Start on major HSMF refactoring. Should compile, but not quite all tests pass as a little bit of work is left

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@896914 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2010-01-07 16:15:20 +00:00
parent e5884f2f66
commit 2bb376f55b
16 changed files with 666 additions and 471 deletions

View File

@ -37,7 +37,7 @@ import org.apache.poi.poifs.property.Property;
public class DirectoryNode public class DirectoryNode
extends EntryNode extends EntryNode
implements DirectoryEntry, POIFSViewable implements DirectoryEntry, POIFSViewable, Iterable<Entry>
{ {
// Map of Entry instances, keyed by their names // Map of Entry instances, keyed by their names
@ -452,6 +452,13 @@ public class DirectoryNode
return getName(); return getName();
} }
/**
* Returns an Iterator over all the entries
*/
public Iterator<Entry> iterator() {
return getEntries();
}
/* ********** END begin implementation of POIFSViewable ********** */ /* ********** END begin implementation of POIFSViewable ********** */
} // end public class DirectoryNode } // end public class DirectoryNode

View File

@ -21,10 +21,15 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import org.apache.poi.hsmf.datatypes.AttachmentChunks;
import org.apache.poi.hsmf.datatypes.Chunk; import org.apache.poi.hsmf.datatypes.Chunk;
import org.apache.poi.hsmf.datatypes.ChunkGroup;
import org.apache.poi.hsmf.datatypes.Chunks; import org.apache.poi.hsmf.datatypes.Chunks;
import org.apache.poi.hsmf.datatypes.NameIdChunks;
import org.apache.poi.hsmf.datatypes.RecipientChunks;
import org.apache.poi.hsmf.datatypes.StringChunk; import org.apache.poi.hsmf.datatypes.StringChunk;
import org.apache.poi.hsmf.exceptions.ChunkNotFoundException; import org.apache.poi.hsmf.exceptions.ChunkNotFoundException;
import org.apache.poi.hsmf.parsers.POIFSChunkParser; import org.apache.poi.hsmf.parsers.POIFSChunkParser;
@ -36,9 +41,12 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
* @author Travis Ferguson * @author Travis Ferguson
*/ */
public class MAPIMessage { public class MAPIMessage {
private POIFSChunkParser chunkParser;
private POIFSFileSystem fs; private POIFSFileSystem fs;
private Chunks chunks;
private Chunks mainChunks;
private NameIdChunks nameIdChunks;
private RecipientChunks recipientChunks;
private AttachmentChunks[] attachmentChunks;
/** /**
* Constructor for creating new files. * Constructor for creating new files.
@ -64,35 +72,59 @@ public class MAPIMessage {
* @throws IOException * @throws IOException
*/ */
public MAPIMessage(InputStream in) throws IOException { public MAPIMessage(InputStream in) throws IOException {
this.fs = new POIFSFileSystem(in); this(new POIFSFileSystem(in));
chunkParser = new POIFSChunkParser(this.fs); }
/**
// Figure out the right string type, based on * Constructor for reading MSG Files from an input stream.
// the chunks present * @param in
chunks = chunkParser.identifyChunks(); * @throws IOException
*/
public MAPIMessage(POIFSFileSystem fs) throws IOException {
this.fs = fs;
// Grab all the chunks
ChunkGroup[] chunkGroups = POIFSChunkParser.parse(this.fs);
// Grab interesting bits
ArrayList<AttachmentChunks> attachments = new ArrayList<AttachmentChunks>();
for(ChunkGroup group : chunkGroups) {
// Should only ever be one of these
if(group instanceof Chunks) {
mainChunks = (Chunks)group;
} else if(group instanceof NameIdChunks) {
nameIdChunks = (NameIdChunks)group;
} else if(group instanceof RecipientChunks) {
recipientChunks = (RecipientChunks)group;
}
// Add to list(s)
if(group instanceof AttachmentChunks) {
attachments.add((AttachmentChunks)group);
}
}
attachmentChunks = attachments.toArray(new AttachmentChunks[attachments.size()]);
} }
/** /**
* Gets a string value based on the passed chunk. * Gets a string value based on the passed chunk.
* @param chunk * @throws ChunkNotFoundException if the chunk isn't there
* @throws ChunkNotFoundException
*/ */
public String getStringFromChunk(StringChunk chunk) throws ChunkNotFoundException { public String getStringFromChunk(StringChunk chunk) throws ChunkNotFoundException {
Chunk out = this.chunkParser.getDocumentNode(chunk); if(chunk == null) {
StringChunk strchunk = (StringChunk)out; throw new ChunkNotFoundException();
return strchunk.toString(); }
return chunk.getValue();
} }
/** /**
* Gets the plain text body of this Outlook Message * Gets the plain text body of this Outlook Message
* @return The string representation of the 'text' version of the body, if available. * @return The string representation of the 'text' version of the body, if available.
* @throws IOException
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getTextBody() throws ChunkNotFoundException { public String getTextBody() throws ChunkNotFoundException {
return getStringFromChunk(chunks.textBodyChunk); return getStringFromChunk(mainChunks.textBodyChunk);
} }
/** /**
@ -100,17 +132,16 @@ public class MAPIMessage {
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getSubject() throws ChunkNotFoundException { public String getSubject() throws ChunkNotFoundException {
return getStringFromChunk(chunks.subjectChunk); return getStringFromChunk(mainChunks.subjectChunk);
} }
/** /**
* Gets the display value of the "TO" line of the outlook message * Gets the display value of the "TO" line of the outlook message
* This is not the actual list of addresses/values that will be sent to if you click Reply in the email. * This is not the actual list of addresses/values that will be sent to if you click Reply in the email.
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getDisplayTo() throws ChunkNotFoundException { public String getDisplayTo() throws ChunkNotFoundException {
return getStringFromChunk(chunks.displayToChunk); return getStringFromChunk(mainChunks.displayToChunk);
} }
/** /**
@ -119,7 +150,7 @@ public class MAPIMessage {
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getDisplayFrom() throws ChunkNotFoundException { public String getDisplayFrom() throws ChunkNotFoundException {
return getStringFromChunk(chunks.displayFromChunk); return getStringFromChunk(mainChunks.displayFromChunk);
} }
/** /**
@ -128,7 +159,7 @@ public class MAPIMessage {
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getDisplayCC() throws ChunkNotFoundException { public String getDisplayCC() throws ChunkNotFoundException {
return getStringFromChunk(chunks.displayCCChunk); return getStringFromChunk(mainChunks.displayCCChunk);
} }
/** /**
@ -137,7 +168,7 @@ public class MAPIMessage {
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getDisplayBCC() throws ChunkNotFoundException { public String getDisplayBCC() throws ChunkNotFoundException {
return getStringFromChunk(chunks.displayBCCChunk); return getStringFromChunk(mainChunks.displayBCCChunk);
} }
@ -147,7 +178,7 @@ public class MAPIMessage {
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getConversationTopic() throws ChunkNotFoundException { public String getConversationTopic() throws ChunkNotFoundException {
return getStringFromChunk(chunks.conversationTopic); return getStringFromChunk(mainChunks.conversationTopic);
} }
/** /**
@ -158,15 +189,13 @@ public class MAPIMessage {
* @throws ChunkNotFoundException * @throws ChunkNotFoundException
*/ */
public String getMessageClass() throws ChunkNotFoundException { public String getMessageClass() throws ChunkNotFoundException {
return getStringFromChunk(chunks.messageClass); return getStringFromChunk(mainChunks.messageClass);
} }
/** /**
* Gets the message attachments. * Gets the message attachments.
*
* @return a map containing attachment name (String) and data (ByteArrayInputStream)
*/ */
public Map getAttachmentFiles() { public AttachmentChunks[] getAttachmentFiles() {
return this.chunkParser.getAttachmentList(); return attachmentChunks;
} }
} }

