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