You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by jo...@apache.org on 2008/09/09 22:25:17 UTC

svn commit: r693591 - in /poi/trunk/src: java/org/apache/poi/hssf/model/ java/org/apache/poi/hssf/record/formula/ testcases/org/apache/poi/hssf/data/ testcases/org/apache/poi/hssf/model/ testcases/org/apache/poi/hssf/record/formula/

Author: josh
Date: Tue Sep  9 13:25:16 2008
New Revision: 693591

URL: http://svn.apache.org/viewvc?rev=693591&view=rev
Log:
Added support for parsing array constants in formulas. (Helping investigation for bug 45752)

Modified:
    poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java
    poi/trunk/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
    poi/trunk/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java
    poi/trunk/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java
    poi/trunk/src/java/org/apache/poi/hssf/record/formula/Ptg.java
    poi/trunk/src/testcases/org/apache/poi/hssf/data/testRVA.xls
    poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
    poi/trunk/src/testcases/org/apache/poi/hssf/model/TestRVA.java
    poi/trunk/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java Tue Sep  9 13:25:16 2008
@@ -22,9 +22,12 @@
 import java.util.Stack;
 
 //import PTGs .. since we need everything, import *
+import org.apache.poi.hssf.record.UnicodeString;
+import org.apache.poi.hssf.record.constant.ErrorConstant;
 import org.apache.poi.hssf.record.formula.*;
 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;
 import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFName;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
@@ -69,9 +72,9 @@
     public static final int FORMULA_TYPE_ARRAY =2;
     public static final int FORMULA_TYPE_CONDFORMAT = 3;
     public static final int FORMULA_TYPE_NAMEDRANGE = 4;
-    // this constant is currently very specific.  The exact differences from general data 
+    // this constant is currently very specific.  The exact differences from general data
     // validation formulas or conditional format formulas is not known yet
-    public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;    
+    public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;
 
     private final String formulaString;
     private final int formulaLength;
