2002-08-05 08:54:05 -04:00
|
|
|
/* ====================================================================
|
2006-12-22 14:18:16 -05:00
|
|
|
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
|
2004-04-09 09:05:39 -04:00
|
|
|
|
|
|
|
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.
|
|
|
|
==================================================================== */
|
2004-08-23 04:52:54 -04:00
|
|
|
|
2002-08-05 08:54:05 -04:00
|
|
|
package org.apache.poi.hssf.record.aggregates;
|
|
|
|
|
2009-12-25 10:15:55 -05:00
|
|
|
import org.apache.poi.hssf.record.ArrayRecord;
|
2008-08-28 01:25:24 -04:00
|
|
|
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
|
|
|
import org.apache.poi.hssf.record.FormulaRecord;
|
2008-08-31 00:45:00 -04:00
|
|
|
import org.apache.poi.hssf.record.Record;
|
2008-09-12 03:43:20 -04:00
|
|
|
import org.apache.poi.hssf.record.RecordFormatException;
|
2008-10-30 16:17:04 -04:00
|
|
|
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
2008-08-28 01:25:24 -04:00
|
|
|
import org.apache.poi.hssf.record.StringRecord;
|
2016-06-13 06:53:03 -04:00
|
|
|
import org.apache.poi.hssf.util.CellRangeAddress8Bit;
|
2010-11-24 11:46:02 -05:00
|
|
|
import org.apache.poi.ss.formula.ptg.ExpPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.Ptg;
|
2009-12-25 10:15:55 -05:00
|
|
|
import org.apache.poi.ss.formula.Formula;
|
2016-06-13 06:53:03 -04:00
|
|
|
import org.apache.poi.ss.usermodel.FormulaError;
|
2009-12-25 10:15:55 -05:00
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
2016-06-13 06:53:03 -04:00
|
|
|
import org.apache.poi.ss.util.CellReference;
|
2002-08-05 08:54:05 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The formula record aggregate is used to join together the formula record and it's
|
2003-05-07 20:02:03 -04:00
|
|
|
* (optional) string record and (optional) Shared Formula Record (template reads, excel optimization).
|
2002-08-05 08:54:05 -04:00
|
|
|
*
|
|
|
|
* @author Glen Stampoultzis (glens at apache.org)
|
2009-12-25 10:15:55 -05:00
|
|
|
* @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - Array Formula support
|
2002-08-05 08:54:05 -04:00
|
|
|
*/
|
2008-08-28 01:25:24 -04:00
|
|
|
public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface {
|
2002-08-05 08:54:05 -04:00
|
|
|
|
2008-10-30 16:17:04 -04:00
|
|
|
private final FormulaRecord _formulaRecord;
|
|
|
|
private SharedValueManager _sharedValueManager;
|
|
|
|
/** caches the calculated result of the formula */
|
|
|
|
private StringRecord _stringRecord;
|
|
|
|
private SharedFormulaRecord _sharedFormulaRecord;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param stringRec may be <code>null</code> if this formula does not have a cached text
|
|
|
|
* value.
|
|
|
|
* @param svm the {@link SharedValueManager} for the current sheet
|
|
|
|
*/
|
|
|
|
public FormulaRecordAggregate(FormulaRecord formulaRec, StringRecord stringRec, SharedValueManager svm) {
|
|
|
|
if (svm == null) {
|
|
|
|
throw new IllegalArgumentException("sfm must not be null");
|
|
|
|
}
|
2008-11-15 12:25:32 -05:00
|
|
|
if (formulaRec.hasCachedResultString()) {
|
|
|
|
if (stringRec == null) {
|
|
|
|
throw new RecordFormatException("Formula record flag is set but String record was not found");
|
|
|
|
}
|
|
|
|
_stringRecord = stringRec;
|
|
|
|
} else {
|
|
|
|
// Usually stringRec is null here (in agreement with what the formula rec says).
|
|
|
|
// In the case where an extra StringRecord is erroneously present, Excel (2007)
|
2009-09-17 20:33:18 -04:00
|
|
|
// ignores it (see bug 46213).
|
2008-11-15 12:25:32 -05:00
|
|
|
_stringRecord = null;
|
2008-10-30 16:17:04 -04:00
|
|
|
}
|
|
|
|
|
2009-09-17 20:33:18 -04:00
|
|
|
_formulaRecord = formulaRec;
|
|
|
|
_sharedValueManager = svm;
|
2008-10-30 16:17:04 -04:00
|
|
|
if (formulaRec.isSharedFormula()) {
|
2008-11-07 18:16:48 -05:00
|
|
|
CellReference firstCell = formulaRec.getFormula().getExpReference();
|
|
|
|
if (firstCell == null) {
|
2008-10-30 16:17:04 -04:00
|
|
|
handleMissingSharedFormulaRecord(formulaRec);
|
2008-11-07 18:16:48 -05:00
|
|
|
} else {
|
|
|
|
_sharedFormulaRecord = svm.linkSharedFormulaRecord(firstCell, this);
|
2008-10-30 16:17:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Sometimes the shared formula flag "seems" to be erroneously set (because the corresponding
|
|
|
|
* {@link SharedFormulaRecord} does not exist). Normally this would leave no way of determining
|
2009-09-17 20:33:18 -04:00
|
|
|
* the {@link Ptg} tokens for the formula. However as it turns out in these
|
2008-10-30 16:17:04 -04:00
|
|
|
* cases, Excel encodes the unshared {@link Ptg} tokens in the right place (inside the {@link
|
|
|
|
* FormulaRecord}). So the the only thing that needs to be done is to ignore the erroneous
|
|
|
|
* shared formula flag.<br/>
|
2009-09-17 20:33:18 -04:00
|
|
|
*
|
2008-10-30 16:17:04 -04:00
|
|
|
* This method may also be used for setting breakpoints to help diagnose issues regarding the
|
2009-09-17 20:33:18 -04:00
|
|
|
* abnormally-set 'shared formula' flags.
|
2008-10-30 16:17:04 -04:00
|
|
|
* (see TestValueRecordsAggregate.testSpuriousSharedFormulaFlag()).<p/>
|
|
|
|
*/
|
|
|
|
private static void handleMissingSharedFormulaRecord(FormulaRecord formula) {
|
|
|
|
// make sure 'unshared' formula is actually available
|
2009-09-17 20:33:18 -04:00
|
|
|
Ptg firstToken = formula.getParsedExpression()[0];
|
2008-10-30 16:17:04 -04:00
|
|
|
if (firstToken instanceof ExpPtg) {
|
|
|
|
throw new RecordFormatException(
|
|
|
|
"SharedFormulaRecord not found for FormulaRecord with (isSharedFormula=true)");
|
|
|
|
}
|
|
|
|
// could log an info message here since this is a fairly unusual occurrence.
|
|
|
|
formula.setSharedFormula(false); // no point leaving the flag erroneously set
|
|
|
|
}
|
|
|
|
|
|
|
|
public FormulaRecord getFormulaRecord() {
|
|
|
|
return _formulaRecord;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* debug only
|
|
|
|
* TODO - encapsulate
|
|
|
|
*/
|
|
|
|
public StringRecord getStringRecord() {
|
|
|
|
return _stringRecord;
|
|
|
|
}
|
|
|
|
|
|
|
|
public short getXFIndex() {
|
|
|
|
return _formulaRecord.getXFIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setXFIndex(short xf) {
|
|
|
|
_formulaRecord.setXFIndex(xf);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setColumn(short col) {
|
|
|
|
_formulaRecord.setColumn(col);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setRow(int row) {
|
|
|
|
_formulaRecord.setRow(row);
|
|
|
|
}
|
|
|
|
|
|
|
|
public short getColumn() {
|
|
|
|
return _formulaRecord.getColumn();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getRow() {
|
|
|
|
return _formulaRecord.getRow();
|
|
|
|
}
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
return _formulaRecord.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void visitContainedRecords(RecordVisitor rv) {
|
|
|
|
rv.visitRecord(_formulaRecord);
|
2009-09-17 20:33:18 -04:00
|
|
|
Record sharedFormulaRecord = _sharedValueManager.getRecordForFirstCell(this);
|
|
|
|
if (sharedFormulaRecord != null) {
|
|
|
|
rv.visitRecord(sharedFormulaRecord);
|
2008-10-30 16:17:04 -04:00
|
|
|
}
|
2009-01-21 18:11:45 -05:00
|
|
|
if (_formulaRecord.hasCachedResultString() && _stringRecord != null) {
|
2008-10-30 16:17:04 -04:00
|
|
|
rv.visitRecord(_stringRecord);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getStringValue() {
|
|
|
|
if(_stringRecord==null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return _stringRecord.getString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setCachedStringResult(String value) {
|
|
|
|
|
|
|
|
// Save the string into a String Record, creating one if required
|
|
|
|
if(_stringRecord == null) {
|
|
|
|
_stringRecord = new StringRecord();
|
|
|
|
}
|
|
|
|
_stringRecord.setString(value);
|
|
|
|
if (value.length() < 1) {
|
|
|
|
_formulaRecord.setCachedResultTypeEmptyString();
|
|
|
|
} else {
|
|
|
|
_formulaRecord.setCachedResultTypeString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public void setCachedBooleanResult(boolean value) {
|
|
|
|
_stringRecord = null;
|
|
|
|
_formulaRecord.setCachedResultBoolean(value);
|
|
|
|
}
|
|
|
|
public void setCachedErrorResult(int errorCode) {
|
|
|
|
_stringRecord = null;
|
|
|
|
_formulaRecord.setCachedResultErrorCode(errorCode);
|
|
|
|
}
|
2016-06-13 06:53:03 -04:00
|
|
|
public void setCachedErrorResult(FormulaError error) {
|
|
|
|
setCachedErrorResult(error.getCode());
|
|
|
|
}
|
2009-01-21 18:11:45 -05:00
|
|
|
public void setCachedDoubleResult(double value) {
|
|
|
|
_stringRecord = null;
|
|
|
|
_formulaRecord.setValue(value);
|
|
|
|
}
|
2008-10-30 16:17:04 -04:00
|
|
|
|
|
|
|
public Ptg[] getFormulaTokens() {
|
2009-12-25 18:04:04 -05:00
|
|
|
if (_sharedFormulaRecord != null) {
|
|
|
|
return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
|
|
|
|
}
|
2009-12-25 10:15:55 -05:00
|
|
|
CellReference expRef = _formulaRecord.getFormula().getExpReference();
|
|
|
|
if (expRef != null) {
|
|
|
|
ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
|
|
|
|
return arec.getFormulaTokens();
|
2008-10-30 16:17:04 -04:00
|
|
|
}
|
2009-12-25 10:15:55 -05:00
|
|
|
return _formulaRecord.getParsedExpression();
|
2008-10-30 16:17:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Also checks for a related shared formula and unlinks it if found
|
|
|
|
*/
|
|
|
|
public void setParsedExpression(Ptg[] ptgs) {
|
|
|
|
notifyFormulaChanging();
|
|
|
|
_formulaRecord.setParsedExpression(ptgs);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void unlinkSharedFormula() {
|
|
|
|
SharedFormulaRecord sfr = _sharedFormulaRecord;
|
|
|
|
if (sfr == null) {
|
|
|
|
throw new IllegalStateException("Formula not linked to shared formula");
|
|
|
|
}
|
|
|
|
Ptg[] ptgs = sfr.getFormulaTokens(_formulaRecord);
|
|
|
|
_formulaRecord.setParsedExpression(ptgs);
|
|
|
|
//Now its not shared!
|
|
|
|
_formulaRecord.setSharedFormula(false);
|
|
|
|
_sharedFormulaRecord = null;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Should be called by any code which is either deleting this formula cell, or changing
|
|
|
|
* its type. This method gives the aggregate a chance to unlink any shared formula
|
|
|
|
* that may be involved with this cell formula.
|
|
|
|
*/
|
|
|
|
public void notifyFormulaChanging() {
|
|
|
|
if (_sharedFormulaRecord != null) {
|
|
|
|
_sharedValueManager.unlink(_sharedFormulaRecord);
|
|
|
|
}
|
|
|
|
}
|
2009-12-25 18:04:04 -05:00
|
|
|
public boolean isPartOfArrayFormula() {
|
|
|
|
if (_sharedFormulaRecord != null) {
|
|
|
|
return false;
|
|
|
|
}
|
2009-12-29 14:47:38 -05:00
|
|
|
CellReference expRef = _formulaRecord.getFormula().getExpReference();
|
|
|
|
ArrayRecord arec = expRef == null ? null : _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
|
|
|
|
return arec != null;
|
2009-12-25 18:04:04 -05:00
|
|
|
}
|
2009-12-25 10:15:55 -05:00
|
|
|
|
2009-12-25 18:04:04 -05:00
|
|
|
public CellRangeAddress getArrayFormulaRange() {
|
2009-12-25 10:15:55 -05:00
|
|
|
if (_sharedFormulaRecord != null) {
|
|
|
|
throw new IllegalStateException("not an array formula cell.");
|
|
|
|
}
|
|
|
|
CellReference expRef = _formulaRecord.getFormula().getExpReference();
|
|
|
|
if (expRef == null) {
|
|
|
|
throw new IllegalStateException("not an array formula cell.");
|
|
|
|
}
|
|
|
|
ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
|
2009-12-25 18:04:04 -05:00
|
|
|
if (arec == null) {
|
|
|
|
throw new IllegalStateException("ArrayRecord was not found for the locator " + expRef.formatAsString());
|
|
|
|
}
|
2009-12-25 10:15:55 -05:00
|
|
|
CellRangeAddress8Bit a = arec.getRange();
|
|
|
|
return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(),a.getLastColumn());
|
|
|
|
}
|
2009-12-25 18:04:04 -05:00
|
|
|
|
|
|
|
public void setArrayFormula(CellRangeAddress r, Ptg[] ptgs) {
|
2009-12-25 10:15:55 -05:00
|
|
|
|
|
|
|
ArrayRecord arr = new ArrayRecord(Formula.create(ptgs), new CellRangeAddress8Bit(r.getFirstRow(), r.getLastRow(), r.getFirstColumn(), r.getLastColumn()));
|
|
|
|
_sharedValueManager.addArrayRecord(arr);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Removes an array formula
|
|
|
|
* @return the range of the array formula containing the specified cell. Never <code>null</code>
|
|
|
|
*/
|
|
|
|
public CellRangeAddress removeArrayFormula(int rowIndex, int columnIndex) {
|
|
|
|
CellRangeAddress8Bit a = _sharedValueManager.removeArrayFormula(rowIndex, columnIndex);
|
2009-12-29 14:47:38 -05:00
|
|
|
// at this point FormulaRecordAggregate#isPartOfArrayFormula() should return false
|
|
|
|
_formulaRecord.setParsedExpression(null);
|
|
|
|
return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(), a.getLastColumn());
|
2009-12-25 10:15:55 -05:00
|
|
|
}
|
2002-08-05 08:54:05 -04:00
|
|
|
}
|