poi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j...@apache.org
Subject svn commit: r781616 - in /poi/trunk/src: documentation/content/xdocs/status.xml java/org/apache/poi/ss/util/CellReference.java testcases/org/apache/poi/hssf/model/TestFormulaParser.java testcases/org/apache/poi/ss/util/TestCellReference.java
Date Wed, 03 Jun 2009 23:23:13 GMT
Author: josh
Date: Wed Jun  3 23:23:13 2009
New Revision: 781616

URL: http://svn.apache.org/viewvc?rev=781616&view=rev
Log:
Bugzilla 47312 - Fixed formula parser to properly reject cell references with a '0' row component

Modified:
    poi/trunk/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/ss/util/CellReference.java
    poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
    poi/trunk/src/testcases/org/apache/poi/ss/util/TestCellReference.java

Modified: poi/trunk/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/status.xml?rev=781616&r1=781615&r2=781616&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/status.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/status.xml Wed Jun  3 23:23:13 2009
@@ -35,6 +35,7 @@
         <release version="3.5-beta7" date="2009-??-??">
         </release>
         <release version="3.5-beta6" date="2009-06-11">
+           <action dev="POI-DEVELOPERS" type="fix">47312 - Fixed formula parser to
properly reject cell references with a '0' row component</action>
            <action dev="POI-DEVELOPERS" type="fix">47199 - Fixed PageSettingsBlock/Sheet
to tolerate margin records after other non-PSB records</action>
            <action dev="POI-DEVELOPERS" type="fix">47069 - Fixed HSSFSheet#getFirstRowNum
and HSSFSheet#getLastRowNum to return correct values after removal of all rows</action>
            <action dev="POI-DEVELOPERS" type="fix">47278 - Fixed XSSFCell to avoid
generating xsi:nil entries in shared string table</action>

Modified: poi/trunk/src/java/org/apache/poi/ss/util/CellReference.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/util/CellReference.java?rev=781616&r1=781615&r2=781616&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/util/CellReference.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/util/CellReference.java Wed Jun  3 23:23:13 2009
@@ -21,7 +21,6 @@
 import java.util.regex.Pattern;
 
 import org.apache.poi.hssf.record.formula.SheetNameFormatter;
-import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.SpreadsheetVersion;
 
 /**
@@ -40,21 +39,21 @@
 		public static final int BAD_CELL_OR_NAMED_RANGE = -1;
 	}
 
-	/** The character ($) that signifies a row or column value is absolute instead of relative
*/ 
+	/** The character ($) that signifies a row or column value is absolute instead of relative
*/
 	private static final char ABSOLUTE_REFERENCE_MARKER = '$';
-	/** The character (!) that separates sheet names from cell references */ 
+	/** The character (!) that separates sheet names from cell references */
 	private static final char SHEET_NAME_DELIMITER = '!';
 	/** The character (') used to quote sheet names when they contain special characters */
 	private static final char SPECIAL_NAME_DELIMITER = '\'';
-	
+
 	/**
 	 * Matches a run of one or more letters followed by a run of one or more digits.
-	 * The run of letters is group 1 and the run of digits is group 2.  
+	 * The run of letters is group 1 and the run of digits is group 2.
 	 * Each group may optionally be prefixed with a single '$'.
 	 */
 	private static final Pattern CELL_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)\\$?([0-9]+)");
 	/**
-	 * Matches a run of one or more letters.  The run of letters is group 1.  
+	 * Matches a run of one or more letters.  The run of letters is group 1.
 	 * The text may optionally be prefixed with a single '$'.
 	 */
 	private static final Pattern COLUMN_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)");
@@ -68,11 +67,11 @@
 	//private static final String BIFF8_LAST_ROW = String.valueOf(SpreadsheetVersion.EXCEL97.getMaxRows());
 	//private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length();
 
