diff --git a/src/java/org/apache/poi/ss/util/AreaReference.java b/src/java/org/apache/poi/ss/util/AreaReference.java index a832fec6b4..ffe672f7ee 100644 --- a/src/java/org/apache/poi/ss/util/AreaReference.java +++ b/src/java/org/apache/poi/ss/util/AreaReference.java @@ -93,19 +93,6 @@ public class AreaReference { } } - private static 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. */ @@ -116,14 +103,14 @@ public class AreaReference { if (swapRows || swapCols) { String firstSheet; String lastSheet; - int firstRow; - int lastRow; - int firstColumn; + int firstRow; + int lastRow; + int firstColumn; int lastColumn; - boolean firstRowAbs; - boolean lastRowAbs; + boolean firstRowAbs; + boolean lastRowAbs; boolean firstColAbs; - boolean lastColAbs; + boolean lastColAbs; if (swapRows) { firstRow = botRight.getRow(); firstRowAbs = botRight.isRowAbsolute(); @@ -158,6 +145,19 @@ public class AreaReference { } _isSingleCell = false; } + + private static 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; + } /** * Is the reference for a contiguous (i.e. @@ -201,9 +201,6 @@ public class AreaReference { && botRight.getRow() == version.getLastRowIndex() && botRight.isRowAbsolute()); } - public boolean isWholeColumnReference() { - return isWholeColumnReference(_version, _firstCell, _lastCell); - } /** * Takes a non-contiguous area reference, and returns an array of contiguous area references @@ -211,7 +208,7 @@ public class AreaReference { */ public static AreaReference[] generateContiguous(SpreadsheetVersion version, String reference) { if (null == version) { - version = DEFAULT_SPREADSHEET_VERSION; // how the code used to behave. + version = DEFAULT_SPREADSHEET_VERSION; // how the code used to behave. } List refs = new ArrayList<>(); String[] splitReferences = splitAreaReferences(reference); @@ -221,13 +218,122 @@ public class AreaReference { return refs.toArray(new AreaReference[0]); } + /** + * Separates Area refs in two parts and returns them as separate elements in a String array, + * each qualified with the sheet name (if present) + * + * @return array with one or two elements. never null + */ + private static String[] separateAreaRefs(String reference) { + // TODO - refactor cell reference parsing logic to one place. + // Current known incarnations: + // FormulaParser.GetName() + // CellReference.separateRefParts() + // AreaReference.separateAreaRefs() (here) + // SheetNameFormatter.format() (inverse) + + + int len = reference.length(); + int delimiterPos = -1; + boolean insideDelimitedName = false; + for(int i=0; i=0) { + throw new IllegalArgumentException("More than one cell delimiter '" + + CELL_DELIMITER + "' appears in area reference '" + reference + "'"); + } + delimiterPos = i; + } + continue; //continue the for-loop + case SPECIAL_NAME_DELIMITER: + break; + default: + continue; //continue the for-loop + } + if(!insideDelimitedName) { + insideDelimitedName = true; + continue; + } + + if(i >= len-1) { + // reference ends with the delimited name. + // Assume names like: "Sheet1!'A1'" are never legal. + throw new IllegalArgumentException("Area reference '" + reference + + "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'"); + } + if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) { + // two consecutive quotes is the escape sequence for a single one + i++; // skip this and keep parsing the special name + } else { + // this is the end of the delimited name + insideDelimitedName = false; + } + } + if(delimiterPos < 0) { + return new String[] { reference, }; + } + + String partA = reference.substring(0, delimiterPos); + String partB = reference.substring(delimiterPos+1); + if(partB.indexOf(SHEET_NAME_DELIMITER) >= 0) { + // partB contains SHEET_NAME_DELIMITER + // TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid? + // FormulaParser has code to handle that. + + throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER + + " in second cell reference of '" + reference + "'"); + } + + int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER); + if(plingPos < 0) { + return new String [] { partA, partB, }; + } + + String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter + + return new String [] { partA, sheetName + partB, }; + } + + /** + * Splits a comma-separated area references string into an array of + * individual references + * @param reference Area references, i.e. A1:B2, 'Sheet1'!A1:B2 + * @return Area references in an array, size >= 1 + */ + private static String[] splitAreaReferences(String reference) { + List results = new ArrayList<>(); + String currentSegment = ""; + StringTokenizer st = new StringTokenizer(reference, ","); + while(st.hasMoreTokens()) { + if (currentSegment.length() > 0) { + currentSegment += ","; + } + currentSegment += st.nextToken(); + int numSingleQuotes = StringUtil.countMatches(currentSegment, '\''); + if (numSingleQuotes == 0 || numSingleQuotes == 2) { + results.add(currentSegment); + currentSegment = ""; + } + } + if (currentSegment.length() > 0) { + results.add(currentSegment); + } + return results.toArray(new String[0]); + } + + public boolean isWholeColumnReference() { + return isWholeColumnReference(_version, _firstCell, _lastCell); + } + /** * @return false if this area reference involves more than one cell */ public boolean isSingleCell() { return _isSingleCell; } - + /** * @return the first cell reference which defines this area. Usually this cell is in the upper * left corner of the area (but this is not a requirement). @@ -235,17 +341,18 @@ public class AreaReference { public CellReference getFirstCell() { return _firstCell; } - + /** * Note - if this area reference refers to a single cell, the return value of this method will * be identical to that of getFirstCell() - * @return the second cell reference which defines this area. For multi-cell areas, this is - * cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner + * @return the second cell reference which defines this area. For multi-cell areas, this is + * cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner * of the area (but this is not a requirement). */ public CellReference getLastCell() { return _lastCell; } + /** * Returns a reference to every cell covered by this area */ @@ -254,14 +361,14 @@ public class AreaReference { if(_isSingleCell) { return new CellReference[] { _firstCell, }; } - + // Interpolate between the two int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow()); int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow()); int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol()); int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol()); String sheetName = _firstCell.getSheetName(); - + List refs = new ArrayList<>(); for(int row=minRow; row<=maxRow; row++) { for(int col=minCol; col<=maxCol; col++) { @@ -319,109 +426,4 @@ public class AreaReference { sb.append(']'); return sb.toString(); } - - /** - * Separates Area refs in two parts and returns them as separate elements in a String array, - * each qualified with the sheet name (if present) - * - * @return array with one or two elements. never null - */ - private static String[] separateAreaRefs(String reference) { - // TODO - refactor cell reference parsing logic to one place. - // Current known incarnations: - // FormulaParser.GetName() - // CellReference.separateRefParts() - // AreaReference.separateAreaRefs() (here) - // SheetNameFormatter.format() (inverse) - - - int len = reference.length(); - int delimiterPos = -1; - boolean insideDelimitedName = false; - for(int i=0; i=0) { - throw new IllegalArgumentException("More than one cell delimiter '" - + CELL_DELIMITER + "' appears in area reference '" + reference + "'"); - } - delimiterPos = i; - } - continue; //continue the for-loop - case SPECIAL_NAME_DELIMITER: - break; - default: - continue; //continue the for-loop - } - if(!insideDelimitedName) { - insideDelimitedName = true; - continue; - } - - if(i >= len-1) { - // reference ends with the delimited name. - // Assume names like: "Sheet1!'A1'" are never legal. - throw new IllegalArgumentException("Area reference '" + reference - + "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'"); - } - if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) { - // two consecutive quotes is the escape sequence for a single one - i++; // skip this and keep parsing the special name - } else { - // this is the end of the delimited name - insideDelimitedName = false; - } - } - if(delimiterPos < 0) { - return new String[] { reference, }; - } - - String partA = reference.substring(0, delimiterPos); - String partB = reference.substring(delimiterPos+1); - if(partB.indexOf(SHEET_NAME_DELIMITER) >= 0) { - // partB contains SHEET_NAME_DELIMITER - // TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid? - // FormulaParser has code to handle that. - - throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER - + " in second cell reference of '" + reference + "'"); - } - - int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER); - if(plingPos < 0) { - return new String [] { partA, partB, }; - } - - String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter - - return new String [] { partA, sheetName + partB, }; - } - - /** - * Splits a comma-separated area references string into an array of - * individual references - * @param reference Area references, i.e. A1:B2, 'Sheet1'!A1:B2 - * @return Area references in an array, size >= 1 - */ - private static String[] splitAreaReferences(String reference) { - List results = new ArrayList<>(); - String currentSegment = ""; - StringTokenizer st = new StringTokenizer(reference, ","); - while(st.hasMoreTokens()) { - if (currentSegment.length() > 0) { - currentSegment += ","; - } - currentSegment += st.nextToken(); - int numSingleQuotes = StringUtil.countMatches(currentSegment, '\''); - if (numSingleQuotes == 0 || numSingleQuotes == 2) { - results.add(currentSegment); - currentSegment = ""; - } - } - if (currentSegment.length() > 0) { - results.add(currentSegment); - } - return results.toArray(new String[0]); - } } diff --git a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java index b92b65adb7..0870a011cc 100644 --- a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java +++ b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java @@ -216,7 +216,7 @@ public final class TestAreaReference { @Test public void testDiscontinousReference() throws Exception { try (InputStream is = HSSFTestDataSamples.openSampleFileStream("44167.xls"); - HSSFWorkbook wb = new HSSFWorkbook(is)) { + HSSFWorkbook wb = new HSSFWorkbook(is)) { InternalWorkbook workbook = TestHSSFWorkbook.getInternalWorkbook(wb); HSSFEvaluationWorkbook eb = HSSFEvaluationWorkbook.create(wb);