Support for base64 images in .orc; performance improvement to the JAXBContext
authorrodinia814 <rodinia814@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 28 Apr 2012 03:16:42 +0000 (03:16 +0000)
committerrodinia814 <rodinia814@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 28 Apr 2012 03:16:42 +0000 (03:16 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@633 180e2498-e6e9-4542-8430-84ac67f01cd8

core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java
core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java
core/test/net/sf/openrocket/preset/xml/BaseComponentDTOTest.java [new file with mode: 0644]
core/test/net/sf/openrocket/preset/xml/OpenRocketComponentSaverTest.java [new file with mode: 0644]

index d107abf5735109bb325a202c07de20c2221e50ab..6be6ebd9618dd8dedb32c4bd46b265037a1804a5 100644 (file)
@@ -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<MaterialDTO> 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<MaterialDTO> materials) throws InvalidComponentPresetException;
 
        void addProps(TypedPropertyMap props, List<MaterialDTO> 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<String, byte[]> {
+        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);
+        }
+    }
 }
index ecfaba5f6ce41309676a7cc6be4928a0d58f0bee..add220a692e6059b52c9d4a9de25f7027e9b477e 100644 (file)
@@ -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<Material> theMaterialList, List<ComponentPreset> 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 (file)
index 0000000..0096810
--- /dev/null
@@ -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 (file)
index 0000000..4325fae
--- /dev/null
@@ -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<Material> theMaterialList, List<ComponentPreset> 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<Material> theMaterialList, List<ComponentPreset> 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(<Object>, <Parameters>);
+} catch(NoSuchMethodException e) {
+} catch(IllegalAccessException e) {
+} catch(InvocationTargetException e) {
+}
+*/
+    }
+
+    /**
+     *
+     * Method: toOpenRocketComponentDTO(List<Material> theMaterialList, List<ComponentPreset> thePresetList)
+     *
+     */
+    @Test
+    public void testToOpenRocketComponentDTO() throws Exception {
+        //TODO: Test goes here...
+/*
+try {
+   Method method = OpenRocketComponentSaver.getClass().getMethod("toOpenRocketComponentDTO", List<Material>.class, List<ComponentPreset>.class);
+   method.setAccessible(true);
+   method.invoke(<Object>, <Parameters>);
+} 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(<Object>, <Parameters>);
+} catch(NoSuchMethodException e) {
+} catch(IllegalAccessException e) {
+} catch(InvocationTargetException e) {
+}
+*/
+    }
+
+}