-    private final int _rowIndex;
-    private final int _colIndex;
-    private final String _sheetName;
-    private final boolean _isRowAbs;
-    private final boolean _isColAbs;
+	private final int _rowIndex;
+	private final int _colIndex;
+	private final String _sheetName;
+	private final boolean _isRowAbs;
+	private final boolean _isColAbs;
 
 	/**
 	 * Create an cell ref from a string representation.  Sheet names containing special characters
should be
@@ -81,7 +80,7 @@
 	public CellReference(String cellRef) {
 		String[] parts = separateRefParts(cellRef);
 		_sheetName = parts[0];
-		String colRef = parts[1]; 
+		String colRef = parts[1];
 		if (colRef.length() < 1) {
 			throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
 		}
@@ -90,7 +89,7 @@
 			colRef=colRef.substring(1);
 		}
 		_colIndex = convertColStringToIndex(colRef);
-		
+
 		String rowRef=parts[2];
 		if (rowRef.length() < 1) {
 			throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
@@ -139,7 +138,7 @@
 	public String getSheetName(){
 		return _sheetName;
 	}
-	
+
 	public static boolean isPartAbsolute(String part) {
 		return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER;
 	}
@@ -153,7 +152,7 @@
 	 * @return zero based column index
 	 */
 	public static int convertColStringToIndex(String ref) {
-	
+
 		int pos = 0;
 		int retval=0;
 		for (int k = ref.length()-1; k >= 0; k--) {
@@ -175,7 +174,7 @@
 
 	/**
 	 * Classifies an identifier as either a simple (2D) cell reference or a named range name
-	 * @return one of the values from <tt>NameType</tt> 
+	 * @return one of the values from <tt>NameType</tt>
 	 */
 	public static int classifyCellReference(String str, SpreadsheetVersion ssVersion) {
 		int len = str.length();
@@ -190,7 +189,7 @@
 				break;
 			default:
 				if (!Character.isLetter(firstChar)) {
-					throw new IllegalArgumentException("Invalid first char (" + firstChar 
+					throw new IllegalArgumentException("Invalid first char (" + firstChar
 							+ ") of cell reference or named range.  Letter expected");
 				}
 		}
@@ -204,7 +203,7 @@
 		}
 		String lettersGroup = cellRefPatternMatcher.group(1);
 		String digitsGroup = cellRefPatternMatcher.group(2);
-        if (cellReferenceIsWithinRange(lettersGroup, digitsGroup, ssVersion)) {
+		if (cellReferenceIsWithinRange(lettersGroup, digitsGroup, ssVersion)) {
 			// valid cell reference
 			return NameType.CELL;
 		}
@@ -233,17 +232,17 @@
 		}
 		return NameType.NAMED_RANGE;
 	}
-	
-	
+
+
 	/**
-	 * Used to decide whether a name of the form "[A-Z]*[0-9]*" that appears in a formula can
be 
+	 * Used to decide whether a name of the form "[A-Z]*[0-9]*" that appears in a formula can
be
 	 * interpreted as a cell reference.  Names of that form can be also used for sheets and/or
-	 * named ranges, and in those circumstances, the question of whether the potential cell

+	 * named ranges, and in those circumstances, the question of whether the potential cell
 	 * reference is valid (in range) becomes important.
 	 * <p/>
 	 * Note - that the maximum sheet size varies across Excel versions:
 	 * <p/>
-	 * <blockquote><table border="0" cellpadding="1" cellspacing="0" 
+	 * <blockquote><table border="0" cellpadding="1" cellspacing="0"
 	 *                 summary="Notable cases.">
 	 *   <tr><th>Version&nbsp;&nbsp;</th><th>File Format&nbsp;&nbsp;</th>
 	 *   	<th>Last Column&nbsp;&nbsp;</th><th>Last Row</th></tr>
@@ -252,7 +251,7 @@
 	 * </table></blockquote>
 	 * POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed
for
 	 * this method:
-	 * <blockquote><table border="0" cellpadding="1" cellspacing="0" 
+	 * <blockquote><table border="0" cellpadding="1" cellspacing="0"
 	 *                 summary="Notable cases.">
 	 *   <tr><th>Input&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
 	 *       <th>Result&nbsp;</th></tr>
@@ -266,7 +265,7 @@
 	 *   <tr><td>"a", "111"</td><td>true</td></tr>
 	 *   <tr><td>"Sheet", "1"</td><td>false</td></tr>
 	 * </table></blockquote>
-	 * 
+	 *
 	 * @param colStr a string of only letter characters
 	 * @param rowStr a string of only digit characters
 	 * @return <code>true</code> if the row and col parameters are within range
of a BIFF8 spreadsheet.
@@ -275,30 +274,23 @@
 		if (!isColumnWithnRange(colStr, ssVersion)) {
 			return false;
 		}
-        String lastRow = String.valueOf(ssVersion.getMaxRows());
-        int lastRowLen = lastRow.length();
 
-        int nDigits = rowStr.length();
-		if(nDigits > lastRowLen) {
-			return false; 
-		}
-		
-		if(nDigits == lastRowLen) {
-			// ASCII comparison is valid if digit count is same
-			if(rowStr.compareTo(lastRow) > 0) {
-				return false;
-			}
-		} else {
-			// apparent row has less chars than max
-			// no need to check range
+		int rowNum = Integer.parseInt(rowStr);
+
+		if (rowNum < 0) {
+			throw new IllegalStateException("Invalid rowStr '" + rowStr + "'.");
 		}
-		
-		return true;
+		if (rowNum == 0) {
+			// execution gets here because caller does first pass of discriminating
+			// potential cell references using a simplistic regex pattern.
+			return false;
+		}
+		return rowNum <= ssVersion.getMaxRows();
 	}
 
 	public static boolean isColumnWithnRange(String colStr, SpreadsheetVersion ssVersion) {
-        String lastCol = ssVersion.getLastColumnName();
-        int lastColLength = lastCol.length();
+		String lastCol = ssVersion.getLastColumnName();
+		int lastColLength = lastCol.length();
 
 		int numberOfLetters = colStr.length();
 		if(numberOfLetters > lastColLength) {
@@ -318,11 +310,11 @@
 
 	/**
 	 * Separates the row from the columns and returns an array of three Strings.  The first
element
-	 * is the sheet name. Only the first element may be null.  The second element in is the
column 
+	 * is the sheet name. Only the first element may be null.  The second element in is the
column
 	 * name still in ALPHA-26 number format.  The third element is the row.
 	 */
 	private static String[] separateRefParts(String reference) {
-		
+
 		int plingPos = reference.lastIndexOf(SHEET_NAME_DELIMITER);
 		String sheetName = parseSheetName(reference, plingPos);
 		int start = plingPos+1;
@@ -331,7 +323,7 @@
 
 
 		int loc = start;
-		// skip initial dollars 
+		// skip initial dollars
 		if (reference.charAt(loc)==ABSOLUTE_REFERENCE_MARKER) {
 			loc++;
 		}
@@ -343,9 +335,9 @@
 			}
 		}
 		return new String[] {
-		   sheetName,
-		   reference.substring(start,loc),
-		   reference.substring(loc),
+			sheetName,
+			reference.substring(start,loc),
+			reference.substring(loc),
 		};
 	}
 
