Sort the collections prior to serialization since that makes the output a little...
[debian/openrocket] / core / src / net / sf / openrocket / preset / xml / OpenRocketComponentSaver.java
1 package net.sf.openrocket.preset.xml;
2
3 import java.io.BufferedWriter;
4 import java.io.IOException;
5 import java.io.OutputStream;
6 import java.io.OutputStreamWriter;
7 import java.io.Reader;
8 import java.io.StringWriter;
9 import java.util.Collections;
10 import java.util.Comparator;
11 import java.util.List;
12
13 import javax.xml.bind.JAXBContext;
14 import javax.xml.bind.JAXBException;
15 import javax.xml.bind.Marshaller;
16 import javax.xml.bind.Unmarshaller;
17
18 import net.sf.openrocket.material.Material;
19 import net.sf.openrocket.preset.ComponentPreset;
20 import net.sf.openrocket.preset.InvalidComponentPresetException;
21 import net.sf.openrocket.startup.Application;
22
23 /**
24  * The active manager class that is the entry point for reading and writing *.orc files.
25  */
26 public class OpenRocketComponentSaver {
27
28     /**
29      * The JAXBContext.  JAXBContext is thread-safe.
30      */
31     private static JAXBContext context = null;
32
33     static {
34         try {
35             context = JAXBContext.newInstance(OpenRocketComponentDTO.class);
36         }
37         catch (JAXBException jaxb) {
38             Application.getLogger().error("Unable to create JAXBContext for loading of *.orc files.", jaxb);
39         }
40     }
41
42     /**
43      * This method marshals a list of materials and ComponentPresets into an .orc formatted XML string.
44      *
45      * @param theMaterialList the list of materials to be included
46      * @param thePresetList   the list of presets to be included
47      *
48      * @return ORC-compliant XML
49      *
50      * @throws JAXBException
51      */
52     public String marshalToOpenRocketComponent(List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
53                                                                                                                     JAXBException {
54         /** The context is thread-safe, but marshallers are not.  Create a local one. */
55         Marshaller marshaller = context.createMarshaller();
56         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
57         StringWriter sw = new StringWriter();
58
59         // We're going to sort the initial data since that makes the output much easier on the eyes.
60         
61         Collections.sort(theMaterialList, new Comparator<Material>() {
62
63                         @Override
64                         public int compare(Material o1, Material o2) {
65                                 return o1.getName().compareTo( o2.getName() );
66                         }
67                 
68         });
69         
70         Collections.sort(thePresetList, new Comparator<ComponentPreset>() {
71
72                         @Override
73                         public int compare(ComponentPreset o1, ComponentPreset o2) {
74                                 int manucmp = o1.getManufacturer().getSimpleName().compareTo( o2.getManufacturer().getSimpleName() );
75                                 
76                                 if ( manucmp != 0 ) {
77                                         return manucmp;
78                                 }
79                                 
80                                 return o1.getPartNo().compareTo( o2.getPartNo());
81                         }
82                 
83         });
84         
85         marshaller.marshal(toOpenRocketComponentDTO(theMaterialList, thePresetList), sw);
86         return sw.toString();
87
88     }
89
90     /**
91      * This method unmarshals from a Reader that is presumed to be open on an XML file in .orc format.
92      *
93      * @param is an open reader; StringBufferInputStream could not be used because it's deprecated and does not handle UTF
94      *           characters correctly
95      *
96      * @return a list of ComponentPresets
97      *
98      * @throws InvalidComponentPresetException
99      *
100      */
101     public List<ComponentPreset> unmarshalFromOpenRocketComponent(Reader is) throws JAXBException,
102                                                                                     InvalidComponentPresetException {
103         return fromOpenRocketComponent(is).asComponentPresets();
104     }
105
106     /**
107      * Write an XML representation of a list of presets.
108      *
109      * @param dest            the stream to write the data to
110      * @param theMaterialList the list of materials to be included
111      * @param thePresetList   the list of presets to be included
112      *
113      * @throws JAXBException
114      * @throws IOException   thrown if the stream could not be written
115      */
116     public void save(OutputStream dest, List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
117                                                                                                              IOException,
118                                                                                                              JAXBException {
119         BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(dest, "UTF-8"));
120         writer.write(marshalToOpenRocketComponent(theMaterialList, thePresetList));
121         writer.flush();
122         writer.close();
123     }
124
125     /**
126      * Read from an open Reader instance XML in .orc format and reconstruct an OpenRocketComponentDTO instance.
127      *
128      * @param is an open Reader; assumed to be opened on a file of XML in .orc format
129      *
130      * @return the OpenRocketComponentDTO that is a POJO representation of the XML; null if the data could not be read or
131      *         was in an invalid format
132      */
133     private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException {
134         /** The context is thread-safe, but unmarshallers are not.  Create a local one. */
135         Unmarshaller unmarshaller = context.createUnmarshaller();
136         return (OpenRocketComponentDTO) unmarshaller.unmarshal(is);
137     }
138
139     /**
140      * Root conversion method.  It iterates over all subcomponents.
141      *
142      * @return a corresponding ORC representation
143      */
144     private OpenRocketComponentDTO toOpenRocketComponentDTO(List<Material> theMaterialList, List<ComponentPreset> thePresetList) {
145         OpenRocketComponentDTO rsd = new OpenRocketComponentDTO();
146
147         if (theMaterialList != null) {
148             for (Material material : theMaterialList) {
149                 rsd.addMaterial(new MaterialDTO(material));
150             }
151         }
152
153         if (thePresetList != null) {
154             for (ComponentPreset componentPreset : thePresetList) {
155                 rsd.addComponent(toComponentDTO(componentPreset));
156             }
157         }
158         return rsd;
159     }
160
161     /**
162      * Factory method that maps a preset to the corresponding DTO handler.
163      *
164      * @param thePreset the preset for which a handler will be found
165      *
166      * @return a subclass of BaseComponentDTO that can be used for marshalling/unmarshalling a preset; null if not found
167      *         for the preset type
168      */
169     private static BaseComponentDTO toComponentDTO(ComponentPreset thePreset) {
170         switch (thePreset.getType()) {
171             case BODY_TUBE:
172                 return new BodyTubeDTO(thePreset);
173             case TUBE_COUPLER:
174                 return new TubeCouplerDTO(thePreset);
175             case NOSE_CONE:
176                 return new NoseConeDTO(thePreset);
177             case TRANSITION:
178                 return new TransitionDTO(thePreset);
179             case BULK_HEAD:
180                 return new BulkHeadDTO(thePreset);
181             case CENTERING_RING:
182                 return new CenteringRingDTO(thePreset);
183             case ENGINE_BLOCK:
184                 return new EngineBlockDTO(thePreset);
185             case LAUNCH_LUG:
186                 return new LaunchLugDTO(thePreset);
187             case STREAMER:
188                 return new StreamerDTO(thePreset);
189             case PARACHUTE:
190                 return new ParachuteDTO(thePreset);
191         }
192
193         return null;
194     }
195 }