Passed
Pull Request — develop_3.0 (#485)
by Adrien
02:50
created

WorksheetManager::addRow()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 43
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 6.0023

Importance

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