From: Josh Micich Date: Fri, 5 Sep 2008 20:38:51 +0000 (+0000) Subject: Modified formula evaluator to handle whole column refs X-Git-Tag: REL_3_2_FINAL~91 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a175eadfd2319b32136faa4e52def1edebfbf446;p=poi.git Modified formula evaluator to handle whole column refs git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@692538 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java index b2cc897b52..b12a3c6ad7 100644 --- a/src/java/org/apache/poi/hssf/record/NameRecord.java +++ b/src/java/org/apache/poi/hssf/record/NameRecord.java @@ -487,7 +487,7 @@ public final class NameRecord extends Record { } private static Ptg createNewPtg(){ - return new Area3DPtg("A1", 0); // TODO - change to not be partially initialised + return new Area3DPtg("A1:A1", 0); // TODO - change to not be partially initialised } /** gets the reference , the area only (range) diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java index 8604eadc37..e6efaaee4d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java @@ -60,8 +60,8 @@ public final class Hlookup implements Function { AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup); - ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); - int rowIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex); + ValueEval veRowIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); + int rowIndex = LookupUtils.resolveRowOrColIndexArg(veRowIndex); ValueVector resultCol = createResultColumnVector(tableArray, rowIndex); return resultCol.getItem(colIndex); } catch (EvaluationException e) { @@ -75,13 +75,13 @@ public final class Hlookup implements Function { * * @throws EvaluationException (#VALUE!) if colIndex is negative, (#REF!) if colIndex is too high */ - private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException { - if(colIndex < 0) { + private ValueVector createResultColumnVector(AreaEval tableArray, int rowIndex) throws EvaluationException { + if(rowIndex < 0) { throw EvaluationException.invalidValue(); } - if(colIndex >= tableArray.getWidth()) { + if(rowIndex >= tableArray.getHeight()) { throw EvaluationException.invalidRef(); } - return LookupUtils.createRowVector(tableArray, colIndex); + return LookupUtils.createRowVector(tableArray, rowIndex); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java index 3c93c0846b..024bd7a05e 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java @@ -90,9 +90,19 @@ public final class Index implements Function { } } - private static ValueEval getValueFromArea(AreaEval ae, int rowIx, int columnIx) throws EvaluationException { + private static ValueEval getValueFromArea(AreaEval ae, int pRowIx, int pColumnIx) throws EvaluationException { int width = ae.getWidth(); int height = ae.getHeight(); + int rowIx; + int columnIx; + if (ae.isRow() && pColumnIx == 0 && pRowIx > 0) { + // TODO - explore all these special cases + rowIx = 0; + columnIx = pRowIx; + } else { + rowIx = pRowIx; + columnIx = pColumnIx; + } // Slightly irregular logic for bounds checking errors if (rowIx >= height || columnIx >= width) { diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java index e6a3ec81c2..972a60aaae 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java @@ -17,6 +17,7 @@ package org.apache.poi.hssf.record.formula.functions; +import org.apache.poi.hssf.record.NumberRecord; import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BoolEval; @@ -587,7 +588,9 @@ final class LookupUtils { if (lookupValue instanceof BlankEval) { // blank eval can never be found in a lookup array - throw new EvaluationException(ErrorEval.NA); + //throw new EvaluationException(ErrorEval.NA); + // TODO - investigate this + return new NumberLookupComparer(NumberEval.ZERO); } if (lookupValue instanceof StringEval) { return new StringLookupComparer((StringEval) lookupValue); diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java index 54f7d465e5..f6b65f6495 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java @@ -60,7 +60,16 @@ public final class Vlookup implements Function { AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup); - ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); + ValueEval veColIndex; + try { + veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); + } catch (EvaluationException e) { + // weird translation of errors for the third arg - needs investigation + if (e.getErrorEval() == ErrorEval.NA) { + return ErrorEval.REF_INVALID; + } + throw e; + } int colIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex); ValueVector resultCol = createResultColumnVector(tableArray, colIndex); return resultCol.getItem(rowIndex); diff --git a/src/java/org/apache/poi/hssf/util/AreaReference.java b/src/java/org/apache/poi/hssf/util/AreaReference.java index 99218222f7..0319843d72 100644 --- a/src/java/org/apache/poi/hssf/util/AreaReference.java +++ b/src/java/org/apache/poi/hssf/util/AreaReference.java @@ -46,29 +46,59 @@ public final class AreaReference { } String[] parts = separateAreaRefs(reference); + String part0 = parts[0]; + if (parts.length == 1) { + // TODO - probably shouldn't initialize area ref when text is really a cell ref + // Need to fix some named range stuff to get rid of this + _firstCell = new CellReference(part0); + + _lastCell = _firstCell; + _isSingleCell = true; + return; + } + if (parts.length != 2) { + throw new IllegalArgumentException("Bad area ref '" + reference + "'"); + } - // Special handling for whole-column references - if(parts.length == 2 && parts[0].length() == 1 && - parts[1].length() == 1 && - parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' && - parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') { + String part1 = parts[1]; + if (isPlainColumn(part0)) { + if (!isPlainColumn(part1)) { + throw new RuntimeException("Bad area ref '" + reference + "'"); + } + // Special handling for whole-column references // Represented internally as x$1 to x$65536 // which is the maximum range of rows - parts[0] = parts[0] + "$1"; - parts[1] = parts[1] + "$65536"; - } - - _firstCell = new CellReference(parts[0]); - - if(parts.length == 2) { - _lastCell = new CellReference(parts[1]); + + boolean firstIsAbs = CellReference.isPartAbsolute(part0); + boolean lastIsAbs = CellReference.isPartAbsolute(part1); + + int col0 = CellReference.convertColStringToIndex(part0); + int col1 = CellReference.convertColStringToIndex(part1); + + _firstCell = new CellReference(0, col0, true, firstIsAbs); + _lastCell = new CellReference(0xFFFF, col1, true, lastIsAbs); _isSingleCell = false; + // TODO - whole row refs } else { - _lastCell = _firstCell; - _isSingleCell = true; + _firstCell = new CellReference(part0); + _lastCell = new CellReference(part1); + _isSingleCell = part0.equals(part1); + } + } + + private boolean isPlainColumn(String refPart) { + for(int i=refPart.length()-1; i>=0; i--) { + int ch = refPart.charAt(i); + if (ch == '$' && i==0) { + continue; + } + if (ch < 'A' || ch > 'Z') { + return false; + } } + return true; } - + /** * Creates an area ref from a pair of Cell References. */ diff --git a/src/java/org/apache/poi/hssf/util/CellReference.java b/src/java/org/apache/poi/hssf/util/CellReference.java index efbc37eff9..4fe07a5a48 100644 --- a/src/java/org/apache/poi/hssf/util/CellReference.java +++ b/src/java/org/apache/poi/hssf/util/CellReference.java @@ -36,17 +36,17 @@ public final class CellReference { public static final int NAMED_RANGE = 2; public static final int BAD_CELL_OR_NAMED_RANGE = -1; } - /** 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 */ - 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 letters followed by a run of digits. The run of letters is group 1 and the - * run of digits is group 2. Each group may optionally be prefixed with a single '$'. - */ + /** 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 */ + 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 letters followed by a run of digits. 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]+)"); /** * Named range names must start with a letter or underscore. Subsequent characters may include @@ -58,89 +58,103 @@ public final class CellReference { private static final String BIFF8_LAST_ROW = String.valueOf(0x10000); 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 - * delimited and escaped as per normal syntax rules for formulas. - */ - public CellReference(String cellRef) { - String[] parts = separateRefParts(cellRef); - _sheetName = parts[0]; - String colRef = parts[1]; - if (colRef.length() < 1) { - throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'"); - } - _isColAbs = colRef.charAt(0) == '$'; - if (_isColAbs) { - colRef=colRef.substring(1); - } - _colIndex = convertColStringToNum(colRef); - - String rowRef=parts[2]; - if (rowRef.length() < 1) { - throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'"); - } - _isRowAbs = rowRef.charAt(0) == '$'; - if (_isRowAbs) { - rowRef=rowRef.substring(1); - } - _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based - } + /** + * Create an cell ref from a string representation. Sheet names containing special characters should be + * delimited and escaped as per normal syntax rules for formulas. + */ + public CellReference(String cellRef) { + String[] parts = separateRefParts(cellRef); + _sheetName = parts[0]; + String colRef = parts[1]; + if (colRef.length() < 1) { + throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'"); + } + _isColAbs = colRef.charAt(0) == '$'; + if (_isColAbs) { + colRef=colRef.substring(1); + } + _colIndex = convertColStringToIndex(colRef); + + String rowRef=parts[2]; + if (rowRef.length() < 1) { + throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'"); + } + _isRowAbs = rowRef.charAt(0) == '$'; + if (_isRowAbs) { + rowRef=rowRef.substring(1); + } + _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based + } - public CellReference(int pRow, int pCol) { - this(pRow, pCol, false, false); - } - public CellReference(int pRow, short pCol) { - this(pRow, (int)pCol, false, false); - } + public CellReference(int pRow, int pCol) { + this(pRow, pCol, false, false); + } + public CellReference(int pRow, short pCol) { + this(pRow, pCol & 0xFFFF, false, false); + } - public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) { - this(null, pRow, pCol, pAbsRow, pAbsCol); - } - public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) { - // TODO - "-1" is a special value being temporarily used for whole row and whole column area references. - // so these checks are currently N.Q.R. - if(pRow < -1) { - throw new IllegalArgumentException("row index may not be negative"); - } - if(pCol < -1) { - throw new IllegalArgumentException("column index may not be negative"); - } - _sheetName = pSheetName; - _rowIndex=pRow; - _colIndex=pCol; - _isRowAbs = pAbsRow; - _isColAbs=pAbsCol; - } + public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) { + this(null, pRow, pCol, pAbsRow, pAbsCol); + } + public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) { + // TODO - "-1" is a special value being temporarily used for whole row and whole column area references. + // so these checks are currently N.Q.R. + if(pRow < -1) { + throw new IllegalArgumentException("row index may not be negative"); + } + if(pCol < -1) { + throw new IllegalArgumentException("column index may not be negative"); + } + _sheetName = pSheetName; + _rowIndex=pRow; + _colIndex=pCol; + _isRowAbs = pAbsRow; + _isColAbs=pAbsCol; + } - public int getRow(){return _rowIndex;} - public short getCol(){return (short) _colIndex;} - public boolean isRowAbsolute(){return _isRowAbs;} - public boolean isColAbsolute(){return _isColAbs;} - /** - * @return possibly null if this is a 2D reference. Special characters are not - * escaped or delimited - */ - public String getSheetName(){ - return _sheetName; - } - - /** - * takes in a column reference portion of a CellRef and converts it from - * ALPHA-26 number format to 0-based base 10. - */ - private int convertColStringToNum(String ref) { - int lastIx = ref.length()-1; - int retval=0; + public int getRow(){return _rowIndex;} + public short getCol(){return (short) _colIndex;} + public boolean isRowAbsolute(){return _isRowAbs;} + public boolean isColAbsolute(){return _isColAbs;} + /** + * @return possibly null if this is a 2D reference. Special characters are not + * escaped or delimited + */ + public String getSheetName(){ + return _sheetName; + } + + public static boolean isPartAbsolute(String part) { + return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER; + } + /** + * takes in a column reference portion of a CellRef and converts it from + * ALPHA-26 number format to 0-based base 10. + * 'A' -> 0 + * 'Z' -> 25 + * 'AA' -> 26 + * 'IV' -> 255 + * @return zero based column index + */ + public static int convertColStringToIndex(String ref) { + int pos = 0; - - for (int k = lastIx; k > -1; k--) { + int retval=0; + for (int k = ref.length()-1; k >= 0; k--) { char thechar = ref.charAt(k); + if (thechar == ABSOLUTE_REFERENCE_MARKER) { + if (k != 0) { + throw new IllegalArgumentException("Bad col ref format '" + + ref + "'"); + } + break; + } // Character.getNumericValue() returns the values // 10-35 for the letter A-Z int shift = (int)Math.pow(26, pos); @@ -148,64 +162,64 @@ public final class CellReference { pos++; } return retval-1; - } + } - /** - * Classifies an identifier as either a simple (2D) cell reference or a named range name - * @return one of the values from NameType - */ - public static int classifyCellReference(String str) { - int len = str.length(); - if (len < 1) { - throw new IllegalArgumentException("Empty string not allowed"); - } - char firstChar = str.charAt(0); - switch (firstChar) { - case ABSOLUTE_REFERENCE_MARKER: - case '.': - case '_': - break; - default: - if (!Character.isLetter(firstChar)) { - throw new IllegalArgumentException("Invalid first char (" + firstChar - + ") of cell reference or named range. Letter expected"); - } - } - if (!Character.isDigit(str.charAt(len-1))) { - // no digits at end of str - return validateNamedRangeName(str); - } - Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str); - if (!cellRefPatternMatcher.matches()) { - return validateNamedRangeName(str); - } - String lettersGroup = cellRefPatternMatcher.group(1); - String digitsGroup = cellRefPatternMatcher.group(2); - if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) { - // valid cell reference - return NameType.CELL; - } - // If str looks like a cell reference, but is out of (row/col) range, it is a valid - // named range name - // This behaviour is a little weird. For example, "IW123" is a valid named range name - // because the column "IW" is beyond the maximum "IV". Note - this behaviour is version - // dependent. In Excel 2007, "IW123" is not a valid named range name. - if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) { - // Of course, named range names cannot have '$' - return NameType.BAD_CELL_OR_NAMED_RANGE; - } - return NameType.NAMED_RANGE; - } + /** + * Classifies an identifier as either a simple (2D) cell reference or a named range name + * @return one of the values from NameType + */ + public static int classifyCellReference(String str) { + int len = str.length(); + if (len < 1) { + throw new IllegalArgumentException("Empty string not allowed"); + } + char firstChar = str.charAt(0); + switch (firstChar) { + case ABSOLUTE_REFERENCE_MARKER: + case '.': + case '_': + break; + default: + if (!Character.isLetter(firstChar)) { + throw new IllegalArgumentException("Invalid first char (" + firstChar + + ") of cell reference or named range. Letter expected"); + } + } + if (!Character.isDigit(str.charAt(len-1))) { + // no digits at end of str + return validateNamedRangeName(str); + } + Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str); + if (!cellRefPatternMatcher.matches()) { + return validateNamedRangeName(str); + } + String lettersGroup = cellRefPatternMatcher.group(1); + String digitsGroup = cellRefPatternMatcher.group(2); + if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) { + // valid cell reference + return NameType.CELL; + } + // If str looks like a cell reference, but is out of (row/col) range, it is a valid + // named range name + // This behaviour is a little weird. For example, "IW123" is a valid named range name + // because the column "IW" is beyond the maximum "IV". Note - this behaviour is version + // dependent. In Excel 2007, "IW123" is not a valid named range name. + if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) { + // Of course, named range names cannot have '$' + return NameType.BAD_CELL_OR_NAMED_RANGE; + } + return NameType.NAMED_RANGE; + } - private static int validateNamedRangeName(String str) { + private static int validateNamedRangeName(String str) { if (!NAMED_RANGE_NAME_PATTERN.matcher(str).matches()) { return NameType.BAD_CELL_OR_NAMED_RANGE; } return NameType.NAMED_RANGE; } - - + + /** * 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 @@ -226,7 +240,7 @@ public final class CellReference { *
* - * + * * * * @@ -275,86 +289,86 @@ public final class CellReference { } /** - * 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 - * 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; + * 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 + * 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; - int length = reference.length(); + int length = reference.length(); - int loc = start; - // skip initial dollars - if (reference.charAt(loc)==ABSOLUTE_REFERENCE_MARKER) { - loc++; - } - // step over column name chars until first digit (or dollars) for row number. - for (; loc < length; loc++) { - char ch = reference.charAt(loc); - if (Character.isDigit(ch) || ch == ABSOLUTE_REFERENCE_MARKER) { - break; - } - } - return new String[] { - sheetName, - reference.substring(start,loc), - reference.substring(loc), - }; - } + int loc = start; + // skip initial dollars + if (reference.charAt(loc)==ABSOLUTE_REFERENCE_MARKER) { + loc++; + } + // step over column name chars until first digit (or dollars) for row number. + for (; loc < length; loc++) { + char ch = reference.charAt(loc); + if (Character.isDigit(ch) || ch == ABSOLUTE_REFERENCE_MARKER) { + break; + } + } + return new String[] { + sheetName, + reference.substring(start,loc), + reference.substring(loc), + }; + } - private static String parseSheetName(String reference, int indexOfSheetNameDelimiter) { - if(indexOfSheetNameDelimiter < 0) { - return null; - } - - boolean isQuoted = reference.charAt(0) == SPECIAL_NAME_DELIMITER; - if(!isQuoted) { - return reference.substring(0, indexOfSheetNameDelimiter); - } - int lastQuotePos = indexOfSheetNameDelimiter-1; - if(reference.charAt(lastQuotePos) != SPECIAL_NAME_DELIMITER) { - throw new RuntimeException("Mismatched quotes: (" + reference + ")"); - } + private static String parseSheetName(String reference, int indexOfSheetNameDelimiter) { + if(indexOfSheetNameDelimiter < 0) { + return null; + } + + boolean isQuoted = reference.charAt(0) == SPECIAL_NAME_DELIMITER; + if(!isQuoted) { + return reference.substring(0, indexOfSheetNameDelimiter); + } + int lastQuotePos = indexOfSheetNameDelimiter-1; + if(reference.charAt(lastQuotePos) != SPECIAL_NAME_DELIMITER) { + throw new RuntimeException("Mismatched quotes: (" + reference + ")"); + } - // TODO - refactor cell reference parsing logic to one place. - // Current known incarnations: - // FormulaParser.GetName() - // CellReference.parseSheetName() (here) - // AreaReference.separateAreaRefs() - // SheetNameFormatter.format() (inverse) - - StringBuffer sb = new StringBuffer(indexOfSheetNameDelimiter); - - for(int i=1; i D - */ - protected static String convertNumToColString(int col) { + /** + * Takes in a 0-based base-10 column and returns a ALPHA-26 + * representation. + * eg column #3 -> D + */ + protected static String convertNumToColString(int col) { // Excel counts column A as the 1st column, we // treat it as the 0th one int excelColNum = col + 1; @@ -373,35 +387,35 @@ public final class CellReference { } return colRef; - } + } - /** - * Example return values: - *
Input           Result 
Result 
"A", "1"true
"a", "111"true
"A", "65536"true
- * - * - * - * - *
ResultComment
A1Cell reference without sheet
Sheet1!A1Standard sheet name
'O''Brien''s Sales'!A1' Sheet name with special characters
- * @return the text representation of this cell reference as it would appear in a formula. - */ - public String formatAsString() { - StringBuffer sb = new StringBuffer(32); - if(_sheetName != null) { - SheetNameFormatter.appendFormat(sb, _sheetName); - sb.append(SHEET_NAME_DELIMITER); - } - appendCellReference(sb); - return sb.toString(); - } - - public String toString() { - StringBuffer sb = new StringBuffer(64); - sb.append(getClass().getName()).append(" ["); - sb.append(formatAsString()); - sb.append("]"); - return sb.toString(); - } + /** + * Example return values: + * + * + * + * + * + *
ResultComment
A1Cell reference without sheet
Sheet1!A1Standard sheet name
'O''Brien''s Sales'!A1' Sheet name with special characters
+ * @return the text representation of this cell reference as it would appear in a formula. + */ + public String formatAsString() { + StringBuffer sb = new StringBuffer(32); + if(_sheetName != null) { + SheetNameFormatter.appendFormat(sb, _sheetName); + sb.append(SHEET_NAME_DELIMITER); + } + appendCellReference(sb); + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(formatAsString()); + sb.append("]"); + return sb.toString(); + } /** * Returns the three parts of the cell reference, the @@ -419,18 +433,18 @@ public final class CellReference { }; } - /** - * Appends cell reference with '$' markers for absolute values as required. - * Sheet name is not included. - */ - /* package */ void appendCellReference(StringBuffer sb) { - if(_isColAbs) { - sb.append(ABSOLUTE_REFERENCE_MARKER); - } - sb.append( convertNumToColString(_colIndex)); - if(_isRowAbs) { - sb.append(ABSOLUTE_REFERENCE_MARKER); - } - sb.append(_rowIndex+1); - } + /** + * Appends cell reference with '$' markers for absolute values as required. + * Sheet name is not included. + */ + /* package */ void appendCellReference(StringBuffer sb) { + if(_isColAbs) { + sb.append(ABSOLUTE_REFERENCE_MARKER); + } + sb.append( convertNumToColString(_colIndex)); + if(_isRowAbs) { + sb.append(ABSOLUTE_REFERENCE_MARKER); + } + sb.append(_rowIndex+1); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java index d1f70a0ad4..d4f100cfc7 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java @@ -40,4 +40,43 @@ public final class TestHSSFFormulaEvaluator extends TestCase { assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType()); assertEquals(3.72, cv.getNumberValue(), 0.0); } + + public void testFullColumnRefs() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet("Sheet1"); + HSSFRow row = sheet.createRow(0); + HSSFCell cell0 = row.createCell(0); + cell0.setCellFormula("sum(D:D)"); + HSSFCell cell1 = row.createCell(1); + cell1.setCellFormula("sum(D:E)"); + + // some values in column D + setValue(sheet, 1, 3, 5.0); + setValue(sheet, 2, 3, 6.0); + setValue(sheet, 5, 3, 7.0); + setValue(sheet, 50, 3, 8.0); + + // some values in column E + setValue(sheet, 1, 4, 9.0); + setValue(sheet, 2, 4, 10.0); + setValue(sheet, 30000, 4, 11.0); + + // some other values + setValue(sheet, 1, 2, 100.0); + setValue(sheet, 2, 5, 100.0); + setValue(sheet, 3, 6, 100.0); + + + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + assertEquals(26.0, fe.evaluate(cell0).getNumberValue(), 0.0); + assertEquals(56.0, fe.evaluate(cell1).getNumberValue(), 0.0); + } + + private static void setValue(HSSFSheet sheet, int rowIndex, int colIndex, double value) { + HSSFRow row = sheet.getRow(rowIndex); + if (row == null) { + row = sheet.createRow(rowIndex); + } + row.createCell(colIndex).setCellValue(value); + } } diff --git a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java index c8424287ce..0b6d73462b 100644 --- a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java +++ b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java @@ -83,7 +83,7 @@ public final class TestAreaReference extends TestCase { public void testReferenceWithSheet() { AreaReference ar; - ar = new AreaReference("Tabelle1!B5"); + ar = new AreaReference("Tabelle1!B5:B5"); assertTrue(ar.isSingleCell()); TestCellReference.confirmCell(ar.getFirstCell(), "Tabelle1", 4, 1, false, false, "Tabelle1!B5"); @@ -113,11 +113,11 @@ public final class TestAreaReference extends TestCase { } } - public void testContiguousReferences() throws Exception { - String refSimple = "$C$10"; + public void testContiguousReferences() { + String refSimple = "$C$10:$C$10"; String ref2D = "$C$10:$D$11"; - String refDCSimple = "$C$10,$D$12,$E$14"; - String refDC2D = "$C$10:$C$11,$D$12,$E$14:$E$20"; + String refDCSimple = "$C$10:$C$10,$D$12:$D$12,$E$14:$E$14"; + String refDC2D = "$C$10:$C$11,$D$12:$D$12,$E$14:$E$20"; String refDC3D = "Tabelle1!$C$10:$C$14,Tabelle1!$D$10:$D$12"; // Check that we detect as contiguous properly @@ -243,16 +243,16 @@ public final class TestAreaReference extends TestCase { private static void confirmResolveCellRef(HSSFWorkbook wb, CellReference cref) { HSSFSheet s = wb.getSheet(cref.getSheetName()); HSSFRow r = s.getRow(cref.getRow()); - HSSFCell c = r.getCell(cref.getCol()); + HSSFCell c = r.getCell((int)cref.getCol()); assertNotNull(c); } public void testSpecialSheetNames() { AreaReference ar; - ar = new AreaReference("'Sheet A'!A1"); + ar = new AreaReference("'Sheet A'!A1:A1"); confirmAreaSheetName(ar, "Sheet A", "'Sheet A'!A1"); - ar = new AreaReference("'Hey! Look Here!'!A1"); + ar = new AreaReference("'Hey! Look Here!'!A1:A1"); confirmAreaSheetName(ar, "Hey! Look Here!", "'Hey! Look Here!'!A1"); ar = new AreaReference("'O''Toole'!A1:B2"); @@ -268,7 +268,24 @@ public final class TestAreaReference extends TestCase { assertEquals(expectedFullText, ar.formatAsString()); } - public static void main(String[] args) { - junit.textui.TestRunner.run(TestAreaReference.class); - } + public void testWholeColumnRefs() { + confirmWholeColumnRef("A:A", 0, 0, false, false); + confirmWholeColumnRef("$C:D", 2, 3, true, false); + confirmWholeColumnRef("AD:$AE", 29, 30, false, true); + + } + + private static void confirmWholeColumnRef(String ref, int firstCol, int lastCol, boolean firstIsAbs, boolean lastIsAbs) { + AreaReference ar = new AreaReference(ref); + confirmCell(ar.getFirstCell(), 0, firstCol, true, firstIsAbs); + confirmCell(ar.getLastCell(), 0xFFFF, lastCol, true, lastIsAbs); + } + + private static void confirmCell(CellReference cell, int row, int col, boolean isRowAbs, + boolean isColAbs) { + assertEquals(row, cell.getRow()); + assertEquals(col, cell.getCol()); + assertEquals(isRowAbs, cell.isRowAbsolute()); + assertEquals(isColAbs, cell.isColAbsolute()); + } }