@@ -139,9 +142,9 @@
     /** Report What Was Expected */
     private RuntimeException expected(String s) {
         String msg;
-        
+
         if (look == '=' && formulaString.substring(0, pointer-1).trim().length() < 1) {
-            msg = "The specified formula '" + formulaString 
+            msg = "The specified formula '" + formulaString
                 + "' starts with an equals sign which is not allowed.";
         } else {
             msg = "Parse error near char " + (pointer-1) + " '" + look + "'"
@@ -193,8 +196,8 @@
     /**
      * 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
-     * which may need to be converted to an area reference.  For example, this method 
-     * may return a value like "A1..B2", in which case the caller must convert it to 
+     * which may need to be converted to an area reference.  For example, this method
+     * 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() {
@@ -250,7 +253,7 @@
     }
 
     private Ptg parseNameOrReference(String name) {
-        
+
         AreaReference areaRef = parseArea(name);
         if (areaRef != null) {
             // will happen if dots are used instead of colon
@@ -372,29 +375,29 @@
     private ParseNode function(String name) {
         Ptg nameToken = null;
         if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) {
-        	// user defined function
+            // user defined function
             // in the token tree, the name is more or less the first argument
-        	
-        
-        	int nameIndex = book.getNameIndex(name);
-        	if (nameIndex >= 0) {
-        		HSSFName hName = book.getNameAt(nameIndex);
-        		if (!hName.isFunctionName()) {
-        			throw new FormulaParseException("Attempt to use name '" + name 
-        					+ "' as a function, but defined name in workbook does not refer to a function");
-        		}
-        		
-        		// calls to user-defined functions within the workbook 
-        		// get a Name token which points to a defined name record
-        		nameToken = new NamePtg(name, this.book);
-        	} else {
-        		
-        		nameToken = book.getNameXPtg(name);
-        		if (nameToken == null) {
-        			throw new FormulaParseException("Name '" + name 
-        					+ "' is completely unknown in the current workbook");
-        		}
-        	}
+
+
+            int nameIndex = book.getNameIndex(name);
+            if (nameIndex >= 0) {
+                HSSFName hName = book.getNameAt(nameIndex);
+                if (!hName.isFunctionName()) {
+                    throw new FormulaParseException("Attempt to use name '" + name
+                            + "' as a function, but defined name in workbook does not refer to a function");
+                }
+
+                // calls to user-defined functions within the workbook
+                // get a Name token which points to a defined name record
+                nameToken = new NamePtg(name, this.book);
+            } else {
+
+                nameToken = book.getNameXPtg(name);
+                if (nameToken == null) {
+                    throw new FormulaParseException("Name '" + name
+                            + "' is completely unknown in the current workbook");
+                }
+            }
         }
 
         Match('(');
@@ -542,7 +545,7 @@
         SkipWhite();
         switch(look) {
             case '#':
-                return new ParseNode(parseErrorLiteral());
+                return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
             case '-':
                 Match('-');
                 return new ParseNode(UnaryMinusPtg.instance, powerFactor());
@@ -555,7 +558,12 @@
                 Match(')');
                 return new ParseNode(ParenthesisPtg.instance, inside);
             case '"':
-                return new ParseNode(parseStringLiteral());
+                return new ParseNode(new StringPtg(parseStringLiteral()));
+            case '{':
+                Match('{');
+                ParseNode arrayNode = parseArray();
+                Match('}');
+                return arrayNode;
         }
         if (IsAlpha(look) || look == '\''){
             return parseFunctionReferenceOrName();
@@ -565,6 +573,95 @@
     }
 
 
+    private ParseNode parseArray() {
+        List rowsData = new ArrayList();
+        while(true) {
+            Object[] singleRowData = parseArrayRow();
+            rowsData.add(singleRowData);
+            if (look == '}') {
+                break;
+            }
+            if (look != ';') {
+                throw expected("'}' or ';'");
+            }
+            Match(';');
+        }
+        int nRows = rowsData.size();
+        Object[][] values2d = new Object[nRows][];
+        rowsData.toArray(values2d);
+        int nColumns = values2d[0].length;
+        checkRowLengths(values2d, nColumns);
+
+        return new ParseNode(new ArrayPtg(values2d));
+    }
+    private void checkRowLengths(Object[][] values2d, int nColumns) {
+        for (int i = 0; i < values2d.length; i++) {
+            int rowLen = values2d[i].length;
+            if (rowLen != nColumns) {
+                throw new FormulaParseException("Array row " + i + " has length " + rowLen
+                        + " but row 0 has length " + nColumns);
+            }
+        }
+    }
+
+    private Object[] parseArrayRow() {
+        List temp = new ArrayList();
+        while (true) {
+            temp.add(parseArrayItem());
+            SkipWhite();
+            switch(look) {
+                case '}':
+                case ';':
+                    break;
+                case ',':
+                    Match(',');
+                    continue;
+                default:
+                    throw expected("'}' or ','");
+
+            }
+            break;
+        }
+
+        Object[] result = new Object[temp.size()];
+        temp.toArray(result);
+        return result;
+    }
+
+    private Object parseArrayItem() {
+        SkipWhite();
+        switch(look) {
+            case '"': return new UnicodeString(parseStringLiteral());
+            case '#': return ErrorConstant.valueOf(parseErrorLiteral());
+            case 'F': case 'f':
+            case 'T': case 't':
+                return parseBooleanLiteral();
+        }
+        // else assume number
+        return convertArrayNumber(parseNumber());
+    }
+
+    private Boolean parseBooleanLiteral() {
+        String iden = parseIdentifier();
+        if ("TRUE".equalsIgnoreCase(iden)) {
+            return Boolean.TRUE;
+        }
+        if ("FALSE".equalsIgnoreCase(iden)) {
+            return Boolean.FALSE;
+        }
+        throw expected("'TRUE' or 'FALSE'");
+    }
+
+    private static Double convertArrayNumber(Ptg ptg) {
+        if (ptg instanceof IntPtg) {
+            return new Double(((IntPtg)ptg).getValue());
+        }
+        if (ptg instanceof NumberPtg) {
+            return new Double(((NumberPtg)ptg).getValue());
+        }
+        throw new RuntimeException("Unexpected ptg (" + ptg.getClass().getName() + ")");
+    }
+
     private Ptg parseNumber() {
         String number2 = null;
         String exponent = null;
@@ -601,7 +698,7 @@
     }
 
 
-    private ErrPtg parseErrorLiteral() {
+    private int parseErrorLiteral() {
         Match('#');
         String part1 = parseIdentifier().toUpperCase();
 
@@ -609,13 +706,13 @@
             case 'V':
                 if(part1.equals("VALUE")) {
                     Match('!');
-                    return ErrPtg.VALUE_INVALID;
+                    return HSSFErrorConstants.ERROR_VALUE;
                 }
                 throw expected("#VALUE!");
             case 'R':
                 if(part1.equals("REF")) {
                     Match('!');
-                    return ErrPtg.REF_INVALID;
+                    return HSSFErrorConstants.ERROR_REF;
                 }
                 throw expected("#REF!");
             case 'D':
@@ -623,21 +720,21 @@
                     Match('/');
                     Match('0');
                     Match('!');
-                    return ErrPtg.DIV_ZERO;
+                    return HSSFErrorConstants.ERROR_DIV_0;
                 }
                 throw expected("#DIV/0!");
             case 'N':
                 if(part1.equals("NAME")) {
                     Match('?');  // only one that ends in '?'
-                    return ErrPtg.NAME_INVALID;
+                    return HSSFErrorConstants.ERROR_NAME;
                 }
                 if(part1.equals("NUM")) {
                     Match('!');
-                    return ErrPtg.NUM_ERROR;
+                    return HSSFErrorConstants.ERROR_NUM;
                 }
                 if(part1.equals("NULL")) {
                     Match('!');
-                    return ErrPtg.NULL_INTERSECTION;
+                    return HSSFErrorConstants.ERROR_NULL;
                 }
                 if(part1.equals("N")) {
                     Match('/');
@@ -646,7 +743,7 @@
                     }
                     Match(look);
                     // Note - no '!' or '?' suffix
-                    return ErrPtg.N_A;
+                    return HSSFErrorConstants.ERROR_NA;
                 }
                 throw expected("#NAME?, #NUM!, #NULL! or #N/A");
 
@@ -699,7 +796,7 @@
     }
 
 
-    private StringPtg parseStringLiteral() {
+    private String parseStringLiteral() {
         Match('"');
 
         StringBuffer token = new StringBuffer();
@@ -713,7 +810,7 @@
             token.append(look);
             GetChar();
         }
-        return new StringPtg(token.toString());
+        return token.toString();
     }
 
     /** Parse and Translate a Math Term */
@@ -970,7 +1067,7 @@
         }
         return result;
     }
