]> source.dussan.org Git - poi.git/commitdiff
Introduce name-based methods for access to named ranges in a workbook, and deprecate...
authorDavid North <dnorth@apache.org>
Fri, 29 Jul 2016 13:24:00 +0000 (13:24 +0000)
committerDavid North <dnorth@apache.org>
Fri, 29 Jul 2016 13:24:00 +0000 (13:24 +0000)
Re-organise internally to use a map so that lookup is O(1) rather than O(n ranges).

Add a dependency on commons collections 4, also ASL licensed so no problem.

https://bz.apache.org/bugzilla/show_bug.cgi?id=59734

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1754521 13f79535-47bb-0310-9956-ffa450edef68

16 files changed:
.classpath
build.xml
maven/poi-ooxml.pom
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/ss/usermodel/Workbook.java
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFFormulaUtils.java
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFName.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java
src/testcases/org/apache/poi/ss/usermodel/BaseTestNamedRange.java
src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftRows.java

index ae3f06128504063a83501af6259b3f7017a674aa..3b3ff5b64cec9126d505d05fab989f815812cf5e 100644 (file)
        <classpathentry kind="lib" path="lib/ant-1.9.4.jar"/>
        <classpathentry kind="lib" path="lib/ant-launcher-1.9.4.jar"/>
        <classpathentry kind="lib" path="lib/log4j-1.2.17.jar"/>
-       <classpathentry kind="lib" path="ooxml-lib/xmlbeans-2.6.0.jar"/>
+       <classpathentry exported="true" kind="lib" path="ooxml-lib/xmlbeans-2.6.0.jar"/>
        <classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
        <classpathentry kind="lib" path="lib/junit-4.12.jar"/>
        <classpathentry kind="lib" path="ooxml-lib/curvesapi-1.04.jar"/>
-       <classpathentry kind="lib" path="ooxml-lib/ooxml-schemas-1.3.jar" sourcepath="ooxml-lib/ooxml-schemas-1.3-sources.jar"/>
-       <classpathentry kind="lib" path="ooxml-lib/ooxml-security-1.1.jar" sourcepath="ooxml-lib/ooxml-security-1.1-sources.jar"/>
+       <classpathentry exported="true" kind="lib" path="ooxml-lib/ooxml-schemas-1.3.jar" sourcepath="ooxml-lib/ooxml-schemas-1.3-sources.jar"/>
+       <classpathentry exported="true" kind="lib" path="ooxml-lib/ooxml-security-1.1.jar" sourcepath="ooxml-lib/ooxml-security-1.1-sources.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
-       <classpathentry kind="lib" path="compile-lib/slf4j-api-1.7.12.jar"/>
+       <classpathentry exported="true" kind="lib" path="compile-lib/slf4j-api-1.7.12.jar"/>
        <classpathentry kind="lib" path="compile-lib/bcpkix-jdk15on-1.54.jar"/>
        <classpathentry kind="lib" path="compile-lib/bcprov-ext-jdk15on-1.54.jar"/>
-       <classpathentry kind="lib" path="compile-lib/xmlsec-2.0.6.jar"/>
-       <classpathentry kind="lib" path="lib/commons-codec-1.10.jar"/>
-       <classpathentry kind="lib" path="lib/commons-logging-1.2.jar"/>
+       <classpathentry exported="true" kind="lib" path="compile-lib/xmlsec-2.0.6.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/commons-codec-1.10.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/commons-logging-1.2.jar"/>
+       <classpathentry kind="lib" path="ooxml-lib/commons-collections4-4.1.jar"/>
        <classpathentry kind="output" path="build/eclipse"/>
 </classpath>
index 0285acae310a7c642b069cf907928e5a8833e279..55abab1f2f92d3ecb42ddeb1a876c9ccc3442248 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -184,6 +184,9 @@ under the License.
     <property name="ooxml.curvesapi.jar" location="${ooxml.lib}/curvesapi-1.04.jar"/>
     <property name="ooxml.curvesapi.url"
               value="${repository.m2}/maven2/com/github/virtuald/curvesapi/1.04/curvesapi-1.04.jar"/>
+    <property name="ooxml.commons-collections4.jar" location="${ooxml.lib}/commons-collections4-4.1.jar"/>
+    <property name="ooxml.commons-collections4.url"
+              value="${repository.m2}/maven2/org/apache/commons/commons-collections4/4.1/commons-collections4-4.1.jar"/>
     <property name="ooxml.xmlbeans23.jar" location="${ooxml.lib}/xmlbeans-2.3.0.jar"/>
     <property name="ooxml.xmlbeans23.url"
               value="${repository.m2}/maven2/org/apache/xmlbeans/xmlbeans/2.3.0/xmlbeans-2.3.0.jar"/>
@@ -326,6 +329,7 @@ under the License.
 
     <path id="ooxml.classpath">
         <pathelement location="${ooxml.curvesapi.jar}"/>
