diff options
47 files changed, 398 insertions, 177 deletions
diff --git a/build.gradle b/build.gradle index 6856adf9c8..7040561247 100644 --- a/build.gradle +++ b/build.gradle @@ -67,7 +67,7 @@ configurations { } dependencies { - antLibs("org.junit.jupiter:junit-jupiter:5.13.3") + antLibs("org.junit.jupiter:junit-jupiter:5.13.4") antLibs("org.apache.ant:ant-junitlauncher:1.10.15") } @@ -89,7 +89,7 @@ allprojects { // apply plugin: 'eclipse' apply plugin: 'idea' - version = '5.4.2-SNAPSHOT' + version = '5.5.0-SNAPSHOT' } /** @@ -108,11 +108,11 @@ subprojects { ext { bouncyCastleVersion = '1.81' - commonsCodecVersion = '1.18.0' + commonsCodecVersion = '1.19.0' commonsCompressVersion = '1.27.1' commonsIoVersion = '2.20.0' commonsMathVersion = '3.6.1' - junitVersion = '5.13.3' + junitVersion = '5.13.4' log4jVersion = '2.24.3' mockitoVersion = '4.11.0' hamcrestVersion = '3.0' @@ -42,7 +42,7 @@ under the License. <description>The Apache POI project Ant build.</description> - <property name="version.id" value="5.4.2-SNAPSHOT"/> + <property name="version.id" value="5.5.0-SNAPSHOT"/> <property name="release.rc" value=""/> <property environment="env"/> @@ -263,21 +263,21 @@ under the License. <!-- jars in the /lib directory, see the fetch-jars target--> - <dependency prefix="main.commons-codec" artifact="commons-codec:commons-codec:1.18.0" usage="main"/> + <dependency prefix="main.commons-codec" artifact="commons-codec:commons-codec:1.19.0" usage="main"/> <dependency prefix="main.commons-collections4" artifact="org.apache.commons:commons-collections4:4.5.0" usage="main"/> <dependency prefix="main.commons-math3" artifact="org.apache.commons:commons-math3:3.6.1" usage="main"/> <dependency prefix="main.commons-io" artifact="commons-io:commons-io:2.20.0" usage="main"/> <dependency prefix="main.com.zaxxer" artifact="com.zaxxer:SparseBitSet:1.3" usage="main"/> <dependency prefix="main.log4j-api" artifact="org.apache.logging.log4j:log4j-api:2.24.3" usage="main"/> - <dependency prefix="main.junit-api" artifact="org.junit.jupiter:junit-jupiter-api:5.13.3" usage="main-tests"/> - <dependency prefix="main.junit-jengine" artifact="org.junit.jupiter:junit-jupiter-engine:5.13.3" usage="main-tests"/> - <dependency prefix="main.junit-params" artifact="org.junit.jupiter:junit-jupiter-params:5.13.3" usage="main-tests"/> + <dependency prefix="main.junit-api" artifact="org.junit.jupiter:junit-jupiter-api:5.13.4" usage="main-tests"/> + <dependency prefix="main.junit-jengine" artifact="org.junit.jupiter:junit-jupiter-engine:5.13.4" usage="main-tests"/> + <dependency prefix="main.junit-params" artifact="org.junit.jupiter:junit-jupiter-params:5.13.4" usage="main-tests"/> <dependency prefix="main.junit-opentest4j" artifact="org.opentest4j:opentest4j:1.2.0" usage="main-tests"/> <dependency prefix="main.junit-apiguardian" artifact="org.apiguardian:apiguardian-api:1.1.2" usage="main-tests"/> - <dependency prefix="main.junit-pcommons" artifact="org.junit.platform:junit-platform-commons:1.13.3" usage="main-tests"/> - <dependency prefix="main.junit-pengine" artifact="org.junit.platform:junit-platform-engine:1.13.3" usage="main-tests"/> - <dependency prefix="main.junit-plauncher" artifact="org.junit.platform:junit-platform-launcher:1.13.3" usage="main-tests"/> + <dependency prefix="main.junit-pcommons" artifact="org.junit.platform:junit-platform-commons:1.13.4" usage="main-tests"/> + <dependency prefix="main.junit-pengine" artifact="org.junit.platform:junit-platform-engine:1.13.4" usage="main-tests"/> + <dependency prefix="main.junit-plauncher" artifact="org.junit.platform:junit-platform-launcher:1.13.4" usage="main-tests"/> <dependency prefix="main.jmh" artifact="org.openjdk.jmh:jmh-core:1.35" usage="main-tests"/> diff --git a/osgi/pom.xml b/osgi/pom.xml index 351b2e276d..ec71465a4f 100644 --- a/osgi/pom.xml +++ b/osgi/pom.xml @@ -24,12 +24,12 @@ <groupId>org.apache.poi</groupId> <artifactId>poi-bundle</artifactId> <packaging>bundle</packaging> - <version>5.4.2-SNAPSHOT</version> + <version>5.5.0-SNAPSHOT</version> <name>Apache POI OSGi bundle</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <poi.version>5.4.2-SNAPSHOT</poi.version> + <poi.version>5.5.0-SNAPSHOT</poi.version> <pax.exam.version>4.14.0</pax.exam.version> </properties> diff --git a/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/SimpleImages.java b/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/SimpleImages.java index 1f996cfd1f..0e4c5670c0 100644 --- a/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/SimpleImages.java +++ b/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/SimpleImages.java @@ -22,10 +22,10 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import org.apache.poi.common.usermodel.PictureType; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.BreakType; -import org.apache.poi.xwpf.usermodel.Document; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; @@ -45,30 +45,30 @@ public final class SimpleImages { XWPFRun r = p.createRun(); for (String imgFile : args) { - int format; + PictureType format; if (imgFile.endsWith(".emf")) { - format = Document.PICTURE_TYPE_EMF; + format = PictureType.EMF; } else if (imgFile.endsWith(".wmf")) { - format = Document.PICTURE_TYPE_WMF; + format = PictureType.WMF; } else if (imgFile.endsWith(".pict")) { - format = Document.PICTURE_TYPE_PICT; + format = PictureType.PICT; } else if (imgFile.endsWith(".jpeg") || imgFile.endsWith(".jpg")) { - format = Document.PICTURE_TYPE_JPEG; + format = PictureType.JPEG; } else if (imgFile.endsWith(".png")) { - format = Document.PICTURE_TYPE_PNG; + format = PictureType.PNG; } else if (imgFile.endsWith(".dib")) { - format = Document.PICTURE_TYPE_DIB; + format = PictureType.DIB; } else if (imgFile.endsWith(".gif")) { - format = Document.PICTURE_TYPE_GIF; + format = PictureType.GIF; } else if (imgFile.endsWith(".tiff")) { - format = Document.PICTURE_TYPE_TIFF; + format = PictureType.TIFF; } else if (imgFile.endsWith(".eps")) { - format = Document.PICTURE_TYPE_EPS; + format = PictureType.EPS; } else if (imgFile.endsWith(".bmp")) { - format = Document.PICTURE_TYPE_BMP; + format = PictureType.BMP; } else if (imgFile.endsWith(".wpg")) { - format = Document.PICTURE_TYPE_WPG; + format = PictureType.WPG; } else { System.err.println("Unsupported picture: " + imgFile + ". Expected emf|wmf|pict|jpeg|png|dib|gif|tiff|eps|bmp|wpg"); diff --git a/poi-ooxml/build.gradle b/poi-ooxml/build.gradle index 8a9309799b..c2ef234d37 100644 --- a/poi-ooxml/build.gradle +++ b/poi-ooxml/build.gradle @@ -58,6 +58,12 @@ dependencies { api project(':poi-ooxml-full') api project(path: ':poi-ooxml-full', configuration: 'archives') + // Can be very useful in local testing to comment out the 2 poi-ooxml-full lines above + // and uncomment the line below to use a pre-built version of poi-ooxml-full. + // Try to use the last release version of poi-ooxml-full. You might be unlucky if + // recent unreleased changes in poi-ooxml-full are needed. + // api "org.apache.poi:poi-ooxml-full:5.4.1" + api "org.apache.xmlbeans:xmlbeans:${xmlbeansVersion}" api "org.apache.commons:commons-compress:${commonsCompressVersion}" api "commons-io:commons-io:${commonsIoVersion}" diff --git a/poi-ooxml/src/main/java/org/apache/poi/ooxml/util/NumberHelper.java b/poi-ooxml/src/main/java/org/apache/poi/ooxml/util/NumberHelper.java index 6d4cd26474..dbb397c448 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/ooxml/util/NumberHelper.java +++ b/poi-ooxml/src/main/java/org/apache/poi/ooxml/util/NumberHelper.java @@ -23,7 +23,7 @@ import org.apache.poi.util.Internal; * Helper class for number related operations. * <p>Note: This class is for internal POI usage only.</p> * - * @since POI 5.4.2 + * @since POI 5.5.0 */ @Internal public class NumberHelper { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java index bfbb775296..4bfb948589 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java @@ -688,7 +688,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai if (axes.isEmpty() && hasAxes()) { parseAxes(); } - return axes; + return Collections.unmodifiableList(axes); } private boolean hasAxes() { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java index 1dc47d47a4..983ef4f138 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java @@ -28,7 +28,6 @@ import org.apache.poi.logging.PoiLogManager; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; -import org.apache.poi.util.Removal; import org.apache.poi.xddf.usermodel.XDDFFillProperties; import org.apache.poi.xddf.usermodel.XDDFLineProperties; import org.apache.poi.xddf.usermodel.XDDFShapeProperties; @@ -85,27 +84,6 @@ public abstract class XDDFChartData { return valueAxes; } - /** - * Calls to {@code getSeries().add(series)} or to {@code getSeries().remove(series)} - * may corrupt the workbook. - * - * <p> - * Instead, use the following methods: - * <ul> - * <li>{@link #getSeriesCount()}</li> - * <li>{@link #getSeries(int)}</li> - * <li>{@link #addSeries(XDDFDataSource,XDDFNumericalDataSource)}</li> - * <li>{@link #removeSeries(int)}</li> - * </ul> - * - * @deprecated since POI 4.1.1 - */ - @Deprecated - @Removal(version = "5.3") - public List<Series> getSeries() { - return Collections.unmodifiableList(series); - } - public final int getSeriesCount() { return series.size(); } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java index 2fad9ddbbe..9f8b581c80 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java @@ -113,7 +113,7 @@ public class XDDFTextParagraph implements Iterable<XDDFTextRun> { } public List<XDDFTextRun> getTextRuns() { - return _runs; + return Collections.unmodifiableList(_runs); } @Override diff --git a/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/XDGFShape.java b/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/XDGFShape.java index 25b3af8f93..02f05ad1c5 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/XDGFShape.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/XDGFShape.java @@ -27,6 +27,7 @@ import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map.Entry; @@ -307,7 +308,7 @@ public class XDGFShape extends XDGFSheet { */ // -> May be null public List<XDGFShape> getShapes() { - return _shapes; + return _shapes == null ? null : Collections.unmodifiableList(_shapes); } // unique to this shape on the page? diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/model/ThemesTable.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/model/ThemesTable.java index 7bd391a06b..4d6655d5fc 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/model/ThemesTable.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/model/ThemesTable.java @@ -36,32 +36,34 @@ import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; * colors and fonts. */ public class ThemesTable extends POIXMLDocumentPart implements Themes { - public enum ThemeElement { - LT1(0, "Lt1"), - DK1(1,"Dk1"), - LT2(2,"Lt2"), - DK2(3,"Dk2"), - ACCENT1(4,"Accent1"), - ACCENT2(5,"Accent2"), - ACCENT3(6,"Accent3"), - ACCENT4(7,"Accent4"), - ACCENT5(8,"Accent5"), - ACCENT6(9,"Accent6"), - HLINK(10,"Hlink"), - FOLHLINK(11,"FolHlink"), - UNKNOWN(-1,null); - - public static ThemeElement byId(int idx) { - if (idx >= values().length || idx < 0) return UNKNOWN; - return values()[idx]; - } - ThemeElement(int idx, String name) { - this.idx = idx; - this.name = name; - } - public final int idx; - public final String name; - } + public enum ThemeElement { + LT1(0, "Lt1"), + DK1(1, "Dk1"), + LT2(2, "Lt2"), + DK2(3, "Dk2"), + ACCENT1(4, "Accent1"), + ACCENT2(5, "Accent2"), + ACCENT3(6, "Accent3"), + ACCENT4(7, "Accent4"), + ACCENT5(8, "Accent5"), + ACCENT6(9, "Accent6"), + HLINK(10, "Hlink"), + FOLHLINK(11, "FolHlink"), + UNKNOWN(-1, null); + + public static ThemeElement byId(int idx) { + if (idx >= values().length || idx < 0) return UNKNOWN; + return values()[idx]; + } + + ThemeElement(int idx, String name) { + this.idx = idx; + this.name = name; + } + + public final int idx; + public final String name; + } private IndexedColorMap colorMap; private ThemeDocument theme; diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java index 206ed84e49..c4a4673571 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java @@ -109,7 +109,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork } catch (NumberFormatException e) {} // Look up an External Link Table for this name - List<ExternalLinksTable> tables = _uBook.getExternalLinksTable(); + List<ExternalLinksTable> tables = _uBook.getExternalLinksTables(); int index = findExternalLinkIndex(bookName, tables); if (index != -1) return index; @@ -126,7 +126,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork // So, add the missing reference and return // Note - this is really rather nasty... ExternalLinksTable fakeLinkTable = new FakeExternalLinksTable(relBookName); - tables.add(fakeLinkTable); + _uBook.addExternalLinksTable(fakeLinkTable); return tables.size(); // 1 based results, 0 = current workbook } @@ -193,7 +193,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork if (externalWorkbookNumber > 0) { // External reference - reference is 1 based, link table is 0 based int linkNumber = externalWorkbookNumber - 1; - ExternalLinksTable linkTable = _uBook.getExternalLinksTable().get(linkNumber); + ExternalLinksTable linkTable = _uBook.getExternalLinksTable(linkNumber); for (org.apache.poi.ss.usermodel.Name name : linkTable.getDefinedNames()) { if (name.getNameName().equals(nameName)) { @@ -300,7 +300,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork if (externalWorkbookNumber > 0) { // External reference - reference is 1 based, link table is 0 based int linkNumber = externalWorkbookNumber - 1; - ExternalLinksTable linkTable = _uBook.getExternalLinksTable().get(linkNumber); + ExternalLinksTable linkTable = _uBook.getExternalLinksTable(linkNumber); workbookName = linkTable.getLinkedFileName(); } else { // Internal reference diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java index 3145e3f43c..b5674ada8e 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java @@ -128,7 +128,7 @@ public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator { XSSFWorkbook xssfWorkbook = xssfCell.getSheet().getWorkbook(); XSSFWorkbook externalWorkbook = (XSSFWorkbook) xssfWorkbook.getCreationHelper() .getReferencedWorkbooks().get(externalSheet.getWorkbookName()); - ExternalLinksTable externalLinksTable = xssfWorkbook.getExternalLinksTable().get(area3DPxg.getExternalWorkbookNumber() - 1); + ExternalLinksTable externalLinksTable = xssfWorkbook.getExternalLinksTable(area3DPxg.getExternalWorkbookNumber() - 1); if (externalWorkbook != null && externalLinksTable != null) { int firstSheet = externalWorkbook.getSheetIndex(area3DPxg.getSheetName()); diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 5c67853c10..59cf488993 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -172,7 +172,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet, OoxmlSheetEx tables.put( rp.getRelationship().getId(), (XSSFTable)p ); } if(p instanceof XSSFPivotTable) { - getWorkbook().getPivotTables().add((XSSFPivotTable) p); + getWorkbook().addPivotTable((XSSFPivotTable) p); } } @@ -4705,13 +4705,12 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet, OoxmlSheetEx @Beta private XSSFPivotTable createPivotTable() { XSSFWorkbook wb = getWorkbook(); - List<XSSFPivotTable> pivotTables = wb.getPivotTables(); int tableId = getWorkbook().getPivotTables().size()+1; //Create relationship between pivotTable and the worksheet XSSFPivotTable pivotTable = (XSSFPivotTable) createRelationship(XSSFRelation.PIVOT_TABLE, getWorkbook().getXssfFactory(), tableId); pivotTable.setParentSheet(this); - pivotTables.add(pivotTable); + wb.addPivotTable(pivotTable); XSSFWorkbook workbook = getWorkbook(); //Create relationship between the pivot cache definition and the workbook @@ -4735,8 +4734,6 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet, OoxmlSheetEx //Set relationships id for pivotCacheDefinition to pivotCacheRecords pivotTable.getPivotCacheDefinition().getCTPivotCacheDefinition().setId(pivotCacheDefinition.getRelationId(pivotCacheRecords)); - wb.setPivotTables(pivotTables); - return pivotTable; } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java index db696efbf2..7f81c7dc77 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java @@ -19,6 +19,7 @@ package org.apache.poi.xssf.usermodel; import java.awt.Color; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -84,7 +85,7 @@ public class XSSFTextParagraph implements Iterable<XSSFTextRun>{ } public List<XSSFTextRun> getTextRuns(){ - return _runs; + return Collections.unmodifiableList(_runs); } public Iterator<XSSFTextRun> iterator(){ @@ -268,7 +269,7 @@ public class XSSFTextParagraph implements Iterable<XSSFTextRun>{ * * @return the color of bullet characters within a given paragraph. * A <code>null</code> value means to use the text font color. - * @since POI 5.4.2 + * @since POI 5.5.0 */ public byte[] getBulletFontColorAsBytes() { ParagraphPropertyFetcher<byte[]> fetcher = new ParagraphPropertyFetcher<byte[]>(getLevel()) { @@ -300,7 +301,7 @@ public class XSSFTextParagraph implements Iterable<XSSFTextRun>{ * Set the color to be used on bullet characters within a given paragraph. * * @param colorArray the bullet color (as byte array) - * @since POI 5.4.2 + * @since POI 5.5.0 */ public void setBulletFontColor(byte[] colorArray) { CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java index ad460a5cc7..3789335a16 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java @@ -63,7 +63,7 @@ public class XSSFTextRun { /** * @param rgb - * @since POI 5.4.2 + * @since POI 5.5.0 */ public void setFontColor(byte[] rgb) { CTTextCharacterProperties rPr = getRPr(); @@ -89,7 +89,7 @@ public class XSSFTextRun { /** * @return the font color as a byte array. - * @since POI 5.4.2 + * @since POI 5.5.0 */ public byte[] getFontColorAsBytes() { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index 0c8ddce4a0..054a3dfb07 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -205,7 +205,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su /** * List of all pivot tables in workbook */ - private List<XSSFPivotTable> pivotTables; + private final List<XSSFPivotTable> pivotTables = new ArrayList<>(); private List<CTPivotCache> pivotCaches; private final XSSFFactory xssfFactory; @@ -386,7 +386,6 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su } // Create arrays for parts attached to the workbook itself - pivotTables = new ArrayList<>(); pivotCaches = new ArrayList<>(); } @@ -524,7 +523,6 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su namedRanges = new ArrayList<>(); namedRangesByName = new ArrayListValuedHashMap<>(); sheets = new ArrayList<>(); - pivotTables = new ArrayList<>(); externalLinks = new ArrayList<>(); } @@ -1042,14 +1040,14 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su */ @Override public List<XSSFPictureData> getAllPictures() { - if(pictures == null){ + if (pictures == null) { List<PackagePart> mediaParts = getPackage().getPartsByName(GET_ALL_PICTURES_PATTERN); pictures = new ArrayList<>(mediaParts.size()); for(PackagePart part : mediaParts){ pictures.add(new XSSFPictureData(part)); } } - return pictures; //YK: should return Collections.unmodifiableList(pictures); + return Collections.unmodifiableList(pictures); } /** @@ -2079,11 +2077,46 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su * @return the {@code ExternalLinksTable} list, which may be empty */ @Internal + public List<ExternalLinksTable> getExternalLinksTables() { + // needs to be the live copy because unfortunately we don't have APIs to + // add and remove external links (this method is annotated @Internal) + return externalLinks; + } + + @Deprecated // use getExternalLinksTables() instead + @Removal(version = "7.0.0") + @Internal public List<ExternalLinksTable> getExternalLinksTable() { + // needs to be the live copy because unfortunately we don't have APIs to + // add and remove external links (this method is annotated @Internal) return externalLinks; } /** + * Adds an External Links Table to the workbook. + * + * @param externalLinksTable the External Links Table to add + * @since POI 5.5.0 + */ + @Internal + public void addExternalLinksTable(ExternalLinksTable externalLinksTable) { + if (externalLinks == null) { + externalLinks = new ArrayList<>(); + } + externalLinks.add(externalLinksTable); + } + + /** + * @param index the index at which to add the External Links Table + * @return externalLinksTable the External Links Table to add + * @since POI 5.5.0 + */ + @Internal + public ExternalLinksTable getExternalLinksTable(int index) { + return externalLinks == null ? null : externalLinks.get(index); + } + + /** * * @return a collection of custom XML mappings defined in this workbook */ @@ -2125,7 +2158,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su POIXMLDocumentPart.RelationPart rp = this.createRelationship(XSSFRelation.EXTERNAL_LINKS, xssfFactory, externalLinkIdx, false); ExternalLinksTable linksTable = rp.getDocumentPart(); linksTable.setLinkedFileName(name); - this.getExternalLinksTable().add(linksTable); + this.addExternalLinksTable(linksTable); CTExternalReference ctExternalReference = this.getCTWorkbook().addNewExternalReferences().addNewExternalReference(); ctExternalReference.setId(rp.getRelationship().getId()); @@ -2385,7 +2418,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su } CTPivotCache cache = caches.addNewPivotCache(); - int tableId = getPivotTables().size()+1; + final int tableId = pivotTables.size() + 1; cache.setCacheId(tableId); cache.setId(rId); if(pivotCaches == null) { @@ -2397,12 +2430,14 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su @Beta public List<XSSFPivotTable> getPivotTables() { + // needs to be the live copy because unfortunately we don't have APIs to + // add and remove external links (this method is annotated @Beta) return pivotTables; } - @Beta - protected void setPivotTables(List<XSSFPivotTable> pivotTables) { - this.pivotTables = pivotTables; + @Internal + public void addPivotTable(XSSFPivotTable pivotTable) { + pivotTables.add(pivotTable); } public XSSFWorkbookType getWorkbookType() { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractFootnoteEndnote.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractFootnoteEndnote.java index d924dcfef7..443aa598b9 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractFootnoteEndnote.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractFootnoteEndnote.java @@ -18,6 +18,7 @@ package org.apache.poi.xwpf.usermodel; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Spliterator; @@ -101,7 +102,7 @@ public abstract class XWPFAbstractFootnoteEndnote implements Iterable<XWPFParag */ @Override public List<XWPFParagraph> getParagraphs() { - return paragraphs; + return Collections.unmodifiableList(paragraphs); } /** @@ -130,7 +131,7 @@ public abstract class XWPFAbstractFootnoteEndnote implements Iterable<XWPFParag */ @Override public List<XWPFTable> getTables() { - return tables; + return Collections.unmodifiableList(tables); } /** @@ -138,7 +139,7 @@ public abstract class XWPFAbstractFootnoteEndnote implements Iterable<XWPFParag * @return List of pictures */ public List<XWPFPictureData> getPictures() { - return pictures; + return Collections.unmodifiableList(pictures); } /** @@ -147,7 +148,7 @@ public abstract class XWPFAbstractFootnoteEndnote implements Iterable<XWPFParag */ @Override public List<IBodyElement> getBodyElements() { - return bodyElements; + return Collections.unmodifiableList(bodyElements); } /** diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractSDT.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractSDT.java index 434d857f2c..78ed571886 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractSDT.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFAbstractSDT.java @@ -30,12 +30,13 @@ public abstract class XWPFAbstractSDT implements ISDTContents { private final String title; private final String tag; private final IBody part; + private final XWPFDocument xwpfDocument; public XWPFAbstractSDT(CTSdtPr pr, IBody part) { title = (pr != null && pr.isSetAlias()) ? pr.getAlias().getVal() : ""; tag = (pr != null && pr.isSetTag()) ? pr.getTag().getVal() : ""; this.part = part; - + this.xwpfDocument = part.getXWPFDocument(); } /** @@ -86,6 +87,6 @@ public abstract class XWPFAbstractSDT implements ISDTContents { } public XWPFDocument getDocument() { - return part.getXWPFDocument(); + return xwpfDocument; } } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFComments.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFComments.java index 1aff90af29..5cbba917eb 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFComments.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFComments.java @@ -25,6 +25,7 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.util.IOUtils; import org.apache.poi.util.Internal; +import org.apache.poi.util.Removal; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; @@ -141,7 +142,10 @@ public class XWPFComments extends POIXMLDocumentPart { * @return the index to this picture (0 based), the added picture can be * obtained from {@link #getAllPictures()} . * @throws InvalidFormatException If the format of the picture is not known. + * @deprecated use {@link #addPictureData(byte[], PictureType)} instead. */ + @Deprecated + @Removal(version = "7.0.0") public String addPictureData(byte[] pictureData, int format) throws InvalidFormatException { return addPictureData(pictureData, PictureType.findByOoxmlId(format)); } @@ -233,7 +237,7 @@ public class XWPFComments extends POIXMLDocumentPart { * Get the list of {@link XWPFComment} in the Comments part. */ public List<XWPFComment> getComments() { - return comments; + return Collections.unmodifiableList(comments); } /** diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index 88a603e160..eb8c4c30c9 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -60,6 +60,7 @@ import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.util.IOUtils; import org.apache.poi.util.Internal; +import org.apache.poi.util.Removal; import org.apache.poi.wp.usermodel.HeaderFooterType; import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; @@ -1724,7 +1725,10 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * @return the next free ImageNumber * @throws InvalidFormatException If the format of the picture is not known. * @see #getNextPicNameNumber(PictureType) + * @deprecated use {@link #getNextPicNameNumber(PictureType)} instead. */ + @Deprecated + @Removal(version = "7.0.0") public int getNextPicNameNumber(int format) throws InvalidFormatException { return getNextPicNameNumber(PictureType.findByOoxmlId(format)); } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java index 225a8f496e..3c80d26a0a 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java @@ -31,6 +31,7 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.util.IOUtils; import org.apache.poi.util.Internal; +import org.apache.poi.util.Removal; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr; @@ -210,7 +211,7 @@ public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBo * @return a list of {@link XWPFParagraph} */ public List<XWPFParagraph> getListParagraph() { - return paragraphs; + return Collections.unmodifiableList(paragraphs); } public List<XWPFPictureData> getAllPictures() { @@ -235,7 +236,10 @@ public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBo * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} . * @throws InvalidFormatException If the format of the picture is not known. * @see #addPictureData(byte[], PictureType) + * @deprecated Use {@link #addPictureData(byte[], PictureType)} instead. */ + @Deprecated + @Removal(version = "7.0.0") public String addPictureData(byte[] pictureData, int format) throws InvalidFormatException { return addPictureData(pictureData, PictureType.findByOoxmlId(format)); } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java index 7d541a627f..7749977f01 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java @@ -28,6 +28,7 @@ import org.apache.poi.ooxml.POIXMLRelation; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.util.IOUtils; +import org.apache.poi.util.Removal; /** * Raw picture data, normally attached to a WordprocessingML Drawing. @@ -152,8 +153,10 @@ public class XWPFPictureData extends POIXMLDocumentPart { * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_GIF * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_DIB * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_SVG - * @see #getPictureTypeEnum() + * @deprecated use #getPictureTypeEnum() */ + @Deprecated + @Removal(version = "7.0.0") // repurpose to return PictureType public int getPictureType() { String contentType = getPackagePart().getContentType(); for (int i = 0; i < RELATIONS.length; i++) { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java index 29339e1453..645373e042 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java @@ -26,11 +26,13 @@ import java.math.BigInteger; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import javax.xml.namespace.QName; import org.apache.poi.common.usermodel.PictureType; +import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.ooxml.POIXMLException; import org.apache.poi.ooxml.util.DocumentHelper; import org.apache.poi.ooxml.util.POIXMLUnits; @@ -41,6 +43,7 @@ import org.apache.poi.xssf.usermodel.XSSFRelation; import org.apache.xmlbeans.*; import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTRelId; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; @@ -75,6 +78,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { private final String pictureText; private final IRunBody parent; private final List<XWPFPicture> pictures; + private final List<XWPFChart> charts; /** * @param r the CTR bean which holds the run attributes @@ -122,14 +126,25 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } pictureText = text.toString(); - // Do we have any embedded pictures? - // (They're a different CTPicture, under the drawingml namespace) + // Do we have any embedded pictures or charts? + // Pictures are a different CTPicture, under the drawingml namespace. + // Charts are relations and use the CTRelId type. pictures = new ArrayList<>(); + charts = new ArrayList<>(); for (XmlObject o : pictTextObjs) { for (CTPicture pict : getCTPictures(o)) { XWPFPicture picture = new XWPFPicture(pict, this); pictures.add(picture); } + XmlObject[] chartRels = o.selectPath("declare namespace c='" + CTChart.type.getName().getNamespaceURI() + "' .//*/c:chart"); + for (XmlObject chartRel : chartRels) { + if (chartRel instanceof CTRelId) { + POIXMLDocumentPart chart = getDocument().getRelationById(((CTRelId) chartRel).getId()); + if (chart instanceof XWPFChart) { + charts.add((XWPFChart) chart); + } + } + } } } @@ -1223,8 +1238,10 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_GIF * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_DIB * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_SVG - * @see #addPicture(InputStream, PictureType, String, int, int) + * @deprecated use #addPicture(InputStream, PictureType, String, int, int) */ + @Deprecated + @Removal(version = "7.0.0") public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height) throws InvalidFormatException, IOException { return addPicture(pictureData, PictureType.findByOoxmlId(pictureType), filename, width, height); @@ -1358,6 +1375,11 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { @Internal public CTInline addChart(String chartRelId) throws InvalidFormatException, IOException { try { + POIXMLDocumentPart chart = getDocument().getRelationById(chartRelId); + if (chart instanceof XWPFChart) { + charts.add((XWPFChart) chart); + } + CTInline inline = run.addNewDrawing().addNewInline(); //xml part of chart in document @@ -1399,7 +1421,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { * embedded picture image such as a .png or .jpg */ public List<XWPFPicture> getEmbeddedPictures() { - return pictures; + return Collections.unmodifiableList(pictures); } /** @@ -1817,4 +1839,12 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { return pr; } + /** + * Returns the charts embedded in the run. + * @return A list of the XWPFChart objects embedded in the run. + * @since POI 5.5.0 + */ + public List<XWPFChart> getEmbeddedCharts() { + return Collections.unmodifiableList(charts); + } } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTCell.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTCell.java index 5589287263..132fa95e11 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTCell.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTCell.java @@ -29,9 +29,11 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtCell; */ public class XWPFSDTCell extends XWPFAbstractSDT implements ICell { private final XWPFSDTContentCell cellContent; + private final CTSdtCell sdtCell; public XWPFSDTCell(CTSdtCell sdtCell, XWPFTableRow xwpfTableRow, IBody part) { super(sdtCell.getSdtPr(), part); + this.sdtCell = sdtCell; cellContent = new XWPFSDTContentCell(sdtCell.getSdtContent(), xwpfTableRow, part); } @@ -40,4 +42,12 @@ public class XWPFSDTCell extends XWPFAbstractSDT implements ICell { return cellContent; } + /** + * Return the underlying XML bean. + * @return the underlying CTSdtCell bean. + * @since POI 5.5.0 + */ + public CTSdtCell getCTSdtCell() { + return sdtCell; + } } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContentCell.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContentCell.java index d43e24fc43..3da8b87b9a 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContentCell.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContentCell.java @@ -40,14 +40,19 @@ public class XWPFSDTContentCell implements ISDTContent { //private List<ICell> cells = new ArrayList<ICell>(). - private String text = ""; + private final CTSdtContentCell sdtContentCell; + private String text; public XWPFSDTContentCell(CTSdtContentCell sdtContentCell, XWPFTableRow xwpfTableRow, IBody part) { super(); + this.sdtContentCell = sdtContentCell; + } + + private String extractTextFromSdtContentCell() { //sdtContentCell is allowed to be null: minOccurs="0" maxOccurs="1" if (sdtContentCell == null) { - return; + return ""; } StringBuilder sb = new StringBuilder(); try (final XmlCursor cursor = sdtContentCell.newCursor()) { @@ -87,7 +92,7 @@ public class XWPFSDTContentCell implements ISDTContent { depth--; } } - text = sb.toString(); + return sb.toString(); } } @@ -103,12 +108,25 @@ public class XWPFSDTContentCell implements ISDTContent { return false; } - + @Override public String getText() { + if (text == null) { + text = extractTextFromSdtContentCell(); + } return text; } + @Override public String toString() { return getText(); } + + /** + * Return the underlying XML bean. + * @return the underlying CTSdtContentCell bean. + * @since POI 5.5.0 + */ + public CTSdtContentCell getCTSdtContentCell() { + return sdtContentCell; + } } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java index f91d9a1d81..27f7dbfaf9 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java @@ -67,6 +67,7 @@ public class XWPFTableCell implements IBody, ICell { } private final CTTc ctTc; + private final XWPFDocument xwpfDocument; protected List<XWPFParagraph> paragraphs; protected List<XWPFTable> tables; protected List<IBodyElement> bodyElements; @@ -81,6 +82,7 @@ public class XWPFTableCell implements IBody, ICell { this.ctTc = cell; this.part = part; this.tableRow = tableRow; + this.xwpfDocument = part.getXWPFDocument(); bodyElements = new ArrayList<>(); paragraphs = new ArrayList<>(); @@ -524,7 +526,26 @@ public class XWPFTableCell implements IBody, ICell { @Override public XWPFDocument getXWPFDocument() { - return part.getXWPFDocument(); + if (xwpfDocument != null) { + return xwpfDocument; + } else if (part instanceof XWPFTableCell) { + return getCellDocument((XWPFTableCell) part, 0); + } else { + return part.getXWPFDocument(); + } + } + + private static final int MAX_RECURSION_DEPTH = 1000; + + private static XWPFDocument getCellDocument(XWPFTableCell cell, final int depth) { + if (depth > MAX_RECURSION_DEPTH) { + throw new IllegalStateException("Recursion depth exceeded while trying to get XWPFDocument from XWPFTableCell"); + } + if (cell.part instanceof XWPFTableCell) { + return getCellDocument((XWPFTableCell) cell.part, depth + 1); + } else { + return cell.part.getXWPFDocument(); + } } // Create a map from this XWPF-level enum to the STVerticalJc.Enum values diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java index f27df6dc24..4b43c0817c 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java @@ -18,6 +18,7 @@ package org.apache.poi.xwpf.usermodel; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.poi.ooxml.util.POIXMLUnits; @@ -211,7 +212,7 @@ public class XWPFTableRow { this.tableCells = cells; } - return tableCells; + return Collections.unmodifiableList(tableCells); } /** diff --git a/poi-ooxml/src/test/java/org/apache/poi/xddf/usermodel/chart/TestXDDFChartRemoveSeries.java b/poi-ooxml/src/test/java/org/apache/poi/xddf/usermodel/chart/TestXDDFChartRemoveSeries.java index 8bea979e33..5ebd7d13b5 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xddf/usermodel/chart/TestXDDFChartRemoveSeries.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xddf/usermodel/chart/TestXDDFChartRemoveSeries.java @@ -112,21 +112,7 @@ class TestXDDFChartRemoveSeries { } workbook.close(); } - - /** - * Attempt to remove the first series by calling chartData.getSeries().remove(0). - * <p> - * This used to corrupt the workbook but the returned <code>List</code> is unmodifiable. - */ - @Test - void testRemoveSeries0() { - procName = "testRemoveSeries0"; - fileName = procName + ".xlsx"; - - assertThrows(UnsupportedOperationException.class, () -> chartData.getSeries().remove(0)); - assertEquals(2, chartData.getSeriesCount()); - } - + /** * Remove the first series by calling chartData.removeSeries(0). * <p> diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/model/TestExternalLinksTable.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/model/TestExternalLinksTable.java index 045a03601b..9677cb0a7f 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/model/TestExternalLinksTable.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/model/TestExternalLinksTable.java @@ -32,18 +32,18 @@ public final class TestExternalLinksTable { @Test void none() throws IOException { try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("SampleSS.xlsx")) { - assertNotNull(wb.getExternalLinksTable()); - assertEquals(0, wb.getExternalLinksTable().size()); + assertNotNull(wb.getExternalLinksTables()); + assertEquals(0, wb.getExternalLinksTables().size()); } } @Test void basicRead() throws IOException { try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx")) { - assertNotNull(wb.getExternalLinksTable()); - assertEquals(1, wb.getExternalLinksTable().size()); + assertNotNull(wb.getExternalLinksTables()); + assertEquals(1, wb.getExternalLinksTables().size()); - ExternalLinksTable links = wb.getExternalLinksTable().get(0); + ExternalLinksTable links = wb.getExternalLinksTable(0); assertEquals(3, links.getSheetNames().size()); assertEquals(2, links.getDefinedNames().size()); @@ -70,13 +70,13 @@ public final class TestExternalLinksTable { @Test void basicReadWriteRead() throws IOException { try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx")) { - Name name = wb.getExternalLinksTable().get(0).getDefinedNames().get(1); + Name name = wb.getExternalLinksTable(0).getDefinedNames().get(1); name.setNameName("Testing"); name.setRefersToFormula("$A$1"); XSSFWorkbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(wb); - assertEquals(1, wbBack.getExternalLinksTable().size()); - ExternalLinksTable links = wbBack.getExternalLinksTable().get(0); + assertEquals(1, wbBack.getExternalLinksTables().size()); + ExternalLinksTable links = wbBack.getExternalLinksTable(0); name = links.getDefinedNames().get(0); assertEquals("NR_Global_B2", name.getNameName()); @@ -95,11 +95,11 @@ public final class TestExternalLinksTable { @Test void readWithReferencesToTwoExternalBooks() throws IOException { try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref2-56737.xlsx")) { - assertNotNull(wb.getExternalLinksTable()); - assertEquals(2, wb.getExternalLinksTable().size()); + assertNotNull(wb.getExternalLinksTables()); + assertEquals(2, wb.getExternalLinksTables().size()); // Check the first one, links to 56737.xlsx - ExternalLinksTable links = wb.getExternalLinksTable().get(0); + ExternalLinksTable links = wb.getExternalLinksTable(0); assertEquals("56737.xlsx", links.getLinkedFileName()); assertEquals(3, links.getSheetNames().size()); assertEquals(2, links.getDefinedNames().size()); @@ -122,7 +122,7 @@ public final class TestExternalLinksTable { // Check the second one, links to 56737.xls, slightly differently - links = wb.getExternalLinksTable().get(1); + links = wb.getExternalLinksTable(1); assertEquals("56737.xls", links.getLinkedFileName()); assertEquals(2, links.getSheetNames().size()); assertEquals(2, links.getDefinedNames().size()); diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java index b42aeba053..fa1cb56136 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java @@ -35,7 +35,7 @@ public final class TestXSSFPictureData { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithDrawing.xlsx"); List<XSSFPictureData> pictures = wb.getAllPictures(); //wb.getAllPictures() should return the same instance across multiple calls - assertSame(pictures, wb.getAllPictures()); + assertEquals(pictures, wb.getAllPictures()); assertEquals(5, pictures.size()); String[] ext = {"jpeg", "emf", "png", "emf", "wmf"}; diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java index 7926912f6c..19da165b85 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java @@ -1391,7 +1391,7 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook { try( XSSFWorkbook workbook2 = new XSSFWorkbook(bosB.toInputStream()) ) { - CTExternalLink link = workbook2.getExternalLinksTable().get(0).getCTExternalLink(); + CTExternalLink link = workbook2.getExternalLinksTable(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); diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFComment.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFComment.java index 79ffa2d701..256fe9fe7e 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFComment.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFComment.java @@ -16,6 +16,7 @@ ==================================================================== */ package org.apache.poi.xwpf.usermodel; +import org.apache.poi.common.usermodel.PictureType; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.LocaleUtil; import org.apache.poi.xwpf.XWPFTestDataSamples; @@ -100,7 +101,7 @@ class TestXWPFComment { XWPFParagraph paragraph = comment.createParagraph(); XWPFRun r = paragraph.createRun(); r.addPicture(new ByteArrayInputStream(new byte[0]), - Document.PICTURE_TYPE_JPEG, "test.jpg", 21, 32); + PictureType.JPEG, "test.jpg", 21, 32); assertEquals(1, comments.getAllPictures().size()); assertEquals(1, doc.getAllPackagePictures().size()); diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java index 926ea82868..452c869995 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java @@ -117,7 +117,7 @@ class TestXWPFPictureData { // Add a picture to the first paragraph header.getParagraphs().get(0).getRuns().get(0).addPicture( new ByteArrayInputStream(new byte[]{1, 2, 3, 4}), - Document.PICTURE_TYPE_JPEG, "test.jpg", 2, 2); + PictureType.JPEG, "test.jpg", 2, 2); // Check verifyOneHeaderPicture(doc); diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java index ed79c2272d..87561e720d 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java @@ -36,6 +36,7 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.Units; import org.apache.poi.wp.usermodel.HeaderFooterType; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.poi.xwpf.XWPFTestDataSamples; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; import org.junit.jupiter.api.AfterEach; @@ -552,7 +553,7 @@ class TestXWPFRun { assertEquals(0, doc.getAllPictures().size()); assertEquals(0, r.getEmbeddedPictures().size()); - r.addPicture(new ByteArrayInputStream(new byte[0]), Document.PICTURE_TYPE_JPEG, "test.jpg", 21, 32); + r.addPicture(new ByteArrayInputStream(new byte[0]), PictureType.JPEG, "test.jpg", 21, 32); assertEquals(1, doc.getAllPictures().size()); assertEquals(1, r.getEmbeddedPictures().size()); @@ -580,7 +581,7 @@ class TestXWPFRun { assertEquals(0, hdr.getAllPictures().size()); assertEquals(0, r.getEmbeddedPictures().size()); - r.addPicture(new ByteArrayInputStream(new byte[0]), Document.PICTURE_TYPE_JPEG, "test.jpg", 21, 32); + r.addPicture(new ByteArrayInputStream(new byte[0]), PictureType.JPEG, "test.jpg", 21, 32); assertEquals(1, hdr.getAllPictures().size()); assertEquals(1, r.getEmbeddedPictures().size()); @@ -627,7 +628,7 @@ class TestXWPFRun { try (XWPFDocument document = new XWPFDocument()) { document.createParagraph().createRun().addPicture( - new ByteArrayInputStream(image), Document.PICTURE_TYPE_JPEG, "test.jpg", Units.toEMU(300), Units.toEMU(100)); + new ByteArrayInputStream(image), PictureType.JPEG, "test.jpg", Units.toEMU(300), Units.toEMU(100)); try (XWPFDocument docBack = writeOutAndReadBack(document)) { List<XWPFPicture> pictures = docBack.getParagraphArray(0).getRuns().get(0).getEmbeddedPictures(); @@ -858,7 +859,7 @@ class TestXWPFRun { assertEquals(0, hdr.getAllPictures().size()); assertEquals(0, r.getEmbeddedPictures().size()); - r.addPicture(new ByteArrayInputStream(new byte[0]), Document.PICTURE_TYPE_JPEG, "test.jpg", 21, 32); + r.addPicture(new ByteArrayInputStream(new byte[0]), PictureType.JPEG, "test.jpg", 21, 32); assertEquals(1, hdr.getAllPictures().size()); assertEquals(1, r.getEmbeddedPictures().size()); @@ -909,4 +910,34 @@ class TestXWPFRun { run.setText("TEST STRING"); assertEquals(1, run.getNumberOfTexts()); } + + @Test + void testGetEmbeddedCharts() throws IOException { + try (XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("61745.docx")) { + List<XWPFChart> charts = sampleDoc.getCharts(); + assertEquals(2, charts.size()); + List<XWPFChart> run1Charts = sampleDoc.getParagraphArray(0).getRuns().get(0).getEmbeddedCharts(); + assertEquals(1, run1Charts.size()); + assertEquals(charts.get(0), run1Charts.get(0)); + List<XWPFChart> run2Charts = sampleDoc.getParagraphArray(1).getRuns().get(0).getEmbeddedCharts(); + assertEquals(1, run2Charts.size()); + assertEquals(charts.get(1), run2Charts.get(0)); + } + } + + @Test + void testAddChartGetEmbeddedCharts() throws InvalidFormatException, IOException { + XWPFRun run1 = p.createRun(); + XWPFChart chart1 = doc.createChart(run1, XDDFChart.DEFAULT_WIDTH, XDDFChart.DEFAULT_HEIGHT); + assertEquals(1, run1.getEmbeddedCharts().size()); + assertEquals(chart1, run1.getEmbeddedCharts().get(0)); + + XWPFRun run2 = p.createRun(); + XWPFChart chart2 = doc.createChart(run2, XDDFChart.DEFAULT_WIDTH, XDDFChart.DEFAULT_HEIGHT); + assertEquals(1, run2.getEmbeddedCharts().size()); + assertEquals(chart2, run2.getEmbeddedCharts().get(0)); + + XWPFRun run3 = p.createRun(); + assertEquals(0, run3.getEmbeddedCharts().size()); + } } diff --git a/poi/src/main/java/org/apache/poi/hssf/util/HSSFColor.java b/poi/src/main/java/org/apache/poi/hssf/util/HSSFColor.java index 107b116798..cc3d3ed796 100644 --- a/poi/src/main/java/org/apache/poi/hssf/util/HSSFColor.java +++ b/poi/src/main/java/org/apache/poi/hssf/util/HSSFColor.java @@ -182,7 +182,7 @@ public class HSSFColor implements Color { * @param index * @param index2 * @param rgb combined value of RGB - * @since POI 5.4.2 + * @since POI 5.5.0 */ public HSSFColor(int index, int index2, int rgb) { this.index = index; diff --git a/poi/src/main/java/org/apache/poi/poifs/dev/POIFSDump.java b/poi/src/main/java/org/apache/poi/poifs/dev/POIFSDump.java index d29745ea82..fdf6bb85f4 100644 --- a/poi/src/main/java/org/apache/poi/poifs/dev/POIFSDump.java +++ b/poi/src/main/java/org/apache/poi/poifs/dev/POIFSDump.java @@ -71,7 +71,7 @@ public final class POIFSDump { DirectoryEntry root = fs.getRoot(); String filenameWithoutPath = new File(filename).getName(); File dumpDir = new File(filenameWithoutPath + "_dump"); - File file = new File(dumpDir, root.getName()); + File file = IOUtils.newFile(dumpDir, root.getName()); if (!file.exists() && !file.mkdirs()) { throw new IOException("Could not create directory " + file); } @@ -104,13 +104,14 @@ public final class POIFSDump { try (DocumentInputStream is = new DocumentInputStream(node)) { bytes = IOUtils.toByteArray(is); } - try (OutputStream out = Files.newOutputStream(new File(parent, node.getName().trim()).toPath())) { + try (OutputStream out = Files.newOutputStream( + IOUtils.newFile(parent, node.getName().trim()).toPath())) { out.write(bytes); } } else if (entry instanceof DirectoryEntry){ DirectoryEntry dir = (DirectoryEntry)entry; - File file = new File(parent, entry.getName()); - if(!file.exists() && !file.mkdirs()) { + File file = IOUtils.newFile(parent, entry.getName()); + if (!file.exists() && !file.mkdirs()) { throw new IOException("Could not create directory " + file); } dump(dir, file); @@ -119,8 +120,9 @@ public final class POIFSDump { } } } + public static void dump(POIFSFileSystem fs, int startBlock, String name, File parent) throws IOException { - File file = new File(parent, name); + File file = IOUtils.newFile(parent, name); try (OutputStream out = Files.newOutputStream(file.toPath())) { POIFSStream stream = new POIFSStream(fs, startBlock); diff --git a/poi/src/main/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java b/poi/src/main/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java index c0db47e5fb..e5a80e9034 100644 --- a/poi/src/main/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java +++ b/poi/src/main/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java @@ -26,6 +26,7 @@ import java.nio.file.Files; import java.util.Map; import java.util.Map.Entry; +import org.apache.poi.util.IOUtils; import org.apache.poi.util.StringUtil; /** @@ -95,7 +96,7 @@ public class VBAMacroExtractor { System.out.println(); System.out.println(moduleCode); } else { - File out = new File(outputDir, moduleName + extension); + File out = IOUtils.newFile(outputDir, moduleName + extension); try (OutputStream fout = Files.newOutputStream(out.toPath()); OutputStreamWriter fwriter = new OutputStreamWriter(fout, StringUtil.UTF8)) { fwriter.write(moduleCode); diff --git a/poi/src/main/java/org/apache/poi/poifs/property/PropertyTable.java b/poi/src/main/java/org/apache/poi/poifs/property/PropertyTable.java index 062627ae52..562413e0c6 100644 --- a/poi/src/main/java/org/apache/poi/poifs/property/PropertyTable.java +++ b/poi/src/main/java/org/apache/poi/poifs/property/PropertyTable.java @@ -111,7 +111,7 @@ public final class PropertyTable implements BATManaged { Property property = _properties.get(0); if (property != null) { if (property instanceof DirectoryProperty) { - populatePropertyTree((DirectoryProperty) property); + populatePropertyTree((DirectoryProperty) property, 0); } else { throw new IOException("Invalid format, cannot convert property " + property + " to DirectoryProperty"); } @@ -227,7 +227,14 @@ public final class PropertyTable implements BATManaged { _header_block.setPropertyCount(countBlocks()); } - private void populatePropertyTree(DirectoryProperty root) throws IOException { + // Maximum depth of the property tree to prevent stackoverflow errors + private static final int MAX_PROPERTY_DEPTH = 1000; + + private void populatePropertyTree(final DirectoryProperty root, final int depth) throws IOException { + if (depth > MAX_PROPERTY_DEPTH) { + throw new IOException("Property tree too deep, likely a corrupt file"); + } + int index = root.getChildIndex(); if (!Property.isValidIndex(index)) { @@ -246,7 +253,7 @@ public final class PropertyTable implements BATManaged { root.addChild(property); if (property.isDirectory()) { - populatePropertyTree(( DirectoryProperty ) property); + populatePropertyTree((DirectoryProperty) property, depth + 1); } index = property.getPreviousChildIndex(); if (isValidIndex(index)) { diff --git a/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java b/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java index d7e2db2236..a34b8b370d 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java @@ -288,7 +288,7 @@ public final class AnalysisToolPak implements UDFFinder { * @param func the function to register * @param force force registration even if the function is already registered or unknown to POI * @throws IllegalArgumentException if the function is unknown or already registered (and `force` is not true). - * @since POI 5.4.2 + * @since POI 5.5.0 */ public static void registerFunction(String name, FreeRefFunction func, boolean force) { AnalysisToolPak inst = (AnalysisToolPak)instance; diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java b/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java index 401b1c47a1..452c8948bb 100644 --- a/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java +++ b/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java @@ -1008,7 +1008,7 @@ public class DateUtil { * If disabled, the cache will not be used and each check will be performed independently. * * @param enable true to enable the cache, false to disable it (enabled, by default) - * @since POI 5.4.2 + * @since POI 5.5.0 */ public static void enableThreadLocalCache(final boolean enable) { // enable thread-local cache for date format checking diff --git a/poi/src/main/java/org/apache/poi/ss/util/CellUtil.java b/poi/src/main/java/org/apache/poi/ss/util/CellUtil.java index b2879fb7fd..7c1b3ad81e 100644 --- a/poi/src/main/java/org/apache/poi/ss/util/CellUtil.java +++ b/poi/src/main/java/org/apache/poi/ss/util/CellUtil.java @@ -880,7 +880,7 @@ public final class CellUtil { * @param dest destination cell style * @param destWorkbook destination workbook (can be null but some font info will not be copied if null is passed) * @throws IllegalArgumentException if source or destination styles are null - * @since POI 5.4.2 + * @since POI 5.5.0 */ @Internal public static void cloneStyle(CellStyle src, CellStyle dest, Workbook destWorkbook) { diff --git a/poi/src/main/java/org/apache/poi/util/DefaultTempFileCreationStrategy.java b/poi/src/main/java/org/apache/poi/util/DefaultTempFileCreationStrategy.java index 584f7dec4c..48495c94a3 100644 --- a/poi/src/main/java/org/apache/poi/util/DefaultTempFileCreationStrategy.java +++ b/poi/src/main/java/org/apache/poi/util/DefaultTempFileCreationStrategy.java @@ -71,7 +71,7 @@ public class DefaultTempFileCreationStrategy implements TempFileCreationStrategy * Creates the strategy allowing to set a custom directory for the temporary files. * <p> * If you provide a non-null dir as input, it must be a directory (if it already exists). - * Since POI 5.4.2, this is checked at construction time. In previous versions, it was checked + * Since POI 5.5.0, this is checked at construction time. In previous versions, it was checked * at the first call to {@link #createTempFile(String, String)} or {@link #createTempDirectory(String)}. * </p> * diff --git a/poi/src/main/java/org/apache/poi/util/IOUtils.java b/poi/src/main/java/org/apache/poi/util/IOUtils.java index ff86043a54..3487c00f96 100644 --- a/poi/src/main/java/org/apache/poi/util/IOUtils.java +++ b/poi/src/main/java/org/apache/poi/util/IOUtils.java @@ -601,7 +601,6 @@ public final class IOUtils { return Arrays.copyOfRange(src, offset, offset+realLength); } - /** * Simple utility function to check that you haven't hit EOF * when reading a byte. @@ -618,6 +617,28 @@ public final class IOUtils { return b; } + /** + * Creates a new file in the given parent directory with the given name. + * There is a check to prevent path traversal attacks. Only path traversal + * that would lead to a file outside the parent directory is regarded as an issue. + * + * @param parent The parent directory where the file should be created. + * @param name The name of the file to create. + * @return The created file. + * @throws IOException If path traversal is detected. + * @since POI 5.5.0 + */ + public static File newFile(final File parent, final String name) throws IOException { + final File file = new File(parent, name); + if (!file.toPath().toAbsolutePath().normalize().startsWith( + parent.toPath().toAbsolutePath().normalize() + )) { + throw new IOException(String.format( + Locale.ROOT, "Failing due to path traversal in `%s`", name)); + } + return file; + } + private static void throwRFE(long length, int maxLength) { throw new RecordFormatException(String.format(Locale.ROOT, "Tried to allocate an array of length %,d" + ", but the maximum length for this record type is %,d.%n" + diff --git a/poi/src/main/java/org/apache/poi/util/TempFile.java b/poi/src/main/java/org/apache/poi/util/TempFile.java index 2b668dc885..f5ab4e6f4a 100644 --- a/poi/src/main/java/org/apache/poi/util/TempFile.java +++ b/poi/src/main/java/org/apache/poi/util/TempFile.java @@ -62,7 +62,7 @@ public final class TempFile { * * @param strategy The new strategy to be used to create the temporary files for this thread. * <code>null</code> can be used to reset the strategy for this thread to the default one. - * @since POI 5.4.2 + * @since POI 5.5.0 */ public static void setThreadLocalTempFileCreationStrategy(TempFileCreationStrategy strategy) { if (strategy == null) { @@ -104,7 +104,7 @@ public final class TempFile { * @param task the task to be executed with the given temp file strategy * @return the result of the given task * - * @since POI 5.4.2 + * @since POI 5.5.0 */ public static <R> R withStrategy(TempFileCreationStrategy newStrategy, Supplier<? extends R> task) { Objects.requireNonNull(newStrategy, "newStrategy"); diff --git a/poi/src/test/java/org/apache/poi/util/TestIOUtils.java b/poi/src/test/java/org/apache/poi/util/TestIOUtils.java index 7f026adc74..094e138eef 100644 --- a/poi/src/test/java/org/apache/poi/util/TestIOUtils.java +++ b/poi/src/test/java/org/apache/poi/util/TestIOUtils.java @@ -41,6 +41,7 @@ import java.nio.charset.StandardCharsets; import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; import org.apache.poi.EmptyFileException; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Isolated; @@ -575,6 +576,59 @@ final class TestIOUtils { assertEquals(4, ret.length); } + @Test + void testNewFile() throws IOException { + final File parent = TempFile.createTempDirectory("create-file-test"); + try { + final String path0 = "path/to/file.txt"; + final File outFile = IOUtils.newFile(parent, path0); + assertTrue(outFile.getAbsolutePath().endsWith(path0), + "unexpected path: " + outFile.getAbsolutePath()); + } finally { + assertTrue(parent.delete()); + } + } + + @Test + void testAllowedPathTraversal() throws IOException { + final File parent = TempFile.createTempDirectory("path-traversal-test"); + try { + // this path is ok because it doesn't walk out of the parent directory + final String path0 = "a/b/c/../d/e/../../f/g/./h"; + File outFile = IOUtils.newFile(parent, path0); + assertTrue(outFile.getAbsolutePath().endsWith(path0), + "unexpected path: " + outFile.getAbsolutePath()); + } finally { + assertTrue(parent.delete()); + } + } + + @Test + void testAllowedPathTraversal2() throws IOException { + final File parent = TempFile.createTempDirectory("path-traversal-test"); + try { + // this path is ok because it doesn't walk out of the parent directory + // the initial slash is ignored and the generated path is relative to the parent directory + final String path0 = "/a/b/c.txt"; + File outFile = IOUtils.newFile(parent, path0); + assertTrue(outFile.getAbsolutePath().endsWith(path0), + "unexpected path: " + outFile.getAbsolutePath()); + } finally { + assertTrue(parent.delete()); + } + } + + @Test + void testDisallowedPathTraversal() throws IOException { + final File parent = TempFile.createTempDirectory("path-traversal-test"); + try { + final String path0 = "../a/b/c.txt"; + Assertions.assertThrows(IOException.class, () -> IOUtils.newFile(parent, path0)); + } finally { + assertTrue(parent.delete()); + } + } + /** * This returns 0 for the first call to skip and then reads * as requested. This tests that the fallback to read() works. diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index c137381f78..ab248da55c 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -70,10 +70,11 @@ </p> </section> - <release version="5.4.2" date="2025-??-??"> + <release version="5.5.0" date="2025-??-??"> <summary> <summary-item>Upgrade batik dependency to 1.19</summary-item> - <summary-item>Upgrade bouncycastle dependency to 1.80</summary-item> + <summary-item>Upgrade bouncycastle dependency to 1.81</summary-item> + <summary-item>Upgrade commons-codec dependency to 1.19.0</summary-item> <summary-item>Upgrade commons-collections4 dependency to 4.5.0</summary-item> <summary-item>Upgrade commons-io dependency to 2.20.0</summary-item> <summary-item>Upgrade pdfbox dependency to 3.0.5</summary-item> |