/* ==================================================================== Copyright 2002-2004 Apache Software Foundation Licensed 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.hpsf.basic; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; import junit.framework.Assert; import junit.framework.TestCase; import org.apache.poi.hpsf.HPSFRuntimeException; import org.apache.poi.hpsf.IllegalPropertySetDataException; import org.apache.poi.hpsf.MutableProperty; import org.apache.poi.hpsf.MutablePropertySet; import org.apache.poi.hpsf.MutableSection; import org.apache.poi.hpsf.NoFormatIDException; import org.apache.poi.hpsf.Property; import org.apache.poi.hpsf.PropertySet; import org.apache.poi.hpsf.PropertySetFactory; import org.apache.poi.hpsf.Section; import org.apache.poi.hpsf.SummaryInformation; import org.apache.poi.hpsf.UnsupportedVariantTypeException; import org.apache.poi.hpsf.Variant; import org.apache.poi.hpsf.VariantSupport; import org.apache.poi.hpsf.WritingNotSupportedException; import org.apache.poi.hpsf.wellknown.PropertyIDMap; import org.apache.poi.hpsf.wellknown.SectionIDMap; import org.apache.poi.poifs.eventfilesystem.POIFSReader; import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent; import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.LittleEndian; /** *
Tests HPSF's writing functionality.
* * @author Rainer Klute (klute@rainer-klute.de) * @since 2003-02-07 * @version $Id$ */ public class TestWrite extends TestCase { static final String POI_FS = "TestHPSFWritingFunctionality.doc"; static final int BYTE_ORDER = 0xfffe; static final int FORMAT = 0x0000; static final int OS_VERSION = 0x00020A04; static final int[] SECTION_COUNT = {1, 2}; static final boolean[] IS_SUMMARY_INFORMATION = {true, false}; static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION = {false, true}; POIFile[] poiFiles; /** *Constructor
* * @param name the test case's name */ public TestWrite(final String name) { super(name); } /** * @see TestCase#setUp() */ public void setUp() { VariantSupport.setLogUnsupportedTypes(false); } /** *Writes an empty property set to a POIFS and reads it back * in.
* * @exception IOException if an I/O exception occurs * @exception UnsupportedVariantTypeException if HPSF does not yet support * a variant type to be written */ public void testNoFormatID() throws IOException, UnsupportedVariantTypeException { final File dataDir = new File(System.getProperty("HPSF.testdata.path")); final File filename = new File(dataDir, POI_FS); filename.deleteOnExit(); /* Create a mutable property set with a section that does not have the * formatID set: */ final OutputStream out = new FileOutputStream(filename); final POIFSFileSystem poiFs = new POIFSFileSystem(); final MutablePropertySet ps = new MutablePropertySet(); ps.clearSections(); ps.addSection(new MutableSection()); /* Write it to a POIFS and the latter to disk: */ try { final ByteArrayOutputStream psStream = new ByteArrayOutputStream(); ps.write(psStream); psStream.close(); final byte[] streamData = psStream.toByteArray(); poiFs.createDocument(new ByteArrayInputStream(streamData), SummaryInformation.DEFAULT_STREAM_NAME); poiFs.writeFilesystem(out); out.close(); Assert.fail("Should have thrown a NoFormatIDException."); } catch (Exception ex) { Assert.assertTrue(ex instanceof NoFormatIDException); } finally { out.close(); } } /** *Writes an empty property set to a POIFS and reads it back * in.
* * @exception IOException if an I/O exception occurs * @exception UnsupportedVariantTypeException if HPSF does not yet support * a variant type to be written */ public void testWriteEmptyPropertySet() throws IOException, UnsupportedVariantTypeException { final File dataDir = new File(System.getProperty("HPSF.testdata.path")); final File filename = new File(dataDir, POI_FS); filename.deleteOnExit(); /* Create a mutable property set and write it to a POIFS: */ final OutputStream out = new FileOutputStream(filename); final POIFSFileSystem poiFs = new POIFSFileSystem(); final MutablePropertySet ps = new MutablePropertySet(); final MutableSection s = (MutableSection) ps.getSections().get(0); s.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID); final ByteArrayOutputStream psStream = new ByteArrayOutputStream(); ps.write(psStream); psStream.close(); final byte[] streamData = psStream.toByteArray(); poiFs.createDocument(new ByteArrayInputStream(streamData), SummaryInformation.DEFAULT_STREAM_NAME); poiFs.writeFilesystem(out); out.close(); /* Read the POIFS: */ final POIFSReader r = new POIFSReader(); r.registerListener(new MyPOIFSReaderListener(), SummaryInformation.DEFAULT_STREAM_NAME); r.read(new FileInputStream(filename)); } /** *Writes a simple property set with a SummaryInformation section to a * POIFS and reads it back in.
* * @exception IOException if an I/O exception occurs * @exception UnsupportedVariantTypeException if HPSF does not yet support * a variant type to be written */ public void testWriteSimplePropertySet() throws IOException, UnsupportedVariantTypeException { final String AUTHOR = "Rainer Klute"; final String TITLE = "Test Document"; final File dataDir = new File(System.getProperty("HPSF.testdata.path")); final File filename = new File(dataDir, POI_FS); filename.deleteOnExit(); final OutputStream out = new FileOutputStream(filename); final POIFSFileSystem poiFs = new POIFSFileSystem(); final MutablePropertySet ps = new MutablePropertySet(); final MutableSection si = new MutableSection(); si.setFormatID(SectionIDMap.SUMMARY_INFORMATION_ID); ps.getSections().set(0, si); final MutableProperty p = new MutableProperty(); p.setID(PropertyIDMap.PID_AUTHOR); p.setType(Variant.VT_LPWSTR); p.setValue(AUTHOR); si.setProperty(p); si.setProperty(PropertyIDMap.PID_TITLE, Variant.VT_LPSTR, TITLE); poiFs.createDocument(ps.toInputStream(), SummaryInformation.DEFAULT_STREAM_NAME); poiFs.writeFilesystem(out); out.close(); /* Read the POIFS: */ final PropertySet[] psa = new PropertySet[1]; final POIFSReader r = new POIFSReader(); r.registerListener(new POIFSReaderListener() { public void processPOIFSReaderEvent (final POIFSReaderEvent event) { try { psa[0] = PropertySetFactory.create(event.getStream()); } catch (Exception ex) { fail(org.apache.poi.hpsf.Util.toString(ex)); } } }, SummaryInformation.DEFAULT_STREAM_NAME); r.read(new FileInputStream(filename)); Assert.assertNotNull(psa[0]); final Section s = (Section) (psa[0].getSections().get(0)); Object p1 = s.getProperty(PropertyIDMap.PID_AUTHOR); Object p2 = s.getProperty(PropertyIDMap.PID_TITLE); Assert.assertEquals(AUTHOR, p1); Assert.assertEquals(TITLE, p2); } /** *Writes a simple property set with two sections to a POIFS and reads it * back in.
* * @exception IOException if an I/O exception occurs * @exception WritingNotSupportedException if HPSF does not yet support * a variant type to be written */ public void testWriteTwoSections() throws WritingNotSupportedException, IOException { final String STREAM_NAME = "PropertySetStream"; final String SECTION1 = "Section 1"; final String SECTION2 = "Section 2"; final File dataDir = new File(System.getProperty("HPSF.testdata.path")); final File filename = new File(dataDir, POI_FS); filename.deleteOnExit(); final OutputStream out = new FileOutputStream(filename); final POIFSFileSystem poiFs = new POIFSFileSystem(); final MutablePropertySet ps = new MutablePropertySet(); ps.clearSections(); final byte[] formatID = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; final MutableSection s1 = new MutableSection(); s1.setFormatID(formatID); s1.setProperty(2, SECTION1); ps.addSection(s1); final MutableSection s2 = new MutableSection(); s2.setFormatID(formatID); s2.setProperty(2, SECTION2); ps.addSection(s2); poiFs.createDocument(ps.toInputStream(), STREAM_NAME); poiFs.writeFilesystem(out); out.close(); /* Read the POIFS: */ final PropertySet[] psa = new PropertySet[1]; final POIFSReader r = new POIFSReader(); r.registerListener(new POIFSReaderListener() { public void processPOIFSReaderEvent (final POIFSReaderEvent event) { try { psa[0] = PropertySetFactory.create(event.getStream()); } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex.toString()); /* FIXME (2): Replace the previous line by the following * one once we no longer need JDK 1.3 compatibility. */ // throw new RuntimeException(ex); } } }, STREAM_NAME); r.read(new FileInputStream(filename)); Assert.assertNotNull(psa[0]); Section s = (Section) (psa[0].getSections().get(0)); Object p = s.getProperty(2); Assert.assertEquals(SECTION1, p); s = (Section) (psa[0].getSections().get(1)); p = s.getProperty(2); Assert.assertEquals(SECTION2, p); } static class MyPOIFSReaderListener implements POIFSReaderListener { public void processPOIFSReaderEvent(final POIFSReaderEvent event) { try { PropertySetFactory.create(event.getStream()); } catch (Exception ex) { fail(org.apache.poi.hpsf.Util.toString(ex)); } } } private static final int CODEPAGE_DEFAULT = -1; private static final int CODEPAGE_UTF8 = 65001; private static final int CODEPAGE_UTF16 = 1200; private static final int CODEPAGE_1252 = 1252; /** *Writes and reads back various variant types and checks whether the * stuff that has been read back equals the stuff that was written.
*/ public void testVariantTypes() { Throwable t = null; final int codepage = CODEPAGE_DEFAULT; try { check(Variant.VT_EMPTY, null, codepage); check(Variant.VT_BOOL, new Boolean(true), codepage); check(Variant.VT_BOOL, new Boolean(false), codepage); check(Variant.VT_CF, new byte[]{0}, codepage); check(Variant.VT_CF, new byte[]{0, 1}, codepage); check(Variant.VT_CF, new byte[]{0, 1, 2}, codepage); check(Variant.VT_CF, new byte[]{0, 1, 2, 3}, codepage); check(Variant.VT_CF, new byte[]{0, 1, 2, 3, 4}, codepage); check(Variant.VT_CF, new byte[]{0, 1, 2, 3, 4, 5}, codepage); check(Variant.VT_CF, new byte[]{0, 1, 2, 3, 4, 5, 6}, codepage); check(Variant.VT_CF, new byte[]{0, 1, 2, 3, 4, 5, 6, 7}, codepage); check(Variant.VT_I2, new Integer(27), codepage); check(Variant.VT_I4, new Long(28), codepage); check(Variant.VT_FILETIME, new Date(), codepage); check(Variant.VT_LPSTR, "", codepage); check(Variant.VT_LPSTR, "\u00e4", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", codepage); check(Variant.VT_LPWSTR, "", codepage); check(Variant.VT_LPWSTR, "\u00e4", codepage); check(Variant.VT_LPWSTR, "\u00e4\u00f6", codepage); check(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc", codepage); check(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc\u00c4", codepage); check(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6", codepage); check(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc", codepage); check(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", codepage); } catch (Exception ex) { t = ex; } catch (Error ex) { t = ex; } if (t != null) fail(org.apache.poi.hpsf.Util.toString(t)); } /** *Writes and reads back strings using several different codepages and * checks whether the stuff that has been read back equals the stuff that * was written.
*/ public void testCodepages() { Throwable t = null; final int[] validCodepages = new int[] {CODEPAGE_DEFAULT, CODEPAGE_UTF8, CODEPAGE_UTF16, CODEPAGE_1252}; for (int i = 0; i < validCodepages.length; i++) { int codepage = validCodepages[i]; try { check(Variant.VT_LPSTR, "", codepage); check(Variant.VT_LPSTR, "\u00e4", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", codepage); if (codepage == 1200 || codepage == 65001) check(Variant.VT_LPSTR, "\u79D1\u5B78", codepage); } catch (Exception ex) { t = ex; } catch (Error ex) { t = ex; } if (t != null) fail(org.apache.poi.hpsf.Util.toString(t) + " with codepage " + codepage); } final int[] invalidCodepages = new int[] {0, 1, 2, 4711, 815}; for (int i = 0; i < invalidCodepages.length; i++) { int codepage = invalidCodepages[i]; try { check(Variant.VT_LPSTR, "", codepage); check(Variant.VT_LPSTR, "\u00e4", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc", codepage); check(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", codepage); fail("UnsupportedEncodingException for codepage " + codepage + " expected."); } catch (UnsupportedEncodingException ex) { /* This is the expected behaviour. */ } catch (Exception ex) { t = ex; } catch (Error ex) { t = ex; } if (t != null) fail(org.apache.poi.hpsf.Util.toString(t)); } } /** *Writes a property and reads it back in.
* * @param variantType The property's variant type. * @param value The property's value. * @throws UnsupportedVariantTypeException if the variant is not supported. * @throws IOException if an I/O exception occurs. */ private void check(final long variantType, final Object value, final int codepage) throws UnsupportedVariantTypeException, IOException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); VariantSupport.write(out, variantType, value, codepage); out.close(); final byte[] b = out.toByteArray(); final Object objRead = VariantSupport.read(b, 0, b.length + LittleEndian.INT_SIZE, variantType, codepage); if (objRead instanceof byte[]) { final int diff = diff((byte[]) value, (byte[]) objRead); if (diff >= 0) fail("Byte arrays are different. First different byte is at " + "index " + diff + "."); } else if (value != null && !value.equals(objRead)) fail("Expected: \"" + value + "\" but was: \"" + objRead + "\". Codepage: " + codepage + "."); else assertEquals(value, objRead); } /** *Compares two byte arrays.
* * @param a The first byte array * @param b The second byte array * @return The index of the first byte that is different. If the byte arrays * are equal, -1 is returned. */ private int diff(final byte[] a, final byte[] b) { final int min = Math.min(a.length, b.length); for (int i = 0; i < min; i++) if (a[i] != b[i]) return i; if (a.length != b.length) return min; return -1; } /** *This test method does a write and read back test with all POI * filesystems in the "data" directory by performing the following * actions for each file:
* *Read its property set streams.
Create a new POI filesystem containing the origin file's * property set streams.
Read the property set streams from the POI filesystem just * created.
Compare each property set stream with the corresponding one from * the origin file and check whether they are equal.
Performs the check described in {@link #testRecreate()} for a single * POI filesystem.
* * @param f the POI filesystem to check */ private void testRecreate(final File f) { try { /* Read the POI filesystem's property set streams: */ final POIFile[] psf1 = Util.readPropertySets(f); /* Create a new POI filesystem containing the origin file's * property set streams: */ final File copy = File.createTempFile(f.getName(), ""); copy.deleteOnExit(); final OutputStream out = new FileOutputStream(copy); final POIFSFileSystem poiFs = new POIFSFileSystem(); for (int i = 0; i < psf1.length; i++) { final InputStream in = new ByteArrayInputStream(psf1[i].getBytes()); final PropertySet psIn = PropertySetFactory.create(in); final MutablePropertySet psOut = new MutablePropertySet(psIn); final ByteArrayOutputStream psStream = new ByteArrayOutputStream(); psOut.write(psStream); psStream.close(); final byte[] streamData = psStream.toByteArray(); poiFs.createDocument(new ByteArrayInputStream(streamData), psf1[i].getName()); poiFs.writeFilesystem(out); } out.close(); /* Read the property set streams from the POI filesystem just * created. */ final POIFile[] psf2 = Util.readPropertySets(copy); for (int i = 0; i < psf2.length; i++) { final byte[] bytes1 = psf1[i].getBytes(); final byte[] bytes2 = psf2[i].getBytes(); final InputStream in1 = new ByteArrayInputStream(bytes1); final InputStream in2 = new ByteArrayInputStream(bytes2); final PropertySet ps1 = PropertySetFactory.create(in1); final PropertySet ps2 = PropertySetFactory.create(in2); /* Compare the property set stream with the corresponding one * from the origin file and check whether they are equal. */ assertEquals("Equality for file "+f.getName(),ps1, ps2); } } catch (Exception ex) { handle(ex); } } /** *Tests writing and reading back a proper dictionary.
*/ public void testDictionary() { try { final File copy = File.createTempFile("Test-HPSF", "ole2"); copy.deleteOnExit(); /* Write: */ final OutputStream out = new FileOutputStream(copy); final POIFSFileSystem poiFs = new POIFSFileSystem(); final MutablePropertySet ps1 = new MutablePropertySet(); final MutableSection s = (MutableSection) ps1.getSections().get(0); final Map m = new HashMap(3, 1.0f); m.put(new Long(1), "String 1"); m.put(new Long(2), "String 2"); m.put(new Long(3), "String 3"); s.setDictionary(m); s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID); int codepage = Property.CP_UNICODE; s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, new Integer(codepage)); poiFs.createDocument(ps1.toInputStream(), "Test"); poiFs.writeFilesystem(out); out.close(); /* Read back: */ final POIFile[] psf = Util.readPropertySets(copy); Assert.assertEquals(1, psf.length); final byte[] bytes = psf[0].getBytes(); final InputStream in = new ByteArrayInputStream(bytes); final PropertySet ps2 = PropertySetFactory.create(in); /* Compare the property set stream with the corresponding one * from the origin file and check whether they are equal. */ assertEquals(ps1, ps2); } catch (Exception ex) { handle(ex); } } /** *Tests writing and reading back a proper dictionary with an invalid * codepage. (HPSF writes Unicode dictionaries only.)
*/ public void testDictionaryWithInvalidCodepage() { try { final File copy = File.createTempFile("Test-HPSF", "ole2"); copy.deleteOnExit(); /* Write: */ final OutputStream out = new FileOutputStream(copy); final POIFSFileSystem poiFs = new POIFSFileSystem(); final MutablePropertySet ps1 = new MutablePropertySet(); final MutableSection s = (MutableSection) ps1.getSections().get(0); final Map m = new HashMap(3, 1.0f); m.put(new Long(1), "String 1"); m.put(new Long(2), "String 2"); m.put(new Long(3), "String 3"); s.setDictionary(m); s.setFormatID(SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID); int codepage = 12345; s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, new Integer(codepage)); poiFs.createDocument(ps1.toInputStream(), "Test"); poiFs.writeFilesystem(out); out.close(); fail("This testcase did not detect the invalid codepage value."); } catch (IllegalPropertySetDataException ex) { assertTrue(true); } catch (Exception ex) { handle(ex); } } /** *Handles unexpected exceptions in testcases.
* * @param ex The exception that has been thrown. */ private void handle(final Exception ex) { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); Throwable t = ex; while (t != null) { t.printStackTrace(pw); if (t instanceof HPSFRuntimeException) t = ((HPSFRuntimeException) t).getReason(); else t = null; if (t != null) pw.println("Caused by:"); } pw.close(); try { sw.close(); } catch (IOException ex2) { ex.printStackTrace(); } fail(sw.toString()); } /** *Runs the test cases stand-alone.
*/ public static void main(final String[] args) throws Throwable { System.setProperty("HPSF.testdata.path", "./src/testcases/org/apache/poi/hpsf/data"); junit.textui.TestRunner.run(TestWrite.class); } }