]> source.dussan.org Git - poi.git/commitdiff
Bugzilla 46973 - fixed defined names to behave better when refersToFormula is unset
authorJosh Micich <josh@apache.org>
Mon, 6 Apr 2009 19:57:21 +0000 (19:57 +0000)
committerJosh Micich <josh@apache.org>
Mon, 6 Apr 2009 19:57:21 +0000 (19:57 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@762479 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/record/formula/Ptg.java
src/java/org/apache/poi/hssf/usermodel/HSSFName.java
src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Name.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java
src/testcases/org/apache/poi/ss/usermodel/BaseTestNamedRange.java

index cd5b4fd678a3a16381fadfaf168182a275d0c1cb..4929d519e6bee7acdfb48632b2fd44fd1e8a6ac3 100644 (file)
@@ -37,6 +37,7 @@
 
                <!-- Don't forget to update status.xml too! -->
         <release version="3.5-beta6" date="2009-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">46973 - Fixed defined names to behave better when refersToFormula is unset</action>
            <action dev="POI-DEVELOPERS" type="fix">46832 - Allow merged regions with columns greater than 255 or rows bigger than 65536 in XSSF</action>
            <action dev="POI-DEVELOPERS" type="fix">46951 - Fixed formula parser to better handle range operators and whole row/column refs.</action>
            <action dev="POI-DEVELOPERS" type="fix">46948 - Fixed evaluation of range operator to allow for area-ref operands</action>
index 1fd834463086c08226b0515f9fe6470b2ccd0e4c..8077cd8de29165260aa0881171c1bbb5d7464cc7 100644 (file)
@@ -34,6 +34,7 @@
        <!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.5-beta6" date="2009-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">46973 - Fixed defined names to behave better when refersToFormula is unset</action>
            <action dev="POI-DEVELOPERS" type="fix">46832 - Allow merged regions with columns greater than 255 or rows bigger than 65536 in XSSF</action>
            <action dev="POI-DEVELOPERS" type="fix">46951 - Fixed formula parser to better handle range operators and whole row/column refs.</action>
            <action dev="POI-DEVELOPERS" type="fix">46948 - Fixed evaluation of range operator to allow for area-ref operands</action>
index 08f690ce4760f335760748c80b4551c977e92aba..46c3ab952622f60ca8f84a984c2d587c57c08ea4 100644 (file)
@@ -321,4 +321,32 @@ public abstract class Ptg implements Cloneable {
         * @return <code>false</code> if this token is classified as 'reference', 'value', or 'array'
         */
        public abstract boolean isBaseToken();
+
+       public static boolean doesFormulaReferToDeletedCell(Ptg[] ptgs) {
+               for (int i = 0; i < ptgs.length; i++) {
+                       if (isDeletedCellRef(ptgs[i])) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+       private static boolean isDeletedCellRef(Ptg ptg) {
+               if (ptg == ErrPtg.REF_INVALID) {
+                       return true;
+               }
+               if (ptg instanceof DeletedArea3DPtg) {
+                       return true;
+               }
+               if (ptg instanceof DeletedRef3DPtg) {
+                       return true;
+               }
+               if (ptg instanceof AreaErrPtg) {
+                       return true;
+               }
+               if (ptg instanceof RefErrorPtg) {
+                       return true;
+               }
+               return false;
+       }
+
 }
index a0f278cc6cd0d66eb7d611553d6223a584bb4356..bceaf340adf9e120854752a93d56dea5a0bf3e1f 100644 (file)
@@ -21,8 +21,8 @@ import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.NameRecord;
 import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.ss.usermodel.Name;
 import org.apache.poi.ss.formula.FormulaType;
+import org.apache.poi.ss.usermodel.Name;
 
 /**
  * High Level Representation of a 'defined name' which could be a 'built-in' name,
@@ -160,46 +160,26 @@ public final class HSSFName implements Name {
         setRefersToFormula(ref);
     }
 
-    /**
-     * Sets the formula that the name is defined to refer to. The following are representative examples:
-     *
-     * <ul>
-     *  <li><code>'My Sheet'!$A$3</code></li>
-     *  <li><code>8.3</code></li>
-     *  <li><code>HR!$A$1:$Z$345</code></li>
-     *  <li><code>SUM(Sheet1!A1,Sheet2!B2)</li>
-     *  <li><code>-PMT(Interest_Rate/12,Number_of_Payments,Loan_Amount)</li>
-     * </ul>
-     *
-     * @param formulaText the reference for this name
-     * @throws IllegalArgumentException if the specified reference is unparsable
-    */
     public void setRefersToFormula(String formulaText) {
         Ptg[] ptgs = HSSFFormulaParser.parse(formulaText, _book, FormulaType.NAMEDRANGE, getSheetIndex());
         _definedNameRec.setNameDefinition(ptgs);
     }
 
-    /**
-     * Returns the formula that the name is defined to refer to. The following are representative examples:
-     *
-     * @return the reference for this name
-     * @see #setRefersToFormula(String)
-     */
     public String getRefersToFormula() {
         if (_definedNameRec.isFunctionName()) {
             throw new IllegalStateException("Only applicable to named ranges");
         }
-        return HSSFFormulaParser.toFormulaString(_book, _definedNameRec.getNameDefinition());
+        Ptg[] ptgs = _definedNameRec.getNameDefinition();
+        if (ptgs.length < 1) {
+            // 'refersToFormula' has not been set yet
+            return null;
+        }
+        return HSSFFormulaParser.toFormulaString(_book, ptgs);
     }
 
-    /**
-     * Tests if this name points to a cell that no longer exists
-     *
-     * @return true if the name refers to a deleted cell, false otherwise
-     */
     public boolean isDeleted(){
-        String formulaText = getRefersToFormula();
-        return formulaText.indexOf("#REF!") != -1;
+        Ptg[] ptgs = _definedNameRec.getNameDefinition();
+        return Ptg.doesFormulaReferToDeletedCell(ptgs);
     }
 
     /**
index 37eff5a422515345d1b501aab11f7dfd75e64b99..696eb3c1aceea341fc5504eefb7707ec4dec0d16 100644 (file)
@@ -118,7 +118,7 @@ public interface Name {
     /**
      * Returns the formula that the name is defined to refer to. 
      *
-     * @return the reference for this name
+     * @return the reference for this name, <code>null</code> if it has not been set yet. Never empty string
      * @see #setRefersToFormula(String)
      */
     String getRefersToFormula();
@@ -134,10 +134,10 @@ public interface Name {
      *  <li><code>-PMT(Interest_Rate/12,Number_of_Payments,Loan_Amount)</li>
      * </ul>
      *
-     * @param ref the reference for this name
-     * @throws IllegalArgumentException if the specified reference is unparsable
+     * @param formulaText the reference for this name
+     * @throws IllegalArgumentException if the specified formulaText is unparsable
     */
-   void setRefersToFormula(String ref);
+   void setRefersToFormula(String formulaText);
 
     /**
      * Checks if this name is a function name
@@ -149,7 +149,7 @@ public interface Name {
     /**
      * Checks if this name points to a cell that no longer exists
      *
-     * @return true if the name refers to a deleted cell, false otherwise
+     * @return <code>true</code> if the name refers to a deleted cell, <code>false</code> otherwise
      */
     boolean isDeleted();
 
index 4517f1292fecb83327496162dab758c62f25b32c..9f3c7f0125955ab2dac32aae6ad1dace3b283b18 100644 (file)
@@ -181,25 +181,18 @@ public final class XSSFName implements Name {
         ctName.setName(name);
     }
 
-    /**
-     * Returns the reference of this named range, such as Sales!C20:C30.
-     *
-     * @return the reference of this named range
-     */
     public String getRefersToFormula() {
-        return ctName.getStringValue();
+        String result = ctName.getStringValue();
+        if (result == null || result.length() < 1) {
+            return null;
+        }
+        return result;
     }
 
-    /**
-     * Sets the reference of this named range, such as Sales!C20:C30.
-     *
-     * @param formulaText the reference to set
-     * @throws IllegalArgumentException if the specified reference is unparsable
-     */
     public void setRefersToFormula(String formulaText) {
         XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(workbook);
         try {
-            Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex());
+            FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex());
         } catch (RuntimeException e) {
             if (e.getClass().getName().startsWith(FormulaParser.class.getName())) {
                 throw new IllegalArgumentException("Unparsable formula '" + formulaText + "'", e);
@@ -209,14 +202,14 @@ public final class XSSFName implements Name {
         ctName.setStringValue(formulaText);
     }
 
-    /**
-     * Tests if this name points to a cell that no longer exists
-     *
-     * @return true if the name refers to a deleted cell, false otherwise
-     */
     public boolean isDeleted(){
-        String ref = getRefersToFormula();
-        return ref != null && ref.indexOf("#REF!") != -1;
+        String formulaText = getRefersToFormula();
+        if (formulaText == null) {
+            return false;
+        }
+        XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(workbook);
+        Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex());
+        return Ptg.doesFormulaReferToDeletedCell(ptgs);
     }
 
     /**
index 253d6b50aa3e2b2afeac586d0d8b0f5730f72449..a645db57b36a14222f351e87c23d3634f64a6f6b 100755 (executable)
 
 package org.apache.poi.ss.usermodel;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
+
+import org.apache.poi.hssf.usermodel.HSSFName;
 import org.apache.poi.ss.ITestDataProvider;
-import org.apache.poi.ss.formula.FormulaParser;
-import org.apache.poi.ss.formula.FormulaType;
-import org.apache.poi.ss.util.CellReference;
 import org.apache.poi.ss.util.AreaReference;
-import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.ss.util.CellReference;
 
 /**
  * Tests of implementations of {@link org.apache.poi.ss.usermodel.Name}.
@@ -395,7 +395,7 @@ public abstract class BaseTestNamedRange extends TestCase {
      * Test that multiple named ranges can be added written and read
      */
     public void testMultipleNamedWrite() {
-        Workbook wb     = getTestDataProvider().createWorkbook();
+        Workbook wb = getTestDataProvider().createWorkbook();
 
 
         wb.createSheet("testSheet1");
@@ -498,4 +498,50 @@ public abstract class BaseTestNamedRange extends TestCase {
         assertEquals("Contents of cell retrieved by its named reference", contents, cvalue);
     }
 
+    
+    /**
+     * Bugzilla attachment 23444 (from bug 46973) has a NAME record with the following encoding: 
+     * <pre>
+     * 00000000 | 18 00 17 00 00 00 00 08 00 00 00 00 00 00 00 00 | ................
+     * 00000010 | 00 00 00 55 50 53 53 74 61 74 65                | ...UPSState
+     * </pre>     
+     * 
+     * This caused trouble for anything that requires {@link HSSFName#getRefersToFormula()}
+     * It is easy enough to re-create the the same data (by not setting the formula). Excel
+     * seems to gracefully remove this uninitialized name record.  It would be nice if POI
+     * could do the same, but that would involve adjusting subsequent name indexes across 
+     * all formulas. <p/>
+     * 
+     * For the moment, POI has been made to behave more sensibly with uninitialised name 
+     * records.
+     */
+    public final void testUninitialisedNameGetRefersToFormula_bug46973() {
+        Workbook wb = getTestDataProvider().createWorkbook();
+        Name n = wb.createName();
+        n.setNameName("UPSState");
+        String formula;
+        try {
+            formula = n.getRefersToFormula();
+        } catch (IllegalArgumentException e) {
+            if (e.getMessage().equals("ptgs must not be null")) {
+                throw new AssertionFailedError("Identified bug 46973");
+            }
+            throw e;
+        }
+        assertNull(formula);
+        assertFalse(n.isDeleted()); // according to exact definition of isDeleted()
+    }
+
+    public void testDeletedCell() {
+        Workbook wb = getTestDataProvider().createWorkbook();
+        Name n = wb.createName();
+        n.setNameName("MyName");
+        // contrived example to expose bug:
+        n.setRefersToFormula("if(A1,\"#REF!\", \"\")");
+
+        if (n.isDeleted()) {
+            throw new AssertionFailedError("Identified bug in recoginising formulas referring to deleted cells");
+        }
+
+    }
 }
\ No newline at end of file