-    
+
     private static String[] getOperands(Stack stack, int nOperands) {
         String[] operands = new String[nOperands];
 

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java Tue Sep  9 13:25:16 2008
@@ -74,7 +74,7 @@
 						+ _formulaType + ") not supported yet");
 		
 		}
-		transformNode(rootNode, rootNodeOperandClass, false, false);
+		transformNode(rootNode, rootNodeOperandClass, false);
 	}
 
 	/**
@@ -83,22 +83,35 @@
 	 * the function return value).
 	 */
 	private void transformNode(ParseNode node, byte desiredOperandClass,
-			boolean callerForceArrayFlag, boolean isDirectChildOfValueOperator) {
+			boolean callerForceArrayFlag) {
 		Ptg token = node.getToken();
 		ParseNode[] children = node.getChildren();
+		boolean isSimpleValueFunc = isSimpleValueFunction(token);
+		
+		if (isSimpleValueFunc) {
+			boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY;
+			for (int i = 0; i < children.length; i++) {
+				transformNode(children[i], desiredOperandClass, localForceArray);
+			}
+			setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag);
+			return;
+		}
+		
 		if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) {
 			// Value Operator Ptgs and Control are base tokens, so token will be unchanged
-			
 			// but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
+			
+			// As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
+			// All direct operands of value operators that are initially 'R' type will 
+			// be converted to 'V' type.
+			byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass;
 			for (int i = 0; i < children.length; i++) {
-				ParseNode child = children[i];
-				transformNode(child, desiredOperandClass, callerForceArrayFlag, true);
+				transformNode(children[i], localDesiredOperandClass, callerForceArrayFlag);
 			}
 			return;
 		}
 		if (token instanceof AbstractFunctionPtg) {
-			transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass,
-					callerForceArrayFlag);
+			transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, callerForceArrayFlag);
 			return;
 		}
 		if (children.length > 0) {
@@ -109,15 +122,24 @@
 			// nothing to do
 			return;
 		}
