1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
|
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.usermodel;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.Removal;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
/**
* Common implementation-independent logic shared by all implementations of {@link Cell}.
* @author Vladislav "gallon" Galas gallon at apache dot org
*/
public abstract class CellBase implements Cell {
/**
* {@inheritDoc}
*/
@Override
public final void setCellType(CellType cellType) {
if (cellType == null || cellType == CellType._NONE) {
throw new IllegalArgumentException("cellType shall not be null nor _NONE");
}
if (cellType == CellType.FORMULA) {
if (getCellType() != CellType.FORMULA){
throw new IllegalArgumentException("Calling Cell.setCellType(CellType.FORMULA) is illegal. " +
"Use setCellFormula(String) directly.");
} else {
return;
}
}
tryToDeleteArrayFormulaIfSet();
setCellTypeImpl(cellType);
}
/**
* {@inheritDoc}
*/
@Override
public void setBlank() {
setCellType(CellType.BLANK);
}
/**
* {@inheritDoc}
*/
@Override
public CellAddress getAddress() {
return new CellAddress(this);
}
/**
* Implementation-specific logic
* @param cellType new cell type. Guaranteed non-null, not _NONE.
*/
protected abstract void setCellTypeImpl(CellType cellType);
/**
* Called when this an array formula in this cell is deleted.
* <p>The purpose of this method is to validate the cell state prior to modification.</p>
*
* @param message a customized exception message for the case if deletion of the cell is impossible. If null, a
* default message will be generated
* @see #setCellType(CellType)
* @see #setCellFormula(String)
* @see Row#removeCell(org.apache.poi.ss.usermodel.Cell)
* @see org.apache.poi.ss.usermodel.Sheet#removeRow(org.apache.poi.ss.usermodel.Row)
* @see org.apache.poi.ss.usermodel.Sheet#shiftRows(int, int, int)
* @see org.apache.poi.ss.usermodel.Sheet#addMergedRegion(org.apache.poi.ss.util.CellRangeAddress)
* @throws IllegalStateException if modification is not allowed
*
* Note. Exposing this to public is ugly. Needed for methods like Sheet#shiftRows.
*/
public final void tryToDeleteArrayFormula(String message) {
assert isPartOfArrayFormulaGroup();
CellRangeAddress arrayFormulaRange = getArrayFormulaRange();
if(arrayFormulaRange.getNumberOfCells() > 1) {
if (message == null) {
message = "Cell " + new CellReference(this).formatAsString() + " is part of a multi-cell array formula. " +
"You cannot change part of an array.";
}
throw new IllegalStateException(message);
}
//un-register the single-cell array formula from the parent sheet through public interface
getRow().getSheet().removeArrayFormula(this);
}
/**
* {@inheritDoc}
*/
@Override
public final void setCellFormula(String formula) throws FormulaParseException, IllegalStateException {
// todo validate formula here, before changing the cell?
tryToDeleteArrayFormulaIfSet();
if (formula == null) {
removeFormula();
return;
}
// formula cells always have a value. If the cell is blank (either initially or after removing an
// array formula), set value to 0
if (getValueType() == CellType.BLANK) {
setCellValue(0);
}
setCellFormulaImpl(formula);
}
/**
* {@inheritDoc}
*/
@Deprecated
@Removal(version = "4.2")
@Override
public final CellType getCellTypeEnum() {
return getCellType();
}
/**
* Implementation-specific setting the formula. Formula is not null.
* Shall not change the value.
* @param formula
*/
protected abstract void setCellFormulaImpl(String formula);
/**
* Get value type of this cell. Can return BLANK, NUMERIC, STRING, BOOLEAN or ERROR.
* For current implementations where type is strongly coupled with formula, is equivalent to
* <code>getCellType() == CellType.FORMULA ? getCachedFormulaResultType() : getCellType()</code>
*
* <p>This is meant as a temporary helper method until the time when value type is decoupled from the formula.</p>
* @return value type
*/
protected final CellType getValueType() {
CellType type = getCellType();
if (type != CellType.FORMULA) {
return type;
}
return getCachedFormulaResultType();
}
/**
* {@inheritDoc}
*/
@Override
public final void removeFormula() {
if (getCellType() == CellType.BLANK) {
return;
}
if (isPartOfArrayFormulaGroup()) {
tryToDeleteArrayFormula(null);
return;
}
removeFormulaImpl();
}
/**
* Implementation-specific removal of the formula.
* The cell is guaranteed to have a regular formula set.
* Shall preserve the "cached" value.
*/
protected abstract void removeFormulaImpl();
private void tryToDeleteArrayFormulaIfSet() {
if (isPartOfArrayFormulaGroup()) {
tryToDeleteArrayFormula(null);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setCellValue(double value) {
if(Double.isInfinite(value)) {
// Excel does not support positive/negative infinities,
// rather, it gives a #DIV/0! error in these cases.
setCellErrorValue(FormulaError.DIV0.getCode());
} else if (Double.isNaN(value)){
setCellErrorValue(FormulaError.NUM.getCode());
} else {
setCellValueImpl(value);
}
}
/**
* Implementation-specific way to set a numeric value.
* <code>value</code> is guaranteed to be a valid (non-NaN) double.
* The implementation is expected to adjust the cell type accordingly, so that after this call
* getCellType() or getCachedFormulaResultType() would return {@link CellType#NUMERIC}.
* @param value the new value to set
*/
protected abstract void setCellValueImpl(double value);
@Override
public void setCellValue(Date value) {
if(value == null) {
setBlank();
return;
}
setCellValueImpl(value);
}
@Override
public void setCellValue(LocalDateTime value) {
if(value == null) {
setBlank();
return;
}
setCellValueImpl(value);
}
/**
* Implementation-specific way to set a date value.
* <code>value</code> is guaranteed to be non-null.
* The implementation is expected to adjust the cell type accordingly, so that after this call
* getCellType() or getCachedFormulaResultType() would return {@link CellType#NUMERIC}.
* @param value the new date to set
*/
protected abstract void setCellValueImpl(Date value);
/**
* Implementation-specific way to set a date value.
* <code>value</code> is guaranteed to be non-null.
* The implementation is expected to adjust the cell type accordingly, so that after this call
* getCellType() or getCachedFormulaResultType() would return {@link CellType#NUMERIC}.
* @param value the new date to set
*/
protected abstract void setCellValueImpl(LocalDateTime value);
/**
* {@inheritDoc}
*/
@Override
public void setCellValue(Calendar value) {
if(value == null) {
setBlank();
return;
}
setCellValueImpl(value);
}
/**
* Implementation-specific way to set a calendar value.
* <code>value</code> is guaranteed to be non-null.
* The implementation is expected to adjust the cell type accordingly, so that after this call
* getCellType() or getCachedFormulaResultType() would return {@link CellType#NUMERIC}.
* @param value the new calendar value to set
*/
protected abstract void setCellValueImpl(Calendar value);
/**
* {@inheritDoc}
*/
@Override
public void setCellValue(String value) {
if(value == null){
setBlank();
return;
}
checkLength(value);
setCellValueImpl(value);
}
/**
* Implementation-specific way to set a string value.
* The value is guaranteed to be non-null and to satisfy the length limitation imposed by the spreadsheet version.
* The implementation is expected to adjust cell type accordingly, so that after this call
* getCellType() or getCachedFormulaResultType() (whichever appropriate) would return {@link CellType#STRING}.
* @param value the new value to set.
*/
protected abstract void setCellValueImpl(String value);
private void checkLength(String value) {
if(value.length() > getSpreadsheetVersion().getMaxTextLength()){
final String message = String.format(Locale.ROOT,
"The maximum length of cell contents (text) is %d characters",
getSpreadsheetVersion().getMaxTextLength());
throw new IllegalArgumentException(message);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setCellValue(RichTextString value) {
if(value == null || value.getString() == null){
setBlank();
return;
}
checkLength(value.getString());
setCellValueImpl(value);
}
/**
* Implementation-specific way to set a RichTextString value.
* The value is guaranteed to be non-null, having non-null value, and to satisfy the length limitation imposed
* by the spreadsheet version.
* The implementation is expected to adjust cell type accordingly, so that after this call
* getCellType() or getCachedFormulaResultType() (whichever appropriate) would return {@link CellType#STRING}.
* @param value the new value to set.
*/
protected abstract void setCellValueImpl(RichTextString value);
/**
* Get the spreadsheet version for the given implementation.
* @return the spreadsheet version
*/
protected abstract SpreadsheetVersion getSpreadsheetVersion();
}
|