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.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;
19 import java.util.HashMap;
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.
26 * Limitations: Rocksim flight simulations are not imported; tube fins are not supported; Rocksim 'pods' are not supported.
28 public class RocksimHandler extends AbstractElementHandler {
31 * The main content handler.
33 private RocksimContentHandler handler = null;
36 * Return the OpenRocketDocument read from the file, or <code>null</code> if a document
37 * has not been read yet.
39 * @return the document read, or null.
41 public OpenRocketDocument getDocument() {
42 return handler.getDocument();
46 public ElementHandler openElement(String element, HashMap<String, String> attributes,
47 WarningSet warnings) {
49 // Check for unknown elements
50 if (!element.equals("RockSimDocument")) {
51 warnings.add(Warning.fromString("Unknown element " + element + ", ignoring."));
55 // Check for first call
56 if (handler != null) {
57 warnings.add(Warning.fromString("Multiple document elements found, ignoring later "
62 handler = new RocksimContentHandler();
69 * Handles the content of the <DesignInformation> tag.
71 class RocksimContentHandler extends AbstractElementHandler {
73 * The OpenRocketDocument that is the container for the rocket.
75 private final OpenRocketDocument doc;
78 * The top-level component, from which all child components are added.
80 private final Rocket rocket;
83 * The rocksim file version.
85 private String version;
90 public RocksimContentHandler() {
91 this.rocket = new Rocket();
92 this.doc = new OpenRocketDocument(rocket);
96 * Get the OpenRocket document that has been created from parsing the Rocksim design file.
98 * @return the instantiated OpenRocketDocument
100 public OpenRocketDocument getDocument() {
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.
112 if (RocksimCommonConstants.FILE_VERSION.equals(element)) {
113 return PlainTextHandler.INSTANCE;
115 if (RocksimCommonConstants.ROCKET_DESIGN.equals(element)) {
116 return new RocketDesignHandler(rocket);
122 public void closeElement(String element, HashMap<String, String> attributes,
123 String content, WarningSet warnings) throws SAXException {
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).
128 if (RocksimCommonConstants.FILE_VERSION.equals(element)) {
134 * Answer the file version.
136 * @return the version of the Rocksim design file
138 public String getVersion() {
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.
149 class RocketDesignHandler extends AbstractElementHandler {
151 * The parent component.
153 private final RocketComponent component;
155 * The parsed stage count. Defaults to 1.
157 private int stageCount = 1;
159 * The overridden stage 1 mass.
161 private double stage1Mass = 0d;
163 * The overridden stage 2 mass.
165 private double stage2Mass = 0d;
167 * The overridden stage 3 mass.
169 private double stage3Mass = 0d;
171 * The overridden stage 1 Cg.
173 private double stage1CG = 0d;
175 * The overridden stage 2 Cg.
177 private double stage2CG = 0d;
179 * The overridden stage 3 Cg.
181 private double stage3CG = 0d;
186 * @param c the parent component
188 public RocketDesignHandler(RocketComponent c) {
193 public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
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.
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);
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);
210 component.addChild(stage);
211 return new StageHandler(stage);
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);
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);
226 component.addChild(stage);
227 return new StageHandler(stage);
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);
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);
243 component.addChild(stage);
244 return new StageHandler(stage);
247 if (RocksimCommonConstants.NAME.equals(element)) {
248 return PlainTextHandler.INSTANCE;
250 if ("StageCount".equals(element)) {
251 return PlainTextHandler.INSTANCE;
253 if ("Stage3Mass".equals(element)) {
254 return PlainTextHandler.INSTANCE;
256 if ("Stage2Mass".equals(element)) {
257 return PlainTextHandler.INSTANCE;
259 if ("Stage1Mass".equals(element)) {
260 return PlainTextHandler.INSTANCE;
262 if ("Stage3CG".equals(element)) {
263 return PlainTextHandler.INSTANCE;
265 if ("Stage2CGAlone".equals(element)) {
266 return PlainTextHandler.INSTANCE;
268 if ("Stage1CGAlone".equals(element)) {
269 return PlainTextHandler.INSTANCE;
275 public void closeElement(String element, HashMap<String, String> attributes,
276 String content, WarningSet warnings) throws SAXException {
278 if (RocksimCommonConstants.NAME.equals(element)) {
279 component.setName(content);
281 if ("StageCount".equals(element)) {
282 stageCount = Integer.parseInt(content);
284 if ("Stage3Mass".equals(element)) {
285 stage3Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
287 if ("Stage2Mass".equals(element)) {
288 stage2Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
290 if ("Stage1Mass".equals(element)) {
291 stage1Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS;
293 if ("Stage3CG".equals(element)) {
294 stage3CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
296 if ("Stage2CGAlone".equals(element)) {
297 stage2CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
299 if ("Stage1CGAlone".equals(element)) {
300 stage1CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
303 catch (NumberFormatException nfe) {
304 warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
311 * A SAX handler for a Rocksim stage.
313 class StageHandler extends AbstractElementHandler {
315 * The parent OpenRocket component.
317 private final RocketComponent component;
322 * @param c the parent component
323 * @throws IllegalArgumentException thrown if <code>c</code> is null
325 public StageHandler(RocketComponent c) throws IllegalArgumentException {
327 throw new IllegalArgumentException("The stage component may not be null.");
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);
337 if (RocksimCommonConstants.BODY_TUBE.equals(element)) {
338 return new BodyTubeHandler(component, warnings);
340 if (RocksimCommonConstants.TRANSITION.equals(element)) {
341 return new TransitionHandler(component, warnings);