@@ -353,7 +345,7 @@
 		if(indexOfSheetNameDelimiter < 0) {
 			return null;
 		}
-		
+
 		boolean isQuoted = reference.charAt(0) == SPECIAL_NAME_DELIMITER;
 		if(!isQuoted) {
 			return reference.substring(0, indexOfSheetNameDelimiter);
@@ -364,14 +356,14 @@
 		}
 
 		// TODO - refactor cell reference parsing logic to one place.
-		// Current known incarnations: 
+		// Current known incarnations:
 		//   FormulaParser.GetName()
 		//   CellReference.parseSheetName() (here)
-		//   AreaReference.separateAreaRefs() 
+		//   AreaReference.separateAreaRefs()
 		//   SheetNameFormatter.format() (inverse)
-		
+
 		StringBuffer sb = new StringBuffer(indexOfSheetNameDelimiter);
-		
+
 		for(int i=1; i<lastQuotePos; i++) { // Note boundaries - skip outer quotes
 			char ch = reference.charAt(i);
 			if(ch != SPECIAL_NAME_DELIMITER) {
@@ -400,20 +392,20 @@
 		// Excel counts column A as the 1st column, we
 		//  treat it as the 0th one
 		int excelColNum = col + 1;
-		
+
 		String colRef = "";
 		int colRemain = excelColNum;
-		
+
 		while(colRemain > 0) {
 			int thisPart = colRemain % 26;
 			if(thisPart == 0) { thisPart = 26; }
 			colRemain = (colRemain - thisPart) / 26;
-			
+
 			// The letter A is at 65
 			char colChar = (char)(thisPart+64);
 			colRef = colChar + colRef;
 		}
-		
+
 		return colRef;
 	}
 
@@ -436,7 +428,7 @@
 		appendCellReference(sb);
 		return sb.toString();
 	}
