| /* ==================================================================== |
| 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}. |
| */ |
| 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; |
| } |
| |
| setCellFormulaImpl(formula); |
| } |
| |
| /** |
| * 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(); |
| } |