diff --git a/src/java/org/apache/poi/hpsf/ClassID.java b/src/java/org/apache/poi/hpsf/ClassID.java index ddd52c686..549cad05b 100644 --- a/src/java/org/apache/poi/hpsf/ClassID.java +++ b/src/java/org/apache/poi/hpsf/ClassID.java @@ -55,6 +55,10 @@ */ package org.apache.poi.hpsf; +import java.io.*; +import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndian; + /** *

Represents a class ID (16 bytes). Unlike other little-endian * type the {@link ClassID} is not just 16 bytes stored in the wrong @@ -229,7 +233,25 @@ public class ClassID return true; } - + /** + * Returns a human readable representation of the Class ID + * in standard format "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" + * @return String representation of the Class ID represented + * by this object. + */ + public String toString() + { + StringBuffer sbClassId = new StringBuffer( 38); + sbClassId.append( '{'); + for( int i=0; i < 16; i++) { + sbClassId.append( HexDump.toHex( bytes[ i])); + if( i == 3 || i == 5 || i == 7 || i == 9) { + sbClassId.append( '-'); + } + } + sbClassId.append( '}'); + return sbClassId.toString(); + } /** * @see Object#hashCode() diff --git a/src/java/org/apache/poi/poifs/property/Property.java b/src/java/org/apache/poi/poifs/property/Property.java index 6811a7250..c09c25619 100644 --- a/src/java/org/apache/poi/poifs/property/Property.java +++ b/src/java/org/apache/poi/poifs/property/Property.java @@ -59,6 +59,8 @@ import java.io.*; import java.util.*; +import org.apache.poi.hpsf.ClassID; + import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.poifs.dev.POIFSViewable; import org.apache.poi.util.ByteField; @@ -87,6 +89,8 @@ public abstract class Property static final private int _previous_property_offset = 0x44; static final private int _next_property_offset = 0x48; static final private int _child_property_offset = 0x4C; + static final private int _storage_clsid_offset = 0x50; + static final private int _user_flags_offset = 0x60; static final private int _seconds_1_offset = 0x64; static final private int _days_1_offset = 0x68; static final private int _seconds_2_offset = 0x6C; @@ -107,6 +111,8 @@ public abstract class Property private IntegerField _previous_property; private IntegerField _next_property; private IntegerField _child_property; + private ClassID _storage_clsid; + private IntegerField _user_flags; private IntegerField _seconds_1; private IntegerField _days_1; private IntegerField _seconds_2; @@ -136,6 +142,8 @@ public abstract class Property _NO_INDEX, _raw_data); _child_property = new IntegerField(_child_property_offset, _NO_INDEX, _raw_data); + _storage_clsid = new ClassID(_raw_data,_storage_clsid_offset); + _user_flags = new IntegerField(_user_flags_offset, 0, _raw_data); _seconds_1 = new IntegerField(_seconds_1_offset, 0, _raw_data); _days_1 = new IntegerField(_days_1_offset, 0, _raw_data); @@ -173,6 +181,8 @@ public abstract class Property _raw_data); _child_property = new IntegerField(_child_property_offset, _raw_data); + _storage_clsid = new ClassID(_raw_data,_storage_clsid_offset); + _user_flags = new IntegerField(_user_flags_offset, 0, _raw_data); _seconds_1 = new IntegerField(_seconds_1_offset, _raw_data); _days_1 = new IntegerField(_days_1_offset, _raw_data); _seconds_2 = new IntegerField(_seconds_2_offset, _raw_data); @@ -295,12 +305,21 @@ public abstract class Property abstract public boolean isDirectory(); + /** + * Sets the storage clsid, which is the Class ID of a COM object which + * reads and writes this stream + * @return storage Class ID for this property stream + */ + public ClassID getStorageClsid() + { + return _storage_clsid; + } + /** * Set the name; silently truncates the name if it's too long. * * @param name the new name */ - protected final void setName(final String name) { char[] char_array = name.toCharArray(); @@ -327,6 +346,20 @@ public abstract class Property * LittleEndianConsts.SHORT_SIZE), _raw_data); } + /** + * Sets the storage class ID for this property stream. This is the Class ID + * of the COM object which can read and write this property stream + * @param clsidStorage Storage Class ID + */ + public void setStorageClsid( ClassID clsidStorage) + { + _storage_clsid = clsidStorage; + if( clsidStorage == null) { + Arrays.fill( _raw_data, _storage_clsid_offset, _storage_clsid_offset + ClassID.LENGTH, (byte) 0); + } else { + clsidStorage.write( _raw_data, _storage_clsid_offset); + } + } /** * Set the property type. Makes no attempt to validate the value. * diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java b/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java new file mode 100644 index 000000000..c36f8161e --- /dev/null +++ b/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java @@ -0,0 +1,171 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" and + * "Apache POI" must not be used to endorse or promote products + * derived from this software without prior written permission. For + * written permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * "Apache POI", nor may "Apache" appear in their name, without + * prior written permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.poi.hpsf.basic; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.poi.hpsf.ClassID; + +/** + *

Tests ClassID structure.

+ * + * @author Michael Zalewski (zalewski@optonline.net) + */ +public class TestClassID extends TestCase +{ + /** + *

Constructor

+ * + * @param name the test case's name + */ + public TestClassID(final String name) + { + super(name); + } + + /** + * Various tests of overridden .equals() + */ + public void testEquals() + { + ClassID clsidTest1 = new ClassID( + new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 } + , 0 + ); + ClassID clsidTest2 = new ClassID( + new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 } + , 0 + ); + ClassID clsidTest3 = new ClassID( + new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11 } + , 0 + ); + Assert.assertEquals( clsidTest1, clsidTest1); + Assert.assertEquals( clsidTest1, clsidTest2); + Assert.assertFalse( clsidTest1.equals( clsidTest3)); + Assert.assertFalse( clsidTest1.equals( null)); + } + /** + * Try to write to a buffer that is too small. This should + * throw an Exception + */ + public void testWriteArrayStoreException() + { + ClassID clsidTest = new ClassID( + new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 } + , 0 + ); + boolean bExceptionOccurred = false; + try { + clsidTest.write( new byte[ 15], 0); + } catch( Exception e) { + bExceptionOccurred = true; + } + Assert.assertTrue( bExceptionOccurred); + + bExceptionOccurred = false; + try { + clsidTest.write( new byte[ 16], 1); + } catch( Exception e) { + bExceptionOccurred = true; + } + Assert.assertTrue( bExceptionOccurred); + + // These should work without throwing an Exception + bExceptionOccurred = false; + try { + clsidTest.write( new byte[ 16], 0); + clsidTest.write( new byte[ 17], 1); + } catch( Exception e) { + bExceptionOccurred = true; + } + Assert.assertFalse( bExceptionOccurred); + } + /** + *