-	
+
 	public String toString() {
 		StringBuffer sb = new StringBuffer(64);
 		sb.append(getClass().getName()).append(" [");
@@ -451,7 +443,7 @@
 	 *  row number, and the A based column letter.
 	 * This will not include any markers for absolute
 	 *  references, so use {@link #formatAsString()}
-	 *  to properly turn references into strings. 
+	 *  to properly turn references into strings.
 	 */
 	public String[] getCellRefParts() {
 		return new String[] {

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=781616&r1=781615&r2=781616&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 Wed Jun  3 23:23:13
2009
@@ -68,6 +68,7 @@
 import org.apache.poi.ss.formula.FormulaParser;
 import org.apache.poi.ss.formula.FormulaParserTestHelper;
 import org.apache.poi.ss.usermodel.BaseTestBugzillaIssues;
+import org.apache.poi.ss.usermodel.Name;
 
 /**
  * Test the low level formula parser functionality. High level tests are to
@@ -1229,4 +1230,46 @@
 		assertNotNull("Ptg array should not be null", result);
 		confirmTokenClasses(result, new Class[] { IntPtg.class, NamePtg.class, AddPtg.class,});
 	}
+
+	/**
+	 * Zero is not a valid row number so cell references like 'A0' are not valid.
+	 * Actually, they should be treated like defined names.
+	 * <br/>
+	 * In addition, leading zeros (on the row component) should be removed from cell
+	 * references during parsing.
+	 */
+	public void testZeroRowRefs() {
+		String badCellRef = "B0"; // bad because zero is not a valid row number
+		String leadingZeroCellRef = "B000001"; // this should get parsed as "B1"
+		HSSFWorkbook wb = new HSSFWorkbook();
+
+		try {
+			HSSFFormulaParser.parse(badCellRef, wb);
+			throw new AssertionFailedError("Identified bug 47312b - Shouldn't be able to parse cell
ref '"
+					+ badCellRef + "'.");
+		} catch (RuntimeException e) {
+			// expected during successful test
+			FormulaParserTestHelper.confirmParseException(e, "Specified named range '"
+					+ badCellRef + "' does not exist in the current workbook.");
+		}
+
+		Ptg[] ptgs;
+		try {
+			ptgs = HSSFFormulaParser.parse(leadingZeroCellRef, wb);
+			assertEquals("B1", ((RefPtg) ptgs[0]).toFormulaString());
+		} catch (RuntimeException e) {
+			FormulaParserTestHelper.confirmParseException(e, "Specified named range '"
+					+ leadingZeroCellRef + "' does not exist in the current workbook.");
+			// close but no cigar
+			throw new AssertionFailedError("Identified bug 47312c - '"
+					+ leadingZeroCellRef + "' should parse as 'B1'.");
+		}
+
+		// create a defined name called 'B0' and try again
+		Name n = wb.createName();
+		n.setNameName("B0");
+		n.setRefersToFormula("1+1");
+		ptgs = HSSFFormulaParser.parse("B0", wb);
+		assertEquals(NamePtg.class, ptgs[0].getClass());
+	}
 }

Modified: poi/trunk/src/testcases/org/apache/poi/ss/util/TestCellReference.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/util/TestCellReference.java?rev=781616&r1=781615&r2=781616&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/util/TestCellReference.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/ss/util/TestCellReference.java Wed Jun  3 23:23:13
2009
@@ -17,15 +17,17 @@
 
 package org.apache.poi.ss.util;
 
+import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.ss.util.CellReference;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 
 /**
  * Tests that the common CellReference works as we need it to
  */
-public class TestCellReference extends TestCase {
+public final class TestCellReference extends TestCase {
 	
 	public void testGetCellRefParts() {
 		CellReference cellReference;
@@ -168,4 +170,35 @@
 		String collRef4 = new CellReference(0, col4).formatAsString();
 		assertEquals("CBA1", collRef4);
 	}
+
+	public void testBadRowNumber() {
+		SpreadsheetVersion v97 = SpreadsheetVersion.EXCEL97;
+		SpreadsheetVersion v2007 = SpreadsheetVersion.EXCEL2007;
+
+		confirmCrInRange(true, "A", "1", v97);
+		confirmCrInRange(true, "IV", "65536", v97);
+		confirmCrInRange(false, "IV", "65537", v97);
+		confirmCrInRange(false, "IW", "65536", v97);
+
+		confirmCrInRange(true, "A", "1", v2007);
+		confirmCrInRange(true, "XFD", "1048576", v2007);
+		confirmCrInRange(false, "XFD", "1048577", v2007);
+		confirmCrInRange(false, "XFE", "1048576", v2007);
+
+		if (CellReference.cellReferenceIsWithinRange("B", "0", v97)) {
+			throw new AssertionFailedError("Identified bug 47312a");
+		}
+
+		confirmCrInRange(false, "A", "0", v97);
+		confirmCrInRange(false, "A", "0", v2007);
+	}
+
+	private static void confirmCrInRange(boolean expResult, String colStr, String rowStr,
+			SpreadsheetVersion sv) {
+		if (expResult == CellReference.cellReferenceIsWithinRange(colStr, rowStr, sv)) {
+			return;
+		}
+		throw new AssertionFailedError("expected (c='" + colStr + "', r='" + rowStr + "' to be
"
+				+ (expResult ? "within" : "out of") + " bounds for version " + sv.name());
+	}
 }



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


Mime
View raw message