Skip to content

Commit 1ed2a18

Browse files
committed
There can be more than one linked ExternalLinks table for a workbook #56744
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1611890 13f79535-47bb-0310-9956-ffa450edef68
1 parent 7d2a909 commit 1ed2a18

File tree

4 files changed

+128
-13
lines changed

4 files changed

+128
-13
lines changed

src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,9 @@ public interface PackageRelationshipTypes {
9999
* Style type.
100100
*/
101101
String STYLE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
102+
103+
/**
104+
* External Link to another Document
105+
*/
106+
String EXTERNAL_LINK_PATH = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath";
102107
}

src/ooxml/java/org/apache/poi/xssf/model/ExternalLinksTable.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2525
import org.apache.poi.POIXMLDocumentPart;
2626
import org.apache.poi.openxml4j.opc.PackagePart;
2727
import org.apache.poi.openxml4j.opc.PackageRelationship;
28+
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
29+
import org.apache.poi.openxml4j.opc.TargetMode;
2830
import org.apache.poi.ss.usermodel.Name;
2931
import org.apache.xmlbeans.XmlException;
3032
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalDefinedName;
@@ -78,6 +80,38 @@ protected void commit() throws IOException {
7880
public CTExternalLink getCTExternalLink(){
7981
return link;
8082
}
83+
84+
/**
85+
* Returns the last recorded name of the file that this
86+
* is linked to
87+
*/
88+
public String getLinkedFileName() {
89+
String rId = link.getExternalBook().getId();
90+
PackageRelationship rel = getPackagePart().getRelationship(rId);
91+
if (rel != null && rel.getTargetMode() == TargetMode.EXTERNAL) {
92+
return rel.getTargetURI().toString();
93+
} else {
94+
return null;
95+
}
96+
}
97+
/**
98+
* Updates the last recorded name for the file that this links to
99+
*/
100+
public void setLinkedFileName(String target) {
101+
String rId = link.getExternalBook().getId();
102+
103+
if (rId == null || rId.isEmpty()) {
104+
// We're a new External Link Table, so nothing to remove
105+
} else {
106+
// Relationships can't be changed, so remove the old one
107+
getPackagePart().removeRelationship(rId);
108+
}
109+
110+
// Have a new one added
111+
PackageRelationship newRel = getPackagePart().addExternalRelationship(
112+
target, PackageRelationshipTypes.EXTERNAL_LINK_PATH);
113+
link.getExternalBook().setId(newRel.getId());
114+
}
81115

82116
@SuppressWarnings("deprecation")
83117
public List<String> getSheetNames() {

src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
7979
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
8080
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedNames;
8181
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDialogsheet;
82+
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalReference;
8283
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet;
8384
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheets;
8485
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
@@ -157,9 +158,9 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
157158
private CalculationChain calcChain;
158159

159160
/**
160-
* External Links, for referencing names or cells in other workbooks
161+
* External Links, for referencing names or cells in other workbooks.
161162
*/
162-
private ExternalLinksTable externalLinks;
163+
private List<ExternalLinksTable> externalLinks;
163164

164165
/**
165166
* A collection of custom XML mappings
@@ -284,16 +285,19 @@ protected void onDocumentRead() throws IOException {
284285
this.workbook = doc.getWorkbook();
285286

286287
Map<String, XSSFSheet> shIdMap = new HashMap<String, XSSFSheet>();
288+
Map<String, ExternalLinksTable> elIdMap = new HashMap<String, ExternalLinksTable>();
287289
for(POIXMLDocumentPart p : getRelations()){
288290
if(p instanceof SharedStringsTable) sharedStringSource = (SharedStringsTable)p;
289291
else if(p instanceof StylesTable) stylesSource = (StylesTable)p;
290292
else if(p instanceof ThemesTable) theme = (ThemesTable)p;
291293
else if(p instanceof CalculationChain) calcChain = (CalculationChain)p;
292-
else if(p instanceof ExternalLinksTable) externalLinks = (ExternalLinksTable)p;
293294
else if(p instanceof MapInfo) mapInfo = (MapInfo)p;
294295
else if (p instanceof XSSFSheet) {
295296
shIdMap.put(p.getPackageRelationship().getId(), (XSSFSheet)p);
296297
}
298+
else if (p instanceof ExternalLinksTable) {
299+
elIdMap.put(p.getPackageRelationship().getId(), (ExternalLinksTable)p);
300+
}
297301
}
298302

299303
if (stylesSource == null) {
@@ -307,7 +311,8 @@ else if (p instanceof XSSFSheet) {
307311
sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
308312
}
309313

310-
// Load individual sheets. The order of sheets is defined by the order of CTSheet elements in the workbook
314+
// Load individual sheets. The order of sheets is defined by the order
315+
// of CTSheet elements in the workbook
311316
sheets = new ArrayList<XSSFSheet>(shIdMap.size());
312317
for (CTSheet ctSheet : this.workbook.getSheets().getSheetArray()) {
313318
XSSFSheet sh = shIdMap.get(ctSheet.getId());
@@ -319,6 +324,20 @@ else if (p instanceof XSSFSheet) {
319324
sh.onDocumentRead();
320325
sheets.add(sh);
321326
}
327+
328+
// Load the external links tables. Their order is defined by the order
329+
// of CTExternalReference elements in the workbook
330+
externalLinks = new ArrayList<ExternalLinksTable>(elIdMap.size());
331+
if (this.workbook.isSetExternalReferences()) {
332+
for (CTExternalReference er : this.workbook.getExternalReferences().getExternalReferenceArray()) {
333+
ExternalLinksTable el = elIdMap.get(er.getId());
334+
if(el == null) {
335+
logger.log(POILogger.WARN, "ExternalLinksTable with r:id " + er.getId()+ " was defined, but didn't exist in package, skipping");
336+
continue;
337+
}
338+
externalLinks.add(el);
339+
}
340+
}
322341

323342
// Process the named ranges
324343
reprocessNamedRanges();
@@ -1611,16 +1630,20 @@ public CalculationChain getCalculationChain() {
16111630
}
16121631

16131632
/**
1614-
* Returns the {@link ExternalLinksTable} object for this workbook.
1633+
* Returns the list of {@link ExternalLinksTable} object for this workbook
16151634
*
16161635
* <p>The external links table specifies details of named ranges etc
16171636
* that are referenced from other workbooks, along with the last seen
16181637
* values of what they point to.</p>
16191638
*
1620-
* @return the <code>ExternalLinksTable</code> object or <code>null</code> if not defined
1639+
* <p>Note that Excel uses index 0 for the current workbook, so the first
1640+
* External Links in a formula would be '[1]Foo' which corresponds to
1641+
* entry 0 in this list.</p>
1642+
1643+
* @return the <code>ExternalLinksTable</code> list, which may be empty
16211644
*/
16221645
@Internal
1623-
public ExternalLinksTable getExternalLinksTable() {
1646+
public List<ExternalLinksTable> getExternalLinksTable() {
16241647
return externalLinks;
16251648
}
16261649

src/ooxml/testcases/org/apache/poi/xssf/model/TestExternalLinksTable.java

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ public final class TestExternalLinksTable {
2929
@Test
3030
public void none() {
3131
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("SampleSS.xlsx");
32-
assertEquals(null, wb.getExternalLinksTable());
32+
assertNotNull(wb.getExternalLinksTable());
33+
assertEquals(0, wb.getExternalLinksTable().size());
3334
}
3435

3536
@Test
3637
public void basicRead() {
3738
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx");
3839
assertNotNull(wb.getExternalLinksTable());
3940
Name name = null;
40-
41-
ExternalLinksTable links = wb.getExternalLinksTable();
41+
42+
assertEquals(1, wb.getExternalLinksTable().size());
43+
44+
ExternalLinksTable links = wb.getExternalLinksTable().get(0);
4245
assertEquals(3, links.getSheetNames().size());
4346
assertEquals(2, links.getDefinedNames().size());
4447

@@ -57,17 +60,20 @@ public void basicRead() {
5760
assertEquals(1, name.getSheetIndex());
5861
assertEquals("Defines", name.getSheetName());
5962
assertEquals("'Defines'!$A$1", name.getRefersToFormula());
63+
64+
assertEquals("56737.xlsx", links.getLinkedFileName());
6065
}
6166

6267
@Test
6368
public void basicReadWriteRead() {
6469
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx");
65-
Name name = wb.getExternalLinksTable().getDefinedNames().get(1);
70+
Name name = wb.getExternalLinksTable().get(0).getDefinedNames().get(1);
6671
name.setNameName("Testing");
6772
name.setRefersToFormula("$A$1");
6873

6974
wb = XSSFTestDataSamples.writeOutAndReadBack(wb);
70-
ExternalLinksTable links = wb.getExternalLinksTable();
75+
assertEquals(1, wb.getExternalLinksTable().size());
76+
ExternalLinksTable links = wb.getExternalLinksTable().get(0);
7177

7278
name = links.getDefinedNames().get(0);
7379
assertEquals("NR_Global_B2", name.getNameName());
@@ -86,6 +92,53 @@ public void basicReadWriteRead() {
8692
public void readWithReferencesToTwoExternalBooks() {
8793
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref2-56737.xlsx");
8894

89-
// TODO Fix so we can see both of them...
95+
assertNotNull(wb.getExternalLinksTable());
96+
Name name = null;
97+
98+
assertEquals(2, wb.getExternalLinksTable().size());
99+
100+
// Check the first one, links to 56737.xlsx
101+
ExternalLinksTable links = wb.getExternalLinksTable().get(0);
102+
assertEquals("56737.xlsx", links.getLinkedFileName());
103+
assertEquals(3, links.getSheetNames().size());
104+
assertEquals(2, links.getDefinedNames().size());
105+
106+
assertEquals("Uses", links.getSheetNames().get(0));
107+
assertEquals("Defines", links.getSheetNames().get(1));
108+
assertEquals("56737", links.getSheetNames().get(2));
109+
110+
name = links.getDefinedNames().get(0);
111+
assertEquals("NR_Global_B2", name.getNameName());
112+
assertEquals(-1, name.getSheetIndex());
113+
assertEquals(null, name.getSheetName());
114+
assertEquals("'Defines'!$B$2", name.getRefersToFormula());
115+
116+
name = links.getDefinedNames().get(1);
117+
assertEquals("NR_To_A1", name.getNameName());
118+
assertEquals(1, name.getSheetIndex());
119+
assertEquals("Defines", name.getSheetName());
120+
assertEquals("'Defines'!$A$1", name.getRefersToFormula());
121+
122+
123+
// Check the second one, links to 56737.xls, slightly differently
124+
links = wb.getExternalLinksTable().get(1);
125+
assertEquals("56737.xls", links.getLinkedFileName());
126+
assertEquals(2, links.getSheetNames().size());
127+
assertEquals(2, links.getDefinedNames().size());
128+
129+
assertEquals("Uses", links.getSheetNames().get(0));
130+
assertEquals("Defines", links.getSheetNames().get(1));
131+
132+
name = links.getDefinedNames().get(0);
133+
assertEquals("NR_Global_B2", name.getNameName());
134+
assertEquals(-1, name.getSheetIndex());
135+
assertEquals(null, name.getSheetName());
136+
assertEquals("'Defines'!$B$2", name.getRefersToFormula());
137+
138+
name = links.getDefinedNames().get(1);
139+
assertEquals("NR_To_A1", name.getNameName());
140+
assertEquals(1, name.getSheetIndex());
141+
assertEquals("Defines", name.getSheetName());
142+
assertEquals("'Defines'!$A$1", name.getRefersToFormula());
90143
}
91144
}

0 commit comments

Comments
 (0)