Passed
Pull Request — develop_3.0 (#434)
by Adrien
03:29
created

WorksheetManager   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 95.95%

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 6
dl 0
loc 189
ccs 71
cts 74
cp 0.9595
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A startSheet() 0 7 1
A throwIfSheetFilePointerIsNotAvailable() 0 6 2
A getTableElementStartAsString() 0 11 1
B addRow() 0 43 6
C getCellXML() 0 33 7
A close() 0 10 2
1
<?php
2
3
namespace Box\Spout\Writer\ODS\Manager;
4
5
use Box\Spout\Common\Exception\InvalidArgumentException;
6
use Box\Spout\Common\Exception\IOException;
7
use Box\Spout\Common\Helper\Escaper\ODS as ODSEscaper;
8
use Box\Spout\Common\Helper\StringHelper;
9
use Box\Spout\Writer\Common\Entity\Cell;
10
use Box\Spout\Writer\Common\Entity\Row;
11
use Box\Spout\Writer\Common\Entity\Worksheet;
12
use Box\Spout\Writer\Common\Manager\WorksheetManagerInterface;
13
use Box\Spout\Writer\ODS\Manager\Style\StyleManager;
14
15
/**
16
 * Class WorksheetManager
17
 * ODS worksheet manager, providing the interfaces to work with ODS worksheets.
18
 */
19
class WorksheetManager implements WorksheetManagerInterface
20
{
21
    /** @var \Box\Spout\Common\Helper\Escaper\ODS Strings escaper */
22
    private $stringsEscaper;
23
24
    /** @var StringHelper String helper */
25
    private $stringHelper;
26
27
    /** @var StyleManager Manages styles */
28
    private $styleManager;
29
30
    /**
31
     * WorksheetManager constructor.
32
     * @param StyleManager $styleManager
33
     * @param ODSEscaper $stringsEscaper
34
     * @param StringHelper $stringHelper
35
     */
36 41
    public function __construct(
37
        StyleManager $styleManager,
38
        ODSEscaper $stringsEscaper,
39
        StringHelper $stringHelper
40
    ) {
41 41
        $this->stringsEscaper = $stringsEscaper;
42 41
        $this->stringHelper = $stringHelper;
43 41
        $this->styleManager = $styleManager;
44 41
    }
45
46
    /**
47
     * Prepares the worksheet to accept data
48
     *
49
     * @param Worksheet $worksheet The worksheet to start
50
     * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
51
     * @return void
52
     */
53 41
    public function startSheet(Worksheet $worksheet)
54
    {
55 41
        $sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
56 41
        $this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
57
58 41
        $worksheet->setFilePointer($sheetFilePointer);
59 41
    }
60
61
    /**
62
     * Checks if the sheet has been sucessfully created. Throws an exception if not.
63
     *
64
     * @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file
65
     * @throws IOException If the sheet data file cannot be opened for writing
66
     * @return void
67
     */
68 41
    private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
69
    {
70 41
        if (!$sheetFilePointer) {
71
            throw new IOException('Unable to open sheet for writing.');
72
        }
73 41
    }
74
75
    /**
76
     * Returns the table XML root node as string.
77
     *
78
     * @param Worksheet $worksheet
79
     * @return string <table> node as string
80
     */
81 34
    public function getTableElementStartAsString(Worksheet $worksheet)
82
    {
83 34
        $externalSheet = $worksheet->getExternalSheet();
84 34
        $escapedSheetName = $this->stringsEscaper->escape($externalSheet->getName());
85 34
        $tableStyleName = 'ta' . ($externalSheet->getIndex() + 1);
86
87 34
        $tableElement  = '<table:table table:style-name="' . $tableStyleName . '" table:name="' . $escapedSheetName . '">';
88 34
        $tableElement .= '<table:table-column table:default-cell-style-name="ce1" table:style-name="co1" table:number-columns-repeated="' . $worksheet->getMaxNumColumns() . '"/>';
89
90 34
        return $tableElement;
91
    }
92
93
    /**
94
     * Adds a row to the worksheet.
95
     *
96
     * @param Worksheet $worksheet The worksheet to add the row to
97
     * @param Row $row The row to be added
98
     * @throws IOException If the data cannot be written
99
     * @throws InvalidArgumentException If a cell value's type is not supported
100
     * @return void
101
     *
102
     * @return void
103
     */
104 30
    public function addRow(Worksheet $worksheet, Row $row)
105
    {
106 30
        $cells = $row->getCells();
107 30
        $cellsCount = count($cells);
108
109 30
        $data = '<table:table-row table:style-name="ro1">';
110
111 30
        $currentCellIndex = 0;
112 30
        $nextCellIndex = 1;
113
114 30
        for ($i = 0; $i < $cellsCount; $i++) {
115
            /** @var Cell $cell */
116 30
            $cell = $cells[$currentCellIndex];
117
            /** @var Cell|null $nextCell */
118 30
            $nextCell = isset($cells[$nextCellIndex]) ? $cells[$nextCellIndex] : null;
119
120
            // @TODO refactoring: move this to its own method
121 30
            if ($nextCell === null || $cell->getValue() !== $nextCell->getValue()) {
122
                // Apply styles - the row style is merged at this point
123 30
                $cell->applyStyle($row->getStyle());
124 30
                $this->styleManager->applyExtraStylesIfNeeded($cell);
125 30
                $registeredStyle = $this->styleManager->registerStyle($cell->getStyle());
126 30
                $styleIndex = $registeredStyle->getId() + 1; // 1-based
127
128 30
                $numTimesValueRepeated = ($nextCellIndex - $currentCellIndex);
129 30
                $data .= $this->getCellXML($cell, $styleIndex, $numTimesValueRepeated);
130 29
                $currentCellIndex = $nextCellIndex;
131
            }
132
133 29
            $nextCellIndex++;
134
        }
135
136 29
        $data .= '</table:table-row>';
137
138 29
        $wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data);
139 29
        if ($wasWriteSuccessful === false) {
140
            throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
141
        }
142
143
        // only update the count if the write worked
144 29
        $lastWrittenRowIndex = $worksheet->getLastWrittenRowIndex();
145 29
        $worksheet->setLastWrittenRowIndex($lastWrittenRowIndex + 1);
146 29
    }
147
148
    /**
149
     * Returns the cell XML content, given its value.
150
     *
151
     * @param Cell $cell The cell to be written
152
     * @param int $styleIndex Index of the used style
153
     * @param int $numTimesValueRepeated Number of times the value is consecutively repeated
154
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
155
     * @return string The cell XML content
156
     */
157 30
    protected function getCellXML(Cell $cell, $styleIndex, $numTimesValueRepeated)
158
    {
159 30
        $data = '<table:table-cell table:style-name="ce' . $styleIndex . '"';
160
161 30
        if ($numTimesValueRepeated !== 1) {
162 4
            $data .= ' table:number-columns-repeated="' . $numTimesValueRepeated . '"';
163
        }
164
165 30
        if ($cell->isString()) {
166 26
            $data .= ' office:value-type="string" calcext:value-type="string">';
167
168 26
            $cellValueLines = explode("\n", $cell->getValue());
169 26
            foreach ($cellValueLines as $cellValueLine) {
170 26
                $data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
171
            }
172
173 26
            $data .= '</table:table-cell>';
174 6
        } elseif ($cell->isBoolean()) {
175 3
            $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="' . $cell->getValue() . '">';
176 3
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
177 3
            $data .= '</table:table-cell>';
178 5
        } elseif ($cell->isNumeric()) {
179 3
            $data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cell->getValue() . '">';
180 3
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
181 3
            $data .= '</table:table-cell>';
182 3
        } elseif ($cell->isEmpty()) {
183 2
            $data .= '/>';
184
        } else {
185 1
            throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
186
        }
187
188 29
        return $data;
189
    }
190
191
    /**
192
     * Closes the worksheet
193
     *
194
     * @param Worksheet $worksheet
195
     * @return void
196
     */
197 34
    public function close(Worksheet $worksheet)
198
    {
199 34
        $worksheetFilePointer = $worksheet->getFilePointer();
200
201 34
        if (!is_resource($worksheetFilePointer)) {
202
            return;
203
        }
204
205 34
        fclose($worksheetFilePointer);
206 34
    }
207
}
208