--- /dev/null
+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;
+ }
+}