Passed
Pull Request — master (#3528)
by Owen
15:11
created

CellReferenceHelper::computeNewColumnIndex()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
c 1
b 0
f 0
dl 0
loc 21
ccs 10
cts 10
cp 1
rs 8.8333
cc 7
nc 4
nop 2
crap 7
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet;
4
5
use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
6
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
7
8
class CellReferenceHelper
9
{
10
    protected string $beforeCellAddress;
11
12
    protected int $beforeColumn;
13
14
    protected bool $beforeColumnAbsolute = false;
15
16
    protected string $beforeColumnString;
17
18
    protected int $beforeRow;
19
20 434
    protected bool $beforeRowAbsolute = false;
21
22 434
    protected int $numberOfColumns;
23 434
24 434
    protected int $numberOfRows;
25
26
    public function __construct(string $beforeCellAddress = 'A1', int $numberOfColumns = 0, int $numberOfRows = 0)
27 434
    {
28 434
        $this->beforeColumnAbsolute = $beforeCellAddress[0] === '$';
29 434
        $this->beforeRowAbsolute = strpos($beforeCellAddress, '$', 1) !== false;
30
        $this->beforeCellAddress = str_replace('$', '', $beforeCellAddress);
31
        $this->numberOfColumns = $numberOfColumns;
32 4
        $this->numberOfRows = $numberOfRows;
33
34 4
        // Get coordinate of $beforeCellAddress
35
        [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
36
        $this->beforeColumnString = $beforeColumn;
37 337
        $this->beforeColumn = (int) Coordinate::columnIndexFromString($beforeColumn);
38
        $this->beforeRow = (int) $beforeRow;
39 337
    }
40 337
41 337
    public function beforeCellAddress(): string
42
    {
43
        return $this->beforeCellAddress;
44 424
    }
45
46 424
    public function refreshRequired(string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): bool
47 1
    {
48
        return $this->beforeCellAddress !== $beforeCellAddress
49
            || $this->numberOfColumns !== $numberOfColumns
50
            || $this->numberOfRows !== $numberOfRows;
51 423
    }
52 423
53 423
    public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false, ?bool $topLeft = null): string
54
    {
55 423
        if (Coordinate::coordinateIsRange($cellReference)) {
56 423
            throw new Exception('Only single cell references may be passed to this method.');
57
        }
58 423
59 5
        // Get coordinate of $cellReference
60 5
        [$newColumn, $newRow] = Coordinate::coordinateFromString($cellReference);
61 419
        $newColumnIndex = Coordinate::columnIndexFromString(str_replace('$', '', $newColumn));
62 357
        $newRowIndex = (int) str_replace('$', '', $newRow);
63 357
64
        $absoluteColumn = $newColumn[0] === '$' ? '$' : '';
65 93
        $absoluteRow = $newRow[0] === '$' ? '$' : '';
66 93
        // Verify which parts should be updated
67
        if ($onlyAbsoluteReferences === true) {
68
            $updateColumn = (($absoluteColumn === '$') && $newColumnIndex >= $this->beforeColumn);
69
            $updateRow = (($absoluteRow === '$') && $newRowIndex >= $this->beforeRow);
70 423
        } elseif ($includeAbsoluteReferences === false) {
71 376
            $updateColumn = (($absoluteColumn !== '$') && $newColumnIndex >= $this->beforeColumn);
72
            $updateRow = (($absoluteRow !== '$') && $newRowIndex >= $this->beforeRow);
73
        } else {
74
            $newColumnIndex = $this->computeNewColumnIndex($newColumnIndex, $topLeft);
75 423
            $newColumn = $absoluteColumn . Coordinate::stringFromColumnIndex($newColumnIndex);
76 373
            $updateColumn = false;
77
78
            $newRowIndex = $this->computeNewRowIndex($newRowIndex, $topLeft);
79
            $newRow = $absoluteRow . $newRowIndex;
80 423
            $updateRow = false;
81
        }
82
83 28
        // Create new column reference
84
        if ($updateColumn) {
85 28
            $newColumn = $this->updateColumnReference($newColumnIndex, $absoluteColumn);
86 28
        }
87
88
        // Create new row reference
89 28
        if ($updateRow) {
90 28
            $newRow = $this->updateRowReference($newRowIndex, $absoluteRow);
91 28
        }
92
93 1
        // Return new reference
94
        return "{$newColumn}{$newRow}";
95 28
    }
96 28
97 28
    public function computeNewColumnIndex(int $newColumnIndex, ?bool $topLeft): int
98
    {
99 1
        // A special case is removing the left/top or bottom/right edge of a range
100
        // $topLeft is null if we aren't adjusting a range at all.
101
        if (
102 27
            $topLeft !== null
103
            && $this->numberOfColumns < 0
104
            && $newColumnIndex >= $this->beforeColumn + $this->numberOfColumns
105 376
            && $newColumnIndex <= $this->beforeColumn - 1
106
        ) {
107 376
            if ($topLeft) {
108
                $newColumnIndex = $this->beforeColumn + $this->numberOfColumns;
109 376
            } else {
110
                $newColumnIndex = $this->beforeColumn + $this->numberOfColumns - 1;
111
            }
112 373
        } elseif ($newColumnIndex >= $this->beforeColumn) {
113
            // Create new column reference
114 373
            $newColumnIndex += $this->numberOfColumns;
115 373
        }
116
117 373
        return $newColumnIndex;
118
    }
119
120
    public function computeNewRowIndex(int $newRowIndex, ?bool $topLeft): int
121
    {
122
        // A special case is removing the left/top or bottom/right edge of a range
123
        // $topLeft is null if we aren't adjusting a range at all.
124
        if (
125
            $topLeft !== null
126
            && $this->numberOfRows < 0
127
            && $newRowIndex >= $this->beforeRow + $this->numberOfRows
128
            && $newRowIndex <= $this->beforeRow - 1
129
        ) {
130
            if ($topLeft) {
131
                $newRowIndex = $this->beforeRow + $this->numberOfRows;
132
            } else {
133
                $newRowIndex = $this->beforeRow + $this->numberOfRows - 1;
134
            }
135
        } elseif ($newRowIndex >= $this->beforeRow) {
136
            $newRowIndex = $newRowIndex + $this->numberOfRows;
137
        }
138
139
        return $newRowIndex;
140
    }
141
142
    public function cellAddressInDeleteRange(string $cellAddress): bool
143
    {
144
        [$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
145
        $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
146
        //    Is cell within the range of rows/columns if we're deleting
147
        if (
148
            $this->numberOfRows < 0
149
            && ($cellRow >= ($this->beforeRow + $this->numberOfRows))
150
            && ($cellRow < $this->beforeRow)
151
        ) {
152
            return true;
153
        } elseif (
154
            $this->numberOfColumns < 0
155
            && ($cellColumnIndex >= ($this->beforeColumn + $this->numberOfColumns))
156
            && ($cellColumnIndex < $this->beforeColumn)
157
        ) {
158
            return true;
159
        }
160
161
        return false;
162
    }
163
164
    protected function updateColumnReference(int $newColumnIndex, string $absoluteColumn): string
165
    {
166
        $newColumn = Coordinate::stringFromColumnIndex(min($newColumnIndex + $this->numberOfColumns, AddressRange::MAX_COLUMN_INT));
167
168
        return "{$absoluteColumn}{$newColumn}";
169
    }
170
171
    protected function updateRowReference(int $newRowIndex, string $absoluteRow): string
172
    {
173
        $newRow = $newRowIndex + $this->numberOfRows;
174
        $newRow = ($newRow > AddressRange::MAX_ROW) ? AddressRange::MAX_ROW : $newRow;
175
176
        return "{$absoluteRow}{$newRow}";
177
    }
178
}
179