View File

@ -16,30 +16,61 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
import java.util.ArrayList;
import java.util.List;
/** /**
* Collection of convenence chunks for standard parts of the MSG file attachment. * Collection of convenence chunks for standard parts of the MSG file attachment.
*/ */
public class AttachmentChunks { public class AttachmentChunks implements ChunkGroup {
public static final String PREFIX = "__attach_version1.0_#";
/* String parts of Outlook Messages Attachments that are currently known */
public static final int ATTACH_DATA = 0x3701;
public static final int ATTACH_EXTENSION = 0x3703;
public static final int ATTACH_FILENAME = 0x3704;
public static final int ATTACH_LONG_FILENAME = 0x3707;
public static final int ATTACH_MIME_TAG = 0x370E;
public static final String namePrefix = "__attach_version1.0_#"; public ByteChunk attachData;
public StringChunk attachExtension;
/* String parts of Outlook Messages Attachments that are currently known */ public StringChunk attachFileName;
public StringChunk attachLongFileName;
public StringChunk attachMimeTag;
public ByteChunk attachData; /** Holds all the chunks that were found. */
public StringChunk attachExtension; private List<Chunk> allChunks = new ArrayList<Chunk>();
public StringChunk attachFileName;
public StringChunk attachLongFileName; public Chunk[] getAll() {
public StringChunk attachMimeTag; return allChunks.toArray(new Chunk[allChunks.size()]);
}
private AttachmentChunks(boolean newStringType) { public Chunk[] getChunks() {
attachData = new ByteChunk(0x3701, 0x0102); return getAll();
attachExtension = new StringChunk(0x3703, newStringType); }
attachFileName = new StringChunk(0x3704, newStringType);
attachLongFileName = new StringChunk(0x3707, newStringType); /**
attachMimeTag = new StringChunk(0x370E, newStringType); * Called by the parser whenever a chunk is found.
} */
public void record(Chunk chunk) {
public static AttachmentChunks getInstance(boolean newStringType) { switch(chunk.getChunkId()) {
return new AttachmentChunks(newStringType); case ATTACH_DATA:
} attachData = (ByteChunk)chunk;
break;
case ATTACH_EXTENSION:
attachExtension = (StringChunk)chunk;
break;
case ATTACH_FILENAME:
attachFileName = (StringChunk)chunk;
break;
case ATTACH_LONG_FILENAME:
attachLongFileName = (StringChunk)chunk;
break;
case ATTACH_MIME_TAG:
attachMimeTag = (StringChunk)chunk;
break;
}
// And add to the main list
allChunks.add(chunk);
}
} }

View File

@ -16,27 +16,24 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
import java.io.ByteArrayOutputStream; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.poi.util.IOUtils;
/** /**
* A Chunk made up of a ByteArrayOutputStream. * A Chunk made up of a ByteArrayOutputStream.
*/ */
public class ByteChunk extends Chunk { public class ByteChunk extends Chunk {
private byte[] value;
private ByteArrayOutputStream value;
/** /**
* Creates a Byte Chunk, for either the old * Creates a Byte Chunk.
* or new style of string chunk types.
*/ */
public ByteChunk(int chunkId, boolean newStyleString) { public ByteChunk(String entryName) {
this(chunkId, getStringType(newStyleString)); super(entryName);
}
private static int getStringType(boolean newStyleString) {
if(newStyleString)
return Types.NEW_STRING;
return Types.OLD_STRING;
} }
/** /**
@ -44,17 +41,21 @@ public class ByteChunk extends Chunk {
* type. * type.
*/ */
public ByteChunk(int chunkId, int type) { public ByteChunk(int chunkId, int type) {
this.chunkId = chunkId; super(chunkId, type);
this.type = type;
}
public ByteArrayOutputStream getValueByteArray() {
return this.value;
} }
public void setValue(ByteArrayOutputStream value) { public void readValue(InputStream value) throws IOException {
this.value = value; this.value = IOUtils.toByteArray(value);
} }
public void writeValue(OutputStream out) throws IOException {
out.write(value);
}
public byte[] getValue() {
return value;
}
public void setValue(byte[] value) {
this.value = value;
}
} }

View File

@ -17,12 +17,33 @@
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
import java.io.ByteArrayOutputStream; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
abstract public class Chunk { abstract public class Chunk {
public static final String DEFAULT_NAME_PREFIX = "__substg1.0_";
protected int chunkId; protected int chunkId;
protected int type; protected int type;
protected String namePrefix = "__substg1.0_"; protected String namePrefix;
protected Chunk(String entryName) {
int splitAt = entryName.lastIndexOf('_');
if(splitAt == -1 || splitAt > (entryName.length()-8)) {
throw new IllegalArgumentException("Invalid chunk name " + entryName);
}
namePrefix = entryName.substring(0, splitAt+1);
String ids = entryName.substring(splitAt+1);
chunkId = Integer.parseInt(ids.substring(0, 4), 16);
type = Integer.parseInt(ids.substring(4, 8), 16);
}
protected Chunk(int chunkId, int type) {
namePrefix = DEFAULT_NAME_PREFIX;
this.chunkId = chunkId;
this.type = type;
}
/** /**
* Gets the id of this chunk * Gets the id of this chunk
@ -52,13 +73,12 @@ abstract public class Chunk {
} }
/** /**
* Gets a reference to a ByteArrayOutputStream that contains the value of this chunk. * Writes the value of this chunk back out again.
*/ */
public abstract ByteArrayOutputStream getValueByteArray(); public abstract void writeValue(OutputStream out) throws IOException;
/** /**
* Sets the value of this chunk using a OutputStream * Reads the value of this chunk using an InputStream
* @param value
*/ */
public abstract void setValue(ByteArrayOutputStream value); public abstract void readValue(InputStream value) throws IOException;
} }

View File

@ -0,0 +1,36 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hsmf.datatypes;
/**
* A group of chunks, that are at the same point in the
* file structure.
*/
public interface ChunkGroup {
/**
* Returns the chunks that make up the group.
* Should certainly contain all the interesting Chunks,
* but needn't always contain all of the Chunks.
*/
public Chunk[] getChunks();
/**
* Called by the parser whenever a chunk is found.
*/
public void record(Chunk chunk);
}

View File

@ -17,60 +17,104 @@
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
import java.util.ArrayList;
import java.util.List;
/** /**
* Collection of convenence chunks for standard parts of the MSG file. * Collection of convenience chunks for standard parts of the MSG file.
* *
* @author Travis Ferguson * Not all of these will be present in any given file
*/ */
public final class Chunks { public final class Chunks implements ChunkGroup {
/* String parts of Outlook Messages that are currently known */ /* String parts of Outlook Messages that are currently known */
public static final int MESSAGE_CLASS = 0x001A;
public static final int SUBJECT = 0x0037;
public static final int DATE = 0x0047;
public static final int CONVERSATION_TOPIC = 0x0070;
public static final int SENT_BY_SERVER_TYPE = 0x0075;
// RECEIVEDEMAIL = 76
public static final int DISPLAY_TO = 0x0E04;
public static final int DISPLAY_FROM = 0x0C1A;
public static final int EMAIL_FROM = 0x0C1F;
public static final int DISPLAY_CC = 0x0E03;
public static final int DISPLAY_BCC = 0x0E02;
public static final int TEXT_BODY = 0x1000;
/** Holds all the chunks that were found. */
private List<Chunk> allChunks = new ArrayList<Chunk>();
/** Type of message that the MSG represents (ie. IPM.Note) */
public StringChunk messageClass;
/** BODY Chunk, for plain/text messages */
public StringChunk textBodyChunk;
/** Subject link chunk, in plain/text */
public StringChunk subjectChunk;
/** Value that is in the TO field (not actually the addresses as they are stored in recip directory nodes */
public StringChunk displayToChunk;
/** Value that is in the FROM field */
public StringChunk displayFromChunk;
/** value that shows in the CC field */
public StringChunk displayCCChunk;
/** Value that shows in the BCC field */
public StringChunk displayBCCChunk;
/** Sort of like the subject line, but without the RE: and FWD: parts. */
public StringChunk conversationTopic;
/** Type of server that the message originated from (SMTP, etc). */
public StringChunk sentByServerType;
/** TODO */
public StringChunk dateChunk;
/** TODO */
public StringChunk emailFromChunk;
/** Type of message that the MSG represents (ie. IPM.Note) */ public Chunk[] getAll() {
public StringChunk messageClass; return allChunks.toArray(new Chunk[allChunks.size()]);
/** BODY Chunk, for plain/text messages */ }
public StringChunk textBodyChunk; public Chunk[] getChunks() {
/** Subject link chunk, in plain/text */ return getAll();
public StringChunk subjectChunk; }
/** Value that is in the TO field (not actually the addresses as they are stored in recip directory nodes */
public StringChunk displayToChunk; /**
/** Value that is in the FROM field */ * Called by the parser whenever a chunk is found.
public StringChunk displayFromChunk; */
/** value that shows in the CC field */ public void record(Chunk chunk) {
public StringChunk displayCCChunk; switch(chunk.getChunkId()) {
/** Value that shows in the BCC field */ case MESSAGE_CLASS:
public StringChunk displayBCCChunk; messageClass = (StringChunk)chunk;
/** Sort of like the subject line, but without the RE: and FWD: parts. */ break;
public StringChunk conversationTopic; case SUBJECT:
/** Type of server that the message originated from (SMTP, etc). */ subjectChunk = (StringChunk)chunk;
public StringChunk sentByServerType; break;
/** TODO */ case DATE:
public StringChunk dateChunk; dateChunk = (StringChunk)chunk;
/** TODO */ break;
public StringChunk emailFromChunk; case CONVERSATION_TOPIC:
/** TODO */ conversationTopic = (StringChunk)chunk;
public StringChunk recipientSearchChunk; break;
/** TODO */ case SENT_BY_SERVER_TYPE:
public StringChunk recipientEmailChunk; sentByServerType = (StringChunk)chunk;
break;
case DISPLAY_TO:
displayToChunk = (StringChunk)chunk;
break;
case DISPLAY_FROM:
displayFromChunk = (StringChunk)chunk;
break;
case EMAIL_FROM:
emailFromChunk = (StringChunk)chunk;
break;
case DISPLAY_CC:
displayCCChunk = (StringChunk)chunk;
break;
case DISPLAY_BCC:
displayBCCChunk = (StringChunk)chunk;
break;
case TEXT_BODY:
textBodyChunk = (StringChunk)chunk;
break;
}
private Chunks(boolean newStringType) { // And add to the main list
messageClass = new StringChunk(0x001A, newStringType); allChunks.add(chunk);
subjectChunk = new StringChunk(0x0037, newStringType); }
dateChunk = new StringChunk(0x0047, newStringType);
conversationTopic = new StringChunk(0x0070, newStringType);
sentByServerType = new StringChunk(0x0075, newStringType);
// RECEIVEDEMAIL = 76
displayToChunk = new StringChunk(0x0E04, newStringType);
displayFromChunk = new StringChunk(0x0C1A, newStringType);
emailFromChunk = new StringChunk(0x0C1F, newStringType);
displayCCChunk = new StringChunk(0x0E03, newStringType);
displayBCCChunk = new StringChunk(0x0E02, newStringType);
recipientSearchChunk = new StringChunk(0x300B, newStringType);
recipientEmailChunk = new StringChunk(0x39FE, newStringType);
textBodyChunk = new StringChunk(0x1000, newStringType);
}
public static Chunks getInstance(boolean newStringType) {
return new Chunks(newStringType);
}
} }

View File

@ -0,0 +1,47 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hsmf.datatypes;
import java.util.ArrayList;
import java.util.List;
/**
* Collection of convenience chunks for the
* NameID part of an outlook file
*/
public final class NameIdChunks implements ChunkGroup {
public static final String PREFIX = "__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);
}
}