+        <pathelement location="${ooxml.commons-collections4.jar}"/>
         <pathelement location="${ooxml.xmlbeans26.jar}"/>
         <pathelement location="${ooxml.xsds.jar}"/>
         <path refid="main.classpath"/>
@@ -623,6 +627,7 @@ under the License.
             <or>
                 <and>
                     <available file="${ooxml.curvesapi.jar}"/>
+                    <available file="${ooxml.commons-collections4.jar}"/>
                     <available file="${ooxml.xmlbeans23.jar}"/>
                     <available file="${ooxml.xmlbeans26.jar}"/>
                 </and>
@@ -633,6 +638,7 @@ under the License.
     <target name="fetch-ooxml-jars" depends="check-ooxml-jars" unless="ooxml.jars.present">
         <mkdir dir="${ooxml.lib}"/>
         <downloadfile src="${ooxml.curvesapi.url}" dest="${ooxml.curvesapi.jar}"/>
+        <downloadfile src="${ooxml.commons-collections4.url}" dest="${ooxml.commons-collections4.jar}"/>
         <downloadfile src="${ooxml.xmlbeans23.url}" dest="${ooxml.xmlbeans23.jar}.orig"/>
         <downloadfile src="${ooxml.xmlbeans26.url}" dest="${ooxml.xmlbeans26.jar}.orig"/>
         <!-- remove piccolo parser, so we don't use unsafe calls to it instead of using jaxp -->
@@ -1874,7 +1880,7 @@ under the License.
                 <globmapper from="*" to="${zipdir}/lib/*"/>
             </mappedresources>
             <mappedresources cache="true">
-                <fileset dir="${ooxml.lib}" includes="xmlbeans-2.6.0.jar.orig,curvesapi-*.jar"/>
+                <fileset dir="${ooxml.lib}" includes="xmlbeans-2.6.0.jar.orig,commons-collections4-*.jar,curvesapi-*.jar"/>
                 <regexpmapper from="^(.*\.jar)(\.orig)?$$" to="${zipdir}/ooxml-lib/\1"/>
             </mappedresources>
             <mappedresources cache="true">
@@ -2116,6 +2122,7 @@ under the License.
             <auxClasspath path="${ooxml.xsds.jar}" />
             <auxClasspath path="${ooxml.security.jar}" />
             <auxClasspath path="${ooxml.curvesapi.jar}" />
+            <auxClasspath path="${ooxml.commons-collections4.jar}" />
             <auxClasspath path="${ooxml.xmlbeans26.jar}" />
             <auxClasspath path="${main.commons-codec.jar}" />
             <auxClasspath path="${main.commons-logging.jar}" />
index 0f3cdb1d51616432e4c4a1d6c532721d1198fa36..2f4c286bce02c8f8fe09f12795a819a0ba6f32f6 100644 (file)
        <artifactId>curvesapi</artifactId>
        <version>1.04</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-collections4</artifactId>
+      <version>4.1</version>
+    </dependency>
   </dependencies>
 </project>
index 35f9862116c39a469673adc3005250f10161a6bc..ba015a9f1136e6ad773cfd125c2bdff47dfe0125 100644 (file)
@@ -92,6 +92,7 @@ import org.apache.poi.ss.formula.SheetNameFormatter;
 import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
 import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
 import org.apache.poi.ss.formula.udf.UDFFinder;
+import org.apache.poi.ss.usermodel.Name;
 import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
@@ -1530,6 +1531,11 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
         return names.get(nameIndex);
     }
 
+    @Override
+    public List<HSSFName> getAllNames() {
+        return Collections.unmodifiableList(names);
+    }
+
     public NameRecord getNameRecord(int nameIndex) {
         return getWorkbook().getNameRecord(nameIndex);
     }
@@ -1701,8 +1707,9 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
      *
      * @param name the name to remove.
      */
