Ptg
+ * tokens into human readable text form. In formula expressions, a sheet name always has a
+ * trailing '!' so there is little chance for ambiguity. It doesn't matter too much what this
+ * method returns but it is worth noting the likely consumers of these formula text strings:
+ * + * POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for + * this method: + *+ *
+ * Version File Format + *Last Column Last Row + * 97-2003 BIFF8 "IV" (2^8) 65536 (2^14) + * 2007 BIFF12 "XFD" (2^14) 1048576 (2^20)
+ */ + /* package */ static boolean cellReferenceIsWithinRange(String rawSheetName, int numberOfLetters) { + + if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) { + // "Sheet1" case etc + return false; // that was easy + } + int nDigits = rawSheetName.length() - numberOfLetters; + if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) { + return false; + } + if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) { + String colStr = rawSheetName.substring(0, BIFF8_LAST_COLUMN_TEXT_LEN).toUpperCase(); + if(colStr.compareTo(BIFF8_LAST_COLUMN) > 0) { + return false; + } + } else { + // apparent column name has less chars than max + // no need to check range + } + + if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) { + String colStr = rawSheetName.substring(numberOfLetters); + // ASCII comparison is valid if digit count is same + if(colStr.compareTo(BIFF8_LAST_ROW) > 0) { + return false; + } + } else { + // apparent row has less chars than max + // no need to check range + } + + return true; + } + + /** + * Note - this method assumes the specified rawSheetName has only letters and digits. It + * cannot be used to match absolute or range references (using the dollar or colon char). + * + * Some notable cases: + *+ *
+ * Input + *Result + * "A1", 1 true + * "a111", 1 true + * "A65536", 1 true + * "A65537", 1 false + * "iv1", 2 true + * "IW1", 2 false + * "AAA1", 3 false + * "a111", 1 true + * "Sheet1", 6 false
+ * + * @return+ *
+ * Input Result Comments + * "A1" true + * "a111" true + * "AA" false + * "aa1" true + * "A1A" false + * "A1A1" false + * "A$1:$C$20" false Not a plain cell reference + * "SALES20080101" true + *Still needs delimiting even though well out of range
true if there is any possible ambiguity that the specified rawSheetName
+ * could be interpreted as a valid cell name.
+ */
+ /* package */ static boolean nameLooksLikePlainCellReference(String rawSheetName) {
+ Matcher matcher = CELL_REF_PATTERN.matcher(rawSheetName);
+ if(!matcher.matches()) {
+ return false;
+ }
+
+ // rawSheetName == "Sheet1" gets this far.
+ String lettersPrefix = matcher.group(1);
+ return cellReferenceIsWithinRange(rawSheetName, lettersPrefix.length());
+ }
+
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java b/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java
index cd2acc7ea9..6c2e3b6412 100644
--- a/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java
@@ -82,7 +82,7 @@ public class TestFormulaEvaluatorDocs extends TestCase {
assertEquals(HSSFCell.CELL_TYPE_FORMULA, wb.getSheetAt(0).getRow(1).getCell((short)2).getCellType());
assertEquals(22.3, wb.getSheetAt(1).getRow(0).getCell((short)0).getNumericCellValue(), 0);
- assertEquals("S1!A1", wb.getSheetAt(1).getRow(0).getCell((short)0).getCellFormula());
+ assertEquals("'S1'!A1", wb.getSheetAt(1).getRow(0).getCell((short)0).getCellFormula());
assertEquals(HSSFCell.CELL_TYPE_FORMULA, wb.getSheetAt(1).getRow(0).getCell((short)0).getCellType());
diff --git a/src/testcases/org/apache/poi/hssf/HSSFTests.java b/src/testcases/org/apache/poi/hssf/HSSFTests.java
index 1bc9df1798..1e0edd6825 100644
--- a/src/testcases/org/apache/poi/hssf/HSSFTests.java
+++ b/src/testcases/org/apache/poi/hssf/HSSFTests.java
@@ -75,13 +75,7 @@ import org.apache.poi.hssf.record.TestUnitsRecord;
import org.apache.poi.hssf.record.TestValueRangeRecord;
import org.apache.poi.hssf.record.aggregates.TestRowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.TestValueRecordsAggregate;
-import org.apache.poi.hssf.record.formula.TestAreaErrPtg;
-import org.apache.poi.hssf.record.formula.TestErrPtg;
-import org.apache.poi.hssf.record.formula.TestFuncPtg;
-import org.apache.poi.hssf.record.formula.TestIntersectionPtg;
-import org.apache.poi.hssf.record.formula.TestPercentPtg;
-import org.apache.poi.hssf.record.formula.TestRangePtg;
-import org.apache.poi.hssf.record.formula.TestUnionPtg;
+import org.apache.poi.hssf.record.formula.AllFormulaTests;
import org.apache.poi.hssf.usermodel.TestBugs;
import org.apache.poi.hssf.usermodel.TestCellStyle;
import org.apache.poi.hssf.usermodel.TestCloneSheet;
@@ -215,13 +209,7 @@ public class HSSFTests
suite.addTest(new TestSuite(TestSheetReferences.class));
- suite.addTest(new TestSuite(TestAreaErrPtg.class));
- suite.addTest(new TestSuite(TestErrPtg.class));
- suite.addTest(new TestSuite(TestFuncPtg.class));
- suite.addTest(new TestSuite(TestIntersectionPtg.class));
- suite.addTest(new TestSuite(TestPercentPtg.class));
- suite.addTest(new TestSuite(TestRangePtg.class));
- suite.addTest(new TestSuite(TestUnionPtg.class));
+ suite.addTest(AllFormulaTests.suite());
suite.addTest(new TestSuite(TestValueRecordsAggregate.class));
suite.addTest(new TestSuite(TestNameRecord.class));
suite.addTest(new TestSuite(TestEventRecordFactory.class));
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/AbstractPtgTestCase.java b/src/testcases/org/apache/poi/hssf/record/formula/AbstractPtgTestCase.java
index 316dfb348e..21e6a8ba3c 100644
--- a/src/testcases/org/apache/poi/hssf/record/formula/AbstractPtgTestCase.java
+++ b/src/testcases/org/apache/poi/hssf/record/formula/AbstractPtgTestCase.java
@@ -26,6 +26,7 @@ import java.io.InputStream;
import junit.framework.TestCase;
+import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
@@ -34,7 +35,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
*
* @author Daniel Noll (daniel at nuix dot com dot au)
*/
-public class AbstractPtgTestCase extends TestCase
+public abstract class AbstractPtgTestCase extends TestCase
{
/** Directory containing the test data. */
private static String dataDir = System.getProperty("HSSF.testdata.path");
@@ -46,7 +47,7 @@ public class AbstractPtgTestCase extends TestCase
* @return the loaded workbook.
* @throws IOException if an error occurs loading the workbook.
*/
- protected static HSSFWorkbook loadWorkbook(String filename)
+ protected static final HSSFWorkbook loadWorkbook(String filename)
throws IOException {
File file = new File(dataDir, filename);
InputStream stream = new BufferedInputStream(new FileInputStream(file));
@@ -59,4 +60,18 @@ public class AbstractPtgTestCase extends TestCase
stream.close();
}
}
+
+ /**
+ * Creates a new Workbook and adds one sheet with the specified name
+ */
+ protected static final Workbook createWorkbookWithSheet(String sheetName) {
+
+ Workbook book = Workbook.createWorkbook();
+ // this creates sheet if it doesn't exist
+ book.checkExternSheet(0);
+ // TODO - this call alone does not create the sheet even though the javadoc says it does
+ book.setSheetName(0, sheetName);
+ return book;
+ }
+
}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java
new file mode 100644
index 0000000000..b126813387
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java
@@ -0,0 +1,47 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+package org.apache.poi.hssf.record.formula;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Collects all tests for this package.
+ *
+ * @author Josh Micich
+ */
+public class AllFormulaTests {
+
+ public static Test suite() {
+ TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula");
+ result.addTestSuite(TestArea3DPtg.class);
+ result.addTestSuite(TestAreaErrPtg.class);
+ result.addTestSuite(TestAreaPtg.class);
+ result.addTestSuite(TestErrPtg.class);
+ result.addTestSuite(TestFuncPtg.class);
+ result.addTestSuite(TestIntersectionPtg.class);
+ result.addTestSuite(TestPercentPtg.class);
+ result.addTestSuite(TestRangePtg.class);
+ result.addTestSuite(TestRef3DPtg.class);
+ result.addTestSuite(TestReferencePtg.class);
+ result.addTestSuite(TestSheetNameFormatter.class);
+ result.addTestSuite(TestUnionPtg.class);
+ return result;
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestArea3DPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestArea3DPtg.java
new file mode 100644
index 0000000000..2af50d4b93
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/formula/TestArea3DPtg.java
@@ -0,0 +1,50 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+package org.apache.poi.hssf.record.formula;
+
+import org.apache.poi.hssf.model.Workbook;
+
+/**
+ * Tests for Area3DPtg
+ *
+ * @author Josh Micich
+ */
+public final class TestArea3DPtg extends AbstractPtgTestCase {
+
+ /**
+ * confirms that sheet names get properly escaped
+ */
+ public void testToFormulaString() {
+
+ Area3DPtg target = new Area3DPtg("A1:B1", (short)0);
+
+ String sheetName = "my sheet";
+ Workbook book = createWorkbookWithSheet(sheetName);
+ assertEquals("'my sheet'!A1:B1", target.toFormulaString(book));
+
+ book.setSheetName(0, "Sheet1");
+ assertEquals("Sheet1!A1:B1", target.toFormulaString(book));
+
+ book.setSheetName(0, "C64");
+ assertEquals("'C64'!A1:B1", target.toFormulaString(book));
+ }
+
+
+
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java
old mode 100755
new mode 100644
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestRef3DPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestRef3DPtg.java
new file mode 100644
index 0000000000..b5fbe3b80d
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/formula/TestRef3DPtg.java
@@ -0,0 +1,44 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+package org.apache.poi.hssf.record.formula;
+
+import org.apache.poi.hssf.model.Workbook;
+
+/**
+ * Tests for Ref3DPtg
+ *
+ * @author Josh Micich
+ */
+public final class TestRef3DPtg extends AbstractPtgTestCase {
+
+ public void testToFormulaString() {
+
+ Ref3DPtg target = new Ref3DPtg("A1", (short)0);
+
+ Workbook book = createWorkbookWithSheet("my sheet");
+
+ assertEquals("'my sheet'!A1", target.toFormulaString(book));
+
+ book.setSheetName(0, "ProfitAndLoss");
+ assertEquals("ProfitAndLoss!A1", target.toFormulaString(book));
+
+ book.setSheetName(0, "profit+loss");
+ assertEquals("'profit+loss'!A1", target.toFormulaString(book));
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java b/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java
new file mode 100644
index 0000000000..768c429580
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java
@@ -0,0 +1,99 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+package org.apache.poi.hssf.record.formula;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for SheetNameFormatter
+ *
+ * @author Josh Micich
+ */
+public final class TestSheetNameFormatter extends TestCase {
+
+ public TestSheetNameFormatter(String testName) {
+ super(testName);
+ }
+
+ private static void confirmFormat(String rawSheetName, String expectedSheetNameEncoding) {
+ assertEquals(expectedSheetNameEncoding, SheetNameFormatter.format(rawSheetName));
+ }
+
+ /**
+ * Tests main public method 'format'
+ */
+ public void testFormat() {
+
+ confirmFormat("abc", "abc");
+ confirmFormat("123", "'123'");
+
+ confirmFormat("my sheet", "'my sheet'"); // space
+ confirmFormat("A:MEM", "'A:MEM'"); // colon
+
+ confirmFormat("O'Brian", "'O''Brian'"); // single quote gets doubled
+
+
+ confirmFormat("3rdTimeLucky", "'3rdTimeLucky'"); // digit in first pos
+ confirmFormat("_", "_"); // plain underscore OK
+ confirmFormat("my_3rd_sheet", "my_3rd_sheet"); // underscores and digits OK
+ confirmFormat("A12220", "'A12220'");
+ confirmFormat("TAXRETURN19980415", "TAXRETURN19980415");
+ }
+
+ private static void confirmCellNameMatch(String rawSheetName, boolean expected) {
+ assertEquals(expected, SheetNameFormatter.nameLooksLikePlainCellReference(rawSheetName));
+ }
+
+ /**
+ * Tests functionality to determine whether a sheet name containing only letters and digits
+ * would look (to Excel) like a cell name.
+ */
+ public void testLooksLikePlainCellReference() {
+
+ confirmCellNameMatch("A1", true);
+ confirmCellNameMatch("a111", true);
+ confirmCellNameMatch("AA", false);
+ confirmCellNameMatch("aa1", true);
+ confirmCellNameMatch("A1A", false);
+ confirmCellNameMatch("A1A1", false);
+ confirmCellNameMatch("SALES20080101", false); // out of range
+ }
+
+ private static void confirmCellRange(String text, int numberOfPrefixLetters, boolean expected) {
+ assertEquals(expected, SheetNameFormatter.cellReferenceIsWithinRange(text, numberOfPrefixLetters));
+ }
+
+ /**
+ * Tests exact boundaries for names that look very close to cell names (i.e. contain 1 or more
+ * letters followed by one or more digits).
+ */
+ public void testCellRange() {
+ confirmCellRange("A1", 1, true);
+ confirmCellRange("a111", 1, true);
+ confirmCellRange("A65536", 1, true);
+ confirmCellRange("A65537", 1, false);
+ confirmCellRange("iv1", 2, true);
+ confirmCellRange("IW1", 2, false);
+ confirmCellRange("AAA1", 3, false);
+ confirmCellRange("a111", 1, true);
+ confirmCellRange("Sheet1", 6, false);
+ confirmCellRange("iV65536", 2, true); // max cell in Excel 97-2003
+ confirmCellRange("IW65537", 2, false);
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java b/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java
index f804822d24..f22de1758c 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java
@@ -351,7 +351,7 @@ public class TestNamedRange
String retrievedPrintArea = workbook.getPrintArea(0);
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
- assertEquals(reference, retrievedPrintArea);
+ assertEquals("'" + sheetName + "'!$A$1:$B$1", retrievedPrintArea);
}
@@ -370,7 +370,7 @@ public class TestNamedRange
String retrievedPrintArea = workbook.getPrintArea(0);
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
- assertEquals(sheetName+"!"+reference, retrievedPrintArea);
+ assertEquals("'" + sheetName + "'!" + reference, retrievedPrintArea);
}
@@ -437,7 +437,7 @@ public class TestNamedRange
String retrievedPrintArea = workbook.getPrintArea(0);
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
- assertEquals("References Match", reference, retrievedPrintArea);
+ assertEquals("References Match", "'" + sheetName + "'!$A$1:$B$1", retrievedPrintArea);
}
@@ -449,9 +449,9 @@ public class TestNamedRange
{
HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheet = workbook.createSheet("Sheet 1");
- sheet = workbook.createSheet("Sheet 2");
- sheet = workbook.createSheet("Sheet 3");
+ HSSFSheet sheet = workbook.createSheet("Sheet1");
+ sheet = workbook.createSheet("Sheet2");
+ sheet = workbook.createSheet("Sheet3");
String sheetName = workbook.getSheetName(0);
String reference = null;
@@ -508,9 +508,30 @@ public class TestNamedRange
String retrievedPrintArea = workbook.getPrintArea(0);
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
- assertEquals(reference, retrievedPrintArea);
+ assertEquals("'" + sheetName + "'!$A$1:$B$1", retrievedPrintArea);
}
-
+
+
+ /**
+ * Tests the parsing of union area expressions, and re-display in the presence of sheet names
+ * with special characters.
+ */
+ public void testPrintAreaUnion(){
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Test Print Area");
+ String sheetName = workbook.getSheetName(0);
+
+
+ String reference = sheetName + "!$A$1:$B$1, " + sheetName + "!$D$1:$F$2";
+ String expResult = "'" + sheetName + "'!$A$1:$B$1,'" + sheetName + "'!$D$1:$F$2";
+ workbook.setPrintArea(0, reference);
+
+ String retrievedPrintArea = workbook.getPrintArea(0);
+
+ assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
+ assertEquals(expResult, retrievedPrintArea);
+ }
+
/**
* Verifies an existing print area is deleted
*