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