diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml
index 1c6b2854a..183475c20 100644
--- a/src/documentation/content/xdocs/changes.xml
+++ b/src/documentation/content/xdocs/changes.xml
@@ -37,6 +37,8 @@
Check out the javadocs for details. +
Check out the javadocs for details.
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index bfd9364ce..9088ef28b 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,8 @@null
if there is any problem converting the text
+ */
+ private static Double convertTextToNumber(String strText) {
+ boolean foundCurrency = false;
+ boolean foundUnaryPlus = false;
+ boolean foundUnaryMinus = false;
+
+ int len = strText.length();
+ int i;
+ for (i = 0; i < len; i++) {
+ char ch = strText.charAt(i);
+ if (Character.isDigit(ch) || ch == '.') {
+ break;
+ }
+ switch (ch) {
+ case ' ':
+ // intervening spaces between '$', '-', '+' are OK
+ continue;
+ case '$':
+ if (foundCurrency) {
+ // only one currency symbols is allowed
+ return null;
+ }
+ foundCurrency = true;
+ continue;
+ case '+':
+ if (foundUnaryMinus || foundUnaryPlus) {
+ return null;
+ }
+ foundUnaryPlus = true;
+ continue;
+ case '-':
+ if (foundUnaryMinus || foundUnaryPlus) {
+ return null;
+ }
+ foundUnaryMinus = true;
+ continue;
+ default:
+ // all other characters are illegal
+ return null;
+ }
+ }
+ if (i >= len) {
+ // didn't find digits or '.'
+ if (foundCurrency || foundUnaryMinus || foundUnaryPlus) {
+ return null;
+ }
+ return ZERO;
+ }
+
+ // remove thousands separators
+
+ boolean foundDecimalPoint = false;
+ int lastThousandsSeparatorIndex = Short.MIN_VALUE;
+
+ StringBuffer sb = new StringBuffer(len);
+ for (; i < len; i++) {
+ char ch = strText.charAt(i);
+ if (Character.isDigit(ch)) {
+ sb.append(ch);
+ continue;
+ }
+ switch (ch) {
+ case ' ':
+ String remainingText = strText.substring(i);
+ if (remainingText.trim().length() > 0) {
+ // intervening spaces not allowed once the digits start
+ return null;
+ }
+ break;
+ case '.':
+ if (foundDecimalPoint) {
+ return null;
+ }
+ if (i - lastThousandsSeparatorIndex < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
+ return null;
+ }
+ foundDecimalPoint = true;
+ sb.append('.');
+ continue;
+ case ',':
+ if (foundDecimalPoint) {
+ // thousands separators not allowed after '.' or 'E'
+ return null;
+ }
+ int distanceBetweenThousandsSeparators = i - lastThousandsSeparatorIndex;
+ // as long as there are 3 or more digits between
+ if (distanceBetweenThousandsSeparators < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
+ return null;
+ }
+ lastThousandsSeparatorIndex = i;
+ // don't append ','
+ continue;
+
+ case 'E':
+ case 'e':
+ if (i - lastThousandsSeparatorIndex < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
+ return null;
+ }
+ // append rest of strText and skip to end of loop
+ sb.append(strText.substring(i));
+ i = len;
+ break;
+ default:
+ // all other characters are illegal
+ return null;
+ }
+ }
+ if (!foundDecimalPoint) {
+ if (i - lastThousandsSeparatorIndex < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
+ return null;
+ }
+ }
+ double d;
+ try {
+ d = Double.parseDouble(sb.toString());
+ } catch (NumberFormatException e) {
+ // still a problem parsing the number - probably out of range
+ return null;
+ }
+ return new Double(foundUnaryMinus ? -d : d);
+ }
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index 75568f4da..fd0e0a535 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -670,7 +670,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
/**
* @return the merged region at the specified index
*/
- public org.apache.poi.hssf.util.CellRangeAddress getMergedRegion(int index) {
+ public CellRangeAddress getMergedRegion(int index) {
return sheet.getMergedRegionAt(index);
}
diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddress.java b/src/java/org/apache/poi/hssf/util/CellRangeAddress.java
index cc67b8eb0..439db43db 100644
--- a/src/java/org/apache/poi/hssf/util/CellRangeAddress.java
+++ b/src/java/org/apache/poi/hssf/util/CellRangeAddress.java
@@ -18,12 +18,12 @@ package org.apache.poi.hssf.util;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.record.SelectionRecord;
-import org.apache.poi.util.LittleEndian;
/**
* See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'
*
* Note - {@link SelectionRecord} uses the BIFF5 version of this structure
+ * @deprecated use {@link org.apache.poi.ss.util.CellRangeAddress}
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
*/
public class CellRangeAddress extends org.apache.poi.ss.util.CellRangeAddress {
diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java b/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java
index 79ea50abb..5f22fb733 100644
--- a/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java
+++ b/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java
@@ -33,6 +33,8 @@ import org.apache.poi.util.LittleEndian;
* range address (called an ADDR structure) contains 4 16-bit-values.
*
*
+ * @deprecated use {@link org.apache.poi.ss.util.CellRangeAddressList}
+ *
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
*/
public class CellRangeAddressList extends org.apache.poi.ss.util.CellRangeAddressList {
diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddress.java b/src/java/org/apache/poi/ss/util/CellRangeAddress.java
index 0d910c682..3cc70cf48 100644
--- a/src/java/org/apache/poi/ss/util/CellRangeAddress.java
+++ b/src/java/org/apache/poi/ss/util/CellRangeAddress.java
@@ -16,6 +16,7 @@
package org.apache.poi.ss.util;
+import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.record.SelectionRecord;
import org.apache.poi.util.LittleEndian;
@@ -42,6 +43,17 @@ public class CellRangeAddress extends CellRangeAddressBase {
LittleEndian.putUShort(data, offset + 6, getLastColumn());
return ENCODED_SIZE;
}
+ public CellRangeAddress(RecordInputStream in) {
+ super(readUShortAndCheck(in), in.readUShort(), in.readUShort(), in.readUShort());
+ }
+
+ private static int readUShortAndCheck(RecordInputStream in) {
+ if (in.remaining() < ENCODED_SIZE) {
+ // Ran out of data
+ throw new RuntimeException("Ran out of data reading CellRangeAddress");
+ }
+ return in.readUShort();
+ }
public CellRangeAddress copy() {
return new CellRangeAddress(getFirstRow(), getLastRow(), getFirstColumn(), getLastColumn());
diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddressList.java b/src/java/org/apache/poi/ss/util/CellRangeAddressList.java
index 72b588248..65474ef98 100644
--- a/src/java/org/apache/poi/ss/util/CellRangeAddressList.java
+++ b/src/java/org/apache/poi/ss/util/CellRangeAddressList.java
@@ -18,6 +18,8 @@ package org.apache.poi.ss.util;
import java.util.ArrayList;
import java.util.List;
+
+import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
@@ -51,7 +53,17 @@ public class CellRangeAddressList {
this();
addCellRangeAddress(firstRow, firstCol, lastRow, lastCol);
}
+ /**
+ * @param in the RecordInputstream to read the record from
+ */
+ public CellRangeAddressList(RecordInputStream in) {
+ this();
+ int nItems = in.readUShort();
+ for (int k = 0; k < nItems; k++) {
+ _list.add(new CellRangeAddress(in));
+ }
+ }
/**
* Get the number of following ADDR structures. The number of this
* structures is automatically set when reading an Excel file and/or
diff --git a/src/testcases/org/apache/poi/hssf/record/TestMergeCellsRecord.java b/src/testcases/org/apache/poi/hssf/record/TestMergeCellsRecord.java
index c0a8f9fe7..b36da1aad 100644
--- a/src/testcases/org/apache/poi/hssf/record/TestMergeCellsRecord.java
+++ b/src/testcases/org/apache/poi/hssf/record/TestMergeCellsRecord.java
@@ -17,10 +17,18 @@
package org.apache.poi.hssf.record;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
+import org.apache.poi.hssf.model.RecordStream;
+import org.apache.poi.hssf.record.aggregates.MergedCellsTable;
+import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
import org.apache.poi.ss.util.CellRangeAddress;
+
/**
* Make sure the merge cells record behaves
* @author Danny Mui (dmui at apache dot org)
@@ -28,25 +36,45 @@ import org.apache.poi.ss.util.CellRangeAddress;
*/
public final class TestMergeCellsRecord extends TestCase {
- /**
- * Make sure when a clone is called, we actually clone it.
- * @throws Exception
- */
- public void testCloneReferences() throws Exception {
- CellRangeAddress[] cras = { new CellRangeAddress(0, 1, 0, 2), };
- MergeCellsRecord merge = new MergeCellsRecord(cras, 0, cras.length);
- MergeCellsRecord clone = (MergeCellsRecord)merge.clone();
+ /**
+ * Make sure when a clone is called, we actually clone it.
+ */
+ public void testCloneReferences() {
+ CellRangeAddress[] cras = { new CellRangeAddress(0, 1, 0, 2), };
+ MergeCellsRecord merge = new MergeCellsRecord(cras, 0, cras.length);
+ MergeCellsRecord clone = (MergeCellsRecord)merge.clone();
+
+ assertNotSame("Merged and cloned objects are the same", merge, clone);
+
+ CellRangeAddress mergeRegion = merge.getAreaAt(0);
+ CellRangeAddress cloneRegion = clone.getAreaAt(0);
+ assertNotSame("Should not point to same objects when cloning", mergeRegion, cloneRegion);
+ assertEquals("New Clone Row From doesnt match", mergeRegion.getFirstRow(), cloneRegion.getFirstRow());
+ assertEquals("New Clone Row To doesnt match", mergeRegion.getLastRow(), cloneRegion.getLastRow());
+ assertEquals("New Clone Col From doesnt match", mergeRegion.getFirstColumn(), cloneRegion.getFirstColumn());
+ assertEquals("New Clone Col To doesnt match", mergeRegion.getLastColumn(), cloneRegion.getLastColumn());
- assertNotSame("Merged and cloned objects are the same", merge, clone);
-
- CellRangeAddress mergeRegion = merge.getAreaAt(0);
- CellRangeAddress cloneRegion = clone.getAreaAt(0);
- assertNotSame("Should not point to same objects when cloning", mergeRegion, cloneRegion);
- assertEquals("New Clone Row From doesnt match", mergeRegion.getFirstRow(), cloneRegion.getFirstRow());
- assertEquals("New Clone Row To doesnt match", mergeRegion.getLastRow(), cloneRegion.getLastRow());
- assertEquals("New Clone Col From doesnt match", mergeRegion.getFirstColumn(), cloneRegion.getFirstColumn());
- assertEquals("New Clone Col To doesnt match", mergeRegion.getLastColumn(), cloneRegion.getLastColumn());
-
- assertFalse(merge.getAreaAt(0) == clone.getAreaAt(0));
- }
+ assertFalse(merge.getAreaAt(0) == clone.getAreaAt(0));
+ }
+
+ private static final RecordVisitor dummyRecordVisitor = new RecordVisitor() {
+ public void visitRecord(Record r) {
+ // do nothing
+ }
+ };
+ public void testMCTable_bug46009() {
+ MergedCellsTable mct = new MergedCellsTable();
+ List recList = new ArrayList();
+ CellRangeAddress[] cras = new CellRangeAddress[] {
+ new CellRangeAddress(0, 0, 0, 3),
+ };
+ recList.add(new MergeCellsRecord(cras, 0, 1));
+ RecordStream rs = new RecordStream(recList, 0);
+ mct.read(rs);
+ try {
+ mct.visitContainedRecords(dummyRecordVisitor);
+ } catch (ArrayStoreException e) {
+ throw new AssertionFailedError("Identified bug 46009");
+ }
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
index c57c40b62..834b5281d 100755
--- a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
+++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
@@ -32,6 +32,7 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestAverage.class);
result.addTestSuite(TestCountFuncs.class);
result.addTestSuite(TestDate.class);
+ result.addTestSuite(TestFind.class);
result.addTestSuite(TestFinanceLib.class);
result.addTestSuite(TestIndex.class);
result.addTestSuite(TestIndexFunctionFromSpreadsheet.class);
@@ -49,6 +50,7 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestStatsLib.class);
result.addTestSuite(TestTFunc.class);
result.addTestSuite(TestTrim.class);
+ result.addTestSuite(TestValue.class);
result.addTestSuite(TestXYNumericFunction.class);
return result;
}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestFind.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestFind.java
new file mode 100644
index 000000000..3a6df7354
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestFind.java
@@ -0,0 +1,76 @@
+/* ====================================================================
+ 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.functions;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.CellValue;
+
+/**
+ * Tests for {@link Find}
+ *
+ * @author Torstein Svendsen (torstei@officenet.no)
+ */
+public final class TestFind extends TestCase {
+
+ public void testFind() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFCell cell = wb.createSheet().createRow(0).createCell(0);
+
+ HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
+
+ confirmResult(fe, cell, "find(\"h\", \"haystack\")", 1);
+ confirmResult(fe, cell, "find(\"a\", \"haystack\",2)", 2);
+ confirmResult(fe, cell, "find(\"a\", \"haystack\",3)", 6);
+
+ // number args converted to text
+ confirmResult(fe, cell, "find(7, 32768)", 3);
+ confirmResult(fe, cell, "find(\"34\", 1341235233412, 3)", 10);
+ confirmResult(fe, cell, "find(5, 87654)", 4);
+
+ // Errors
+ confirmError(fe, cell, "find(\"n\", \"haystack\")", HSSFErrorConstants.ERROR_VALUE);
+ confirmError(fe, cell, "find(\"k\", \"haystack\",9)", HSSFErrorConstants.ERROR_VALUE);
+ confirmError(fe, cell, "find(\"k\", \"haystack\",#REF!)", HSSFErrorConstants.ERROR_REF);
+ confirmError(fe, cell, "find(\"k\", \"haystack\",0)", HSSFErrorConstants.ERROR_VALUE);
+ confirmError(fe, cell, "find(#DIV/0!, #N/A, #REF!)", HSSFErrorConstants.ERROR_DIV_0);
+ confirmError(fe, cell, "find(2, #N/A, #REF!)", HSSFErrorConstants.ERROR_NA);
+ }
+
+ private static void confirmResult(HSSFFormulaEvaluator fe, HSSFCell cell, String formulaText,
+ int expectedResult) {
+ cell.setCellFormula(formulaText);
+ fe.notifyUpdateCell(cell);
+ CellValue result = fe.evaluate(cell);
+ assertEquals(result.getCellType(), HSSFCell.CELL_TYPE_NUMERIC);
+ assertEquals(expectedResult, result.getNumberValue(), 0.0);
+ }
+
+ private static void confirmError(HSSFFormulaEvaluator fe, HSSFCell cell, String formulaText,
+ int expectedErrorCode) {
+ cell.setCellFormula(formulaText);
+ fe.notifyUpdateCell(cell);
+ CellValue result = fe.evaluate(cell);
+ assertEquals(result.getCellType(), HSSFCell.CELL_TYPE_ERROR);
+ assertEquals(expectedErrorCode, result.getErrorValue());
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestValue.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestValue.java
new file mode 100644
index 000000000..5f74fbf1a
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestValue.java
@@ -0,0 +1,94 @@
+/* ====================================================================
+ 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.functions;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+
+/**
+ * Tests for {@link Value}
+ *
+ * @author Josh Micich
+ */
+public final class TestValue extends TestCase {
+
+ private static Eval invokeValue(String strText) {
+ Eval[] args = new Eval[] { new StringEval(strText), };
+ return new Value().evaluate(args, -1, (short) -1);
+ }
+
+ private static void confirmValue(String strText, double expected) {
+ Eval result = invokeValue(strText);
+ assertEquals(NumberEval.class, result.getClass());
+ assertEquals(expected, ((NumberEval) result).getNumberValue(), 0.0);
+ }
+
+ private static void confirmValueError(String strText) {
+ Eval result = invokeValue(strText);
+ assertEquals(ErrorEval.class, result.getClass());
+ assertEquals(ErrorEval.VALUE_INVALID, result);
+ }
+
+ public void testBasic() {
+
+ confirmValue("100", 100);
+ confirmValue("-2.3", -2.3);
+ confirmValue(".5", 0.5);
+ confirmValue(".5e2", 50);
+ confirmValue(".5e-2", 0.005);
+ confirmValue(".5e+2", 50);
+ confirmValue("+5", 5);
+ confirmValue("$1,000", 1000);
+ confirmValue("100.5e1", 1005);
+ confirmValue("1,0000", 10000);
+ confirmValue("1,000,0000", 10000000);
+ confirmValue("1,000,0000,00000", 1000000000000.0);
+ confirmValue(" 100 ", 100);
+ confirmValue(" + 100", 100);
+ confirmValue("10000", 10000);
+ confirmValue("$-5", -5);
+ confirmValue("$.5", 0.5);
+ confirmValue("123e+5", 12300000);
+ confirmValue("1,000e2", 100000);
+ confirmValue("$10e2", 1000);
+ confirmValue("$1,000e2", 100000);
+ }
+
+ public void testErrors() {
+ confirmValueError("1+1");
+ confirmValueError("1 1");
+ confirmValueError("1,00.0");
+ confirmValueError("1,00");
+ confirmValueError("$1,00.5e1");
+ confirmValueError("1,00.5e1");
+ confirmValueError("1,0,000");
+ confirmValueError("1,00,000");
+ confirmValueError("++100");
+ confirmValueError("$$5");
+ confirmValueError("-");
+ confirmValueError("+");
+ confirmValueError("$");
+ confirmValueError(",300");
+ confirmValueError("0.233,4");
+ confirmValueError("1e2.5");
+ }
+}