DGP - support for clusters(import and export); added thickness to tube coupler; clean...
[debian/openrocket] / core / src / net / sf / openrocket / file / rocksim / importt / RocksimHandler.java
1 /*
2  * RocksimHandler.java
3  *
4  */
5 package net.sf.openrocket.file.rocksim.importt;
6
7 import net.sf.openrocket.aerodynamics.Warning;
8 import net.sf.openrocket.aerodynamics.WarningSet;
9 import net.sf.openrocket.document.OpenRocketDocument;
10 import net.sf.openrocket.file.rocksim.RocksimCommonConstants;
11 import net.sf.openrocket.file.simplesax.ElementHandler;
12 import net.sf.openrocket.file.simplesax.PlainTextHandler;
13 import net.sf.openrocket.rocketcomponent.Rocket;
14 import net.sf.openrocket.rocketcomponent.RocketComponent;
15 import net.sf.openrocket.rocketcomponent.Stage;
16 import org.xml.sax.SAXException;
17
18 import java.util.HashMap;
19
20 /**
21  * This class is a Sax element handler for Rocksim version 9 design files.  It parses the Rocksim file (typically
22  * a .rkt extension) and creates corresponding OpenRocket components.  This is a best effort approach and may not
23  * be an exact replica.
24  * <p/>
25  * Limitations: Rocksim flight simulations are not imported; tube fins are not supported; Rocksim 'pods' are not supported.
26  */
27 public class RocksimHandler extends ElementHandler {
28
29     /**
30      * The main content handler.
31      */
32     private RocksimContentHandler handler = null;
33
34     /**
35      * Return the OpenRocketDocument read from the file, or <code>null</code> if a document
36      * has not been read yet.
37      *
38      * @return the document read, or null.
39      */
40     public OpenRocketDocument getDocument() {
41         return handler.getDocument();
42     }
43
44     @Override
45     public ElementHandler openElement(String element, HashMap<String, String> attributes,
46                                       WarningSet warnings) {
47
48         // Check for unknown elements
49         if (!element.equals("RockSimDocument")) {
50             warnings.add(Warning.fromString("Unknown element " + element + ", ignoring."));
51             return null;
52         }
53
54         // Check for first call
55         if (handler != null) {
56             warnings.add(Warning.fromString("Multiple document elements found, ignoring later "
57                                             + "ones."));
58             return null;
59         }
60
61         handler = new RocksimContentHandler();
62         return handler;
63     }
64
65 }
66
67 /**
68  * Handles the content of the <DesignInformation> tag.
69  */
70 class RocksimContentHandler extends ElementHandler {
71     /**
72      * The OpenRocketDocument that is the container for the rocket.
73      */
74     private final OpenRocketDocument doc;
75
76     /**
77      * The top-level component, from which all child components are added.
78      */
79     private final Rocket rocket;
80
81     /**
82      * The rocksim file version.
83      */
84     private String version;
85
86     /**
87      * Constructor.
88      */
89     public RocksimContentHandler() {
90         this.rocket = new Rocket();
91         this.doc = new OpenRocketDocument(rocket);
92     }
93
94     /**
95      * Get the OpenRocket document that has been created from parsing the Rocksim design file.
96      *
97      * @return the instantiated OpenRocketDocument
98      */
99     public OpenRocketDocument getDocument() {
100         return doc;
101     }
102
103     @Override
104     public ElementHandler openElement(String element, HashMap<String, String> attributes,
105                                       WarningSet warnings) {
106         if ("DesignInformation".equals(element)) {
107             //The next sub-element is "RocketDesign", which is really the only thing that matters.  Rather than
108             //create another handler just for that element, handle it here.
109             return this;
110         }
111         if ("FileVersion".equals(element)) {
112             return PlainTextHandler.INSTANCE;
113         }
114         if ("RocketDesign".equals(element)) {
115             return new RocketDesignHandler(rocket);
116         }
117         return null;
118     }
119
120     @Override
121     public void closeElement(String element, HashMap<String, String> attributes,
122                              String content, WarningSet warnings) throws SAXException {
123         /**
124          * SAX handler for Rocksim file version number.  The value is not used currently, but could be used in the future
125          * for backward/forward compatibility reasons (different lower level handlers could be called via a strategy pattern).
126          */
127         if ("FileVersion".equals(element)) {
128             version = content;
129         }
130     }
131
132     /**
133      * Answer the file version.
134      *
135      * @return the version of the Rocksim design file
136      */
137     public String getVersion() {
138         return version;
139     }
140 }
141
142
143 /**
144  * A SAX handler for the high level Rocksim design.  This structure includes sub-structures for each of the stages.
145  * Correct functioning of this handler is predicated on the stage count element appearing before the actual stage parts
146  * structures.  If that invariant is not true, then behavior will be unpredictable.
147  */
148 class RocketDesignHandler extends ElementHandler {
149     /**
150      * The parent component.
151      */
152     private final RocketComponent component;
153     /**
154      * The parsed stage count.  Defaults to 1.
155      */
156     private int stageCount = 1;
157     /**
158      * The overridden stage 1 mass.
159      */
160     private double stage1Mass = 0d;
161     /**
162      * The overridden stage 2 mass.
163      */
164     private double stage2Mass = 0d;
165     /**
166      * The overridden stage 3 mass.
167      */
168     private double stage3Mass = 0d;
169     /**
170      * The overridden stage 1 Cg.
171      */
172     private double stage1CG = 0d;
173     /**
174      * The overridden stage 2 Cg.
175      */
176     private double stage2CG = 0d;
177     /**
178      * The overridden stage 3 Cg.
179      */
180     private double stage3CG = 0d;
181
182     /**
183      * Constructor.
184      *
185      * @param c the parent component
186      */
187     public RocketDesignHandler(RocketComponent c) {
188         component = c;
189     }
190
191     @Override
192     public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
193         /**
194          * In Rocksim stages are from the top down, so a single stage rocket is actually stage '3'.  A 2-stage
195          * rocket defines stage '2' as the initial booster with stage '3' sitting atop it.  And so on.
196          */
197         if ("Stage3Parts".equals(element)) {
198             final Stage stage = new Stage();
199             if (stage3Mass > 0.0d) {
200                 stage.setMassOverridden(true);
201                 stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
202                 stage.setOverrideMass(stage3Mass);
203             }
204             if (stage3CG > 0.0d) {
205                 stage.setCGOverridden(true);
206                 stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
207                 stage.setOverrideCGX(stage3CG);
208             }
209             component.addChild(stage);
210             return new StageHandler(stage);
211         }
212         if ("Stage2Parts".equals(element)) {
213             if (stageCount >= 2) {
214                 final Stage stage = new Stage();
215                 if (stage2Mass > 0.0d) {
216                     stage.setMassOverridden(true);
217                     stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
218                     stage.setOverrideMass(stage2Mass);
219                 }
220                 if (stage2CG > 0.0d) {
221                     stage.setCGOverridden(true);
222                     stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
223                     stage.setOverrideCGX(stage2CG);
224                 }
225                 component.addChild(stage);
226                 return new StageHandler(stage);
227             }
228         }
229         if ("Stage1Parts".equals(element)) {
230             if (stageCount == 3) {
231                 final Stage stage = new Stage();
232                 if (stage1Mass > 0.0d) {
233                     stage.setMassOverridden(true);
234                     stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
235                     stage.setOverrideMass(stage1Mass);
236                 }
237                 if (stage1CG > 0.0d) {
238                     stage.setCGOverridden(true);
239                     stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
240                     stage.setOverrideCGX(stage1CG);
241                 }
242                 component.addChild(stage);
243                 return new StageHandler(stage);
244             }
245         }
246         if ("Name".equals(element)) {
247             return PlainTextHandler.INSTANCE;
248         }
249         if ("StageCount".equals(element)) {
250             return PlainTextHandler.INSTANCE;
251         }
252         if ("Stage3Mass".equals(element)) {
253             return PlainTextHandler.INSTANCE;
254         }
255         if ("Stage2Mass".equals(element)) {
256             return PlainTextHandler.INSTANCE;
257         }
258         if ("Stage1Mass".equals(element)) {
259             return PlainTextHandler.INSTANCE;
260         }
261         if ("Stage3CG".equals(element)) {
262             return PlainTextHandler.INSTANCE;
263         }
264         if ("Stage2CGAlone".equals(element)) {
265             return PlainTextHandler.INSTANCE;
266         }
267         if ("Stage1CGAlone".equals(element)) {
268             return PlainTextHandler.INSTANCE;
269         }
270         return null;
271     }
272
273     @Override
274     public void closeElement(String element, HashMap<String, String> attributes,
275                              String content, WarningSet warnings) throws SAXException {
276         try {
277             if ("Name".equals(element)) {
278                 component.setName(content);
279             }
280             if ("StageCount".equals(element)) {
281                 stageCount = Integer.parseInt(content);
282             }
283             if ("Stage3Mass".equals(element)) {
284                 stage3Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
285             }
286             if ("Stage2Mass".equals(element)) {
287                 stage2Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
288             }
289             if ("Stage1Mass".equals(element)) {
290                 stage1Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
291             }
292             if ("Stage3CG".equals(element)) {
293                 stage3CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
294             }
295             if ("Stage2CGAlone".equals(element)) {
296                 stage2CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
297             }
298             if ("Stage1CGAlone".equals(element)) {
299                 stage1CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
300             }
301         }
302         catch (NumberFormatException nfe) {
303             warnings.add("Could not convert " + element + " value of " + content + ".  It is expected to be a number.");
304         }
305     }
306
307 }
308
309 /**
310  * A SAX handler for a Rocksim stage.
311  */
312 class StageHandler extends ElementHandler {
313     /**
314      * The parent OpenRocket component.
315      */
316     private final RocketComponent component;
317
318     /**
319      * Constructor.
320      *
321      * @param c the parent component
322      * @throws IllegalArgumentException thrown if <code>c</code> is null
323      */
324     public StageHandler(RocketComponent c) throws IllegalArgumentException {
325         if (c == null) {
326             throw new IllegalArgumentException("The stage component may not be null.");
327         }
328         component = c;
329     }
330
331     @Override
332     public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
333         if (RocksimCommonConstants.NOSE_CONE.equals(element)) {
334             return new NoseConeHandler(component, warnings);
335         }
336         if (RocksimCommonConstants.BODY_TUBE.equals(element)) {
337             return new BodyTubeHandler(component, warnings);
338         }
339         if (RocksimCommonConstants.TRANSITION.equals(element)) {
340             return new TransitionHandler(component, warnings);
341         }
342         return null;
343     }
344 }