Browse Source

[github-254] Implement XSSFWorkbook linkExternalWorkbook. Thanks to @aspojo. This closes #254

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1893728 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_5_2_0
PJ Fanning 2 years ago
parent
commit
bd1ea63abe

+ 2
- 1
poi-integration/build.gradle View File

testImplementation 'org.apache.ant:ant:1.10.11' testImplementation 'org.apache.ant:ant:1.10.11'
testImplementation 'org.apache.commons:commons-collections4:4.4' testImplementation 'org.apache.commons:commons-collections4:4.4'
testImplementation 'com.google.guava:guava:31.0.1-jre' testImplementation 'com.google.guava:guava:31.0.1-jre'
testRuntimeOnly 'org.slf4j:slf4j-api:1.7.32'
testRuntimeOnly "org.apache.logging.log4j:log4j-core:${log4jVersion}"
testRuntimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}"


misc(project(':poi-ooxml')) { misc(project(':poi-ooxml')) {
capabilities { capabilities {

+ 21
- 1
poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCreationHelper.java View File



import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor;


import java.util.HashMap;
import java.util.Map;

public class XSSFCreationHelper implements CreationHelper { public class XSSFCreationHelper implements CreationHelper {
private final XSSFWorkbook workbook; private final XSSFWorkbook workbook;
private final Map<String, Workbook> referencedWorkbooks;


/** /**
* Should only be called by {@link XSSFWorkbook#getCreationHelper()} * Should only be called by {@link XSSFWorkbook#getCreationHelper()}
@Internal @Internal
public XSSFCreationHelper(XSSFWorkbook wb) { public XSSFCreationHelper(XSSFWorkbook wb) {
workbook = wb; workbook = wb;
referencedWorkbooks = new HashMap<>();
} }


/** /**
*/ */
@Override @Override
public XSSFFormulaEvaluator createFormulaEvaluator() { public XSSFFormulaEvaluator createFormulaEvaluator() {
return new XSSFFormulaEvaluator(workbook);
XSSFFormulaEvaluator evaluator = new XSSFFormulaEvaluator(workbook);
Map<String, FormulaEvaluator> evaluatorMap = new HashMap<>();
evaluatorMap.put("", evaluator);
this.referencedWorkbooks.forEach((name,otherWorkbook)->evaluatorMap.put(name,otherWorkbook.getCreationHelper().createFormulaEvaluator()));
evaluator.setupReferencedWorkbooks(evaluatorMap);
return evaluator;
} }


/** /**
public AreaReference createAreaReference(CellReference topLeft, CellReference bottomRight) { public AreaReference createAreaReference(CellReference topLeft, CellReference bottomRight) {
return new AreaReference(topLeft, bottomRight, workbook.getSpreadsheetVersion()); return new AreaReference(topLeft, bottomRight, workbook.getSpreadsheetVersion());
} }

protected Map<String, Workbook> getReferencedWorkbooks() {
return referencedWorkbooks;
}

protected void addExternalWorkbook(String name, Workbook workbook) {
this.referencedWorkbooks.put(name,workbook);
}
} }

+ 43
- 6
poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java View File

namedRangesByName = new ArrayListValuedHashMap<>(); namedRangesByName = new ArrayListValuedHashMap<>();
sheets = new ArrayList<>(); sheets = new ArrayList<>();
pivotTables = new ArrayList<>(); pivotTables = new ArrayList<>();

externalLinks = new ArrayList<>();
} }


private void setBookViewsIfMissing() { private void setBookViewsIfMissing() {
* referencing the specified external workbook to be added to this one. Allows * referencing the specified external workbook to be added to this one. Allows
* formulas such as "[MyOtherWorkbook.xlsx]Sheet3!$A$5" to be added to the * formulas such as "[MyOtherWorkbook.xlsx]Sheet3!$A$5" to be added to the
* file, for workbooks not already linked / referenced. * file, for workbooks not already linked / referenced.
*
* Note: this is not implemented and thus currently throws an Exception stating this.
* <p>
* This support is still regarded as in beta and may change
* <p>
* see https://bz.apache.org/bugzilla/show_bug.cgi?id=57184
* *
* @param name The name the workbook will be referenced as in formulas * @param name The name the workbook will be referenced as in formulas
* @param workbook The open workbook to fetch the link required information from * @param workbook The open workbook to fetch the link required information from
*
* @throws RuntimeException stating that this method is not implemented yet.
* @return index position for external workbook
* @since POI 5.1.0
*/ */
@Beta
@Override @Override
@NotImplemented
public int linkExternalWorkbook(String name, Workbook workbook) { public int linkExternalWorkbook(String name, Workbook workbook) {
throw new RuntimeException("Not Implemented - see bug #57184");
int externalLinkIdx=-1;
if (!getCreationHelper().getReferencedWorkbooks().containsKey(name)){
externalLinkIdx = this.getNextPartNumber(XSSFRelation.EXTERNAL_LINKS,
this.getPackagePart().getPackage().getPartsByContentType(XSSFRelation.EXTERNAL_LINKS.getContentType()).size());
POIXMLDocumentPart.RelationPart rp = this.createRelationship(XSSFRelation.EXTERNAL_LINKS, XSSFFactory.getInstance(), externalLinkIdx, false);
ExternalLinksTable linksTable = rp.getDocumentPart();
linksTable.setLinkedFileName(name);
this.getExternalLinksTable().add(linksTable);

CTExternalReference ctExternalReference = this.getCTWorkbook().addNewExternalReferences().addNewExternalReference();
ctExternalReference.setId(rp.getRelationship().getId());

} else {
List<RelationPart> relationParts = getRelationParts();
for (RelationPart relationPart : relationParts) {
if (relationPart.getDocumentPart() instanceof ExternalLinksTable) {
ExternalLinksTable linksTable = relationPart.getDocumentPart();
String linkedFileName = linksTable.getLinkedFileName();
if(linkedFileName.equals(name)){
String s = relationPart.getRelationship().getTargetURI().toString();
String s2 = XSSFRelation.EXTERNAL_LINKS.getDefaultFileName();
String numStr = s.substring(s2.indexOf('#'), s2.indexOf('.'));
externalLinkIdx = Integer.parseInt(numStr);
break;
}
}
}
}

XSSFCreationHelper creationHelper = getCreationHelper();
creationHelper.addExternalWorkbook(name,workbook);

return externalLinkIdx;

} }


/** /**

+ 38
- 3
poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java View File



import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM; import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM;
import static org.apache.poi.hssf.HSSFTestDataSamples.openSampleFileStream; import static org.apache.poi.hssf.HSSFTestDataSamples.openSampleFileStream;
import static org.apache.poi.xssf.XSSFTestDataSamples.openSampleWorkbook;
import static org.apache.poi.xssf.XSSFTestDataSamples.writeOut;
import static org.apache.poi.xssf.XSSFTestDataSamples.writeOutAndReadBack;
import static org.apache.poi.xssf.XSSFTestDataSamples.*;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import java.util.zip.CRC32; import java.util.zip.CRC32;


import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xssf.XSSFITestDataProvider; import org.apache.poi.xssf.XSSFITestDataProvider;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.StylesTable;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCalcPr; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCalcPr;
private static String ref(Cell cell) { private static String ref(Cell cell) {
return new CellReference(cell).formatAsString(); return new CellReference(cell).formatAsString();
} }

@Test
void testLinkExternalWorkbook() throws Exception {
String nameA = "link-external-workbook-a.xlsx";

try (
XSSFWorkbook a = new XSSFWorkbook();
XSSFWorkbook b = new XSSFWorkbook()
) {
XSSFRow row1 = a.createSheet().createRow(0);
row1.createCell(0).setCellValue(10);
row1.createCell(1).setCellValue(20);

XSSFRow row2 = b.createSheet().createRow(0);
XSSFCell cell = row2.createCell(0);

b.linkExternalWorkbook(nameA, a);
String formula = String.format("SUM('[%s]Sheet0'!A1:B1)", nameA);
cell.setCellFormula(formula);
XSSFFormulaEvaluator evaluator = b.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateFormulaCell(cell);
double cellValue = cell.getNumericCellValue();
assertEquals(cellValue,30.0);
/*
try (FileOutputStream out = new FileOutputStream(getSampleFile(nameA))) {
a.write(out);
}
String nameB = "link-external-workbook-b.xlsx";
try (FileOutputStream out = new FileOutputStream(getSampleFile(nameB))) {
b.write(out);
}
*/
}
}

} }

BIN
test-data/spreadsheet/link-external-workbook-a.xlsx View File


BIN
test-data/spreadsheet/link-external-workbook-b.xlsx View File


Loading…
Cancel
Save