Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java
 Sat Dec 10 23:35:12 2016
@@ -24,66 +24,68 @@ import java.io.OutputStream;
 import org.apache.poi.util.LittleEndian;
 
 /**
- * A {@link PropertiesChunk} for a Message or Embedded-Message.
- * This has a 32 byte header
+ * A {@link PropertiesChunk} for a Message or Embedded-Message. This has a 32
+ * byte header
  */
 public class MessagePropertiesChunk extends PropertiesChunk {
-   private long nextRecipientId;
-   private long nextAttachmentId;
-   private long recipientCount;
-   private long attachmentCount;
-
-   public MessagePropertiesChunk(ChunkGroup parentGroup) {
-      super(parentGroup);
-   }
-   
-   public long getNextRecipientId() {
-      return nextRecipientId;
-   }
-   public long getNextAttachmentId() {
-      return nextAttachmentId;
-   }
-
-   public long getRecipientCount() {
-      return recipientCount;
-   }
-   public long getAttachmentCount() {
-      return attachmentCount;
-   }
-
-   @Override
-   public void readValue(InputStream stream) throws IOException {
-      // 8 bytes of reserved zeros
-      LittleEndian.readLong(stream);
-      
-      // Nexts and counts
-      nextRecipientId = LittleEndian.readUInt(stream);
-      nextAttachmentId = LittleEndian.readUInt(stream);
-      recipientCount = LittleEndian.readUInt(stream);
-      attachmentCount = LittleEndian.readUInt(stream);
-      
-      // 8 bytes of reserved zeros
-      LittleEndian.readLong(stream);
-      
-      // Now properties
-      readProperties(stream);
-   }
-
-   @Override
-   public void writeValue(OutputStream out) throws IOException {
-      // 8 bytes of reserved zeros
-      out.write(new byte[8]);
-      
-      // Nexts and counts
-      LittleEndian.putUInt(nextRecipientId, out);
-      LittleEndian.putUInt(nextAttachmentId, out);
-      LittleEndian.putUInt(recipientCount, out);
-      LittleEndian.putUInt(attachmentCount, out);
-      
-      // 8 bytes of reserved zeros
-      out.write(new byte[8]);
-      
-      // Now properties
-      writeProperties(out);
-   }
+    private long nextRecipientId;
+    private long nextAttachmentId;
+    private long recipientCount;
+    private long attachmentCount;
+
+    public MessagePropertiesChunk(ChunkGroup parentGroup) {
+        super(parentGroup);
+    }
+
+    public long getNextRecipientId() {
+        return nextRecipientId;
+    }
+
+    public long getNextAttachmentId() {
+        return nextAttachmentId;
+    }
+
+    public long getRecipientCount() {
+        return recipientCount;
+    }
+
+    public long getAttachmentCount() {
+        return attachmentCount;
+    }
+
+    @Override
+    public void readValue(InputStream stream) throws IOException {
+        // 8 bytes of reserved zeros
+        LittleEndian.readLong(stream);
+
+        // Nexts and counts
+        nextRecipientId = LittleEndian.readUInt(stream);
+        nextAttachmentId = LittleEndian.readUInt(stream);
+        recipientCount = LittleEndian.readUInt(stream);
+        attachmentCount = LittleEndian.readUInt(stream);
+
+        // 8 bytes of reserved zeros
+        LittleEndian.readLong(stream);
+
+        // Now properties
+        readProperties(stream);
+    }
+
+    @Override
+    public void writeValue(OutputStream out) throws IOException {
+        // 8 bytes of reserved zeros
+        out.write(new byte[8]);
+
+        // Nexts and counts
+        LittleEndian.putUInt(nextRecipientId, out);
+        LittleEndian.putUInt(nextAttachmentId, out);
+        LittleEndian.putUInt(recipientCount, out);
+        LittleEndian.putUInt(attachmentCount, out);
+
+        // 8 bytes of reserved zeros
+        out.write(new byte[8]);
+
+        // Now properties
+        writeProperties(out);
+    }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java
 Sat Dec 10 23:35:12 2016
@@ -31,100 +31,106 @@ import org.apache.poi.util.POILogFactory
 import org.apache.poi.util.POILogger;
 
 /**
- * A Chunk that holds the details given back by the
- *  server at submission time.
- * This includes the date the message was given to the
- *  server, and an ID that's used if you want to cancel
- *  a message or similar
+ * A Chunk that holds the details given back by the server at submission time.
+ * This includes the date the message was given to the server, and an ID that's
+ * used if you want to cancel a message or similar
  */
 public class MessageSubmissionChunk extends Chunk {
-   private static POILogger logger = 
POILogFactory.getLogger(MessageSubmissionChunk.class);
-   private String rawId;
-   private Calendar date;
-
-   private static final Pattern datePatern = 
-            
Pattern.compile("(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)Z?"); 
-
-   /**
-    * Creates a Byte Chunk.
-    */
-   public MessageSubmissionChunk(String namePrefix, int chunkId, MAPIType 
type) {
-      super(namePrefix, chunkId, type);
-   }
-
-   /**
-    * Create a Byte Chunk, with the specified
-    *  type.
-    */
-   public MessageSubmissionChunk(int chunkId, MAPIType type) {
-      super(chunkId, type);
-   }
-
-   public void readValue(InputStream value) throws IOException {
-      // Stored in the file as us-ascii
-     byte[] data = IOUtils.toByteArray(value); 
-     rawId = new String(data, Charset.forName("ASCII"));
-      
-      // Now process the date
-      String[] parts = rawId.split(";");
-      for(String part : parts) {
-         if(part.startsWith("l=")) {
-            // Format of this bit appears to be l=<id>-<time>-<number>
-            // ID may contain hyphens.
-
-            String dateS = null;
-            final int numberPartBegin = part.lastIndexOf('-');
-            if (numberPartBegin != -1) {
-                final int datePartBegin = part.lastIndexOf('-', 
numberPartBegin-1);
-                if (datePartBegin != -1 && 
-                        // cannot extract date if only one hyphen is in the 
string...
-                        numberPartBegin > datePartBegin) {
-                    dateS = part.substring(datePartBegin + 1, numberPartBegin);
+    private static POILogger logger = POILogFactory
+            .getLogger(MessageSubmissionChunk.class);
+    private String rawId;
+    private Calendar date;
+
+    private static final Pattern datePatern = Pattern
+        .compile("(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)Z?");
+
+    /**
+     * Creates a Byte Chunk.
+     */
+    public MessageSubmissionChunk(String namePrefix, int chunkId,
+            MAPIType type) {
+        super(namePrefix, chunkId, type);
+    }
+
+    /**
+     * Create a Byte Chunk, with the specified type.
+     */
+    public MessageSubmissionChunk(int chunkId, MAPIType type) {
+        super(chunkId, type);
+    }
+
+    public void readValue(InputStream value) throws IOException {
+        // Stored in the file as us-ascii
+        byte[] data = IOUtils.toByteArray(value);
+        rawId = new String(data, Charset.forName("ASCII"));
+
+        // Now process the date
+        String[] parts = rawId.split(";");
+        for (String part : parts) {
+            if (part.startsWith("l=")) {
+                // Format of this bit appears to be l=<id>-<time>-<number>
+                // ID may contain hyphens.
+
+                String dateS = null;
+                final int numberPartBegin = part.lastIndexOf('-');
+                if (numberPartBegin != -1) {
+                    final int datePartBegin = part.lastIndexOf('-',
+                            numberPartBegin - 1);
+                    if (datePartBegin != -1 &&
+                    // cannot extract date if only one hyphen is in the
+                    // string...
+                            numberPartBegin > datePartBegin) {
+                        dateS = part.substring(datePartBegin + 1,
+                                numberPartBegin);
+                    }
+                }
+                if (dateS != null) {
+                    // Should be yymmddhhmmssZ
+                    Matcher m = datePatern.matcher(dateS);
+                    if (m.matches()) {
+                        date = LocaleUtil.getLocaleCalendar();
+
+                        // work around issues with dates like 1989, which 
appear as "89" here
+                        int year = Integer.parseInt(m.group(1));
+                        date.set(Calendar.YEAR, year + (year > 80 ? 1900 : 
2000));
+
+                        // Java is 0 based
+                        date.set(Calendar.MONTH, Integer.parseInt(m.group(2)) 
- 1);
+                        date.set(Calendar.DATE, Integer.parseInt(m.group(3)));
+                        date.set(Calendar.HOUR_OF_DAY,
+                                Integer.parseInt(m.group(4)));
+                        date.set(Calendar.MINUTE, 
Integer.parseInt(m.group(5)));
+                        date.set(Calendar.SECOND, 
Integer.parseInt(m.group(6)));
+                        date.clear(Calendar.MILLISECOND);
+                    } else {
+                        logger.log(POILogger.WARN,
+                                "Warning - unable to make sense of date "
+                                        + dateS);
+                    }
                 }
             }
-            if (dateS != null) {
-               // Should be yymmddhhmmssZ
-               Matcher m = datePatern.matcher(dateS);
-               if(m.matches()) {
-                  date = LocaleUtil.getLocaleCalendar();
-
-                  // work around issues with dates like 1989, which appear as 
"89" here
-                  int year = Integer.parseInt(m.group(1));
-                  date.set(Calendar.YEAR,  year + (year > 80 ? 1900 : 2000));
-
-                  date.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1); 
// Java is 0 based
-                  date.set(Calendar.DATE,  Integer.parseInt(m.group(3)));
-                  date.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4)));
-                  date.set(Calendar.MINUTE,      Integer.parseInt(m.group(5)));
-                  date.set(Calendar.SECOND,      Integer.parseInt(m.group(6)));
-                  date.clear(Calendar.MILLISECOND);
-               } else {
-                  logger.log(POILogger.WARN, "Warning - unable to make sense 
of date " + dateS);
-               }
-            }
-         }
-      }
-   }
-
-   public void writeValue(OutputStream out) throws IOException {
-     byte[] data = rawId.getBytes(Charset.forName("ASCII")); 
-     out.write(data);
-   }
-   
-   /**
-    * @return the date that the server accepted the
-    *  message, as found from the message ID it generated.
-    *
-    */
-   public Calendar getAcceptedAtTime() {
-      return date;
-   }
-   
-   /**
-    * @return the full ID that the server generated when
-    *  it accepted the message.
-    */
-   public String getSubmissionId() {
-      return rawId;
-   }
+        }
+    }
+
+    public void writeValue(OutputStream out) throws IOException {
+        byte[] data = rawId.getBytes(Charset.forName("ASCII"));
+        out.write(data);
+    }
+
+    /**
+     * @return the date that the server accepted the message, as found from the
+     *         message ID it generated.
+     *
+     */
+    public Calendar getAcceptedAtTime() {
+        return date;
+    }
+
+    /**
+     * @return the full ID that the server generated when it accepted the
+     *         message.
+     */
+    public String getSubmissionId() {
+        return rawId;
+    }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java 
(original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java 
Sat Dec 10 23:35:12 2016
@@ -20,37 +20,35 @@ package org.apache.poi.hsmf.datatypes;
 import java.util.ArrayList;
 import java.util.List;
 
-
 /**
- * Collection of convenience chunks for the
- *  NameID part of an outlook file
+ * Collection of convenience chunks for the NameID part of an outlook file
  */
 public final class NameIdChunks implements ChunkGroup {
-   public static final String NAME = "__nameid_version1.0";
-   
-   /** Holds all the chunks that were found. */
-   private List<Chunk> allChunks = new ArrayList<Chunk>();
-   
-   public Chunk[] getAll() {
-      return allChunks.toArray(new Chunk[allChunks.size()]);
-   }
-   public Chunk[] getChunks() {
-      return getAll();
-   }
-       
-   /**
-    * Called by the parser whenever a chunk is found.
-    */
-   public void record(Chunk chunk) {
-      allChunks.add(chunk);
-   }
-   
-   /**
-    * Used to flag that all the chunks of the NameID
-    *  have now been located.
-    */
-   public void chunksComplete() {
-      // Currently, we don't need to do anything special once
-      //  all the chunks have been located
-   }
+    public static final String NAME = "__nameid_version1.0";
+
+    /** Holds all the chunks that were found. */
+    private List<Chunk> allChunks = new ArrayList<Chunk>();
+
+    public Chunk[] getAll() {
+        return allChunks.toArray(new Chunk[allChunks.size()]);
+    }
+
+    public Chunk[] getChunks() {
+        return getAll();
+    }
+
+    /**
+     * Called by the parser whenever a chunk is found.
+     */
+    public void record(Chunk chunk) {
+        allChunks.add(chunk);
+    }
+
+    /**
+     * Used to flag that all the chunks of the NameID have now been located.
+     */
+    public void chunksComplete() {
+        // Currently, we don't need to do anything special once
+        // all the chunks have been located
+    }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java 
(original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java 
Sat Dec 10 23:35:12 2016
@@ -42,240 +42,238 @@ import org.apache.poi.util.POILogFactory
 import org.apache.poi.util.POILogger;
 
 /**
- * <p>A Chunk which holds (single) fixed-length properties, and pointer
- *  to the variable length ones / multi-valued ones (which get their 
- *  own chunk).
- * <p>There are two kinds of PropertiesChunks, which differ only in 
- *  their headers.
+ * <p>
+ * A Chunk which holds (single) fixed-length properties, and pointer to the
+ * variable length ones / multi-valued ones (which get their own chunk).
+ * <p>
+ * There are two kinds of PropertiesChunks, which differ only in their headers.
  */
 public abstract class PropertiesChunk extends Chunk {
-   public static final String NAME = "__properties_version1.0";
-   
-   /** For logging problems we spot with the file */
-   private POILogger logger = POILogFactory.getLogger(PropertiesChunk.class);
-
-   /**
-    * Holds properties, indexed by type. If a property is multi-valued,
-    *  or variable length, it will be held via a {@link 
ChunkBasedPropertyValue}.
-    */
-   private Map<MAPIProperty, PropertyValue> properties = 
-         new HashMap<MAPIProperty, PropertyValue>();
-
-   /**
-    * The ChunkGroup that these properties apply to. Used when
-    *  matching chunks to variable sized and multi-valued properties
-    */
-   private ChunkGroup parentGroup;
-   
-   /**
-    * Creates a Properties Chunk.
-    */
-   protected PropertiesChunk(ChunkGroup parentGroup) {
-       super(NAME, -1, Types.UNKNOWN);
-       this.parentGroup = parentGroup;
-   }
-
-   @Override
-   public String getEntryName() {
-       return NAME;
-   }
-       
-   /**
-    * Returns all the properties in the chunk, without
-    *  looking up any chunk-based values
-    */
-   public Map<MAPIProperty, PropertyValue> getRawProperties() {
-      return properties;
-   }
-
-   /**
-    * <p>Returns all the properties in the chunk, along with their
-    *  values.
-    * <p>Any chunk-based values will be looked up and returned as such
-    */
-   public Map<MAPIProperty, List<PropertyValue>> getProperties() {
-       Map<MAPIProperty, List<PropertyValue>> props = 
-               new HashMap<MAPIProperty, 
List<PropertyValue>>(properties.size());
-       for (MAPIProperty prop : properties.keySet()) {
-           props.put(prop, getValues(prop));
-       }
-       return props;
-   }
-
-   /**
-    * Returns all values for the given property, looking up chunk based
-    *  ones as required, of null if none exist
-    */
-   public List<PropertyValue> getValues(MAPIProperty property) {
-       PropertyValue val = properties.get(property);
-       if (val == null) {
-           return null;
-       }
-       if (val instanceof ChunkBasedPropertyValue) {
-           // ChunkBasedPropertyValue cval = (ChunkBasedPropertyValue)val;
-           // TODO Lookup
-           return Collections.emptyList();
-       } else {
-           return Collections.singletonList(val);
-       }
-   }
-
-   /**
-    * Returns the value / pointer to the value chunk of 
-    *  the property, or null if none exists
-    */
-   public PropertyValue getRawValue(MAPIProperty property) {
-       return properties.get(property);
-   }
-       
-   /**
-    * Called once the parent ChunkGroup has been populated, to match
-    *  up the Chunks in it with our Variable Sized Properties.
-    */
-   protected void matchVariableSizedPropertiesToChunks() {
-      // Index the Parent Group chunks for easy lookup
-      // TODO Is this the right way?
-      Map<Integer,Chunk> chunks = new HashMap<Integer, Chunk>();
-      for (Chunk chunk : parentGroup.getChunks()) {
-         chunks.put(chunk.chunkId, chunk);
-      }
-      
-      // Loop over our values, looking for chunk based ones
-      for (PropertyValue val : properties.values()) {
-           if (val instanceof ChunkBasedPropertyValue) {
-              ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue)val;
-              Chunk chunk = chunks.get(cVal.getProperty().id);
-//System.err.println(cVal.getProperty() + " = " + cVal + " -> " + 
HexDump.toHex(cVal.data));                  
-                  
-              // TODO Make sense of the raw offset value
-              
-              if (chunk != null) {
-                 cVal.setValue(chunk);
-              } else {
-                 logger.log(POILogger.WARN, "No chunk found matching Property 
" + cVal);
-              }
-         }
-      }
-   }
-
-   protected void readProperties(InputStream value) throws IOException {
-      boolean going = true;
-      while (going) {
-         try {
-            // Read in the header
-            int typeID = LittleEndian.readUShort(value);
-            int id     = LittleEndian.readUShort(value);
-            long flags = LittleEndian.readUInt(value);
-            
-            // Turn the Type and ID into helper objects
-            MAPIType type = Types.getById(typeID);
-            MAPIProperty prop = MAPIProperty.get(id);
-            
-            // Wrap properties we don't know about as custom ones
-            if (prop == MAPIProperty.UNKNOWN) {
-                prop = MAPIProperty.createCustom(id, type, "Unknown " + id);
-            }
-            if (type == null) {
-                logger.log(POILogger.WARN, "Invalid type found, expected ", 
prop.usualType, 
-                        " but got ", typeID, " for property ", prop);
-                going = false;
-                break;
-            }
-            
-            // Sanity check the property's type against the value's type
-            if (prop.usualType != type) {
-                // Is it an allowed substitution?
-                if (type == Types.ASCII_STRING && prop.usualType == 
Types.UNICODE_STRING ||
-                    type == Types.UNICODE_STRING && prop.usualType == 
Types.ASCII_STRING) {
-                    // It's fine to go with the specified instead of the normal
-                } else if (prop.usualType == Types.UNKNOWN) {
-                    // We don't know what this property normally is, but it 
has come
-                    // through with a valid type, so use that
-                    logger.log(POILogger.INFO, "Property definition for ", 
prop, 
-                            " is missing a type definition, found a value with 
type ", type);
+    public static final String NAME = "__properties_version1.0";
+
+    /** For logging problems we spot with the file */
+    private POILogger logger = POILogFactory.getLogger(PropertiesChunk.class);
+
+    /**
+     * Holds properties, indexed by type. If a property is multi-valued, or
+     * variable length, it will be held via a {@link ChunkBasedPropertyValue}.
+     */
+    private Map<MAPIProperty, PropertyValue> properties = new 
HashMap<MAPIProperty, PropertyValue>();
+
+    /**
+     * The ChunkGroup that these properties apply to. Used when matching chunks
+     * to variable sized and multi-valued properties
+     */
+    private ChunkGroup parentGroup;
+
+    /**
+     * Creates a Properties Chunk.
+     */
+    protected PropertiesChunk(ChunkGroup parentGroup) {
+        super(NAME, -1, Types.UNKNOWN);
+        this.parentGroup = parentGroup;
+    }
+
+    @Override
+    public String getEntryName() {
+        return NAME;
+    }
+
+    /**
+     * Returns all the properties in the chunk, without looking up any
+     * chunk-based values
+     */
+    public Map<MAPIProperty, PropertyValue> getRawProperties() {
+        return properties;
+    }
+
+    /**
+     * <p>
+     * Returns all the properties in the chunk, along with their values.
+     * <p>
+     * Any chunk-based values will be looked up and returned as such
+     */
+    public Map<MAPIProperty, List<PropertyValue>> getProperties() {
+        Map<MAPIProperty, List<PropertyValue>> props =
+            new HashMap<MAPIProperty, List<PropertyValue>>(properties.size());
+        for (MAPIProperty prop : properties.keySet()) {
+            props.put(prop, getValues(prop));
+        }
+        return props;
+    }
+
+    /**
+     * Returns all values for the given property, looking up chunk based ones 
as
+     * required, of null if none exist
+     */
+    public List<PropertyValue> getValues(MAPIProperty property) {
+        PropertyValue val = properties.get(property);
+        if (val == null) {
+            return null;
+        }
+        if (val instanceof ChunkBasedPropertyValue) {
+            // ChunkBasedPropertyValue cval = (ChunkBasedPropertyValue)val;
+            // TODO Lookup
+            return Collections.emptyList();
+        } else {
+            return Collections.singletonList(val);
+        }
+    }
+
+    /**
+     * Returns the value / pointer to the value chunk of the property, or null
+     * if none exists
+     */
+    public PropertyValue getRawValue(MAPIProperty property) {
+        return properties.get(property);
+    }
+
+    /**
+     * Called once the parent ChunkGroup has been populated, to match up the
+     * Chunks in it with our Variable Sized Properties.
+     */
+    protected void matchVariableSizedPropertiesToChunks() {
+        // Index the Parent Group chunks for easy lookup
+        // TODO Is this the right way?
+        Map<Integer, Chunk> chunks = new HashMap<Integer, Chunk>();
+        for (Chunk chunk : parentGroup.getChunks()) {
+            chunks.put(chunk.chunkId, chunk);
+        }
+
+        // Loop over our values, looking for chunk based ones
+        for (PropertyValue val : properties.values()) {
+            if (val instanceof ChunkBasedPropertyValue) {
+                ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue) val;
+                Chunk chunk = chunks.get(cVal.getProperty().id);
+                // System.err.println(cVal.getProperty() + " = " + cVal + " -> 
"
+                // + HexDump.toHex(cVal.data));
+
+                // TODO Make sense of the raw offset value
+
+                if (chunk != null) {
+                    cVal.setValue(chunk);
                 } else {
-                   // Oh dear, something has gone wrong...
-                   logger.log(POILogger.WARN, "Type mismatch, expected ", 
prop.usualType, 
-                              " but got ", type, " for property ", prop);
-                   going = false;
-                   break;
+                    logger.log(POILogger.WARN, "No chunk found matching 
Property " + cVal);
                 }
             }
-            
-            // TODO Detect if it is multi-valued, since if it is
-            //  then even fixed-length strings store their multiple
-            //  values in another chunk (much as variable length ones)
-            
-            // Work out how long the "data" is
-            // This might be the actual data, or just a pointer
-            //  to another chunk which holds the data itself
-            boolean isPointer = false;
-            int length = type.getLength();
-            if (! type.isFixedLength()) {
-               isPointer = true;
-               length = 8;
-            }
-            
-            // Grab the data block
-            byte[] data = new byte[length];
-            IOUtils.readFully(value, data);
-            
-            // Skip over any padding
-            if (length < 8) {
-                byte[] padding = new byte[8-length];
-                IOUtils.readFully(value, padding);
-            }
-            
-            // Wrap and store
-            PropertyValue propVal = null;
-            if (isPointer) {
-                // We'll match up the chunk later
-                propVal = new ChunkBasedPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.NULL) {
-                propVal = new NullPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.BOOLEAN) {
-                propVal = new BooleanPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.SHORT) {
-                propVal = new ShortPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.LONG) {
-                propVal = new LongPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.LONG_LONG) {
-                propVal = new LongLongPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.FLOAT) {
-                propVal = new FloatPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.DOUBLE) {
-                propVal = new DoublePropertyValue(prop, flags, data);
-            }
-            else if (type == Types.CURRENCY) {
-                propVal = new CurrencyPropertyValue(prop, flags, data);
-            }
-            else if (type == Types.TIME) {
-                propVal = new TimePropertyValue(prop, flags, data);
-            }
-            // TODO Add in the rest of the types
-            else {
-                propVal = new PropertyValue(prop, flags, data);
-            }
+        }
+    }
+
+    protected void readProperties(InputStream value) throws IOException {
+        boolean going = true;
+        while (going) {
+            try {
+                // Read in the header
+                int typeID = LittleEndian.readUShort(value);
+                int id = LittleEndian.readUShort(value);
+                long flags = LittleEndian.readUInt(value);
+
+                // Turn the Type and ID into helper objects
+                MAPIType type = Types.getById(typeID);
+                MAPIProperty prop = MAPIProperty.get(id);
+
+                // Wrap properties we don't know about as custom ones
+                if (prop == MAPIProperty.UNKNOWN) {
+                    prop = MAPIProperty.createCustom(id, type, "Unknown " + 
id);
+                }
+                if (type == null) {
+                    logger.log(POILogger.WARN, "Invalid type found, expected ",
+                            prop.usualType, " but got ", typeID,
+                            " for property ", prop);
+                    going = false;
+                    break;
+                }
+
+                // Sanity check the property's type against the value's type
+                if (prop.usualType != type) {
+                    // Is it an allowed substitution?
+                    if (type == Types.ASCII_STRING
+                        && prop.usualType == Types.UNICODE_STRING
+                        || type == Types.UNICODE_STRING
+                        && prop.usualType == Types.ASCII_STRING) {
+                        // It's fine to go with the specified instead of the
+                        // normal
+                    } else if (prop.usualType == Types.UNKNOWN) {
+                        // We don't know what this property normally is, but it
+                        // has come
+                        // through with a valid type, so use that
+                        logger.log(POILogger.INFO, "Property definition for ", 
prop,
+                            " is missing a type definition, found a value with 
type ", type);
+                    } else {
+                        // Oh dear, something has gone wrong...
+                        logger.log(POILogger.WARN, "Type mismatch, expected ",
+                            prop.usualType, " but got ", type, " for property 
", prop);
+                        going = false;
+                        break;
+                    }
+                }
+
+                // TODO Detect if it is multi-valued, since if it is
+                // then even fixed-length strings store their multiple
+                // values in another chunk (much as variable length ones)
+
+                // Work out how long the "data" is
+                // This might be the actual data, or just a pointer
+                // to another chunk which holds the data itself
+                boolean isPointer = false;
+                int length = type.getLength();
+                if (!type.isFixedLength()) {
+                    isPointer = true;
+                    length = 8;
+                }
+
+                // Grab the data block
+                byte[] data = new byte[length];
+                IOUtils.readFully(value, data);
+
+                // Skip over any padding
+                if (length < 8) {
+                    byte[] padding = new byte[8 - length];
+                    IOUtils.readFully(value, padding);
+                }
 
-            if (properties.get(prop) != null) {
-                logger.log(POILogger.WARN, "Duplicate values found for " + 
prop);
+                // Wrap and store
+                PropertyValue propVal = null;
+                if (isPointer) {
+                    // We'll match up the chunk later
+                    propVal = new ChunkBasedPropertyValue(prop, flags, data);
+                } else if (type == Types.NULL) {
+                    propVal = new NullPropertyValue(prop, flags, data);
+                } else if (type == Types.BOOLEAN) {
+                    propVal = new BooleanPropertyValue(prop, flags, data);
+                } else if (type == Types.SHORT) {
+                    propVal = new ShortPropertyValue(prop, flags, data);
+                } else if (type == Types.LONG) {
+                    propVal = new LongPropertyValue(prop, flags, data);
+                } else if (type == Types.LONG_LONG) {
+                    propVal = new LongLongPropertyValue(prop, flags, data);
+                } else if (type == Types.FLOAT) {
+                    propVal = new FloatPropertyValue(prop, flags, data);
+                } else if (type == Types.DOUBLE) {
+                    propVal = new DoublePropertyValue(prop, flags, data);
+                } else if (type == Types.CURRENCY) {
+                    propVal = new CurrencyPropertyValue(prop, flags, data);
+                } else if (type == Types.TIME) {
+                    propVal = new TimePropertyValue(prop, flags, data);
+                }
+                // TODO Add in the rest of the types
+                else {
+                    propVal = new PropertyValue(prop, flags, data);
+                }
+
+                if (properties.get(prop) != null) {
+                    logger.log(POILogger.WARN,
+                            "Duplicate values found for " + prop);
+                }
+                properties.put(prop, propVal);
+            } catch (BufferUnderrunException e) {
+                // Invalid property, ended short
+                going = false;
             }
-            properties.put(prop, propVal);
-         } catch (BufferUnderrunException e) {
-            // Invalid property, ended short
-            going = false;
-         }
-      }
-   }
-
-   protected void writeProperties(OutputStream out) throws IOException {
-       // TODO
-   }
+        }
+    }
+
+    protected void writeProperties(OutputStream out) throws IOException {
+        // TODO
+    }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java 
(original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java 
Sat Dec 10 23:35:12 2016
@@ -24,212 +24,228 @@ import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LocaleUtil;
 
 /**
- * An instance of a {@link MAPIProperty} inside a {@link PropertiesChunk}.
- * Where the {@link Types} type is a fixed length one, this will contain the
- *  actual value.
- * Where the {@link Types} type is a variable length one, this will contain
- *  the length of the property, and the value will be in the associated {@link 
Chunk}.
+ * An instance of a {@link MAPIProperty} inside a {@link PropertiesChunk}. 
Where
+ * the {@link Types} type is a fixed length one, this will contain the actual
+ * value. Where the {@link Types} type is a variable length one, this will
+ * contain the length of the property, and the value will be in the associated
+ * {@link Chunk}.
  */
 public class PropertyValue {
-   private MAPIProperty property;
-   private long flags;
-   protected byte[] data;
-   
-   public PropertyValue(MAPIProperty property, long flags, byte[] data) {
-      this.property = property;
-      this.flags = flags;
-      this.data = data;
-   }
-   
-   public MAPIProperty getProperty() {
-      return property;
-   }
-
-   /**
-    * Get the raw value flags.
-    * TODO Also provide getters for the flag meanings
-    */
-   public long getFlags() {
-      return flags;
-   }
-
-   public Object getValue() {
-      return data;
-   }
-   public void setRawValue(byte[] value) {
-      this.data = value;
-   }
-   
-   public String toString() {
-      Object v = getValue();
-      if (v == null)
-          return "(No value available)";
-      
-      if (v instanceof byte[]) {
-          return ByteChunk.toDebugFriendlyString((byte[])v);
-      } else {
-          // Just use the normal toString on the value
-          return v.toString();
-      }
-   }
-   
-   public static class NullPropertyValue extends PropertyValue {
-       public NullPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-          super(property, flags, data);
-       }
-       
-       public Void getValue() {
-           return null;
-       }
-   }
-   
-   public static class BooleanPropertyValue extends PropertyValue {
-       public BooleanPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-          super(property, flags, data);
-       }
-       
-       public Boolean getValue() {
-          short val = LittleEndian.getShort(data);
-          return val > 0;
-       }
-       public void setValue(boolean value) {
-          if (data.length != 2) {
-             data = new byte[2];
-          }
-          if (value) {
-              LittleEndian.putShort(data, 0, (short)1);
-          }
-       }
-   }
-    
-   public static class ShortPropertyValue extends PropertyValue {
-       public ShortPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-          super(property, flags, data);
-       }
-       
-       public Short getValue() {
-          return LittleEndian.getShort(data);
-       }
-       public void setValue(short value) {
-          if (data.length != 2) {
-             data = new byte[2];
-          }
-          LittleEndian.putShort(data, 0, value);
-       }
-   }
-    
-   public static class LongPropertyValue extends PropertyValue {
-       public LongPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-          super(property, flags, data);
-       }
-       
-       public Integer getValue() {
-          return LittleEndian.getInt(data);
-       }
-       public void setValue(int value) {
-          if (data.length != 4) {
-             data = new byte[4];
-          }
-          LittleEndian.putInt(data, 0, value);
-       }
-    }
-    
-   public static class LongLongPropertyValue extends PropertyValue {
-      public LongLongPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-         super(property, flags, data);
-      }
-      
-      public Long getValue() {
-         return LittleEndian.getLong(data);
-      }
-      public void setValue(long value) {
-         if (data.length != 8) {
-            data = new byte[8];
-         }
-         LittleEndian.putLong(data, 0, value);
-      }
-   }
-   
-   public static class FloatPropertyValue extends PropertyValue {
-       public FloatPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-          super(property, flags, data);
-       }
-       
-       public Float getValue() {
-          return LittleEndian.getFloat(data);
-       }
-       public void setValue(float value) {
-          if (data.length != 4) {
-             data = new byte[4];
-          }
-          LittleEndian.putFloat(data, 0, value);
-       }
-   }
-    
-   public static class DoublePropertyValue extends PropertyValue {
-       public DoublePropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-          super(property, flags, data);
-       }
-       
-       public Double getValue() {
-          return LittleEndian.getDouble(data);
-       }
-       public void setValue(double value) {
-          if (data.length != 8) {
-             data = new byte[8];
-          }
-          LittleEndian.putDouble(data, 0, value);
-       }
-   }
-    
-   /**
-    * signed 64-bit integer that represents a base ten decimal, 
-    * with four digits to the right of the decimal point
-    */
-   public static class CurrencyPropertyValue extends PropertyValue {
-       private static final BigInteger SHIFT = BigInteger.valueOf(10000);
-       public CurrencyPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
-          super(property, flags, data);
-       }
-       
-       public BigInteger getValue() {
-           long unshifted = LittleEndian.getLong(data);
-           return BigInteger.valueOf(unshifted).divide(SHIFT);
-       }
-       public void setValue(BigInteger value) {
-          if (data.length != 8) {
-             data = new byte[8];
-          }
-          long shifted = value.multiply(SHIFT).longValue();
-          LittleEndian.putLong(data, 0, shifted);
-       }
-   }
-    
-   /**
-    * 64-bit integer specifying the number of 100ns periods since Jan 1, 1601
-    */
-   public static class TimePropertyValue extends PropertyValue {
-      private static final long OFFSET = 1000L * 60L * 60L * 24L * (365L * 
369L + 89L);
-      public TimePropertyValue(MAPIProperty property, long flags, byte[] data) 
{
-         super(property, flags, data);
-      }
-      
-      public Calendar getValue() {
-         long time = LittleEndian.getLong(data);
-         time = (time / 10 / 1000) - OFFSET;
-
-         Calendar timeC = LocaleUtil.getLocaleCalendar();
-         timeC.setTimeInMillis(time);
-
-         return timeC;
-      }
-      public void setValue(Calendar value) {
-         if (data.length != 8) {
-            data = new byte[8];
-         }
-         long time = value.getTimeInMillis();
-         time = (time + OFFSET) *10*1000;
-         LittleEndian.putLong(data, 0, time);
-      }
-   }
+    private MAPIProperty property;
+    private long flags;
+    protected byte[] data;
+
+    public PropertyValue(MAPIProperty property, long flags, byte[] data) {
+        this.property = property;
+        this.flags = flags;
+        this.data = data;
+    }
+
+    public MAPIProperty getProperty() {
+        return property;
+    }
+
+    /**
+     * Get the raw value flags. TODO Also provide getters for the flag meanings
+     */
+    public long getFlags() {
+        return flags;
+    }
+
+    public Object getValue() {
+        return data;
+    }
+
+    public void setRawValue(byte[] value) {
+        this.data = value;
+    }
+
+    public String toString() {
+        Object v = getValue();
+        if (v == null)
+            return "(No value available)";
+
+        if (v instanceof byte[]) {
+            return ByteChunk.toDebugFriendlyString((byte[]) v);
+        } else {
+            // Just use the normal toString on the value
+            return v.toString();
+        }
+    }
+
+    public static class NullPropertyValue extends PropertyValue {
+        public NullPropertyValue(MAPIProperty property, long flags,
+                byte[] data) {
+            super(property, flags, data);
+        }
+
+        public Void getValue() {
+            return null;
+        }
+    }
+
+    public static class BooleanPropertyValue extends PropertyValue {
+        public BooleanPropertyValue(MAPIProperty property, long flags,
+                byte[] data) {
+            super(property, flags, data);
+        }
+
+        public Boolean getValue() {
+            short val = LittleEndian.getShort(data);
+            return val > 0;
+        }
+
+        public void setValue(boolean value) {
+            if (data.length != 2) {
+                data = new byte[2];
+            }
+            if (value) {
+                LittleEndian.putShort(data, 0, (short) 1);
+            }
+        }
+    }
+
+    public static class ShortPropertyValue extends PropertyValue {
+        public ShortPropertyValue(MAPIProperty property, long flags,
+                byte[] data) {
+            super(property, flags, data);
+        }
+
+        public Short getValue() {
+            return LittleEndian.getShort(data);
+        }
+
+        public void setValue(short value) {
+            if (data.length != 2) {
+                data = new byte[2];
+            }
+            LittleEndian.putShort(data, 0, value);
+        }
+    }
+
+    public static class LongPropertyValue extends PropertyValue {
+        public LongPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
+            super(property, flags, data);
+        }
+
+        public Integer getValue() {
+            return LittleEndian.getInt(data);
+        }
+
+        public void setValue(int value) {
+            if (data.length != 4) {
+                data = new byte[4];
+            }
+            LittleEndian.putInt(data, 0, value);
+        }
+    }
+
+    public static class LongLongPropertyValue extends PropertyValue {
+        public LongLongPropertyValue(MAPIProperty property, long flags,
+                byte[] data) {
+            super(property, flags, data);
+        }
+
+        public Long getValue() {
+            return LittleEndian.getLong(data);
+        }
+
+        public void setValue(long value) {
+            if (data.length != 8) {
+                data = new byte[8];
+            }
+            LittleEndian.putLong(data, 0, value);
+        }
+    }
+
+    public static class FloatPropertyValue extends PropertyValue {
+        public FloatPropertyValue(MAPIProperty property, long flags,
+                byte[] data) {
+            super(property, flags, data);
+        }
+
+        public Float getValue() {
+            return LittleEndian.getFloat(data);
+        }
+
+        public void setValue(float value) {
+            if (data.length != 4) {
+                data = new byte[4];
+            }
+            LittleEndian.putFloat(data, 0, value);
+        }
+    }
+
+    public static class DoublePropertyValue extends PropertyValue {
+        public DoublePropertyValue(MAPIProperty property, long flags, byte[] 
data) {
+            super(property, flags, data);
+        }
+
+        public Double getValue() {
+            return LittleEndian.getDouble(data);
+        }
+
+        public void setValue(double value) {
+            if (data.length != 8) {
+                data = new byte[8];
+            }
+            LittleEndian.putDouble(data, 0, value);
+        }
+    }
+
+    /**
+     * signed 64-bit integer that represents a base ten decimal, with four
+     * digits to the right of the decimal point
+     */
+    public static class CurrencyPropertyValue extends PropertyValue {
+        private static final BigInteger SHIFT = BigInteger.valueOf(10000);
+
+        public CurrencyPropertyValue(MAPIProperty property, long flags, byte[] 
data) {
+            super(property, flags, data);
+        }
+
+        public BigInteger getValue() {
+            long unshifted = LittleEndian.getLong(data);
+            return BigInteger.valueOf(unshifted).divide(SHIFT);
+        }
+
+        public void setValue(BigInteger value) {
+            if (data.length != 8) {
+                data = new byte[8];
+            }
+            long shifted = value.multiply(SHIFT).longValue();
+            LittleEndian.putLong(data, 0, shifted);
+        }
+    }
+
+    /**
+     * 64-bit integer specifying the number of 100ns periods since Jan 1, 1601
+     */
+    public static class TimePropertyValue extends PropertyValue {
+        private static final long OFFSET = 1000L * 60L * 60L * 24L
+                * (365L * 369L + 89L);
+
+        public TimePropertyValue(MAPIProperty property, long flags, byte[] 
data) {
+            super(property, flags, data);
+        }
+
+        public Calendar getValue() {
+            long time = LittleEndian.getLong(data);
+            time = (time / 10 / 1000) - OFFSET;
+
+            Calendar timeC = LocaleUtil.getLocaleCalendar();
+            timeC.setTimeInMillis(time);
+
+            return timeC;
+        }
+
+        public void setValue(Calendar value) {
+            if (data.length != 8) {
+                data = new byte[8];
+            }
+            long time = value.getTimeInMillis();
+            time = (time + OFFSET) * 10 * 1000;
+            LittleEndian.putLong(data, 0, time);
+        }
+    }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java 
(original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java 
Sat Dec 10 23:35:12 2016
@@ -27,209 +27,199 @@ import java.util.Map;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
-
 /**
- * Collection of convenience chunks for the
- *  Recip(ient) part of an outlook file.
+ * Collection of convenience chunks for the Recip(ient) part of an outlook 
file.
  * 
- * If a message has multiple recipients, there will be
- *  several of these.
+ * If a message has multiple recipients, there will be several of these.
  */
 public final class RecipientChunks implements ChunkGroupWithProperties {
-   private static POILogger logger = 
POILogFactory.getLogger(RecipientChunks.class);
+    private static POILogger logger = 
POILogFactory.getLogger(RecipientChunks.class);
+
+    public static final String PREFIX = "__recip_version1.0_#";
 
-   public static final String PREFIX = "__recip_version1.0_#";
-   
-   public static final MAPIProperty RECIPIENT_NAME   = 
MAPIProperty.DISPLAY_NAME;
-   public static final MAPIProperty DELIVERY_TYPE    = MAPIProperty.ADDRTYPE;
-   public static final MAPIProperty RECIPIENT_EMAIL_ADDRESS = 
MAPIProperty.EMAIL_ADDRESS;
-   public static final MAPIProperty RECIPIENT_SEARCH        = 
MAPIProperty.SEARCH_KEY;
-   public static final MAPIProperty RECIPIENT_SMTP_ADDRESS  = 
MAPIProperty.SMTP_ADDRESS;
-   public static final MAPIProperty RECIPIENT_DISPLAY_NAME  = 
MAPIProperty.RECIPIENT_DISPLAY_NAME;
-   
-   /** Our 0 based position in the list of recipients */
-   public int recipientNumber;
-   
-   /** TODO */
-   public ByteChunk recipientSearchChunk;
-   /**
-    * The "name", which could be their name if an
-    *  internal person, or their email address
-    *  if an external person
-    */
-   public StringChunk recipientNameChunk;
-   /** 
-    * The email address of the recipient, which
-    *  could be in SMTP or SEARCH format, but
-    *  isn't always present...
-    */
-   public StringChunk recipientEmailChunk;
-   /** 
-    * The smtp destination email address of
-    *  the recipient, but isn't always present...
-    */
-   public StringChunk recipientSMTPChunk;
-   /**
-    * Normally EX or SMTP. Will generally affect
-    *  where the email address ends up.
-    */
-   public StringChunk deliveryTypeChunk;
-   /**
-    * The display name of the recipient.
-    * Normally seems to hold the same value
-    *  as in recipientNameChunk
-    */
-   public StringChunk recipientDisplayNameChunk;
-   /**
-    * Holds the fixed sized properties, and the
-    *  pointers to the data of variable sized ones
-    */
-   private PropertiesChunk recipientProperties;
-   
-   public RecipientChunks(String name) {
-      recipientNumber = -1;
-      int splitAt = name.lastIndexOf('#');
-      if(splitAt > -1) {
-         String number = name.substring(splitAt+1);
-         try {
-            recipientNumber = Integer.parseInt(number, 16);
-         } catch(NumberFormatException e) {
-                logger.log(POILogger.ERROR, "Invalid recipient number in name 
" + name);
-         }
-      }
-   }
-   
-   /**
-    * Tries to find their name,
-    *  in whichever chunk holds it.
-    */
-   public String getRecipientName() {
-      if(recipientNameChunk != null) {
-         return recipientNameChunk.getValue();
-      }
-      if(recipientDisplayNameChunk != null) {
-         return recipientDisplayNameChunk.getValue();
-      }
-      
-      // Can't find it
-      return null;
-   }
-   
-   /**
-    * Tries to find their email address, in
-    *  whichever chunk holds it given the
-    *  delivery type.
-    */
-   public String getRecipientEmailAddress() {
-      // If we have this, it really has the email 
-      if(recipientSMTPChunk != null) {
-         return recipientSMTPChunk.getValue();
-      }
-      
-      // This might be a real email, or might be
-      //  in CN=... format
-      if(recipientEmailChunk != null) {
-         String email = recipientEmailChunk.getValue();
-         int cne = email.indexOf("/CN="); 
-         if(cne == -1) {
-            // Normal smtp address
-            return email;
-         } else {
-            // /O=..../CN=em@ail
-            return email.substring(cne+4);
-         }
-      }
-      
-      // Might be in the name field, check there
-      if(recipientNameChunk != null) {
-         String name = recipientNameChunk.getValue();
-         if(name.indexOf('@') > -1) {
-            // Strip leading and trailing quotes if needed
-            if(name.startsWith("'") && name.endsWith("'")) {
-               return name.substring(1, name.length()-1);
+    public static final MAPIProperty RECIPIENT_NAME = 
MAPIProperty.DISPLAY_NAME;
+    public static final MAPIProperty DELIVERY_TYPE = MAPIProperty.ADDRTYPE;
+    public static final MAPIProperty RECIPIENT_EMAIL_ADDRESS = 
MAPIProperty.EMAIL_ADDRESS;
+    public static final MAPIProperty RECIPIENT_SEARCH = 
MAPIProperty.SEARCH_KEY;
+    public static final MAPIProperty RECIPIENT_SMTP_ADDRESS = 
MAPIProperty.SMTP_ADDRESS;
+    public static final MAPIProperty RECIPIENT_DISPLAY_NAME = 
MAPIProperty.RECIPIENT_DISPLAY_NAME;
+
+    /** Our 0 based position in the list of recipients */
+    public int recipientNumber;
+
+    /** TODO */
+    public ByteChunk recipientSearchChunk;
+    /**
+     * The "name", which could be their name if an internal person, or their
+     * email address if an external person
+     */
+    public StringChunk recipientNameChunk;
+    /**
+     * The email address of the recipient, which could be in SMTP or SEARCH
+     * format, but isn't always present...
+     */
+    public StringChunk recipientEmailChunk;
+    /**
+     * The smtp destination email address of the recipient, but isn't always
+     * present...
+     */
+    public StringChunk recipientSMTPChunk;
+    /**
+     * Normally EX or SMTP. Will generally affect where the email address ends
+     * up.
+     */
+    public StringChunk deliveryTypeChunk;
+    /**
+     * The display name of the recipient. Normally seems to hold the same value
+     * as in recipientNameChunk
+     */
+    public StringChunk recipientDisplayNameChunk;
+    /**
+     * Holds the fixed sized properties, and the pointers to the data of
+     * variable sized ones
+     */
+    private PropertiesChunk recipientProperties;
+
+    public RecipientChunks(String name) {
+        recipientNumber = -1;
+        int splitAt = name.lastIndexOf('#');
+        if (splitAt > -1) {
+            String number = name.substring(splitAt + 1);
+            try {
+                recipientNumber = Integer.parseInt(number, 16);
+            } catch (NumberFormatException e) {
+                logger.log(POILogger.ERROR,
+                        "Invalid recipient number in name " + name);
             }
-            return name;
-         }
-      }
-      
-      // Check the search chunk, see if it's 
-      //  encoded as a SMTP destination in there.
-      if(recipientSearchChunk != null) {
-         String search = recipientSearchChunk.getAs7bitString();
-         if(search.indexOf("SMTP:") != -1) {
-            return search.substring(search.indexOf("SMTP:") + 5);
-         }
-      }
-      
-      // Can't find it
-      return null;
-   }
-   
-   /** Holds all the chunks that were found. */
-   private List<Chunk> allChunks = new ArrayList<Chunk>();
-
-   public Map<MAPIProperty,List<PropertyValue>> getProperties() {
-      if (recipientProperties != null) {
-         return recipientProperties.getProperties();
-      }
-      else return Collections.emptyMap();
-   }
-   public Chunk[] getAll() {
-      return allChunks.toArray(new Chunk[allChunks.size()]);
-   }
-   public Chunk[] getChunks() {
-      return getAll();
-   }
-       
-   /**
-    * Called by the parser whenever a chunk is found.
-    */
-   public void record(Chunk chunk) {
-      if(chunk.getChunkId() == RECIPIENT_SEARCH.id) {
-         // TODO - parse
-         recipientSearchChunk = (ByteChunk)chunk;
-      }
-      else if(chunk.getChunkId() == RECIPIENT_NAME.id) {
-         recipientDisplayNameChunk = (StringChunk)chunk;
-      }
-      else if(chunk.getChunkId() == RECIPIENT_DISPLAY_NAME.id) {
-         recipientNameChunk = (StringChunk)chunk;
-      }
-      else if(chunk.getChunkId() == RECIPIENT_EMAIL_ADDRESS.id) {
-         recipientEmailChunk = (StringChunk)chunk;
-      }
-      else if(chunk.getChunkId() == RECIPIENT_SMTP_ADDRESS.id) {
-         recipientSMTPChunk = (StringChunk)chunk;
-      }
-      else if(chunk.getChunkId() == DELIVERY_TYPE.id) {
-         deliveryTypeChunk = (StringChunk)chunk;
-      }
-      else if(chunk instanceof PropertiesChunk) {
-         recipientProperties = (PropertiesChunk) chunk;
-      }
-
-      // And add to the main list
-      allChunks.add(chunk);
-   }
-   
-   public void chunksComplete() {
-      if (recipientProperties != null) {
-         recipientProperties.matchVariableSizedPropertiesToChunks();
-      } else {
-         logger.log(POILogger.WARN, "Recipeints Chunk didn't contain a list of 
properties!");
-      }
-   }
-
-   /**
-    * Orders by the recipient number.
-    */
-   public static class RecipientChunksSorter implements 
Comparator<RecipientChunks>, Serializable {
-      public int compare(RecipientChunks a, RecipientChunks b) {
-         if(a.recipientNumber < b.recipientNumber)
-            return -1;
-         if(a.recipientNumber > b.recipientNumber)
-            return +1;
-         return 0;
-      }
-   }
+        }
+    }
+
+    /**
+     * Tries to find their name, in whichever chunk holds it.
+     */
+    public String getRecipientName() {
+        if (recipientNameChunk != null) {
+            return recipientNameChunk.getValue();
+        }
+        if (recipientDisplayNameChunk != null) {
+            return recipientDisplayNameChunk.getValue();
+        }
+
+        // Can't find it
+        return null;
+    }
+
+    /**
+     * Tries to find their email address, in whichever chunk holds it given the
+     * delivery type.
+     */
+    public String getRecipientEmailAddress() {
+        // If we have this, it really has the email
+        if (recipientSMTPChunk != null) {
+            return recipientSMTPChunk.getValue();
+        }
+
+        // This might be a real email, or might be
+        // in CN=... format
+        if (recipientEmailChunk != null) {
+            String email = recipientEmailChunk.getValue();
+            int cne = email.indexOf("/CN=");
+            if (cne == -1) {
+                // Normal smtp address
+                return email;
+            } else {
+                // /O=..../CN=em@ail
+                return email.substring(cne + 4);
+            }
+        }
+
+        // Might be in the name field, check there
+        if (recipientNameChunk != null) {
+            String name = recipientNameChunk.getValue();
+            if (name.indexOf('@') > -1) {
+                // Strip leading and trailing quotes if needed
+                if (name.startsWith("'") && name.endsWith("'")) {
+                    return name.substring(1, name.length() - 1);
+                }
+                return name;
+            }
+        }
+
+        // Check the search chunk, see if it's
+        // encoded as a SMTP destination in there.
+        if (recipientSearchChunk != null) {
+            String search = recipientSearchChunk.getAs7bitString();
+            if (search.indexOf("SMTP:") != -1) {
+                return search.substring(search.indexOf("SMTP:") + 5);
+            }
+        }
+
+        // Can't find it
+        return null;
+    }
+
+    /** Holds all the chunks that were found. */
+    private List<Chunk> allChunks = new ArrayList<Chunk>();
+
+    public Map<MAPIProperty, List<PropertyValue>> getProperties() {
+        if (recipientProperties != null) {
+            return recipientProperties.getProperties();
+        } else
+            return Collections.emptyMap();
+    }
+
+    public Chunk[] getAll() {
+        return allChunks.toArray(new Chunk[allChunks.size()]);
+    }
+
+    public Chunk[] getChunks() {
+        return getAll();
+    }
+
+    /**
+     * Called by the parser whenever a chunk is found.
+     */
+    public void record(Chunk chunk) {
+        if (chunk.getChunkId() == RECIPIENT_SEARCH.id) {
+            // TODO - parse
+            recipientSearchChunk = (ByteChunk) chunk;
+        } else if (chunk.getChunkId() == RECIPIENT_NAME.id) {
+            recipientDisplayNameChunk = (StringChunk) chunk;
+        } else if (chunk.getChunkId() == RECIPIENT_DISPLAY_NAME.id) {
+            recipientNameChunk = (StringChunk) chunk;
+        } else if (chunk.getChunkId() == RECIPIENT_EMAIL_ADDRESS.id) {
+            recipientEmailChunk = (StringChunk) chunk;
+        } else if (chunk.getChunkId() == RECIPIENT_SMTP_ADDRESS.id) {
+            recipientSMTPChunk = (StringChunk) chunk;
+        } else if (chunk.getChunkId() == DELIVERY_TYPE.id) {
+            deliveryTypeChunk = (StringChunk) chunk;
+        } else if (chunk instanceof PropertiesChunk) {
+            recipientProperties = (PropertiesChunk) chunk;
+        }
+
+        // And add to the main list
+        allChunks.add(chunk);
+    }
+
+    public void chunksComplete() {
+        if (recipientProperties != null) {
+            recipientProperties.matchVariableSizedPropertiesToChunks();
+        } else {
+            logger.log(POILogger.WARN, "Recipeints Chunk didn't contain a list 
of properties!");
+        }
+    }
+
+    /**
+     * Orders by the recipient number.
+     */
+    public static class RecipientChunksSorter
+            implements Comparator<RecipientChunks>, Serializable {
+        public int compare(RecipientChunks a, RecipientChunks b) {
+            if (a.recipientNumber < b.recipientNumber)
+                return -1;
+            if (a.recipientNumber > b.recipientNumber)
+                return +1;
+            return 0;
+        }
+    }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java
 Sat Dec 10 23:35:12 2016
@@ -24,30 +24,29 @@ import java.io.OutputStream;
 import org.apache.poi.util.LittleEndian;
 
 /**
- * A {@link PropertiesChunk} for a Storage Properties, such as
- *  Attachments and Recipients.
- * This only has a 8 byte header
+ * A {@link PropertiesChunk} for a Storage Properties, such as Attachments and
+ * Recipients. This only has a 8 byte header
  */
 public class StoragePropertiesChunk extends PropertiesChunk {
-   public StoragePropertiesChunk(ChunkGroup parentGroup) {
-      super(parentGroup);
-   }
-   
-   @Override
-   public void readValue(InputStream stream) throws IOException {
-      // 8 bytes of reserved zeros
-      LittleEndian.readLong(stream);
-      
-      // Now properties
-      readProperties(stream);
-   }
+    public StoragePropertiesChunk(ChunkGroup parentGroup) {
+        super(parentGroup);
+    }
 
-   @Override
-   public void writeValue(OutputStream out) throws IOException {
-      // 8 bytes of reserved zeros
-      out.write(new byte[8]);
-      
-      // Now properties
-      writeProperties(out);
-   }
+    @Override
+    public void readValue(InputStream stream) throws IOException {
+        // 8 bytes of reserved zeros
+        LittleEndian.readLong(stream);
+
+        // Now properties
+        readProperties(stream);
+    }
+
+    @Override
+    public void writeValue(OutputStream out) throws IOException {
+        // 8 bytes of reserved zeros
+        out.write(new byte[8]);
+
+        // Now properties
+        writeProperties(out);
+    }
 }
\ No newline at end of file

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java 
(original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java 
Sat Dec 10 23:35:12 2016
@@ -30,120 +30,119 @@ import org.apache.poi.util.StringUtil;
  * A Chunk made up of a single string.
  */
 public class StringChunk extends Chunk {
-   private static final String DEFAULT_ENCODING = "CP1252"; 
-   private String encoding7Bit = DEFAULT_ENCODING;
-   private byte[] rawValue;
-   private String value;
-
-   /**
-    * Creates a String Chunk.
-    */
-   public StringChunk(String namePrefix, int chunkId, MAPIType type) {
-      super(namePrefix, chunkId, type);
-   }
-
-   /**
-    * Create a String Chunk, with the specified
-    *  type.
-    */
-   public StringChunk(int chunkId, MAPIType type) {
-      super(chunkId, type);
-   }
-
-   /**
-    * Returns the Encoding that will be used to
-    *  decode any "7 bit" (non unicode) data.
-    * Most files default to CP1252
-    */
-   public String get7BitEncoding() {
-      return encoding7Bit;
-   }
-
-   /**
-    * Sets the Encoding that will be used to
-    *  decode any "7 bit" (non unicode) data.
-    * This doesn't appear to be stored anywhere
-    *  specific in the file, so you may need
-    *  to guess by looking at headers etc
-    */
-   public void set7BitEncoding(String encoding) {
-      this.encoding7Bit = encoding;
-
-      // Re-read the String if we're a 7 bit one
-      if(type == Types.ASCII_STRING) {
-         parseString();
-      }
-   }
-
-   public void readValue(InputStream value) throws IOException {
-      rawValue = IOUtils.toByteArray(value);
-      parseString();
-   }
-   private void parseString() {
-      String tmpValue;
-      if (type == Types.ASCII_STRING) {
-         tmpValue = parseAs7BitData(rawValue, encoding7Bit);
-      } else if (type == Types.UNICODE_STRING) {
-         tmpValue = StringUtil.getFromUnicodeLE(rawValue);
-      } else {
-         throw new IllegalArgumentException("Invalid type " + type + " for 
String Chunk");
-      }
-
-      // Clean up
-      this.value = tmpValue.replace("\0", "");
-   }
-
-   public void writeValue(OutputStream out) throws IOException {
-      out.write(rawValue);
-   }
-   private void storeString() {
-      if (type == Types.ASCII_STRING) {
-         rawValue = value.getBytes(Charset.forName(encoding7Bit));
-      } else if (type == Types.UNICODE_STRING) {
-         rawValue = StringUtil.getToUnicodeLE(value);
-      } else {
-         throw new IllegalArgumentException("Invalid type " + type + " for 
String Chunk");
-      }
-   }
-
-   /**
-    * Returns the Text value of the chunk
-    */
-   public String getValue() {
-      return this.value;
-   }
-
-   public byte[] getRawValue() {
-      return this.rawValue;
-   }
-
-   public void setValue(String str) {
-      this.value = str;
-      storeString();
-   }
-
-   public String toString() {
-      return this.value;
-   }
-   
-   /**
-    * Parses as non-unicode, supposedly 7 bit CP1252 data
-    *  and returns the string that that yields.
-    */
-   protected static String parseAs7BitData(byte[] data) {
-      return parseAs7BitData(data, DEFAULT_ENCODING);
-   }
-   /**
-    * Parses as non-unicode, supposedly 7 bit data
-    *  and returns the string that that yields.
-    */
-   protected static String parseAs7BitData(byte[] data, String encoding) {
-      // Handle any encoding aliases, where outlook describes it differently
-      if ("ansi".equals(encoding)) {
-          encoding = DEFAULT_ENCODING;
-      }
-      
-      // Decode
-      return new String(data, Charset.forName(encoding));
-   }
+    private static final String DEFAULT_ENCODING = "CP1252";
+    private String encoding7Bit = DEFAULT_ENCODING;
+    private byte[] rawValue;
+    private String value;
+
+    /**
+     * Creates a String Chunk.
+     */
+    public StringChunk(String namePrefix, int chunkId, MAPIType type) {
+        super(namePrefix, chunkId, type);
+    }
+
+    /**
+     * Create a String Chunk, with the specified type.
+     */
+    public StringChunk(int chunkId, MAPIType type) {
+        super(chunkId, type);
+    }
+
+    /**
+     * Returns the Encoding that will be used to decode any "7 bit" (non
+     * unicode) data. Most files default to CP1252
+     */
+    public String get7BitEncoding() {
+        return encoding7Bit;
+    }
+
+    /**
+     * Sets the Encoding that will be used to decode any "7 bit" (non unicode)
+     * data. This doesn't appear to be stored anywhere specific in the file, so
+     * you may need to guess by looking at headers etc
+     */
+    public void set7BitEncoding(String encoding) {
+        this.encoding7Bit = encoding;
+
+        // Re-read the String if we're a 7 bit one
+        if (type == Types.ASCII_STRING) {
+            parseString();
+        }
+    }
+
+    public void readValue(InputStream value) throws IOException {
+        rawValue = IOUtils.toByteArray(value);
+        parseString();
+    }
+
+    private void parseString() {
+        String tmpValue;
+        if (type == Types.ASCII_STRING) {
+            tmpValue = parseAs7BitData(rawValue, encoding7Bit);
+        } else if (type == Types.UNICODE_STRING) {
+            tmpValue = StringUtil.getFromUnicodeLE(rawValue);
+        } else {
+            throw new IllegalArgumentException("Invalid type " + type + " for 
String Chunk");
+        }
+
+        // Clean up
+        this.value = tmpValue.replace("\0", "");
+    }
+
+    public void writeValue(OutputStream out) throws IOException {
+        out.write(rawValue);
+    }
+
+    private void storeString() {
+        if (type == Types.ASCII_STRING) {
+            rawValue = value.getBytes(Charset.forName(encoding7Bit));
+        } else if (type == Types.UNICODE_STRING) {
+            rawValue = StringUtil.getToUnicodeLE(value);
+        } else {
+            throw new IllegalArgumentException("Invalid type " + type + " for 
String Chunk");
+        }
+    }
+
+    /**
+     * Returns the Text value of the chunk
+     */
+    public String getValue() {
+        return this.value;
+    }
+
+    public byte[] getRawValue() {
+        return this.rawValue;
+    }
+
+    public void setValue(String str) {
+        this.value = str;
+        storeString();
+    }
+
+    public String toString() {
+        return this.value;
+    }
+
+    /**
+     * Parses as non-unicode, supposedly 7 bit CP1252 data and returns the
+     * string that that yields.
+     */
+    protected static String parseAs7BitData(byte[] data) {
+        return parseAs7BitData(data, DEFAULT_ENCODING);
+    }
+
+    /**
+     * Parses as non-unicode, supposedly 7 bit data and returns the string that
+     * that yields.
+     */
+    protected static String parseAs7BitData(byte[] data, String encoding) {
+        // Handle any encoding aliases, where outlook describes it differently
+        if ("ansi".equals(encoding)) {
+            encoding = DEFAULT_ENCODING;
+        }
+
+        // Decode
+        return new String(data, Charset.forName(encoding));
+    }
 }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java?rev=1773544&r1=1773543&r2=1773544&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java 
(original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java Sat 
Dec 10 23:35:12 2016
@@ -23,158 +23,169 @@ import java.util.Map;
 
 /**
  * The types list and details are available from
- *  
http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertytype%28v=EXCHG.140%29.aspx
+ * 
http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertytype%28v=EXCHG.140%29.aspx
  */
 public final class Types {
-   private static Map<Integer, MAPIType> builtInTypes = new HashMap<Integer, 
MAPIType>();
-   private static Map<Integer, MAPIType> customTypes = new HashMap<Integer, 
Types.MAPIType>();
+    private static Map<Integer, MAPIType> builtInTypes = new HashMap<Integer, 
MAPIType>();
+    private static Map<Integer, MAPIType> customTypes = new HashMap<Integer, 
Types.MAPIType>();
 
-   /** Unspecified */
-   public static final MAPIType UNSPECIFIED = new MAPIType(0x0000, 
"Unspecified", -1);
-   /** Unknown */
-   public static final MAPIType UNKNOWN = new MAPIType(-1, "Unknown", -1);
-
-   /** Null - NULL property value */
-   public static final MAPIType NULL = new MAPIType(0x0001, "Null", 0);
-   /** I2 - signed 16-bit value */
-   public static final MAPIType SHORT = new MAPIType(0x0002, "Short", 2);
-   /** Long - signed 32-bit value */
-   public static final MAPIType LONG = new MAPIType(0x0003, "Long", 4);
-   /** R4 - 4-byte floating point value */
-   public static final MAPIType FLOAT = new MAPIType(0x0004, "Float", 4);
-   /** Double - floating point double */
-   public static final MAPIType DOUBLE = new MAPIType(0x0005, "Double", 8);
-   /** Currency - signed 64-bit integer that represents a base ten decimal 
with four digits to the right of the decimal point */
-   public static final MAPIType CURRENCY = new MAPIType(0x0006, "Currency", 8);
-   /** AppTime - application time value */
-   public static final MAPIType APP_TIME = new MAPIType(0x0007, "Application 
Time", 8);
-   /** Error - 32-bit error value */
-   public static final MAPIType ERROR = new MAPIType(0x000A, "Error", 4);
-   /** Boolean - 16-bit Boolean value. '0' is false. Non-zero is true */
-   public static final MAPIType BOOLEAN = new MAPIType(0x000B, "Boolean", 2);
-   /** Object/Directory - embedded object in a property */
-   public static final MAPIType DIRECTORY = new MAPIType(0x000D, "Directory", 
-1);
-   /** I8 - 8-byte signed integer */
-   public static final MAPIType LONG_LONG = new MAPIType(0x0014, "Long Long", 
8);
-   /** SysTime - FILETIME 64-bit integer specifying the number of 100ns 
periods since Jan 1, 1601 */
-   public static final MAPIType TIME = new MAPIType(0x0040, "Time", 8);
-   /** ClassId - OLE GUID */
-   public static final MAPIType CLS_ID = new MAPIType(0x0048, "CLS ID GUID", 
16);
-
-   /** Binary - counted byte array */
-   public static final MAPIType BINARY = new MAPIType(0x0102, "Binary", -1);
-
-   /** 
-    * An 8-bit string, probably in CP1252, but don't quote us...
-    * Normally used for everything before Outlook 3.0, and some
-    *  fields in Outlook 3.0.
-    */
-   public static final MAPIType ASCII_STRING = new MAPIType(0x001E, "ASCII 
String", -1);
-   /** A string, from Outlook 3.0 onwards. Normally unicode */
-   public static final MAPIType UNICODE_STRING = new MAPIType(0x001F, "Unicode 
String", -1);
-
-   /** MultiValued - Value part contains multiple values */
-   public static final int MULTIVALUED_FLAG = 0x1000;
-
-   public static final class MAPIType {
-      private final int id;
-      private final String name;
-      private final int length;
-   
-      /**
-       * Creates a standard, built-in type
-       */
-      private MAPIType(int id, String name, int length) {
-         this.id = id;
-         this.name = name;
-         this.length = length;
-         builtInTypes.put(id, this);
-      }
-      /**
-       * Creates a custom type
-       */
-      private MAPIType(int id, int length) {
-         this.id = id;
-         this.name = asCustomName(id);
-         this.length = length;
-         customTypes.put(id, this);
-      }
-      
-      /**
-       * Returns the length, in bytes, of values of this type, or
-       *  -1 if it is a variable length type.
-       */
-      public int getLength() {
-         return length;
-      }
-      /**
-       * Is this type a fixed-length type, or a variable-length one?
-       */
-      public boolean isFixedLength() {
-         return (length != -1);
-      }
-      
-      public int getId() {
-         return id;
-      }
-      public String getName() {
-         return name;
-      }
-      
-      public String toString() {
-         return id + " / 0x" + asFileEnding() + " - " + name + " @ " + length;
-      }
-      
-      /**
-       * Return the 4 character hex encoded version,
-       *  as used in file endings
-       */
-      public String asFileEnding() {
-         return Types.asFileEnding(id);
-      }
-   }
-   
-   public static MAPIType getById(int typeId) {
-      return builtInTypes.get(typeId);
-   }
-
-   public static String asFileEnding(int type) {
-      String str = Integer.toHexString(type).toUpperCase(Locale.ROOT);
-      while(str.length() < 4) {
-         str = "0" + str;
-      }
-      return str;
-   }
-   public static String asName(int typeId) {
-      MAPIType type = builtInTypes.get(typeId);
-      if (type != null) {
-         return type.name;
-      }
-      return asCustomName(typeId);
-   }
-   private static String asCustomName(int typeId) {
-      return "0x" + Integer.toHexString(typeId);
-   }
-   
-   public static MAPIType createCustom(int typeId) {
-      // Check they're not being silly, and asking for a built-in one...
-      if (getById(typeId) != null) {
-         return getById(typeId);
-      }
-      
-      // Try to get an existing definition of this
-      MAPIType type = customTypes.get(typeId);
-      
-      // If none, do a thread-safe creation
-      if (type == null) {
-         synchronized (customTypes) {
-            type = customTypes.get(typeId);
-            if (type == null) {
-               type = new MAPIType(typeId, -1);
+    /** Unspecified */
+    public static final MAPIType UNSPECIFIED = new MAPIType(0x0000,
+            "Unspecified", -1);
+    /** Unknown */
+    public static final MAPIType UNKNOWN = new MAPIType(-1, "Unknown", -1);
+
+    /** Null - NULL property value */
+    public static final MAPIType NULL = new MAPIType(0x0001, "Null", 0);
+    /** I2 - signed 16-bit value */
+    public static final MAPIType SHORT = new MAPIType(0x0002, "Short", 2);
+    /** Long - signed 32-bit value */
+    public static final MAPIType LONG = new MAPIType(0x0003, "Long", 4);
+    /** R4 - 4-byte floating point value */
+    public static final MAPIType FLOAT = new MAPIType(0x0004, "Float", 4);
+    /** Double - floating point double */
+    public static final MAPIType DOUBLE = new MAPIType(0x0005, "Double", 8);
+    /**
+     * Currency - signed 64-bit integer that represents a base ten decimal with
+     * four digits to the right of the decimal point
+     */
+    public static final MAPIType CURRENCY = new MAPIType(0x0006, "Currency", 
8);
+    /** AppTime - application time value */
+    public static final MAPIType APP_TIME = new MAPIType(0x0007, "Application 
Time", 8);
+    /** Error - 32-bit error value */
+    public static final MAPIType ERROR = new MAPIType(0x000A, "Error", 4);
+    /** Boolean - 16-bit Boolean value. '0' is false. Non-zero is true */
+    public static final MAPIType BOOLEAN = new MAPIType(0x000B, "Boolean", 2);
+    /** Object/Directory - embedded object in a property */
+    public static final MAPIType DIRECTORY = new MAPIType(0x000D, "Directory", 
-1);
+    /** I8 - 8-byte signed integer */
+    public static final MAPIType LONG_LONG = new MAPIType(0x0014, "Long Long", 
8);
+    /**
+     * SysTime - FILETIME 64-bit integer specifying the number of 100ns periods
+     * since Jan 1, 1601
+     */
+    public static final MAPIType TIME = new MAPIType(0x0040, "Time", 8);
+    /** ClassId - OLE GUID */
+    public static final MAPIType CLS_ID = new MAPIType(0x0048, "CLS ID GUID", 
16);
+
+    /** Binary - counted byte array */
+    public static final MAPIType BINARY = new MAPIType(0x0102, "Binary", -1);
+
+    /**
+     * An 8-bit string, probably in CP1252, but don't quote us... Normally used
+     * for everything before Outlook 3.0, and some fields in Outlook 3.0.
+     */
+    public static final MAPIType ASCII_STRING = new MAPIType(0x001E, "ASCII 
String", -1);
+    /** A string, from Outlook 3.0 onwards. Normally unicode */
+    public static final MAPIType UNICODE_STRING = new MAPIType(0x001F, 
"Unicode String", -1);
+
+    /** MultiValued - Value part contains multiple values */
+    public static final int MULTIVALUED_FLAG = 0x1000;
+
+    public static final class MAPIType {
+        private final int id;
+        private final String name;
+        private final int length;
+
+        /**
+         * Creates a standard, built-in type
+         */
+        private MAPIType(int id, String name, int length) {
+            this.id = id;
+            this.name = name;
+            this.length = length;
+            builtInTypes.put(id, this);
+        }
+
+        /**
+         * Creates a custom type
+         */
+        private MAPIType(int id, int length) {
+            this.id = id;
+            this.name = asCustomName(id);
+            this.length = length;
+            customTypes.put(id, this);
+        }
+
+        /**
+         * Returns the length, in bytes, of values of this type, or -1 if it is
+         * a variable length type.
+         */
+        public int getLength() {
+            return length;
+        }
+
+        /**
+         * Is this type a fixed-length type, or a variable-length one?
+         */
+        public boolean isFixedLength() {
+            return (length != -1);
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String toString() {
+            return id + " / 0x" + asFileEnding() + " - " + name + " @ "
+                    + length;
+        }
+
+        /**
+         * Return the 4 character hex encoded version, as used in file endings
+         */
+        public String asFileEnding() {
+            return Types.asFileEnding(id);
+        }
+    }
+
+    public static MAPIType getById(int typeId) {
+        return builtInTypes.get(typeId);
+    }
+
+    public static String asFileEnding(int type) {
+        String str = Integer.toHexString(type).toUpperCase(Locale.ROOT);
+        while (str.length() < 4) {
+            str = "0" + str;
+        }
+        return str;
+    }
+
+    public static String asName(int typeId) {
+        MAPIType type = builtInTypes.get(typeId);
+        if (type != null) {
+            return type.name;
+        }
+        return asCustomName(typeId);
+    }
+
+    private static String asCustomName(int typeId) {
+        return "0x" + Integer.toHexString(typeId);
+    }
+
+    public static MAPIType createCustom(int typeId) {
+        // Check they're not being silly, and asking for a built-in one...
+        if (getById(typeId) != null) {
+            return getById(typeId);
+        }
+
+        // Try to get an existing definition of this
+        MAPIType type = customTypes.get(typeId);
+
+        // If none, do a thread-safe creation
+        if (type == null) {
+            synchronized (customTypes) {
+                type = customTypes.get(typeId);
+                if (type == null) {
+                    type = new MAPIType(typeId, -1);
+                }
             }
-         }
-      }
-      
-      return type;
-   }
+        }
+
+        return type;
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@poi.apache.org
For additional commands, e-mail: commits-h...@poi.apache.org

Reply via email to