git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1897311 13f79535-47bb-0310-9956-ffa450edef68tags/REL_5_2_1
@@ -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"); | |||
// 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 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"); | |||
wb.close(); | |||
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"); | |||
// 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 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(); | |||
} | |||
@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."); |