View File

@ -0,0 +1,66 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hsmf.datatypes;
import java.util.ArrayList;
import java.util.List;
/**
* Collection of convenience chunks for the
* Recip(ient) part of an outlook file
*/
public final class RecipientChunks implements ChunkGroup {
public static final String PREFIX = "__recip_version1.0_#";
public static final int RECIPIENT_SEARCH = 0x300B;
public static final int RECIPIENT_EMAIL = 0x39FE;
/** TODO */
public StringChunk recipientSearchChunk;
/** TODO */
public StringChunk recipientEmailChunk;
/** 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) {
switch(chunk.getChunkId()) {
case RECIPIENT_SEARCH:
recipientSearchChunk = (StringChunk)chunk;
break;
case RECIPIENT_EMAIL:
recipientEmailChunk = (StringChunk)chunk;
break;
}
// And add to the main list
allChunks.add(chunk);
}
}

View File

@ -17,30 +17,27 @@
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
import java.io.ByteArrayOutputStream; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import org.apache.poi.hsmf.datatypes.Types; import org.apache.poi.hsmf.datatypes.Types;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.StringUtil;
/** /**
* A Chunk made up of a single string. * A Chunk made up of a single string.
* @author Travis Ferguson
*/ */
public class StringChunk extends Chunk { public class StringChunk extends Chunk {
private String value; private String value;
/** /**
* Creates a String Chunk, for either the old * Creates a String Chunk.
* or new style of string chunk types.
*/ */
public StringChunk(int chunkId, boolean newStyleString) { public StringChunk(String entryName) {
this(chunkId, getStringType(newStyleString)); super(entryName);
}
private static int getStringType(boolean newStyleString) {
if(newStyleString)
return Types.NEW_STRING;
return Types.OLD_STRING;
} }
/** /**
@ -48,39 +45,57 @@ public class StringChunk extends Chunk {
* type. * type.
*/ */
public StringChunk(int chunkId, int type) { public StringChunk(int chunkId, int type) {
this.chunkId = chunkId; super(chunkId, type);
this.type = type;
} }
/* (non-Javadoc) public void readValue(InputStream value) throws IOException {
* @see org.apache.poi.hsmf.Chunk.Chunk#getValueByteArray() String tmpValue;
*/ byte[] data = IOUtils.toByteArray(value);
public ByteArrayOutputStream getValueByteArray() {
// TODO Auto-generated method stub switch(type) {
return null; case Types.ASCII_STRING:
} try {
tmpValue = new String(data, "UTF-16LE");
/* (non-Javadoc) } catch (UnsupportedEncodingException e) {
* @see org.apache.poi.hsmf.Chunk.Chunk#setValue(java.io.ByteArrayOutputStream) throw new RuntimeException("Core encoding not found, JVM broken?", e);
*/ }
public void setValue(ByteArrayOutputStream value) { break;
String tmpValue; case Types.UNICODE_STRING:
if (type == Types.NEW_STRING) { tmpValue = StringUtil.getFromUnicodeLE(data);
try { break;
tmpValue = new String(value.toByteArray(), "UTF-16LE"); default:
} catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Invalid type " + type + " for String Chunk");
throw new RuntimeException("Core encoding not found, JVM broken?", e); }
}
} else { // Clean up
try {
tmpValue = new String(value.toByteArray(), "CP1252");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Core encoding not found, JVM broken?", e);
}
}
this.value = tmpValue.replace("\0", ""); this.value = tmpValue.replace("\0", "");
} }
public void writeValue(OutputStream out) throws IOException {
byte[] data;
switch(type) {
case Types.ASCII_STRING:
try {
data = value.getBytes("UTF-16LE");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Core encoding not found, JVM broken?", e);
}
break;
case Types.UNICODE_STRING:
data = new byte[value.length()*2];
StringUtil.putUnicodeLE(value, data, 0);
break;
default:
throw new IllegalArgumentException("Invalid type " + type + " for String Chunk");
}
out.write(data);
}
public String getValue() {
return this.value;
}
public String toString() { public String toString() {
return this.value; return this.value;
} }

