Initial support for union operator in FormulaParser (fix for broken junits)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@718838 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-11-19 02:01:58 +00:00
parent 65af4a310a
commit 6f28f85004
10 changed files with 204 additions and 53 deletions

View File

@ -60,9 +60,9 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
CellReference firstCell = ar.getFirstCell();
CellReference lastCell = ar.getLastCell();
setFirstRow(firstCell.getRow());
setFirstColumn(firstCell.getCol());
setFirstColumn(firstCell.getCol() == -1 ? 0 : firstCell.getCol());
setLastRow(lastCell.getRow());
setLastColumn(lastCell.getCol());
setLastColumn(lastCell.getCol() == -1 ? 0xFF : lastCell.getCol());
setFirstColRelative(!firstCell.isColAbsolute());
setLastColRelative(!lastCell.isColAbsolute());
setFirstRowRelative(!firstCell.isRowAbsolute());

View File

@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.poi.POIDocument;
import org.apache.poi.ddf.EscherBSERecord;
@ -78,10 +79,10 @@ import org.apache.poi.util.POILogger;
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
* @author Shawn Laubach (slaubach at apache dot org)
* @version 2.0-pre
*
*/
public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook
{
public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
private static final int MAX_ROW = 0xFFFF;
private static final short MAX_COLUMN = (short)0x00FF;
@ -802,8 +803,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
}
public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
return workbook.getSheetIndexFromExternSheetIndex(externSheetNumber);
}
return workbook.getSheetIndexFromExternSheetIndex(externSheetNumber);
}
private HSSFSheet[] getSheets() {
HSSFSheet[] result = new HSSFSheet[_sheets.size()];
@ -811,7 +812,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
return result;
}
/**
/**
* Get the HSSFSheet object at the given index.
* @param index of the sheet number (0-based physical & logical)
* @return HSSFSheet at the provided index
@ -1340,9 +1341,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
return result;
}
public NameRecord getNameRecord(int nameIndex) {
return getWorkbook().getNameRecord(nameIndex);
}
public NameRecord getNameRecord(int nameIndex) {
return getWorkbook().getNameRecord(nameIndex);
}
/** gets the named range name
* @param index the named range index (0 based)
@ -1367,13 +1368,19 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
if (name == null) {
name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
// adding one here because 0 indicates a global named region; doesn't make sense for print areas
}
name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
// adding one here because 0 indicates a global named region; doesn't make sense for print areas
}
String[] parts = COMMA_PATTERN.split(reference);
StringBuffer sb = new StringBuffer(32);
SheetNameFormatter.appendFormat(sb, getSheetName(sheetIndex));
sb.append("!");
sb.append(reference);
for (int i = 0; i < parts.length; i++) {
if(i>0) {
sb.append(",");
}
SheetNameFormatter.appendFormat(sb, getSheetName(sheetIndex));
sb.append("!");
sb.append(parts[i]);
}
name.setNameDefinition(HSSFFormulaParser.parse(sb.toString(), this));
}
@ -1409,8 +1416,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
//adding one here because 0 indicates a global named region; doesn't make sense for print areas
if (name == null) {
return null;
}
return null;
}
return HSSFFormulaParser.toFormulaString(this, name.getNameDefinition());
}
@ -1721,7 +1728,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
}
public CreationHelper getCreationHelper() {
return new HSSFCreationHelper(this);
return new HSSFCreationHelper(this);
}
private static byte[] newUID() {

View File

@ -39,6 +39,7 @@ import org.apache.poi.hssf.record.formula.GreaterThanPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.LessEqualPtg;
import org.apache.poi.hssf.record.formula.LessThanPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg;
import org.apache.poi.hssf.record.formula.MultiplyPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
@ -56,6 +57,7 @@ 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.record.formula.UnionPtg;
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
@ -370,6 +372,11 @@ public final class FormulaParser {
// TODO - handle <book name> ! <named range name>
int externIdx = getExternalSheetIndex(iden.getName());
String secondIden = parseUnquotedIdentifier();
if (isRowOrCol(secondIden) && look == ':') {
GetChar();
String thirdIden = parseUnquotedIdentifier();
return new Area3DPtg(secondIden + ":" + thirdIden, externIdx);
}
AreaReference areaRef = parseArea(secondIden);
if (areaRef == null) {
return new Ref3DPtg(secondIden, externIdx);
@ -418,6 +425,33 @@ public final class FormulaParser {
+ name + "' is not a range as expected");
}
private static boolean isRowOrCol(String str) {
int i=0;
if (str.charAt(i) == '$') {
i++;
}
if (IsDigit(str.charAt(i))) {
while (i<str.length()) {
if (!IsDigit(str.charAt(i))) {
return false;
}
i++;
}
return true;
}
if (IsAlpha(str.charAt(i))) {
while (i<str.length()) {
if (!IsAlpha(str.charAt(i))) {
return false;
}
i++;
}
return true;
}
return false;
}
private int getExternalSheetIndex(String name) {
if (name.charAt(0) == '[') {
// we have a sheet name qualified with workbook name e.g. '[MyData.xls]Sheet1'
@ -578,7 +612,7 @@ public final class FormulaParser {
/** get arguments to a function */
private ParseNode[] Arguments() {
//average 2 args per function
List temp = new ArrayList(2);
List<ParseNode> temp = new ArrayList<ParseNode>(2);
SkipWhite();
if(look == ')') {
return ParseNode.EMPTY_ARRAY;
@ -676,7 +710,7 @@ public final class FormulaParser {
private ParseNode parseArray() {
List rowsData = new ArrayList();
List<Object[]> rowsData = new ArrayList<Object[]>();
while(true) {
Object[] singleRowData = parseArrayRow();
rowsData.add(singleRowData);
@ -707,7 +741,7 @@ public final class FormulaParser {
}
private Object[] parseArrayRow() {
List temp = new ArrayList();
List<Object> temp = new ArrayList<Object>();
while (true) {
temp.add(parseArrayItem());
SkipWhite();
@ -937,6 +971,26 @@ public final class FormulaParser {
result = new ParseNode(operator, result, other);
}
}
private ParseNode unionExpression() {
ParseNode result = comparisonExpression();
boolean hasUnions = false;
while (true) {
SkipWhite();
switch(look) {
case ',':
GetChar();
hasUnions = true;
ParseNode other = comparisonExpression();
result = new ParseNode(UnionPtg.instance, result, other);
continue;
}
if (hasUnions) {
MemFuncPtg memFuncPtg = new MemFuncPtg(result.getEncodedSize());
result = new ParseNode(memFuncPtg, result);
}
return result;
}
}
private ParseNode comparisonExpression() {
ParseNode result = concatExpression();
@ -1040,7 +1094,7 @@ end;
private void parse() {
pointer=0;
GetChar();
_rootNode = comparisonExpression();
_rootNode = unionExpression();
if(pointer <= formulaLength) {
String msg = "Unused input [" + formulaString.substring(pointer-1)

View File

@ -19,9 +19,11 @@ 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.MemFuncPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RangePtg;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
/**
* This class performs 'operand class' transformation. Non-base tokens are classified into three
@ -98,7 +100,9 @@ final class OperandClassTransformer {
return;
}
if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) {
if (token instanceof ValueOperatorPtg || token instanceof ControlPtg
|| token instanceof MemFuncPtg
|| token instanceof UnionPtg) {
// Value Operator Ptgs and Control are base tokens, so token will be unchanged
// but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag

View File

@ -17,8 +17,10 @@
package org.apache.poi.ss.formula;
import org.apache.poi.hssf.record.formula.ArrayPtg;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
/**
@ -62,6 +64,13 @@ final class ParseNode {
private int getTokenCount() {
return _tokenCount;
}
public int getEncodedSize() {
int result = _token instanceof ArrayPtg ? ArrayPtg.PLAIN_TOKEN_SIZE : _token.getSize();
for (int i = 0; i < _children.length; i++) {
result += _children[i].getEncodedSize();
}
return result;
}
/**
* Collects the array of <tt>Ptg</tt> tokens for the specified tree.
@ -72,14 +81,20 @@ final class ParseNode {
return temp.getResult();
}
private void collectPtgs(TokenCollector temp) {
if (isIf(getToken())) {
if (isIf(_token)) {
collectIfPtgs(temp);
return;
}
boolean isPreFixOperator = _token instanceof MemFuncPtg;
if (isPreFixOperator) {
temp.add(_token);
}
for (int i=0; i< getChildren().length; i++) {
getChildren()[i].collectPtgs(temp);
}
temp.add(getToken());
if (!isPreFixOperator) {
temp.add(_token);
}
}
/**
* The IF() function gets marked up with two or three tAttr tokens.
@ -136,7 +151,7 @@ final class ParseNode {
temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
}
temp.add(getToken());
temp.add(_token);
}
private static boolean isIf(Ptg token) {

View File

@ -188,7 +188,7 @@ public final class XSSFName implements Name {
ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.CELL); // TODO - use type NAMEDRANGE
} catch (RuntimeException e) {
if (e.getClass().getName().startsWith(FormulaParser.class.getName())) {
throw new IllegalArgumentException("Unparsable formula '" + formulaText + "'");
throw new IllegalArgumentException("Unparsable formula '" + formulaText + "'", e);
}
throw e;
}

View File

@ -17,30 +17,59 @@
package org.apache.poi.xssf.usermodel;
import java.io.*;
import java.util.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.hssf.record.formula.SheetNameFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.PackageHelper;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.model.*;
import org.apache.poi.POIXMLException;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlException;
import org.openxml4j.exceptions.OpenXML4JException;
import org.openxml4j.opc.*;
import org.openxml4j.opc.Package;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
import org.openxml4j.opc.PackagePart;
import org.openxml4j.opc.PackagePartName;
import org.openxml4j.opc.PackageRelationship;
import org.openxml4j.opc.PackageRelationshipTypes;
import org.openxml4j.opc.PackagingURIHelper;
import org.openxml4j.opc.TargetMode;
import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookView;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookViews;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedNames;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDialogsheet;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STSheetState;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument;
/**
* High level representation of a SpreadsheetML workbook. This is the first object most users
@ -48,6 +77,7 @@ import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelations
* top level object for creating new sheets/etc.
*/
public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<XSSFSheet> {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
/**
* Width of one character of the default font in pixels. Same for Calibry and Arial.
@ -831,7 +861,17 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
}
//short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
//name.setExternSheetNumber(externSheetIndex);
name.setReference(reference);
String[] parts = COMMA_PATTERN.split(reference);
StringBuffer sb = new StringBuffer(32);
for (int i = 0; i < parts.length; i++) {
if(i>0) {
sb.append(",");
}
SheetNameFormatter.appendFormat(sb, getSheetName(sheetIndex));
sb.append("!");
sb.append(parts[i]);
}
name.setFormula(sb.toString());
}
/**
@ -922,7 +962,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
CellReference colRef = new CellReference(sheetName, startR, startC, true, true);
CellReference colRef2 = new CellReference(sheetName, endR, endC, true, true);
return "'" + sheetName + "'!$" + colRef.getCellRefParts()[2] + "$" + colRef.getCellRefParts()[1] + ":$" + colRef2.getCellRefParts()[2] + "$" + colRef2.getCellRefParts()[1];
return "$" + colRef.getCellRefParts()[2] + "$" + colRef.getCellRefParts()[1] + ":$" + colRef2.getCellRefParts()[2] + "$" + colRef2.getCellRefParts()[1];
}
private XSSFName getBuiltInName(String builtInCode, int sheetNumber) {

View File

@ -24,6 +24,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.constant.ErrorConstant;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaI;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.ArrayPtg;
@ -36,6 +37,7 @@ import org.apache.poi.hssf.record.formula.ErrPtg;
import org.apache.poi.hssf.record.formula.FuncPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg;
import org.apache.poi.hssf.record.formula.MultiplyPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
@ -49,6 +51,7 @@ 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.record.formula.UnionPtg;
import org.apache.poi.hssf.usermodel.FormulaExtractor;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
@ -57,6 +60,7 @@ import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.FormulaParser;
import org.apache.poi.ss.formula.FormulaParserTestHelper;
/**
@ -509,6 +513,10 @@ public final class TestFormulaParser extends TestCase {
/* package */ static Ptg[] confirmTokenClasses(String formula, Class[] expectedClasses) {
Ptg[] ptgs = parseFormula(formula);
confirmTokenClasses(ptgs, expectedClasses);
return ptgs;
}
private static void confirmTokenClasses(Ptg[] ptgs, Class[] expectedClasses) {
assertEquals(expectedClasses.length, ptgs.length);
for (int i = 0; i < expectedClasses.length; i++) {
if(expectedClasses[i] != ptgs[i].getClass()) {
@ -517,7 +525,6 @@ public final class TestFormulaParser extends TestCase {
+ ptgs[i].getClass().getName() + ")");
}
}
return ptgs;
}
public void testPower() {
@ -818,7 +825,7 @@ public final class TestFormulaParser extends TestCase {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFName name = wb.createName();
name.setReference("Sheet1!B1");
name.setFormula("Sheet1!B1");
name.setNameName("pfy1");
Ptg[] ptgs;
@ -944,4 +951,26 @@ public final class TestFormulaParser extends TestCase {
assertEquals(expectedExternSheetIndex, ((Ref3DPtg)ptg0).getExternSheetIndex());
}
public void testUnion() {
String formula = "Sheet1!$B$2:$C$3,OFFSET(Sheet1!$E$2:$E$4,1,Sheet1!$A$1),Sheet1!$D$6";
HSSFWorkbook wb = new HSSFWorkbook();
wb.createSheet("Sheet1");
Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb));
Class[] expectedClasses = {
// TODO - AttrPtg.class, // Excel prepends this
MemFuncPtg.class,
Area3DPtg.class,
Area3DPtg.class,
IntPtg.class,
Ref3DPtg.class,
FuncVarPtg.class,
UnionPtg.class,
Ref3DPtg.class,
UnionPtg.class,
};
confirmTokenClasses(ptgs, expectedClasses);
MemFuncPtg mf = (MemFuncPtg)ptgs[0];
assertEquals(45, mf.getLenRefSubexpression());
}
}

View File

@ -178,4 +178,10 @@ public final class TestCellRange extends TestCase
assertEquals("isFullRowRange", isFullRow, cr.isFullRowRange());
assertEquals("isFullColumnRange", isFullColumn, cr.isFullColumnRange());
}
public void testNumberOfCells() {
assertEquals(1, oneCell.getNumberOfCells());
assertEquals(100, box9x9.getNumberOfCells());
assertEquals(121, box10to20c.getNumberOfCells());
}
}

View File

@ -355,16 +355,12 @@ public final class TestNamedRange extends TestCase {
public void testPrintAreaUnion(){
HSSFWorkbook workbook = new HSSFWorkbook();
workbook.createSheet("Test Print Area");
String sheetName = workbook.getSheetName(0);
if (false) { // TODO - fix formula parser to support unions
String reference = "'" + sheetName + "'!$A$1:$B$1,'" + sheetName + "'!$D$1:$F$2";
workbook.setPrintArea(0, reference);
String retrievedPrintArea = workbook.getPrintArea(0);
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
assertEquals(reference, retrievedPrintArea);
}
String reference = "$A$1:$B$1,$D$1:$F$2";
workbook.setPrintArea(0, reference);
String retrievedPrintArea = workbook.getPrintArea(0);
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
assertEquals("'Test Print Area'!$A$1:$B$1,'Test Print Area'!$D$1:$F$2", retrievedPrintArea);
}
/**