Merged revisions 699178,699487,699489,699761 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk ........ r699178 | josh | 2008-09-25 21:49:20 -0700 (Thu, 25 Sep 2008) | 1 line Changed HSSFEvaluationWorkbook to avoid re-parsing cell formulas during execution. (working towards fix for bug 45865) ........ r699487 | josh | 2008-09-26 13:25:45 -0700 (Fri, 26 Sep 2008) | 1 line Fix formula parser to properly support the range operator. Small fixes to parsing of sheet names and full column references. ........ r699489 | josh | 2008-09-26 13:32:06 -0700 (Fri, 26 Sep 2008) | 1 line Code cleanup in junit ........ r699761 | josh | 2008-09-27 19:04:31 -0700 (Sat, 27 Sep 2008) | 1 line Bug 45865 - modified Formula Parser/Evaluator to handle cross-worksheet formulas ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@700234 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0d50343293
commit
96cb7321ba
@ -67,6 +67,7 @@
|
||||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.2-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
|
||||
|
@ -64,6 +64,7 @@
|
||||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.2-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
|
||||
|
@ -29,6 +29,7 @@ import org.apache.poi.hssf.record.ExternalNameRecord;
|
||||
import org.apache.poi.hssf.record.NameRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.SupBookRecord;
|
||||
import org.apache.poi.hssf.record.UnicodeString;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
|
||||
/**
|
||||
@ -109,8 +110,8 @@ final class LinkTable {
|
||||
temp.toArray(_crnBlocks);
|
||||
}
|
||||
|
||||
public ExternalBookBlock(short numberOfSheets) {
|
||||
_externalBookRecord = SupBookRecord.createInternalReferences(numberOfSheets);
|
||||
public ExternalBookBlock(int numberOfSheets) {
|
||||
_externalBookRecord = SupBookRecord.createInternalReferences((short)numberOfSheets);
|
||||
_externalNameRecords = new ExternalNameRecord[0];
|
||||
_crnBlocks = new CRNBlock[0];
|
||||
}
|
||||
@ -197,7 +198,7 @@ final class LinkTable {
|
||||
return ExternSheetRecord.combine(esrs);
|
||||
}
|
||||
|
||||
public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) {
|
||||
public LinkTable(int numberOfSheets, WorkbookRecordList workbookRecordList) {
|
||||
_workbookRecordList = workbookRecordList;
|
||||
_definedNames = new ArrayList();
|
||||
_externalBookBlocks = new ExternalBookBlock[] {
|
||||
@ -303,8 +304,62 @@ final class LinkTable {
|
||||
return lastName.getSheetNumber() == firstName.getSheetNumber();
|
||||
}
|
||||
|
||||
public String[] getExternalBookAndSheetName(int extRefIndex) {
|
||||
int ebIx = _externSheetRecord.getExtbookIndexFromRefIndex(extRefIndex);
|
||||
SupBookRecord ebr = _externalBookBlocks[ebIx].getExternalBookRecord();
|
||||
if (!ebr.isExternalReferences()) {
|
||||
return null;
|
||||
}
|
||||
int shIx = _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
|
||||
UnicodeString usSheetName = ebr.getSheetNames()[shIx];
|
||||
return new String[] {
|
||||
ebr.getURL(),
|
||||
usSheetName.getString(),
|
||||
};
|
||||
}
|
||||
|
||||
public int getIndexToSheet(int extRefIndex) {
|
||||
public int getExternalSheetIndex(String workbookName, String sheetName) {
|
||||
SupBookRecord ebrTarget = null;
|
||||
int externalBookIndex = -1;
|
||||
for (int i=0; i<_externalBookBlocks.length; i++) {
|
||||
SupBookRecord ebr = _externalBookBlocks[i].getExternalBookRecord();
|
||||
if (!ebr.isExternalReferences()) {
|
||||
continue;
|
||||
}
|
||||
if (workbookName.equals(ebr.getURL())) { // not sure if 'equals()' works when url has a directory
|
||||
ebrTarget = ebr;
|
||||
externalBookIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ebrTarget == null) {
|
||||
throw new RuntimeException("No external workbook with name '" + workbookName + "'");
|
||||
}
|
||||
int sheetIndex = getSheetIndex(ebrTarget.getSheetNames(), sheetName);
|
||||
|
||||
int result = _externSheetRecord.getRefIxForSheet(externalBookIndex, sheetIndex);
|
||||
if (result < 0) {
|
||||
throw new RuntimeException("ExternSheetRecord does not contain combination ("
|
||||
+ externalBookIndex + ", " + sheetIndex + ")");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getSheetIndex(UnicodeString[] sheetNames, String sheetName) {
|
||||
for (int i = 0; i < sheetNames.length; i++) {
|
||||
if (sheetNames[i].getString().equals(sheetName)) {
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
throw new RuntimeException("External workbook does not contain sheet '" + sheetName + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extRefIndex as from a {@link Ref3DPtg} or {@link Area3DPtg}
|
||||
* @return -1 if the reference is to an external book
|
||||
*/
|
||||
public int getIndexToInternalSheet(int extRefIndex) {
|
||||
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
|
||||
}
|
||||
|
||||
@ -315,20 +370,26 @@ final class LinkTable {
|
||||
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
|
||||
}
|
||||
|
||||
public int addSheetIndexToExternSheet(int sheetNumber) {
|
||||
// TODO - what about the first parameter (extBookIndex)?
|
||||
return _externSheetRecord.addRef(0, sheetNumber, sheetNumber);
|
||||
public int checkExternSheet(int sheetIndex) {
|
||||
int thisWbIndex = -1; // this is probably always zero
|
||||
for (int i=0; i<_externalBookBlocks.length; i++) {
|
||||
SupBookRecord ebr = _externalBookBlocks[i].getExternalBookRecord();
|
||||
if (ebr.isInternalReferences()) {
|
||||
thisWbIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (thisWbIndex < 0) {
|
||||
throw new RuntimeException("Could not find 'internal references' EXTERNALBOOK");
|
||||
}
|
||||
|
||||
public short checkExternSheet(int sheetIndex) {
|
||||
|
||||
//Trying to find reference to this sheet
|
||||
int i = _externSheetRecord.getRefIxForSheet(sheetIndex);
|
||||
int i = _externSheetRecord.getRefIxForSheet(thisWbIndex, sheetIndex);
|
||||
if (i>=0) {
|
||||
return (short)i;
|
||||
return i;
|
||||
}
|
||||
//We Haven't found reference to this sheet
|
||||
return (short)addSheetIndexToExternSheet((short) sheetIndex);
|
||||
//We haven't found reference to this sheet
|
||||
return _externSheetRecord.addRef(thisWbIndex, sheetIndex, sheetIndex);
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ import org.apache.poi.ddf.*;
|
||||
import org.apache.poi.hssf.record.*;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
@ -1914,8 +1915,7 @@ public final class Workbook implements Model {
|
||||
*/
|
||||
public String findSheetNameFromExternSheet(int externSheetIndex){
|
||||
|
||||
int indexToSheet = linkTable.getIndexToSheet(externSheetIndex);
|
||||
|
||||
int indexToSheet = linkTable.getIndexToInternalSheet(externSheetIndex);
|
||||
if (indexToSheet < 0) {
|
||||
// TODO - what does '-1' mean here?
|
||||
//error check, bail out gracefully!
|
||||
@ -1927,6 +1927,13 @@ public final class Workbook implements Model {
|
||||
}
|
||||
return getSheetName(indexToSheet);
|
||||
}
|
||||
public ExternalSheet getExternalSheet(int externSheetIndex) {
|
||||
String[] extNames = linkTable.getExternalBookAndSheetName(externSheetIndex);
|
||||
if (extNames == null) {
|
||||
return null;
|
||||
}
|
||||
return new ExternalSheet(extNames[0], extNames[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the sheet index for a particular external sheet number.
|
||||
@ -1944,9 +1951,14 @@ public final class Workbook implements Model {
|
||||
* @return index to extern sheet
|
||||
*/
|
||||
public short checkExternSheet(int sheetNumber){
|
||||
return getOrCreateLinkTable().checkExternSheet(sheetNumber);
|
||||
return (short)getOrCreateLinkTable().checkExternSheet(sheetNumber);
|
||||
}
|
||||
|
||||
public int getExternalSheetIndex(String workbookName, String sheetName) {
|
||||
return getOrCreateLinkTable().getExternalSheetIndex(workbookName, sheetName);
|
||||
}
|
||||
|
||||
|
||||
/** gets the total number of names
|
||||
* @return number of names
|
||||
*/
|
||||
|
@ -250,10 +250,13 @@ public class ExternSheetRecord extends Record {
|
||||
return _list.size() - 1;
|
||||
}
|
||||
|
||||
public int getRefIxForSheet(int sheetIndex) {
|
||||
public int getRefIxForSheet(int externalBookIndex, int sheetIndex) {
|
||||
int nItems = _list.size();
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
RefSubRecord ref = getRef(i);
|
||||
if (ref.getExtBookIndex() != externalBookIndex) {
|
||||
continue;
|
||||
}
|
||||
if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) {
|
||||
return i;
|
||||
}
|
||||
|
@ -221,8 +221,33 @@ public final class SupBookRecord extends Record {
|
||||
{
|
||||
return sid;
|
||||
}
|
||||
public UnicodeString getURL() {
|
||||
return field_2_encoded_url;
|
||||
public String getURL() {
|
||||
String encodedUrl = field_2_encoded_url.getString();
|
||||
switch(encodedUrl.charAt(0)) {
|
||||
case 0: // Reference to an empty workbook name
|
||||
return encodedUrl.substring(1); // will this just be empty string?
|
||||
case 1: // encoded file name
|
||||
return decodeFileName(encodedUrl);
|
||||
case 2: // Self-referential external reference
|
||||
return encodedUrl.substring(1);
|
||||
|
||||
}
|
||||
return encodedUrl;
|
||||
}
|
||||
private static String decodeFileName(String encodedUrl) {
|
||||
return encodedUrl.substring(1);
|
||||
// TODO the following special characters may appear in the rest of the string, and need to get interpreted
|
||||
/* see "MICROSOFT OFFICE EXCEL 97-2007 BINARY FILE FORMAT SPECIFICATION"
|
||||
chVolume 1
|
||||
chSameVolume 2
|
||||
chDownDir 3
|
||||
chUpDir 4
|
||||
chLongVolume 5
|
||||
chStartupDir 6
|
||||
chAltStartupDir 7
|
||||
chLibDir 8
|
||||
|
||||
*/
|
||||
}
|
||||
public UnicodeString[] getSheetNames() {
|
||||
return (UnicodeString[]) field_3_sheet_names.clone();
|
||||
|
@ -18,8 +18,9 @@
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
import org.apache.poi.ss.formula.WorkbookDependentFormula;
|
||||
import org.apache.poi.ss.formula.ExternSheetReferenceToken;
|
||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||
import org.apache.poi.ss.formula.WorkbookDependentFormula;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
@ -31,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
* @version 1.0-pre
|
||||
*/
|
||||
public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFormula {
|
||||
public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFormula, ExternSheetReferenceToken {
|
||||
public final static byte sid = 0x3b;
|
||||
private final static int SIZE = 11; // 10 + 1 for Ptg
|
||||
|
||||
@ -76,8 +77,8 @@ public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFor
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
public short getExternSheetIndex() {
|
||||
return (short)field_1_index_extern_sheet;
|
||||
public int getExternSheetIndex() {
|
||||
return field_1_index_extern_sheet;
|
||||
}
|
||||
|
||||
public void setExternSheetIndex(int index) {
|
||||
|
@ -50,10 +50,10 @@ public interface AreaI {
|
||||
|
||||
public OffsetArea(int baseRow, int baseColumn, int relFirstRowIx, int relLastRowIx,
|
||||
int relFirstColIx, int relLastColIx) {
|
||||
_firstRow = baseRow + relFirstRowIx;
|
||||
_lastRow = baseRow + relLastRowIx;
|
||||
_firstColumn = baseColumn + relFirstColIx;
|
||||
_lastColumn = baseColumn + relLastColIx;
|
||||
_firstRow = baseRow + Math.min(relFirstRowIx, relLastRowIx);
|
||||
_lastRow = baseRow + Math.max(relFirstRowIx, relLastRowIx);
|
||||
_firstColumn = baseColumn + Math.min(relFirstColIx, relLastColIx);
|
||||
_lastColumn = baseColumn + Math.max(relFirstColIx, relLastColIx);
|
||||
}
|
||||
|
||||
public int getFirstColumn() {
|
||||
@ -72,5 +72,4 @@ public interface AreaI {
|
||||
return _lastRow;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -76,14 +76,30 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
|
||||
checkColumnBounds(lastColumn);
|
||||
checkRowBounds(firstRow);
|
||||
checkRowBounds(lastRow);
|
||||
|
||||
if (lastRow > firstRow) {
|
||||
setFirstRow(firstRow);
|
||||
setLastRow(lastRow);
|
||||
setFirstColumn(firstColumn);
|
||||
setLastColumn(lastColumn);
|
||||
setFirstRowRelative(firstRowRelative);
|
||||
setLastRowRelative(lastRowRelative);
|
||||
} else {
|
||||
setFirstRow(lastRow);
|
||||
setLastRow(firstRow);
|
||||
setFirstRowRelative(lastRowRelative);
|
||||
setLastRowRelative(firstRowRelative);
|
||||
}
|
||||
|
||||
if (lastColumn > firstColumn) {
|
||||
setFirstColumn(firstColumn);
|
||||
setLastColumn(lastColumn);
|
||||
setFirstColRelative(firstColRelative);
|
||||
setLastColRelative(lastColRelative);
|
||||
} else {
|
||||
setFirstColumn(lastColumn);
|
||||
setLastColumn(firstColumn);
|
||||
setFirstColRelative(lastColRelative);
|
||||
setLastColRelative(firstColRelative);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkColumnBounds(int colIx) {
|
||||
|
@ -50,6 +50,8 @@ public final class AttrPtg extends ControlPtg {
|
||||
private static final BitField baxcel = BitFieldFactory.getInstance(0x20); // 'assignment-style formula in a macro sheet'
|
||||
private static final BitField space = BitFieldFactory.getInstance(0x40);
|
||||
|
||||
public static final AttrPtg SUM = new AttrPtg(0x0010, 0, null, -1);
|
||||
|
||||
public static final class SpaceType {
|
||||
private SpaceType() {
|
||||
// no instances of this class
|
||||
|
@ -18,6 +18,7 @@
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
@ -29,14 +30,23 @@ final class ExternSheetNameResolver {
|
||||
}
|
||||
|
||||
public static String prependSheetName(FormulaRenderingWorkbook book, int field_1_index_extern_sheet, String cellRefText) {
|
||||
ExternalSheet externalSheet = book.getExternalSheet(field_1_index_extern_sheet);
|
||||
StringBuffer sb;
|
||||
if (externalSheet != null) {
|
||||
String wbName = externalSheet.getWorkbookName();
|
||||
String sheetName = externalSheet.getSheetName();
|
||||
sb = new StringBuffer(wbName.length() + sheetName.length() + cellRefText.length() + 4);
|
||||
SheetNameFormatter.appendFormat(sb, wbName, sheetName);
|
||||
} else {
|
||||
String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
|
||||
StringBuffer sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
|
||||
sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
|
||||
if (sheetName.length() < 1) {
|
||||
// What excel does if sheet has been deleted
|
||||
sb.append("#REF"); // note - '!' added just once below
|
||||
} else {
|
||||
SheetNameFormatter.appendFormat(sb, sheetName);
|
||||
}
|
||||
}
|
||||
sb.append('!');
|
||||
sb.append(cellRefText);
|
||||
return sb.toString();
|
||||
|
@ -19,8 +19,9 @@ package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
import org.apache.poi.ss.util.CellReference;
|
||||
import org.apache.poi.ss.formula.WorkbookDependentFormula;
|
||||
import org.apache.poi.ss.formula.ExternSheetReferenceToken;
|
||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||
import org.apache.poi.ss.formula.WorkbookDependentFormula;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
@ -31,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
* @version 1.0-pre
|
||||
*/
|
||||
public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula {
|
||||
public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula, ExternSheetReferenceToken {
|
||||
public final static byte sid = 0x3a;
|
||||
|
||||
private final static int SIZE = 7; // 6 + 1 for Ptg
|
||||
@ -75,11 +76,11 @@ public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormu
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
public int getExternSheetIndex(){
|
||||
public int getExternSheetIndex() {
|
||||
return field_1_index_extern_sheet;
|
||||
}
|
||||
|
||||
public void setExternSheetIndex(int index){
|
||||
public void setExternSheetIndex(int index) {
|
||||
field_1_index_extern_sheet = index;
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,22 @@ public final class SheetNameFormatter {
|
||||
out.append(rawSheetName);
|
||||
}
|
||||
}
|
||||
public static void appendFormat(StringBuffer out, String workbookName, String rawSheetName) {
|
||||
boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName);
|
||||
if(needsQuotes) {
|
||||
out.append(DELIMITER);
|
||||
out.append('[');
|
||||
appendAndEscape(out, workbookName.replace('[', '(').replace(']', ')'));
|
||||
out.append(']');
|
||||
appendAndEscape(out, rawSheetName);
|
||||
out.append(DELIMITER);
|
||||
} else {
|
||||
out.append('[');
|
||||
out.append(workbookName);
|
||||
out.append(']');
|
||||
out.append(rawSheetName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendAndEscape(StringBuffer sb, String rawSheetName) {
|
||||
int len = rawSheetName.length();
|
||||
@ -101,13 +117,27 @@ public final class SheetNameFormatter {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (nameLooksLikeBooleanLiteral(rawSheetName)) {
|
||||
return true;
|
||||
}
|
||||
// Error constant literals all contain '#' and other special characters
|
||||
// so they don't get this far
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean nameLooksLikeBooleanLiteral(String rawSheetName) {
|
||||
switch(rawSheetName.charAt(0)) {
|
||||
case 'T': case 't':
|
||||
return "TRUE".equalsIgnoreCase(rawSheetName);
|
||||
case 'F': case 'f':
|
||||
return "FALSE".equalsIgnoreCase(rawSheetName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if the presence of the specified character in a sheet name would
|
||||
* require the sheet name to be delimited in formulas. This includes every non-alphanumeric
|
||||
* character besides underscore '_'.
|
||||
* character besides underscore '_' and dot '.'.
|
||||
*/
|
||||
/* package */ static boolean isSpecialChar(char ch) {
|
||||
// note - Character.isJavaIdentifierPart() would allow dollars '$'
|
||||
@ -115,7 +145,8 @@ public final class SheetNameFormatter {
|
||||
return false;
|
||||
}
|
||||
switch(ch) {
|
||||
case '_': // underscore is ok
|
||||
case '.': // dot is OK
|
||||
case '_': // underscore is OK
|
||||
return false;
|
||||
case '\n':
|
||||
case '\r':
|
||||
|
@ -0,0 +1,71 @@
|
||||
/* ====================================================================
|
||||
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.hssf.record.formula.eval;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class RangeEval implements OperationEval {
|
||||
|
||||
public static final OperationEval instance = new RangeEval();
|
||||
|
||||
private RangeEval() {
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
if(args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
RefEval reA = evaluateRef(args[0]);
|
||||
RefEval reB = evaluateRef(args[1]);
|
||||
return resolveRange(reA, reB);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
private static AreaEval resolveRange(RefEval reA, RefEval reB) {
|
||||
|
||||
int height = reB.getRow() - reA.getRow();
|
||||
int width = reB.getColumn() - reA.getColumn();
|
||||
|
||||
return reA.offset(0, height, 0, width);
|
||||
}
|
||||
|
||||
private static RefEval evaluateRef(Eval arg) throws EvaluationException {
|
||||
if (arg instanceof RefEval) {
|
||||
return (RefEval) arg;
|
||||
}
|
||||
if (arg instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval)arg);
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected ref arg class (" + arg.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@ package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.NameRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -39,6 +41,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||
int sheetIndex = _uBook.getSheetIndex(sheetName);
|
||||
return _iBook.checkExternSheet(sheetIndex);
|
||||
}
|
||||
public int getExternalSheetIndex(String workbookName, String sheetName) {
|
||||
return _iBook.getExternalSheetIndex(workbookName, sheetName);
|
||||
}
|
||||
|
||||
public EvaluationName getName(int index) {
|
||||
return new Name(_iBook.getNameRecord(index), index);
|
||||
@ -57,6 +62,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||
public int getSheetIndex(Sheet sheet) {
|
||||
return _uBook.getSheetIndex(sheet);
|
||||
}
|
||||
public int getSheetIndex(String sheetName) {
|
||||
return _uBook.getSheetIndex(sheetName);
|
||||
}
|
||||
|
||||
public String getSheetName(int sheetIndex) {
|
||||
return _uBook.getSheetName(sheetIndex);
|
||||
@ -75,7 +83,11 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||
}
|
||||
public int convertFromExternSheetIndex(int externSheetIndex) {
|
||||
return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public ExternalSheet getExternalSheet(int externSheetIndex) {
|
||||
return _iBook.getExternalSheet(externSheetIndex);
|
||||
}
|
||||
|
||||
public HSSFWorkbook getWorkbook() {
|
||||
return _uBook;
|
||||
@ -96,8 +108,16 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||
return new Name(_iBook.getNameRecord(ix), ix);
|
||||
}
|
||||
public Ptg[] getFormulaTokens(Cell cell) {
|
||||
if (false) {
|
||||
// re-parsing the formula text also works, but is a waste of time
|
||||
// It is useful from time to time to run all unit tests with this code
|
||||
// to make sure that all formulas POI can evaluate can also be parsed.
|
||||
return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);
|
||||
}
|
||||
HSSFCell hCell = (HSSFCell) cell;
|
||||
FormulaRecord fr = ((FormulaRecordAggregate) hCell.getCellValueRecord()).getFormulaRecord();
|
||||
return fr.getParsedExpression();
|
||||
}
|
||||
|
||||
private static final class Name implements EvaluationName {
|
||||
|
||||
|
@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
|
||||
import org.apache.poi.ss.formula.WorkbookEvaluator;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
@ -57,6 +58,21 @@ public class HSSFFormulaEvaluator /* almost implements FormulaEvaluator */ {
|
||||
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinates several formula evaluators together so that formulas that involve external
|
||||
* references can be evaluated.
|
||||
* @param workbookNames the simple file names used to identify the workbooks in formulas
|
||||
* with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")
|
||||
* @param evaluators all evaluators for the full set of workbooks required by the formulas.
|
||||
*/
|
||||
public static void setupEnvironment(String[] workbookNames, HSSFFormulaEvaluator[] evaluators) {
|
||||
WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];
|
||||
for (int i = 0; i < wbEvals.length; i++) {
|
||||
wbEvals[i] = evaluators[i]._bookEvaluator;
|
||||
}
|
||||
CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
|
||||
|
@ -30,6 +30,7 @@ public final class AreaReference extends org.apache.poi.ss.util.AreaReference {
|
||||
|
||||
/**
|
||||
* Creates an area ref from a pair of Cell References.
|
||||
* Also normalises such that the top-left
|
||||
*/
|
||||
public AreaReference(CellReference topLeft, CellReference botRight) {
|
||||
super(topLeft, botRight);
|
||||
|
@ -18,22 +18,13 @@
|
||||
package org.apache.poi.hssf.util;
|
||||
|
||||
/**
|
||||
* Common convertion functions between Excel style A1, C27 style
|
||||
* Common conversion functions between Excel style A1, C27 style
|
||||
* cell references, and POI usermodel style row=0, column=0
|
||||
* style references.
|
||||
* @author Avik Sengupta
|
||||
* @author Dennis Doubleday (patch to seperateRowColumns())
|
||||
*/
|
||||
public final class CellReference extends org.apache.poi.ss.util.CellReference {
|
||||
/**
|
||||
* Used to classify identifiers found in formulas as cell references or not.
|
||||
*/
|
||||
public static final class NameType {
|
||||
public static final int CELL = 1;
|
||||
public static final int NAMED_RANGE = 2;
|
||||
public static final int BAD_CELL_OR_NAMED_RANGE = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an cell ref from a string representation. Sheet names containing special characters should be
|
||||
* delimited and escaped as per normal syntax rules for formulas.
|
||||
@ -45,9 +36,6 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
|
||||
public CellReference(int pRow, int pCol) {
|
||||
super(pRow, pCol, true, true);
|
||||
}
|
||||
public CellReference(int pRow, short pCol) {
|
||||
super(pRow, (int)pCol, true, true);
|
||||
}
|
||||
|
||||
public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
|
||||
super(null, pRow, pCol, pAbsRow, pAbsCol);
|
||||
@ -55,10 +43,6 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
|
||||
public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
|
||||
super(pSheetName, pRow, pCol, pAbsRow, pAbsCol);
|
||||
}
|
||||
|
||||
protected void appendCellReference(StringBuffer sb) {
|
||||
super.appendCellReference(sb);
|
||||
}
|
||||
protected static String convertNumToColString(int col) {
|
||||
return org.apache.poi.ss.util.CellReference.convertNumToColString(col);
|
||||
}
|
||||
|
@ -17,25 +17,32 @@
|
||||
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
|
||||
/**
|
||||
* Stores the parameters that identify the evaluation of one cell.<br/>
|
||||
*/
|
||||
final class CellLocation {
|
||||
public static final CellLocation[] EMPTY_ARRAY = { };
|
||||
|
||||
private final EvaluationWorkbook _book;
|
||||
private final int _sheetIndex;
|
||||
private final int _rowIndex;
|
||||
private final int _columnIndex;
|
||||
private final int _hashCode;
|
||||
|
||||
public CellLocation(int sheetIndex, int rowIndex, int columnIndex) {
|
||||
public CellLocation(EvaluationWorkbook book, int sheetIndex, int rowIndex, int columnIndex) {
|
||||
if (sheetIndex < 0) {
|
||||
throw new IllegalArgumentException("sheetIndex must not be negative");
|
||||
}
|
||||
_book = book;
|
||||
_sheetIndex = sheetIndex;
|
||||
_rowIndex = rowIndex;
|
||||
_columnIndex = columnIndex;
|
||||
_hashCode = sheetIndex + 17 * (rowIndex + 17 * columnIndex);
|
||||
_hashCode = System.identityHashCode(book) + sheetIndex + 17 * (rowIndex + 17 * columnIndex);
|
||||
}
|
||||
public Object getBook() {
|
||||
return _book;
|
||||
}
|
||||
public int getSheetIndex() {
|
||||
return _sheetIndex;
|
||||
@ -49,15 +56,18 @@ final class CellLocation {
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
CellLocation other = (CellLocation) obj;
|
||||
if (getSheetIndex() != other.getSheetIndex()) {
|
||||
return false;
|
||||
}
|
||||
if (getRowIndex() != other.getRowIndex()) {
|
||||
return false;
|
||||
}
|
||||
if (getColumnIndex() != other.getColumnIndex()) {
|
||||
return false;
|
||||
}
|
||||
if (getSheetIndex() != other.getSheetIndex()) {
|
||||
return false;
|
||||
}
|
||||
if (getBook() != other.getBook()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public int hashCode() {
|
||||
@ -68,7 +78,8 @@ final class CellLocation {
|
||||
* @return human readable string for debug purposes
|
||||
*/
|
||||
public String formatAsString() {
|
||||
return "ShIx=" + getSheetIndex() + " R=" + getRowIndex() + " C=" + getColumnIndex();
|
||||
CellReference cr = new CellReference(_rowIndex, _columnIndex, false, false);
|
||||
return "ShIx=" + getSheetIndex() + " " + cr.formatAsString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
@ -0,0 +1,155 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Manages a collection of {@link WorkbookEvaluator}s, in order to support evaluation of formulas
|
||||
* across spreadsheets.<p/>
|
||||
*
|
||||
* For POI internal use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class CollaboratingWorkbooksEnvironment {
|
||||
|
||||
public static final CollaboratingWorkbooksEnvironment EMPTY = new CollaboratingWorkbooksEnvironment();
|
||||
|
||||
private final Map _evaluatorsByName;
|
||||
private final WorkbookEvaluator[] _evaluators;
|
||||
|
||||
private boolean _unhooked;
|
||||
private CollaboratingWorkbooksEnvironment() {
|
||||
_evaluatorsByName = Collections.EMPTY_MAP;
|
||||
_evaluators = new WorkbookEvaluator[0];
|
||||
}
|
||||
public static void setup(String[] workbookNames, WorkbookEvaluator[] evaluators) {
|
||||
int nItems = workbookNames.length;
|
||||
if (evaluators.length != nItems) {
|
||||
throw new IllegalArgumentException("Number of workbook names is " + nItems
|
||||
+ " but number of evaluators is " + evaluators.length);
|
||||
}
|
||||
if (nItems < 1) {
|
||||
throw new IllegalArgumentException("Must provide at least one collaborating worbook");
|
||||
}
|
||||
new CollaboratingWorkbooksEnvironment(workbookNames, evaluators, nItems);
|
||||
}
|
||||
|
||||
private CollaboratingWorkbooksEnvironment(String[] workbookNames, WorkbookEvaluator[] evaluators, int nItems) {
|
||||
Map m = new HashMap(nItems * 3 / 2);
|
||||
IdentityHashMap uniqueEvals = new IdentityHashMap(nItems * 3 / 2);
|
||||
for(int i=0; i<nItems; i++) {
|
||||
String wbName = workbookNames[i];
|
||||
WorkbookEvaluator wbEval = evaluators[i];
|
||||
if (m.containsKey(wbName)) {
|
||||
throw new IllegalArgumentException("Duplicate workbook name '" + wbName + "'");
|
||||
}
|
||||
if (uniqueEvals.containsKey(wbEval)) {
|
||||
String msg = "Attempted to register same workbook under names '"
|
||||
+ uniqueEvals.get(wbEval) + "' and '" + wbName + "'";
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
uniqueEvals.put(wbEval, wbName);
|
||||
m.put(wbName, wbEval);
|
||||
}
|
||||
unhookOldEnvironments(evaluators);
|
||||
hookNewEnvironment(evaluators, this);
|
||||
_unhooked = false;
|
||||
_evaluators = evaluators;
|
||||
_evaluatorsByName = m;
|
||||
}
|
||||
|
||||
private static void hookNewEnvironment(WorkbookEvaluator[] evaluators, CollaboratingWorkbooksEnvironment env) {
|
||||
|
||||
// All evaluators will need to share the same cache.
|
||||
// but the cache takes an optional evaluation listener.
|
||||
int nItems = evaluators.length;
|
||||
IEvaluationListener evalListener = evaluators[0].getEvaluationListener();
|
||||
// make sure that all evaluators have the same listener
|
||||
for(int i=0; i<nItems; i++) {
|
||||
if(evalListener != evaluators[i].getEvaluationListener()) {
|
||||
// This would be very complex to support
|
||||
throw new RuntimeException("Workbook evaluators must all have the same evaluation listener");
|
||||
}
|
||||
}
|
||||
EvaluationCache cache = new EvaluationCache(evalListener);
|
||||
|
||||
for(int i=0; i<nItems; i++) {
|
||||
evaluators[i].attachToEnvironment(env, cache);
|
||||
}
|
||||
|
||||
}
|
||||
private void unhookOldEnvironments(WorkbookEvaluator[] evaluators) {
|
||||
Set oldEnvs = new HashSet();
|
||||
for(int i=0; i<evaluators.length; i++) {
|
||||
oldEnvs.add(evaluators[i].getEnvironment());
|
||||
}
|
||||
CollaboratingWorkbooksEnvironment[] oldCWEs = new CollaboratingWorkbooksEnvironment[oldEnvs.size()];
|
||||
oldEnvs.toArray(oldCWEs);
|
||||
for (int i = 0; i < oldCWEs.length; i++) {
|
||||
oldCWEs[i].unhook();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void unhook() {
|
||||
if (_evaluators.length < 1) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < _evaluators.length; i++) {
|
||||
_evaluators[i].detachFromEnvironment();
|
||||
}
|
||||
_unhooked = true;
|
||||
}
|
||||
|
||||
public WorkbookEvaluator getWorkbookEvaluator(String workbookName) {
|
||||
if (_unhooked) {
|
||||
throw new IllegalStateException("This environment has been unhooked");
|
||||
}
|
||||
WorkbookEvaluator result = (WorkbookEvaluator) _evaluatorsByName.get(workbookName);
|
||||
if (result == null) {
|
||||
StringBuffer sb = new StringBuffer(256);
|
||||
sb.append("Could not resolve external workbook name '").append(workbookName).append("'.");
|
||||
if (_evaluators.length < 1) {
|
||||
sb.append(" Workbook environment has not been set up.");
|
||||
} else {
|
||||
sb.append(" The following workbook names are valid: (");
|
||||
Iterator i = _evaluatorsByName.keySet().iterator();
|
||||
int count=0;
|
||||
while(i.hasNext()) {
|
||||
if (count++>0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append("'").append(i.next()).append("'");
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
throw new RuntimeException(sb.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -81,13 +81,7 @@ final class EvaluationCache {
|
||||
+ cellLoc.formatAsString());
|
||||
}
|
||||
}
|
||||
if (_evaluationListener == null) {
|
||||
// optimisation - don't bother sorting if there is no listener.
|
||||
} else {
|
||||
// for testing
|
||||
// make order of callbacks to listener more deterministic
|
||||
Arrays.sort(usedCells, CellLocationComparator);
|
||||
}
|
||||
sortCellLocationsForLogging(usedCells);
|
||||
CellCacheEntry entry = getEntry(cellLoc);
|
||||
CellLocation[] consumingFormulaCells = entry.getConsumingCells();
|
||||
CellLocation[] prevUsedCells = entry.getUsedCells();
|
||||
@ -110,6 +104,18 @@ final class EvaluationCache {
|
||||
recurseClearCachedFormulaResults(consumingFormulaCells, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sorts the supplied cellLocs so that the order of call-backs to the evaluation
|
||||
* listener is more deterministic
|
||||
*/
|
||||
private void sortCellLocationsForLogging(CellLocation[] cellLocs) {
|
||||
if (_evaluationListener == null) {
|
||||
// optimisation - don't bother sorting if there is no listener.
|
||||
} else {
|
||||
Arrays.sort(cellLocs, CellLocationComparator);
|
||||
}
|
||||
}
|
||||
|
||||
private void unlinkConsumingCells(CellLocation[] prevUsedCells, CellLocation[] usedCells,
|
||||
CellLocation cellLoc) {
|
||||
if (prevUsedCells == null) {
|
||||
@ -149,6 +155,7 @@ final class EvaluationCache {
|
||||
* @param formulaCells
|
||||
*/
|
||||
private void recurseClearCachedFormulaResults(CellLocation[] formulaCells, int depth) {
|
||||
sortCellLocationsForLogging(formulaCells);
|
||||
int nextDepth = depth+1;
|
||||
for (int i = 0; i < formulaCells.length; i++) {
|
||||
CellLocation fc = formulaCells[i];
|
||||
@ -196,6 +203,10 @@ final class EvaluationCache {
|
||||
CellLocation clB = (CellLocation) b;
|
||||
|
||||
int cmp;
|
||||
cmp = System.identityHashCode(clA.getBook()) - System.identityHashCode(clB.getBook());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
cmp = clA.getSheetIndex() - clB.getSheetIndex();
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
|
@ -31,12 +31,36 @@ import org.apache.poi.ss.usermodel.Sheet;
|
||||
*/
|
||||
public interface EvaluationWorkbook {
|
||||
String getSheetName(int sheetIndex);
|
||||
/**
|
||||
* @return -1 if the specified sheet is from a different book
|
||||
*/
|
||||
int getSheetIndex(Sheet sheet);
|
||||
int getSheetIndex(String sheetName);
|
||||
|
||||
Sheet getSheet(int sheetIndex);
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook
|
||||
*/
|
||||
ExternalSheet getExternalSheet(int externSheetIndex);
|
||||
int convertFromExternSheetIndex(int externSheetIndex);
|
||||
EvaluationName getName(NamePtg namePtg);
|
||||
String resolveNameXText(NameXPtg ptg);
|
||||
Ptg[] getFormulaTokens(Cell cell);
|
||||
|
||||
class ExternalSheet {
|
||||
private final String _workbookName;
|
||||
private final String _sheetName;
|
||||
|
||||
public ExternalSheet(String workbookName, String sheetName) {
|
||||
_workbookName = workbookName;
|
||||
_sheetName = sheetName;
|
||||
}
|
||||
public String getWorkbookName() {
|
||||
return _workbookName;
|
||||
}
|
||||
public String getSheetName() {
|
||||
return _sheetName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
/**
|
||||
* Should be implemented by any {@link Ptg} subclass that needs has an extern sheet index <br/>
|
||||
*
|
||||
* For POI internal use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface ExternSheetReferenceToken {
|
||||
int getExternSheetIndex();
|
||||
}
|
@ -49,6 +49,7 @@ import org.apache.poi.hssf.record.formula.ParenthesisPtg;
|
||||
import org.apache.poi.hssf.record.formula.PercentPtg;
|
||||
import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.RangePtg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
@ -60,7 +61,7 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||
import org.apache.poi.hssf.util.AreaReference;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.hssf.util.CellReference.NameType;
|
||||
import org.apache.poi.ss.util.CellReference.NameType;
|
||||
|
||||
/**
|
||||
* This class parses a formula string into a List of tokens in RPN order.
|
||||
@ -81,6 +82,33 @@ import org.apache.poi.hssf.util.CellReference.NameType;
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class FormulaParser {
|
||||
private static final class Identifier {
|
||||
private final String _name;
|
||||
private final boolean _isQuoted;
|
||||
|
||||
public Identifier(String name, boolean isQuoted) {
|
||||
_name = name;
|
||||
_isQuoted = isQuoted;
|
||||
}
|
||||
public String getName() {
|
||||
return _name;
|
||||
}
|
||||
public boolean isQuoted() {
|
||||
return _isQuoted;
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName());
|
||||
sb.append(" [");
|
||||
if (_isQuoted) {
|
||||
sb.append("'").append(_name).append("'");
|
||||
} else {
|
||||
sb.append(_name);
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific exception thrown when a supplied formula does not parse properly.<br/>
|
||||
@ -176,23 +204,23 @@ public final class FormulaParser {
|
||||
}
|
||||
|
||||
/** Recognize an Alpha Character */
|
||||
private boolean IsAlpha(char c) {
|
||||
private static boolean IsAlpha(char c) {
|
||||
return Character.isLetter(c) || c == '$' || c=='_';
|
||||
}
|
||||
|
||||
/** Recognize a Decimal Digit */
|
||||
private boolean IsDigit(char c) {
|
||||
private static boolean IsDigit(char c) {
|
||||
return Character.isDigit(c);
|
||||
}
|
||||
|
||||
/** Recognize an Alphanumeric */
|
||||
private boolean IsAlNum(char c) {
|
||||
return (IsAlpha(c) || IsDigit(c));
|
||||
private static boolean IsAlNum(char c) {
|
||||
return IsAlpha(c) || IsDigit(c);
|
||||
}
|
||||
|
||||
/** Recognize White Space */
|
||||
private boolean IsWhite( char c) {
|
||||
return (c ==' ' || c== TAB);
|
||||
private static boolean IsWhite( char c) {
|
||||
return c ==' ' || c== TAB;
|
||||
}
|
||||
|
||||
/** Skip Over Leading White Space */
|
||||
@ -213,7 +241,13 @@ public final class FormulaParser {
|
||||
}
|
||||
GetChar();
|
||||
}
|
||||
|
||||
private String parseUnquotedIdentifier() {
|
||||
Identifier iden = parseIdentifier();
|
||||
if (iden.isQuoted()) {
|
||||
throw expected("unquoted identifier");
|
||||
}
|
||||
return iden.getName();
|
||||
}
|
||||
/**
|
||||
* Parses a sheet name, named range name, or simple cell reference.<br/>
|
||||
* Note - identifiers in Excel can contain dots, so this method may return a String
|
||||
@ -221,18 +255,17 @@ public final class FormulaParser {
|
||||
* may return a value like "A1..B2", in which case the caller must convert it to
|
||||
* an area reference like "A1:B2"
|
||||
*/
|
||||
private String parseIdentifier() {
|
||||
StringBuffer Token = new StringBuffer();
|
||||
if (!IsAlpha(look) && look != '\'') {
|
||||
private Identifier parseIdentifier() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (!IsAlpha(look) && look != '\'' && look != '[') {
|
||||
throw expected("Name");
|
||||
}
|
||||
if(look == '\'')
|
||||
{
|
||||
boolean isQuoted = look == '\'';
|
||||
if(isQuoted) {
|
||||
Match('\'');
|
||||
boolean done = look == '\'';
|
||||
while(!done)
|
||||
{
|
||||
Token.append(look);
|
||||
while(!done) {
|
||||
sb.append(look);
|
||||
GetChar();
|
||||
if(look == '\'')
|
||||
{
|
||||
@ -240,17 +273,15 @@ public final class FormulaParser {
|
||||
done = look != '\'';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// allow for any sequence of dots and identifier chars
|
||||
// special case of two consecutive dots is best treated in the calling code
|
||||
while (IsAlNum(look) || look == '.') {
|
||||
Token.append(look);
|
||||
while (IsAlNum(look) || look == '.' || look == '[' || look == ']') {
|
||||
sb.append(look);
|
||||
GetChar();
|
||||
}
|
||||
}
|
||||
return Token.toString();
|
||||
return new Identifier(sb.toString(), isQuoted);
|
||||
}
|
||||
|
||||
/** Get a Number */
|
||||
@ -265,72 +296,112 @@ public final class FormulaParser {
|
||||
}
|
||||
|
||||
private ParseNode parseFunctionReferenceOrName() {
|
||||
String name = parseIdentifier();
|
||||
Identifier iden = parseIdentifier();
|
||||
if (look == '('){
|
||||
//This is a function
|
||||
return function(name);
|
||||
return function(iden.getName());
|
||||
}
|
||||
return new ParseNode(parseNameOrReference(name));
|
||||
if (!iden.isQuoted()) {
|
||||
String name = iden.getName();
|
||||
if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
|
||||
return new ParseNode(new BoolPtg(name.toUpperCase()));
|
||||
}
|
||||
}
|
||||
return parseRangeExpression(iden);
|
||||
}
|
||||
|
||||
private Ptg parseNameOrReference(String name) {
|
||||
private ParseNode parseRangeExpression(Identifier iden) {
|
||||
Ptg ptgA = parseNameOrCellRef(iden);
|
||||
if (look == ':') {
|
||||
GetChar();
|
||||
Identifier iden2 = parseIdentifier();
|
||||
Ptg ptgB = parseNameOrCellRef(iden2);
|
||||
Ptg simplified = reduceRangeExpression(ptgA, ptgB);
|
||||
|
||||
if (simplified == null) {
|
||||
ParseNode[] children = {
|
||||
new ParseNode(ptgA),
|
||||
new ParseNode(ptgB),
|
||||
};
|
||||
return new ParseNode(RangePtg.instance, children);
|
||||
}
|
||||
return new ParseNode(simplified);
|
||||
}
|
||||
return new ParseNode(ptgA);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* "A1", "B3" -> "A1:B3"
|
||||
* "sheet1!A1", "B3" -> "sheet1!A1:B3"
|
||||
*
|
||||
* @return <code>null</code> if the range expression cannot / shouldn't be reduced.
|
||||
*/
|
||||
private static Ptg reduceRangeExpression(Ptg ptgA, Ptg ptgB) {
|
||||
if (!(ptgB instanceof RefPtg)) {
|
||||
// only when second ref is simple 2-D ref can the range
|
||||
// expression be converted to an area ref
|
||||
return null;
|
||||
}
|
||||
RefPtg refB = (RefPtg) ptgB;
|
||||
|
||||
if (ptgA instanceof RefPtg) {
|
||||
RefPtg refA = (RefPtg) ptgA;
|
||||
return new AreaPtg(refA.getRow(), refB.getRow(), refA.getColumn(), refB.getColumn(),
|
||||
refA.isRowRelative(), refB.isRowRelative(), refA.isColRelative(), refB.isColRelative());
|
||||
}
|
||||
if (ptgA instanceof Ref3DPtg) {
|
||||
Ref3DPtg refA = (Ref3DPtg) ptgA;
|
||||
return new Area3DPtg(refA.getRow(), refB.getRow(), refA.getColumn(), refB.getColumn(),
|
||||
refA.isRowRelative(), refB.isRowRelative(), refA.isColRelative(), refB.isColRelative(),
|
||||
refA.getExternSheetIndex());
|
||||
}
|
||||
// Note - other operand types (like AreaPtg) which probably can't evaluate
|
||||
// do not cause validation errors at parse time
|
||||
return null;
|
||||
}
|
||||
|
||||
private Ptg parseNameOrCellRef(Identifier iden) {
|
||||
|
||||
if (look == '!') {
|
||||
GetChar();
|
||||
// 3-D ref
|
||||
// this code assumes iden is a sheetName
|
||||
// TODO - handle <book name> ! <named range name>
|
||||
int externIdx = getExternalSheetIndex(iden.getName());
|
||||
String secondIden = parseUnquotedIdentifier();
|
||||
AreaReference areaRef = parseArea(secondIden);
|
||||
if (areaRef == null) {
|
||||
return new Ref3DPtg(secondIden, externIdx);
|
||||
}
|
||||
// will happen if dots are used instead of colon
|
||||
return new Area3DPtg(areaRef.formatAsString(), externIdx);
|
||||
}
|
||||
|
||||
String name = iden.getName();
|
||||
AreaReference areaRef = parseArea(name);
|
||||
if (areaRef != null) {
|
||||
// will happen if dots are used instead of colon
|
||||
return new AreaPtg(areaRef.formatAsString());
|
||||
}
|
||||
|
||||
if (look == ':' || look == '.') { // this is a AreaReference
|
||||
GetChar();
|
||||
|
||||
while (look == '.') { // formulas can have . or .. or ... instead of :
|
||||
GetChar();
|
||||
}
|
||||
|
||||
String first = name;
|
||||
String second = parseIdentifier();
|
||||
return new AreaPtg(first+":"+second);
|
||||
}
|
||||
|
||||
if (look == '!') {
|
||||
Match('!');
|
||||
String sheetName = name;
|
||||
String first = parseIdentifier();
|
||||
int externIdx = book.getExternalSheetIndex(sheetName);
|
||||
areaRef = parseArea(name);
|
||||
if (areaRef != null) {
|
||||
// will happen if dots are used instead of colon
|
||||
return new Area3DPtg(areaRef.formatAsString(), externIdx);
|
||||
}
|
||||
if (look == ':') {
|
||||
Match(':');
|
||||
String second=parseIdentifier();
|
||||
if (look == '!') {
|
||||
//The sheet name was included in both of the areas. Only really
|
||||
//need it once
|
||||
Match('!');
|
||||
String third=parseIdentifier();
|
||||
|
||||
if (!sheetName.equals(second))
|
||||
throw new RuntimeException("Unhandled double sheet reference.");
|
||||
|
||||
return new Area3DPtg(first+":"+third,externIdx);
|
||||
}
|
||||
return new Area3DPtg(first+":"+second,externIdx);
|
||||
}
|
||||
return new Ref3DPtg(first, externIdx);
|
||||
}
|
||||
if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
|
||||
return new BoolPtg(name.toUpperCase());
|
||||
}
|
||||
|
||||
// This can be either a cell ref or a named range
|
||||
// Try to spot which it is
|
||||
|
||||
|
||||
int nameType = CellReference.classifyCellReference(name);
|
||||
if (nameType == NameType.CELL) {
|
||||
return new RefPtg(name);
|
||||
}
|
||||
if (look == ':') {
|
||||
if (nameType == NameType.COLUMN) {
|
||||
GetChar();
|
||||
String secondIden = parseUnquotedIdentifier();
|
||||
if (CellReference.classifyCellReference(secondIden) != NameType.COLUMN) {
|
||||
throw new FormulaParseException("Expected full column after '" + name
|
||||
+ ":' but got '" + secondIden + "'");
|
||||
}
|
||||
return new AreaPtg(name + ":" + secondIden);
|
||||
}
|
||||
}
|
||||
if (nameType != NameType.NAMED_RANGE) {
|
||||
new FormulaParseException("Name '" + name
|
||||
+ "' does not look like a cell reference or named range");
|
||||
@ -347,6 +418,17 @@ public final class FormulaParser {
|
||||
+ name + "' is not a range as expected");
|
||||
}
|
||||
|
||||
private int getExternalSheetIndex(String name) {
|
||||
if (name.charAt(0) == '[') {
|
||||
// we have a sheet name qualified with workbook name e.g. '[MyData.xls]Sheet1'
|
||||
int pos = name.lastIndexOf(']'); // safe because sheet names never have ']'
|
||||
String wbName = name.substring(1, pos);
|
||||
String sheetName = name.substring(pos+1);
|
||||
return book.getExternalSheetIndex(wbName, sheetName);
|
||||
}
|
||||
return book.getExternalSheetIndex(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name an 'identifier' like string (i.e. contains alphanums, and dots)
|
||||
* @return <code>null</code> if name cannot be split at a dot
|
||||
@ -585,7 +667,7 @@ public final class FormulaParser {
|
||||
Match('}');
|
||||
return arrayNode;
|
||||
}
|
||||
if (IsAlpha(look) || look == '\''){
|
||||
if (IsAlpha(look) || look == '\'' || look == '['){
|
||||
return parseFunctionReferenceOrName();
|
||||
}
|
||||
// else - assume number
|
||||
@ -662,7 +744,7 @@ public final class FormulaParser {
|
||||
}
|
||||
|
||||
private Boolean parseBooleanLiteral() {
|
||||
String iden = parseIdentifier();
|
||||
String iden = parseUnquotedIdentifier();
|
||||
if ("TRUE".equalsIgnoreCase(iden)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
@ -720,7 +802,7 @@ public final class FormulaParser {
|
||||
|
||||
private int parseErrorLiteral() {
|
||||
Match('#');
|
||||
String part1 = parseIdentifier().toUpperCase();
|
||||
String part1 = parseUnquotedIdentifier().toUpperCase();
|
||||
|
||||
switch(part1.charAt(0)) {
|
||||
case 'V':
|
||||
|
@ -32,6 +32,16 @@ public interface FormulaParsingWorkbook {
|
||||
*/
|
||||
EvaluationName getName(String name);
|
||||
|
||||
int getExternalSheetIndex(String sheetName);
|
||||
NameXPtg getNameXPtg(String name);
|
||||
|
||||
/**
|
||||
* gets the externSheet index for a sheet from this workbook
|
||||
*/
|
||||
int getExternalSheetIndex(String sheetName);
|
||||
/**
|
||||
* gets the externSheet index for a sheet from an external workbook
|
||||
* @param workbookName e.g. "Budget.xls"
|
||||
* @param sheetName a name of a sheet in that workbook
|
||||
*/
|
||||
int getExternalSheetIndex(String workbookName, String sheetName);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||
|
||||
/**
|
||||
* Abstracts a workbook for the purpose of converting formula to text.<br/>
|
||||
@ -29,6 +30,10 @@ import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
*/
|
||||
public interface FormulaRenderingWorkbook {
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook
|
||||
*/
|
||||
ExternalSheet getExternalSheet(int externSheetIndex);
|
||||
String getSheetNameByExternSheet(int externSheetIndex);
|
||||
String resolveNameXText(NameXPtg nameXPtg);
|
||||
String getNameText(NamePtg namePtg);
|
||||
|
@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.ControlPtg;
|
||||
import org.apache.poi.hssf.record.formula.RangePtg;
|
||||
import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
||||
@ -115,6 +116,10 @@ final class OperandClassTransformer {
|
||||
return;
|
||||
}
|
||||
if (children.length > 0) {
|
||||
if (token == RangePtg.instance) {
|
||||
// TODO is any token transformation required under the various ref operators?
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Node should not have any children");
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||
import org.apache.poi.hssf.record.formula.PercentPtg;
|
||||
import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.RangePtg;
|
||||
import org.apache.poi.hssf.record.formula.SubtractPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
|
||||
@ -57,6 +58,7 @@ import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperationEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.PercentEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.PowerEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RangeEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.SubtractEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
|
||||
@ -101,6 +103,7 @@ final class OperationEvaluatorFactory {
|
||||
add(m, SubtractPtg.class, SubtractEval.instance);
|
||||
add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
|
||||
add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
|
||||
add(m, RangePtg.class, RangeEval.instance);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,18 @@ import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Area3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.BoolPtg;
|
||||
import org.apache.poi.hssf.record.formula.ControlPtg;
|
||||
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.DeletedRef3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.ErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.MemErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.MemFuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.MissingArgPtg;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
@ -35,6 +41,7 @@ import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefErrorPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnionPtg;
|
||||
@ -53,6 +60,7 @@ import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
@ -68,13 +76,14 @@ import org.apache.poi.ss.usermodel.Sheet;
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class WorkbookEvaluator {
|
||||
public final class WorkbookEvaluator {
|
||||
|
||||
private final EvaluationWorkbook _workbook;
|
||||
private final EvaluationCache _cache;
|
||||
private EvaluationCache _cache;
|
||||
|
||||
private final IEvaluationListener _evaluationListener;
|
||||
private final Map _sheetIndexesBySheet;
|
||||
private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
|
||||
|
||||
public WorkbookEvaluator(EvaluationWorkbook workbook) {
|
||||
this (workbook, null);
|
||||
@ -84,6 +93,7 @@ public class WorkbookEvaluator {
|
||||
_evaluationListener = evaluationListener;
|
||||
_cache = new EvaluationCache(evaluationListener);
|
||||
_sheetIndexesBySheet = new IdentityHashMap();
|
||||
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,6 +111,21 @@ public class WorkbookEvaluator {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
/* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache) {
|
||||
_collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
|
||||
_cache = cache;
|
||||
}
|
||||
/* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
|
||||
return _collaboratingWorkbookEnvironment;
|
||||
}
|
||||
|
||||
/* package */ void detachFromEnvironment() {
|
||||
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
|
||||
_cache = new EvaluationCache(_evaluationListener);
|
||||
}
|
||||
/* package */ IEvaluationListener getEvaluationListener() {
|
||||
return _evaluationListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called whenever there are changes to input cells in the evaluated workbook.
|
||||
@ -123,7 +148,7 @@ public class WorkbookEvaluator {
|
||||
throw new IllegalArgumentException("value must not be null");
|
||||
}
|
||||
int sheetIndex = getSheetIndex(sheet);
|
||||
_cache.setValue(new CellLocation(sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
|
||||
_cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
|
||||
|
||||
}
|
||||
/**
|
||||
@ -132,13 +157,17 @@ public class WorkbookEvaluator {
|
||||
*/
|
||||
public void notifySetFormula(Sheet sheet, int rowIndex, int columnIndex) {
|
||||
int sheetIndex = getSheetIndex(sheet);
|
||||
_cache.setValue(new CellLocation(sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
|
||||
_cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
|
||||
|
||||
}
|
||||
private int getSheetIndex(Sheet sheet) {
|
||||
Integer result = (Integer) _sheetIndexesBySheet.get(sheet);
|
||||
if (result == null) {
|
||||
result = new Integer(_workbook.getSheetIndex(sheet));
|
||||
int sheetIndex = _workbook.getSheetIndex(sheet);
|
||||
if (sheetIndex < 0) {
|
||||
throw new RuntimeException("Specified sheet from a different book");
|
||||
}
|
||||
result = new Integer(sheetIndex);
|
||||
_sheetIndexesBySheet.put(sheet, result);
|
||||
}
|
||||
return result.intValue();
|
||||
@ -146,7 +175,7 @@ public class WorkbookEvaluator {
|
||||
|
||||
public ValueEval evaluate(Cell srcCell) {
|
||||
int sheetIndex = getSheetIndex(srcCell.getSheet());
|
||||
CellLocation cellLoc = new CellLocation(sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
|
||||
CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
|
||||
return internalEvaluate(srcCell, cellLoc, new EvaluationTracker(_cache));
|
||||
}
|
||||
|
||||
@ -181,10 +210,10 @@ public class WorkbookEvaluator {
|
||||
isPlainFormulaCell = false;
|
||||
Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
|
||||
if(evalListener == null) {
|
||||
result = evaluateCell(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
|
||||
result = evaluateFormula(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
|
||||
} else {
|
||||
evalListener.onStartEvaluate(sheetIndex, rowIndex, columnIndex, ptgs);
|
||||
result = evaluateCell(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
|
||||
result = evaluateFormula(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
|
||||
evalListener.onEndEvaluate(sheetIndex, rowIndex, columnIndex, result);
|
||||
}
|
||||
}
|
||||
@ -225,17 +254,31 @@ public class WorkbookEvaluator {
|
||||
}
|
||||
throw new RuntimeException("Unexpected cell type (" + cellType + ")");
|
||||
}
|
||||
private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
|
||||
// visibility raised for testing
|
||||
/* package */ ValueEval evaluateFormula(int sheetIndex, int srcRowNum, int srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
|
||||
|
||||
Stack stack = new Stack();
|
||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||
|
||||
// since we don't know how to handle these yet :(
|
||||
Ptg ptg = ptgs[i];
|
||||
if (ptg instanceof AttrPtg) {
|
||||
AttrPtg attrPtg = (AttrPtg) ptg;
|
||||
if (attrPtg.isSum()) {
|
||||
// Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator
|
||||
// expects the equivalent function token
|
||||
byte nArgs = 1; // tAttrSum always has 1 parameter
|
||||
ptg = new FuncVarPtg("SUM", nArgs);
|
||||
}
|
||||
}
|
||||
if (ptg instanceof ControlPtg) {
|
||||
// skip Parentheses, Attr, etc
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof MemFuncPtg) {
|
||||
// can ignore, rest of tokens for this expression are in OK RPN order
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof MemErrPtg) { continue; }
|
||||
if (ptg instanceof MissingArgPtg) {
|
||||
// TODO - might need to push BlankEval or MissingArgEval
|
||||
@ -289,7 +332,7 @@ public class WorkbookEvaluator {
|
||||
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
|
||||
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
|
||||
*/
|
||||
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
|
||||
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
|
||||
if (evaluationResult instanceof RefEval) {
|
||||
RefEval rv = (RefEval) evaluationResult;
|
||||
return rv.getInnerValueEval();
|
||||
@ -321,6 +364,20 @@ public class WorkbookEvaluator {
|
||||
}
|
||||
return operation.evaluate(ops, srcRowNum, (short)srcColNum);
|
||||
}
|
||||
private SheetRefEvaluator createExternSheetRefEvaluator(EvaluationTracker tracker,
|
||||
ExternSheetReferenceToken ptg) {
|
||||
int externSheetIndex = ptg.getExternSheetIndex();
|
||||
ExternalSheet externalSheet = _workbook.getExternalSheet(externSheetIndex);
|
||||
if (externalSheet != null) {
|
||||
WorkbookEvaluator otherEvaluator = _collaboratingWorkbookEnvironment.getWorkbookEvaluator(externalSheet.getWorkbookName());
|
||||
EvaluationWorkbook otherBook = otherEvaluator._workbook;
|
||||
int otherSheetIndex = otherBook.getSheetIndex(externalSheet.getSheetName());
|
||||
return new SheetRefEvaluator(otherEvaluator, tracker, otherBook, otherSheetIndex);
|
||||
}
|
||||
int otherSheetIndex = _workbook.convertFromExternSheetIndex(externSheetIndex);
|
||||
return new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
|
||||
@ -329,6 +386,8 @@ public class WorkbookEvaluator {
|
||||
* passed here!
|
||||
*/
|
||||
private Eval getEvalForPtg(Ptg ptg, int sheetIndex, EvaluationTracker tracker) {
|
||||
// consider converting all these (ptg instanceof XxxPtg) expressions to (ptg.getClass() == XxxPtg.class)
|
||||
|
||||
if (ptg instanceof NamePtg) {
|
||||
// named ranges, macro functions
|
||||
NamePtg namePtg = (NamePtg) ptg;
|
||||
@ -361,16 +420,18 @@ public class WorkbookEvaluator {
|
||||
if (ptg instanceof ErrPtg) {
|
||||
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
|
||||
}
|
||||
if (ptg instanceof AreaErrPtg ||ptg instanceof RefErrorPtg
|
||||
|| ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) {
|
||||
return ErrorEval.REF_INVALID;
|
||||
}
|
||||
if (ptg instanceof Ref3DPtg) {
|
||||
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
||||
int otherSheetIndex = _workbook.convertFromExternSheetIndex(refPtg.getExternSheetIndex());
|
||||
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
|
||||
SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, refPtg);
|
||||
return new LazyRefEval(refPtg, sre);
|
||||
}
|
||||
if (ptg instanceof Area3DPtg) {
|
||||
Area3DPtg aptg = (Area3DPtg) ptg;
|
||||
int otherSheetIndex = _workbook.convertFromExternSheetIndex(aptg.getExternSheetIndex());
|
||||
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
|
||||
SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, aptg);
|
||||
return new LazyAreaEval(aptg, sre);
|
||||
}
|
||||
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, sheetIndex);
|
||||
@ -410,7 +471,7 @@ public class WorkbookEvaluator {
|
||||
} else {
|
||||
cell = row.getCell(columnIndex);
|
||||
}
|
||||
CellLocation cellLoc = new CellLocation(sheetIndex, rowIndex, columnIndex);
|
||||
CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex);
|
||||
tracker.acceptDependency(cellLoc);
|
||||
return internalEvaluate(cell, cellLoc, tracker);
|
||||
}
|
||||
|
@ -103,8 +103,45 @@ public class AreaReference {
|
||||
* Creates an area ref from a pair of Cell References.
|
||||
*/
|
||||
public AreaReference(CellReference topLeft, CellReference botRight) {
|
||||
boolean swapRows = topLeft.getRow() > botRight.getRow();
|
||||
boolean swapCols = topLeft.getCol() > botRight.getCol();
|
||||
if (swapRows || swapCols) {
|
||||
int firstRow;
|
||||
int lastRow;
|
||||
int firstColumn;
|
||||
int lastColumn;
|
||||
boolean firstRowAbs;
|
||||
boolean lastRowAbs;
|
||||
boolean firstColAbs;
|
||||
boolean lastColAbs;
|
||||
if (swapRows) {
|
||||
firstRow = botRight.getRow();
|
||||
firstRowAbs = botRight.isRowAbsolute();
|
||||
lastRow = topLeft.getRow();
|
||||
lastRowAbs = topLeft.isRowAbsolute();
|
||||
} else {
|
||||
firstRow = topLeft.getRow();
|
||||
firstRowAbs = topLeft.isRowAbsolute();
|
||||
lastRow = botRight.getRow();
|
||||
lastRowAbs = botRight.isRowAbsolute();
|
||||
}
|
||||
if (swapCols) {
|
||||
firstColumn = botRight.getCol();
|
||||
firstColAbs = botRight.isColAbsolute();
|
||||
lastColumn = topLeft.getCol();
|
||||
lastColAbs = topLeft.isColAbsolute();
|
||||
} else {
|
||||
firstColumn = topLeft.getCol();
|
||||
firstColAbs = topLeft.isColAbsolute();
|
||||
lastColumn = botRight.getCol();
|
||||
lastColAbs = botRight.isColAbsolute();
|
||||
}
|
||||
_firstCell = new CellReference(firstRow, firstColumn, firstRowAbs, firstColAbs);
|
||||
_lastCell = new CellReference(lastRow, lastColumn, lastRowAbs, lastColAbs);
|
||||
} else {
|
||||
_firstCell = topLeft;
|
||||
_lastCell = botRight;
|
||||
}
|
||||
_isSingleCell = false;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ public class CellReference {
|
||||
public static final class NameType {
|
||||
public static final int CELL = 1;
|
||||
public static final int NAMED_RANGE = 2;
|
||||
public static final int COLUMN = 3;
|
||||
public static final int BAD_CELL_OR_NAMED_RANGE = -1;
|
||||
}
|
||||
|
||||
@ -45,10 +46,16 @@ public class CellReference {
|
||||
private static final char SPECIAL_NAME_DELIMITER = '\'';
|
||||
|
||||
/**
|
||||
* Matches a run of letters followed by a run of digits. The run of letters is group 1 and the
|
||||
* run of digits is group 2. Each group may optionally be prefixed with a single '$'.
|
||||
* Matches a run of one or more letters followed by a run of one or more digits.
|
||||
* The run of letters is group 1 and the run of digits is group 2.
|
||||
* Each group may optionally be prefixed with a single '$'.
|
||||
*/
|
||||
private static final Pattern CELL_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)\\$?([0-9]+)");
|
||||
/**
|
||||
* Matches a run of one or more letters. The run of letters is group 1.
|
||||
* The text may optionally be prefixed with a single '$'.
|
||||
*/
|
||||
private static final Pattern COLUMN_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)");
|
||||
/**
|
||||
* Named range names must start with a letter or underscore. Subsequent characters may include
|
||||
* digits or dot. (They can even end in dot).
|
||||
@ -94,10 +101,10 @@ public class CellReference {
|
||||
}
|
||||
|
||||
public CellReference(int pRow, int pCol) {
|
||||
this(pRow, pCol & 0xFFFF, false, false);
|
||||
this(pRow, pCol, false, false);
|
||||
}
|
||||
public CellReference(int pRow, short pCol) {
|
||||
this(pRow, (int)pCol, false, false);
|
||||
this(pRow, pCol & 0xFFFF, false, false);
|
||||
}
|
||||
|
||||
public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
|
||||
@ -134,7 +141,6 @@ public class CellReference {
|
||||
public static boolean isPartAbsolute(String part) {
|
||||
return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER;
|
||||
}
|
||||
|
||||
/**
|
||||
* takes in a column reference portion of a CellRef and converts it from
|
||||
* ALPHA-26 number format to 0-based base 10.
|
||||
@ -144,7 +150,8 @@ public class CellReference {
|
||||
* 'IV' -> 255
|
||||
* @return zero based column index
|
||||
*/
|
||||
protected static int convertColStringToIndex(String ref) {
|
||||
public static int convertColStringToIndex(String ref) {
|
||||
|
||||
int pos = 0;
|
||||
int retval=0;
|
||||
for (int k = ref.length()-1; k >= 0; k--) {
|
||||
@ -203,7 +210,7 @@ public class CellReference {
|
||||
// named range name
|
||||
// This behaviour is a little weird. For example, "IW123" is a valid named range name
|
||||
// because the column "IW" is beyond the maximum "IV". Note - this behaviour is version
|
||||
// dependent. In Excel 2007, "IW123" is not a valid named range name.
|
||||
// dependent. In BIFF12, "IW123" is not a valid named range name, but in BIFF8 it is.
|
||||
if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) {
|
||||
// Of course, named range names cannot have '$'
|
||||
return NameType.BAD_CELL_OR_NAMED_RANGE;
|
||||
@ -212,11 +219,17 @@ public class CellReference {
|
||||
}
|
||||
|
||||
private static int validateNamedRangeName(String str) {
|
||||
Matcher colMatcher = COLUMN_REF_PATTERN.matcher(str);
|
||||
if (colMatcher.matches()) {
|
||||
String colStr = colMatcher.group(1);
|
||||
if (isColumnWithnRange(colStr)) {
|
||||
return NameType.COLUMN;
|
||||
}
|
||||
}
|
||||
if (!NAMED_RANGE_NAME_PATTERN.matcher(str).matches()) {
|
||||
return NameType.BAD_CELL_OR_NAMED_RANGE;
|
||||
}
|
||||
return NameType.NAMED_RANGE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -257,23 +270,13 @@ public class CellReference {
|
||||
* @return <code>true</code> if the row and col parameters are within range of a BIFF8 spreadsheet.
|
||||
*/
|
||||
public static boolean cellReferenceIsWithinRange(String colStr, String rowStr) {
|
||||
int numberOfLetters = colStr.length();
|
||||
if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
|
||||
// "Sheet1" case etc
|
||||
return false; // that was easy
|
||||
if (!isColumnWithnRange(colStr)) {
|
||||
return false;
|
||||
}
|
||||
int nDigits = rowStr.length();
|
||||
if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) {
|
||||
return false;
|
||||
}
|
||||
if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
|
||||
if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// apparent column name has less chars than max
|
||||
// no need to check range
|
||||
}
|
||||
|
||||
if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) {
|
||||
// ASCII comparison is valid if digit count is same
|
||||
@ -288,6 +291,23 @@ public class CellReference {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isColumnWithnRange(String colStr) {
|
||||
int numberOfLetters = colStr.length();
|
||||
if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
|
||||
// "Sheet1" case etc
|
||||
return false; // that was easy
|
||||
}
|
||||
if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
|
||||
if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// apparent column name has less chars than max
|
||||
// no need to check range
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Separates the row from the columns and returns an array of three Strings. The first element
|
||||
* is the sheet name. Only the first element may be null. The second element in is the column
|
||||
@ -437,7 +457,7 @@ public class CellReference {
|
||||
* Appends cell reference with '$' markers for absolute values as required.
|
||||
* Sheet name is not included.
|
||||
*/
|
||||
protected void appendCellReference(StringBuffer sb) {
|
||||
/* package */ void appendCellReference(StringBuffer sb) {
|
||||
if(_isColAbs) {
|
||||
sb.append(ABSOLUTE_REFERENCE_MARKER);
|
||||
}
|
||||
|
@ -57,10 +57,6 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||
return convertToExternalSheetIndex(sheetIndex);
|
||||
}
|
||||
|
||||
public EvaluationName getName(int index) {
|
||||
return new Name(_uBook.getNameAt(index), index, this);
|
||||
}
|
||||
|
||||
public EvaluationName getName(String name) {
|
||||
for(int i=0; i < _uBook.getNumberOfNames(); i++) {
|
||||
String nameText = _uBook.getNameName(i);
|
||||
@ -88,14 +84,15 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||
return _uBook.getSheetAt(sheetIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Doesn't do anything - returns the same index
|
||||
* TODO - figure out if this is a ole2 specific thing, or
|
||||
* if we need to do something proper here too!
|
||||
*/
|
||||
public Sheet getSheetByExternSheetIndex(int externSheetIndex) {
|
||||
int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);
|
||||
return _uBook.getSheetAt(sheetIndex);
|
||||
public ExternalSheet getExternalSheet(int externSheetIndex) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
public int getExternalSheetIndex(String workbookName, String sheetName) {
|
||||
throw new RuntimeException("not implemented yet");
|
||||
}
|
||||
public int getSheetIndex(String sheetName) {
|
||||
return _uBook.getSheetIndex(sheetName);
|
||||
}
|
||||
|
||||
public Workbook getWorkbook() {
|
||||
|
@ -28,7 +28,7 @@ import org.apache.poi.hssf.model.AllModelTests;
|
||||
import org.apache.poi.hssf.record.AllRecordTests;
|
||||
import org.apache.poi.hssf.usermodel.AllUserModelTests;
|
||||
import org.apache.poi.hssf.util.AllHSSFUtilTests;
|
||||
import org.apache.poi.ss.formula.TestEvaluationCache;
|
||||
import org.apache.poi.ss.formula.AllSSFormulaTests;
|
||||
|
||||
/**
|
||||
* Test Suite for all sub-packages of org.apache.poi.hssf<br/>
|
||||
@ -53,7 +53,7 @@ public final class HSSFTests {
|
||||
}
|
||||
suite.addTest(new TestSuite(TestEventRecordFactory.class));
|
||||
suite.addTest(new TestSuite(TestModelFactory.class));
|
||||
suite.addTest(new TestSuite(TestEvaluationCache.class));
|
||||
suite.addTest(AllSSFormulaTests.suite());
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
BIN
src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls
Normal file
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls
Normal file
Binary file not shown.
@ -43,11 +43,13 @@ import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.PercentPtg;
|
||||
import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
import org.apache.poi.hssf.record.formula.SubtractPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
|
||||
import org.apache.poi.hssf.usermodel.FormulaExtractor;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
||||
@ -880,6 +882,66 @@ public final class TestFormulaParser extends TestCase {
|
||||
Object[][] values = aptg.getTokenArrayValues();
|
||||
assertEquals(ErrorConstant.valueOf(HSSFErrorConstants.ERROR_REF), values[0][3]);
|
||||
assertEquals(Boolean.FALSE, values[1][0]);
|
||||
|
||||
}
|
||||
|
||||
public void testRangeOperator() {
|
||||
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet sheet = wb.createSheet();
|
||||
HSSFCell cell = sheet.createRow(0).createCell(0);
|
||||
|
||||
wb.setSheetName(0, "Sheet1");
|
||||
cell.setCellFormula("Sheet1!B$4:Sheet1!$C1"); // explicit range ':' operator
|
||||
assertEquals("Sheet1!B$4:Sheet1!$C1", cell.getCellFormula());
|
||||
|
||||
cell.setCellFormula("Sheet1!B$4:$C1"); // plain area ref
|
||||
assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); // note - area ref is normalised
|
||||
|
||||
cell.setCellFormula("Sheet1!$C1...B$4"); // different syntax for plain area ref
|
||||
assertEquals("Sheet1!B1:$C$4", cell.getCellFormula());
|
||||
|
||||
// with funny sheet name
|
||||
wb.setSheetName(0, "A1...A2");
|
||||
cell.setCellFormula("A1...A2!B1");
|
||||
assertEquals("A1...A2!B1", cell.getCellFormula());
|
||||
}
|
||||
|
||||
public void testBooleanNamedSheet() {
|
||||
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet sheet = wb.createSheet("true");
|
||||
HSSFCell cell = sheet.createRow(0).createCell(0);
|
||||
cell.setCellFormula("'true'!B2");
|
||||
|
||||
assertEquals("'true'!B2", cell.getCellFormula());
|
||||
}
|
||||
|
||||
public void testParseExternalWorkbookReference() {
|
||||
HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls");
|
||||
HSSFCell cell = wbA.getSheetAt(0).getRow(0).getCell(0);
|
||||
|
||||
// make sure formula in sample is as expected
|
||||
assertEquals("[multibookFormulaB.xls]BSheet1!B1", cell.getCellFormula());
|
||||
Ptg[] expectedPtgs = FormulaExtractor.getPtgs(cell);
|
||||
confirmSingle3DRef(expectedPtgs, 1);
|
||||
|
||||
// now try (re-)parsing the formula
|
||||
Ptg[] actualPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]BSheet1!B1", wbA);
|
||||
confirmSingle3DRef(actualPtgs, 1); // externalSheetIndex 1 -> BSheet1
|
||||
|
||||
// try parsing a formula pointing to a different external sheet
|
||||
Ptg[] otherPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]AnotherSheet!B1", wbA);
|
||||
confirmSingle3DRef(otherPtgs, 0); // externalSheetIndex 0 -> AnotherSheet
|
||||
|
||||
// try setting the same formula in a cell
|
||||
cell.setCellFormula("[multibookFormulaB.xls]AnotherSheet!B1");
|
||||
assertEquals("[multibookFormulaB.xls]AnotherSheet!B1", cell.getCellFormula());
|
||||
}
|
||||
private static void confirmSingle3DRef(Ptg[] ptgs, int expectedExternSheetIndex) {
|
||||
assertEquals(1, ptgs.length);
|
||||
Ptg ptg0 = ptgs[0];
|
||||
assertEquals(Ref3DPtg.class, ptg0.getClass());
|
||||
assertEquals(expectedExternSheetIndex, ((Ref3DPtg)ptg0).getExternSheetIndex());
|
||||
}
|
||||
|
||||
}
|
@ -78,7 +78,7 @@ public final class TestSupBookRecord extends TestCase {
|
||||
|
||||
assertEquals( 34, record.getRecordSize() ); //sid+size+data
|
||||
|
||||
assertEquals("testURL", record.getURL().getString());
|
||||
assertEquals("testURL", record.getURL());
|
||||
UnicodeString[] sheetNames = record.getSheetNames();
|
||||
assertEquals(2, sheetNames.length);
|
||||
assertEquals("Sheet1", sheetNames[0].getString());
|
||||
|
@ -20,16 +20,12 @@ package org.apache.poi.hssf.record.formula;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for SheetNameFormatter
|
||||
* Tests for {@link SheetNameFormatter}
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestSheetNameFormatter extends TestCase {
|
||||
|
||||
public TestSheetNameFormatter(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
private static void confirmFormat(String rawSheetName, String expectedSheetNameEncoding) {
|
||||
assertEquals(expectedSheetNameEncoding, SheetNameFormatter.format(rawSheetName));
|
||||
}
|
||||
@ -55,6 +51,16 @@ public final class TestSheetNameFormatter extends TestCase {
|
||||
confirmFormat("TAXRETURN19980415", "TAXRETURN19980415");
|
||||
}
|
||||
|
||||
public void testBooleanLiterals() {
|
||||
confirmFormat("TRUE", "'TRUE'");
|
||||
confirmFormat("FALSE", "'FALSE'");
|
||||
confirmFormat("True", "'True'");
|
||||
confirmFormat("fAlse", "'fAlse'");
|
||||
|
||||
confirmFormat("Yes", "Yes");
|
||||
confirmFormat("No", "No");
|
||||
}
|
||||
|
||||
private static void confirmCellNameMatch(String rawSheetName, boolean expected) {
|
||||
assertEquals(expected, SheetNameFormatter.nameLooksLikePlainCellReference(rawSheetName));
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ public class AllFormulaEvalTests {
|
||||
result.addTestSuite(TestFormulaBugs.class);
|
||||
result.addTestSuite(TestFormulasFromSpreadsheet.class);
|
||||
result.addTestSuite(TestPercentEval.class);
|
||||
result.addTestSuite(TestRangeEval.class);
|
||||
result.addTestSuite(TestUnaryPlusEval.class);
|
||||
return result;
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ public final class TestFormulaBugs extends TestCase {
|
||||
throw new AssertionFailedError("Identified bug 42448");
|
||||
}
|
||||
|
||||
assertEquals("SUMPRODUCT(A!C7:C67,B8:B68)/B69", cell.getCellFormula());
|
||||
assertEquals("SUMPRODUCT(A!C7:A!C67,B8:B68)/B69", cell.getCellFormula());
|
||||
|
||||
// might as well evaluate the sucker...
|
||||
|
||||
|
@ -0,0 +1,95 @@
|
||||
/* ====================================================================
|
||||
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.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AreaI;
|
||||
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
|
||||
import org.apache.poi.hssf.util.AreaReference;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test for unary plus operator evaluator.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestRangeEval extends TestCase {
|
||||
|
||||
public void testPermutations() {
|
||||
|
||||
confirm("B3", "D7", "B3:D7");
|
||||
confirm("B1", "B1", "B1:B1");
|
||||
|
||||
confirm("B7", "D3", "B3:D7");
|
||||
confirm("D3", "B7", "B3:D7");
|
||||
confirm("D7", "B3", "B3:D7");
|
||||
}
|
||||
|
||||
private static void confirm(String refA, String refB, String expectedAreaRef) {
|
||||
|
||||
Eval[] args = {
|
||||
createRefEval(refA),
|
||||
createRefEval(refB),
|
||||
};
|
||||
AreaReference ar = new AreaReference(expectedAreaRef);
|
||||
Eval result = RangeEval.instance.evaluate(args, 0, (short)0);
|
||||
assertTrue(result instanceof AreaEval);
|
||||
AreaEval ae = (AreaEval) result;
|
||||
assertEquals(ar.getFirstCell().getRow(), ae.getFirstRow());
|
||||
assertEquals(ar.getLastCell().getRow(), ae.getLastRow());
|
||||
assertEquals(ar.getFirstCell().getCol(), ae.getFirstColumn());
|
||||
assertEquals(ar.getLastCell().getCol(), ae.getLastColumn());
|
||||
}
|
||||
|
||||
private static Eval createRefEval(String refStr) {
|
||||
CellReference cr = new CellReference(refStr);
|
||||
return new MockRefEval(cr.getRow(), cr.getCol());
|
||||
|
||||
}
|
||||
|
||||
private static final class MockRefEval extends RefEvalBase {
|
||||
|
||||
public MockRefEval(int rowIndex, int columnIndex) {
|
||||
super(rowIndex, columnIndex);
|
||||
}
|
||||
public ValueEval getInnerValueEval() {
|
||||
throw new RuntimeException("not expected to be called during this test");
|
||||
}
|
||||
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx,
|
||||
int relLastColIx) {
|
||||
AreaI area = new OffsetArea(getRow(), getColumn(),
|
||||
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
|
||||
return new MockAreaEval(area);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MockAreaEval extends AreaEvalBase {
|
||||
|
||||
public MockAreaEval(AreaI ptg) {
|
||||
super(ptg);
|
||||
}
|
||||
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
|
||||
throw new RuntimeException("not expected to be called during this test");
|
||||
}
|
||||
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx,
|
||||
int relLastColIx) {
|
||||
throw new RuntimeException("not expected to be called during this test");
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,6 @@
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
@ -42,12 +41,8 @@ public final class TestFormulas extends TestCase {
|
||||
/**
|
||||
* Add 1+1 -- WHoohoo!
|
||||
*/
|
||||
public void testBasicAddIntegers() {
|
||||
|
||||
public void testBasicAddIntegers()
|
||||
throws Exception {
|
||||
|
||||
File file = TempFile.createTempFile("testFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -58,95 +53,78 @@ public final class TestFormulas extends TestCase {
|
||||
c = r.createCell(1);
|
||||
c.setCellFormula(1 + "+" + 1);
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(1);
|
||||
c = r.getCell(1);
|
||||
|
||||
assertTrue("Formula is as expected",("1+1".equals(c.getCellFormula())));
|
||||
in.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add various integers
|
||||
*/
|
||||
|
||||
public void testAddIntegers()
|
||||
throws Exception {
|
||||
public void testAddIntegers() {
|
||||
binomialOperator("+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply various integers
|
||||
*/
|
||||
|
||||
public void testMultplyIntegers()
|
||||
throws Exception {
|
||||
public void testMultplyIntegers() {
|
||||
binomialOperator("*");
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract various integers
|
||||
*/
|
||||
public void testSubtractIntegers()
|
||||
throws Exception {
|
||||
public void testSubtractIntegers() {
|
||||
binomialOperator("-");
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract various integers
|
||||
*/
|
||||
public void testDivideIntegers()
|
||||
throws Exception {
|
||||
public void testDivideIntegers() {
|
||||
binomialOperator("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Exponentialize various integers;
|
||||
*/
|
||||
public void testPowerIntegers()
|
||||
throws Exception {
|
||||
public void testPowerIntegers() {
|
||||
binomialOperator("^");
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatinate two numbers 1&2 = 12
|
||||
* Concatenate two numbers 1&2 = 12
|
||||
*/
|
||||
public void testConcatIntegers()
|
||||
throws Exception {
|
||||
public void testConcatIntegers() {
|
||||
binomialOperator("&");
|
||||
}
|
||||
|
||||
/**
|
||||
* tests 1*2+3*4
|
||||
*/
|
||||
public void testOrderOfOperationsMultiply()
|
||||
throws Exception {
|
||||
public void testOrderOfOperationsMultiply() {
|
||||
orderTest("1*2+3*4");
|
||||
}
|
||||
|
||||
/**
|
||||
* tests 1*2+3^4
|
||||
*/
|
||||
public void testOrderOfOperationsPower()
|
||||
throws Exception {
|
||||
public void testOrderOfOperationsPower() {
|
||||
orderTest("1*2+3^4");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that parenthesis are obeyed
|
||||
*/
|
||||
public void testParenthesis()
|
||||
throws Exception {
|
||||
public void testParenthesis() {
|
||||
orderTest("(1*3)+2+(1+2)*(3^4)^5");
|
||||
}
|
||||
|
||||
public void testReferencesOpr()
|
||||
throws Exception {
|
||||
public void testReferencesOpr() {
|
||||
String[] operation = new String[] {
|
||||
"+", "-", "*", "/", "^", "&"
|
||||
};
|
||||
@ -159,16 +137,12 @@ public final class TestFormulas extends TestCase {
|
||||
* Tests creating a file with floating point in a formula.
|
||||
*
|
||||
*/
|
||||
public void testFloat()
|
||||
throws Exception {
|
||||
public void testFloat() {
|
||||
floatTest("*");
|
||||
floatTest("/");
|
||||
}
|
||||
|
||||
private void floatTest(String operator)
|
||||
throws Exception {
|
||||
File file = TempFile.createTempFile("testFormulaFloat",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
private static void floatTest(String operator) {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -180,10 +154,10 @@ public final class TestFormulas extends TestCase {
|
||||
c = r.createCell(1);
|
||||
c.setCellFormula(""+Float.MIN_VALUE + operator + Float.MIN_VALUE);
|
||||
|
||||
for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2) ) {
|
||||
for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2) ) {
|
||||
r = s.createRow(x);
|
||||
|
||||
for (short y = 1; y < 256 && y > 0; y= (short) (y +2)) {
|
||||
for (int y = 1; y < 256 && y > 0; y= (short) (y +2)) {
|
||||
|
||||
c = r.createCell(y);
|
||||
c.setCellFormula("" + x+"."+y + operator + y +"."+x);
|
||||
@ -196,69 +170,48 @@ public final class TestFormulas extends TestCase {
|
||||
c = r.createCell(0);
|
||||
c.setCellFormula("" + Float.MAX_VALUE + operator + Float.MAX_VALUE);
|
||||
}
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
out=null;wb=null; //otherwise we get out of memory error!
|
||||
floatVerify(operator,file);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
|
||||
floatVerify(operator, wb);
|
||||
}
|
||||
|
||||
private void floatVerify(String operator, File file)
|
||||
throws Exception {
|
||||
private static void floatVerify(String operator, HSSFWorkbook wb) {
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook(in);
|
||||
HSSFSheet s = wb.getSheetAt(0);
|
||||
HSSFRow r = null;
|
||||
HSSFCell c = null;
|
||||
|
||||
// dont know how to check correct result .. for the moment, we just verify that the file can be read.
|
||||
// don't know how to check correct result .. for the moment, we just verify that the file can be read.
|
||||
|
||||
for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
|
||||
r = s.getRow(x);
|
||||
for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
|
||||
HSSFRow r = s.getRow(x);
|
||||
|
||||
for (short y = 1; y < 256 && y > 0; y=(short)(y+2)) {
|
||||
for (int y = 1; y < 256 && y > 0; y=(short)(y+2)) {
|
||||
|
||||
c = r.getCell(y);
|
||||
HSSFCell c = r.getCell(y);
|
||||
assertTrue("got a formula",c.getCellFormula()!=null);
|
||||
|
||||
assertTrue("loop Formula is as expected "+x+"."+y+operator+y+"."+x+"!="+c.getCellFormula(),(
|
||||
(""+x+"."+y+operator+y+"."+x).equals(c.getCellFormula()) ));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
}
|
||||
|
||||
public void testAreaSum()
|
||||
throws Exception {
|
||||
public void testAreaSum() {
|
||||
areaFunctionTest("SUM");
|
||||
}
|
||||
|
||||
public void testAreaAverage()
|
||||
throws Exception {
|
||||
public void testAreaAverage() {
|
||||
areaFunctionTest("AVERAGE");
|
||||
}
|
||||
|
||||
public void testRefArraySum()
|
||||
throws Exception {
|
||||
public void testRefArraySum() {
|
||||
refArrayFunctionTest("SUM");
|
||||
}
|
||||
|
||||
public void testAreaArraySum()
|
||||
throws Exception {
|
||||
public void testAreaArraySum() {
|
||||
refAreaArrayFunctionTest("SUM");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void operationRefTest(String operator)
|
||||
throws Exception {
|
||||
File file = TempFile.createTempFile("testFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
private static void operationRefTest(String operator) {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -317,21 +270,16 @@ public final class TestFormulas extends TestCase {
|
||||
c.setCellFormula("" + "B1" + operator + "IV255");
|
||||
}
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
operationalRefVerify(operator,file);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
operationalRefVerify(operator, wb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the sheet we wrote out by binomialOperator and makes sure the formulas
|
||||
* all match what we expect (x operator y)
|
||||
*/
|
||||
private void operationalRefVerify(String operator, File file)
|
||||
throws Exception {
|
||||
private static void operationalRefVerify(String operator, HSSFWorkbook wb) {
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook(in);
|
||||
HSSFSheet s = wb.getSheetAt(0);
|
||||
HSSFRow r = null;
|
||||
HSSFCell c = null;
|
||||
@ -345,46 +293,43 @@ public final class TestFormulas extends TestCase {
|
||||
));
|
||||
|
||||
|
||||
for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
|
||||
for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
|
||||
r = s.getRow(x);
|
||||
|
||||
for (short y = 1; y < 256 && y > 0; y++) {
|
||||
for (int y = 1; y < 256 && y > 0; y++) {
|
||||
|
||||
String ref=null;
|
||||
String ref2=null;
|
||||
short refx1=0;
|
||||
short refy1=0;
|
||||
short refx2=0;
|
||||
short refy2=0;
|
||||
int refx1;
|
||||
int refy1;
|
||||
int refx2;
|
||||
int refy2;
|
||||
if (x +50 < Short.MAX_VALUE) {
|
||||
refx1=(short)(x+50);
|
||||
refx2=(short)(x+46);
|
||||
refx1=x+50;
|
||||
refx2=x+46;
|
||||
} else {
|
||||
refx1=(short)(x-4);
|
||||
refx2=(short)(x-3);
|
||||
refx1=x-4;
|
||||
refx2=x-3;
|
||||
}
|
||||
|
||||
if (y+50 < 255) {
|
||||
refy1=(short)(y+50);
|
||||
refy2=(short)(y+49);
|
||||
refy1=y+50;
|
||||
refy2=y+49;
|
||||
} else {
|
||||
refy1=(short)(y-4);
|
||||
refy2=(short)(y-3);
|
||||
refy1=y-4;
|
||||
refy2=y-3;
|
||||
}
|
||||
|
||||
c = r.getCell(y);
|
||||
CellReference cr= new CellReference(refx1, refy1, false, false);
|
||||
String ref=cr.formatAsString();
|
||||
ref=cr.formatAsString();
|
||||
cr=new CellReference(refx2,refy2, false, false);
|
||||
ref2=cr.formatAsString();
|
||||
String ref2=cr.formatAsString();
|
||||
|
||||
|
||||
assertTrue("loop Formula is as expected "+ref+operator+ref2+"!="+c.getCellFormula(),(
|
||||
(""+ref+operator+ref2).equals(c.getCellFormula())
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,13 +337,7 @@ public final class TestFormulas extends TestCase {
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
|
||||
assertTrue("maxval Formula is as expected",(
|
||||
("B1"+operator+"IV255").equals(c.getCellFormula())
|
||||
)
|
||||
);
|
||||
|
||||
in.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
assertEquals("B1"+operator+"IV255", c.getCellFormula());
|
||||
}
|
||||
|
||||
|
||||
@ -406,10 +345,7 @@ public final class TestFormulas extends TestCase {
|
||||
/**
|
||||
* tests order wrting out == order writing in for a given formula
|
||||
*/
|
||||
private void orderTest(String formula)
|
||||
throws Exception {
|
||||
File file = TempFile.createTempFile("testFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
private static void orderTest(String formula) {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -420,12 +356,7 @@ public final class TestFormulas extends TestCase {
|
||||
c = r.createCell(1);
|
||||
c.setCellFormula(formula);
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
|
||||
//get our minimum values
|
||||
@ -434,8 +365,6 @@ public final class TestFormulas extends TestCase {
|
||||
assertTrue("minval Formula is as expected",
|
||||
formula.equals(c.getCellFormula())
|
||||
);
|
||||
|
||||
in.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -443,10 +372,7 @@ public final class TestFormulas extends TestCase {
|
||||
* huge set of x operator y formulas. Next we call binomialVerify and verify
|
||||
* that they are all how we expect.
|
||||
*/
|
||||
private void binomialOperator(String operator)
|
||||
throws Exception {
|
||||
File file = TempFile.createTempFile("testFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
private static void binomialOperator(String operator) {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -474,23 +400,15 @@ public final class TestFormulas extends TestCase {
|
||||
c = r.createCell(0);
|
||||
c.setCellFormula("" + Short.MAX_VALUE + operator + Short.MAX_VALUE);
|
||||
}
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
binomialVerify(operator,file);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
binomialVerify(operator, wb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the sheet we wrote out by binomialOperator and makes sure the formulas
|
||||
* all match what we expect (x operator y)
|
||||
*/
|
||||
private void binomialVerify(String operator, File file)
|
||||
throws Exception {
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook(in);
|
||||
private static void binomialVerify(String operator, HSSFWorkbook wb) {
|
||||
HSSFSheet s = wb.getSheetAt(0);
|
||||
HSSFRow r = null;
|
||||
HSSFCell c = null;
|
||||
@ -502,10 +420,10 @@ public final class TestFormulas extends TestCase {
|
||||
( ("1"+operator+"1").equals(c.getCellFormula())
|
||||
));
|
||||
|
||||
for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
|
||||
for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
|
||||
r = s.getRow(x);
|
||||
|
||||
for (short y = 1; y < 256 && y > 0; y++) {
|
||||
for (int y = 1; y < 256 && y > 0; y++) {
|
||||
|
||||
c = r.getCell(y);
|
||||
|
||||
@ -513,8 +431,6 @@ public final class TestFormulas extends TestCase {
|
||||
(""+x+operator+y).equals(c.getCellFormula())
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -522,27 +438,17 @@ public final class TestFormulas extends TestCase {
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
|
||||
|
||||
assertTrue("maxval Formula is as expected",(
|
||||
(""+Short.MAX_VALUE+operator+Short.MAX_VALUE).equals(c.getCellFormula())
|
||||
)
|
||||
);
|
||||
|
||||
in.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Writes a function then tests to see if its correct
|
||||
*
|
||||
*/
|
||||
public void areaFunctionTest(String function)
|
||||
throws Exception {
|
||||
public static void areaFunctionTest(String function) {
|
||||
|
||||
File file = TempFile.createTempFile("testFormulaAreaFunction"+function,".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -554,13 +460,7 @@ public final class TestFormulas extends TestCase {
|
||||
c = r.createCell(0);
|
||||
c.setCellFormula(function+"(A2:A3)");
|
||||
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
@ -568,18 +468,13 @@ public final class TestFormulas extends TestCase {
|
||||
assertTrue("function ="+function+"(A2:A3)",
|
||||
( (function+"(A2:A3)").equals((function+"(A2:A3)")) )
|
||||
);
|
||||
in.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a function then tests to see if its correct
|
||||
*
|
||||
*/
|
||||
public void refArrayFunctionTest(String function)
|
||||
throws Exception {
|
||||
public void refArrayFunctionTest(String function) {
|
||||
|
||||
File file = TempFile.createTempFile("testFormulaArrayFunction"+function,".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -591,13 +486,7 @@ public final class TestFormulas extends TestCase {
|
||||
c = r.createCell(0);
|
||||
c.setCellFormula(function+"(A2,A3)");
|
||||
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
@ -605,7 +494,6 @@ public final class TestFormulas extends TestCase {
|
||||
assertTrue("function ="+function+"(A2,A3)",
|
||||
( (function+"(A2,A3)").equals(c.getCellFormula()) )
|
||||
);
|
||||
in.close();
|
||||
}
|
||||
|
||||
|
||||
@ -613,11 +501,8 @@ public final class TestFormulas extends TestCase {
|
||||
* Writes a function then tests to see if its correct
|
||||
*
|
||||
*/
|
||||
public void refAreaArrayFunctionTest(String function)
|
||||
throws Exception {
|
||||
public void refAreaArrayFunctionTest(String function) {
|
||||
|
||||
File file = TempFile.createTempFile("testFormulaAreaArrayFunction"+function,".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
@ -631,12 +516,7 @@ public final class TestFormulas extends TestCase {
|
||||
c=r.createCell(1);
|
||||
c.setCellFormula(function+"($A$2:$A4,B$2:B4)");
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
@ -649,22 +529,17 @@ public final class TestFormulas extends TestCase {
|
||||
assertTrue("function ="+function+"($A$2:$A4,B$2:B4)",
|
||||
( (function+"($A$2:$A4,B$2:B4)").equals(c.getCellFormula()) )
|
||||
);
|
||||
in.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void testAbsRefs() throws Exception {
|
||||
File file = TempFile.createTempFile("testFormulaAbsRef",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
public void testAbsRefs() {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet();
|
||||
HSSFRow r = null;
|
||||
HSSFCell c = null;
|
||||
|
||||
HSSFRow r;
|
||||
HSSFCell c;
|
||||
|
||||
r = s.createRow(0);
|
||||
|
||||
c = r.createCell(0);
|
||||
c.setCellFormula("A3+A2");
|
||||
c=r.createCell(1);
|
||||
@ -676,12 +551,7 @@ public final class TestFormulas extends TestCase {
|
||||
c=r.createCell(4);
|
||||
c.setCellFormula("SUM($A$3,$A$2)");
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
@ -694,14 +564,9 @@ public final class TestFormulas extends TestCase {
|
||||
assertTrue("$A$3+$A$2", ("$A$3+$A$2").equals(c.getCellFormula()));
|
||||
c = r.getCell(4);
|
||||
assertTrue("SUM($A$3,$A$2)", ("SUM($A$3,$A$2)").equals(c.getCellFormula()));
|
||||
in.close();
|
||||
}
|
||||
|
||||
public void testSheetFunctions()
|
||||
throws IOException
|
||||
{
|
||||
File file = TempFile.createTempFile("testSheetFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
public void testSheetFunctions() {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet("A");
|
||||
HSSFRow r = null;
|
||||
@ -715,20 +580,15 @@ public final class TestFormulas extends TestCase {
|
||||
c=r.createCell(0); c.setCellFormula("AVERAGE(A!A1:B1)");
|
||||
c=r.createCell(1); c.setCellFormula("A!A1+A!B1");
|
||||
c=r.createCell(2); c.setCellFormula("A!$A$1+A!$B1");
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
assertTrue("file exists",file.exists());
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
s = wb.getSheet("B");
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
assertTrue("expected: AVERAGE(A!A1:B1) got: "+c.getCellFormula(), ("AVERAGE(A!A1:B1)").equals(c.getCellFormula()));
|
||||
c = r.getCell(1);
|
||||
assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
|
||||
in.close();
|
||||
}
|
||||
|
||||
public void testRVAoperands() throws Exception {
|
||||
@ -769,11 +629,7 @@ public final class TestFormulas extends TestCase {
|
||||
assertTrue("file exists",file.exists());
|
||||
}
|
||||
|
||||
public void testStringFormulas()
|
||||
throws IOException
|
||||
{
|
||||
File file = TempFile.createTempFile("testStringFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
public void testStringFormulas() {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet("A");
|
||||
HSSFRow r = null;
|
||||
@ -783,26 +639,17 @@ public final class TestFormulas extends TestCase {
|
||||
c=r.createCell(2); c.setCellFormula("LOWER(\"ABC\")");
|
||||
c=r.createCell(3); c.setCellFormula("CONCATENATE(\" my \",\" name \")");
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
|
||||
wb = openSample("StringFormulas.xls");
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(0);
|
||||
assertTrue("expected: UPPER(\"xyz\") got "+c.getCellFormula(), ("UPPER(\"xyz\")").equals(c.getCellFormula()));
|
||||
//c = r.getCell((short)1);
|
||||
//assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
|
||||
assertEquals("UPPER(\"xyz\")", c.getCellFormula());
|
||||
}
|
||||
|
||||
public void testLogicalFormulas() {
|
||||
|
||||
|
||||
public void testLogicalFormulas()
|
||||
throws IOException
|
||||
{
|
||||
|
||||
File file = TempFile.createTempFile("testLogicalFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet("A");
|
||||
HSSFRow r = null;
|
||||
@ -810,26 +657,14 @@ public final class TestFormulas extends TestCase {
|
||||
r = s.createRow(0);
|
||||
c=r.createCell(1); c.setCellFormula("IF(A1<A2,B1,B2)");
|
||||
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(1);
|
||||
assertEquals("Formula in cell 1 ","IF(A1<A2,B1,B2)",c.getCellFormula());
|
||||
in.close();
|
||||
}
|
||||
|
||||
public void testDateFormulas()
|
||||
throws IOException
|
||||
{
|
||||
File file = TempFile.createTempFile("testDateFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
public void testDateFormulas() {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet("testSheet1");
|
||||
HSSFRow r = null;
|
||||
@ -853,19 +688,11 @@ public final class TestFormulas extends TestCase {
|
||||
c.setCellStyle(cellStyle);
|
||||
}
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
}
|
||||
|
||||
|
||||
public void testIfFormulas()
|
||||
throws IOException
|
||||
{
|
||||
File file = TempFile.createTempFile("testIfFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
public void testIfFormulas() {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet s = wb.createSheet("testSheet1");
|
||||
HSSFRow r = null;
|
||||
@ -876,19 +703,12 @@ public final class TestFormulas extends TestCase {
|
||||
c=r.createCell(3); c.setCellFormula("MAX(A1:B1)");
|
||||
c=r.createCell(4); c.setCellFormula("IF(A1=D1,\"A1\",\"B1\")");
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
assertTrue("file exists",file.exists());
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
wb = new HSSFWorkbook(in);
|
||||
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
s = wb.getSheetAt(0);
|
||||
r = s.getRow(0);
|
||||
c = r.getCell(4);
|
||||
|
||||
assertTrue("expected: IF(A1=D1,\"A1\",\"B1\") got "+c.getCellFormula(), ("IF(A1=D1,\"A1\",\"B1\")").equals(c.getCellFormula()));
|
||||
in.close();
|
||||
|
||||
wb = openSample("IfFormulaTest.xls");
|
||||
s = wb.getSheetAt(0);
|
||||
@ -897,10 +717,8 @@ public final class TestFormulas extends TestCase {
|
||||
assertTrue("expected: IF(A3=A1,\"A1\",\"A2\") got "+c.getCellFormula(), ("IF(A3=A1,\"A1\",\"A2\")").equals(c.getCellFormula()));
|
||||
//c = r.getCell((short)1);
|
||||
//assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
|
||||
in.close();
|
||||
|
||||
File simpleIf = TempFile.createTempFile("testSimpleIfFormulaWrite",".xls");
|
||||
out = new FileOutputStream(simpleIf);
|
||||
|
||||
wb = new HSSFWorkbook();
|
||||
s = wb.createSheet("testSheet1");
|
||||
r = null;
|
||||
@ -908,14 +726,8 @@ public final class TestFormulas extends TestCase {
|
||||
r = s.createRow(0);
|
||||
c=r.createCell(0); c.setCellFormula("IF(1=1,0,1)");
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists", simpleIf.exists());
|
||||
HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
|
||||
assertTrue("length of simpleIf file is zero", (simpleIf.length()>0));
|
||||
|
||||
File nestedIf = TempFile.createTempFile("testNestedIfFormula",".xls");
|
||||
out = new FileOutputStream(nestedIf);
|
||||
wb = new HSSFWorkbook();
|
||||
s = wb.createSheet("testSheet1");
|
||||
r = null;
|
||||
@ -939,17 +751,10 @@ public final class TestFormulas extends TestCase {
|
||||
|
||||
formulaCell.setCellFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))");
|
||||
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
assertTrue("file exists", nestedIf.exists());
|
||||
|
||||
assertTrue("length of nestedIf file is zero", (nestedIf.length()>0));
|
||||
HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
}
|
||||
|
||||
public void testSumIf()
|
||||
throws IOException
|
||||
{
|
||||
public void testSumIf() {
|
||||
String function ="SUMIF(A1:A5,\">4000\",B1:B5)";
|
||||
|
||||
HSSFWorkbook wb = openSample("sumifformula.xls");
|
||||
@ -960,40 +765,34 @@ public final class TestFormulas extends TestCase {
|
||||
assertEquals(function, c.getCellFormula());
|
||||
|
||||
|
||||
File file = TempFile.createTempFile("testSumIfFormula",".xls");
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
wb = new HSSFWorkbook();
|
||||
s = wb.createSheet();
|
||||
|
||||
r = s.createRow(0);
|
||||
c=r.createCell(0); c.setCellValue((double)1000);
|
||||
c=r.createCell(1); c.setCellValue((double)1);
|
||||
c=r.createCell(0); c.setCellValue(1000);
|
||||
c=r.createCell(1); c.setCellValue(1);
|
||||
|
||||
|
||||
r = s.createRow(1);
|
||||
c=r.createCell(0); c.setCellValue((double)2000);
|
||||
c=r.createCell(1); c.setCellValue((double)2);
|
||||
c=r.createCell(0); c.setCellValue(2000);
|
||||
c=r.createCell(1); c.setCellValue(2);
|
||||
|
||||
r = s.createRow(2);
|
||||
c=r.createCell(0); c.setCellValue((double)3000);
|
||||
c=r.createCell(1); c.setCellValue((double)3);
|
||||
c=r.createCell(0); c.setCellValue(3000);
|
||||
c=r.createCell(1); c.setCellValue(3);
|
||||
|
||||
r = s.createRow(3);
|
||||
c=r.createCell(0); c.setCellValue((double)4000);
|
||||
c=r.createCell(1); c.setCellValue((double)4);
|
||||
c=r.createCell(0); c.setCellValue(4000);
|
||||
c=r.createCell(1); c.setCellValue(4);
|
||||
|
||||
r = s.createRow(4);
|
||||
c=r.createCell(0); c.setCellValue((double)5000);
|
||||
c=r.createCell(1); c.setCellValue((double)5);
|
||||
c=r.createCell(0); c.setCellValue(5000);
|
||||
c=r.createCell(1); c.setCellValue(5);
|
||||
|
||||
r = s.getRow(0);
|
||||
c=r.createCell(2); c.setCellFormula(function);
|
||||
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
assertTrue("sumif file doesnt exists", (file.exists()));
|
||||
assertTrue("sumif == 0 bytes", file.length() > 0);
|
||||
HSSFTestDataSamples.writeOutAndReadBack(wb);
|
||||
}
|
||||
|
||||
public void testSquareMacro() {
|
||||
@ -1064,7 +863,7 @@ public final class TestFormulas extends TestCase {
|
||||
/** Unknown Ptg 3D*/
|
||||
public void test27272_2() throws Exception {
|
||||
HSSFWorkbook wb = openSample("27272_2.xls");
|
||||
assertEquals("Reference for named range ", "'LOAD.POD_HISTORIES'!#REF!",wb.getNameAt(0).getReference());
|
||||
assertEquals("Reference for named range ", "LOAD.POD_HISTORIES!#REF!",wb.getNameAt(0).getReference());
|
||||
File outF = File.createTempFile("bug27272_2",".xls");
|
||||
wb.write(new FileOutputStream(outF));
|
||||
System.out.println("Open "+outF.getAbsolutePath()+" in Excel");
|
||||
@ -1085,8 +884,4 @@ public final class TestFormulas extends TestCase {
|
||||
assertEquals("DZ2*2", wb.getSheetAt(0).getRow(1).getCell(128).toString());
|
||||
assertEquals("B32770*2", wb.getSheetAt(0).getRow(32768).getCell(1).toString());
|
||||
}
|
||||
|
||||
public static void main(String [] args) {
|
||||
junit.textui.TestRunner.run(TestFormulas.class);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
/**
|
||||
* Test suite for org.apache.poi.ss.formula
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class AllSSFormulaTests {
|
||||
public static Test suite() {
|
||||
TestSuite result = new TestSuite(AllSSFormulaTests.class.getName());
|
||||
result.addTestSuite(TestEvaluationCache.class);
|
||||
result.addTestSuite(TestWorkbookEvaluator.class);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.formula.AreaErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.DeletedRef3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.RefErrorPtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
/**
|
||||
* Tests {@link WorkbookEvaluator}.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class TestWorkbookEvaluator extends TestCase {
|
||||
|
||||
/**
|
||||
* Make sure that the evaluator can directly handle tAttrSum (instead of relying on re-parsing
|
||||
* the whole formula which converts tAttrSum to tFuncVar("SUM") )
|
||||
*/
|
||||
public void testAttrSum() {
|
||||
|
||||
Ptg[] ptgs = {
|
||||
new IntPtg(42),
|
||||
AttrPtg.SUM,
|
||||
};
|
||||
|
||||
ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
|
||||
assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the evaluator can directly handle (deleted) ref error tokens
|
||||
* (instead of relying on re-parsing the whole formula which converts these
|
||||
* to the error constant #REF! )
|
||||
*/
|
||||
public void testRefErr() {
|
||||
|
||||
confirmRefErr(new RefErrorPtg());
|
||||
confirmRefErr(new AreaErrPtg());
|
||||
confirmRefErr(new DeletedRef3DPtg(0));
|
||||
confirmRefErr(new DeletedArea3DPtg(0));
|
||||
}
|
||||
private static void confirmRefErr(Ptg ptg) {
|
||||
Ptg[] ptgs = {
|
||||
ptg,
|
||||
};
|
||||
|
||||
ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
|
||||
assertEquals(ErrorEval.REF_INVALID, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the evaluator can directly handle tAttrSum (instead of relying on re-parsing
|
||||
* the whole formula which converts tAttrSum to tFuncVar("SUM") )
|
||||
*/
|
||||
public void testMemFunc() {
|
||||
|
||||
Ptg[] ptgs = {
|
||||
new IntPtg(42),
|
||||
AttrPtg.SUM,
|
||||
};
|
||||
|
||||
ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
|
||||
assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0);
|
||||
}
|
||||
|
||||
|
||||
public void testEvaluateMultipleWorkbooks() {
|
||||
HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls");
|
||||
HSSFWorkbook wbB = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaB.xls");
|
||||
|
||||
HSSFFormulaEvaluator evaluatorA = new HSSFFormulaEvaluator(wbA);
|
||||
HSSFFormulaEvaluator evaluatorB = new HSSFFormulaEvaluator(wbB);
|
||||
|
||||
// Hook up the workbook evaluators to enable evaluation of formulas across books
|
||||
String[] bookNames = { "multibookFormulaA.xls", "multibookFormulaB.xls", };
|
||||
HSSFFormulaEvaluator[] evaluators = { evaluatorA, evaluatorB, };
|
||||
HSSFFormulaEvaluator.setupEnvironment(bookNames, evaluators);
|
||||
|
||||
HSSFCell cell;
|
||||
|
||||
HSSFSheet aSheet1 = wbA.getSheetAt(0);
|
||||
HSSFSheet bSheet1 = wbB.getSheetAt(0);
|
||||
|
||||
// Simple case - single link from wbA to wbB
|
||||
confirmFormula(wbA, 0, 0, 0, "[multibookFormulaB.xls]BSheet1!B1");
|
||||
cell = aSheet1.getRow(0).getCell(0);
|
||||
confirmEvaluation(35, evaluatorA, cell);
|
||||
|
||||
|
||||
// more complex case - back link into wbA
|
||||
// [wbA]ASheet1!A2 references (among other things) [wbB]BSheet1!B2
|
||||
confirmFormula(wbA, 0, 1, 0, "[multibookFormulaB.xls]BSheet1!$B$2+2*A3");
|
||||
// [wbB]BSheet1!B2 references (among other things) [wbA]AnotherSheet!A1:B2
|
||||
confirmFormula(wbB, 0, 1, 1, "SUM([multibookFormulaA.xls]AnotherSheet!$A$1:$B$2)+B3");
|
||||
|
||||
cell = aSheet1.getRow(1).getCell(0);
|
||||
confirmEvaluation(264, evaluatorA, cell);
|
||||
|
||||
// change [wbB]BSheet1!B3 (from 50 to 60)
|
||||
bSheet1.getRow(2).getCell(1).setCellValue(60);
|
||||
evaluatorB.setCachedPlainValue(bSheet1, 2, 1, new NumberEval(60));
|
||||
confirmEvaluation(274, evaluatorA, cell);
|
||||
|
||||
// change [wbA]ASheet1!A3 (from 100 to 80)
|
||||
aSheet1.getRow(2).getCell(0).setCellValue(80);
|
||||
evaluatorA.setCachedPlainValue(aSheet1, 2, 0, new NumberEval(80));
|
||||
confirmEvaluation(234, evaluatorA, cell);
|
||||
|
||||
// change [wbA]AnotherSheet!A1 (from 2 to 3)
|
||||
wbA.getSheetAt(1).getRow(0).getCell(0).setCellValue(3);
|
||||
evaluatorA.setCachedPlainValue(wbA.getSheetAt(1), 0, 0, new NumberEval(3));
|
||||
confirmEvaluation(235, evaluatorA, cell);
|
||||
}
|
||||
|
||||
private static void confirmEvaluation(double expectedValue, HSSFFormulaEvaluator fe, HSSFCell cell) {
|
||||
assertEquals(expectedValue, fe.evaluate(cell).getNumberValue(), 0.0);
|
||||
}
|
||||
|
||||
private static void confirmFormula(HSSFWorkbook wb, int sheetIndex, int rowIndex, int columnIndex,
|
||||
String expectedFormula) {
|
||||
HSSFCell cell = wb.getSheetAt(sheetIndex).getRow(rowIndex).getCell(columnIndex);
|
||||
assertEquals(expectedFormula, cell.getCellFormula());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user