Switch HMEF attributes to a factory scheme for creation, then add subtypes for cleaner code. Also adds a few more tests

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1076310 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2011-03-02 17:52:12 +00:00
parent 20f6e280a5
commit 145de166a1
11 changed files with 408 additions and 54 deletions

View File

@ -20,8 +20,11 @@ package org.apache.poi.hmef;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.MAPIAttribute;
import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.TNEFMAPIAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
import org.apache.poi.hsmf.datatypes.MAPIProperty;
/**
@ -33,21 +36,59 @@ public final class Attachment {
private final List<TNEFAttribute> attributes = new ArrayList<TNEFAttribute>();
private final List<MAPIAttribute> mapiAttributes = new ArrayList<MAPIAttribute>();
protected void addAttribute(TNEFAttribute attr) {
attributes.add(attr);
if(attr instanceof TNEFMAPIAttribute) {
TNEFMAPIAttribute tnefMAPI = (TNEFMAPIAttribute)attr;
mapiAttributes.addAll( tnefMAPI.getMAPIAttributes() );
}
}
protected void addAttribute(MAPIAttribute attr) {
mapiAttributes.add(attr);
/**
* Return the attachment attribute with the given ID,
* or null if there isn't one.
*/
public TNEFAttribute getMessageAttribute(TNEFProperty id) {
for(TNEFAttribute attr : attributes) {
if(attr.getProperty() == id) {
return attr;
}
}
return null;
}
/**
* Return the attachment MAPI Attribute with the given ID,
* or null if there isn't one.
*/
public MAPIAttribute getMessageMAPIAttribute(MAPIProperty id) {
for(MAPIAttribute attr : mapiAttributes) {
if(attr.getProperty() == id) {
return attr;
}
}
return null;
}
/**
* Returns all HMEF/TNEF attributes of the attachment,
* such as filename, icon and contents
*/
public List<TNEFAttribute> getAttributes() {
return attributes;
}
/**
* Returns all MAPI attributes of the attachment,
* such as extension, encoding, size and position
*/
public List<MAPIAttribute> getMAPIAttributes() {
return mapiAttributes;
}
public String getFilename() {
TNEFAttribute attr = null;
return null;
}
}

View File

@ -23,7 +23,10 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hmef.attribute.MAPIAttribute;
import org.apache.poi.hmef.attribute.MAPIRtfAttribute;
import org.apache.poi.hmef.attribute.MAPIStringAttribute;
import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.TNEFMAPIAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.util.LittleEndian;
@ -59,24 +62,6 @@ public final class HMEFMessage {
// Now begin processing the contents
process(inp, 0);
// Finally expand out the MAPI Attributes
for(TNEFAttribute attr : messageAttributes) {
if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
mapiAttributes.addAll(
MAPIAttribute.create(attr)
);
}
}
for(Attachment attachment : attachments) {
for(TNEFAttribute attr : attachment.getAttributes()) {
if(attr.getProperty()== TNEFProperty.ID_MAPIPROPERTIES) {
attachment.getMAPIAttributes().addAll(
MAPIAttribute.create(attr)
);
}
}
}
}
private void process(InputStream inp, int lastLevel) throws IOException {
@ -87,11 +72,16 @@ public final class HMEFMessage {
}
// Build the attribute
TNEFAttribute attr = new TNEFAttribute(inp);
TNEFAttribute attr = TNEFAttribute.create(inp);
// Decide what to attach it to, based on the levels and IDs
if(level == TNEFProperty.LEVEL_MESSAGE) {
messageAttributes.add(attr);
if(attr instanceof TNEFMAPIAttribute) {
TNEFMAPIAttribute tnefMAPI = (TNEFMAPIAttribute)attr;
mapiAttributes.addAll( tnefMAPI.getMAPIAttributes() );
}
} else if(level == TNEFProperty.LEVEL_ATTACHMENT) {
// Previous attachment or a new one?
if(attachments.size() == 0 || attr.getProperty() == TNEFProperty.ID_ATTACHRENDERDATA) {
@ -99,7 +89,8 @@ public final class HMEFMessage {
}
// Save the attribute for it
attachments.get(attachments.size()-1).addAttribute(attr);
Attachment attach = attachments.get(attachments.size()-1);
attach.addAttribute(attr);
} else {
throw new IllegalStateException("Unhandled level " + level);
}
@ -158,4 +149,40 @@ public final class HMEFMessage {
}
return null;
}
/**
* Return the string value of the mapi property, or null
* if it isn't set
*/
private String getString(MAPIProperty id) {
MAPIAttribute attr = getMessageMAPIAttribute(id);
if(id == null) {
return null;
}
if(attr instanceof MAPIStringAttribute) {
return ((MAPIStringAttribute)attr).getDataString();
}
if(attr instanceof MAPIRtfAttribute) {
return ((MAPIRtfAttribute)attr).getDataString();
}
System.err.println("Warning, no string property found: " + attr.toString());
return null;
}
/**
* Returns the Message Subject, or null if the mapi property
* for this isn't set
*/
public String getSubject() {
return getString(MAPIProperty.CONVERSATION_TOPIC);
}
/**
* Returns the Message Body, as RTF, or null if the mapi property
* for this isn't set
*/
public String getBody() {
return getString(MAPIProperty.RTF_COMPRESSED);
}
}

