]> source.dussan.org Git - poi.git/commitdiff
There can be more than one linked ExternalLinks table for a workbook #56744
authorNick Burch <nick@apache.org>
Sat, 19 Jul 2014 11:27:46 +0000 (11:27 +0000)
committerNick Burch <nick@apache.org>
Sat, 19 Jul 2014 11:27:46 +0000 (11:27 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1611890 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java
src/ooxml/java/org/apache/poi/xssf/model/ExternalLinksTable.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
src/ooxml/testcases/org/apache/poi/xssf/model/TestExternalLinksTable.java

index 9344a64abfd9f248738e69cbc085e76eb1c1fc1b..134e6a9fa3d56d3921c666ea34616952e7af4266 100644 (file)
@@ -99,4 +99,9 @@ public interface PackageRelationshipTypes {
         * Style type.
         */
        String STYLE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
+       
+       /**
+        * External Link to another Document
+        */
+       String EXTERNAL_LINK_PATH = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath";
 }
index 22a0de9c457faeecf9981a9f856cb7b39fc8b0df..9d66b6fa48ce7a77b1cab24ee905999b055c3c6d 100644 (file)
@@ -25,6 +25,8 @@ import java.util.List;
 import org.apache.poi.POIXMLDocumentPart;
 import org.apache.poi.openxml4j.opc.PackagePart;
 import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+import org.apache.poi.openxml4j.opc.TargetMode;
 import org.apache.poi.ss.usermodel.Name;
 import org.apache.xmlbeans.XmlException;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalDefinedName;
@@ -78,6 +80,38 @@ public class ExternalLinksTable extends POIXMLDocumentPart {
     public CTExternalLink getCTExternalLink(){
         return link;
     }
+    
+    /**
+     * Returns the last recorded name of the file that this
+     *  is linked to
+     */
+    public String getLinkedFileName() {
+        String rId = link.getExternalBook().getId();
+        PackageRelationship rel = getPackagePart().getRelationship(rId);
+        if (rel != null && rel.getTargetMode() == TargetMode.EXTERNAL) {
+            return rel.getTargetURI().toString();
+        } else {
+            return null;
+        }
+    }
+    /**
+     * Updates the last recorded name for the file that this links to
+     */
+    public void setLinkedFileName(String target) {
+        String rId = link.getExternalBook().getId();
+        
+        if (rId == null || rId.isEmpty()) {
+            // We're a new External Link Table, so nothing to remove
+        } else {
+            // Relationships can't be changed, so remove the old one
+            getPackagePart().removeRelationship(rId);
+        }
+        
+        // Have a new one added
+        PackageRelationship newRel = getPackagePart().addExternalRelationship(
+                                target, PackageRelationshipTypes.EXTERNAL_LINK_PATH);
+        link.getExternalBook().setId(newRel.getId());
+    }
 
     @SuppressWarnings("deprecation")
     public List<String> getSheetNames() {
index f1025bce74a0b9985d702d502a9f4a9be229c644..84e118233c05536641504f17c27f2c84aedd1a60 100644 (file)
@@ -79,6 +79,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCalcPr;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedNames;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDialogsheet;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalReference;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheets;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
@@ -157,9 +158,9 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
     private CalculationChain calcChain;
     
     /**
-     * External Links, for referencing names or cells in other workbooks
+     * External Links, for referencing names or cells in other workbooks.
      */
-    private ExternalLinksTable externalLinks;
+    private List<ExternalLinksTable> externalLinks;
 
     /**
      * A collection of custom XML mappings
@@ -284,16 +285,19 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
             this.workbook = doc.getWorkbook();
 
             Map<String, XSSFSheet> shIdMap = new HashMap<String, XSSFSheet>();
+            Map<String, ExternalLinksTable> elIdMap = new HashMap<String, ExternalLinksTable>();
             for(POIXMLDocumentPart p : getRelations()){
                 if(p instanceof SharedStringsTable) sharedStringSource = (SharedStringsTable)p;
                 else if(p instanceof StylesTable) stylesSource = (StylesTable)p;
                 else if(p instanceof ThemesTable) theme = (ThemesTable)p;
                 else if(p instanceof CalculationChain) calcChain = (CalculationChain)p;
-                else if(p instanceof ExternalLinksTable) externalLinks = (ExternalLinksTable)p;
                 else if(p instanceof MapInfo) mapInfo = (MapInfo)p;
                 else if (p instanceof XSSFSheet) {
                     shIdMap.put(p.getPackageRelationship().getId(), (XSSFSheet)p);
                 }
+                else if (p instanceof ExternalLinksTable) {
+                    elIdMap.put(p.getPackageRelationship().getId(), (ExternalLinksTable)p);
+                }
             }
             
             if (stylesSource == null) {
@@ -307,7 +311,8 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
                 sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
             }
 
-            // Load individual sheets. The order of sheets is defined by the order of CTSheet elements in the workbook
+            // Load individual sheets. The order of sheets is defined by the order
+            //  of CTSheet elements in the workbook
             sheets = new ArrayList<XSSFSheet>(shIdMap.size());
             for (CTSheet ctSheet : this.workbook.getSheets().getSheetArray()) {
                 XSSFSheet sh = shIdMap.get(ctSheet.getId());
@@ -319,6 +324,20 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
                 sh.onDocumentRead();
                 sheets.add(sh);
             }
+            
+            // Load the external links tables. Their order is defined by the order 
+            //  of CTExternalReference elements in the workbook
+            externalLinks = new ArrayList<ExternalLinksTable>(elIdMap.size());
+            if (this.workbook.isSetExternalReferences()) {
+                for (CTExternalReference er : this.workbook.getExternalReferences().getExternalReferenceArray()) {
+                    ExternalLinksTable el = elIdMap.get(er.getId());
+                    if(el == null) {
+                        logger.log(POILogger.WARN, "ExternalLinksTable with r:id " + er.getId()+ " was defined, but didn't exist in package, skipping");
+                        continue;
+                    }
+                    externalLinks.add(el);
+                }
+            }
 
             // Process the named ranges
             reprocessNamedRanges();
@@ -1611,16 +1630,20 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
     }
     
     /**
-     * Returns the {@link ExternalLinksTable} object for this workbook.
+     * Returns the list of {@link ExternalLinksTable} object for this workbook
      * 
      * <p>The external links table specifies details of named ranges etc
      *  that are referenced from other workbooks, along with the last seen
      *  values of what they point to.</p>
      *
-     * @return the <code>ExternalLinksTable</code> object or <code>null</code> if not defined
+     * <p>Note that Excel uses index 0 for the current workbook, so the first
+     *  External Links in a formula would be '[1]Foo' which corresponds to
+     *  entry 0 in this list.</p>
+
+     * @return the <code>ExternalLinksTable</code> list, which may be empty
      */
     @Internal
-    public ExternalLinksTable getExternalLinksTable() {
+    public List<ExternalLinksTable> getExternalLinksTable() {
         return externalLinks;
     }
 
index 612924ad5f6b6aaf3807561469ea254e19361fa5..1fbf86a335ccb410346ccad4a26c1f5c61ba9f0f 100644 (file)
@@ -29,7 +29,8 @@ public final class TestExternalLinksTable {
     @Test
     public void none() {
         XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("SampleSS.xlsx");
-        assertEquals(null, wb.getExternalLinksTable());
+        assertNotNull(wb.getExternalLinksTable());
+        assertEquals(0, wb.getExternalLinksTable().size());
     }
     
     @Test
@@ -37,8 +38,10 @@ public final class TestExternalLinksTable {
         XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx");
         assertNotNull(wb.getExternalLinksTable());
         Name name = null;
-        
-        ExternalLinksTable links = wb.getExternalLinksTable();
+
+        assertEquals(1, wb.getExternalLinksTable().size());
+
+        ExternalLinksTable links = wb.getExternalLinksTable().get(0);
         assertEquals(3, links.getSheetNames().size());
         assertEquals(2, links.getDefinedNames().size());
         
@@ -57,17 +60,20 @@ public final class TestExternalLinksTable {
         assertEquals(1, name.getSheetIndex());
         assertEquals("Defines", name.getSheetName());
         assertEquals("'Defines'!$A$1", name.getRefersToFormula());
+        
+        assertEquals("56737.xlsx", links.getLinkedFileName());
     }
     
     @Test
     public void basicReadWriteRead() {
         XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx");
-        Name name = wb.getExternalLinksTable().getDefinedNames().get(1);
+        Name name = wb.getExternalLinksTable().get(0).getDefinedNames().get(1);
         name.setNameName("Testing");
         name.setRefersToFormula("$A$1");
         
         wb = XSSFTestDataSamples.writeOutAndReadBack(wb);
-        ExternalLinksTable links = wb.getExternalLinksTable();
+        assertEquals(1, wb.getExternalLinksTable().size());
+        ExternalLinksTable links = wb.getExternalLinksTable().get(0);
         
         name = links.getDefinedNames().get(0);
         assertEquals("NR_Global_B2", name.getNameName());
@@ -86,6 +92,53 @@ public final class TestExternalLinksTable {
     public void readWithReferencesToTwoExternalBooks() {
         XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref2-56737.xlsx");
         
-        // TODO Fix so we can see both of them...
+        assertNotNull(wb.getExternalLinksTable());
+        Name name = null;
+
+        assertEquals(2, wb.getExternalLinksTable().size());
+
+        // Check the first one, links to 56737.xlsx
+        ExternalLinksTable links = wb.getExternalLinksTable().get(0);
+        assertEquals("56737.xlsx", links.getLinkedFileName());
+        assertEquals(3, links.getSheetNames().size());
+        assertEquals(2, links.getDefinedNames().size());
+        
+        assertEquals("Uses",    links.getSheetNames().get(0));
+        assertEquals("Defines", links.getSheetNames().get(1));
+        assertEquals("56737",   links.getSheetNames().get(2));
+        
+        name = links.getDefinedNames().get(0);
+        assertEquals("NR_Global_B2", name.getNameName());
+        assertEquals(-1, name.getSheetIndex());
+        assertEquals(null, name.getSheetName());
+        assertEquals("'Defines'!$B$2", name.getRefersToFormula());
+        
+        name = links.getDefinedNames().get(1);
+        assertEquals("NR_To_A1", name.getNameName());
+        assertEquals(1, name.getSheetIndex());
+        assertEquals("Defines", name.getSheetName());
+        assertEquals("'Defines'!$A$1", name.getRefersToFormula());
+
+        
+        // Check the second one, links to 56737.xls, slightly differently
+        links = wb.getExternalLinksTable().get(1);
+        assertEquals("56737.xls", links.getLinkedFileName());
+        assertEquals(2, links.getSheetNames().size());
+        assertEquals(2, links.getDefinedNames().size());
+        
+        assertEquals("Uses",    links.getSheetNames().get(0));
+        assertEquals("Defines", links.getSheetNames().get(1));
+        
+        name = links.getDefinedNames().get(0);
+        assertEquals("NR_Global_B2", name.getNameName());
+        assertEquals(-1, name.getSheetIndex());
+        assertEquals(null, name.getSheetName());
+        assertEquals("'Defines'!$B$2", name.getRefersToFormula());
+        
+        name = links.getDefinedNames().get(1);
+        assertEquals("NR_To_A1", name.getNameName());
+        assertEquals(1, name.getSheetIndex());
+        assertEquals("Defines", name.getSheetName());
+        assertEquals("'Defines'!$A$1", name.getRefersToFormula());
     }
 }