-		if (isDirectChildOfValueOperator) {
-			// As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
-			// All direct operands of value operators that are initially 'R' type will 
-			// be converted to 'V' type.
-			if (token.getPtgClass() == Ptg.CLASS_REF) {
-				token.setClass(Ptg.CLASS_VALUE); 
+		token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
+	}
+
+	private static boolean isSimpleValueFunction(Ptg token) {
+		if (token instanceof AbstractFunctionPtg) {
+			AbstractFunctionPtg aptg = (AbstractFunctionPtg) token;
+			if (aptg.getDefaultOperandClass() != Ptg.CLASS_VALUE) {
+				return false;
+			}
+			int numberOfOperands = aptg.getNumberOfOperands();
+			for (int i=numberOfOperands-1; i>=0; i--) {
+				if (aptg.getParameterClass(i) != Ptg.CLASS_VALUE) {
+					return false;
+				}
 			}
+			return true;
 		}
-		token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
+		return false;
 	}
 
 	private byte transformClass(byte currentOperandClass, byte desiredOperandClass,
@@ -185,6 +207,7 @@
 						switch (defaultReturnOperandClass) {
 							case Ptg.CLASS_REF:
 								afp.setClass(Ptg.CLASS_REF);
+//								afp.setClass(Ptg.CLASS_ARRAY);
 								break;
 							case Ptg.CLASS_VALUE:
 								afp.setClass(Ptg.CLASS_ARRAY);
@@ -220,7 +243,17 @@
 		for (int i = 0; i < children.length; i++) {
 			ParseNode child = children[i];
 			byte paramOperandClass = afp.getParameterClass(i);
-			transformNode(child, paramOperandClass, localForceArrayFlag, false);
+			transformNode(child, paramOperandClass, localForceArrayFlag);
+		}
+	}
+
+	private void setSimpleValueFuncClass(AbstractFunctionPtg afp,
+			byte desiredOperandClass, boolean callerForceArrayFlag) {
+
+		if (callerForceArrayFlag  || desiredOperandClass == Ptg.CLASS_ARRAY) {
+			afp.setClass(Ptg.CLASS_ARRAY);
+		} else {
+			afp.setClass(Ptg.CLASS_VALUE); 
 		}
 	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java Tue Sep  9 13:25:16 2008
@@ -44,8 +44,11 @@
 	 * (not including the data which comes after all formula tokens)
 	 */
 	public static final int PLAIN_TOKEN_SIZE = 1+RESERVED_FIELD_LEN;
+
+	private static final byte[] DEFAULT_RESERVED_DATA = new byte[RESERVED_FIELD_LEN];
+
 	// TODO - fix up field visibility and subclasses
-	private byte[] field_1_reserved;
+	private final byte[] field_1_reserved;
 	
 	// data from these fields comes after the Ptg data of all tokens in current formula
 	private short  token_1_columns;
@@ -59,8 +62,42 @@
 			field_1_reserved[i] = in.readByte();
 		}
 	}
-	public Object[] getTokenArrayValues() {
-		return (Object[]) token_3_arrayValues.clone();
+	/**
+	 * @param values2d array values arranged in rows
+	 */
+	public ArrayPtg(Object[][] values2d) {
+		int nColumns = values2d[0].length;
+		int nRows = values2d.length;
+		// convert 2-d to 1-d array (row by row according to getValueIndex())
+		token_1_columns = (short) nColumns;
+		token_2_rows = (short) nRows;
+
+		Object[] vv = new Object[token_1_columns * token_2_rows];
+		for (int r=0; r<nRows; r++) {
+			Object[] rowData = values2d[r];
+			for (int c=0; c<nColumns; c++) {
+				vv[getValueIndex(c, r)] = rowData[c];
+			}
+		}
+		
+		token_3_arrayValues = vv;
+		field_1_reserved = DEFAULT_RESERVED_DATA;
+	}
+	/**
+	 * @return 2-d array (inner index is rowIx, outer index is colIx)
+	 */
+	public Object[][] getTokenArrayValues() {
+		if (token_3_arrayValues == null) {
+			throw new IllegalStateException("array values not read yet");
+		}
+		Object[][] result = new Object[token_2_rows][token_1_columns];
+		for (int r = 0; r < token_2_rows; r++) {
+			Object[] rowData = result[r];
+			for (int c = 0; c < token_1_columns; c++) {
+				rowData[c] = token_3_arrayValues[getValueIndex(c, r)];
+			}
+		}
+		return result;
 	}
 	
 	public boolean isBaseToken() {
@@ -88,27 +125,21 @@
 		token_3_arrayValues = ConstantValueParser.parse(in, totalCount);
 	}
 
-	public String toString()
-	{
-		StringBuffer buffer = new StringBuffer("[ArrayPtg]\n");
+	public String toString() {
+		StringBuffer sb = new StringBuffer("[ArrayPtg]\n");
 
-		buffer.append("columns = ").append(getColumnCount()).append("\n");
-		buffer.append("rows = ").append(getRowCount()).append("\n");
+		sb.append("nRows = ").append(getRowCount()).append("\n");
+		sb.append("nCols = ").append(getColumnCount()).append("\n");
 		if (token_3_arrayValues == null) {
-			buffer.append("  #values#uninitialised#\n");
+			sb.append("  #values#uninitialised#\n");
 		} else {
-			for (int x=0;x<getColumnCount();x++) {
-				for (int y=0;y<getRowCount();y++) {
-					Object o = token_3_arrayValues[getValueIndex(x, y)];
-					buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n"); 
-				}
-			}
+			sb.append("  ").append(formatAsString());
 		}
-		return buffer.toString();
+		return sb.toString();
 	}
 
 	/**
-	 * Note - (2D) array elements are stored column by column 
+	 * Note - (2D) array elements are stored row by row
 	 * @return the index into the internal 1D array for the specified column and row
 	 */
 	/* package */ int getValueIndex(int colIx, int rowIx) {
@@ -120,7 +151,7 @@
 			throw new IllegalArgumentException("Specified rowIx (" + rowIx 
 					+ ") is outside the allowed range (0.." + (token_2_rows-1) + ")");
 		}
-		return rowIx + token_2_rows * colIx;
+		return rowIx * token_1_columns + colIx;
 	}
 
 	public void writeBytes(byte[] data, int offset) {
@@ -153,16 +184,15 @@
 			+ ConstantValueParser.getEncodedSize(token_3_arrayValues);
 	}
 
-	public String toFormulaString(HSSFWorkbook book)
-	{
+	public String formatAsString() {
 		StringBuffer b = new StringBuffer();
 		b.append("{");
-		for (int x=0;x<getColumnCount();x++) {
-		  	if (x > 0) {
+	  	for (int y=0;y<getRowCount();y++) {
+			if (y > 0) {
 				b.append(";");
 			}
-		  	for (int y=0;y<getRowCount();y++) {
-				if (y > 0) {
+			for (int x=0;x<getColumnCount();x++) {
+			  	if (x > 0) {
 					b.append(",");
 				}
 		  		Object o = token_3_arrayValues[getValueIndex(x, y)];
@@ -172,11 +202,14 @@
 		b.append("}");
 		return b.toString();
 	}
+	public String toFormulaString(HSSFWorkbook book) {
+		return formatAsString();
+	}
 	
 	private static String getConstantText(Object o) {
 
 		if (o == null) {
-			return ""; // TODO - how is 'empty value' represented in formulas?
+			throw new RuntimeException("Array item cannot be null");
 		}
 		if (o instanceof UnicodeString) {
 			return "\"" + ((UnicodeString)o).getString() + "\"";
@@ -196,11 +229,4 @@
 	public byte getDefaultOperandClass() {
 		return Ptg.CLASS_ARRAY;
 	}
-	
-	public Object clone() {
-	  ArrayPtg ptg = (ArrayPtg) super.clone();
-	  ptg.field_1_reserved = (byte[]) field_1_reserved.clone();
-	  ptg.token_3_arrayValues = (Object[]) token_3_arrayValues.clone();
-	  return ptg;
-	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java Tue Sep  9 13:25:16 2008
@@ -24,12 +24,12 @@
  * @author Daniel Noll (daniel at nuix dot com dot au)
  */
 public final class ErrPtg extends ScalarConstantPtg {
-    
+
     // convenient access to namespace
     private static final HSSFErrorConstants EC = null;
-    
+
     /** <b>#NULL!</b>  - Intersection of two cell ranges is empty */
-    public static final ErrPtg NULL_INTERSECTION = new ErrPtg(EC.ERROR_NULL); 
+    public static final ErrPtg NULL_INTERSECTION = new ErrPtg(EC.ERROR_NULL);
     /** <b>#DIV/0!</b> - Division by zero */
     public static final ErrPtg DIV_ZERO = new ErrPtg(EC.ERROR_DIV_0);
     /** <b>#VALUE!</b> - Wrong type of operand */
@@ -37,28 +37,28 @@
     /** <b>#REF!</b> - Illegal or deleted cell reference */
     public static final ErrPtg REF_INVALID = new ErrPtg(EC.ERROR_REF);
     /** <b>#NAME?</b> - Wrong function or range name */
-    public static final ErrPtg NAME_INVALID = new ErrPtg(EC.ERROR_NAME); 
+    public static final ErrPtg NAME_INVALID = new ErrPtg(EC.ERROR_NAME);
     /** <b>#NUM!</b> - Value range overflow */
     public static final ErrPtg NUM_ERROR = new ErrPtg(EC.ERROR_NUM);
     /** <b>#N/A</b> - Argument or function not available */
     public static final ErrPtg N_A = new ErrPtg(EC.ERROR_NA);
-    
-    
+
+
     public static final short sid  = 0x1c;
     private static final int  SIZE = 2;
     private final int field_1_error_code;
 
     /** Creates new ErrPtg */
 
-    public ErrPtg(int errorCode) {
+    private ErrPtg(int errorCode) {
         if(!HSSFErrorConstants.isValidCode(errorCode)) {
             throw new IllegalArgumentException("Invalid error code (" + errorCode + ")");
         }
         field_1_error_code = errorCode;
     }
- 
-    public ErrPtg(RecordInputStream in) {
-        this(in.readByte());
+
+    public static ErrPtg read(RecordInputStream in) {
+        return valueOf(in.readByte());
     }
 
     public void writeBytes(byte [] array, int offset)
@@ -78,4 +78,17 @@
     public int getErrorCode() {
         return field_1_error_code;
     }
+
+    public static ErrPtg valueOf(int code) {
+        switch(code) {
+            case HSSFErrorConstants.ERROR_DIV_0: return DIV_ZERO;
+            case HSSFErrorConstants.ERROR_NA: return N_A;
+            case HSSFErrorConstants.ERROR_NAME: return NAME_INVALID;
+            case HSSFErrorConstants.ERROR_NULL: return NULL_INTERSECTION;
+            case HSSFErrorConstants.ERROR_NUM: return NUM_ERROR;
+            case HSSFErrorConstants.ERROR_REF: return REF_INVALID;
+            case HSSFErrorConstants.ERROR_VALUE: return VALUE_INVALID;
+        }
+        throw new RuntimeException("Unexpected error code (" + code + ")");
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/formula/Ptg.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/formula/Ptg.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/formula/Ptg.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/formula/Ptg.java Tue Sep  9 13:25:16 2008
@@ -162,7 +162,7 @@
 			case StringPtg.sid:       return new StringPtg(in);       // 0x17
 			case AttrPtg.sid:                
 			case 0x1a:        return new AttrPtg(in); // 0x19
-			case ErrPtg.sid:          return new ErrPtg(in);          // 0x1c
+			case ErrPtg.sid:          return ErrPtg.read(in);         // 0x1c
 			case BoolPtg.sid:         return new BoolPtg(in);         // 0x1d
 			case IntPtg.sid:          return new IntPtg(in);          // 0x1e
 			case NumberPtg.sid:       return new NumberPtg(in);       // 0x1f

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/data/testRVA.xls
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/data/testRVA.xls?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
Binary files - no diff available.

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java Tue Sep  9 13:25:16 2008
@@ -22,10 +22,12 @@
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.model.FormulaParser.FormulaParseException;
+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.AreaI;
 import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.ArrayPtg;
 import org.apache.poi.hssf.record.formula.AttrPtg;
 import org.apache.poi.hssf.record.formula.BoolPtg;
 import org.apache.poi.hssf.record.formula.ConcatPtg;
@@ -48,6 +50,7 @@
 import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
 import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
 import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
 import org.apache.poi.hssf.usermodel.HSSFName;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
@@ -862,4 +865,18 @@
 		assertEquals(65535, aptg.getLastRow());
 		
 	}
+	public void testParseArray()  {
+		Ptg[] ptgs;
+		ptgs = parseFormula("mode({1,2,2,#REF!;FALSE,3,3,2})");
+		assertEquals(2, ptgs.length);
+		Ptg ptg0 = ptgs[0];
+		assertEquals(ArrayPtg.class, ptg0.getClass());
+		assertEquals("{1.0,2.0,2.0,#REF!;FALSE,3.0,3.0,2.0}", ptg0.toFormulaString(null));
+		
+		ArrayPtg aptg = (ArrayPtg) ptg0;
+		Object[][] values = aptg.getTokenArrayValues();
+		assertEquals(ErrorConstant.valueOf(HSSFErrorConstants.ERROR_REF), values[0][3]);
+		assertEquals(Boolean.FALSE, values[1][0]);
+		
+	}
 }
\ No newline at end of file

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/model/TestRVA.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/model/TestRVA.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/model/TestRVA.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/model/TestRVA.java Tue Sep  9 13:25:16 2008
@@ -17,6 +17,10 @@
 
 package org.apache.poi.hssf.model;
 
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
@@ -42,12 +46,21 @@
 
 	public void testFormulas() {
 		HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testRVA.xls");
+		try {
+			wb = new HSSFWorkbook(new FileInputStream("C:/josh/client/poi/svn/trunk-h2/src/testcases/org/apache/poi/hssf/data/testRVA.xls"));
+		} catch (FileNotFoundException e1) {
+			throw new RuntimeException(e1);
+		} catch (IOException e1) {
+			throw new RuntimeException(e1);
+		}
 		HSSFSheet sheet = wb.getSheetAt(0);
 
 		int countFailures = 0;
 		int countErrors = 0;
 
 		int rowIx = 0;
+//		rowIx = 34;
+//		rowIx =32;
 		while (rowIx < 65535) {
 			HSSFRow row = sheet.getRow(rowIx);
 			if (row == null) {
@@ -61,8 +74,10 @@
 			try {
 				confirmCell(cell, formula, wb);
 			} catch (AssertionFailedError e) {
+				System.out.flush();
 				System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'");
 				System.err.println(e.getMessage());
+				System.err.flush();
 				countFailures++;
 			} catch (RuntimeException e) {
 				System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'");
@@ -70,6 +85,8 @@
 				e.printStackTrace();
 			}
 			rowIx++;
+//			if (rowIx>30) break;
+//			break;
 		}
 		if (countErrors + countFailures > 0) {
 			String msg = "One or more RVA tests failed: countFailures=" + countFailures
@@ -104,8 +121,8 @@
 			if (excelPtg.getClass() != poiPtg.getClass()) {
 				hasMismatch = true;
 				sb.append("  mismatch token type[" + i + "] " + getShortClassName(excelPtg) + " "
-						+ getOperandClassName(excelPtg) + " - " + getShortClassName(poiPtg) + " "
-						+ getOperandClassName(poiPtg));
+						+ excelPtg.getRVAType() + " - " + getShortClassName(poiPtg) + " "
+						+ poiPtg.getRVAType());
 				sb.append(NEW_LINE);
 				continue;
 			}
@@ -113,16 +130,16 @@
 				continue;
 			}
 			sb.append("  token[" + i + "] " + excelPtg.toString() + " "
-					+ getOperandClassName(excelPtg));
+					+ excelPtg.getRVAType());
 
 			if (excelPtg.getPtgClass() != poiPtg.getPtgClass()) {
 				hasMismatch = true;
-				sb.append(" - was " + getOperandClassName(poiPtg));
+				sb.append(" - was " + poiPtg.getRVAType());
 			}
 			sb.append(NEW_LINE);
 		}
 		if (false) { // set 'true' to see trace of RVA values
-			System.out.println(formula);
+			System.out.println(formulaCell.getRowIndex() + " " + formula);
 			System.out.println(sb.toString());
 		}
 		if (hasMismatch) {
@@ -135,14 +152,4 @@
 		int pos = cn.lastIndexOf('.');
 		return cn.substring(pos + 1);
 	}
-
-	private static String getOperandClassName(Ptg ptg) {
-		byte ptgClass = ptg.getPtgClass();
-		switch (ptgClass) {
-			case Ptg.CLASS_REF:   return "R";
-			case Ptg.CLASS_VALUE: return "V";
-			case Ptg.CLASS_ARRAY: return "A";
-		}
-		throw new RuntimeException("Unknown operand class (" + ptgClass + ")");
-	}
 }

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java?rev=693591&r1=693590&r2=693591&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java Tue Sep  9 13:25:16 2008
@@ -60,15 +60,15 @@
 		ptg.readTokenValues(new TestcaseRecordInputStream(0, ENCODED_CONSTANT_DATA));
 		assertEquals(3, ptg.getColumnCount());
 		assertEquals(2, ptg.getRowCount());
-		Object[] values = ptg.getTokenArrayValues();
-		assertEquals(6, values.length);
+		Object[][] values = ptg.getTokenArrayValues();
+		assertEquals(2, values.length);
 		
 		
-		assertEquals(Boolean.TRUE, values[0]);
-		assertEquals(new UnicodeString("ABCD"), values[1]);
-		assertEquals(new Double(0), values[3]);
-		assertEquals(Boolean.FALSE, values[4]);
-		assertEquals(new UnicodeString("FG"), values[5]);
+		assertEquals(Boolean.TRUE, values[0][0]);
+		assertEquals(new UnicodeString("ABCD"), values[0][1]);
+		assertEquals(new Double(0), values[1][0]);
+		assertEquals(Boolean.FALSE, values[1][1]);
+		assertEquals(new UnicodeString("FG"), values[1][2]);
 		
 		byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length];
 		ptg.writeTokenValueBytes(outBuf, 0);
@@ -89,10 +89,10 @@
 		assertEquals(2, ptg.getRowCount());
 		
 		assertEquals(0, ptg.getValueIndex(0, 0));
-		assertEquals(2, ptg.getValueIndex(1, 0));
-		assertEquals(4, ptg.getValueIndex(2, 0));
-		assertEquals(1, ptg.getValueIndex(0, 1));
-		assertEquals(3, ptg.getValueIndex(1, 1));
+		assertEquals(1, ptg.getValueIndex(1, 0));
+		assertEquals(2, ptg.getValueIndex(2, 0));
+		assertEquals(3, ptg.getValueIndex(0, 1));
+		assertEquals(4, ptg.getValueIndex(1, 1));
 		assertEquals(5, ptg.getValueIndex(2, 1));
 	}
 	
@@ -110,7 +110,7 @@
 		if (formula.equals("SUM({1.0,6.0,11.0;2.0,7.0,12.0;3.0,8.0,13.0;4.0,9.0,14.0;5.0,10.0,15.0})")) {
 			throw new AssertionFailedError("Identified bug 42564 b");
 		}
-		assertEquals("SUM({1.0,2.0,3.0;4.0,5.0,6.0;7.0,8.0,9.0;10.0,11.0,12.0;13.0,14.0,15.0})", formula);
+		assertEquals("SUM({1.0,2.0,3.0,4.0,5.0;6.0,7.0,8.0,9.0,10.0;11.0,12.0,13.0,14.0,15.0})", formula);
 	}
 
 	public void testToFormulaString() {
@@ -127,7 +127,7 @@
 			}
 			throw e;
 		}
-		assertEquals("{TRUE,\"ABCD\";\"E\",0.0;FALSE,\"FG\"}", actualFormula);
+		assertEquals("{TRUE,\"ABCD\",\"E\";0.0,FALSE,\"FG\"}", actualFormula);
 	}
 	
 	/**



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org