The way that HSSF and XSSF stores references to external sheets are rather different, so begin to reflect that in how we parse their formulas into Ptgs

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1611906 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2014-07-19 12:49:41 +00:00
parent 03a1b6e3f6
commit 31cd7f7e57
10 changed files with 301 additions and 117 deletions

View File

@ -30,10 +30,15 @@ import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.formula.FormulaParsingWorkbook;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.formula.SheetIdentifier;
import org.apache.poi.ss.formula.ptg.Area3DPtg;
import org.apache.poi.ss.formula.ptg.NamePtg;
import org.apache.poi.ss.formula.ptg.NameXPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@ -65,7 +70,16 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return _iBook.getExternalSheetIndex(workbookName, sheetName);
}
public NameXPtg getNameXPtg(String name, int sheetRefIndex) {
public Ptg get3DReferencePtg(CellReference cr, SheetIdentifier sheet) {
int extIx = getSheetExtIx(sheet);
return new Ref3DPtg(cr, extIx);
}
public Ptg get3DReferencePtg(AreaReference areaRef, SheetIdentifier sheet) {
int extIx = getSheetExtIx(sheet);
return new Area3DPtg(areaRef, extIx);
}
public NameXPtg getNameXPtg(String name, SheetIdentifier sheet) {
int sheetRefIndex = getSheetExtIx(sheet);
return _iBook.getNameXPtg(name, sheetRefIndex, _uBook.getUDFFinder());
}
@ -178,6 +192,21 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
}
}
private int getSheetExtIx(SheetIdentifier sheetIden) {
int extIx;
if (sheetIden == null) {
extIx = -1;
} else {
String sName = sheetIden.getSheetIdentifier().getName();
if (sheetIden.getBookName() == null) {
extIx = getExternalSheetIndex(sName);
} else {
extIx = getExternalSheetIndex(sheetIden.getBookName(), sName);
}
}
return extIx;
}
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL97;
}

View File