-    void removeName(HSSFName name) {
-      int index = getNameIndex(name);
+    @Override
+    public void removeName(Name name) {
+      int index = getNameIndex((HSSFName) name);
       removeName(index);
     }
 
index f1f1a4cd2c2d3c92faa181b4768d413d7e0b43d6..e52615fb1a9d870fc80140c6e78ffad2f0c7b20d 100644 (file)
@@ -369,6 +369,13 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
      */
     List<? extends Name> getNames(String name);
 
+    /**
+     * Returns all defined names.
+     *
+     * @return a list of the defined names. An empty list is returned if none is found.
+     */
+    List<? extends Name> getAllNames();
+
     /**
      * @param nameIndex position of the named range (0-based)
      * @return the defined name at the specified index
@@ -407,6 +414,13 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
      */
     void removeName(String name);
 
+    /**
+     * Remove a defined name
+     *
+      * @param name the name of the defined name
+     */
+    void removeName(Name name);
+
     /**
      * Adds the linking required to allow formulas referencing
      *  the specified external workbook to be added to this one.
index 58e16c772ce3f6dfb311352d712550336526bed4..331ad9a0fd3e9d786c222a11c69d446e607ea06e 100644 (file)
@@ -1006,12 +1006,25 @@ public class SXSSFWorkbook implements Workbook {
         return _wb.getNames(name);
     }
 
+    /**
+     * Returns all defined names
+     *
+     * @return all defined names
+     */
+    @Override
+    public List<? extends Name> getAllNames()
+    {
+        return _wb.getAllNames();
+    }
+
     /**
      * @param nameIndex position of the named range (0-based)
      * @return the defined name at the specified index
      * @throws IllegalArgumentException if the supplied index is invalid
+     * @deprecated 3.16. New projects should avoid accessing named ranges by index.
      */
     @Override
+    @Deprecated
     public Name getNameAt(int nameIndex)
     {
         return _wb.getNameAt(nameIndex);
@@ -1036,8 +1049,12 @@ public class SXSSFWorkbook implements Workbook {
      *
      * @param name the name of the defined name
      * @return zero based index of the defined name. <code>-1</code> if not found.
+     *
+     * @deprecated 3.16. New projects should avoid accessing named ranges by index.
+     * Use {@link #getName(String)} instead.
      */
     @Override
+    @Deprecated
     public int getNameIndex(String name)
     {
         return _wb.getNameIndex(name);
@@ -1047,8 +1064,11 @@ public class SXSSFWorkbook implements Workbook {
      * Remove the defined name at the specified index
      *
      * @param index named range index (0 based)
+     *
+     * @deprecated 3.16. New projects should use {@link #removeName(Name)}.
      */
     @Override
+    @Deprecated
     public void removeName(int index)
     {
         _wb.removeName(index);
@@ -1057,10 +1077,24 @@ public class SXSSFWorkbook implements Workbook {
     /**
      * Remove a defined name by name
      *
-      * @param name the name of the defined name
+     * @param name the name of the defined name
+     *
+     * @deprecated 3.16. New projects should use {@link #removeName(Name)}.
      */
     @Override
+    @Deprecated
     public void removeName(String name)
+    {
+        _wb.removeName(name);
+    }
+
+    /**
+     * Remove the given defined name
+     *
+     * @param name the name to remove
+     */
+    @Override
+    public void removeName(Name name)
     {
         _wb.removeName(name);
     }
index f3c6bc2673082b3e50aee45ccbea797f5a274531..9c5e6ffb10212ddeed5d7ac6456973ba808fa2b7 100644 (file)
@@ -232,7 +232,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
         
         // Otherwise, try it as a named range
         if (sheet == null) {
-            if (_uBook.getNameIndex(name) > -1) {
+            if (!_uBook.getNames(name).isEmpty()) {
                 return new NameXPxg(null, name);
             }
             return null;
index 462bd6517f69c1619ac5499eda5c4f07efe4b4ac..0710626201266d3d8deb7d197db61ba6ea49815f 100644 (file)
@@ -167,19 +167,18 @@ public final class XSSFName implements Name {
     public void setNameName(String name) {
         validateName(name);
 
+        String oldName = getNameName();
         int sheetIndex = getSheetIndex();
-        int numberOfNames = _workbook.getNumberOfNames();
         //Check to ensure no other names have the same case-insensitive name at the same scope
-        for (int i = 0; i < numberOfNames; i++) {
-            XSSFName nm = _workbook.getNameAt(i);
-            if ((nm != this)
-                    && name.equalsIgnoreCase(nm.getNameName())
-                    && (sheetIndex == nm.getSheetIndex())) {
+        for (XSSFName foundName : _workbook.getNames(name)) {
+            if (foundName.getSheetIndex() == sheetIndex && foundName != this) {
                 String msg = "The "+(sheetIndex == -1 ? "workbook" : "sheet")+" already contains this name: " + name;
                 throw new IllegalArgumentException(msg);
             }
         }
         _ctName.setName(name);
+        //Need to update the name -> named ranges map
+        _workbook.updateName(this, oldName);
     }
 
     public String getRefersToFormula() {
index 1e9b3d6ceeb68d6a79befbd406e8e4d949f2e927..bcc2ed27d7a7ee31d7501f98fdbd0209f55b3b1b 100644 (file)
@@ -29,16 +29,20 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.regex.Pattern;
 
 import javax.xml.namespace.QName;
 
+import org.apache.commons.collections4.ListValuedMap;
+import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
 import org.apache.poi.POIXMLDocument;
 import org.apache.poi.POIXMLDocumentPart;
 import org.apache.poi.POIXMLException;
@@ -59,6 +63,7 @@ import org.apache.poi.ss.formula.SheetNameFormatter;
 import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
 import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
 import org.apache.poi.ss.formula.udf.UDFFinder;
+import org.apache.poi.ss.usermodel.Name;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -140,6 +145,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
      */
     private List<XSSFSheet> sheets;
 
+    /**
+     * this holds the XSSFName objects attached to this workbook, keyed by lower-case name
+     */
+    private ListValuedMap<String, XSSFName> namedRangesByName;
+
     /**
      * this holds the XSSFName objects attached to this workbook
      */
@@ -442,6 +452,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
         stylesSource.setWorkbook(this);
 
         namedRanges = new ArrayList<XSSFName>();
+        namedRangesByName = new ArrayListValuedHashMap<String, XSSFName>();
         sheets = new ArrayList<XSSFSheet>();
         pivotTables = new ArrayList<XSSFPivotTable>();
     }
@@ -733,8 +744,13 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
     public XSSFName createName() {
         CTDefinedName ctName = CTDefinedName.Factory.newInstance();
         ctName.setName("");
+        return createAndStoreName(ctName);
+    }
+
+    private XSSFName createAndStoreName(CTDefinedName ctName) {
         XSSFName name = new XSSFName(ctName, this);
         namedRanges.add(name);
+        namedRangesByName.put(ctName.getName().toLowerCase(Locale.ENGLISH), name);
         return name;
     }
 
@@ -938,28 +954,47 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
         return stylesSource.getFontAt(idx);
     }
 
+    /**
+     * Get the first named range with the given name.
+     *
+     * Note: names of named ranges are not unique as they are scoped by sheet.
+     * {@link #getNames(String name)} returns all named ranges with the given name.
+     *
+     * @param name  named range name
+     * @return XSSFName with the given name. <code>null</code> is returned no named range could be found.
+     */
     @Override
     public XSSFName getName(String name) {
-        int nameIndex = getNameIndex(name);
-        if (nameIndex < 0) {
+        Collection<XSSFName> list = getNames(name);
+        if (list.isEmpty()) {
             return null;
         }
-        return namedRanges.get(nameIndex);
+        return list.iterator().next();
     }
 
+    /**
+     * Get the named ranges with the given name.
+     * <i>Note:</i>Excel named ranges are case-insensitive and
+     * this method performs a case-insensitive search.
+     *
+     * @param name  named range name
+     * @return list of XSSFNames with the given name. An empty list if no named ranges could be found
+     */
     @Override
     public List<XSSFName> getNames(String name) {
-        List<XSSFName> names = new ArrayList<XSSFName>();
-        for(XSSFName nr : namedRanges) {
-            if(nr.getNameName().equals(name)) {
-                names.add(nr);
-            }
-        }
-
-        return names;
+        return Collections.unmodifiableList(namedRangesByName.get(name.toLowerCase(Locale.ENGLISH)));
     }
 
+    /**
+     * Get the named range at the given index.
+     *
+     * @param nameIndex the index of the named range
+     * @return the XSSFName at the given index
+     *
+     * @deprecated 3.16. New projects should avoid accessing named ranges by index.
+     */
     @Override
+    @Deprecated
     public XSSFName getNameAt(int nameIndex) {
         int nNames = namedRanges.size();
         if (nNames < 1) {
@@ -973,21 +1008,30 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
     }
 
     /**
-     * Gets the named range index by his name
-     * <i>Note:</i>Excel named ranges are case-insensitive and
-     * this method performs a case-insensitive search.
+     * Get a list of all the named ranges in the workbook.
+     *
+     * @return list of XSSFNames in the workbook
+     */
+    @Override
+    public List<XSSFName> getAllNames() {
+        return Collections.unmodifiableList(namedRanges);
+    }
+
+    /**
+     * Gets the named range index by name.
      *
      * @param name named range name
-     * @return named range index
+     * @return named range index. <code>-1</code> is returned if no named ranges could be found.
+     *
+     * @deprecated 3.16. New projects should avoid accessing named ranges by index.
+     * Use {@link #getName(String)} instead.
      */
     @Override
+    @Deprecated
     public int getNameIndex(String name) {
-        int i = 0;
-        for(XSSFName nr : namedRanges) {
-            if(nr.getNameName().equals(name)) {
-                return i;
-            }
-            i++;
+        XSSFName nm = getName(name);
+        if (nm != null) {
+            return namedRanges.indexOf(nm);
         }
         return -1;
     }
@@ -1258,22 +1302,40 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
         return getPackagePart().getContentType().equals(XSSFRelation.MACROS_WORKBOOK.getContentType());
     }
 
+    /**
+     * Remove the named range at the given index.
+     *
+     * @param index the index of the named range name to remove
+     *
+     * @deprecated 3.16. New projects should use {@link #removeName(Name)}.
+     */
     @Override
+    @Deprecated
     public void removeName(int nameIndex) {
-        namedRanges.remove(nameIndex);
+        removeName(getNameAt(nameIndex));
     }
 
+    /**
+     * Remove the first named range found with the given name.
+     * 
+     * Note: names of named ranges are not unique (name + sheet
+     * index is unique), so {@link #removeName(Name)} should
+     * be used if possible.
+     * 
+     * @param name the named range name to remove
+     *
+     * @throws IllegalArgumentException if no named range could be found
+     *
+     * @deprecated 3.16. New projects should use {@link #removeName(Name)}.
+     */
     @Override
+    @Deprecated
     public void removeName(String name) {
-        int idx = 0;
-        for (XSSFName nm : namedRanges) {
-            if(nm.getNameName().equalsIgnoreCase(name)) {
-                removeName(idx);
-                return;
-            }
-            idx++;
+        List<XSSFName> names = namedRangesByName.get(name.toLowerCase(Locale.ENGLISH));
+        if (names.isEmpty()) {
+            throw new IllegalArgumentException("Named range was not found: " + name);
         }
-        throw new IllegalArgumentException("Named range was not found: " + name);
+        removeName(names.get(0));
     }
 
 
@@ -1282,13 +1344,24 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
      * (name + sheet index is unique), this method is more accurate.
      *
      * @param name the name to remove.
+     *
+     * @throws IllegalArgumentException if the named range is not a part of this XSSFWorkbook
      */
-    void removeName(XSSFName name) {
-        if (!namedRanges.remove(name)) {
+    @Override
+    public void removeName(Name name) {
+        if (!namedRangesByName.removeMapping(name.getNameName().toLowerCase(Locale.ENGLISH), name)
+                || !namedRanges.remove(name)) {
             throw new IllegalArgumentException("Name was not found: " + name);
         }
     }
 
+    void updateName(XSSFName name, String oldName) {
+        if (!namedRangesByName.removeMapping(oldName.toLowerCase(Locale.ENGLISH), name)) {
+            throw new IllegalArgumentException("Name was not found: " + name);
+        }
+        namedRangesByName.put(name.getNameName().toLowerCase(Locale.ENGLISH), name);
+    }
+
 
     /**
      * Delete the printarea for the sheet specified
@@ -1297,13 +1370,9 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
      */
     @Override
     public void removePrintArea(int sheetIndex) {
-        int cont = 0;
-        for (XSSFName name : namedRanges) {
-            if (name.getNameName().equals(XSSFName.BUILTIN_PRINT_AREA) && name.getSheetIndex() == sheetIndex) {
-                namedRanges.remove(cont);
-                break;
-            }
-            cont++;
+        XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
+        if (name != null) {
+            removeName(name);
         }
     }
 
@@ -1369,17 +1438,20 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
         }
 
         //adjust indices of names ranges
-        for (Iterator<XSSFName> it = namedRanges.iterator(); it.hasNext();) {
-            XSSFName nm = it.next();
+        List<XSSFName> toRemove = new ArrayList<XSSFName>();
+        for (XSSFName nm : namedRanges) {
             CTDefinedName ct = nm.getCTName();
             if(!ct.isSetLocalSheetId()) continue;
             if (ct.getLocalSheetId() == index) {
-                it.remove();
+                toRemove.add(nm);
             } else if (ct.getLocalSheetId() > index){
                 // Bump down by one, so still points at the same sheet
                 ct.setLocalSheetId(ct.getLocalSheetId()-1);
             }
         }
+        for (XSSFName nm : toRemove) {
+            removeName(nm);
+        }
     }
 
     /**
@@ -1514,8 +1586,8 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
     }
 
     XSSFName getBuiltInName(String builtInCode, int sheetNumber) {
-        for (XSSFName name : namedRanges) {
-            if (name.getNameName().equalsIgnoreCase(builtInCode) && name.getSheetIndex() == sheetNumber) {
+        for (XSSFName name : namedRangesByName.get(builtInCode.toLowerCase(Locale.ENGLISH))) {
+            if (name.getSheetIndex() == sheetNumber) {
                 return name;
             }
         }
@@ -1537,15 +1609,12 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
         nameRecord.setName(builtInName);
         nameRecord.setLocalSheetId(sheetNumber);
 
-        XSSFName name = new XSSFName(nameRecord, this);
-        for (XSSFName nr : namedRanges) {
-            if (nr.equals(name))
-                throw new POIXMLException("Builtin (" + builtInName
-                        + ") already exists for sheet (" + sheetNumber + ")");
+        if (getBuiltInName(builtInName, sheetNumber) != null) {
+            throw new POIXMLException("Builtin (" + builtInName
+                    + ") already exists for sheet (" + sheetNumber + ")");
         }
 
-        namedRanges.add(name);
-        return name;
+        return createAndStoreName(nameRecord);
     }
 
     /**
@@ -1665,10 +1734,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
     }
     
     private void reprocessNamedRanges() {
+        namedRangesByName = new ArrayListValuedHashMap<String, XSSFName>();
         namedRanges = new ArrayList<XSSFName>();
         if(workbook.isSetDefinedNames()) {
             for(CTDefinedName ctName : workbook.getDefinedNames().getDefinedNameArray()) {
-                namedRanges.add(new XSSFName(ctName, this));
+                createAndStoreName(ctName);
             }
         }
     }
index 1d146c09c642f1ddfa66f2121ba353da7b52c264..ef0c5ea6334b99b81ed6713c3059fd4861052650 100644 (file)
@@ -65,9 +65,7 @@ public final class XSSFFormulaUtils {
      */
     public void updateSheetName(final int sheetIndex, final String oldName, final String newName) {
         // update named ranges
-        final int numberOfNames = _wb.getNumberOfNames();
-        for (int i = 0; i < numberOfNames; i++) {
-            XSSFName nm = _wb.getNameAt(i);
+        for (XSSFName nm : _wb.getAllNames()) {
             if (nm.getSheetIndex() == -1 || nm.getSheetIndex() == sheetIndex) {
                 updateName(nm, oldName, newName);
             }
index 63104dd9e2603ce5eb6efd4d8ab88fc28a8dea73..d11ed1fa81fc52144ee77d8df87e3a248d8b5027 100644 (file)
@@ -83,9 +83,7 @@ public final class XSSFRowShifter extends RowShifter {
     public void updateNamedRanges(FormulaShifter shifter) {
         Workbook wb = sheet.getWorkbook();
         XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create((XSSFWorkbook) wb);
-        final int numberOfNames = wb.getNumberOfNames();
-        for (int i = 0; i < numberOfNames; i++) {
-            Name name = wb.getNameAt(i);
+        for (Name name : wb.getAllNames()) {
             String formula = name.getRefersToFormula();
             int sheetIndex = name.getSheetIndex();
             final int rowIndex = -1; //don't care, named ranges are not allowed to include structured references
index 3280a9121bd23cae14c487528b81f4c6fc86f8c7..2be21e830cc36665b4aa995a9fa1dd8291168e86 100644 (file)
@@ -115,25 +115,25 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
         assertFalse(wb.isMacroEnabled());
         assertEquals(3, wb.getNumberOfNames());
 
-        assertEquals(0, wb.getNameAt(0).getCTName().getLocalSheetId());
-        assertFalse(wb.getNameAt(0).getCTName().isSetLocalSheetId());
-        assertEquals("SheetA!$A$1", wb.getNameAt(0).getRefersToFormula());
-        assertEquals("SheetA", wb.getNameAt(0).getSheetName());
+        assertEquals(0, wb.getName("SheetAA1").getCTName().getLocalSheetId());
+        assertFalse(wb.getName("SheetAA1").getCTName().isSetLocalSheetId());
+        assertEquals("SheetA!$A$1", wb.getName("SheetAA1").getRefersToFormula());
+        assertEquals("SheetA", wb.getName("SheetAA1").getSheetName());
 
-        assertEquals(0, wb.getNameAt(1).getCTName().getLocalSheetId());
-        assertFalse(wb.getNameAt(1).getCTName().isSetLocalSheetId());
-        assertEquals("SheetB!$A$1", wb.getNameAt(1).getRefersToFormula());
-        assertEquals("SheetB", wb.getNameAt(1).getSheetName());
+        assertEquals(0, wb.getName("SheetBA1").getCTName().getLocalSheetId());
+        assertFalse(wb.getName("SheetBA1").getCTName().isSetLocalSheetId());
+        assertEquals("SheetB!$A$1", wb.getName("SheetBA1").getRefersToFormula());
+        assertEquals("SheetB", wb.getName("SheetBA1").getSheetName());
 
-        assertEquals(0, wb.getNameAt(2).getCTName().getLocalSheetId());
-        assertFalse(wb.getNameAt(2).getCTName().isSetLocalSheetId());
-        assertEquals("SheetC!$A$1", wb.getNameAt(2).getRefersToFormula());
-        assertEquals("SheetC", wb.getNameAt(2).getSheetName());
+        assertEquals(0, wb.getName("SheetCA1").getCTName().getLocalSheetId());
+        assertFalse(wb.getName("SheetCA1").getCTName().isSetLocalSheetId());
+        assertEquals("SheetC!$A$1", wb.getName("SheetCA1").getRefersToFormula());
+        assertEquals("SheetC", wb.getName("SheetCA1").getSheetName());
 
         // Save and re-load, still there
         XSSFWorkbook nwb = XSSFTestDataSamples.writeOutAndReadBack(wb);
         assertEquals(3, nwb.getNumberOfNames());
-        assertEquals("SheetA!$A$1", nwb.getNameAt(0).getRefersToFormula());
+        assertEquals("SheetA!$A$1", nwb.getName("SheetAA1").getRefersToFormula());
 
         nwb.close();
         wb.close();
index 3e0f0b1651371bb8b43bd5b344c467b0d7bdf221..a188a11ed3aa4ec4d78aae0691b5c2ac0cf6d9e6 100644 (file)
@@ -53,9 +53,8 @@ public final class TestXSSFName extends BaseTestNamedRange {
             //sheet.createFreezePane(0, 3);
         }
         assertEquals(1, wb.getNumberOfNames());
-        XSSFName nr1 = wb.getNameAt(0);
+        XSSFName nr1 = wb.getName(XSSFName.BUILTIN_PRINT_TITLE);
 
-        assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr1.getNameName());
         assertEquals("'First Sheet'!$A:$A,'First Sheet'!$1:$4", nr1.getRefersToFormula());
 
         //remove the columns part
@@ -77,9 +76,8 @@ public final class TestXSSFName extends BaseTestNamedRange {
         wb.close();
         
         assertEquals(1, nwb.getNumberOfNames());
-        nr1 = nwb.getNameAt(0);
+        nr1 = nwb.getName(XSSFName.BUILTIN_PRINT_TITLE);
 
-        assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr1.getNameName());
         assertEquals("'First Sheet'!$A:$A,'First Sheet'!$1:$4", nr1.getRefersToFormula());
 
         // check that setting RR&C on a second sheet causes a new Print_Titles built-in
@@ -89,7 +87,7 @@ public final class TestXSSFName extends BaseTestNamedRange {
         sheet2.setRepeatingColumns(CellRangeAddress.valueOf("B:C"));
 
         assertEquals(2, nwb.getNumberOfNames());
-        XSSFName nr2 = nwb.getNameAt(1);
+        XSSFName nr2 = nwb.getNames(XSSFName.BUILTIN_PRINT_TITLE).get(1);
 
         assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr2.getNameName());
         assertEquals("SecondSheet!$B:$C,SecondSheet!$1:$1", nr2.getRefersToFormula());
@@ -98,4 +96,38 @@ public final class TestXSSFName extends BaseTestNamedRange {
         sheet2.setRepeatingColumns(null);
         nwb.close();
     }
+
+    @Test
+    public void testSetNameName() throws Exception {
+        // Test that renaming named ranges doesn't break our new named range map
+        XSSFWorkbook wb = new XSSFWorkbook();
+        wb.createSheet("First Sheet");
+
+        // Two named ranges called "name1", one scoped to sheet1 and one globally
+        XSSFName nameSheet1 = wb.createName();
+        nameSheet1.setNameName("name1");
+        nameSheet1.setRefersToFormula("'First Sheet'!$A$1");
+        nameSheet1.setSheetIndex(0);
+
+        XSSFName nameGlobal = wb.createName();
+        nameGlobal.setNameName("name1");
+        nameGlobal.setRefersToFormula("'First Sheet'!$B$1");
+
+        // Rename sheet-scoped name to "name2", check everything is updated properly
+        // and that the other name is unaffected
+        nameSheet1.setNameName("name2");
+        assertEquals(1, wb.getNames("name1").size());
+        assertEquals(1, wb.getNames("name2").size());
+        assertEquals(nameGlobal, wb.getName("name1"));
+        assertEquals(nameSheet1, wb.getName("name2"));
+
+        // Rename the other name to "name" and check everything again
+        nameGlobal.setNameName("name2");
+        assertEquals(0, wb.getNames("name1").size());
+        assertEquals(2, wb.getNames("name2").size());
+        assertTrue(wb.getNames("name2").contains(nameGlobal));
+        assertTrue(wb.getNames("name2").contains(nameSheet1));
+
+        wb.close();
+    }
 }
index c3420c780a5e7b753756a2bd52d4a36444069817..7f832c4d9cb77964d69a1631681ca77be32795a5 100644 (file)
@@ -1140,4 +1140,44 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
 
        wb.close();
     }
+
+    @Test
+    public void testRemoveSheet() throws IOException {
+        // Test removing a sheet maintains the named ranges correctly
+        XSSFWorkbook wb = new XSSFWorkbook();
+        wb.createSheet("Sheet1");
+        wb.createSheet("Sheet2");
+
+        XSSFName sheet1Name = wb.createName();
+        sheet1Name.setNameName("name1");
+        sheet1Name.setSheetIndex(0);
+        sheet1Name.setRefersToFormula("Sheet1!$A$1");
+
+        XSSFName sheet2Name = wb.createName();
+        sheet2Name.setNameName("name1");
+        sheet2Name.setSheetIndex(1);
+        sheet2Name.setRefersToFormula("Sheet2!$A$1");
+
+        assertTrue(wb.getAllNames().contains(sheet1Name));
+        assertTrue(wb.getAllNames().contains(sheet2Name));
+
+        assertEquals(2, wb.getNames("name1").size());
+        assertEquals(sheet1Name, wb.getNames("name1").get(0));
+        assertEquals(sheet2Name, wb.getNames("name1").get(1));
+
+        // Remove sheet1, we should only have sheet2Name now
+        wb.removeSheetAt(0);
+
+        assertFalse(wb.getAllNames().contains(sheet1Name));
+        assertTrue(wb.getAllNames().contains(sheet2Name));
+        assertEquals(1, wb.getNames("name1").size());
+        assertEquals(sheet2Name, wb.getNames("name1").get(0));
+
+        // Check by index as well for sanity
+        assertEquals(1, wb.getNumberOfNames());
+        assertEquals(0, wb.getNameIndex("name1"));
+        assertEquals(sheet2Name, wb.getNameAt(0));
+
+        wb.close();
+    }
 }
index 75fb77e827777f585a23cf95bd1a67ca26f0d45c..92e362df57f9c7214f3ca0e946c4f92f70a76e49 100644 (file)
@@ -201,11 +201,7 @@ public abstract class BaseTestNamedRange {
             assertEquals("The sheet already contains this name: aaa", e.getMessage());
         }
 
-        int cnt = 0;
-        for (int i = 0; i < wb.getNumberOfNames(); i++) {
-            if("aaa".equals(wb.getNameAt(i).getNameName())) cnt++;
-        }
-        assertEquals(3, cnt);
+        assertEquals(3, wb.getNames("aaa").size());
         
         wb.close();
     }
@@ -250,11 +246,11 @@ public abstract class BaseTestNamedRange {
          // Write the workbook to a file
          // Read the Excel file and verify its content
          Workbook wb2 = _testDataProvider.writeOutAndReadBack(wb1);
-         Name nm1 = wb2.getNameAt(wb2.getNameIndex("RangeTest1"));
+         Name nm1 = wb2.getName("RangeTest1");
          assertTrue("Name is "+nm1.getNameName(),"RangeTest1".equals(nm1.getNameName()));
          assertTrue("Reference is "+nm1.getRefersToFormula(),(wb2.getSheetName(0)+"!$A$1:$L$41").equals(nm1.getRefersToFormula()));
 
-         Name nm2 = wb2.getNameAt(wb2.getNameIndex("RangeTest2"));
+         Name nm2 = wb2.getName("RangeTest2");
          assertTrue("Name is "+nm2.getNameName(),"RangeTest2".equals(nm2.getNameName()));
          assertTrue("Reference is "+nm2.getRefersToFormula(),(wb2.getSheetName(1)+"!$A$1:$O$21").equals(nm2.getRefersToFormula()));
          
@@ -466,11 +462,11 @@ public abstract class BaseTestNamedRange {
         wb1.getNameAt(0);
 
         Workbook wb2 = _testDataProvider.writeOutAndReadBack(wb1);
-        Name nm =wb2.getNameAt(wb2.getNameIndex("RangeTest"));
+        Name nm =wb2.getName("RangeTest");
         assertTrue("Name is "+nm.getNameName(),"RangeTest".equals(nm.getNameName()));
         assertTrue("Reference is "+nm.getRefersToFormula(),(wb2.getSheetName(0)+"!$D$4:$E$8").equals(nm.getRefersToFormula()));
 
-        nm = wb2.getNameAt(wb2.getNameIndex("AnotherTest"));
+        nm = wb2.getName("AnotherTest");
         assertTrue("Name is "+nm.getNameName(),"AnotherTest".equals(nm.getNameName()));
         assertTrue("Reference is "+nm.getRefersToFormula(),newNamedRange2.getRefersToFormula().equals(nm.getRefersToFormula()));
         
@@ -499,8 +495,7 @@ public abstract class BaseTestNamedRange {
         namedCell.setRefersToFormula(reference);
 
         // retrieve the newly created named range
-        int namedCellIdx = wb.getNameIndex(cellName);
-        Name aNamedCell = wb.getNameAt(namedCellIdx);
+        Name aNamedCell = wb.getName(cellName);
         assertNotNull(aNamedCell);
 
         // retrieve the cell at the named range and test its contents
@@ -540,8 +535,7 @@ public abstract class BaseTestNamedRange {
         namedCell.setRefersToFormula(reference);
 
         // retrieve the newly created named range
-        int namedCellIdx = wb.getNameIndex(cname);
-        Name aNamedCell = wb.getNameAt(namedCellIdx);
+        Name aNamedCell = wb.getName(cname);
         assertNotNull(aNamedCell);
 
         // retrieve the cell at the named range and test its contents
index 3f8febb11a28c16df438eae21aac92b6f37ebe55..efae0d80c631d08ab4f64e4c4f5ae071e55e77c3 100644 (file)
@@ -261,17 +261,17 @@ public abstract class BaseTestSheetShiftRows {
         name4.setSheetIndex(1);
 
         sheet1.shiftRows(0, 1, 2);  //shift down the top row on Sheet1.
-        name1 = wb.getNameAt(0);
+        name1 = wb.getName("name1");
         assertEquals("Sheet1!$A$3+Sheet1!$B$3", name1.getRefersToFormula());
 
-        name2 = wb.getNameAt(1);
+        name2 = wb.getName("name2");
         assertEquals("Sheet1!$A$3", name2.getRefersToFormula());
 
         //name3 and name4 refer to Sheet2 and should not be affected
-        name3 = wb.getNameAt(2);
+        name3 = wb.getName("name3");
         assertEquals("Sheet2!$A$1", name3.getRefersToFormula());
 
-        name4 = wb.getNameAt(3);
+        name4 = wb.getName("name4");
         assertEquals("A1", name4.getRefersToFormula());
         
         wb.close();