From c79342aeacbf83c2361439512ce65031357d74df Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Thu, 5 Jun 2008 03:12:35 +0000 Subject: [PATCH] Fix for bug 45123 - SharedFormulaRecord.convertSharedFormulas was ignoring token operand classes git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@663436 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 1 + src/documentation/content/xdocs/status.xml | 1 + .../poi/hssf/record/SharedFormulaRecord.java | 10 +- .../poi/hssf/record/AllRecordTests.java | 1 + .../hssf/record/TestSharedFormulaRecord.java | 97 +++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 08aebb79d..5cc5a00e0 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 45123 - Fixed SharedFormulaRecord.convertSharedFormulas() to propagate token operand classes 45087 - Correctly detect date formats like [Black]YYYY as being date based 45060 - Improved token class transformation during formula parsing 44840 - Improved handling of HSSFObjectData, especially for entries with data held not in POIFS diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 0431bd3fd..b8c44d9b9 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 45123 - Fixed SharedFormulaRecord.convertSharedFormulas() to propagate token operand classes 45087 - Correctly detect date formats like [Black]YYYY as being date based 45060 - Improved token class transformation during formula parsing 44840 - Improved handling of HSSFObjectData, especially for entries with data held not in POIFS diff --git a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java index 2b0c50d12..e4c0f28ea 100755 --- a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java +++ b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java @@ -201,6 +201,10 @@ public final class SharedFormulaRecord extends Record { if (ptgs != null) for (int k = 0; k < ptgs.size(); k++) { Ptg ptg = (Ptg) ptgs.get(k); + byte originalOperandClass = -1; + if (!ptg.isBaseToken()) { + originalOperandClass = ptg.getPtgClass(); + } if (ptg instanceof RefNPtg) { RefNPtg refNPtg = (RefNPtg)ptg; ptg = new ReferencePtg(fixupRelativeRow(formulaRow,refNPtg.getRow(),refNPtg.isRowRelative()), @@ -249,7 +253,11 @@ public final class SharedFormulaRecord extends Record { areaNAPtg.isLastRowRelative(), areaNAPtg.isFirstColRelative(), areaNAPtg.isLastColRelative()); - } + } + if (!ptg.isBaseToken()) { + ptg.setClass(originalOperandClass); + } + newPtgStack.add(ptg); } return newPtgStack; diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java index 2b8e3c3ab..988be1dac 100755 --- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java +++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java @@ -95,6 +95,7 @@ public final class AllRecordTests { result.addTestSuite(TestSeriesTextRecord.class); result.addTestSuite(TestSeriesToChartGroupRecord.class); result.addTestSuite(TestSheetPropertiesRecord.class); + result.addTestSuite(TestSharedFormulaRecord.class); result.addTestSuite(TestStringRecord.class); result.addTestSuite(TestSubRecord.class); result.addTestSuite(TestSupBookRecord.class); diff --git a/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java new file mode 100644 index 000000000..cb08edec2 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java @@ -0,0 +1,97 @@ +/* ==================================================================== + 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; + +import java.util.List; +import java.util.Stack; + +import junit.framework.AssertionFailedError; +import junit.framework.ComparisonFailure; +import junit.framework.TestCase; + +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.formula.RefAPtg; + +/** + * @author Josh Micich + */ +public final class TestSharedFormulaRecord extends TestCase { + + /** + * Binary data for an encoded formula. Taken from attachment 22062 (bugzilla 45123/45421). + * The shared formula is in Sheet1!C6:C21, with text "SUMPRODUCT(--(End_Acct=$C6),--(End_Bal))" + * This data is found at offset 0x1A4A (within the shared formula record). + * The critical thing about this formula is that it contains shared formula tokens (tRefN*, + * tAreaN*) with operand class 'array'. + */ + private static final byte[] SHARED_FORMULA_WITH_REF_ARRAYS_DATA = { + 0x1A, 0x00, + 0x63, 0x02, 0x00, 0x00, 0x00, + 0x6C, 0x00, 0x00, 0x02, (byte)0x80, // tRefNA + 0x0B, + 0x15, + 0x13, + 0x13, + 0x63, 0x03, 0x00, 0x00, 0x00, + 0x15, + 0x13, + 0x13, + 0x42, 0x02, (byte)0xE4, 0x00, + }; + + /** + * The method SharedFormulaRecord.convertSharedFormulas() converts formulas from + * 'shared formula' to 'single cell formula' format. It is important that token operand + * classes are preserved during this transformation, because Excel may not tolerate the + * incorrect encoding. The formula here is one such example (Excel displays #VALUE!). + */ + public void testConvertSharedFormulasOperandClasses_bug45123() { + + TestcaseRecordInputStream in = new TestcaseRecordInputStream(0, SHARED_FORMULA_WITH_REF_ARRAYS_DATA); + short encodedLen = in.readShort(); + Stack sharedFormula = Ptg.createParsedExpressionTokens(encodedLen, in); + + Stack convertedFormula = SharedFormulaRecord.convertSharedFormulas(sharedFormula, 100, 200); + + RefAPtg refPtg = (RefAPtg) convertedFormula.get(1); + assertEquals("$C101", refPtg.toFormulaString(null)); + if (refPtg.getPtgClass() == Ptg.CLASS_REF) { + throw new AssertionFailedError("Identified bug 45123"); + } + + confirmOperandClasses(toPtgArray(sharedFormula), toPtgArray(convertedFormula)); + } + + private static void confirmOperandClasses(Ptg[] originalPtgs, Ptg[] convertedPtgs) { + assertEquals(originalPtgs.length, convertedPtgs.length); + for (int i = 0; i < convertedPtgs.length; i++) { + Ptg originalPtg = originalPtgs[i]; + Ptg convertedPtg = convertedPtgs[i]; + if (originalPtg.getPtgClass() != convertedPtg.getPtgClass()) { + throw new ComparisonFailure("Different operand class for token[" + i + "]", + String.valueOf(originalPtg.getPtgClass()), String.valueOf(convertedPtg.getPtgClass())); + } + } + } + + private static Ptg[] toPtgArray(List list) { + Ptg[] result = new Ptg[list.size()]; + list.toArray(result); + return result; + } +}