Tests the {@link PropertySet} methods. The test file has two + * property set: the first one is a {@link SummaryInformation}, + * the second one is a {@link DocumentSummaryInformation}.

+ */ + public void testClassID() + { + ClassID clsidTest = new ClassID( + new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 } + , 0 + ); + Assert.assertEquals( + clsidTest.toString().toUpperCase() + , "{04030201-0605-0807-090A-0B0C0D0E0F10}" + ); + } + + + + /** + *

Runs the test cases stand-alone.

+ */ + public static void main(final String[] args) + { + System.setProperty("HPSF.testdata.path", + "./src/testcases/org/apache/poi/hpsf/data"); + junit.textui.TestRunner.run(TestClassID.class); + } + +} diff --git a/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java b/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java index 74a380756..e677a7983 100644 --- a/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java +++ b/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java @@ -150,6 +150,7 @@ public class TestDocumentProperty ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x57, ( byte ) 0x00, ( byte ) 0x6F, ( byte ) 0x00, ( byte ) 0x72, ( byte ) 0x00, ( byte ) 0x6B, ( byte ) 0x00, ( byte ) 0x62, ( byte ) 0x00, ( byte ) 0x6F, ( byte ) 0x00, @@ -182,6 +183,7 @@ public class TestDocumentProperty ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x05, ( byte ) 0x00, ( byte ) 0x53, ( byte ) 0x00, ( byte ) 0x75, ( byte ) 0x00, ( byte ) 0x6D, ( byte ) 0x00, ( byte ) 0x6D, ( byte ) 0x00, ( byte ) 0x61, ( byte ) 0x00, @@ -214,6 +216,7 @@ public class TestDocumentProperty ( byte ) 0x08, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, + ( byte ) 0x05, ( byte ) 0x00, ( byte ) 0x44, ( byte ) 0x00, ( byte ) 0x6F, ( byte ) 0x00, ( byte ) 0x63, ( byte ) 0x00, ( byte ) 0x75, ( byte ) 0x00, ( byte ) 0x6D, ( byte ) 0x00, diff --git a/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java b/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java index 430c52147..8357a0a46 100644 --- a/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java +++ b/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java @@ -217,11 +217,11 @@ public class TestRootProperty ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00 }; - verifyReadingProperty(0, input, 0, "Root Entry"); + verifyReadingProperty(0, input, 0, "Root Entry", "{00020820-0000-0000-C000-000000000046}"); } private void verifyReadingProperty(int index, byte [] input, int offset, - String name) + String name, String sClsId) throws IOException { RootProperty property = new RootProperty(index, input, @@ -242,6 +242,7 @@ public class TestRootProperty assertEquals(index, property.getIndex()); assertEquals(name, property.getName()); assertTrue(!property.getChildren().hasNext()); + assertEquals(property.getStorageClsid().toString(), sClsId); } /**