From d14beb4b84b00846ef742ec4ee90906b10da0efb Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Fri, 4 Jun 2010 12:02:36 +0000 Subject: [PATCH] Fix bug #49386 - avoid NPE when extracting OOXML file properties which are dates Also tidy up POIXMLPropertiesTextExtractor to exclude properties which are missing git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@951384 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../poi/POIXMLPropertiesTextExtractor.java | 89 +++++++++++------- .../opc/internal/PackagePropertiesPart.java | 9 +- .../poi/TestXMLPropertiesTextExtractor.java | 29 +++++- test-data/slideshow/49386-null_dates.pptx | Bin 0 -> 17124 bytes 5 files changed, 87 insertions(+), 41 deletions(-) create mode 100644 test-data/slideshow/49386-null_dates.pptx diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 4ec3ca5ba..c29e9f9ee 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 49386 - avoid NPE when extracting OOXML file properties which are dates 49377 - only call DecimalFormat.setRoundingMode on Java 1.6 - it's needed to match excel's rendering of numbers 49378 - correct 1.6ism Parse the HSMF headers chunk if present, and use it to find Dates in text extraction if needed diff --git a/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java b/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java index 47fe05ed9..6d3134055 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java +++ b/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java @@ -20,6 +20,8 @@ package org.apache.poi; import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart; import org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty; +import java.util.Date; + /** * A {@link POITextExtractor} for returning the textual * content of the OOXML file properties, eg author @@ -41,6 +43,24 @@ public class POIXMLPropertiesTextExtractor extends POIXMLTextExtractor { public POIXMLPropertiesTextExtractor(POIXMLTextExtractor otherExtractor) { super(otherExtractor.getDocument()); } + + private void appendIfPresent(StringBuffer text, String thing, boolean value) { + appendIfPresent(text, thing, Boolean.toString(value)); + } + private void appendIfPresent(StringBuffer text, String thing, int value) { + appendIfPresent(text, thing, Integer.toString(value)); + } + private void appendIfPresent(StringBuffer text, String thing, Date value) { + if(value == null) { return; } + appendIfPresent(text, thing, value.toString()); + } + private void appendIfPresent(StringBuffer text, String thing, String value) { + if(value == null) { return; } + text.append(thing); + text.append(" = "); + text.append(value); + text.append("\n"); + } /** * Returns the core document properties, eg author @@ -50,25 +70,26 @@ public class POIXMLPropertiesTextExtractor extends POIXMLTextExtractor { PackagePropertiesPart props = getDocument().getProperties().getCoreProperties().getUnderlyingProperties(); - text.append("Category = " + props.getCategoryProperty().getValue() + "\n"); - text.append("ContentStatus = " + props.getContentStatusProperty().getValue() + "\n"); - text.append("ContentType = " + props.getContentTypeProperty().getValue() + "\n"); - text.append("Created = " + props.getCreatedProperty().getValue() + "\n"); - text.append("CreatedString = " + props.getCreatedPropertyString() + "\n"); - text.append("Creator = " + props.getCreatorProperty().getValue() + "\n"); - text.append("Description = " + props.getDescriptionProperty().getValue() + "\n"); - text.append("Identifier = " + props.getIdentifierProperty().getValue() + "\n"); - text.append("Keywords = " + props.getKeywordsProperty().getValue() + "\n"); - text.append("Language = " + props.getLanguageProperty().getValue() + "\n"); - text.append("LastModifiedBy = " + props.getLastModifiedByProperty().getValue() + "\n"); - text.append("LastPrinted = " + props.getLastPrintedProperty().getValue() + "\n"); - text.append("LastPrintedString = " + props.getLastPrintedPropertyString() + "\n"); - text.append("Modified = " + props.getModifiedProperty().getValue() + "\n"); - text.append("ModifiedString = " + props.getModifiedPropertyString() + "\n"); - text.append("Revision = " + props.getRevisionProperty().getValue() + "\n"); - text.append("Subject = " + props.getSubjectProperty().getValue() + "\n"); - text.append("Title = " + props.getTitleProperty().getValue() + "\n"); - text.append("Version = " + props.getVersionProperty().getValue() + "\n"); + appendIfPresent(text, "Category", props.getCategoryProperty().getValue()); + appendIfPresent(text, "Category", props.getCategoryProperty().getValue()); + appendIfPresent(text, "ContentStatus", props.getContentStatusProperty().getValue()); + appendIfPresent(text, "ContentType", props.getContentTypeProperty().getValue()); + appendIfPresent(text, "Created", props.getCreatedProperty().getValue()); + appendIfPresent(text, "CreatedString", props.getCreatedPropertyString()); + appendIfPresent(text, "Creator", props.getCreatorProperty().getValue()); + appendIfPresent(text, "Description", props.getDescriptionProperty().getValue()); + appendIfPresent(text, "Identifier", props.getIdentifierProperty().getValue()); + appendIfPresent(text, "Keywords", props.getKeywordsProperty().getValue()); + appendIfPresent(text, "Language", props.getLanguageProperty().getValue()); + appendIfPresent(text, "LastModifiedBy", props.getLastModifiedByProperty().getValue()); + appendIfPresent(text, "LastPrinted", props.getLastPrintedProperty().getValue()); + appendIfPresent(text, "LastPrintedString", props.getLastPrintedPropertyString()); + appendIfPresent(text, "Modified", props.getModifiedProperty().getValue()); + appendIfPresent(text, "ModifiedString", props.getModifiedPropertyString()); + appendIfPresent(text, "Revision", props.getRevisionProperty().getValue()); + appendIfPresent(text, "Subject", props.getSubjectProperty().getValue()); + appendIfPresent(text, "Title", props.getTitleProperty().getValue()); + appendIfPresent(text, "Version", props.getVersionProperty().getValue()); return text.toString(); } @@ -81,21 +102,21 @@ public class POIXMLPropertiesTextExtractor extends POIXMLTextExtractor { org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.CTProperties props = getDocument().getProperties().getExtendedProperties().getUnderlyingProperties(); - text.append("Application = " + props.getApplication() + "\n"); - text.append("AppVersion = " + props.getAppVersion() + "\n"); - text.append("Characters = " + props.getCharacters() + "\n"); - text.append("CharactersWithSpaces = " + props.getCharactersWithSpaces() + "\n"); - text.append("Company = " + props.getCompany() + "\n"); - text.append("HyperlinkBase = " + props.getHyperlinkBase() + "\n"); - text.append("HyperlinksChanged = " + props.getHyperlinksChanged() + "\n"); - text.append("Lines = " + props.getLines() + "\n"); - text.append("LinksUpToDate = " + props.getLinksUpToDate() + "\n"); - text.append("Manager = " + props.getManager() + "\n"); - text.append("Pages = " + props.getPages() + "\n"); - text.append("Paragraphs = " + props.getParagraphs() + "\n"); - text.append("PresentationFormat = " + props.getPresentationFormat() + "\n"); - text.append("Template = " + props.getTemplate() + "\n"); - text.append("TotalTime = " + props.getTotalTime() + "\n"); + appendIfPresent(text, "Application", props.getApplication()); + appendIfPresent(text, "AppVersion", props.getAppVersion()); + appendIfPresent(text, "Characters", props.getCharacters()); + appendIfPresent(text, "CharactersWithSpaces", props.getCharactersWithSpaces()); + appendIfPresent(text, "Company", props.getCompany()); + appendIfPresent(text, "HyperlinkBase", props.getHyperlinkBase()); + appendIfPresent(text, "HyperlinksChanged", props.getHyperlinksChanged()); + appendIfPresent(text, "Lines", props.getLines()); + appendIfPresent(text, "LinksUpToDate", props.getLinksUpToDate()); + appendIfPresent(text, "Manager", props.getManager()); + appendIfPresent(text, "Pages", props.getPages()); + appendIfPresent(text, "Paragraphs", props.getParagraphs()); + appendIfPresent(text, "PresentationFormat", props.getPresentationFormat()); + appendIfPresent(text, "Template", props.getTemplate()); + appendIfPresent(text, "TotalTime", props.getTotalTime()); return text.toString(); } diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java index 800895df6..af40d361a 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java @@ -583,10 +583,15 @@ public final class PackagePropertiesPart extends PackagePart implements if (d == null) { return ""; } + Date date = d.getValue(); + if (date == null) { + return ""; + } + SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss'Z'"); - df.setTimeZone(TimeZone.getTimeZone("UTC")); - return df.format(d.getValue()); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + return df.format(date); } @Override diff --git a/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java b/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java index 303946a11..b19477f04 100644 --- a/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java +++ b/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java @@ -16,17 +16,17 @@ ==================================================================== */ package org.apache.poi; -import java.io.File; +import junit.framework.TestCase; -import org.apache.poi.xssf.extractor.XSSFExcelExtractor; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.util.PackageHelper; - -import junit.framework.TestCase; +import org.apache.poi.xslf.XSLFSlideShow; +import org.apache.poi.xssf.extractor.XSSFExcelExtractor; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; public final class TestXMLPropertiesTextExtractor extends TestCase { private static final POIDataSamples _ssSamples = POIDataSamples.getSpreadSheetInstance(); + private static final POIDataSamples _slSamples = POIDataSamples.getSlideShowInstance(); public void testGetFromMainExtractor() throws Exception { OPCPackage pkg = PackageHelper.open(_ssSamples.openResourceAsStream("ExcelWithAttachments.xlsm")); @@ -87,4 +87,23 @@ public final class TestXMLPropertiesTextExtractor extends TestCase { public void testCustom() { // TODO! } + + /** + * Bug #49386 - some properties, especially + * dates can be null + */ + public void testWithSomeNulls() throws Exception { + OPCPackage pkg = OPCPackage.open( + _slSamples.openResourceAsStream("49386-null_dates.pptx") + ); + XSLFSlideShow sl = new XSLFSlideShow(pkg); + + POIXMLPropertiesTextExtractor ext = new POIXMLPropertiesTextExtractor(sl); + ext.getText(); + + String text = ext.getText(); + assertFalse(text.contains("Created =")); // With date is null + assertTrue(text.contains("CreatedString = ")); // Via string is blank + assertTrue(text.contains("LastModifiedBy = IT Client Services")); + } } diff --git a/test-data/slideshow/49386-null_dates.pptx b/test-data/slideshow/49386-null_dates.pptx new file mode 100644 index 0000000000000000000000000000000000000000..77fc378ce9fb2237c1cecf5c1649c6ad51afef4e GIT binary patch literal 17124 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 He%$>(>B{tJ literal 0 HcmV?d00001