diff --git a/src/java/org/apache/poi/hpsf/ClassID.java b/src/java/org/apache/poi/hpsf/ClassID.java index 6d5676703..e9939af46 100644 --- a/src/java/org/apache/poi/hpsf/ClassID.java +++ b/src/java/org/apache/poi/hpsf/ClassID.java @@ -56,6 +56,8 @@ package org.apache.poi.hpsf; import java.io.*; + +import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; /** @@ -98,15 +100,21 @@ public class ClassID */ public ClassID() { - bytes = new byte[LENGTH]; - for (int i = 0; i < LENGTH; i++) - bytes[i] = 0x00; + bytes = new byte[LENGTH]; + for (int i = 0; i < LENGTH; i++) + bytes[i] = 0x00; } - public final static int LENGTH = 16; + /**

The number of bytes occupied by this object in the byte + * stream.

*/ + public static final int LENGTH = 16; + /** + * @return The number of bytes occupied by this object in the byte + * stream. + */ public int length() { return LENGTH; @@ -117,10 +125,12 @@ public class ClassID /** *

Gets the bytes making out the class ID. They are returned in * correct order, i.e. big-endian.

+ * + * @return the bytes making out the class ID. */ public byte[] getBytes() { - return bytes; + return bytes; } @@ -153,9 +163,9 @@ public class ClassID bytes[6] = src[7 + offset]; bytes[7] = src[6 + offset]; - /* Read 8 bytes. */ - for (int i = 8; i < 16; i++) - bytes[i] = src[i + offset]; + /* Read 8 bytes. */ + for (int i = 8; i < 16; i++) + bytes[i] = src[i + offset]; return bytes; } @@ -170,30 +180,75 @@ public class ClassID * * @param offset The offset within the dst byte array. * - * @throws ArrayIndexOutOfBoundsException if there is not enough - * room for the class ID in the byte array. There must be at least - * 16 bytes in the byte array after the offset - * position. + * @exception ArrayStoreException if there is not enough room for the class + * ID 16 bytes in the byte array after the offset position. */ public void write(final byte[] dst, final int offset) + throws ArrayStoreException { + /* Check array size: */ + if (dst.length < 16) + throw new ArrayStoreException + ("Destination byte[] must have room for at least 16 bytes, " + + "but has a length of only " + dst.length + "."); /* Write double word. */ - dst[0 + offset] = bytes[3]; - dst[1 + offset] = bytes[2]; - dst[2 + offset] = bytes[1]; - dst[3 + offset] = bytes[0]; + dst[0 + offset] = bytes[3]; + dst[1 + offset] = bytes[2]; + dst[2 + offset] = bytes[1]; + dst[3 + offset] = bytes[0]; /* Write first word. */ - dst[4 + offset] = bytes[5]; - dst[5 + offset] = bytes[4]; + dst[4 + offset] = bytes[5]; + dst[5 + offset] = bytes[4]; /* Write second word. */ - dst[6 + offset] = bytes[7]; - dst[7 + offset] = bytes[6]; + dst[6 + offset] = bytes[7]; + dst[7 + offset] = bytes[6]; - /* Write 8 bytes. */ - for (int i = 8; i < 16; i++) - dst[i + offset] = bytes[i]; + /* Write 8 bytes. */ + for (int i = 8; i < 16; i++) + dst[i + offset] = bytes[i]; } + + + /** + *

Checks whether this ClassID is equal to another + * object.

+ * + * @param o the object to compare this PropertySet with + * @return true if the objects are equal, else + * false.

+ */ + public boolean equals(final Object o) + { + if (o == null || !(o instanceof ClassID)) + return false; + final ClassID cid = (ClassID) o; + if (bytes.length != cid.bytes.length) + return false; + for (int i = 0; i < bytes.length; i++) + if (bytes[i] != cid.bytes[i]) + return false; + 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(); + } } 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); } /**