From b8329a9693a876ecaf40f25847b3f61908a46444 Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Wed, 19 Nov 2008 02:01:58 +0000 Subject: [PATCH] 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 --- .../poi/hssf/record/formula/AreaPtgBase.java | 4 +- .../poi/hssf/usermodel/HSSFWorkbook.java | 43 +++++++------ .../apache/poi/ss/formula/FormulaParser.java | 62 +++++++++++++++++-- .../ss/formula/OperandClassTransformer.java | 8 ++- .../org/apache/poi/ss/formula/ParseNode.java | 21 ++++++- .../apache/poi/xssf/usermodel/XSSFName.java | 2 +- .../poi/xssf/usermodel/XSSFWorkbook.java | 60 +++++++++++++++--- .../poi/hssf/model/TestFormulaParser.java | 33 +++++++++- .../poi/hssf/record/cf/TestCellRange.java | 6 ++ .../poi/hssf/usermodel/TestNamedRange.java | 16 ++--- 10 files changed, 203 insertions(+), 52 deletions(-) diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java index d6f2b44aaf..ded679ada4 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java @@ -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()); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index cb56fa2798..3b47ea31ba 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -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() { diff --git a/src/java/org/apache/poi/ss/formula/FormulaParser.java b/src/java/org/apache/poi/ss/formula/FormulaParser.java index 1398399ff4..e3e8749101 100644 --- a/src/java/org/apache/poi/ss/formula/FormulaParser.java +++ b/src/java/org/apache/poi/ss/formula/FormulaParser.java @@ -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 ! 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 temp = new ArrayList(2); SkipWhite(); if(look == ')') { return ParseNode.EMPTY_ARRAY; @@ -676,7 +710,7 @@ public final class FormulaParser { private ParseNode parseArray() { - List rowsData = new ArrayList(); + List rowsData = new ArrayList(); while(true) { Object[] singleRowData = parseArrayRow(); rowsData.add(singleRowData); @@ -707,7 +741,7 @@ public final class FormulaParser { } private Object[] parseArrayRow() { - List temp = new ArrayList(); + List temp = new ArrayList(); 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) diff --git a/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java b/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java index 91226bd53b..2bf8cadca6 100644 --- a/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java +++ b/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java @@ -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.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; -import org.apache.poi.hssf.record.formula.Ptg; /** * 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 diff --git a/src/java/org/apache/poi/ss/formula/ParseNode.java b/src/java/org/apache/poi/ss/formula/ParseNode.java index 75685dee64..83e2b08175 100644 --- a/src/java/org/apache/poi/ss/formula/ParseNode.java +++ b/src/java/org/apache/poi/ss/formula/ParseNode.java @@ -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 Ptg 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) { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java index ebdfdd7238..2be7cffa59 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java @@ -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; } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index efa2d1289d..63f9152b89 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -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 { + 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, Iterable0) { + 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