@ -27,7 +27,6 @@ import org.apache.poi.ss.formula.function.FunctionMetadata;
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
import org.apache.poi.ss.formula.ptg.AddPtg;
import org.apache.poi.ss.formula.ptg.Area3DPtg;
import org.apache.poi.ss.formula.ptg.AreaPtg;
import org.apache.poi.ss.formula.ptg.ArrayPtg;
import org.apache.poi.ss.formula.ptg.AttrPtg;
@ -58,7 +57,6 @@ import org.apache.poi.ss.formula.ptg.PercentPtg;
import org.apache.poi.ss.formula.ptg.PowerPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.RangePtg;
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
import org.apache.poi.ss.formula.ptg.RefPtg;
import org.apache.poi.ss.formula.ptg.StringPtg;
import org.apache.poi.ss.formula.ptg.SubtractPtg;
@ -83,85 +81,8 @@ import org.apache.poi.ss.util.CellReference.NameType;
* <p/>
* For POI internal use only
* <p/>
*
*
* @author Avik Sengupta <avik at apache dot org>
* @author Andrew C. oliver (acoliver at apache dot org)
* @author Eric Ladner (eladner at goldinc dot com)
* @author Cameron Riley (criley at ekmail.com)
* @author Peter M. Murray (pete at quantrix dot com)
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
* @author Josh Micich
* @author David Lewis (DLewis400 at gmail dot com)
*/
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();
}
}
private static final class SheetIdentifier {
private final String _bookName;
private final Identifier _sheetIdentifier;
public SheetIdentifier(String bookName, Identifier sheetIdentifier) {
_bookName = bookName;
_sheetIdentifier = sheetIdentifier;
}
public String getBookName() {
return _bookName;
}
public Identifier getSheetIdentifier() {
return _sheetIdentifier;
}
private void asFormulaString(StringBuffer sb) {
if (_bookName != null) {
sb.append(" [").append(_sheetIdentifier.getName()).append("]");
}
if (_sheetIdentifier.isQuoted()) {
sb.append("'").append(_sheetIdentifier.getName()).append("'");
} else {
sb.append(_sheetIdentifier.getName());
}
}
public String asFormulaString() {
StringBuffer sb = new StringBuffer(32);
asFormulaString(sb);
return sb.toString();
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName());
sb.append(" [");
asFormulaString(sb);
sb.append("]");
return sb.toString();
}
}
private final String _formulaString;
private final int _formulaLength;
/** points at the next character to be read (after the {@link #look} char) */
@ -478,8 +399,7 @@ public final class FormulaParser {
throw new FormulaParseException("Cell reference or Named Range "
+ "expected after sheet name at index " + _pointer + ".");
}
int extIx = getSheetExtIx(sheetIden);
NameXPtg nameXPtg = _book.getNameXPtg(name, extIx);
NameXPtg nameXPtg = _book.getNameXPtg(name, sheetIden);
if (nameXPtg == null) {
throw new FormulaParseException("Specified name '" + name +
"' for sheet " + sheetIden.asFormulaString() + " not found");
@ -663,21 +583,6 @@ public final class FormulaParser {
return false;
}
private int getSheetExtIx(SheetIdentifier sheetIden) {
int extIx;
if (sheetIden == null) {
extIx = Integer.MIN_VALUE;
} else {
String sName = sheetIden.getSheetIdentifier().getName();
if (sheetIden.getBookName() == null) {
extIx = _book.getExternalSheetIndex(sName);
} else {
extIx = _book.getExternalSheetIndex(sheetIden.getBookName(), sName);
}
}
return extIx;
}
/**
*
* @param sheetIden may be <code>null</code>
@ -686,15 +591,13 @@ public final class FormulaParser {
*/
private ParseNode createAreaRefParseNode(SheetIdentifier sheetIden, SimpleRangePart part1,
SimpleRangePart part2) throws FormulaParseException {
int extIx = getSheetExtIx(sheetIden);
Ptg ptg;
if (part2 == null) {
CellReference cr = part1.getCellReference();
if (sheetIden == null) {
ptg = new RefPtg(cr);
} else {
ptg = new Ref3DPtg(cr, extIx);
ptg = _book.get3DReferencePtg(cr, sheetIden);
}
} else {
AreaReference areaRef = createAreaRef(part1, part2);
@ -702,7 +605,7 @@ public final class FormulaParser {
if (sheetIden == null) {
ptg = new AreaPtg(areaRef);
} else {
ptg = new Area3DPtg(areaRef, extIx);
ptg = _book.get3DReferencePtg(areaRef, sheetIden);
}
}
return new ParseNode(ptg);
@ -895,7 +798,7 @@ public final class FormulaParser {
}
}
Identifier iden = new Identifier(sb.toString(), true);
NameIdentifier iden = new NameIdentifier(sb.toString(), true);
// quoted identifier - can't concatenate anything more
SkipWhite();
if (look == '!') {
@ -916,7 +819,7 @@ public final class FormulaParser {
SkipWhite();
if (look == '!') {
GetChar();
return new SheetIdentifier(bookName, new Identifier(sb.toString(), false));
return new SheetIdentifier(bookName, new NameIdentifier(sb.toString(), false));
}
return null;
}
@ -987,7 +890,7 @@ public final class FormulaParser {
}
EvaluationName hName = _book.getName(name, _sheetIndex);
if (hName == null) {
nameToken = _book.getNameXPtg(name, -1);
nameToken = _book.getNameXPtg(name, null);
if (nameToken == null) {
throw new FormulaParseException("Name '" + name
+ "' is completely unknown in the current workbook");

View File

@ -19,6 +19,9 @@ package org.apache.poi.ss.formula;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.ptg.NameXPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
/**
* Abstracts a workbook for the purpose of formula parsing.<br/>
@ -33,7 +36,17 @@ public interface FormulaParsingWorkbook {
*/
EvaluationName getName(String name, int sheetIndex);
NameXPtg getNameXPtg(String name, int sheetRefIndex);
NameXPtg getNameXPtg(String name, SheetIdentifier sheet);
/**
* Produce the appropriate Ptg for a 3d cell reference
*/
Ptg get3DReferencePtg(CellReference cell, SheetIdentifier sheet);
/**
* Produce the appropriate Ptg for a 3d area reference
*/
Ptg get3DReferencePtg(AreaReference area, SheetIdentifier sheet);
/**
* gets the externSheet index for a sheet from this workbook

View File

@ -0,0 +1,46 @@
/* ====================================================================
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;
public class NameIdentifier {
private final String _name;
private final boolean _isQuoted;
public NameIdentifier(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();
}
}

View File

@ -0,0 +1,57 @@
/* ====================================================================
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;
public class SheetIdentifier {
public String _bookName;
public NameIdentifier _sheetIdentifier;
public SheetIdentifier(String bookName, NameIdentifier sheetIdentifier) {
_bookName = bookName;
_sheetIdentifier = sheetIdentifier;
}
public String getBookName() {
return _bookName;
}
public NameIdentifier getSheetIdentifier() {
return _sheetIdentifier;
}
private void asFormulaString(StringBuffer sb) {
if (_bookName != null) {
sb.append(" [").append(_sheetIdentifier.getName()).append("]");
}
if (_sheetIdentifier.isQuoted()) {
sb.append("'").append(_sheetIdentifier.getName()).append("'");
} else {
sb.append(_sheetIdentifier.getName());
}
}
public String asFormulaString() {
StringBuffer sb = new StringBuffer(32);
asFormulaString(sb);
return sb.toString();
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName());
sb.append(" [");
asFormulaString(sb);
sb.append("]");
return sb.toString();
}
}

View File

@ -65,6 +65,7 @@ import org.apache.poi.ss.formula.ptg.NumberPtg;
import org.apache.poi.ss.formula.ptg.OperationPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
import org.apache.poi.ss.formula.ptg.RefErrorPtg;
import org.apache.poi.ss.formula.ptg.RefPtg;
import org.apache.poi.ss.formula.ptg.StringPtg;
@ -675,6 +676,10 @@ public final class WorkbookEvaluator {
Ref3DPtg rptg = (Ref3DPtg) ptg;
return ec.getRef3DEval(rptg.getRow(), rptg.getColumn(), rptg.getExternSheetIndex());
}
if (ptg instanceof Ref3DPxg) {
Ref3DPtg rptg = (Ref3DPtg) ptg;
// TODO Return the right eval, should be easy as we already know the sheet details
}
if (ptg instanceof Area3DPtg) {
Area3DPtg aptg = (Area3DPtg) ptg;
return ec.getArea3DEval(aptg.getFirstRow(), aptg.getFirstColumn(), aptg.getLastRow(), aptg.getLastColumn(), aptg.getExternSheetIndex());

View File

@ -17,19 +17,21 @@
package org.apache.poi.ss.formula.ptg;
import org.apache.poi.ss.util.CellReference;
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.ss.util.CellReference;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
* Title: Reference 3D Ptg <P>
* Description: Defined a cell in extern sheet. <P>
* REFERENCE: <P>
* @author Libin Roman (Vista Portal LDT. Developer)
* @author Jason Height (jheight at chariot dot net dot au)
* <p>Title: Reference 3D Ptg</p>
* <p>Description: Defined a cell in extern sheet.</p>
* <p>REFERENCE: </p>
*
* <p>This is HSSF only, as it matches the HSSF file format way of
* referring to the sheet by an extern index. The XSSF equivalent
* is {@link Ref3DPxg}
*/
public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula, ExternSheetReferenceToken {
public final static byte sid = 0x3a;

View File

@ -0,0 +1,97 @@
/* ====================================================================
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.ptg;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.LittleEndianOutput;
/**
* <p>Title: XSSF 3D Reference</p>
* <p>Description: Defines a cell in an external or different sheet.</p>
* <p>REFERENCE: </p>
*
* <p>This is XSSF only, as it stores the sheet / book references
* in String form. The HSSF equivalent using indexes is {@link Ref3DPtg}</p>
*/
public final class Ref3DPxg extends RefPtgBase {
private int externalWorkbookNumber = -1;
private String sheetName;
public Ref3DPxg(int externalWorkbookNumber, String sheetName, String cellref) {
this(externalWorkbookNumber, sheetName, new CellReference(cellref));
}
public Ref3DPxg(int externalWorkbookNumber, String sheetName, CellReference c) {
super(c);
this.externalWorkbookNumber = externalWorkbookNumber;
this.sheetName = sheetName;
}
public Ref3DPxg(String sheetName, String cellref) {
this(sheetName, new CellReference(cellref));
}
public Ref3DPxg(String sheetName, CellReference c) {
this(-1, sheetName, c);
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName());
sb.append(" [");
if (externalWorkbookNumber >= 0) {
sb.append(" [");
sb.append("workbook=").append(getExternalWorkbookNumber());
sb.append("] ");
}
sb.append("sheet=").append(getSheetName());
sb.append(" ! ");
sb.append(formatReferenceAsString());
sb.append("]");
return sb.toString();
}
public int getExternalWorkbookNumber() {
return externalWorkbookNumber;
}
public String getSheetName() {
return sheetName;
}
public String format2DRefAsString() {
return formatReferenceAsString();
}
public String toFormulaString() {
StringBuffer sb = new StringBuffer();
if (externalWorkbookNumber >= 0) {
sb.append('[');
sb.append(externalWorkbookNumber);
sb.append(']');
}
sb.append(sheetName);
sb.append('!');
sb.append(formatReferenceAsString());
return sb.toString();
}
public int getSize() {
return 1;
}
public void write(LittleEndianOutput out) {
throw new IllegalStateException("XSSF-only Ptg, should not be serialised");
}
}

View File

@ -26,12 +26,17 @@ import org.apache.poi.ss.formula.FormulaParser;
import org.apache.poi.ss.formula.FormulaParsingWorkbook;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.formula.SheetIdentifier;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.ptg.Area3DPtg;
import org.apache.poi.ss.formula.ptg.NamePtg;
import org.apache.poi.ss.formula.ptg.NameXPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
/**
@ -78,6 +83,19 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return convertToExternalSheetIndex(sheetIndex);
}
private int resolveBookIndex(String bookName) {
// Is it already in numeric form?
if (bookName.startsWith("[") && bookName.endsWith("]")) {
bookName = bookName.substring(1, bookName.length()-2);
try {
return Integer.parseInt(bookName);
} catch (NumberFormatException e) {}
}
// Look up an External Link Table for this name
throw new RuntimeException("Not implemented yet"); // TODO
}
public EvaluationName getName(String name, int sheetIndex) {
for (int i = 0; i < _uBook.getNumberOfNames(); i++) {
XSSFName nm = _uBook.getNameAt(i);
@ -102,7 +120,7 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
throw new RuntimeException("Not implemented yet");
}
public NameXPtg getNameXPtg(String name, int sheetRefIndex) {
public NameXPtg getNameXPtg(String name, SheetIdentifier sheet) {
// First, try to find it as a User Defined Function
IndexedUDFFinder udfFinder = (IndexedUDFFinder)getUDFFinder();
FreeRefFunction func = udfFinder.findFunction(name);
@ -119,6 +137,20 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return null;
}
}
public Ptg get3DReferencePtg(CellReference cell, SheetIdentifier sheet) {
String sheetName = sheet._sheetIdentifier.getName();
if (sheet._bookName != null) {
int bookIndex = resolveBookIndex(sheet._bookName);
return new Ref3DPxg(bookIndex, sheetName, cell);
} else {
return new Ref3DPxg(sheetName, cell);
}
}
public Ptg get3DReferencePtg(AreaReference area, SheetIdentifier sheet) {
// TODO Implement properly
return new Area3DPtg(area, getExternalSheetIndex(sheet._sheetIdentifier.getName()));
}
public String resolveNameXText(NameXPtg n) {
int idx = n.getNameIndex();

View File

@ -27,7 +27,7 @@ import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.formula.ptg.FuncPtg;
import org.apache.poi.ss.formula.ptg.IntPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
import org.apache.poi.ss.formula.ptg.RefPtg;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.junit.Ignore;
@ -106,9 +106,9 @@ public final class TestXSSFFormulaParser {
// Reference to a single cell in a different sheet
ptgs = parse(fpb, "Uses!A1");
assertEquals(1, ptgs.length);
assertEquals(Ref3DPtg.class, ptgs[0].getClass());
assertEquals("A1", ((Ref3DPtg)ptgs[0]).format2DRefAsString());
assertEquals("Uses!A1", ((Ref3DPtg)ptgs[0]).toFormulaString(fpb));
assertEquals(Ref3DPxg.class, ptgs[0].getClass());
assertEquals("A1", ((Ref3DPxg)ptgs[0]).format2DRefAsString());
assertEquals("Uses!A1", ((Ref3DPxg)ptgs[0]).toFormulaString());
// Reference to a sheet scoped named range from another sheet
ptgs = parse(fpb, "Defines!NR_To_A1");