View File

@ -82,7 +82,14 @@ public class MAPIAttribute {
* the list of MAPI Attributes contained within it
*/
public static List<MAPIAttribute> create(TNEFAttribute parent) throws IOException {
if(parent.getProperty() != TNEFProperty.ID_MAPIPROPERTIES) {
if(parent.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
// Regular MAPI Properties, normally on the message
}
else if(parent.getProperty() == TNEFProperty.ID_ATTACHMENT) {
// MAPI Properties for an attachment
}
else {
// Something else, oh dear...
throw new IllegalArgumentException(
"Can only create from a MAPIProperty attribute, " +
"instead received a " + parent.getProperty() + " one"

View File

@ -32,19 +32,18 @@ import org.apache.poi.util.LittleEndian;
* Note - the types and IDs differ from standard Outlook/MAPI
* ones, so we can't just re-use the HSMF ones.
*/
public final class TNEFAttribute {
public class TNEFAttribute {
private final TNEFProperty property;
private final int type;
private final byte[] data;
private final int checksum;
/**
* Constructs a single new attribute from
* the contents of the stream
* Constructs a single new attribute from the id, type,
* and the contents of the stream
*/
public TNEFAttribute(InputStream inp) throws IOException {
int id = LittleEndian.readUShort(inp);
this.type = LittleEndian.readUShort(inp);
protected TNEFAttribute(int id, int type, InputStream inp) throws IOException {
this.type = type;
int length = LittleEndian.readInt(inp);
property = TNEFProperty.getBest(id, type);
@ -52,9 +51,26 @@ public final class TNEFAttribute {
IOUtils.readFully(inp, data);
checksum = LittleEndian.readUShort(inp);
}
/**
* Creates a new TNEF Attribute by reading data from
* the stream within a {@link HMEFMessage}
*/
public static TNEFAttribute create(InputStream inp) throws IOException {
int id = LittleEndian.readUShort(inp);
int type = LittleEndian.readUShort(inp);
// TODO Handle the MapiProperties attribute in
// a different way, as we need to recurse into it
// Create as appropriate
if(id == TNEFProperty.ID_MAPIPROPERTIES.id ||
id == TNEFProperty.ID_ATTACHMENT.id) {
return new TNEFMAPIAttribute(id, type, inp);
}
if(type == TNEFProperty.TYPE_STRING ||
type == TNEFProperty.TYPE_TEXT) {
return new TNEFStringAttribute(id, type, inp);
}
return new TNEFAttribute(id, type, inp);
}
public TNEFProperty getProperty() {
@ -70,7 +86,7 @@ public final class TNEFAttribute {
}
public String toString() {
return "Attachment " + property.toString() + ", type=" + type +
return "Attribute " + property.toString() + ", type=" + type +
", data length=" + data.length;
}
}

View File

@ -0,0 +1,52 @@
/* ====================================================================
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.hmef.attribute;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.poi.hmef.Attachment;
import org.apache.poi.hmef.HMEFMessage;
/**
* A TNEF Attribute holding MAPI Attributes, which applies to a
* {@link HMEFMessage} or one of its {@link Attachment}s.
*/
public final class TNEFMAPIAttribute extends TNEFAttribute {
private final List<MAPIAttribute> attributes;
/**
* Constructs a single new mapi containing attribute from the
* id, type, and the contents of the stream
*/
protected TNEFMAPIAttribute(int id, int type, InputStream inp) throws IOException {
super(id, type, inp);
attributes = MAPIAttribute.create(this);
}
public List<MAPIAttribute> getMAPIAttributes() {
return attributes;
}
public String toString() {
return "Attribute " + getProperty().toString() + ", type=" + getType() +
", " + attributes.size() + " MAPI Attributes";
}
}

View File

@ -0,0 +1,55 @@
/* ====================================================================
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.hmef.attribute;
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.hmef.Attachment;
import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.util.StringUtil;
/**
* An String attribute which applies to a {@link HMEFMessage}
* or one of its {@link Attachment}s.
*/
public final class TNEFStringAttribute extends TNEFAttribute {
/**
* Constructs a single new string attribute from the id, type,
* and the contents of the stream
*/
protected TNEFStringAttribute(int id, int type, InputStream inp) throws IOException {
super(id, type, inp);
}
public String getString() {
byte[] data = getData();
// TODO Verify if these are the right way around
if(getType() == TNEFProperty.TYPE_TEXT) {
return StringUtil.getFromUnicodeLE(data);
}
return StringUtil.getFromCompressedUnicode(
data, 0, data.length
);
}
public String toString() {
return "Attribute " + getProperty().toString() + ", type=" + getType() +
", data=" + getString();
}
}

View File

@ -79,6 +79,7 @@ public final class HMEFDumper {
private void dump() throws IOException {
int level;
int attachments = 0;
while(true) {
// Fetch the level
@ -88,7 +89,16 @@ public final class HMEFDumper {
}
// Build the attribute
TNEFAttribute attr = new TNEFAttribute(inp);
TNEFAttribute attr = TNEFAttribute.create(inp);
// Is it a new attachment?
if(level == TNEFProperty.LEVEL_ATTACHMENT &&
attr.getProperty() == TNEFProperty.ID_ATTACHRENDERDATA) {
attachments++;
System.out.println();
System.out.println("Attachment # " + attachments);
System.out.println();
}
// Print the attribute into
System.out.println(
@ -125,7 +135,8 @@ public final class HMEFDumper {
}
System.out.println();
if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES ||
attr.getProperty() == TNEFProperty.ID_ATTACHMENT) {
List<MAPIAttribute> attrs = MAPIAttribute.create(attr);
for(MAPIAttribute ma : attrs) {
System.out.println(indent + indent + ma);

View File

@ -0,0 +1,60 @@
/* ====================================================================
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.hmef;
import junit.framework.TestCase;
import org.apache.poi.POIDataSamples;
public final class TestAttachments extends TestCase {
private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
/**
* Check the file is as we expect
*/
public void testCounts() throws Exception {
HMEFMessage msg = new HMEFMessage(
_samples.openResourceAsStream("quick-winmail.dat")
);
// Should have 5 attachments
assertEquals(5, msg.getAttachments().size());
}
/**
* Check some basic bits about the attachments
*/
public void testBasicAttachments() throws Exception {
// TODO
}
/**
* Query the attachments in detail, and check we see
* the right values for key things
*/
public void testAttachmentDetails() throws Exception {
// TODO
}
/**
* Ensure the attachment contents come back as they should do
*/
public void testAttachmentContents() throws Exception {
// TODO
}
}

View File

@ -20,8 +20,8 @@ package org.apache.poi.hmef;
import junit.framework.TestCase;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
import org.apache.poi.util.LittleEndian;
public final class TestHMEFMessage extends TestCase {
private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
@ -56,13 +56,9 @@ public final class TestHMEFMessage extends TestCase {
int mapiAttrCount = attach.getMAPIAttributes().size();
assertEquals(6, attrCount);
// TODO
// assertTrue("Should be 3-4 attributes, found " + mapiAttrCount, mapiAttrCount >= 20);
// assertTrue("Should be 3-4 attributes, found " + mapiAttrCount, mapiAttrCount <= 25);
assertTrue("Should be 20-25 mapi attributes, found " + mapiAttrCount, mapiAttrCount >= 20);
assertTrue("Should be 20-25 mapi attributes, found " + mapiAttrCount, mapiAttrCount <= 25);
}
// TODO
}
public void testBasicMessageAttributes() throws Exception {
@ -88,18 +84,22 @@ public final class TestHMEFMessage extends TestCase {
assertNull(msg.getMessageAttribute(TNEFProperty.ID_ATTACHDATA));
// Now check the details of one or two
// TODO
assertEquals(
0x010000,
LittleEndian.getInt( msg.getMessageAttribute(TNEFProperty.ID_TNEFVERSION).getData() )
);
assertEquals(
"IPM.Microsoft Mail.Note\0",
new String(msg.getMessageAttribute(TNEFProperty.ID_MESSAGECLASS).getData(), "ASCII")
);
}
public void testBasicMessageMAPIAttributes() throws Exception {
// TODO
}
public void testBasicAttachments() throws Exception {
// TODO
}
public void testMessageAttributeDetails() throws Exception {
// TODO
HMEFMessage msg = new HMEFMessage(
_samples.openResourceAsStream("quick-winmail.dat")
);
assertEquals("This is a test message", msg.getSubject());
assertEquals("{\\rtf1", msg.getBody().substring(0, 6));
}
}

View File

@ -0,0 +1,42 @@
/* ====================================================================
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.hmef.attribute;
import junit.framework.TestCase;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hmef.HMEFMessage;
public final class TestMAPIAttributes extends TestCase {
private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
public void testOpen() throws Exception {
HMEFMessage msg = new HMEFMessage(
_samples.openResourceAsStream("quick-winmail.dat")
);
assertNotNull(msg);
}
// Test basics
// Test counts
// Check untyped
// Check typed
// Check common
}

View File

@ -0,0 +1,43 @@
/* ====================================================================
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.hmef.attribute;
import junit.framework.TestCase;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hmef.HMEFMessage;
public final class TestTNEFAttributes extends TestCase {
private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
public void testOpen() throws Exception {
HMEFMessage msg = new HMEFMessage(
_samples.openResourceAsStream("quick-winmail.dat")
);
assertNotNull(msg);
}
// Test counts
// Test basics
// Test string
// Test a bit of mapi
}