5 package net.sf.openrocket.file.rocksim.importt;
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;
18 import java.util.HashMap;
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.
25 * Limitations: Rocksim flight simulations are not imported; tube fins are not supported; Rocksim 'pods' are not supported.
27 public class RocksimHandler extends ElementHandler {
30 * The main content handler.
32 private RocksimContentHandler handler = null;
35 * Return the OpenRocketDocument read from the file, or <code>null</code> if a document
36 * has not been read yet.
38 * @return the document read, or null.
40 public OpenRocketDocument getDocument() {
41 return handler.getDocument();
45 public ElementHandler openElement(String element, HashMap<String, String> attributes,
46 WarningSet warnings) {
48 // Check for unknown elements
49 if (!element.equals("RockSimDocument")) {
50 warnings.add(Warning.fromString("Unknown element " + element + ", ignoring."));
54 // Check for first call
55 if (handler != null) {
56 warnings.add(Warning.fromString("Multiple document elements found, ignoring later "
61 handler = new RocksimContentHandler();
68 * Handles the content of the <DesignInformation> tag.
70 class RocksimContentHandler extends ElementHandler {
72 * The OpenRocketDocument that is the container for the rocket.
74 private final OpenRocketDocument doc;
77 * The top-level component, from which all child components are added.
79 private final Rocket rocket;
82 * The rocksim file version.
84 private String version;
89 public RocksimContentHandler() {
90 this.rocket = new Rocket();
91 this.doc = new OpenRocketDocument(rocket);
95 * Get the OpenRocket document that has been created from parsing the Rocksim design file.
97 * @return the instantiated OpenRocketDocument
99 public OpenRocketDocument getDocument() {
104 public ElementHandler openElement(String element, HashMap<String, String> attributes,
105 WarningSet warnings) {
106 if (RocksimCommonConstants.DESIGN_INFORMATION.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.
111 if (RocksimCommonConstants.FILE_VERSION.equals(element)) {
112 return PlainTextHandler.INSTANCE;
114 if (RocksimCommonConstants.ROCKET_DESIGN.equals(element)) {
115 return new RocketDesignHandler(rocket);
121 public void closeElement(String element, HashMap<String, String> attributes,
122 String content, WarningSet warnings) throws SAXException {
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).
127 if (RocksimCommonConstants.FILE_VERSION.equals(element)) {
133 * Answer the file version.
135 * @return the version of the Rocksim design file
137 public String getVersion() {
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.
148 class RocketDesignHandler extends ElementHandler {
150 * The parent component.
152 private final RocketComponent component;
154 * The parsed stage count. Defaults to 1.
156 private int stageCount = 1;
158 * The overridden stage 1 mass.
160 private double stage1Mass = 0d;
162 * The overridden stage 2 mass.
164 private double stage2Mass = 0d;
166 * The overridden stage 3 mass.
168 private double stage3Mass = 0d;
170 * The overridden stage 1 Cg.
172 private double stage1CG = 0d;
174 * The overridden stage 2 Cg.
176 private double stage2CG = 0d;
178 * The overridden stage 3 Cg.
180 private double stage3CG = 0d;
185 * @param c the parent component
187 public RocketDesignHandler(RocketComponent c) {
192 public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
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.
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);
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);
209 component.addChild(stage);
210 return new StageHandler(stage);
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);
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);
225 component.addChild(stage);
226 return new StageHandler(stage);
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);
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);
242 component.addChild(stage);
243 return new StageHandler(stage);
246 if (RocksimCommonConstants.NAME.equals(element)) {
247 return PlainTextHandler.INSTANCE;
249 if ("StageCount".equals(element)) {
250 return PlainTextHandler.INSTANCE;
252 if ("Stage3Mass".equals(element)) {
253 return PlainTextHandler.INSTANCE;
255 if ("Stage2Mass".equals(element)) {
256 return PlainTextHandler.INSTANCE;
258 if ("Stage1Mass".equals(element)) {
259 return PlainTextHandler.INSTANCE;
261 if ("Stage3CG".equals(element)) {
262 return PlainTextHandler.INSTANCE;
264 if ("Stage2CGAlone".equals(element)) {
265 return PlainTextHandler.INSTANCE;
267 if ("Stage1CGAlone".equals(element)) {
268 return PlainTextHandler.INSTANCE;
274 public void closeElement(String element, HashMap<String, String> attributes,
275 String content, WarningSet warnings) throws SAXException {
277 if (RocksimCommonConstants.NAME.equals(element)) {
278 component.setName(content);
280 if ("StageCount".equals(element)) {
281 stageCount = Integer.parseInt(content);
283 if ("Stage3Mass".equals(element)) {
284 stage3Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
286 if ("Stage2Mass".equals(element)) {
287 stage2Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
289 if ("Stage1Mass".equals(element)) {
290 stage1Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
292 if ("Stage3CG".equals(element)) {
293 stage3CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
295 if ("Stage2CGAlone".equals(element)) {
296 stage2CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
298 if ("Stage1CGAlone".equals(element)) {
299 stage1CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
302 catch (NumberFormatException nfe) {
303 warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
310 * A SAX handler for a Rocksim stage.
312 class StageHandler extends ElementHandler {
314 * The parent OpenRocket component.
316 private final RocketComponent component;
321 * @param c the parent component
322 * @throws IllegalArgumentException thrown if <code>c</code> is null
324 public StageHandler(RocketComponent c) throws IllegalArgumentException {
326 throw new IllegalArgumentException("The stage component may not be null.");
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);
336 if (RocksimCommonConstants.BODY_TUBE.equals(element)) {
337 return new BodyTubeHandler(component, warnings);
339 if (RocksimCommonConstants.TRANSITION.equals(element)) {
340 return new TransitionHandler(component, warnings);