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.
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
+ * 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); } /**