mirror of
https://github.com/apache/poi.git
synced 2025-02-22 10:06:37 +00:00
[github-296] cache data of external workbook. Thanks to aspojo. This closes #296
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1897311 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
78deb9874e
commit
68ce022531
@ -33,9 +33,15 @@ import org.apache.poi.ss.usermodel.Name;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.Removal;
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalBook;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalCell;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalDefinedName;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalLink;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalRow;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalSheetData;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalSheetDataSet;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalSheetName;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalSheetNames;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.ExternalLinkDocument;
|
||||
|
||||
/**
|
||||
@ -123,7 +129,7 @@ public class ExternalLinksTable extends POIXMLDocumentPart {
|
||||
|
||||
// Have a new one added
|
||||
PackageRelationship newRel = getPackagePart().addExternalRelationship(
|
||||
target, PackageRelationshipTypes.EXTERNAL_LINK_PATH);
|
||||
target, PackageRelationshipTypes.EXTERNAL_LINK_PATH);
|
||||
link.getExternalBook().setId(newRel.getId());
|
||||
}
|
||||
|
||||
@ -148,6 +154,80 @@ public class ExternalLinksTable extends POIXMLDocumentPart {
|
||||
}
|
||||
|
||||
|
||||
public void cacheData(String sheetName, long rowR, String cellR, String v) {
|
||||
CTExternalBook externalBook = link.getExternalBook();
|
||||
synchronized (externalBook.monitor()) {
|
||||
CTExternalSheetData sheetData = getSheetData(getSheetNameIndex(sheetName));
|
||||
CTExternalRow row = getRow(sheetData, rowR);
|
||||
CTExternalCell cell = getCell(row, cellR);
|
||||
cell.setV(v);
|
||||
}
|
||||
}
|
||||
|
||||
private int getSheetNameIndex(String sheetName) {
|
||||
CTExternalBook externalBook = link.getExternalBook();
|
||||
CTExternalSheetNames sheetNames = externalBook.getSheetNames();
|
||||
if (sheetNames == null) {
|
||||
sheetNames = externalBook.addNewSheetNames();
|
||||
}
|
||||
int index = -1;
|
||||
for (int i = 0; i < sheetNames.sizeOfSheetNameArray(); i++) {
|
||||
CTExternalSheetName ctExternalSheetName = sheetNames.getSheetNameArray(i);
|
||||
if (ctExternalSheetName.getVal().equals(sheetName)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == -1) {
|
||||
CTExternalSheetName ctExternalSheetName = sheetNames.addNewSheetName();
|
||||
ctExternalSheetName.setVal(sheetName);
|
||||
index = sheetNames.sizeOfSheetNameArray() - 1;
|
||||
}
|
||||
return index;
|
||||
|
||||
}
|
||||
|
||||
private CTExternalSheetData getSheetData(int sheetId) {
|
||||
|
||||
CTExternalSheetDataSet sheetDataSet = link.getExternalBook().getSheetDataSet();
|
||||
if (sheetDataSet == null) {
|
||||
sheetDataSet = link.getExternalBook().addNewSheetDataSet();
|
||||
}
|
||||
CTExternalSheetData ctExternalSheetData = null;
|
||||
for (CTExternalSheetData item : sheetDataSet.getSheetDataArray()) {
|
||||
if (item.getSheetId() == sheetId) {
|
||||
ctExternalSheetData = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ctExternalSheetData == null) {
|
||||
ctExternalSheetData = sheetDataSet.addNewSheetData();
|
||||
ctExternalSheetData.setSheetId(sheetId);
|
||||
}
|
||||
return ctExternalSheetData;
|
||||
}
|
||||
|
||||
private CTExternalRow getRow(CTExternalSheetData sheetData, long rowR) {
|
||||
for (CTExternalRow ctExternalRow : sheetData.getRowArray()) {
|
||||
if (ctExternalRow.getR() == rowR) {
|
||||
return ctExternalRow;
|
||||
}
|
||||
}
|
||||
CTExternalRow ctExternalRow = sheetData.addNewRow();
|
||||
ctExternalRow.setR(rowR);
|
||||
return ctExternalRow;
|
||||
}
|
||||
|
||||
private CTExternalCell getCell(CTExternalRow row, String cellR) {
|
||||
for (CTExternalCell ctExternalCell : row.getCellArray()) {
|
||||
if (ctExternalCell.getR().equals(cellR)) {
|
||||
return ctExternalCell;
|
||||
}
|
||||
}
|
||||
CTExternalCell ctExternalCell = row.addNewCell();
|
||||
ctExternalCell.setR(cellR);
|
||||
return ctExternalCell;
|
||||
}
|
||||
// TODO Last seen data
|
||||
|
||||
|
||||
@ -226,4 +306,4 @@ public class ExternalLinksTable extends POIXMLDocumentPart {
|
||||
throw new IllegalStateException("Not Supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,14 @@ import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
import org.apache.poi.ss.formula.eval.NumberEval;
|
||||
import org.apache.poi.ss.formula.eval.StringEval;
|
||||
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.ptg.Area3DPxg;
|
||||
import org.apache.poi.ss.formula.ptg.Ptg;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellType;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
import org.apache.poi.ss.usermodel.RichTextString;
|
||||
import org.apache.poi.ss.util.CellReference;
|
||||
import org.apache.poi.xssf.model.ExternalLinksTable;
|
||||
|
||||
/**
|
||||
* Internal POI use only - parent of XSSF and SXSSF formula evaluators
|
||||
@ -55,6 +59,7 @@ public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator {
|
||||
protected CellValue evaluateFormulaCellValue(Cell cell) {
|
||||
EvaluationCell evalCell = toEvaluationCell(cell);
|
||||
ValueEval eval = _bookEvaluator.evaluate(evalCell);
|
||||
cacheExternalWorkbookCells(evalCell);
|
||||
if (eval instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) eval;
|
||||
return new CellValue(ne.getNumberValue());
|
||||
@ -73,9 +78,57 @@ public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator {
|
||||
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* cache cell value of external workbook
|
||||
*
|
||||
* @param evalCell sourceCell
|
||||
*/
|
||||
private void cacheExternalWorkbookCells(EvaluationCell evalCell) {
|
||||
//
|
||||
Ptg[] formulaTokens = getEvaluationWorkbook().getFormulaTokens(evalCell);
|
||||
for (Ptg ptg : formulaTokens) {
|
||||
if (ptg instanceof Area3DPxg) {
|
||||
Area3DPxg area3DPxg = (Area3DPxg) ptg;
|
||||
if (area3DPxg.getExternalWorkbookNumber() > 0) {
|
||||
EvaluationWorkbook.ExternalSheet externalSheet = getEvaluationWorkbook().getExternalSheet(area3DPxg.getSheetName(), area3DPxg.getLastSheetName(), area3DPxg.getExternalWorkbookNumber());
|
||||
|
||||
XSSFCell xssfCell = ((XSSFEvaluationCell) evalCell).getXSSFCell();
|
||||
XSSFWorkbook externalWorkbook = (XSSFWorkbook) xssfCell.getSheet().getWorkbook().getCreationHelper().getReferencedWorkbooks().get(externalSheet.getWorkbookName());
|
||||
ExternalLinksTable externalLinksTable = xssfCell.getSheet().getWorkbook().getExternalLinksTable().get(area3DPxg.getExternalWorkbookNumber() - 1);
|
||||
|
||||
int firstSheet = externalWorkbook.getSheetIndex(area3DPxg.getSheetName());
|
||||
int lastSheet = firstSheet;
|
||||
if (area3DPxg.getLastSheetName() != null) {
|
||||
lastSheet = externalWorkbook.getSheetIndex(area3DPxg.getLastSheetName());
|
||||
}
|
||||
|
||||
for (int sheetIndex = firstSheet; sheetIndex <= lastSheet; sheetIndex++) {
|
||||
XSSFSheet sheet = externalWorkbook.getSheetAt(sheetIndex);
|
||||
int firstRow = area3DPxg.getFirstRow();
|
||||
int lastRow = area3DPxg.getLastRow();
|
||||
for (int rowIndex = firstRow; rowIndex <= lastRow; rowIndex++) {
|
||||
XSSFRow row = sheet.getRow(rowIndex);
|
||||
int firstColumn = area3DPxg.getFirstColumn();
|
||||
int lastColumn = area3DPxg.getLastColumn();
|
||||
for (int cellIndex = firstColumn; cellIndex <= lastColumn; cellIndex++) {
|
||||
XSSFCell cell = row.getCell(cellIndex);
|
||||
String cellValue = cell.getRawValue();
|
||||
String cellR = new CellReference(cell).formatAsString(false);
|
||||
externalLinksTable.cacheData(sheet.getSheetName(), rowIndex + 1, cellR, cellValue);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCellType(Cell cell, CellType cellType) {
|
||||
if (cell instanceof XSSFCell) {
|
||||
if (cell instanceof XSSFCell) {
|
||||
EvaluationWorkbook evaluationWorkbook = getEvaluationWorkbook();
|
||||
BaseXSSFEvaluationWorkbook xewb = BaseXSSFEvaluationWorkbook.class.isAssignableFrom(evaluationWorkbook.getClass()) ? (BaseXSSFEvaluationWorkbook) evaluationWorkbook : null;
|
||||
|
||||
|
@ -61,6 +61,8 @@ import org.apache.poi.xssf.XSSFITestDataProvider;
|
||||
import org.apache.poi.xssf.model.StylesTable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCalcPr;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalLink;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalSheetData;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotCache;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr;
|
||||
@ -109,13 +111,13 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
|
||||
// Check the package contains what we'd expect it to
|
||||
try (OPCPackage pkg = OPCPackage.open(file.toString())) {
|
||||
PackagePart wbRelPart =
|
||||
pkg.getPart(PackagingURIHelper.createPartName("/xl/_rels/workbook.xml.rels"));
|
||||
pkg.getPart(PackagingURIHelper.createPartName("/xl/_rels/workbook.xml.rels"));
|
||||
assertNotNull(wbRelPart);
|
||||
assertTrue(wbRelPart.isRelationshipPart());
|
||||
assertEquals(ContentTypes.RELATIONSHIPS_PART, wbRelPart.getContentType());
|
||||
|
||||
PackagePart wbPart =
|
||||
pkg.getPart(PackagingURIHelper.createPartName("/xl/workbook.xml"));
|
||||
pkg.getPart(PackagingURIHelper.createPartName("/xl/workbook.xml"));
|
||||
// Links to the three sheets, shared strings and styles
|
||||
assertTrue(wbPart.hasRelationships());
|
||||
assertEquals(5, wbPart.getRelationships().size());
|
||||
@ -294,11 +296,11 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
|
||||
|
||||
// Add two more styles
|
||||
assertEquals(StylesTable.FIRST_CUSTOM_STYLE_ID + 8,
|
||||
st.putNumberFormat("testFORMAT"));
|
||||
st.putNumberFormat("testFORMAT"));
|
||||
assertEquals(StylesTable.FIRST_CUSTOM_STYLE_ID + 8,
|
||||
st.putNumberFormat("testFORMAT"));
|
||||
st.putNumberFormat("testFORMAT"));
|
||||
assertEquals(StylesTable.FIRST_CUSTOM_STYLE_ID + 9,
|
||||
st.putNumberFormat("testFORMAT2"));
|
||||
st.putNumberFormat("testFORMAT2"));
|
||||
assertEquals(10, st.getNumDataFormats());
|
||||
|
||||
|
||||
@ -370,7 +372,7 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
|
||||
assertEquals(1, allPictures.size());
|
||||
|
||||
PackagePartName imagePartName = PackagingURIHelper
|
||||
.createPartName("/xl/media/image1.jpeg");
|
||||
.createPartName("/xl/media/image1.jpeg");
|
||||
PackagePart imagePart = workbook.getPackage().getPart(imagePartName);
|
||||
assertNotNull(imagePart);
|
||||
|
||||
@ -1069,31 +1071,31 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
|
||||
*/
|
||||
@Test
|
||||
void getTable() throws IOException {
|
||||
XSSFWorkbook wb = openSampleWorkbook("WithTable.xlsx");
|
||||
XSSFTable table1 = wb.getTable("Tabella1");
|
||||
assertNotNull(table1, "Tabella1 was not found in workbook");
|
||||
assertEquals("Tabella1", table1.getName(), "Table name");
|
||||
assertEquals("Foglio1", table1.getSheetName(), "Sheet name");
|
||||
XSSFWorkbook wb = openSampleWorkbook("WithTable.xlsx");
|
||||
XSSFTable table1 = wb.getTable("Tabella1");
|
||||
assertNotNull(table1, "Tabella1 was not found in workbook");
|
||||
assertEquals("Tabella1", table1.getName(), "Table name");
|
||||
assertEquals("Foglio1", table1.getSheetName(), "Sheet name");
|
||||
|
||||
// Table lookup should be case-insensitive
|
||||
assertSame(table1, wb.getTable("TABELLA1"), "Case insensitive table name lookup");
|
||||
// Table lookup should be case-insensitive
|
||||
assertSame(table1, wb.getTable("TABELLA1"), "Case insensitive table name lookup");
|
||||
|
||||
// If workbook does not contain any data tables matching the provided name, getTable should return null
|
||||
assertNull(wb.getTable(null), "Null table name should not throw NPE");
|
||||
assertNull(wb.getTable("Foglio1"), "Should not be able to find non-existent table");
|
||||
// If workbook does not contain any data tables matching the provided name, getTable should return null
|
||||
assertNull(wb.getTable(null), "Null table name should not throw NPE");
|
||||
assertNull(wb.getTable("Foglio1"), "Should not be able to find non-existent table");
|
||||
|
||||
// If a table is added after getTable is called it should still be reachable by XSSFWorkbook.getTable
|
||||
// This test makes sure that if any caching is done that getTable never uses a stale cache
|
||||
XSSFTable table2 = wb.getSheet("Foglio2").createTable(null);
|
||||
table2.setName("Table2");
|
||||
assertSame(table2, wb.getTable("Table2"), "Did not find Table2");
|
||||
// If a table is added after getTable is called it should still be reachable by XSSFWorkbook.getTable
|
||||
// This test makes sure that if any caching is done that getTable never uses a stale cache
|
||||
XSSFTable table2 = wb.getSheet("Foglio2").createTable(null);
|
||||
table2.setName("Table2");
|
||||
assertSame(table2, wb.getTable("Table2"), "Did not find Table2");
|
||||
|
||||
// If table name is modified after getTable is called, the table can only be found by its new name
|
||||
// This test makes sure that if any caching is done that getTable never uses a stale cache
|
||||
table1.setName("Table1");
|
||||
assertSame(table1, wb.getTable("TABLE1"), "Did not find Tabella1 renamed to Table1");
|
||||
// If table name is modified after getTable is called, the table can only be found by its new name
|
||||
// This test makes sure that if any caching is done that getTable never uses a stale cache
|
||||
table1.setName("Table1");
|
||||
assertSame(table1, wb.getTable("TABLE1"), "Did not find Tabella1 renamed to Table1");
|
||||
|
||||
wb.close();
|
||||
wb.close();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@ -1299,6 +1301,46 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheExternalWorkbook() throws Exception {
|
||||
String nameA = "cache-external-workbook-a.xlsx";
|
||||
|
||||
try (
|
||||
UnsynchronizedByteArrayOutputStream bosA = new UnsynchronizedByteArrayOutputStream();
|
||||
UnsynchronizedByteArrayOutputStream bosB = new UnsynchronizedByteArrayOutputStream();
|
||||
XSSFWorkbook workbookA = new XSSFWorkbook();
|
||||
XSSFWorkbook workbookB = new XSSFWorkbook()
|
||||
) {
|
||||
XSSFRow row1 = workbookA.createSheet().createRow(0);
|
||||
double v1 = 10, v2 = 10, sum = v1 + v2;
|
||||
row1.createCell(0).setCellValue(v1);
|
||||
row1.createCell(1).setCellValue(v2);
|
||||
|
||||
XSSFRow row = workbookB.createSheet().createRow(0);
|
||||
XSSFCell cell = row.createCell(0);
|
||||
|
||||
workbookB.linkExternalWorkbook(nameA, workbookA);
|
||||
String formula = String.format(LocaleUtil.getUserLocale(), "SUM('[%s]Sheet0'!A1:B1)", nameA);
|
||||
cell.setCellFormula(formula);
|
||||
XSSFFormulaEvaluator evaluator = workbookB.getCreationHelper().createFormulaEvaluator();
|
||||
evaluator.evaluateFormulaCell(cell);
|
||||
|
||||
assertEquals(sum, cell.getNumericCellValue());
|
||||
|
||||
workbookA.write(bosA);
|
||||
workbookB.write(bosB);
|
||||
|
||||
try(
|
||||
XSSFWorkbook workbook2 = new XSSFWorkbook(bosB.toInputStream())
|
||||
) {
|
||||
CTExternalLink link = workbook2.getExternalLinksTable().get(0).getCTExternalLink();
|
||||
CTExternalSheetData sheetData = link.getExternalBook().getSheetDataSet().getSheetDataArray(0);
|
||||
assertEquals(Double.valueOf(sheetData.getRowArray(0).getCellArray(0).getV()), v1);
|
||||
assertEquals(Double.valueOf(sheetData.getRowArray(0).getCellArray(1).getV()), v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void expectFormattedContent(Cell cell, String value) {
|
||||
assertEquals(value, new DataFormatter().formatCellValue(cell),
|
||||
"Cell " + ref(cell) + " has wrong formatted content.");
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user