View File

@ -18,20 +18,20 @@
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
public final class Types { public final class Types {
public static int BINARY = 0x0102; public static final int BINARY = 0x0102;
/** /**
* An 8-bit string, probably in US-ASCII, but don't quote us... * An 8-bit string, probably in CP1252, but don't quote us...
* Normally used for everything before Outlook 3.0, and some * Normally used for everything before Outlook 3.0, and some
* fields in Outlook 3.0 * fields in Outlook 3.0
*/ */
public static int ASCII_STRING = 0x001E; public static final int ASCII_STRING = 0x001E;
/** A string, from Outlook 3.0 onwards. Normally unicode */ /** A string, from Outlook 3.0 onwards. Normally unicode */
public static int UNICODE_STRING = 0x001F; public static final int UNICODE_STRING = 0x001F;
public static int LONG = 0x0003; public static final int LONG = 0x0003;
public static int TIME = 0x0040; public static final int TIME = 0x0040;
public static int BOOLEAN = 0x000B; public static final int BOOLEAN = 0x000B;
public static String asFileEnding(int type) { public static String asFileEnding(int type) {
String str = Integer.toHexString(type).toUpperCase(); String str = Integer.toHexString(type).toUpperCase();

View File

@ -20,6 +20,9 @@ package org.apache.poi.hsmf.exceptions;
public final class ChunkNotFoundException extends Exception { public final class ChunkNotFoundException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public ChunkNotFoundException() {
super("Chunk not found");
}
public ChunkNotFoundException(String chunkName) { public ChunkNotFoundException(String chunkName) {
super(chunkName + " was named, but not found in POIFS object"); super(chunkName + " was named, but not found in POIFS object");
} }

View File

@ -17,283 +17,127 @@
package org.apache.poi.hsmf.parsers; package org.apache.poi.hsmf.parsers;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.poi.hsmf.datatypes.AttachmentChunks; import org.apache.poi.hsmf.datatypes.AttachmentChunks;
import org.apache.poi.hsmf.datatypes.ByteChunk;
import org.apache.poi.hsmf.datatypes.Chunk; import org.apache.poi.hsmf.datatypes.Chunk;
import org.apache.poi.hsmf.datatypes.ChunkGroup;
import org.apache.poi.hsmf.datatypes.Chunks; import org.apache.poi.hsmf.datatypes.Chunks;
import org.apache.poi.hsmf.datatypes.NameIdChunks;
import org.apache.poi.hsmf.datatypes.RecipientChunks;
import org.apache.poi.hsmf.datatypes.StringChunk;
import org.apache.poi.hsmf.datatypes.Types; import org.apache.poi.hsmf.datatypes.Types;
import org.apache.poi.hsmf.exceptions.ChunkNotFoundException;
import org.apache.poi.hsmf.exceptions.DirectoryChunkNotFoundException;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.DocumentNode; import org.apache.poi.poifs.filesystem.DocumentNode;
import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.POIFSDocument;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.poifs.property.DirectoryProperty;
import org.apache.poi.poifs.property.DocumentProperty;
import org.apache.poi.poifs.storage.BlockWritable;
/** /**
* Provides a HashMap with the ability to parse a PIOFS object and provide * Processes a POIFS of a .msg file into groups of Chunks, such as
* an 'easy to access' hashmap structure for the document chunks inside it. * core data, attachment #1 data, attachment #2 data, recipient
* * data and so on.
* @author Travis Ferguson
*/ */
public final class POIFSChunkParser { public final class POIFSChunkParser {
public static ChunkGroup[] parse(POIFSFileSystem fs) throws IOException {
return parse(fs.getRoot());
}
public static ChunkGroup[] parse(DirectoryNode node) throws IOException {
Chunks mainChunks = new Chunks();
ArrayList<ChunkGroup> groups = new ArrayList<ChunkGroup>();
groups.add(mainChunks);
public POIFSChunkParser(POIFSFileSystem fs) throws IOException { // Find our top level children
this.setFileSystem(fs); // Note - we don't handle children of children yet, as
} // there doesn't seem to be any use of that in Outlook
for(Entry entry : node) {
if(entry instanceof DirectoryNode) {
/** DirectoryNode dir = (DirectoryNode)node;
* Set the POIFileSystem object that this object is using. ChunkGroup group = null;
* @param fs
*/ // Do we know what to do with it?
public void setFileSystem(POIFSFileSystem fs) throws IOException { if(dir.getName().startsWith(AttachmentChunks.PREFIX)) {
this.fs = fs; group = new AttachmentChunks();
this.reparseFileSystem(); }
} if(dir.getName().startsWith(NameIdChunks.PREFIX)) {
group = new NameIdChunks();
/** }
* Get a reference to the FileSystem object that this object is currently using. if(dir.getName().startsWith(RecipientChunks.PREFIX)) {
*/ group = new NameIdChunks();
public POIFSFileSystem getFileSystem() { }
return this.fs;
} if(group != null) {
processChunks(dir, group);
/** groups.add(group);
* Reparse the FileSystem object, resetting all the chunks stored in this object } else {
* // Unknown directory, skip silently
*/ }
public void reparseFileSystem() throws IOException { }
// first clear this object of all chunks }
DirectoryEntry root = this.fs.getRoot();
Iterator<Entry> iter = root.getEntries(); // Now do the top level chunks
processChunks(node, mainChunks);
this.directoryMap = this.processPOIIterator(iter);
} // Finish
return groups.toArray(new ChunkGroup[groups.size()]);
/** }
* Returns a list of the standard chunk types, as
* appropriate for the chunks we find in the file. /**
*/ * Creates all the chunks for a given Directory, but
public Chunks identifyChunks() { * doesn't recurse or descend
return Chunks.getInstance(this.isNewChunkVersion(this.directoryMap)); */
} protected static void processChunks(DirectoryNode node, ChunkGroup grouping) {
for(Entry entry : node) {
/** if(entry instanceof DocumentNode) {
* Returns a list of the standard chunk types, as process((DocumentNode)entry, grouping);
* appropriate for the chunks we find in the file attachment. }
*/ }
private AttachmentChunks identifyAttachmentChunks(Map attachmentMap) { }
return AttachmentChunks.getInstance(this.isNewChunkVersion(attachmentMap));
} /**
* Creates a chunk, and gives it to its parent group
/** */
* Return chunk version of the map in parameter protected static void process(DocumentNode entry, ChunkGroup grouping) {
*/ if(entry.getName().length() < 9) {
private boolean isNewChunkVersion(Map map) { // Name in the wrong format
// Are they of the old or new type of strings? return;
boolean hasOldStrings = false; }
boolean hasNewStrings = false; if(entry.getName().indexOf('_') == -1) {
String oldStringEnd = Types.asFileEnding(Types.OLD_STRING); // Name in the wrong format
String newStringEnd = Types.asFileEnding(Types.NEW_STRING); return;
}
for(Iterator i = map.keySet().iterator(); i.hasNext();) {
String entry = (String)i.next(); // See if we can get a type for it
String ending = entry.getName().substring(entry.getName().length()-4);
if(entry.endsWith( oldStringEnd )) { try {
hasOldStrings = true; int type = Integer.parseInt(ending, 16);
} Chunk chunk = null;
if(entry.endsWith( newStringEnd )) {
hasNewStrings = true; switch(type) {
} case Types.BINARY:
} chunk = new ByteChunk(entry.getName());
break;
if(hasOldStrings && hasNewStrings) { case Types.ASCII_STRING:
throw new IllegalStateException("Your file contains string chunks of both the old and new types. Giving up"); case Types.UNICODE_STRING:
} else if(hasNewStrings) { chunk = new StringChunk(entry.getName());
return true; break;
} }
return false;
} if(chunk != null) {
try {
/** DocumentInputStream inp = new DocumentInputStream(entry);
* Pull the chunk data that's stored in this object's hashmap out and return it as a HashMap. chunk.readValue(inp);
* @param entryName grouping.record(chunk);
*/ } catch(IOException e) {
public Object getChunk(HashMap dirMap, String entryName) { System.err.println("Error reading from part " + entry.getName() + " - " + e.toString());
if(dirMap == null) { }
return null; }
} } catch(NumberFormatException e) {
return dirMap.get(entryName); // Name in the wrong format
} return;
}
/** }
* Pull a directory/hashmap out of this hashmap and return it
* @param directoryName
* @return HashMap containing the chunks stored in the named directoryChunk
* @throws DirectoryChunkNotFoundException This is thrown should the directoryMap HashMap on this object be null
* or for some reason the directory is not found, is equal to null, or is for some reason not a HashMap/aka Directory Node.
*/
public HashMap getDirectoryChunk(String directoryName) throws DirectoryChunkNotFoundException {
DirectoryChunkNotFoundException excep = new DirectoryChunkNotFoundException(directoryName);
Object obj = getChunk(this.directoryMap, directoryName);
if(obj == null || !(obj instanceof HashMap)) throw excep;
return (HashMap)obj;
}
/**
* Pulls a ByteArrayOutputStream from this objects HashMap, this can be used to read a byte array of the contents of the given chunk.
* @param dirNode
* @param chunk
* @throws ChunkNotFoundException
*/
public Chunk getDocumentNode(HashMap dirNode, Chunk chunk) throws ChunkNotFoundException {
String entryName = chunk.getEntryName();
ChunkNotFoundException excep = new ChunkNotFoundException(entryName);
Object obj = getChunk(dirNode, entryName);
if(obj == null || !(obj instanceof ByteArrayOutputStream)) throw excep;
chunk.setValue((ByteArrayOutputStream)obj);
return chunk;
}
/**
* Pulls a Chunk out of this objects root Node tree.
* @param chunk
* @throws ChunkNotFoundException
*/
public Chunk getDocumentNode(Chunk chunk) throws ChunkNotFoundException {
return getDocumentNode(this.directoryMap, chunk);
}
/**
*
* @return a map containing attachment name (String) and data (ByteArrayInputStream)
*/
public Map getAttachmentList() {
Map attachments = new HashMap();
List attachmentList = new ArrayList();
for(Iterator i = directoryMap.keySet().iterator(); i.hasNext();) {
String entry = (String)i.next();
if(entry.startsWith(AttachmentChunks.namePrefix)) {
String attachmentIdString = entry.replace(AttachmentChunks.namePrefix, "");
try {
int attachmentId = Integer.parseInt(attachmentIdString);
attachmentList.add(directoryMap.get(entry));
} catch (NumberFormatException nfe) {
System.err.println("Invalid attachment id");
}
}
}
for (Iterator iterator = attachmentList.iterator(); iterator.hasNext();) {
HashMap AttachmentChunkMap = (HashMap) iterator.next();
AttachmentChunks attachmentChunks = this.identifyAttachmentChunks(AttachmentChunkMap);
try {
Chunk fileName = this.getDocumentNode(AttachmentChunkMap, attachmentChunks.attachLongFileName);
Chunk content = this.getDocumentNode(AttachmentChunkMap, attachmentChunks.attachData);
attachments.put(fileName.toString(), new ByteArrayInputStream(content.getValueByteArray().toByteArray()));
} catch (ChunkNotFoundException e) {
System.err.println("Invalid attachment chunk");
}
}
return attachments;
}
/**
* Processes an iterator returned by a POIFS call to getRoot().getEntries()
* @param iter
* @return
* @throws IOException
*/
private HashMap<String, HashMap<?,?>> processPOIIterator(Iterator<Entry> iter) throws IOException {
HashMap<String, HashMap<?,?>> currentNode = new HashMap<String, HashMap<?,?>>();
while(iter.hasNext()) {
Entry entry = iter.next();
if(entry instanceof DocumentNode) {
this.processDocumentNode((DocumentNode)entry, currentNode);
} else if(entry instanceof DirectoryNode) {
DirectoryNode dir = (DirectoryNode)entry;
String blockName = dir.getName();
// Recurse down, storing on the hashmap
currentNode.put(blockName, processPOIIterator(dir.getEntries()));
} else if(entry instanceof DirectoryProperty) {
//don't do anything with the directory property chunk...
} else {
System.err.println("Unknown node: " + entry.toString());
}
}
return currentNode;
}
/**
* Processes a document node and adds it to the current directory HashMap
* @param obj
* @throws java.io.IOException
*/
private void processDocumentNode(DocumentNode obj, HashMap currentObj) throws IOException {
String blockName = obj.getName();
Iterator viewIt = null;
if( obj.preferArray()) {
Object[] arr = obj.getViewableArray();
ArrayList viewList = new ArrayList(arr.length);
for(int i = 0; i < arr.length; i++) {
viewList.add(arr[i]);
}
viewIt = viewList.iterator();
} else {
viewIt = obj.getViewableIterator();
}
while(viewIt.hasNext()) {
Object view = viewIt.next();
if(view instanceof DocumentProperty) {
//we don't care about the properties
} else if(view instanceof POIFSDocument) {
//check if our node has blocks or if it can just be read raw.
int blockCount = ((POIFSDocument)view).countBlocks();
//System.out.println("Block Name: " + blockName);
if(blockCount <= 0) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
BlockWritable[] bws = ((POIFSDocument)view).getSmallBlocks();
for(int i = 0; i < bws.length; i++) {
bws[i].writeBlocks(out);
}
currentObj.put(blockName, out);
} else {
ByteArrayOutputStream out = new ByteArrayOutputStream();
((POIFSDocument)view).writeBlocks(out);
currentObj.put(blockName, out);
}
} else {
System.err.println("Unknown View Type: " + view.toString());
}
}
}
/* private instance variables */
private static final long serialVersionUID = 1L;
private POIFSFileSystem fs;
private HashMap directoryMap;
} }

View File

@ -20,6 +20,7 @@ package org.apache.poi.hsmf.model;
import org.apache.poi.hsmf.datatypes.Chunk; import org.apache.poi.hsmf.datatypes.Chunk;
import org.apache.poi.hsmf.datatypes.Chunks; import org.apache.poi.hsmf.datatypes.Chunks;
import org.apache.poi.hsmf.datatypes.StringChunk; import org.apache.poi.hsmf.datatypes.StringChunk;
import org.apache.poi.hsmf.datatypes.Types;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -31,47 +32,54 @@ import junit.framework.TestCase;
* *
*/ */
public final class TestChunkData extends TestCase { public final class TestChunkData extends TestCase {
private Chunks chunks = Chunks.getInstance(false);
public void testChunkCreate() { public void testChunkCreate() {
StringChunk chunk = new StringChunk(0x0200, false); Chunk chunk;
TestCase.assertEquals("__substg1.0_0200001E", chunk.getEntryName());
chunk = new StringChunk(0x0200, 0x001E);
assertEquals("__substg1.0_0200001E", chunk.getEntryName());
assertEquals(0x0200, chunk.getChunkId());
assertEquals(0x001E, chunk.getType());
chunk = new StringChunk("__substg1.0_0200001E");
assertEquals("__substg1.0_0200001E", chunk.getEntryName());
assertEquals(0x0200, chunk.getChunkId());
assertEquals(0x001E, chunk.getType());
/* test the lower and upper limits of the chunk ids */ /* test the lower and upper limits of the chunk ids */
chunk = new StringChunk(0x0000, false); chunk = new StringChunk(0x0000, 0x001E);
TestCase.assertEquals("__substg1.0_0000001E", chunk.getEntryName()); assertEquals("__substg1.0_0000001E", chunk.getEntryName());
chunk = new StringChunk(0xFFFF, false); chunk = new StringChunk(0xFFFF, 0x001E);
TestCase.assertEquals("__substg1.0_FFFF001E", chunk.getEntryName()); assertEquals("__substg1.0_FFFF001E", chunk.getEntryName());
chunk = new StringChunk(0xFFFF, true); chunk = new StringChunk(0xFFFF, 0x001F);
TestCase.assertEquals("__substg1.0_FFFF001F", chunk.getEntryName()); assertEquals("__substg1.0_FFFF001F", chunk.getEntryName());
} }
public void testTextBodyChunk() { public void testTextBodyChunk() {
StringChunk chunk = new StringChunk(0x1000, false); StringChunk chunk = new StringChunk(0x1000, Types.UNICODE_STRING);
TestCase.assertEquals(chunk.getEntryName(), chunks.textBodyChunk.getEntryName()); assertEquals(chunk.getChunkId(), Chunks.TEXT_BODY);
} }
public void testDisplayToChunk() { public void testDisplayToChunk() {
StringChunk chunk = new StringChunk(0x0E04, false); StringChunk chunk = new StringChunk(0x0E04, Types.UNICODE_STRING);
TestCase.assertEquals(chunk.getEntryName(), chunks.displayToChunk.getEntryName()); assertEquals(chunk.getChunkId(), Chunks.DISPLAY_TO);
} }
public void testDisplayCCChunk() { public void testDisplayCCChunk() {
StringChunk chunk = new StringChunk(0x0E03, false); StringChunk chunk = new StringChunk(0x0E03, Types.UNICODE_STRING);
TestCase.assertEquals(chunk.getEntryName(), chunks.displayCCChunk.getEntryName()); assertEquals(chunk.getChunkId(), Chunks.DISPLAY_CC);
} }
public void testDisplayBCCChunk() { public void testDisplayBCCChunk() {
StringChunk chunk = new StringChunk(0x0E02, false); StringChunk chunk = new StringChunk(0x0E02, Types.UNICODE_STRING);
TestCase.assertEquals(chunk.getEntryName(), chunks.displayBCCChunk.getEntryName()); assertEquals(chunk.getChunkId(), Chunks.DISPLAY_BCC);
} }
public void testSubjectChunk() { public void testSubjectChunk() {
Chunk chunk = new StringChunk(0x0037, false); Chunk chunk = new StringChunk(0x0037, Types.UNICODE_STRING);
TestCase.assertEquals(chunk.getEntryName(), chunks.subjectChunk.getEntryName()); assertEquals(chunk.getChunkId(), Chunks.SUBJECT);
} }
} }

View File

@ -26,6 +26,7 @@ import java.util.Map;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hsmf.MAPIMessage; import org.apache.poi.hsmf.MAPIMessage;
import org.apache.poi.hsmf.datatypes.AttachmentChunks;
import org.apache.poi.hsmf.exceptions.ChunkNotFoundException; import org.apache.poi.hsmf.exceptions.ChunkNotFoundException;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
@ -55,8 +56,8 @@ public class TestFileWithAttachmentsRead extends TestCase {
*/ */
// public void testReadDisplayCC() throws ChunkNotFoundException { // public void testReadDisplayCC() throws ChunkNotFoundException {
public void testRetrieveAttachments() { public void testRetrieveAttachments() {
Map attachmentsMap = mapiMessage.getAttachmentFiles(); AttachmentChunks[] attachments = mapiMessage.getAttachmentFiles();
int obtained = attachmentsMap.size(); int obtained = attachments.length;
int expected = 2; int expected = 2;
TestCase.assertEquals(obtained, expected); TestCase.assertEquals(obtained, expected);
@ -69,19 +70,16 @@ public class TestFileWithAttachmentsRead extends TestCase {
* *
*/ */
public void testReadAttachments() throws IOException { public void testReadAttachments() throws IOException {
Map attachmentsMap = mapiMessage.getAttachmentFiles(); AttachmentChunks[] attachments = mapiMessage.getAttachmentFiles();
for (Iterator iterator = attachmentsMap.keySet().iterator(); iterator.hasNext();) { for (AttachmentChunks attachment : attachments) {
String fileName = (String) iterator.next(); assertTrue(attachment.attachFileName.getValue().length() > 0);
ByteArrayInputStream fileStream = (ByteArrayInputStream) attachmentsMap.get(fileName); assertTrue(attachment.attachLongFileName.getValue().length() > 0);
ByteArrayOutputStream fileContent = new ByteArrayOutputStream(); assertTrue(attachment.attachExtension.getValue().length() > 0);
assertTrue(attachment.attachMimeTag.getValue().length() > 0);
while (fileStream.available() > 0) {
fileContent.write(fileStream.read());
}
String obtained = new String(fileContent.toByteArray(), "UTF-8");
assertTrue(obtained.trim().length() > 0);
} }
// TODO better checking
} }
} }

View File

@ -0,0 +1,46 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hsmf.model;
import org.apache.poi.hsmf.datatypes.Types;
import junit.framework.TestCase;
/**
* Verifies that the Types class is behaving properly.
* Also check that no changes have been made that will
* break the library.
*/
public final class TestTypes extends TestCase {
public void testTypeIds() {
assertEquals(0x1e, Types.ASCII_STRING);
assertEquals(0x1f, Types.UNICODE_STRING);
assertEquals(0x0102, Types.BINARY);
assertEquals(0x000B, Types.BOOLEAN);
assertEquals(0x0003, Types.LONG);
assertEquals(0x0040, Types.TIME);
}
public void testTypeFormatting() {
assertEquals("0000", Types.asFileEnding(0x0000));
assertEquals("0020", Types.asFileEnding(0x0020));
assertEquals("0102", Types.asFileEnding(0x0102));
assertEquals("FEDC", Types.asFileEnding(0xfedc));
}
}