Merge commit '42b2e5ca519766e37ce6941ba4faecc9691cc403' into upstream
[debian/openrocket] / core / src / net / sf / openrocket / preset / xml / OpenRocketComponentSaver.java
diff --git a/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java b/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java
new file mode 100644 (file)
index 0000000..934d4be
--- /dev/null
@@ -0,0 +1,206 @@
+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;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * The active manager class that is the entry point for reading and writing *.orc files.
+ */
+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);
+        }
+    }
+
+    public boolean save(File file, List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
+                                                                                                     JAXBException,
+                                                                                                     IOException {
+        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
+        writer.write(marshalToOpenRocketComponent(theMaterialList, thePresetList));
+        writer.flush();
+        writer.close();
+        return true;
+    }
+
+    /**
+     * This method marshals a list of materials and ComponentPresets into an .orc formatted XML string.
+     *
+     * @param theMaterialList the list of materials to be included
+     * @param thePresetList   the list of presets to be included
+     *
+     * @return ORC-compliant XML
+     *
+     * @throws JAXBException
+     */
+    public String marshalToOpenRocketComponent(List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
+                                                                                                                    JAXBException {
+        /** 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();
+
+        // We're going to sort the initial data since that makes the output much easier on the eyes.
+
+        Collections.sort(theMaterialList, new Comparator<Material>() {
+
+                       @Override
+                       public int compare(Material o1, Material o2) {
+                               return o1.getName().compareTo( o2.getName() );
+                       }
+
+        });
+
+        Collections.sort(thePresetList, new Comparator<ComponentPreset>() {
+
+                       @Override
+                       public int compare(ComponentPreset o1, ComponentPreset o2) {
+                               int manucmp = o1.getManufacturer().getSimpleName().compareTo( o2.getManufacturer().getSimpleName() );
+
+                               if ( manucmp != 0 ) {
+                                       return manucmp;
+                               }
+
+                               return o1.getPartNo().compareTo( o2.getPartNo());
+                       }
+
+        });
+
+        marshaller.marshal(toOpenRocketComponentDTO(theMaterialList, thePresetList), sw);
+        return sw.toString();
+
+    }
+
+    /**
+     * This method unmarshals from a Reader that is presumed to be open on an XML file in .orc format.
+     *
+     * @param is an open reader; StringBufferInputStream could not be used because it's deprecated and does not handle
+     *           UTF characters correctly
+     *
+     * @return a list of ComponentPresets
+     *
+     * @throws InvalidComponentPresetException
+     *
+     */
+    public OpenRocketComponentDTO unmarshalFromOpenRocketComponent(Reader is) throws JAXBException,
+                                                                                    InvalidComponentPresetException {
+        return fromOpenRocketComponent(is);
+    }
+
+    /**
+     * Write an XML representation of a list of presets.
+     *
+     * @param dest            the stream to write the data to
+     * @param theMaterialList the list of materials to be included
+     * @param thePresetList   the list of presets to be included
+     *
+     * @throws JAXBException
+     * @throws IOException   thrown if the stream could not be written
+     */
+    public void save(OutputStream dest, List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
+                                                                                                             IOException,
+                                                                                                             JAXBException {
+        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(dest, "UTF-8"));
+        writer.write(marshalToOpenRocketComponent(theMaterialList, thePresetList));
+        writer.flush();
+        writer.close();
+    }
+
+    /**
+     * Read from an open Reader instance XML in .orc format and reconstruct an OpenRocketComponentDTO instance.
+     *
+     * @param is an open Reader; assumed to be opened on a file of XML in .orc format
+     *
+     * @return the OpenRocketComponentDTO that is a POJO representation of the XML; null if the data could not be read
+     *         or was in an invalid format
+     */
+    private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException {
+        /** The context is thread-safe, but unmarshallers are not.  Create a local one. */
+        Unmarshaller unmarshaller = context.createUnmarshaller();
+        return (OpenRocketComponentDTO) unmarshaller.unmarshal(is); //new StreamSource(is));
+    }
+
+    /**
+     * Root conversion method.  It iterates over all subcomponents.
+     *
+     * @return a corresponding ORC representation
+     */
+    private OpenRocketComponentDTO toOpenRocketComponentDTO(List<Material> theMaterialList, List<ComponentPreset> thePresetList) {
+        OpenRocketComponentDTO rsd = new OpenRocketComponentDTO();
+
+        if (theMaterialList != null) {
+            for (Material material : theMaterialList) {
+                rsd.addMaterial(new MaterialDTO(material));
+            }
+        }
+
+        if (thePresetList != null) {
+            for (ComponentPreset componentPreset : thePresetList) {
+                rsd.addComponent(toComponentDTO(componentPreset));
+            }
+        }
+        return rsd;
+    }
+
+    /**
+     * Factory method that maps a preset to the corresponding DTO handler.
+     *
+     * @param thePreset the preset for which a handler will be found
+     *
+     * @return a subclass of BaseComponentDTO that can be used for marshalling/unmarshalling a preset; null if not found
+     *         for the preset type
+     */
+    private static BaseComponentDTO toComponentDTO(ComponentPreset thePreset) {
+        switch (thePreset.getType()) {
+            case BODY_TUBE:
+                return new BodyTubeDTO(thePreset);
+            case TUBE_COUPLER:
+                return new TubeCouplerDTO(thePreset);
+            case NOSE_CONE:
+                return new NoseConeDTO(thePreset);
+            case TRANSITION:
+                return new TransitionDTO(thePreset);
+            case BULK_HEAD:
+                return new BulkHeadDTO(thePreset);
+            case CENTERING_RING:
+                return new CenteringRingDTO(thePreset);
+            case ENGINE_BLOCK:
+                return new EngineBlockDTO(thePreset);
+            case LAUNCH_LUG:
+                return new LaunchLugDTO(thePreset);
+            case STREAMER:
+                return new StreamerDTO(thePreset);
+            case PARACHUTE:
+                return new ParachuteDTO(thePreset);
+        }
+
+        return null;
+    }
+}