From 45dae98850a28b0a3044d5bd74d06767a0033e66 Mon Sep 17 00:00:00 2001 From: Doug Pedrick Date: Sat, 28 Apr 2012 03:16:42 +0000 Subject: [PATCH] Support for base64 images in .orc; performance improvement to the JAXBContext --- .../preset/xml/BaseComponentDTO.java | 83 ++++++++++--- .../preset/xml/OpenRocketComponentSaver.java | 24 +++- .../preset/xml/BaseComponentDTOTest.java | 72 +++++++++++ .../xml/OpenRocketComponentSaverTest.java | 112 ++++++++++++++++++ 4 files changed, 270 insertions(+), 21 deletions(-) create mode 100644 core/test/net/sf/openrocket/preset/xml/BaseComponentDTOTest.java create mode 100644 core/test/net/sf/openrocket/preset/xml/OpenRocketComponentSaverTest.java diff --git a/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java b/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java index d107abf57..6be6ebd96 100644 --- a/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java +++ b/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java @@ -1,14 +1,6 @@ package net.sf.openrocket.preset.xml; -import java.util.List; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlValue; - import net.sf.openrocket.database.Databases; import net.sf.openrocket.material.Material; import net.sf.openrocket.motor.Manufacturer; @@ -17,6 +9,22 @@ import net.sf.openrocket.preset.InvalidComponentPresetException; import net.sf.openrocket.preset.TypedPropertyMap; import net.sf.openrocket.unit.UnitGroup; +import javax.imageio.ImageIO; +import javax.xml.bind.DatatypeConverter; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlInlineBinaryData; +import javax.xml.bind.annotation.XmlValue; +import javax.xml.bind.annotation.adapters.XmlAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; + /** * Base class for the external representation of all component presets. */ @@ -35,6 +43,10 @@ public abstract class BaseComponentDTO { private AnnotatedMassDTO mass; @XmlElement(name="Filled") private Boolean filled; + @XmlInlineBinaryData + @XmlJavaTypeAdapter(Base64Adapter.class) + @XmlElement(name = "Thumbnail") + private byte[] image; /** * Default constructor. @@ -118,7 +130,30 @@ public abstract class BaseComponentDTO { this.filled = filled; } - public abstract ComponentPreset asComponentPreset(List materials) throws InvalidComponentPresetException; + public byte[] getImageData() { + return image; + } + + public void setImageData(final byte[] theImage) { + image = theImage; + } + + public BufferedImage getImage() throws IOException { + if (image != null) { + return ImageIO.read(new ByteArrayInputStream(image)); + } + return null; + } + + public void setImage(BufferedImage theImage) throws IOException { + if (theImage != null) { + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ImageIO.write(theImage, "png", byteArrayOutputStream); + image = byteArrayOutputStream.toByteArray(); + } + } + + public abstract ComponentPreset asComponentPreset(List materials) throws InvalidComponentPresetException; void addProps(TypedPropertyMap props, List materialList) { props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(manufacturer)); @@ -162,15 +197,15 @@ public abstract class BaseComponentDTO { material = theMaterial.getName(); } } - + static class AnnotatedLengthDTO { @XmlAttribute(name="Unit", required=false) private String unitName = "m"; @XmlValue private double length; - + AnnotatedLengthDTO() {} - + AnnotatedLengthDTO( double length ) { this.length = length; } @@ -179,21 +214,37 @@ public abstract class BaseComponentDTO { return UnitGroup.UNITS_LENGTH.getUnit(unitName).fromUnit(length); } } - + static class AnnotatedMassDTO { @XmlAttribute(name="Unit", required=false) private String unitName = "kg"; @XmlValue private double mass; - + AnnotatedMassDTO() {} - + AnnotatedMassDTO( double mass ) { this.mass = mass; } - + public double getValue() { return UnitGroup.UNITS_MASS.getUnit(unitName).fromUnit(mass); } } + + static class Base64Adapter extends XmlAdapter { + public byte[] unmarshal(String s) { + if (s == null) { + return null; + } + return DatatypeConverter.parseBase64Binary(s); + } + + public String marshal(byte[] bytes) { + if (bytes == null) { + return null; + } + return DatatypeConverter.printBase64Binary(bytes); + } + } } diff --git a/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java b/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java index ecfaba5f6..add220a69 100644 --- a/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java +++ b/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java @@ -3,6 +3,7 @@ package net.sf.openrocket.preset.xml; import net.sf.openrocket.material.Material; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.InvalidComponentPresetException; +import net.sf.openrocket.startup.Application; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -21,6 +22,20 @@ import java.util.List; */ public class OpenRocketComponentSaver { + /** + * The JAXBContext. JAXBContext is thread-safe. + */ + private static JAXBContext context = null; + + static { + try { + context = JAXBContext.newInstance(OpenRocketComponentDTO.class); + } + catch (JAXBException jaxb) { + Application.getLogger().error("Unable to create JAXBContext for loading of *.orc files.", jaxb); + } + } + /** * This method marshals a list of materials and ComponentPresets into an .orc formatted XML string. * @@ -33,9 +48,8 @@ public class OpenRocketComponentSaver { */ public String marshalToOpenRocketComponent(List theMaterialList, List thePresetList) throws JAXBException { - - JAXBContext binder = JAXBContext.newInstance(OpenRocketComponentDTO.class); - Marshaller marshaller = binder.createMarshaller(); + /** The context is thread-safe, but marshallers are not. Create a local one. */ + Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); StringWriter sw = new StringWriter(); @@ -88,8 +102,8 @@ public class OpenRocketComponentSaver { * was in an invalid format */ private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException { - JAXBContext bind = JAXBContext.newInstance(OpenRocketComponentDTO.class); - Unmarshaller unmarshaller = bind.createUnmarshaller(); + /** The context is thread-safe, but unmarshallers are not. Create a local one. */ + Unmarshaller unmarshaller = context.createUnmarshaller(); return (OpenRocketComponentDTO) unmarshaller.unmarshal(is); } diff --git a/core/test/net/sf/openrocket/preset/xml/BaseComponentDTOTest.java b/core/test/net/sf/openrocket/preset/xml/BaseComponentDTOTest.java new file mode 100644 index 000000000..0096810d3 --- /dev/null +++ b/core/test/net/sf/openrocket/preset/xml/BaseComponentDTOTest.java @@ -0,0 +1,72 @@ +package net.sf.openrocket.preset.xml; + +import net.sf.openrocket.motor.Manufacturer; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.preset.ComponentPresetFactory; +import net.sf.openrocket.preset.TypedPropertyMap; +import org.junit.Assert; +import org.junit.Test; + +import javax.imageio.ImageIO; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.io.StringReader; +import java.io.StringWriter; + +/** + */ +public class BaseComponentDTOTest { + + + @Test + public void testImage() throws Exception { + TypedPropertyMap presetspec = new TypedPropertyMap(); + presetspec.put(ComponentPreset.TYPE, ComponentPreset.Type.BODY_TUBE); + presetspec.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer("manufacturer")); + presetspec.put(ComponentPreset.PARTNO, "partno"); + presetspec.put(ComponentPreset.LENGTH, 2.0); + presetspec.put(ComponentPreset.OUTER_DIAMETER, 2.0); + presetspec.put(ComponentPreset.INNER_DIAMETER, 1.0); + presetspec.put(ComponentPreset.MASS, 100.0); + ComponentPreset preset = ComponentPresetFactory.create(presetspec); + + //Convert the presets to a BodyTubeDTO + BodyTubeDTO dto = new BodyTubeDTO(preset); + + //Add an image to the DTO. + BufferedImage image = ImageIO.read(this.getClass().getResourceAsStream("or_launcher.png")); + dto.setImage(image); + + JAXBContext binder = JAXBContext.newInstance(OpenRocketComponentDTO.class); + Marshaller marshaller = binder.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + StringWriter sw = new StringWriter(); + + //Serialize the dto to XML + marshaller.marshal(dto, sw); + String xml = sw.toString(); + + //Read the XML back to create the dto again + Unmarshaller unmarshaller = binder.createUnmarshaller(); + BodyTubeDTO redone = (BodyTubeDTO) unmarshaller.unmarshal(new StringReader(xml)); + + //Compare the image. + Assert.assertArrayEquals(((DataBufferByte) image.getData().getDataBuffer()).getData(), + ((DataBufferByte) redone.getImage().getData().getDataBuffer()).getData()); + + //Assert the rest of the attributes. + Assert.assertEquals(dto.getInsideDiameter(), redone.getInsideDiameter(), 0.00001); + Assert.assertEquals(dto.getLength(), redone.getLength(), 0.00001); + Assert.assertEquals(dto.getOutsideDiameter(), redone.getOutsideDiameter(), 0.00001); + Assert.assertEquals(dto.getDescription(), redone.getDescription()); + Assert.assertEquals(dto.getManufacturer(), redone.getManufacturer()); + Assert.assertEquals(dto.getMass(), redone.getMass(), 0.00001); + Assert.assertEquals(dto.getPartNo(), redone.getPartNo()); + + //Uncomment if you want to write the image to a file to view it. +// ImageIO.write(redone.getImage(), "png", new FileOutputStream("redone.png")); + } +} diff --git a/core/test/net/sf/openrocket/preset/xml/OpenRocketComponentSaverTest.java b/core/test/net/sf/openrocket/preset/xml/OpenRocketComponentSaverTest.java new file mode 100644 index 000000000..4325fae2e --- /dev/null +++ b/core/test/net/sf/openrocket/preset/xml/OpenRocketComponentSaverTest.java @@ -0,0 +1,112 @@ +package net.sf.openrocket.preset.xml; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** +* OpenRocketComponentSaver Tester. +* +*/ +public class OpenRocketComponentSaverTest { + + @Before + public void before() throws Exception { + } + + @After + public void after() throws Exception { + } + + /** + * + * Method: marshalToOpenRocketComponent(List theMaterialList, List thePresetList) + * + */ + @Test + public void testMarshalToOpenRocketComponent() throws Exception { + //TODO: Test goes here... + } + + /** + * + * Method: unmarshalFromOpenRocketComponent(Reader is) + * + */ + @Test + public void testUnmarshalFromOpenRocketComponent() throws Exception { + //TODO: Test goes here... + } + + /** + * + * Method: save(OutputStream dest, List theMaterialList, List thePresetList) + * + */ + @Test + public void testSave() throws Exception { + //TODO: Test goes here... + } + + + /** + * + * Method: fromOpenRocketComponent(Reader is) + * + */ + @Test + public void testFromOpenRocketComponent() throws Exception { + //TODO: Test goes here... +/* +try { + Method method = OpenRocketComponentSaver.getClass().getMethod("fromOpenRocketComponent", Reader.class); + method.setAccessible(true); + method.invoke(, ); +} catch(NoSuchMethodException e) { +} catch(IllegalAccessException e) { +} catch(InvocationTargetException e) { +} +*/ + } + + /** + * + * Method: toOpenRocketComponentDTO(List theMaterialList, List thePresetList) + * + */ + @Test + public void testToOpenRocketComponentDTO() throws Exception { + //TODO: Test goes here... +/* +try { + Method method = OpenRocketComponentSaver.getClass().getMethod("toOpenRocketComponentDTO", List.class, List.class); + method.setAccessible(true); + method.invoke(, ); +} catch(NoSuchMethodException e) { +} catch(IllegalAccessException e) { +} catch(InvocationTargetException e) { +} +*/ + } + + /** + * + * Method: toComponentDTO(ComponentPreset thePreset) + * + */ + @Test + public void testToComponentDTO() throws Exception { + //TODO: Test goes here... +/* +try { + Method method = OpenRocketComponentSaver.getClass().getMethod("toComponentDTO", ComponentPreset.class); + method.setAccessible(true); + method.invoke(, ); +} catch(NoSuchMethodException e) { +} catch(IllegalAccessException e) { +} catch(InvocationTargetException e) { +} +*/ + } + +}