From 724572138ef85ce9ae0ea0ddb20c0008d5df6268 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 19 Sep 2007 11:56:36 +0000 Subject: [PATCH] Move POIDocument out of the scratchpad git-svn-id: https://svn.eu.apache.org/repos/asf/poi/trunk@577259 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 4 + src/java/org/apache/poi/POIDocument.java | 216 +++++++++++++++++++++ src/testcases/org/apache/poi/TestPOIDocument.java | 117 +++++++++++ .../apache/poi/hslf/data/basic_test_ppt_file.ppt | Bin 0 -> 15360 bytes src/testcases/org/apache/poi/hwpf/data/test2.doc | Bin 0 -> 19968 bytes 5 files changed, 337 insertions(+) create mode 100644 src/java/org/apache/poi/POIDocument.java create mode 100644 src/testcases/org/apache/poi/TestPOIDocument.java create mode 100644 src/testcases/org/apache/poi/hslf/data/basic_test_ppt_file.ppt create mode 100755 src/testcases/org/apache/poi/hwpf/data/test2.doc diff --git a/build.xml b/build.xml index ce1cf7f..4dac62c 100644 --- a/build.xml +++ b/build.xml @@ -387,6 +387,10 @@ under the License. + + diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java new file mode 100644 index 0000000..bc3b78d --- /dev/null +++ b/src/java/org/apache/poi/POIDocument.java @@ -0,0 +1,216 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import org.apache.poi.hpsf.DocumentSummaryInformation; +import org.apache.poi.hpsf.MutablePropertySet; +import org.apache.poi.hpsf.PropertySet; +import org.apache.poi.hpsf.PropertySetFactory; +import org.apache.poi.hpsf.SummaryInformation; +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.DocumentEntry; +import org.apache.poi.poifs.filesystem.DocumentInputStream; +import org.apache.poi.poifs.filesystem.Entry; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * This holds the common functionality for all POI + * Document classes. + * Currently, this relates to Document Information Properties + * + * @author Nick Burch + */ +public abstract class POIDocument { + /** Holds metadata on our document */ + protected SummaryInformation sInf; + /** Holds further metadata on our document */ + protected DocumentSummaryInformation dsInf; + /** The open POIFS FileSystem that contains our document */ + protected POIFSFileSystem filesystem; + + /** For our own logging use */ + protected POILogger logger = POILogFactory.getLogger(this.getClass()); + + + /** + * Fetch the Document Summary Information of the document + */ + public DocumentSummaryInformation getDocumentSummaryInformation() { return dsInf; } + + /** + * Fetch the Summary Information of the document + */ + public SummaryInformation getSummaryInformation() { return sInf; } + + /** + * Find, and create objects for, the standard + * Documment Information Properties (HPSF) + */ + protected void readProperties() { + // DocumentSummaryInformation + dsInf = (DocumentSummaryInformation)getPropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME); + + // SummaryInformation + sInf = (SummaryInformation)getPropertySet(SummaryInformation.DEFAULT_STREAM_NAME); + } + + /** + * For a given named property entry, either return it or null if + * if it wasn't found + */ + protected PropertySet getPropertySet(String setName) { + DocumentInputStream dis; + try { + // Find the entry, and get an input stream for it + dis = filesystem.createDocumentInputStream(setName); + } catch(IOException ie) { + // Oh well, doesn't exist + System.err.println("Error getting property set with name " + setName + "\n" + ie); + return null; + } + + try { + // Create the Property Set + PropertySet set = PropertySetFactory.create(dis); + return set; + } catch(IOException ie) { + // Must be corrupt or something like that + System.err.println("Error creating property set with name " + setName + "\n" + ie); + } catch(org.apache.poi.hpsf.HPSFException he) { + // Oh well, doesn't exist + System.err.println("Error creating property set with name " + setName + "\n" + he); + } + return null; + } + + /** + * Writes out the standard Documment Information Properties (HPSF) + * @param outFS the POIFSFileSystem to write the properties into + */ + protected void writeProperties(POIFSFileSystem outFS) throws IOException { + writeProperties(outFS, null); + } + /** + * Writes out the standard Documment Information Properties (HPSF) + * @param outFS the POIFSFileSystem to write the properties into + * @param writtenEntries a list of POIFS entries to add the property names too + */ + protected void writeProperties(POIFSFileSystem outFS, List writtenEntries) throws IOException { + if(sInf != null) { + writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME,sInf,outFS); + if(writtenEntries != null) { + writtenEntries.add(SummaryInformation.DEFAULT_STREAM_NAME); + } + } + if(dsInf != null) { + writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME,dsInf,outFS); + if(writtenEntries != null) { + writtenEntries.add(DocumentSummaryInformation.DEFAULT_STREAM_NAME); + } + } + } + + /** + * Writes out a given ProperySet + * @param name the (POIFS Level) name of the property to write + * @param set the PropertySet to write out + * @param outFS the POIFSFileSystem to write the property into + */ + protected void writePropertySet(String name, PropertySet set, POIFSFileSystem outFS) throws IOException { + try { + MutablePropertySet mSet = new MutablePropertySet(set); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + mSet.write(bOut); + byte[] data = bOut.toByteArray(); + ByteArrayInputStream bIn = new ByteArrayInputStream(data); + outFS.createDocument(bIn,name); + + logger.log(POILogger.INFO, "Wrote property set " + name + " of size " + data.length); + } catch(org.apache.poi.hpsf.WritingNotSupportedException wnse) { + System.err.println("Couldn't write property set with name " + name + " as not supported by HPSF yet"); + } + } + + /** + * Copies nodes from one POIFS to the other minus the excepts + * @param source is the source POIFS to copy from + * @param target is the target POIFS to copy to + * @param excepts is a list of Strings specifying what nodes NOT to copy + */ + protected void copyNodes(POIFSFileSystem source, POIFSFileSystem target, + List excepts) throws IOException { + //System.err.println("CopyNodes called"); + + DirectoryEntry root = source.getRoot(); + DirectoryEntry newRoot = target.getRoot(); + + Iterator entries = root.getEntries(); + + while (entries.hasNext()) { + Entry entry = (Entry)entries.next(); + if (!isInList(entry.getName(), excepts)) { + copyNodeRecursively(entry,newRoot); + } + } + } + + /** + * Checks to see if the String is in the list, used when copying + * nodes between one POIFS and another + */ + private boolean isInList(String entry, List list) { + for (int k = 0; k < list.size(); k++) { + if (list.get(k).equals(entry)) { + return true; + } + } + return false; + } + + /** + * Copies an Entry into a target POIFS directory, recursively + */ + private void copyNodeRecursively(Entry entry, DirectoryEntry target) + throws IOException { + //System.err.println("copyNodeRecursively called with "+entry.getName()+ + // ","+target.getName()); + DirectoryEntry newTarget = null; + if (entry.isDirectoryEntry()) { + newTarget = target.createDirectory(entry.getName()); + Iterator entries = ((DirectoryEntry)entry).getEntries(); + + while (entries.hasNext()) { + copyNodeRecursively((Entry)entries.next(),newTarget); + } + } else { + DocumentEntry dentry = (DocumentEntry)entry; + DocumentInputStream dstream = new DocumentInputStream(dentry); + target.createDocument(dentry.getName(),dstream); + dstream.close(); + } + } +} diff --git a/src/testcases/org/apache/poi/TestPOIDocument.java b/src/testcases/org/apache/poi/TestPOIDocument.java new file mode 100644 index 0000000..3b80aba --- /dev/null +++ b/src/testcases/org/apache/poi/TestPOIDocument.java @@ -0,0 +1,117 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + + +package org.apache.poi; + + +import junit.framework.TestCase; +import java.io.*; + +import org.apache.poi.hslf.HSLFSlideShow; +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.poifs.filesystem.*; + +/** + * Tests that POIDocument correctly loads and saves the common + * (hspf) Document Properties + * + * @author Nick Burch (nick at torchbox dot com) + */ +public class TestPOIDocument extends TestCase { + // The POI Documents to work on + private POIDocument doc; + private POIDocument doc2; + // POIFS primed on the test (powerpoint and word) data + private POIFSFileSystem pfs; + private POIFSFileSystem pfs2; + + /** + * Set things up, using a PowerPoint document and + * a Word Document for our testing + */ + public void setUp() throws Exception { + String dirnameHSLF = System.getProperty("HSLF.testdata.path"); + String filenameHSLF = dirnameHSLF + "/basic_test_ppt_file.ppt"; + String dirnameHWPF = System.getProperty("HWPF.testdata.path"); + String filenameHWPF = dirnameHWPF + "/test2.doc"; + + FileInputStream fisHSLF = new FileInputStream(filenameHSLF); + pfs = new POIFSFileSystem(fisHSLF); + doc = new HSLFSlideShow(pfs); + + FileInputStream fisHWPF = new FileInputStream(filenameHWPF); + pfs2 = new POIFSFileSystem(fisHWPF); + doc2 = new HWPFDocument(pfs2); + } + + public void testReadProperties() throws Exception { + // We should have both sets + assertNotNull(doc.getDocumentSummaryInformation()); + assertNotNull(doc.getSummaryInformation()); + + // Check they are as expected for the test doc + assertEquals("Hogwarts", doc.getSummaryInformation().getAuthor()); + assertEquals(10598, doc.getDocumentSummaryInformation().getByteCount()); + } + + public void testReadProperties2() throws Exception { + // Check again on the word one + assertNotNull(doc2.getDocumentSummaryInformation()); + assertNotNull(doc2.getSummaryInformation()); + + assertEquals("Hogwarts", doc2.getSummaryInformation().getAuthor()); + assertEquals("", doc2.getSummaryInformation().getKeywords()); + assertEquals(0, doc2.getDocumentSummaryInformation().getByteCount()); + } + + public void testWriteProperties() throws Exception { + // Just check we can write them back out into a filesystem + POIFSFileSystem outFS = new POIFSFileSystem(); + doc.writeProperties(outFS); + + // Should now hold them + assertNotNull( + outFS.createDocumentInputStream("\005SummaryInformation") + ); + assertNotNull( + outFS.createDocumentInputStream("\005DocumentSummaryInformation") + ); + } + + public void testWriteReadProperties() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Write them out + POIFSFileSystem outFS = new POIFSFileSystem(); + doc.writeProperties(outFS); + outFS.writeFilesystem(baos); + + // Create a new version + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + POIFSFileSystem inFS = new POIFSFileSystem(bais); + + // Check they're still there + doc.filesystem = inFS; + doc.readProperties(); + + // Delegate test + testReadProperties(); + } +} diff --git a/src/testcases/org/apache/poi/hslf/data/basic_test_ppt_file.ppt b/src/testcases/org/apache/poi/hslf/data/basic_test_ppt_file.ppt new file mode 100644 index 0000000000000000000000000000000000000000..af16393f44e0f315c212b60392e2bae2130987bc GIT binary patch literal 15360 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 literal 0 HcwPel00001 diff --git a/src/testcases/org/apache/poi/hwpf/data/test2.doc b/src/testcases/org/apache/poi/hwpf/data/test2.doc new file mode 100755 index 0000000000000000000000000000000000000000..06921df39545bd0544f062a88c4532632cbea80e GIT binary patch literal 19968 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 HZY=%_n7aT< literal 0 HcwPel00001 -- 2.11.4.GIT