From f161a2dca6c139588c92dca42ab17a68441360ab Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Wed, 24 Oct 2012 21:43:21 -0500 Subject: [PATCH] Refactor OpenRocketLoader moving all the internal classes to their own files. --- .../openrocket/importt/AppearanceHandler.java | 103 + .../openrocket/importt/AtmosphereHandler.java | 75 + .../openrocket/importt/BooleanSetter.java | 31 + .../importt/ClusterConfigurationSetter.java | 36 + .../file/openrocket/importt/ColorSetter.java | 54 + .../openrocket/importt/ComponentHandler.java | 55 + .../importt/ComponentParameterHandler.java | 114 + .../importt/ComponentPresetSetter.java | 74 + .../importt/CustomExpressionHandler.java | 57 + .../openrocket/importt/DatatypeHandler.java | 49 + .../DeploymentConfigurationHandler.java | 62 + .../openrocket/importt/DocumentConfig.java | 425 +++ .../file/openrocket/importt/DoubleSetter.java | 78 + .../file/openrocket/importt/EnumSetter.java | 32 + .../importt/FinSetPointHandler.java | 70 + .../importt/FinTabPositionSetter.java | 46 + .../importt/FlightDataBranchHandler.java | 153 + .../openrocket/importt/FlightDataHandler.java | 137 + .../file/openrocket/importt/IntSetter.java | 28 + .../openrocket/importt/MaterialSetter.java | 73 + .../importt/MotorConfigurationHandler.java | 70 + .../file/openrocket/importt/MotorHandler.java | 174 ++ .../openrocket/importt/MotorMountHandler.java | 111 + .../importt/OpenRocketContentHandler.java | 75 + .../openrocket/importt/OpenRocketHandler.java | 104 + .../openrocket/importt/OpenRocketLoader.java | 2457 ----------------- .../openrocket/importt/OverrideSetter.java | 31 + .../openrocket/importt/PositionSetter.java | 48 + .../file/openrocket/importt/Setter.java | 20 + .../importt/SimulationConditionsHandler.java | 122 + .../importt/SimulationsHandler.java | 48 + .../importt/SingleSimulationHandler.java | 111 + .../StageSeparationConfigurationHandler.java | 59 + .../file/openrocket/importt/StringSetter.java | 22 + 34 files changed, 2747 insertions(+), 2457 deletions(-) create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/AtmosphereHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/BooleanSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/ClusterConfigurationSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/ColorSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/ComponentHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/ComponentPresetSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/CustomExpressionHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/DatatypeHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/DeploymentConfigurationHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/DoubleSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/EnumSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/FinSetPointHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/FinTabPositionSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/FlightDataBranchHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/IntSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/MaterialSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/MotorConfigurationHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/MotorHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/MotorMountHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketContentHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/OverrideSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/Setter.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/SimulationConditionsHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/SimulationsHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/StageSeparationConfigurationHandler.java create mode 100644 core/src/net/sf/openrocket/file/openrocket/importt/StringSetter.java diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java new file mode 100644 index 000000000..caaa561d3 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java @@ -0,0 +1,103 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.appearance.AppearanceBuilder; +import net.sf.openrocket.appearance.Decal.EdgeMode; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; + +import org.xml.sax.SAXException; + +class AppearanceHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final RocketComponent component; + + private final AppearanceBuilder builder = new AppearanceBuilder(); + private boolean isInDecal = false; + public AppearanceHandler( RocketComponent component, DocumentLoadingContext context ) { + this.context = context; + this.component = component; + } + @Override + public ElementHandler openElement(String element,HashMap attributes, WarningSet warnings) + throws SAXException { + if ( "decal".equals(element) ) { + String name = attributes.remove("name"); + builder.setImage(name); + double rotation = Double.parseDouble(attributes.remove("rotation")); + builder.setRotation(rotation); + String edgeModeName = attributes.remove("edgemode"); + EdgeMode edgeMode = EdgeMode.valueOf(edgeModeName); + builder.setEdgeMode(edgeMode); + isInDecal = true; + return this; + } + return PlainTextHandler.INSTANCE; + } + @Override + public void closeElement(String element,HashMap attributes, String content, WarningSet warnings) throws SAXException { + if ( "ambient".equals(element) ) { + int red = Integer.parseInt(attributes.get("red")); + int green = Integer.parseInt(attributes.get("green")); + int blue = Integer.parseInt(attributes.get("blue")); + builder.setAmbient( new Color(red,green,blue)); + return; + } + if ( "diffuse".equals(element) ) { + int red = Integer.parseInt(attributes.get("red")); + int green = Integer.parseInt(attributes.get("green")); + int blue = Integer.parseInt(attributes.get("blue")); + builder.setDiffuse( new Color(red,green,blue)); + return; + } + if ( "specular".equals(element) ) { + int red = Integer.parseInt(attributes.get("red")); + int green = Integer.parseInt(attributes.get("green")); + int blue = Integer.parseInt(attributes.get("blue")); + builder.setSpecular( new Color(red,green,blue)); + return; + } + if ( isInDecal && "center".equals(element) ) { + double x = Double.parseDouble(attributes.get("x")); + double y = Double.parseDouble(attributes.get("y")); + builder.setCenter(x,y); + return; + } + if ( isInDecal && "offset".equals(element) ) { + double x = Double.parseDouble(attributes.get("x")); + double y = Double.parseDouble(attributes.get("y")); + builder.setOffset(x,y); + return; + } + if ( isInDecal && "scale".equals(element) ) { + double x = Double.parseDouble(attributes.get("x")); + double y = Double.parseDouble(attributes.get("y")); + builder.setScale(x,y); + return; + } + if( isInDecal && "decal".equals(element) ) { + isInDecal = false; + return; + } + + super.closeElement(element, attributes, content, warnings); + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + if ( "decal".equals(element) ) { + isInDecal = false; + return; + } + component.setAppearance(builder.getAppearance()); + super.endHandler(element, attributes, content, warnings); + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/AtmosphereHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/AtmosphereHandler.java new file mode 100644 index 000000000..91f1f252b --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/AtmosphereHandler.java @@ -0,0 +1,75 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.simulation.SimulationOptions; + +import org.xml.sax.SAXException; + +class AtmosphereHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final String model; + private double temperature = Double.NaN; + private double pressure = Double.NaN; + + public AtmosphereHandler(String model, DocumentLoadingContext context) { + this.model = model; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + double d = Double.NaN; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException ignore) { + } + + if (element.equals("basetemperature")) { + if (Double.isNaN(d)) { + warnings.add("Illegal base temperature specified, ignoring."); + } + temperature = d; + } else if (element.equals("basepressure")) { + if (Double.isNaN(d)) { + warnings.add("Illegal base pressure specified, ignoring."); + } + pressure = d; + } else { + super.closeElement(element, attributes, content, warnings); + } + } + + + public void storeSettings(SimulationOptions cond, WarningSet warnings) { + if (!Double.isNaN(pressure)) { + cond.setLaunchPressure(pressure); + } + if (!Double.isNaN(temperature)) { + cond.setLaunchTemperature(temperature); + } + + if ("isa".equals(model)) { + cond.setISAAtmosphere(true); + } else if ("extendedisa".equals(model)) { + cond.setISAAtmosphere(false); + } else { + cond.setISAAtmosphere(true); + warnings.add("Unknown atmospheric model, using ISA."); + } + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/BooleanSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/BooleanSetter.java new file mode 100644 index 000000000..b0aef58c0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/BooleanSetter.java @@ -0,0 +1,31 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// BooleanSetter - set a boolean value +class BooleanSetter implements Setter { + private final Reflection.Method setMethod; + + public BooleanSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + s = s.trim(); + if (s.equalsIgnoreCase("true")) { + setMethod.invoke(c, true); + } else if (s.equalsIgnoreCase("false")) { + setMethod.invoke(c, false); + } else { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ClusterConfigurationSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/ClusterConfigurationSetter.java new file mode 100644 index 000000000..4fce5c064 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ClusterConfigurationSetter.java @@ -0,0 +1,36 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.ClusterConfiguration; +import net.sf.openrocket.rocketcomponent.Clusterable; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +class ClusterConfigurationSetter implements Setter { + + @Override + public void set(RocketComponent component, String value, HashMap attributes, + WarningSet warnings) { + + if (!(component instanceof Clusterable)) { + warnings.add("Illegal component defined as cluster."); + return; + } + + ClusterConfiguration config = null; + for (ClusterConfiguration c : ClusterConfiguration.CONFIGURATIONS) { + if (c.getXMLName().equals(value)) { + config = c; + break; + } + } + + if (config == null) { + warnings.add("Illegal cluster configuration specified."); + return; + } + + ((Clusterable) component).setClusterConfiguration(config); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ColorSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/ColorSetter.java new file mode 100644 index 000000000..f290162e0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ColorSetter.java @@ -0,0 +1,54 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Reflection; + +//// ColorSetter - sets a Color value +class ColorSetter implements Setter { + private final Reflection.Method setMethod; + + public ColorSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + String red = attributes.get("red"); + String green = attributes.get("green"); + String blue = attributes.get("blue"); + + if (red == null || green == null || blue == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + int r, g, b; + try { + r = Integer.parseInt(red); + g = Integer.parseInt(green); + b = Integer.parseInt(blue); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + Color color = new Color(r, g, b); + setMethod.invoke(c, color); + + if (!s.trim().equals("")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentHandler.java new file mode 100644 index 000000000..b26e081e3 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentHandler.java @@ -0,0 +1,55 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.Reflection; + +/** + * A handler that creates components from the corresponding elements. The control of the + * contents is passed on to ComponentParameterHandler. + */ +class ComponentHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final RocketComponent parent; + + public ComponentHandler(RocketComponent parent, DocumentLoadingContext context) { + this.parent = parent; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Attempt to construct new component + Constructor constructor = DocumentConfig.constructors + .get(element); + if (constructor == null) { + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + return null; + } + + RocketComponent c; + try { + c = constructor.newInstance(); + } catch (InstantiationException e) { + throw new BugException("Error constructing component.", e); + } catch (IllegalAccessException e) { + throw new BugException("Error constructing component.", e); + } catch (InvocationTargetException e) { + throw Reflection.handleWrappedException(e); + } + + parent.addChild(c); + + return new ComponentParameterHandler(c, context); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java new file mode 100644 index 000000000..5614b2b60 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java @@ -0,0 +1,114 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; + +/** + * A handler that populates the parameters of a previously constructed rocket component. + * This uses the setters, or delegates the handling to another handler for specific + * elements. + */ +class ComponentParameterHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final RocketComponent component; + + public ComponentParameterHandler(RocketComponent c, DocumentLoadingContext context) { + this.component = c; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Check for specific elements that contain other elements + if (element.equals("subcomponents")) { + return new ComponentHandler(component, context); + } + if ( element.equals("appearance")) { + return new AppearanceHandler(component,context); + } + if (element.equals("motormount")) { + if (!(component instanceof MotorMount)) { + warnings.add(Warning.fromString("Illegal component defined as motor mount.")); + return null; + } + return new MotorMountHandler((MotorMount) component, context); + } + if (element.equals("finpoints")) { + if (!(component instanceof FreeformFinSet)) { + warnings.add(Warning.fromString("Illegal component defined for fin points.")); + return null; + } + return new FinSetPointHandler((FreeformFinSet) component, context); + } + if (element.equals("motorconfiguration")) { + if (!(component instanceof Rocket)) { + warnings.add(Warning.fromString("Illegal component defined for motor configuration.")); + return null; + } + return new MotorConfigurationHandler((Rocket) component, context); + } + if ( element.equals("deploymentconfiguration")) { + if ( !(component instanceof RecoveryDevice) ) { + warnings.add(Warning.fromString("Illegal component defined as recovery device.")); + return null; + } + return new DeploymentConfigurationHandler( (RecoveryDevice) component, context ); + } + if ( element.equals("separationconfiguration")) { + if ( !(component instanceof Stage) ) { + warnings.add(Warning.fromString("Illegal component defined as stage.")); + return null; + } + return new StageSeparationConfigurationHandler( (Stage) component, context ); + } + + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("subcomponents") || element.equals("motormount") || + element.equals("finpoints") || element.equals("motorconfiguration") || + element.equals("appearance") || element.equals("deploymentconfiguration") || + element.equals("separationconfiguration")) { + return; + } + + // Search for the correct setter class + + Class c; + for (c = component.getClass(); c != null; c = c.getSuperclass()) { + String setterKey = c.getSimpleName() + ":" + element; + Setter s = DocumentConfig.setters.get(setterKey); + if (s != null) { + // Setter found + s.set(component, content, attributes, warnings); + break; + } + if (DocumentConfig.setters.containsKey(setterKey)) { + // Key exists but is null -> invalid parameter + c = null; + break; + } + } + if (c == null) { + warnings.add(Warning.fromString("Unknown parameter type '" + element + "' for " + + component.getComponentName() + ", ignoring.")); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentPresetSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentPresetSetter.java new file mode 100644 index 000000000..dead76d17 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentPresetSetter.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Reflection; + +////ComponentPresetSetter - sets a ComponentPreset value +class ComponentPresetSetter implements Setter { + private final Reflection.Method setMethod; + + public ComponentPresetSetter(Reflection.Method set) { + this.setMethod = set; + } + + @Override + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + String manufacturerName = attributes.get("manufacturer"); + if (manufacturerName == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no manufacturer specified. Ignored")); + return; + } + + String productNo = attributes.get("partno"); + if (productNo == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no partno specified. Ignored")); + return; + } + + String digest = attributes.get("digest"); + if (digest == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no digest specified.")); + } + + String type = attributes.get("type"); + if (type == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no type specified.")); + } + + List presets = Application.getComponentPresetDao().find(manufacturerName, productNo); + + ComponentPreset matchingPreset = null; + + for (ComponentPreset preset : presets) { + if (digest != null && preset.getDigest().equals(digest)) { + // Found one with matching digest. Take it. + matchingPreset = preset; + break; + } + if (type != null && preset.getType().name().equals(type) && matchingPreset != null) { + // Found the first one with matching type. + matchingPreset = preset; + } + } + + // Was any found? + if (matchingPreset == null) { + warnings.add(Warning.fromString("No matching ComponentPreset for component " + c.getName() + " found matching " + manufacturerName + " " + productNo)); + return; + } + + if (digest != null && !matchingPreset.getDigest().equals(digest)) { + warnings.add(Warning.fromString("ComponentPreset for component " + c.getName() + " has wrong digest")); + } + + setMethod.invoke(c, matchingPreset); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/CustomExpressionHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/CustomExpressionHandler.java new file mode 100644 index 000000000..66b6ac030 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/CustomExpressionHandler.java @@ -0,0 +1,57 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.simulation.customexpression.CustomExpression; + +import org.xml.sax.SAXException; + +class CustomExpressionHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final OpenRocketContentHandler contentHandler; + public CustomExpression currentExpression; + + public CustomExpressionHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { + this.context = context; + this.contentHandler = contentHandler; + currentExpression = new CustomExpression(contentHandler.getDocument()); + + } + + @Override + public ElementHandler openElement(String element, + HashMap attributes, WarningSet warnings) + throws SAXException { + + return this; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + if (element.equals("type")) { + contentHandler.getDocument().addCustomExpression(currentExpression); + } + + if (element.equals("name")) { + currentExpression.setName(content); + } + + if (element.equals("symbol")) { + currentExpression.setSymbol(content); + } + + if (element.equals("unit") && attributes.get("unittype").equals("auto")) { + currentExpression.setUnit(content); + } + + if (element.equals("expression")) { + currentExpression.setExpression(content); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DatatypeHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/DatatypeHandler.java new file mode 100644 index 000000000..ece9463a2 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DatatypeHandler.java @@ -0,0 +1,49 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; + +import org.xml.sax.SAXException; + +class DatatypeHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final OpenRocketContentHandler contentHandler; + private CustomExpressionHandler customExpressionHandler = null; + + public DatatypeHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { + this.context = context; + this.contentHandler = contentHandler; + } + + @Override + public ElementHandler openElement(String element, + HashMap attributes, WarningSet warnings) + throws SAXException { + + if (element.equals("type") && attributes.get("source").equals("customexpression")) { + customExpressionHandler = new CustomExpressionHandler(contentHandler, context); + return customExpressionHandler; + } + else { + warnings.add(Warning.fromString("Unknown datatype " + element + " defined, ignoring")); + } + + return this; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + attributes.remove("source"); + super.closeElement(element, attributes, content, warnings); + + if (customExpressionHandler != null) { + contentHandler.getDocument().addCustomExpression(customExpressionHandler.currentExpression); + } + + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DeploymentConfigurationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/DeploymentConfigurationHandler.java new file mode 100644 index 000000000..c3cf2726e --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DeploymentConfigurationHandler.java @@ -0,0 +1,62 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; + +import org.xml.sax.SAXException; + +class DeploymentConfigurationHandler extends AbstractElementHandler { + private final RecoveryDevice recoveryDevice; + private DeploymentConfiguration config; + + public DeploymentConfigurationHandler( RecoveryDevice recoveryDevice, DocumentLoadingContext context ) { + this.recoveryDevice = recoveryDevice; + config = new DeploymentConfiguration(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) + throws SAXException { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, + WarningSet warnings) throws SAXException { + + content = content.trim(); + + if ( "deployevent".equals(element) ) { + DeployEvent type = (DeployEvent) DocumentConfig.findEnum(content, DeployEvent.class); + if ( type == null ) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + config.setDeployEvent( type ); + return; + } else if ( "deployaltitude".equals(element) ) { + config.setDeployAltitude( Double.parseDouble(content)); + return; + } else if ( "deploydelay".equals(element) ) { + config.setDeployDelay( Double.parseDouble(content)); + return; + } + super.closeElement(element, attributes, content, warnings); + + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + String configId = attributes.get("configid"); + recoveryDevice.setFlightConfiguration(configId, config); + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java new file mode 100644 index 000000000..dbf897a5c --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java @@ -0,0 +1,425 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.material.Material; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.rocketcomponent.BodyComponent; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.Bulkhead; +import net.sf.openrocket.rocketcomponent.CenteringRing; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; +import net.sf.openrocket.rocketcomponent.EllipticalFinSet; +import net.sf.openrocket.rocketcomponent.EngineBlock; +import net.sf.openrocket.rocketcomponent.ExternalComponent; +import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.InnerTube; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.MassComponent; +import net.sf.openrocket.rocketcomponent.MassObject; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.Parachute; +import net.sf.openrocket.rocketcomponent.RadiusRingComponent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.ReferenceType; +import net.sf.openrocket.rocketcomponent.RingComponent; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.ShockCord; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; +import net.sf.openrocket.rocketcomponent.Streamer; +import net.sf.openrocket.rocketcomponent.StructuralComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.rocketcomponent.TubeCoupler; +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.LineStyle; +import net.sf.openrocket.util.Reflection; + +class DocumentConfig { + + /* Remember to update OpenRocketSaver as well! */ + public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6"}; + + /** + * Divisor used in converting an integer version to the point-represented version. + * The integer version divided by this value is the major version and the remainder is + * the minor version. For example 101 corresponds to file version "1.1". + */ + public static final int FILE_VERSION_DIVISOR = 100; + + + //////// Component constructors + static final HashMap> constructors = new HashMap>(); + static { + try { + // External components + constructors.put("bodytube", BodyTube.class.getConstructor(new Class[0])); + constructors.put("transition", Transition.class.getConstructor(new Class[0])); + constructors.put("nosecone", NoseCone.class.getConstructor(new Class[0])); + constructors.put("trapezoidfinset", TrapezoidFinSet.class.getConstructor(new Class[0])); + constructors.put("ellipticalfinset", EllipticalFinSet.class.getConstructor(new Class[0])); + constructors.put("freeformfinset", FreeformFinSet.class.getConstructor(new Class[0])); + constructors.put("launchlug", LaunchLug.class.getConstructor(new Class[0])); + + // Internal components + constructors.put("engineblock", EngineBlock.class.getConstructor(new Class[0])); + constructors.put("innertube", InnerTube.class.getConstructor(new Class[0])); + constructors.put("tubecoupler", TubeCoupler.class.getConstructor(new Class[0])); + constructors.put("bulkhead", Bulkhead.class.getConstructor(new Class[0])); + constructors.put("centeringring", CenteringRing.class.getConstructor(new Class[0])); + + constructors.put("masscomponent", MassComponent.class.getConstructor(new Class[0])); + constructors.put("shockcord", ShockCord.class.getConstructor(new Class[0])); + constructors.put("parachute", Parachute.class.getConstructor(new Class[0])); + constructors.put("streamer", Streamer.class.getConstructor(new Class[0])); + + // Other + constructors.put("stage", Stage.class.getConstructor(new Class[0])); + + } catch (NoSuchMethodException e) { + throw new BugException( + "Error in constructing the 'constructors' HashMap."); + } + } + + + //////// Parameter setters + /* + * The keys are of the form Class:param, where Class is the class name and param + * the element name. Setters are searched for in descending class order. + * A setter of null means setting the parameter is not allowed. + */ + static final HashMap setters = new HashMap(); + static { + // RocketComponent + setters.put("RocketComponent:name", new StringSetter( + Reflection.findMethod(RocketComponent.class, "setName", String.class))); + setters.put("RocketComponent:color", new ColorSetter( + Reflection.findMethod(RocketComponent.class, "setColor", Color.class))); + setters.put("RocketComponent:linestyle", new EnumSetter( + Reflection.findMethod(RocketComponent.class, "setLineStyle", LineStyle.class), + LineStyle.class)); + setters.put("RocketComponent:position", new PositionSetter()); + setters.put("RocketComponent:overridemass", new OverrideSetter( + Reflection.findMethod(RocketComponent.class, "setOverrideMass", double.class), + Reflection.findMethod(RocketComponent.class, "setMassOverridden", boolean.class))); + setters.put("RocketComponent:overridecg", new OverrideSetter( + Reflection.findMethod(RocketComponent.class, "setOverrideCGX", double.class), + Reflection.findMethod(RocketComponent.class, "setCGOverridden", boolean.class))); + setters.put("RocketComponent:overridesubcomponents", new BooleanSetter( + Reflection.findMethod(RocketComponent.class, "setOverrideSubcomponents", boolean.class))); + setters.put("RocketComponent:comment", new StringSetter( + Reflection.findMethod(RocketComponent.class, "setComment", String.class))); + setters.put("RocketComponent:preset", new ComponentPresetSetter( + Reflection.findMethod(RocketComponent.class, "loadPreset", ComponentPreset.class))); + + // ExternalComponent + setters.put("ExternalComponent:finish", new EnumSetter( + Reflection.findMethod(ExternalComponent.class, "setFinish", Finish.class), + Finish.class)); + setters.put("ExternalComponent:material", new MaterialSetter( + Reflection.findMethod(ExternalComponent.class, "setMaterial", Material.class), + Material.Type.BULK)); + + // BodyComponent + setters.put("BodyComponent:length", new DoubleSetter( + Reflection.findMethod(BodyComponent.class, "setLength", double.class))); + + // SymmetricComponent + setters.put("SymmetricComponent:thickness", new DoubleSetter( + Reflection.findMethod(SymmetricComponent.class, "setThickness", double.class), + "filled", + Reflection.findMethod(SymmetricComponent.class, "setFilled", boolean.class))); + + // BodyTube + setters.put("BodyTube:radius", new DoubleSetter( + Reflection.findMethod(BodyTube.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(BodyTube.class, "setOuterRadiusAutomatic", boolean.class))); + + // Transition + setters.put("Transition:shape", new EnumSetter( + Reflection.findMethod(Transition.class, "setType", Transition.Shape.class), + Transition.Shape.class)); + setters.put("Transition:shapeclipped", new BooleanSetter( + Reflection.findMethod(Transition.class, "setClipped", boolean.class))); + setters.put("Transition:shapeparameter", new DoubleSetter( + Reflection.findMethod(Transition.class, "setShapeParameter", double.class))); + + setters.put("Transition:foreradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeRadius", double.class), + "auto", + Reflection.findMethod(Transition.class, "setForeRadiusAutomatic", boolean.class))); + setters.put("Transition:aftradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftRadius", double.class), + "auto", + Reflection.findMethod(Transition.class, "setAftRadiusAutomatic", boolean.class))); + + setters.put("Transition:foreshoulderradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeShoulderRadius", double.class))); + setters.put("Transition:foreshoulderlength", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeShoulderLength", double.class))); + setters.put("Transition:foreshoulderthickness", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeShoulderThickness", double.class))); + setters.put("Transition:foreshouldercapped", new BooleanSetter( + Reflection.findMethod(Transition.class, "setForeShoulderCapped", boolean.class))); + + setters.put("Transition:aftshoulderradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftShoulderRadius", double.class))); + setters.put("Transition:aftshoulderlength", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftShoulderLength", double.class))); + setters.put("Transition:aftshoulderthickness", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class))); + setters.put("Transition:aftshouldercapped", new BooleanSetter( + Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class))); + + // NoseCone - disable disallowed elements + setters.put("NoseCone:foreradius", null); + setters.put("NoseCone:foreshoulderradius", null); + setters.put("NoseCone:foreshoulderlength", null); + setters.put("NoseCone:foreshoulderthickness", null); + setters.put("NoseCone:foreshouldercapped", null); + + // FinSet + setters.put("FinSet:fincount", new IntSetter( + Reflection.findMethod(FinSet.class, "setFinCount", int.class))); + setters.put("FinSet:rotation", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0)); + setters.put("FinSet:thickness", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setThickness", double.class))); + setters.put("FinSet:crosssection", new EnumSetter( + Reflection.findMethod(FinSet.class, "setCrossSection", FinSet.CrossSection.class), + FinSet.CrossSection.class)); + setters.put("FinSet:cant", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0)); + setters.put("FinSet:tabheight", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setTabHeight", double.class))); + setters.put("FinSet:tablength", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setTabLength", double.class))); + setters.put("FinSet:tabposition", new FinTabPositionSetter()); + + // TrapezoidFinSet + setters.put("TrapezoidFinSet:rootchord", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setRootChord", double.class))); + setters.put("TrapezoidFinSet:tipchord", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setTipChord", double.class))); + setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setSweep", double.class))); + setters.put("TrapezoidFinSet:height", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setHeight", double.class))); + + // EllipticalFinSet + setters.put("EllipticalFinSet:rootchord", new DoubleSetter( + Reflection.findMethod(EllipticalFinSet.class, "setLength", double.class))); + setters.put("EllipticalFinSet:height", new DoubleSetter( + Reflection.findMethod(EllipticalFinSet.class, "setHeight", double.class))); + + // FreeformFinSet points handled as a special handler + + // LaunchLug + setters.put("LaunchLug:radius", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setOuterRadius", double.class))); + setters.put("LaunchLug:length", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setLength", double.class))); + setters.put("LaunchLug:thickness", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setThickness", double.class))); + setters.put("LaunchLug:radialdirection", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // InternalComponent - nothing + + // StructuralComponent + setters.put("StructuralComponent:material", new MaterialSetter( + Reflection.findMethod(StructuralComponent.class, "setMaterial", Material.class), + Material.Type.BULK)); + + // RingComponent + setters.put("RingComponent:length", new DoubleSetter( + Reflection.findMethod(RingComponent.class, "setLength", double.class))); + setters.put("RingComponent:radialposition", new DoubleSetter( + Reflection.findMethod(RingComponent.class, "setRadialPosition", double.class))); + setters.put("RingComponent:radialdirection", new DoubleSetter( + Reflection.findMethod(RingComponent.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // ThicknessRingComponent - radius on separate components due to differing automatics + setters.put("ThicknessRingComponent:thickness", new DoubleSetter( + Reflection.findMethod(ThicknessRingComponent.class, "setThickness", double.class))); + + // EngineBlock + setters.put("EngineBlock:outerradius", new DoubleSetter( + Reflection.findMethod(EngineBlock.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class))); + + // TubeCoupler + setters.put("TubeCoupler:outerradius", new DoubleSetter( + Reflection.findMethod(TubeCoupler.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class))); + + // InnerTube + setters.put("InnerTube:outerradius", new DoubleSetter( + Reflection.findMethod(InnerTube.class, "setOuterRadius", double.class))); + setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter()); + setters.put("InnerTube:clusterscale", new DoubleSetter( + Reflection.findMethod(InnerTube.class, "setClusterScale", double.class))); + setters.put("InnerTube:clusterrotation", new DoubleSetter( + Reflection.findMethod(InnerTube.class, "setClusterRotation", double.class), + Math.PI / 180.0)); + + // RadiusRingComponent + + // Bulkhead + setters.put("RadiusRingComponent:innerradius", new DoubleSetter( + Reflection.findMethod(RadiusRingComponent.class, "setInnerRadius", double.class))); + setters.put("Bulkhead:outerradius", new DoubleSetter( + Reflection.findMethod(Bulkhead.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class))); + + // CenteringRing + setters.put("CenteringRing:innerradius", new DoubleSetter( + Reflection.findMethod(CenteringRing.class, "setInnerRadius", double.class), + "auto", + Reflection.findMethod(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class))); + setters.put("CenteringRing:outerradius", new DoubleSetter( + Reflection.findMethod(CenteringRing.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class))); + + + // MassObject + setters.put("MassObject:packedlength", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setLength", double.class))); + setters.put("MassObject:packedradius", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setRadius", double.class))); + setters.put("MassObject:radialposition", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setRadialPosition", double.class))); + setters.put("MassObject:radialdirection", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // MassComponent + setters.put("MassComponent:mass", new DoubleSetter( + Reflection.findMethod(MassComponent.class, "setComponentMass", double.class))); + + // ShockCord + setters.put("ShockCord:cordlength", new DoubleSetter( + Reflection.findMethod(ShockCord.class, "setCordLength", double.class))); + setters.put("ShockCord:material", new MaterialSetter( + Reflection.findMethod(ShockCord.class, "setMaterial", Material.class), + Material.Type.LINE)); + + // RecoveryDevice + setters.put("RecoveryDevice:cd", new DoubleSetter( + Reflection.findMethod(RecoveryDevice.class, "setCD", double.class), + "auto", + Reflection.findMethod(RecoveryDevice.class, "setCDAutomatic", boolean.class))); + setters.put("RecoveryDevice:deployevent", new EnumSetter( + Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployEvent", DeployEvent.class), + DeployEvent.class)); + setters.put("RecoveryDevice:deployaltitude", new DoubleSetter( + Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployAltitude", double.class))); + setters.put("RecoveryDevice:deploydelay", new DoubleSetter( + Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployDelay", double.class))); + setters.put("RecoveryDevice:material", new MaterialSetter( + Reflection.findMethod(RecoveryDevice.class, "setMaterial", Material.class), + Material.Type.SURFACE)); + + // Parachute + setters.put("Parachute:diameter", new DoubleSetter( + Reflection.findMethod(Parachute.class, "setDiameter", double.class))); + setters.put("Parachute:linecount", new IntSetter( + Reflection.findMethod(Parachute.class, "setLineCount", int.class))); + setters.put("Parachute:linelength", new DoubleSetter( + Reflection.findMethod(Parachute.class, "setLineLength", double.class))); + setters.put("Parachute:linematerial", new MaterialSetter( + Reflection.findMethod(Parachute.class, "setLineMaterial", Material.class), + Material.Type.LINE)); + + // Streamer + setters.put("Streamer:striplength", new DoubleSetter( + Reflection.findMethod(Streamer.class, "setStripLength", double.class))); + setters.put("Streamer:stripwidth", new DoubleSetter( + Reflection.findMethod(Streamer.class, "setStripWidth", double.class))); + + // Rocket + // handled by separate handler + setters.put("Rocket:referencetype", new EnumSetter( + Reflection.findMethod(Rocket.class, "setReferenceType", ReferenceType.class), + ReferenceType.class)); + setters.put("Rocket:customreference", new DoubleSetter( + Reflection.findMethod(Rocket.class, "setCustomReferenceLength", double.class))); + setters.put("Rocket:designer", new StringSetter( + Reflection.findMethod(Rocket.class, "setDesigner", String.class))); + setters.put("Rocket:revision", new StringSetter( + Reflection.findMethod(Rocket.class, "setRevision", String.class))); + + // Stage + setters.put("Stage:separationevent", new EnumSetter( + Reflection.findMethod(Stage.class, "setDefaultSeparationEvent", StageSeparationConfiguration.SeparationEvent.class), + StageSeparationConfiguration.SeparationEvent.class)); + setters.put("Stage:separationdelay", new DoubleSetter( + Reflection.findMethod(Stage.class, "setDefaultSeparationDelay", double.class))); + + } + + + /** + * Search for a enum value that has the corresponding name as an XML value. The current + * conversion from enum name to XML value is to lowercase the name and strip out all + * underscore characters. This method returns a match to these criteria, or null + * if no such enum exists. + * + * @param then enum type. + * @param name the XML value, null ok. + * @param enumClass the class of the enum. + * @return the found enum value, or null. + */ + public static > Enum findEnum(String name, + Class> enumClass) { + + if (name == null) + return null; + name = name.trim(); + for (Enum e : enumClass.getEnumConstants()) { + if (e.name().toLowerCase(Locale.ENGLISH).replace("_", "").equals(name)) { + return e; + } + } + return null; + } + + + /** + * Convert a string to a double including formatting specifications of the OpenRocket + * file format. This accepts all formatting that is valid for + * Double.parseDouble(s) and a few others as well ("Inf", "-Inf"). + * + * @param s the string to parse. + * @return the numerical value. + * @throws NumberFormatException the the string cannot be parsed. + */ + public static double stringToDouble(String s) throws NumberFormatException { + if (s == null) + throw new NumberFormatException("null string"); + if (s.equalsIgnoreCase("NaN")) + return Double.NaN; + if (s.equalsIgnoreCase("Inf")) + return Double.POSITIVE_INFINITY; + if (s.equalsIgnoreCase("-Inf")) + return Double.NEGATIVE_INFINITY; + return Double.parseDouble(s); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DoubleSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/DoubleSetter.java new file mode 100644 index 000000000..a7a50b9b2 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DoubleSetter.java @@ -0,0 +1,78 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// DoubleSetter - sets a double value or (alternatively) if a specific string is encountered +//// calls a setXXX(boolean) method. +class DoubleSetter implements Setter { + private final Reflection.Method setMethod; + private final String specialString; + private final Reflection.Method specialMethod; + private final double multiplier; + + /** + * Set only the double value. + * @param set set method for the double value. + */ + public DoubleSetter(Reflection.Method set) { + this.setMethod = set; + this.specialString = null; + this.specialMethod = null; + this.multiplier = 1.0; + } + + /** + * Multiply with the given multiplier and set the double value. + * @param set set method for the double value. + * @param mul multiplier. + */ + public DoubleSetter(Reflection.Method set, double mul) { + this.setMethod = set; + this.specialString = null; + this.specialMethod = null; + this.multiplier = mul; + } + + /** + * Set the double value, or if the value equals the special string, use the + * special setter and set it to true. + * + * @param set double setter. + * @param special special string + * @param specialMethod boolean setter. + */ + public DoubleSetter(Reflection.Method set, String special, + Reflection.Method specialMethod) { + this.setMethod = set; + this.specialString = special; + this.specialMethod = specialMethod; + this.multiplier = 1.0; + } + + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + s = s.trim(); + + // Check for special case + if (specialMethod != null && s.equalsIgnoreCase(specialString)) { + specialMethod.invoke(c, true); + return; + } + + // Normal case + try { + double d = Double.parseDouble(s); + setMethod.invoke(c, d * multiplier); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/EnumSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/EnumSetter.java new file mode 100644 index 000000000..8b93becbd --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/EnumSetter.java @@ -0,0 +1,32 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// EnumSetter - sets a generic enum type +class EnumSetter> implements Setter { + private final Reflection.Method setter; + private final Class enumClass; + + public EnumSetter(Reflection.Method set, Class enumClass) { + this.setter = set; + this.enumClass = enumClass; + } + + @Override + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + + Enum setEnum = DocumentConfig.findEnum(name, enumClass); + if (setEnum == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + setter.invoke(c, setEnum); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FinSetPointHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/FinSetPointHandler.java new file mode 100644 index 000000000..fd92dbf4c --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FinSetPointHandler.java @@ -0,0 +1,70 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.ArrayList; +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.IllegalFinPointException; +import net.sf.openrocket.util.Coordinate; + +import org.xml.sax.SAXException; + +/** + * A handler that reads the specifications within the freeformfinset's + * elements. + */ +class FinSetPointHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final FreeformFinSet finset; + private final ArrayList coordinates = new ArrayList(); + + public FinSetPointHandler(FreeformFinSet finset, DocumentLoadingContext context) { + this.finset = finset; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + String strx = attributes.remove("x"); + String stry = attributes.remove("y"); + if (strx == null || stry == null) { + warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); + return; + } + try { + double x = Double.parseDouble(strx); + double y = Double.parseDouble(stry); + coordinates.add(new Coordinate(x, y)); + } catch (NumberFormatException e) { + warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); + return; + } + + super.closeElement(element, attributes, content, warnings); + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + try { + finset.setPoints(coordinates.toArray(new Coordinate[0])); + } catch (IllegalFinPointException e) { + warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring.")); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FinTabPositionSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/FinTabPositionSetter.java new file mode 100644 index 000000000..f6e046d19 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FinTabPositionSetter.java @@ -0,0 +1,46 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; +import net.sf.openrocket.util.Reflection; + +class FinTabPositionSetter extends DoubleSetter { + + public FinTabPositionSetter() { + super(Reflection.findMethod(FinSet.class, "setTabShift", double.class)); + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + if (!(c instanceof FinSet)) { + throw new IllegalStateException("FinTabPositionSetter called for component " + c); + } + + String relative = attributes.get("relativeto"); + FinSet.TabRelativePosition position = + (TabRelativePosition) DocumentConfig.findEnum(relative, + FinSet.TabRelativePosition.class); + + if (position != null) { + + ((FinSet) c).setTabRelativePosition(position); + + } else { + if (relative == null) { + warnings.add("Required attribute 'relativeto' not found for fin tab position."); + } else { + warnings.add("Illegal attribute value '" + relative + "' encountered."); + } + } + + super.set(c, s, attributes, warnings); + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataBranchHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataBranchHandler.java new file mode 100644 index 000000000..6bb8a2d07 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataBranchHandler.java @@ -0,0 +1,153 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.FlightEvent.Type; +import net.sf.openrocket.simulation.customexpression.CustomExpression; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; + +class FlightDataBranchHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final FlightDataType[] types; + private final FlightDataBranch branch; + + private static final LogHelper log = Application.getLogger(); + private final SingleSimulationHandler simHandler; + + public FlightDataBranchHandler(String name, String typeList, SingleSimulationHandler simHandler, DocumentLoadingContext context) { + this.simHandler = simHandler; + this.context = context; + String[] split = typeList.split(","); + types = new FlightDataType[split.length]; + for (int i = 0; i < split.length; i++) { + String typeName = split[i]; + FlightDataType matching = findFlightDataType(typeName); + types[i] = matching; + //types[i] = FlightDataType.getType(typeName, matching.getSymbol(), matching.getUnitGroup()); + } + + // TODO: LOW: May throw an IllegalArgumentException + branch = new FlightDataBranch(name, types); + } + + // Find the full flight data type given name only + // Note: this way of doing it requires that custom expressions always come before flight data in the file, + // not the nicest but this is always the case anyway. + private FlightDataType findFlightDataType(String name) { + + // Kevins version with lookup by key. Not using right now + /* + if ( key != null ) { + for (FlightDataType t : FlightDataType.ALL_TYPES){ + if (t.getKey().equals(key) ){ + return t; + } + } + } + */ + + // Look in built in types + for (FlightDataType t : FlightDataType.ALL_TYPES) { + if (t.getName().equals(name)) { + return t; + } + } + + // Look in custom expressions + for (CustomExpression exp : simHandler.getDocument().getCustomExpressions()) { + if (exp.getName().equals(name)) { + return exp.getType(); + } + } + + log.warn("Could not find the flight data type '" + name + "' used in the XML file. Substituted type with unknown symbol and units."); + return FlightDataType.getType(name, "Unknown", UnitGroup.UNITS_NONE); + } + + public FlightDataBranch getBranch() { + branch.immute(); + return branch; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("datapoint")) + return PlainTextHandler.INSTANCE; + if (element.equals("event")) + return PlainTextHandler.INSTANCE; + + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return null; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("event")) { + double time; + FlightEvent.Type type; + + try { + time = DocumentConfig.stringToDouble(attributes.get("time")); + } catch (NumberFormatException e) { + warnings.add("Illegal event specification, ignoring."); + return; + } + + type = (Type) DocumentConfig.findEnum(attributes.get("type"), FlightEvent.Type.class); + if (type == null) { + warnings.add("Illegal event specification, ignoring."); + return; + } + + branch.addEvent(new FlightEvent(type, time)); + return; + } + + if (!element.equals("datapoint")) { + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return; + } + + // element == "datapoint" + + + // Check line format + String[] split = content.split(","); + if (split.length != types.length) { + warnings.add("Data point did not contain correct amount of values, ignoring point."); + return; + } + + // Parse the doubles + double[] values = new double[split.length]; + for (int i = 0; i < values.length; i++) { + try { + values[i] = DocumentConfig.stringToDouble(split[i]); + } catch (NumberFormatException e) { + warnings.add("Data point format error, ignoring point."); + return; + } + } + + // Add point to branch + branch.addPoint(); + for (int i = 0; i < types.length; i++) { + branch.setValue(types[i], values[i]); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java new file mode 100644 index 000000000..4251807a8 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java @@ -0,0 +1,137 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.FlightDataBranch; + +class FlightDataHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + + private FlightDataBranchHandler dataHandler; + private WarningSet warningSet = new WarningSet(); + private List branches = new ArrayList(); + + private SingleSimulationHandler simHandler; + private FlightData data; + + + public FlightDataHandler(SingleSimulationHandler simHandler, DocumentLoadingContext context) { + this.context = context; + this.simHandler = simHandler; + } + + public FlightData getFlightData() { + return data; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("warning")) { + return PlainTextHandler.INSTANCE; + } + if (element.equals("databranch")) { + if (attributes.get("name") == null || attributes.get("types") == null) { + warnings.add("Illegal flight data definition, ignoring."); + return null; + } + dataHandler = new FlightDataBranchHandler(attributes.get("name"), + attributes.get("types"), + simHandler, context); + return dataHandler; + } + + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return null; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("databranch")) { + FlightDataBranch branch = dataHandler.getBranch(); + if (branch.getLength() > 0) { + branches.add(branch); + } + } else if (element.equals("warning")) { + warningSet.add(Warning.fromString(content)); + } + } + + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (branches.size() > 0) { + data = new FlightData(branches.toArray(new FlightDataBranch[0])); + } else { + double maxAltitude = Double.NaN; + double maxVelocity = Double.NaN; + double maxAcceleration = Double.NaN; + double maxMach = Double.NaN; + double timeToApogee = Double.NaN; + double flightTime = Double.NaN; + double groundHitVelocity = Double.NaN; + double launchRodVelocity = Double.NaN; + double deploymentVelocity = Double.NaN; + + try { + maxAltitude = DocumentConfig.stringToDouble(attributes.get("maxaltitude")); + } catch (NumberFormatException ignore) { + } + try { + maxVelocity = DocumentConfig.stringToDouble(attributes.get("maxvelocity")); + } catch (NumberFormatException ignore) { + } + try { + maxAcceleration = DocumentConfig.stringToDouble(attributes.get("maxacceleration")); + } catch (NumberFormatException ignore) { + } + try { + maxMach = DocumentConfig.stringToDouble(attributes.get("maxmach")); + } catch (NumberFormatException ignore) { + } + try { + timeToApogee = DocumentConfig.stringToDouble(attributes.get("timetoapogee")); + } catch (NumberFormatException ignore) { + } + try { + flightTime = DocumentConfig.stringToDouble(attributes.get("flighttime")); + } catch (NumberFormatException ignore) { + } + try { + groundHitVelocity = + DocumentConfig.stringToDouble(attributes.get("groundhitvelocity")); + } catch (NumberFormatException ignore) { + } + try { + launchRodVelocity = DocumentConfig.stringToDouble(attributes.get("launchrodvelocity")); + } catch (NumberFormatException ignore) { + } + try { + deploymentVelocity = DocumentConfig.stringToDouble(attributes.get("deploymentvelocity")); + } catch (NumberFormatException ignore) { + } + + data = new FlightData(maxAltitude, maxVelocity, maxAcceleration, maxMach, + timeToApogee, flightTime, groundHitVelocity, launchRodVelocity, deploymentVelocity); + } + + data.getWarningSet().addAll(warningSet); + data.immute(); + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/IntSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/IntSetter.java new file mode 100644 index 000000000..c3987342e --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/IntSetter.java @@ -0,0 +1,28 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// IntSetter - set an integer value +class IntSetter implements Setter { + private final Reflection.Method setMethod; + + public IntSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + try { + int n = Integer.parseInt(s); + setMethod.invoke(c, n); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MaterialSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/MaterialSetter.java new file mode 100644 index 000000000..3aae93c04 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MaterialSetter.java @@ -0,0 +1,73 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.database.Databases; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +////MaterialSetter - sets a Material value +class MaterialSetter implements Setter { + private final Reflection.Method setMethod; + private final Material.Type type; + + public MaterialSetter(Reflection.Method set, Material.Type type) { + this.setMethod = set; + this.type = type; + } + + @Override + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + + Material mat; + + // Check name != "" + name = name.trim(); + if (name.equals("")) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + + // Parse density + double density; + String str; + str = attributes.remove("density"); + if (str == null) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + try { + density = Double.parseDouble(str); + } catch (NumberFormatException e) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + + // Parse thickness + // double thickness = 0; + // str = attributes.remove("thickness"); + // try { + // if (str != null) + // thickness = Double.parseDouble(str); + // } catch (NumberFormatException e){ + // warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + // return; + // } + + // Check type if specified + str = attributes.remove("type"); + if (str != null && !type.name().toLowerCase(Locale.ENGLISH).equals(str)) { + warnings.add(Warning.fromString("Illegal material type specified, ignoring.")); + return; + } + + mat = Databases.findMaterial(type, name, density); + + setMethod.invoke(c, mat); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MotorConfigurationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/MotorConfigurationHandler.java new file mode 100644 index 000000000..b20d3a865 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MotorConfigurationHandler.java @@ -0,0 +1,70 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.Rocket; + +import org.xml.sax.SAXException; + +class MotorConfigurationHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final Rocket rocket; + private String name = null; + private boolean inNameElement = false; + + public MotorConfigurationHandler(Rocket rocket, DocumentLoadingContext context) { + this.rocket = rocket; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (inNameElement || !element.equals("name")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return null; + } + inNameElement = true; + + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + name = content; + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + String configid = attributes.remove("configid"); + if (configid == null || configid.equals("")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (!rocket.addMotorConfigurationID(configid)) { + warnings.add("Duplicate motor configuration ID used."); + return; + } + + if (name != null && name.trim().length() > 0) { + rocket.setFlightConfigurationName(configid, name); + } + + if ("true".equals(attributes.remove("default"))) { + rocket.getDefaultConfiguration().setFlightConfigurationID(configid); + } + + super.closeElement(element, attributes, content, warnings); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MotorHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/MotorHandler.java new file mode 100644 index 000000000..14468a35f --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MotorHandler.java @@ -0,0 +1,174 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; + +import org.xml.sax.SAXException; + +class MotorHandler extends AbstractElementHandler { + /** File version where latest digest format was introduced */ + private static final int MOTOR_DIGEST_VERSION = 104; + + private final DocumentLoadingContext context; + private Motor.Type type = null; + private String manufacturer = null; + private String designation = null; + private String digest = null; + private double diameter = Double.NaN; + private double length = Double.NaN; + private double delay = Double.NaN; + + private Double ignitionDelay = null; + private MotorConfiguration.IgnitionEvent ignitionEvent = null; + + public MotorHandler(DocumentLoadingContext context) { + this.context = context; + } + + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + + /** + * Return the motor to use, or null. + */ + public Motor getMotor(WarningSet warnings) { + return context.getMotorFinder().findMotor(type, manufacturer, designation, diameter, length, digest, warnings); + } + + /** + * Return the delay to use for the motor. + */ + public double getDelay(WarningSet warnings) { + if (Double.isNaN(delay)) { + warnings.add(Warning.fromString("Motor delay not specified, assuming no ejection charge.")); + return Motor.PLUGGED; + } + return delay; + } + + public Double getIgnitionDelay() { + return ignitionDelay; + } + + public MotorConfiguration.IgnitionEvent getIgnitionEvent() { + return ignitionEvent; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + content = content.trim(); + + if (element.equals("type")) { + + // Motor type + type = null; + for (Motor.Type t : Motor.Type.values()) { + if (t.name().toLowerCase(Locale.ENGLISH).equals(content.trim())) { + type = t; + break; + } + } + if (type == null) { + warnings.add(Warning.fromString("Unknown motor type '" + content + "', ignoring.")); + } + + } else if (element.equals("manufacturer")) { + + // Manufacturer + manufacturer = content.trim(); + + } else if (element.equals("designation")) { + + // Designation + designation = content.trim(); + + } else if (element.equals("digest")) { + + // Digest is used only for file versions saved using the same digest algorithm + if (context.getFileVersion() >= MOTOR_DIGEST_VERSION) { + digest = content.trim(); + } + + } else if (element.equals("diameter")) { + + // Diameter + diameter = Double.NaN; + try { + diameter = Double.parseDouble(content.trim()); + } catch (NumberFormatException e) { + // Ignore + } + if (Double.isNaN(diameter)) { + warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); + } + + } else if (element.equals("length")) { + + // Length + length = Double.NaN; + try { + length = Double.parseDouble(content.trim()); + } catch (NumberFormatException ignore) { + } + + if (Double.isNaN(length)) { + warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); + } + + } else if (element.equals("delay")) { + + // Delay + delay = Double.NaN; + if (content.equals("none")) { + delay = Motor.PLUGGED; + } else { + try { + delay = Double.parseDouble(content.trim()); + } catch (NumberFormatException ignore) { + } + + if (Double.isNaN(delay)) { + warnings.add(Warning.fromString("Illegal motor delay specified, ignoring.")); + } + + } + + } else if ( element.equals("ignitionevent")) { + + for (MotorConfiguration.IgnitionEvent e : MotorConfiguration.IgnitionEvent.values()) { + if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) { + ignitionEvent = e; + break; + } + } + if (ignitionEvent == null) { + warnings.add(Warning.fromString("Unknown ignition event type '" + content + "', ignoring.")); + } + + } else if ( element.equals("ignitiondelay")) { + try { + ignitionDelay = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); + } + } else { + super.closeElement(element, attributes, content, warnings); + } + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MotorMountHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/MotorMountHandler.java new file mode 100644 index 000000000..f53ae9ca0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MotorMountHandler.java @@ -0,0 +1,111 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; +import net.sf.openrocket.rocketcomponent.MotorMount; + +import org.xml.sax.SAXException; + +class MotorMountHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final MotorMount mount; + private MotorHandler motorHandler; + + public MotorMountHandler(MotorMount mount, DocumentLoadingContext context) { + this.mount = mount; + this.context = context; + mount.setMotorMount(true); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("motor")) { + motorHandler = new MotorHandler(context); + return motorHandler; + } + + if (element.equals("ignitionevent") || + element.equals("ignitiondelay") || + element.equals("overhang")) { + return PlainTextHandler.INSTANCE; + } + + warnings.add(Warning.fromString("Unknown element '" + element + "' encountered, ignoring.")); + return null; + } + + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + if (element.equals("motor")) { + String id = attributes.get("configid"); + if (id == null || id.equals("")) { + warnings.add(Warning.fromString("Illegal motor specification, ignoring.")); + return; + } + + Motor motor = motorHandler.getMotor(warnings); + mount.setMotor(id, motor); + mount.setMotorDelay(id, motorHandler.getDelay(warnings)); + MotorConfiguration motorConfig = mount.getFlightConfiguration(id); + motorConfig.setIgnitionEvent( motorHandler.getIgnitionEvent()); + motorConfig.setIgnitionDelay( motorHandler.getIgnitionDelay()); + return; + } + + if (element.equals("ignitionevent")) { + MotorConfiguration.IgnitionEvent event = null; + for (MotorConfiguration.IgnitionEvent e : MotorConfiguration.IgnitionEvent.values()) { + if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) { + event = e; + break; + } + } + if (event == null) { + warnings.add(Warning.fromString("Unknown ignition event type '" + content + "', ignoring.")); + return; + } + mount.setDefaultIgnitionEvent(event); + return; + } + + if (element.equals("ignitiondelay")) { + double d; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); + return; + } + mount.setDefaultIgnitionDelay(d); + return; + } + + if (element.equals("overhang")) { + double d; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal overhang specified, ignoring.")); + return; + } + mount.setMotorOverhang(d); + return; + } + + super.closeElement(element, attributes, content, warnings); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketContentHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketContentHandler.java new file mode 100644 index 000000000..58416ab01 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketContentHandler.java @@ -0,0 +1,75 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.rocketcomponent.Rocket; + +/** + * Handles the content of the tag. + */ +class OpenRocketContentHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final OpenRocketDocument doc; + private final Rocket rocket; + + private boolean rocketDefined = false; + private boolean simulationsDefined = false; + private boolean datatypesDefined = false; + + public OpenRocketContentHandler(DocumentLoadingContext context) { + this.context = context; + this.rocket = new Rocket(); + this.doc = new OpenRocketDocument(rocket); + } + + public OpenRocketDocument getDocument() { + if (!rocketDefined) + return null; + return doc; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("rocket")) { + if (rocketDefined) { + warnings.add(Warning + .fromString("Multiple rocket designs within one document, " + + "ignoring later ones.")); + return null; + } + rocketDefined = true; + return new ComponentParameterHandler(rocket, context); + } + + if (element.equals("datatypes")) { + if (datatypesDefined) { + warnings.add(Warning.fromString("Multiple datatype blocks. Ignoring later ones.")); + return null; + } + datatypesDefined = true; + return new DatatypeHandler(this, context); + } + + if (element.equals("simulations")) { + if (simulationsDefined) { + warnings.add(Warning + .fromString("Multiple simulation definitions within one document, " + + "ignoring later ones.")); + return null; + } + simulationsDefined = true; + return new SimulationsHandler(doc, context); + } + + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + + return null; + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketHandler.java new file mode 100644 index 000000000..2518c3b94 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketHandler.java @@ -0,0 +1,104 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; + +import org.xml.sax.SAXException; + +/** + * The starting point of the handlers. Accepts a single element and hands + * the contents to be read by a OpenRocketContentsHandler. + */ +class OpenRocketHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private OpenRocketContentHandler handler = null; + + public OpenRocketHandler(DocumentLoadingContext context) { + this.context = context; + } + + /** + * Return the OpenRocketDocument read from the file, or null if a document + * has not been read yet. + * + * @return the document read, or null. + */ + public OpenRocketDocument getDocument() { + return handler.getDocument(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Check for unknown elements + if (!element.equals("openrocket")) { + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + return null; + } + + // Check for first call + if (handler != null) { + warnings.add(Warning.fromString("Multiple document elements found, ignoring later " + + "ones.")); + return null; + } + + // Check version number + String version = null; + String creator = attributes.remove("creator"); + String docVersion = attributes.remove("version"); + for (String v : DocumentConfig.SUPPORTED_VERSIONS) { + if (v.equals(docVersion)) { + version = v; + break; + } + } + if (version == null) { + String str = "Unsupported document version"; + if (docVersion != null) + str += " " + docVersion; + if (creator != null && !creator.trim().equals("")) + str += " (written using '" + creator.trim() + "')"; + str += ", attempting to read file anyway."; + warnings.add(str); + } + + context.setFileVersion(parseVersion(docVersion)); + + handler = new OpenRocketContentHandler(context); + return handler; + } + + + private int parseVersion(String docVersion) { + if (docVersion == null) + return 0; + + Matcher m = Pattern.compile("^([0-9]+)\\.([0-9]+)$").matcher(docVersion); + if (m.matches()) { + int major = Integer.parseInt(m.group(1)); + int minor = Integer.parseInt(m.group(2)); + return major * DocumentConfig.FILE_VERSION_DIVISOR + minor; + } else { + return 0; + } + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + attributes.remove("version"); + attributes.remove("creator"); + super.closeElement(element, attributes, content, warnings); + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java index e972f0a35..5558a2a7e 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java @@ -2,93 +2,19 @@ package net.sf.openrocket.file.openrocket.importt; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import net.sf.openrocket.aerodynamics.Warning; -import net.sf.openrocket.aerodynamics.WarningSet; -import net.sf.openrocket.appearance.AppearanceBuilder; -import net.sf.openrocket.appearance.Decal.EdgeMode; -import net.sf.openrocket.database.Databases; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.document.Simulation.Status; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.file.AbstractRocketLoader; import net.sf.openrocket.file.MotorFinder; import net.sf.openrocket.file.RocketLoadException; -import net.sf.openrocket.file.simplesax.AbstractElementHandler; -import net.sf.openrocket.file.simplesax.ElementHandler; -import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.file.simplesax.SimpleSAX; import net.sf.openrocket.logging.LogHelper; -import net.sf.openrocket.material.Material; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.preset.ComponentPreset; -import net.sf.openrocket.rocketcomponent.BodyComponent; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.Bulkhead; -import net.sf.openrocket.rocketcomponent.CenteringRing; -import net.sf.openrocket.rocketcomponent.ClusterConfiguration; -import net.sf.openrocket.rocketcomponent.Clusterable; -import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; -import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.EngineBlock; -import net.sf.openrocket.rocketcomponent.ExternalComponent; -import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.IllegalFinPointException; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.InternalComponent; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.MassComponent; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.MotorConfiguration; -import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.Parachute; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; -import net.sf.openrocket.rocketcomponent.RecoveryDevice; -import net.sf.openrocket.rocketcomponent.ReferenceType; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.RocketComponent.Position; -import net.sf.openrocket.rocketcomponent.ShockCord; -import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; -import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration.SeparationEvent; -import net.sf.openrocket.rocketcomponent.Streamer; -import net.sf.openrocket.rocketcomponent.StructuralComponent; -import net.sf.openrocket.rocketcomponent.SymmetricComponent; -import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; -import net.sf.openrocket.rocketcomponent.TubeCoupler; -import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; -import net.sf.openrocket.simulation.FlightEvent; -import net.sf.openrocket.simulation.FlightEvent.Type; -import net.sf.openrocket.simulation.SimulationOptions; -import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.unit.UnitGroup; -import net.sf.openrocket.util.BugException; -import net.sf.openrocket.util.Color; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.GeodeticComputationStrategy; -import net.sf.openrocket.util.LineStyle; -import net.sf.openrocket.util.Reflection; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -168,2386 +94,3 @@ public class OpenRocketLoader extends AbstractRocketLoader { } - - -class DocumentConfig { - - /* Remember to update OpenRocketSaver as well! */ - public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6"}; - - /** - * Divisor used in converting an integer version to the point-represented version. - * The integer version divided by this value is the major version and the remainder is - * the minor version. For example 101 corresponds to file version "1.1". - */ - public static final int FILE_VERSION_DIVISOR = 100; - - - //////// Component constructors - static final HashMap> constructors = new HashMap>(); - static { - try { - // External components - constructors.put("bodytube", BodyTube.class.getConstructor(new Class[0])); - constructors.put("transition", Transition.class.getConstructor(new Class[0])); - constructors.put("nosecone", NoseCone.class.getConstructor(new Class[0])); - constructors.put("trapezoidfinset", TrapezoidFinSet.class.getConstructor(new Class[0])); - constructors.put("ellipticalfinset", EllipticalFinSet.class.getConstructor(new Class[0])); - constructors.put("freeformfinset", FreeformFinSet.class.getConstructor(new Class[0])); - constructors.put("launchlug", LaunchLug.class.getConstructor(new Class[0])); - - // Internal components - constructors.put("engineblock", EngineBlock.class.getConstructor(new Class[0])); - constructors.put("innertube", InnerTube.class.getConstructor(new Class[0])); - constructors.put("tubecoupler", TubeCoupler.class.getConstructor(new Class[0])); - constructors.put("bulkhead", Bulkhead.class.getConstructor(new Class[0])); - constructors.put("centeringring", CenteringRing.class.getConstructor(new Class[0])); - - constructors.put("masscomponent", MassComponent.class.getConstructor(new Class[0])); - constructors.put("shockcord", ShockCord.class.getConstructor(new Class[0])); - constructors.put("parachute", Parachute.class.getConstructor(new Class[0])); - constructors.put("streamer", Streamer.class.getConstructor(new Class[0])); - - // Other - constructors.put("stage", Stage.class.getConstructor(new Class[0])); - - } catch (NoSuchMethodException e) { - throw new BugException( - "Error in constructing the 'constructors' HashMap."); - } - } - - - //////// Parameter setters - /* - * The keys are of the form Class:param, where Class is the class name and param - * the element name. Setters are searched for in descending class order. - * A setter of null means setting the parameter is not allowed. - */ - static final HashMap setters = new HashMap(); - static { - // RocketComponent - setters.put("RocketComponent:name", new StringSetter( - Reflection.findMethod(RocketComponent.class, "setName", String.class))); - setters.put("RocketComponent:color", new ColorSetter( - Reflection.findMethod(RocketComponent.class, "setColor", Color.class))); - setters.put("RocketComponent:linestyle", new EnumSetter( - Reflection.findMethod(RocketComponent.class, "setLineStyle", LineStyle.class), - LineStyle.class)); - setters.put("RocketComponent:position", new PositionSetter()); - setters.put("RocketComponent:overridemass", new OverrideSetter( - Reflection.findMethod(RocketComponent.class, "setOverrideMass", double.class), - Reflection.findMethod(RocketComponent.class, "setMassOverridden", boolean.class))); - setters.put("RocketComponent:overridecg", new OverrideSetter( - Reflection.findMethod(RocketComponent.class, "setOverrideCGX", double.class), - Reflection.findMethod(RocketComponent.class, "setCGOverridden", boolean.class))); - setters.put("RocketComponent:overridesubcomponents", new BooleanSetter( - Reflection.findMethod(RocketComponent.class, "setOverrideSubcomponents", boolean.class))); - setters.put("RocketComponent:comment", new StringSetter( - Reflection.findMethod(RocketComponent.class, "setComment", String.class))); - setters.put("RocketComponent:preset", new ComponentPresetSetter( - Reflection.findMethod(RocketComponent.class, "loadPreset", ComponentPreset.class))); - - // ExternalComponent - setters.put("ExternalComponent:finish", new EnumSetter( - Reflection.findMethod(ExternalComponent.class, "setFinish", Finish.class), - Finish.class)); - setters.put("ExternalComponent:material", new MaterialSetter( - Reflection.findMethod(ExternalComponent.class, "setMaterial", Material.class), - Material.Type.BULK)); - - // BodyComponent - setters.put("BodyComponent:length", new DoubleSetter( - Reflection.findMethod(BodyComponent.class, "setLength", double.class))); - - // SymmetricComponent - setters.put("SymmetricComponent:thickness", new DoubleSetter( - Reflection.findMethod(SymmetricComponent.class, "setThickness", double.class), - "filled", - Reflection.findMethod(SymmetricComponent.class, "setFilled", boolean.class))); - - // BodyTube - setters.put("BodyTube:radius", new DoubleSetter( - Reflection.findMethod(BodyTube.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(BodyTube.class, "setOuterRadiusAutomatic", boolean.class))); - - // Transition - setters.put("Transition:shape", new EnumSetter( - Reflection.findMethod(Transition.class, "setType", Transition.Shape.class), - Transition.Shape.class)); - setters.put("Transition:shapeclipped", new BooleanSetter( - Reflection.findMethod(Transition.class, "setClipped", boolean.class))); - setters.put("Transition:shapeparameter", new DoubleSetter( - Reflection.findMethod(Transition.class, "setShapeParameter", double.class))); - - setters.put("Transition:foreradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeRadius", double.class), - "auto", - Reflection.findMethod(Transition.class, "setForeRadiusAutomatic", boolean.class))); - setters.put("Transition:aftradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftRadius", double.class), - "auto", - Reflection.findMethod(Transition.class, "setAftRadiusAutomatic", boolean.class))); - - setters.put("Transition:foreshoulderradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeShoulderRadius", double.class))); - setters.put("Transition:foreshoulderlength", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeShoulderLength", double.class))); - setters.put("Transition:foreshoulderthickness", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeShoulderThickness", double.class))); - setters.put("Transition:foreshouldercapped", new BooleanSetter( - Reflection.findMethod(Transition.class, "setForeShoulderCapped", boolean.class))); - - setters.put("Transition:aftshoulderradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftShoulderRadius", double.class))); - setters.put("Transition:aftshoulderlength", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftShoulderLength", double.class))); - setters.put("Transition:aftshoulderthickness", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class))); - setters.put("Transition:aftshouldercapped", new BooleanSetter( - Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class))); - - // NoseCone - disable disallowed elements - setters.put("NoseCone:foreradius", null); - setters.put("NoseCone:foreshoulderradius", null); - setters.put("NoseCone:foreshoulderlength", null); - setters.put("NoseCone:foreshoulderthickness", null); - setters.put("NoseCone:foreshouldercapped", null); - - // FinSet - setters.put("FinSet:fincount", new IntSetter( - Reflection.findMethod(FinSet.class, "setFinCount", int.class))); - setters.put("FinSet:rotation", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0)); - setters.put("FinSet:thickness", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setThickness", double.class))); - setters.put("FinSet:crosssection", new EnumSetter( - Reflection.findMethod(FinSet.class, "setCrossSection", FinSet.CrossSection.class), - FinSet.CrossSection.class)); - setters.put("FinSet:cant", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0)); - setters.put("FinSet:tabheight", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setTabHeight", double.class))); - setters.put("FinSet:tablength", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setTabLength", double.class))); - setters.put("FinSet:tabposition", new FinTabPositionSetter()); - - // TrapezoidFinSet - setters.put("TrapezoidFinSet:rootchord", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setRootChord", double.class))); - setters.put("TrapezoidFinSet:tipchord", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setTipChord", double.class))); - setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setSweep", double.class))); - setters.put("TrapezoidFinSet:height", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setHeight", double.class))); - - // EllipticalFinSet - setters.put("EllipticalFinSet:rootchord", new DoubleSetter( - Reflection.findMethod(EllipticalFinSet.class, "setLength", double.class))); - setters.put("EllipticalFinSet:height", new DoubleSetter( - Reflection.findMethod(EllipticalFinSet.class, "setHeight", double.class))); - - // FreeformFinSet points handled as a special handler - - // LaunchLug - setters.put("LaunchLug:radius", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setOuterRadius", double.class))); - setters.put("LaunchLug:length", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setLength", double.class))); - setters.put("LaunchLug:thickness", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setThickness", double.class))); - setters.put("LaunchLug:radialdirection", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // InternalComponent - nothing - - // StructuralComponent - setters.put("StructuralComponent:material", new MaterialSetter( - Reflection.findMethod(StructuralComponent.class, "setMaterial", Material.class), - Material.Type.BULK)); - - // RingComponent - setters.put("RingComponent:length", new DoubleSetter( - Reflection.findMethod(RingComponent.class, "setLength", double.class))); - setters.put("RingComponent:radialposition", new DoubleSetter( - Reflection.findMethod(RingComponent.class, "setRadialPosition", double.class))); - setters.put("RingComponent:radialdirection", new DoubleSetter( - Reflection.findMethod(RingComponent.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // ThicknessRingComponent - radius on separate components due to differing automatics - setters.put("ThicknessRingComponent:thickness", new DoubleSetter( - Reflection.findMethod(ThicknessRingComponent.class, "setThickness", double.class))); - - // EngineBlock - setters.put("EngineBlock:outerradius", new DoubleSetter( - Reflection.findMethod(EngineBlock.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class))); - - // TubeCoupler - setters.put("TubeCoupler:outerradius", new DoubleSetter( - Reflection.findMethod(TubeCoupler.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class))); - - // InnerTube - setters.put("InnerTube:outerradius", new DoubleSetter( - Reflection.findMethod(InnerTube.class, "setOuterRadius", double.class))); - setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter()); - setters.put("InnerTube:clusterscale", new DoubleSetter( - Reflection.findMethod(InnerTube.class, "setClusterScale", double.class))); - setters.put("InnerTube:clusterrotation", new DoubleSetter( - Reflection.findMethod(InnerTube.class, "setClusterRotation", double.class), - Math.PI / 180.0)); - - // RadiusRingComponent - - // Bulkhead - setters.put("RadiusRingComponent:innerradius", new DoubleSetter( - Reflection.findMethod(RadiusRingComponent.class, "setInnerRadius", double.class))); - setters.put("Bulkhead:outerradius", new DoubleSetter( - Reflection.findMethod(Bulkhead.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class))); - - // CenteringRing - setters.put("CenteringRing:innerradius", new DoubleSetter( - Reflection.findMethod(CenteringRing.class, "setInnerRadius", double.class), - "auto", - Reflection.findMethod(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class))); - setters.put("CenteringRing:outerradius", new DoubleSetter( - Reflection.findMethod(CenteringRing.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class))); - - - // MassObject - setters.put("MassObject:packedlength", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setLength", double.class))); - setters.put("MassObject:packedradius", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setRadius", double.class))); - setters.put("MassObject:radialposition", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setRadialPosition", double.class))); - setters.put("MassObject:radialdirection", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // MassComponent - setters.put("MassComponent:mass", new DoubleSetter( - Reflection.findMethod(MassComponent.class, "setComponentMass", double.class))); - - // ShockCord - setters.put("ShockCord:cordlength", new DoubleSetter( - Reflection.findMethod(ShockCord.class, "setCordLength", double.class))); - setters.put("ShockCord:material", new MaterialSetter( - Reflection.findMethod(ShockCord.class, "setMaterial", Material.class), - Material.Type.LINE)); - - // RecoveryDevice - setters.put("RecoveryDevice:cd", new DoubleSetter( - Reflection.findMethod(RecoveryDevice.class, "setCD", double.class), - "auto", - Reflection.findMethod(RecoveryDevice.class, "setCDAutomatic", boolean.class))); - setters.put("RecoveryDevice:deployevent", new EnumSetter( - Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployEvent", DeployEvent.class), - DeployEvent.class)); - setters.put("RecoveryDevice:deployaltitude", new DoubleSetter( - Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployAltitude", double.class))); - setters.put("RecoveryDevice:deploydelay", new DoubleSetter( - Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployDelay", double.class))); - setters.put("RecoveryDevice:material", new MaterialSetter( - Reflection.findMethod(RecoveryDevice.class, "setMaterial", Material.class), - Material.Type.SURFACE)); - - // Parachute - setters.put("Parachute:diameter", new DoubleSetter( - Reflection.findMethod(Parachute.class, "setDiameter", double.class))); - setters.put("Parachute:linecount", new IntSetter( - Reflection.findMethod(Parachute.class, "setLineCount", int.class))); - setters.put("Parachute:linelength", new DoubleSetter( - Reflection.findMethod(Parachute.class, "setLineLength", double.class))); - setters.put("Parachute:linematerial", new MaterialSetter( - Reflection.findMethod(Parachute.class, "setLineMaterial", Material.class), - Material.Type.LINE)); - - // Streamer - setters.put("Streamer:striplength", new DoubleSetter( - Reflection.findMethod(Streamer.class, "setStripLength", double.class))); - setters.put("Streamer:stripwidth", new DoubleSetter( - Reflection.findMethod(Streamer.class, "setStripWidth", double.class))); - - // Rocket - // handled by separate handler - setters.put("Rocket:referencetype", new EnumSetter( - Reflection.findMethod(Rocket.class, "setReferenceType", ReferenceType.class), - ReferenceType.class)); - setters.put("Rocket:customreference", new DoubleSetter( - Reflection.findMethod(Rocket.class, "setCustomReferenceLength", double.class))); - setters.put("Rocket:designer", new StringSetter( - Reflection.findMethod(Rocket.class, "setDesigner", String.class))); - setters.put("Rocket:revision", new StringSetter( - Reflection.findMethod(Rocket.class, "setRevision", String.class))); - - // Stage - setters.put("Stage:separationevent", new EnumSetter( - Reflection.findMethod(Stage.class, "setDefaultSeparationEvent", StageSeparationConfiguration.SeparationEvent.class), - StageSeparationConfiguration.SeparationEvent.class)); - setters.put("Stage:separationdelay", new DoubleSetter( - Reflection.findMethod(Stage.class, "setDefaultSeparationDelay", double.class))); - - } - - - /** - * Search for a enum value that has the corresponding name as an XML value. The current - * conversion from enum name to XML value is to lowercase the name and strip out all - * underscore characters. This method returns a match to these criteria, or null - * if no such enum exists. - * - * @param then enum type. - * @param name the XML value, null ok. - * @param enumClass the class of the enum. - * @return the found enum value, or null. - */ - public static > Enum findEnum(String name, - Class> enumClass) { - - if (name == null) - return null; - name = name.trim(); - for (Enum e : enumClass.getEnumConstants()) { - if (e.name().toLowerCase(Locale.ENGLISH).replace("_", "").equals(name)) { - return e; - } - } - return null; - } - - - /** - * Convert a string to a double including formatting specifications of the OpenRocket - * file format. This accepts all formatting that is valid for - * Double.parseDouble(s) and a few others as well ("Inf", "-Inf"). - * - * @param s the string to parse. - * @return the numerical value. - * @throws NumberFormatException the the string cannot be parsed. - */ - public static double stringToDouble(String s) throws NumberFormatException { - if (s == null) - throw new NumberFormatException("null string"); - if (s.equalsIgnoreCase("NaN")) - return Double.NaN; - if (s.equalsIgnoreCase("Inf")) - return Double.POSITIVE_INFINITY; - if (s.equalsIgnoreCase("-Inf")) - return Double.NEGATIVE_INFINITY; - return Double.parseDouble(s); - } -} - - - - - -/** - * The starting point of the handlers. Accepts a single element and hands - * the contents to be read by a OpenRocketContentsHandler. - */ -class OpenRocketHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private OpenRocketContentHandler handler = null; - - public OpenRocketHandler(DocumentLoadingContext context) { - this.context = context; - } - - /** - * Return the OpenRocketDocument read from the file, or null if a document - * has not been read yet. - * - * @return the document read, or null. - */ - public OpenRocketDocument getDocument() { - return handler.getDocument(); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Check for unknown elements - if (!element.equals("openrocket")) { - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - return null; - } - - // Check for first call - if (handler != null) { - warnings.add(Warning.fromString("Multiple document elements found, ignoring later " - + "ones.")); - return null; - } - - // Check version number - String version = null; - String creator = attributes.remove("creator"); - String docVersion = attributes.remove("version"); - for (String v : DocumentConfig.SUPPORTED_VERSIONS) { - if (v.equals(docVersion)) { - version = v; - break; - } - } - if (version == null) { - String str = "Unsupported document version"; - if (docVersion != null) - str += " " + docVersion; - if (creator != null && !creator.trim().equals("")) - str += " (written using '" + creator.trim() + "')"; - str += ", attempting to read file anyway."; - warnings.add(str); - } - - context.setFileVersion(parseVersion(docVersion)); - - handler = new OpenRocketContentHandler(context); - return handler; - } - - - private int parseVersion(String docVersion) { - if (docVersion == null) - return 0; - - Matcher m = Pattern.compile("^([0-9]+)\\.([0-9]+)$").matcher(docVersion); - if (m.matches()) { - int major = Integer.parseInt(m.group(1)); - int minor = Integer.parseInt(m.group(2)); - return major * DocumentConfig.FILE_VERSION_DIVISOR + minor; - } else { - return 0; - } - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - attributes.remove("version"); - attributes.remove("creator"); - super.closeElement(element, attributes, content, warnings); - } - - -} - - -/** - * Handles the content of the tag. - */ -class OpenRocketContentHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final OpenRocketDocument doc; - private final Rocket rocket; - - private boolean rocketDefined = false; - private boolean simulationsDefined = false; - private boolean datatypesDefined = false; - - public OpenRocketContentHandler(DocumentLoadingContext context) { - this.context = context; - this.rocket = new Rocket(); - this.doc = new OpenRocketDocument(rocket); - } - - public OpenRocketDocument getDocument() { - if (!rocketDefined) - return null; - return doc; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("rocket")) { - if (rocketDefined) { - warnings.add(Warning - .fromString("Multiple rocket designs within one document, " - + "ignoring later ones.")); - return null; - } - rocketDefined = true; - return new ComponentParameterHandler(rocket, context); - } - - if (element.equals("datatypes")) { - if (datatypesDefined) { - warnings.add(Warning.fromString("Multiple datatype blocks. Ignoring later ones.")); - return null; - } - datatypesDefined = true; - return new DatatypeHandler(this, context); - } - - if (element.equals("simulations")) { - if (simulationsDefined) { - warnings.add(Warning - .fromString("Multiple simulation definitions within one document, " - + "ignoring later ones.")); - return null; - } - simulationsDefined = true; - return new SimulationsHandler(doc, context); - } - - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - - return null; - } -} - -class DatatypeHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final OpenRocketContentHandler contentHandler; - private CustomExpressionHandler customExpressionHandler = null; - - public DatatypeHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { - this.context = context; - this.contentHandler = contentHandler; - } - - @Override - public ElementHandler openElement(String element, - HashMap attributes, WarningSet warnings) - throws SAXException { - - if (element.equals("type") && attributes.get("source").equals("customexpression")) { - customExpressionHandler = new CustomExpressionHandler(contentHandler, context); - return customExpressionHandler; - } - else { - warnings.add(Warning.fromString("Unknown datatype " + element + " defined, ignoring")); - } - - return this; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { - attributes.remove("source"); - super.closeElement(element, attributes, content, warnings); - - if (customExpressionHandler != null) { - contentHandler.getDocument().addCustomExpression(customExpressionHandler.currentExpression); - } - - } - -} - -class CustomExpressionHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final OpenRocketContentHandler contentHandler; - public CustomExpression currentExpression; - - public CustomExpressionHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { - this.context = context; - this.contentHandler = contentHandler; - currentExpression = new CustomExpression(contentHandler.getDocument()); - - } - - @Override - public ElementHandler openElement(String element, - HashMap attributes, WarningSet warnings) - throws SAXException { - - return this; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - if (element.equals("type")) { - contentHandler.getDocument().addCustomExpression(currentExpression); - } - - if (element.equals("name")) { - currentExpression.setName(content); - } - - if (element.equals("symbol")) { - currentExpression.setSymbol(content); - } - - if (element.equals("unit") && attributes.get("unittype").equals("auto")) { - currentExpression.setUnit(content); - } - - if (element.equals("expression")) { - currentExpression.setExpression(content); - } - } -} - -/** - * A handler that creates components from the corresponding elements. The control of the - * contents is passed on to ComponentParameterHandler. - */ -class ComponentHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final RocketComponent parent; - - public ComponentHandler(RocketComponent parent, DocumentLoadingContext context) { - this.parent = parent; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Attempt to construct new component - Constructor constructor = DocumentConfig.constructors - .get(element); - if (constructor == null) { - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - return null; - } - - RocketComponent c; - try { - c = constructor.newInstance(); - } catch (InstantiationException e) { - throw new BugException("Error constructing component.", e); - } catch (IllegalAccessException e) { - throw new BugException("Error constructing component.", e); - } catch (InvocationTargetException e) { - throw Reflection.handleWrappedException(e); - } - - parent.addChild(c); - - return new ComponentParameterHandler(c, context); - } -} - - -/** - * A handler that populates the parameters of a previously constructed rocket component. - * This uses the setters, or delegates the handling to another handler for specific - * elements. - */ -class ComponentParameterHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final RocketComponent component; - - public ComponentParameterHandler(RocketComponent c, DocumentLoadingContext context) { - this.component = c; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Check for specific elements that contain other elements - if (element.equals("subcomponents")) { - return new ComponentHandler(component, context); - } - if ( element.equals("appearance")) { - return new AppearanceHandler(component,context); - } - if (element.equals("motormount")) { - if (!(component instanceof MotorMount)) { - warnings.add(Warning.fromString("Illegal component defined as motor mount.")); - return null; - } - return new MotorMountHandler((MotorMount) component, context); - } - if (element.equals("finpoints")) { - if (!(component instanceof FreeformFinSet)) { - warnings.add(Warning.fromString("Illegal component defined for fin points.")); - return null; - } - return new FinSetPointHandler((FreeformFinSet) component, context); - } - if (element.equals("motorconfiguration")) { - if (!(component instanceof Rocket)) { - warnings.add(Warning.fromString("Illegal component defined for motor configuration.")); - return null; - } - return new MotorConfigurationHandler((Rocket) component, context); - } - if ( element.equals("deploymentconfiguration")) { - if ( !(component instanceof RecoveryDevice) ) { - warnings.add(Warning.fromString("Illegal component defined as recovery device.")); - return null; - } - return new DeploymentConfigurationHandler( (RecoveryDevice) component, context ); - } - if ( element.equals("separationconfiguration")) { - if ( !(component instanceof Stage) ) { - warnings.add(Warning.fromString("Illegal component defined as stage.")); - return null; - } - return new StageSeparationConfigurationHandler( (Stage) component, context ); - } - - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("subcomponents") || element.equals("motormount") || - element.equals("finpoints") || element.equals("motorconfiguration") || - element.equals("appearance") || element.equals("deploymentconfiguration") || - element.equals("separationconfiguration")) { - return; - } - - // Search for the correct setter class - - Class c; - for (c = component.getClass(); c != null; c = c.getSuperclass()) { - String setterKey = c.getSimpleName() + ":" + element; - Setter s = DocumentConfig.setters.get(setterKey); - if (s != null) { - // Setter found - s.set(component, content, attributes, warnings); - break; - } - if (DocumentConfig.setters.containsKey(setterKey)) { - // Key exists but is null -> invalid parameter - c = null; - break; - } - } - if (c == null) { - warnings.add(Warning.fromString("Unknown parameter type '" + element + "' for " - + component.getComponentName() + ", ignoring.")); - } - } -} - -class AppearanceHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final RocketComponent component; - - private final AppearanceBuilder builder = new AppearanceBuilder(); - private boolean isInDecal = false; - public AppearanceHandler( RocketComponent component, DocumentLoadingContext context ) { - this.context = context; - this.component = component; - } - @Override - public ElementHandler openElement(String element,HashMap attributes, WarningSet warnings) - throws SAXException { - if ( "decal".equals(element) ) { - String name = attributes.remove("name"); - builder.setImage(name); - double rotation = Double.parseDouble(attributes.remove("rotation")); - builder.setRotation(rotation); - String edgeModeName = attributes.remove("edgemode"); - EdgeMode edgeMode = EdgeMode.valueOf(edgeModeName); - builder.setEdgeMode(edgeMode); - isInDecal = true; - return this; - } - return PlainTextHandler.INSTANCE; - } - @Override - public void closeElement(String element,HashMap attributes, String content, WarningSet warnings) throws SAXException { - if ( "ambient".equals(element) ) { - int red = Integer.parseInt(attributes.get("red")); - int green = Integer.parseInt(attributes.get("green")); - int blue = Integer.parseInt(attributes.get("blue")); - builder.setAmbient( new Color(red,green,blue)); - return; - } - if ( "diffuse".equals(element) ) { - int red = Integer.parseInt(attributes.get("red")); - int green = Integer.parseInt(attributes.get("green")); - int blue = Integer.parseInt(attributes.get("blue")); - builder.setDiffuse( new Color(red,green,blue)); - return; - } - if ( "specular".equals(element) ) { - int red = Integer.parseInt(attributes.get("red")); - int green = Integer.parseInt(attributes.get("green")); - int blue = Integer.parseInt(attributes.get("blue")); - builder.setSpecular( new Color(red,green,blue)); - return; - } - if ( isInDecal && "center".equals(element) ) { - double x = Double.parseDouble(attributes.get("x")); - double y = Double.parseDouble(attributes.get("y")); - builder.setCenter(x,y); - return; - } - if ( isInDecal && "offset".equals(element) ) { - double x = Double.parseDouble(attributes.get("x")); - double y = Double.parseDouble(attributes.get("y")); - builder.setOffset(x,y); - return; - } - if ( isInDecal && "scale".equals(element) ) { - double x = Double.parseDouble(attributes.get("x")); - double y = Double.parseDouble(attributes.get("y")); - builder.setScale(x,y); - return; - } - if( isInDecal && "decal".equals(element) ) { - isInDecal = false; - return; - } - - super.closeElement(element, attributes, content, warnings); - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - if ( "decal".equals(element) ) { - isInDecal = false; - return; - } - component.setAppearance(builder.getAppearance()); - super.endHandler(element, attributes, content, warnings); - } - -} - - -/** - * A handler that reads the specifications within the freeformfinset's - * elements. - */ -class FinSetPointHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final FreeformFinSet finset; - private final ArrayList coordinates = new ArrayList(); - - public FinSetPointHandler(FreeformFinSet finset, DocumentLoadingContext context) { - this.finset = finset; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - String strx = attributes.remove("x"); - String stry = attributes.remove("y"); - if (strx == null || stry == null) { - warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); - return; - } - try { - double x = Double.parseDouble(strx); - double y = Double.parseDouble(stry); - coordinates.add(new Coordinate(x, y)); - } catch (NumberFormatException e) { - warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); - return; - } - - super.closeElement(element, attributes, content, warnings); - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - try { - finset.setPoints(coordinates.toArray(new Coordinate[0])); - } catch (IllegalFinPointException e) { - warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring.")); - } - } -} - - -class MotorMountHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final MotorMount mount; - private MotorHandler motorHandler; - - public MotorMountHandler(MotorMount mount, DocumentLoadingContext context) { - this.mount = mount; - this.context = context; - mount.setMotorMount(true); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("motor")) { - motorHandler = new MotorHandler(context); - return motorHandler; - } - - if (element.equals("ignitionevent") || - element.equals("ignitiondelay") || - element.equals("overhang")) { - return PlainTextHandler.INSTANCE; - } - - warnings.add(Warning.fromString("Unknown element '" + element + "' encountered, ignoring.")); - return null; - } - - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - if (element.equals("motor")) { - String id = attributes.get("configid"); - if (id == null || id.equals("")) { - warnings.add(Warning.fromString("Illegal motor specification, ignoring.")); - return; - } - - Motor motor = motorHandler.getMotor(warnings); - mount.setMotor(id, motor); - mount.setMotorDelay(id, motorHandler.getDelay(warnings)); - MotorConfiguration motorConfig = mount.getFlightConfiguration(id); - motorConfig.setIgnitionEvent( motorHandler.getIgnitionEvent()); - motorConfig.setIgnitionDelay( motorHandler.getIgnitionDelay()); - return; - } - - if (element.equals("ignitionevent")) { - MotorConfiguration.IgnitionEvent event = null; - for (MotorConfiguration.IgnitionEvent e : MotorConfiguration.IgnitionEvent.values()) { - if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) { - event = e; - break; - } - } - if (event == null) { - warnings.add(Warning.fromString("Unknown ignition event type '" + content + "', ignoring.")); - return; - } - mount.setDefaultIgnitionEvent(event); - return; - } - - if (element.equals("ignitiondelay")) { - double d; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException nfe) { - warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); - return; - } - mount.setDefaultIgnitionDelay(d); - return; - } - - if (element.equals("overhang")) { - double d; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException nfe) { - warnings.add(Warning.fromString("Illegal overhang specified, ignoring.")); - return; - } - mount.setMotorOverhang(d); - return; - } - - super.closeElement(element, attributes, content, warnings); - } -} - - - - -class MotorConfigurationHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final Rocket rocket; - private String name = null; - private boolean inNameElement = false; - - public MotorConfigurationHandler(Rocket rocket, DocumentLoadingContext context) { - this.rocket = rocket; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (inNameElement || !element.equals("name")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return null; - } - inNameElement = true; - - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - name = content; - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - String configid = attributes.remove("configid"); - if (configid == null || configid.equals("")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (!rocket.addMotorConfigurationID(configid)) { - warnings.add("Duplicate motor configuration ID used."); - return; - } - - if (name != null && name.trim().length() > 0) { - rocket.setFlightConfigurationName(configid, name); - } - - if ("true".equals(attributes.remove("default"))) { - rocket.getDefaultConfiguration().setFlightConfigurationID(configid); - } - - super.closeElement(element, attributes, content, warnings); - } -} - - -class MotorHandler extends AbstractElementHandler { - /** File version where latest digest format was introduced */ - private static final int MOTOR_DIGEST_VERSION = 104; - - private final DocumentLoadingContext context; - private Motor.Type type = null; - private String manufacturer = null; - private String designation = null; - private String digest = null; - private double diameter = Double.NaN; - private double length = Double.NaN; - private double delay = Double.NaN; - - private Double ignitionDelay = null; - private MotorConfiguration.IgnitionEvent ignitionEvent = null; - - public MotorHandler(DocumentLoadingContext context) { - this.context = context; - } - - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - - /** - * Return the motor to use, or null. - */ - public Motor getMotor(WarningSet warnings) { - return context.getMotorFinder().findMotor(type, manufacturer, designation, diameter, length, digest, warnings); - } - - /** - * Return the delay to use for the motor. - */ - public double getDelay(WarningSet warnings) { - if (Double.isNaN(delay)) { - warnings.add(Warning.fromString("Motor delay not specified, assuming no ejection charge.")); - return Motor.PLUGGED; - } - return delay; - } - - public Double getIgnitionDelay() { - return ignitionDelay; - } - - public MotorConfiguration.IgnitionEvent getIgnitionEvent() { - return ignitionEvent; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - content = content.trim(); - - if (element.equals("type")) { - - // Motor type - type = null; - for (Motor.Type t : Motor.Type.values()) { - if (t.name().toLowerCase(Locale.ENGLISH).equals(content.trim())) { - type = t; - break; - } - } - if (type == null) { - warnings.add(Warning.fromString("Unknown motor type '" + content + "', ignoring.")); - } - - } else if (element.equals("manufacturer")) { - - // Manufacturer - manufacturer = content.trim(); - - } else if (element.equals("designation")) { - - // Designation - designation = content.trim(); - - } else if (element.equals("digest")) { - - // Digest is used only for file versions saved using the same digest algorithm - if (context.getFileVersion() >= MOTOR_DIGEST_VERSION) { - digest = content.trim(); - } - - } else if (element.equals("diameter")) { - - // Diameter - diameter = Double.NaN; - try { - diameter = Double.parseDouble(content.trim()); - } catch (NumberFormatException e) { - // Ignore - } - if (Double.isNaN(diameter)) { - warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); - } - - } else if (element.equals("length")) { - - // Length - length = Double.NaN; - try { - length = Double.parseDouble(content.trim()); - } catch (NumberFormatException ignore) { - } - - if (Double.isNaN(length)) { - warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); - } - - } else if (element.equals("delay")) { - - // Delay - delay = Double.NaN; - if (content.equals("none")) { - delay = Motor.PLUGGED; - } else { - try { - delay = Double.parseDouble(content.trim()); - } catch (NumberFormatException ignore) { - } - - if (Double.isNaN(delay)) { - warnings.add(Warning.fromString("Illegal motor delay specified, ignoring.")); - } - - } - - } else if ( element.equals("ignitionevent")) { - - for (MotorConfiguration.IgnitionEvent e : MotorConfiguration.IgnitionEvent.values()) { - if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) { - ignitionEvent = e; - break; - } - } - if (ignitionEvent == null) { - warnings.add(Warning.fromString("Unknown ignition event type '" + content + "', ignoring.")); - } - - } else if ( element.equals("ignitiondelay")) { - try { - ignitionDelay = Double.parseDouble(content); - } catch (NumberFormatException nfe) { - warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); - } - } else { - super.closeElement(element, attributes, content, warnings); - } - } - -} - -class DeploymentConfigurationHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final RecoveryDevice recoveryDevice; - private DeploymentConfiguration config; - private String configId; - - public DeploymentConfigurationHandler( RecoveryDevice recoveryDevice, DocumentLoadingContext context ) { - this.recoveryDevice = recoveryDevice; - this.context = context; - config = new DeploymentConfiguration(); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) - throws SAXException { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, - WarningSet warnings) throws SAXException { - - content = content.trim(); - - if ( "deployevent".equals(element) ) { - DeployEvent type = (DeployEvent) DocumentConfig.findEnum(content, DeployEvent.class); - if ( type == null ) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - config.setDeployEvent( type ); - return; - } else if ( "deployaltitude".equals(element) ) { - config.setDeployAltitude( Double.parseDouble(content)); - return; - } else if ( "deploydelay".equals(element) ) { - config.setDeployDelay( Double.parseDouble(content)); - return; - } - super.closeElement(element, attributes, content, warnings); - - } - - @Override - public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { - String configId = attributes.get("configid"); - recoveryDevice.setFlightConfiguration(configId, config); - } - -} - -class StageSeparationConfigurationHandler extends AbstractElementHandler { - private final Stage stage; - private StageSeparationConfiguration config; - - public StageSeparationConfigurationHandler( Stage stage, DocumentLoadingContext context ) { - this.stage = stage; - config = new StageSeparationConfiguration(); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) - throws SAXException { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, - WarningSet warnings) throws SAXException { - - content = content.trim(); - - if ( "separationevent".equals(element) ) { - SeparationEvent type = (SeparationEvent) DocumentConfig.findEnum(content, SeparationEvent.class); - if ( type == null ) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - config.setSeparationEvent( type ); - return; - } else if ( "separationdelay".equals(element) ) { - config.setSeparationDelay( Double.parseDouble(content)); - return; - } - super.closeElement(element, attributes, content, warnings); - - } - - @Override - public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { - String configId = attributes.get("configid"); - stage.setFlightConfiguration(configId, config); - } - -} - -class SimulationsHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final OpenRocketDocument doc; - private SingleSimulationHandler handler; - - public SimulationsHandler(OpenRocketDocument doc, DocumentLoadingContext context) { - this.doc = doc; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (!element.equals("simulation")) { - warnings.add("Unknown element '" + element + "', ignoring."); - return null; - } - - handler = new SingleSimulationHandler(doc, context); - return handler; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - attributes.remove("status"); - - //Finished loading. Rebuilding custom expressions in case something has changed such as listener variable come available. - for (CustomExpression exp : doc.getCustomExpressions()){ - exp.setExpression(exp.getExpressionString()); - } - - super.closeElement(element, attributes, content, warnings); - } -} - -class SingleSimulationHandler extends AbstractElementHandler { - - private final DocumentLoadingContext context; - - private final OpenRocketDocument doc; - - private String name; - - private SimulationConditionsHandler conditionHandler; - private FlightDataHandler dataHandler; - - private final List listeners = new ArrayList(); - - public SingleSimulationHandler(OpenRocketDocument doc, DocumentLoadingContext context) { - this.doc = doc; - this.context = context; - } - - public OpenRocketDocument getDocument() { - return doc; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("name") || element.equals("simulator") || - element.equals("calculator") || element.equals("listener")) { - return PlainTextHandler.INSTANCE; - } else if (element.equals("conditions")) { - conditionHandler = new SimulationConditionsHandler(doc.getRocket(), context); - return conditionHandler; - } else if (element.equals("flightdata")) { - dataHandler = new FlightDataHandler(this, context); - return dataHandler; - } else { - warnings.add("Unknown element '" + element + "', ignoring."); - return null; - } - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("name")) { - name = content; - } else if (element.equals("simulator")) { - if (!content.trim().equals("RK4Simulator")) { - warnings.add("Unknown simulator '" + content.trim() + "' specified, ignoring."); - } - } else if (element.equals("calculator")) { - if (!content.trim().equals("BarrowmanCalculator")) { - warnings.add("Unknown calculator '" + content.trim() + "' specified, ignoring."); - } - } else if (element.equals("listener") && content.trim().length() > 0) { - listeners.add(content.trim()); - } - - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - - String s = attributes.get("status"); - Simulation.Status status = (Status) DocumentConfig.findEnum(s, Simulation.Status.class); - if (status == null) { - warnings.add("Simulation status unknown, assuming outdated."); - status = Simulation.Status.OUTDATED; - } - - SimulationOptions conditions; - if (conditionHandler != null) { - conditions = conditionHandler.getConditions(); - } else { - warnings.add("Simulation conditions not defined, using defaults."); - conditions = new SimulationOptions(doc.getRocket()); - } - - if (name == null) - name = "Simulation"; - - FlightData data; - if (dataHandler == null) - data = null; - else - data = dataHandler.getFlightData(); - - Simulation simulation = new Simulation(doc.getRocket(), status, name, - conditions, listeners, data); - - doc.addSimulation(simulation); - } -} - -class SimulationConditionsHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private SimulationOptions conditions; - private AtmosphereHandler atmosphereHandler; - - public SimulationConditionsHandler(Rocket rocket, DocumentLoadingContext context) { - this.context = context; - conditions = new SimulationOptions(rocket); - // Set up default loading settings (which may differ from the new defaults) - conditions.setGeodeticComputation(GeodeticComputationStrategy.FLAT); - } - - public SimulationOptions getConditions() { - return conditions; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - if (element.equals("atmosphere")) { - atmosphereHandler = new AtmosphereHandler(attributes.get("model"), context); - return atmosphereHandler; - } - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - double d = Double.NaN; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException ignore) { - } - - - if (element.equals("configid")) { - if (content.equals("")) { - conditions.setMotorConfigurationID(null); - } else { - conditions.setMotorConfigurationID(content); - } - } else if (element.equals("launchrodlength")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod length defined, ignoring."); - } else { - conditions.setLaunchRodLength(d); - } - } else if (element.equals("launchrodangle")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod angle defined, ignoring."); - } else { - conditions.setLaunchRodAngle(d * Math.PI / 180); - } - } else if (element.equals("launchroddirection")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod direction defined, ignoring."); - } else { - conditions.setLaunchRodDirection(d * Math.PI / 180); - } - } else if (element.equals("windaverage")) { - if (Double.isNaN(d)) { - warnings.add("Illegal average windspeed defined, ignoring."); - } else { - conditions.setWindSpeedAverage(d); - } - } else if (element.equals("windturbulence")) { - if (Double.isNaN(d)) { - warnings.add("Illegal wind turbulence intensity defined, ignoring."); - } else { - conditions.setWindTurbulenceIntensity(d); - } - } else if (element.equals("launchaltitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch altitude defined, ignoring."); - } else { - conditions.setLaunchAltitude(d); - } - } else if (element.equals("launchlatitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch latitude defined, ignoring."); - } else { - conditions.setLaunchLatitude(d); - } - } else if (element.equals("launchlongitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch longitude."); - } else { - conditions.setLaunchLongitude(d); - } - } else if (element.equals("geodeticmethod")) { - GeodeticComputationStrategy gcs = - (GeodeticComputationStrategy) DocumentConfig.findEnum(content, GeodeticComputationStrategy.class); - if (gcs != null) { - conditions.setGeodeticComputation(gcs); - } else { - warnings.add("Unknown geodetic computation method '" + content + "'"); - } - } else if (element.equals("atmosphere")) { - atmosphereHandler.storeSettings(conditions, warnings); - } else if (element.equals("timestep")) { - if (Double.isNaN(d) || d <= 0 ) { - warnings.add("Illegal time step defined, ignoring."); - } else { - conditions.setTimeStep(d); - } - } - } -} - - -class AtmosphereHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final String model; - private double temperature = Double.NaN; - private double pressure = Double.NaN; - - public AtmosphereHandler(String model, DocumentLoadingContext context) { - this.model = model; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - double d = Double.NaN; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException ignore) { - } - - if (element.equals("basetemperature")) { - if (Double.isNaN(d)) { - warnings.add("Illegal base temperature specified, ignoring."); - } - temperature = d; - } else if (element.equals("basepressure")) { - if (Double.isNaN(d)) { - warnings.add("Illegal base pressure specified, ignoring."); - } - pressure = d; - } else { - super.closeElement(element, attributes, content, warnings); - } - } - - - public void storeSettings(SimulationOptions cond, WarningSet warnings) { - if (!Double.isNaN(pressure)) { - cond.setLaunchPressure(pressure); - } - if (!Double.isNaN(temperature)) { - cond.setLaunchTemperature(temperature); - } - - if ("isa".equals(model)) { - cond.setISAAtmosphere(true); - } else if ("extendedisa".equals(model)) { - cond.setISAAtmosphere(false); - } else { - cond.setISAAtmosphere(true); - warnings.add("Unknown atmospheric model, using ISA."); - } - } - -} - - -class FlightDataHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - - private FlightDataBranchHandler dataHandler; - private WarningSet warningSet = new WarningSet(); - private List branches = new ArrayList(); - - private SingleSimulationHandler simHandler; - private FlightData data; - - - public FlightDataHandler(SingleSimulationHandler simHandler, DocumentLoadingContext context) { - this.context = context; - this.simHandler = simHandler; - } - - public FlightData getFlightData() { - return data; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("warning")) { - return PlainTextHandler.INSTANCE; - } - if (element.equals("databranch")) { - if (attributes.get("name") == null || attributes.get("types") == null) { - warnings.add("Illegal flight data definition, ignoring."); - return null; - } - dataHandler = new FlightDataBranchHandler(attributes.get("name"), - attributes.get("types"), - simHandler, context); - return dataHandler; - } - - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return null; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("databranch")) { - FlightDataBranch branch = dataHandler.getBranch(); - if (branch.getLength() > 0) { - branches.add(branch); - } - } else if (element.equals("warning")) { - warningSet.add(Warning.fromString(content)); - } - } - - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (branches.size() > 0) { - data = new FlightData(branches.toArray(new FlightDataBranch[0])); - } else { - double maxAltitude = Double.NaN; - double maxVelocity = Double.NaN; - double maxAcceleration = Double.NaN; - double maxMach = Double.NaN; - double timeToApogee = Double.NaN; - double flightTime = Double.NaN; - double groundHitVelocity = Double.NaN; - double launchRodVelocity = Double.NaN; - double deploymentVelocity = Double.NaN; - - try { - maxAltitude = DocumentConfig.stringToDouble(attributes.get("maxaltitude")); - } catch (NumberFormatException ignore) { - } - try { - maxVelocity = DocumentConfig.stringToDouble(attributes.get("maxvelocity")); - } catch (NumberFormatException ignore) { - } - try { - maxAcceleration = DocumentConfig.stringToDouble(attributes.get("maxacceleration")); - } catch (NumberFormatException ignore) { - } - try { - maxMach = DocumentConfig.stringToDouble(attributes.get("maxmach")); - } catch (NumberFormatException ignore) { - } - try { - timeToApogee = DocumentConfig.stringToDouble(attributes.get("timetoapogee")); - } catch (NumberFormatException ignore) { - } - try { - flightTime = DocumentConfig.stringToDouble(attributes.get("flighttime")); - } catch (NumberFormatException ignore) { - } - try { - groundHitVelocity = - DocumentConfig.stringToDouble(attributes.get("groundhitvelocity")); - } catch (NumberFormatException ignore) { - } - try { - launchRodVelocity = DocumentConfig.stringToDouble(attributes.get("launchrodvelocity")); - } catch (NumberFormatException ignore) { - } - try { - deploymentVelocity = DocumentConfig.stringToDouble(attributes.get("deploymentvelocity")); - } catch (NumberFormatException ignore) { - } - - data = new FlightData(maxAltitude, maxVelocity, maxAcceleration, maxMach, - timeToApogee, flightTime, groundHitVelocity, launchRodVelocity, deploymentVelocity); - } - - data.getWarningSet().addAll(warningSet); - data.immute(); - } - - -} - - -class FlightDataBranchHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final FlightDataType[] types; - private final FlightDataBranch branch; - - private static final LogHelper log = Application.getLogger(); - private final SingleSimulationHandler simHandler; - - public FlightDataBranchHandler(String name, String typeList, SingleSimulationHandler simHandler, DocumentLoadingContext context) { - this.simHandler = simHandler; - this.context = context; - String[] split = typeList.split(","); - types = new FlightDataType[split.length]; - for (int i = 0; i < split.length; i++) { - String typeName = split[i]; - FlightDataType matching = findFlightDataType(typeName); - types[i] = matching; - //types[i] = FlightDataType.getType(typeName, matching.getSymbol(), matching.getUnitGroup()); - } - - // TODO: LOW: May throw an IllegalArgumentException - branch = new FlightDataBranch(name, types); - } - - // Find the full flight data type given name only - // Note: this way of doing it requires that custom expressions always come before flight data in the file, - // not the nicest but this is always the case anyway. - private FlightDataType findFlightDataType(String name) { - - // Kevins version with lookup by key. Not using right now - /* - if ( key != null ) { - for (FlightDataType t : FlightDataType.ALL_TYPES){ - if (t.getKey().equals(key) ){ - return t; - } - } - } - */ - - // Look in built in types - for (FlightDataType t : FlightDataType.ALL_TYPES) { - if (t.getName().equals(name)) { - return t; - } - } - - // Look in custom expressions - for (CustomExpression exp : simHandler.getDocument().getCustomExpressions()) { - if (exp.getName().equals(name)) { - return exp.getType(); - } - } - - log.warn("Could not find the flight data type '" + name + "' used in the XML file. Substituted type with unknown symbol and units."); - return FlightDataType.getType(name, "Unknown", UnitGroup.UNITS_NONE); - } - - public FlightDataBranch getBranch() { - branch.immute(); - return branch; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("datapoint")) - return PlainTextHandler.INSTANCE; - if (element.equals("event")) - return PlainTextHandler.INSTANCE; - - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return null; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("event")) { - double time; - FlightEvent.Type type; - - try { - time = DocumentConfig.stringToDouble(attributes.get("time")); - } catch (NumberFormatException e) { - warnings.add("Illegal event specification, ignoring."); - return; - } - - type = (Type) DocumentConfig.findEnum(attributes.get("type"), FlightEvent.Type.class); - if (type == null) { - warnings.add("Illegal event specification, ignoring."); - return; - } - - branch.addEvent(new FlightEvent(type, time)); - return; - } - - if (!element.equals("datapoint")) { - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return; - } - - // element == "datapoint" - - - // Check line format - String[] split = content.split(","); - if (split.length != types.length) { - warnings.add("Data point did not contain correct amount of values, ignoring point."); - return; - } - - // Parse the doubles - double[] values = new double[split.length]; - for (int i = 0; i < values.length; i++) { - try { - values[i] = DocumentConfig.stringToDouble(split[i]); - } catch (NumberFormatException e) { - warnings.add("Data point format error, ignoring point."); - return; - } - } - - // Add point to branch - branch.addPoint(); - for (int i = 0; i < types.length; i++) { - branch.setValue(types[i], values[i]); - } - } -} - - - - - -///////////////// Setters implementation - - -//// Interface -interface Setter { - /** - * Set the specified value to the given component. - * - * @param component the component to which to set. - * @param value the value within the element. - * @param attributes attributes for the element. - * @param warnings the warning set to use. - */ - public void set(RocketComponent component, String value, - HashMap attributes, WarningSet warnings); -} - - -//// StringSetter - sets the value to the contained String -class StringSetter implements Setter { - private final Reflection.Method setMethod; - - public StringSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - setMethod.invoke(c, s); - } -} - -//// IntSetter - set an integer value -class IntSetter implements Setter { - private final Reflection.Method setMethod; - - public IntSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - try { - int n = Integer.parseInt(s); - setMethod.invoke(c, n); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - -//// BooleanSetter - set a boolean value -class BooleanSetter implements Setter { - private final Reflection.Method setMethod; - - public BooleanSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - s = s.trim(); - if (s.equalsIgnoreCase("true")) { - setMethod.invoke(c, true); - } else if (s.equalsIgnoreCase("false")) { - setMethod.invoke(c, false); - } else { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - - -//// DoubleSetter - sets a double value or (alternatively) if a specific string is encountered -//// calls a setXXX(boolean) method. -class DoubleSetter implements Setter { - private final Reflection.Method setMethod; - private final String specialString; - private final Reflection.Method specialMethod; - private final double multiplier; - - /** - * Set only the double value. - * @param set set method for the double value. - */ - public DoubleSetter(Reflection.Method set) { - this.setMethod = set; - this.specialString = null; - this.specialMethod = null; - this.multiplier = 1.0; - } - - /** - * Multiply with the given multiplier and set the double value. - * @param set set method for the double value. - * @param mul multiplier. - */ - public DoubleSetter(Reflection.Method set, double mul) { - this.setMethod = set; - this.specialString = null; - this.specialMethod = null; - this.multiplier = mul; - } - - /** - * Set the double value, or if the value equals the special string, use the - * special setter and set it to true. - * - * @param set double setter. - * @param special special string - * @param specialMethod boolean setter. - */ - public DoubleSetter(Reflection.Method set, String special, - Reflection.Method specialMethod) { - this.setMethod = set; - this.specialString = special; - this.specialMethod = specialMethod; - this.multiplier = 1.0; - } - - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - s = s.trim(); - - // Check for special case - if (specialMethod != null && s.equalsIgnoreCase(specialString)) { - specialMethod.invoke(c, true); - return; - } - - // Normal case - try { - double d = Double.parseDouble(s); - setMethod.invoke(c, d * multiplier); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - -class OverrideSetter implements Setter { - private final Reflection.Method setMethod; - private final Reflection.Method enabledMethod; - - public OverrideSetter(Reflection.Method set, Reflection.Method enabledMethod) { - this.setMethod = set; - this.enabledMethod = enabledMethod; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - try { - double d = Double.parseDouble(s); - setMethod.invoke(c, d); - enabledMethod.invoke(c, true); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - -//// EnumSetter - sets a generic enum type -class EnumSetter> implements Setter { - private final Reflection.Method setter; - private final Class enumClass; - - public EnumSetter(Reflection.Method set, Class enumClass) { - this.setter = set; - this.enumClass = enumClass; - } - - @Override - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - - Enum setEnum = DocumentConfig.findEnum(name, enumClass); - if (setEnum == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - setter.invoke(c, setEnum); - } -} - - -//// ColorSetter - sets a Color value -class ColorSetter implements Setter { - private final Reflection.Method setMethod; - - public ColorSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - String red = attributes.get("red"); - String green = attributes.get("green"); - String blue = attributes.get("blue"); - - if (red == null || green == null || blue == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - int r, g, b; - try { - r = Integer.parseInt(red); - g = Integer.parseInt(green); - b = Integer.parseInt(blue); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - Color color = new Color(r, g, b); - setMethod.invoke(c, color); - - if (!s.trim().equals("")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - -////ComponentPresetSetter - sets a ComponentPreset value -class ComponentPresetSetter implements Setter { - private final Reflection.Method setMethod; - - public ComponentPresetSetter(Reflection.Method set) { - this.setMethod = set; - } - - @Override - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - String manufacturerName = attributes.get("manufacturer"); - if (manufacturerName == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no manufacturer specified. Ignored")); - return; - } - - String productNo = attributes.get("partno"); - if (productNo == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no partno specified. Ignored")); - return; - } - - String digest = attributes.get("digest"); - if (digest == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no digest specified.")); - } - - String type = attributes.get("type"); - if (type == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no type specified.")); - } - - List presets = Application.getComponentPresetDao().find(manufacturerName, productNo); - - ComponentPreset matchingPreset = null; - - for (ComponentPreset preset : presets) { - if (digest != null && preset.getDigest().equals(digest)) { - // Found one with matching digest. Take it. - matchingPreset = preset; - break; - } - if (type != null && preset.getType().name().equals(type) && matchingPreset != null) { - // Found the first one with matching type. - matchingPreset = preset; - } - } - - // Was any found? - if (matchingPreset == null) { - warnings.add(Warning.fromString("No matching ComponentPreset for component " + c.getName() + " found matching " + manufacturerName + " " + productNo)); - return; - } - - if (digest != null && !matchingPreset.getDigest().equals(digest)) { - warnings.add(Warning.fromString("ComponentPreset for component " + c.getName() + " has wrong digest")); - } - - setMethod.invoke(c, matchingPreset); - } -} - - -////MaterialSetter - sets a Material value -class MaterialSetter implements Setter { - private final Reflection.Method setMethod; - private final Material.Type type; - - public MaterialSetter(Reflection.Method set, Material.Type type) { - this.setMethod = set; - this.type = type; - } - - @Override - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - - Material mat; - - // Check name != "" - name = name.trim(); - if (name.equals("")) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - - // Parse density - double density; - String str; - str = attributes.remove("density"); - if (str == null) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - try { - density = Double.parseDouble(str); - } catch (NumberFormatException e) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - - // Parse thickness - // double thickness = 0; - // str = attributes.remove("thickness"); - // try { - // if (str != null) - // thickness = Double.parseDouble(str); - // } catch (NumberFormatException e){ - // warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - // return; - // } - - // Check type if specified - str = attributes.remove("type"); - if (str != null && !type.name().toLowerCase(Locale.ENGLISH).equals(str)) { - warnings.add(Warning.fromString("Illegal material type specified, ignoring.")); - return; - } - - mat = Databases.findMaterial(type, name, density); - - setMethod.invoke(c, mat); - } -} - - - - - -class PositionSetter implements Setter { - - @Override - public void set(RocketComponent c, String value, HashMap attributes, - WarningSet warnings) { - - RocketComponent.Position type = (Position) DocumentConfig.findEnum(attributes.get("type"), - RocketComponent.Position.class); - if (type == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - double pos; - try { - pos = Double.parseDouble(value); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (c instanceof FinSet) { - ((FinSet) c).setRelativePosition(type); - c.setPositionValue(pos); - } else if (c instanceof LaunchLug) { - ((LaunchLug) c).setRelativePosition(type); - c.setPositionValue(pos); - } else if (c instanceof InternalComponent) { - ((InternalComponent) c).setRelativePosition(type); - c.setPositionValue(pos); - } else { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - - } -} - - -class FinTabPositionSetter extends DoubleSetter { - - public FinTabPositionSetter() { - super(Reflection.findMethod(FinSet.class, "setTabShift", double.class)); - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - if (!(c instanceof FinSet)) { - throw new IllegalStateException("FinTabPositionSetter called for component " + c); - } - - String relative = attributes.get("relativeto"); - FinSet.TabRelativePosition position = - (TabRelativePosition) DocumentConfig.findEnum(relative, - FinSet.TabRelativePosition.class); - - if (position != null) { - - ((FinSet) c).setTabRelativePosition(position); - - } else { - if (relative == null) { - warnings.add("Required attribute 'relativeto' not found for fin tab position."); - } else { - warnings.add("Illegal attribute value '" + relative + "' encountered."); - } - } - - super.set(c, s, attributes, warnings); - } - - -} - - -class ClusterConfigurationSetter implements Setter { - - @Override - public void set(RocketComponent component, String value, HashMap attributes, - WarningSet warnings) { - - if (!(component instanceof Clusterable)) { - warnings.add("Illegal component defined as cluster."); - return; - } - - ClusterConfiguration config = null; - for (ClusterConfiguration c : ClusterConfiguration.CONFIGURATIONS) { - if (c.getXMLName().equals(value)) { - config = c; - break; - } - } - - if (config == null) { - warnings.add("Illegal cluster configuration specified."); - return; - } - - ((Clusterable) component).setClusterConfiguration(config); - } -} diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OverrideSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/OverrideSetter.java new file mode 100644 index 000000000..1cdf8585b --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OverrideSetter.java @@ -0,0 +1,31 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +class OverrideSetter implements Setter { + private final Reflection.Method setMethod; + private final Reflection.Method enabledMethod; + + public OverrideSetter(Reflection.Method set, Reflection.Method enabledMethod) { + this.setMethod = set; + this.enabledMethod = enabledMethod; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + try { + double d = Double.parseDouble(s); + setMethod.invoke(c, d); + enabledMethod.invoke(c, true); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java new file mode 100644 index 000000000..fc7d00779 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java @@ -0,0 +1,48 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.InternalComponent; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.RocketComponent.Position; + +class PositionSetter implements Setter { + + @Override + public void set(RocketComponent c, String value, HashMap attributes, + WarningSet warnings) { + + RocketComponent.Position type = (Position) DocumentConfig.findEnum(attributes.get("type"), + RocketComponent.Position.class); + if (type == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + double pos; + try { + pos = Double.parseDouble(value); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (c instanceof FinSet) { + ((FinSet) c).setRelativePosition(type); + c.setPositionValue(pos); + } else if (c instanceof LaunchLug) { + ((LaunchLug) c).setRelativePosition(type); + c.setPositionValue(pos); + } else if (c instanceof InternalComponent) { + ((InternalComponent) c).setRelativePosition(type); + c.setPositionValue(pos); + } else { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/Setter.java b/core/src/net/sf/openrocket/file/openrocket/importt/Setter.java new file mode 100644 index 000000000..79dc82aa0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/Setter.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +//// Interface +interface Setter { + /** + * Set the specified value to the given component. + * + * @param component the component to which to set. + * @param value the value within the element. + * @param attributes attributes for the element. + * @param warnings the warning set to use. + */ + public void set(RocketComponent component, String value, + HashMap attributes, WarningSet warnings); +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/SimulationConditionsHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationConditionsHandler.java new file mode 100644 index 000000000..4dbb34c12 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationConditionsHandler.java @@ -0,0 +1,122 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.simulation.SimulationOptions; +import net.sf.openrocket.util.GeodeticComputationStrategy; + +class SimulationConditionsHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private SimulationOptions conditions; + private AtmosphereHandler atmosphereHandler; + + public SimulationConditionsHandler(Rocket rocket, DocumentLoadingContext context) { + this.context = context; + conditions = new SimulationOptions(rocket); + // Set up default loading settings (which may differ from the new defaults) + conditions.setGeodeticComputation(GeodeticComputationStrategy.FLAT); + } + + public SimulationOptions getConditions() { + return conditions; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + if (element.equals("atmosphere")) { + atmosphereHandler = new AtmosphereHandler(attributes.get("model"), context); + return atmosphereHandler; + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + double d = Double.NaN; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException ignore) { + } + + + if (element.equals("configid")) { + if (content.equals("")) { + conditions.setMotorConfigurationID(null); + } else { + conditions.setMotorConfigurationID(content); + } + } else if (element.equals("launchrodlength")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod length defined, ignoring."); + } else { + conditions.setLaunchRodLength(d); + } + } else if (element.equals("launchrodangle")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod angle defined, ignoring."); + } else { + conditions.setLaunchRodAngle(d * Math.PI / 180); + } + } else if (element.equals("launchroddirection")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod direction defined, ignoring."); + } else { + conditions.setLaunchRodDirection(d * Math.PI / 180); + } + } else if (element.equals("windaverage")) { + if (Double.isNaN(d)) { + warnings.add("Illegal average windspeed defined, ignoring."); + } else { + conditions.setWindSpeedAverage(d); + } + } else if (element.equals("windturbulence")) { + if (Double.isNaN(d)) { + warnings.add("Illegal wind turbulence intensity defined, ignoring."); + } else { + conditions.setWindTurbulenceIntensity(d); + } + } else if (element.equals("launchaltitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch altitude defined, ignoring."); + } else { + conditions.setLaunchAltitude(d); + } + } else if (element.equals("launchlatitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch latitude defined, ignoring."); + } else { + conditions.setLaunchLatitude(d); + } + } else if (element.equals("launchlongitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch longitude."); + } else { + conditions.setLaunchLongitude(d); + } + } else if (element.equals("geodeticmethod")) { + GeodeticComputationStrategy gcs = + (GeodeticComputationStrategy) DocumentConfig.findEnum(content, GeodeticComputationStrategy.class); + if (gcs != null) { + conditions.setGeodeticComputation(gcs); + } else { + warnings.add("Unknown geodetic computation method '" + content + "'"); + } + } else if (element.equals("atmosphere")) { + atmosphereHandler.storeSettings(conditions, warnings); + } else if (element.equals("timestep")) { + if (Double.isNaN(d) || d <= 0 ) { + warnings.add("Illegal time step defined, ignoring."); + } else { + conditions.setTimeStep(d); + } + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/SimulationsHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationsHandler.java new file mode 100644 index 000000000..a083af58e --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationsHandler.java @@ -0,0 +1,48 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.simulation.customexpression.CustomExpression; + +import org.xml.sax.SAXException; + +class SimulationsHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final OpenRocketDocument doc; + private SingleSimulationHandler handler; + + public SimulationsHandler(OpenRocketDocument doc, DocumentLoadingContext context) { + this.doc = doc; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (!element.equals("simulation")) { + warnings.add("Unknown element '" + element + "', ignoring."); + return null; + } + + handler = new SingleSimulationHandler(doc, context); + return handler; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + attributes.remove("status"); + + //Finished loading. Rebuilding custom expressions in case something has changed such as listener variable come available. + for (CustomExpression exp : doc.getCustomExpressions()){ + exp.setExpression(exp.getExpressionString()); + } + + super.closeElement(element, attributes, content, warnings); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java new file mode 100644 index 000000000..dfb7fccc4 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java @@ -0,0 +1,111 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.document.Simulation.Status; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.SimulationOptions; + +class SingleSimulationHandler extends AbstractElementHandler { + + private final DocumentLoadingContext context; + + private final OpenRocketDocument doc; + + private String name; + + private SimulationConditionsHandler conditionHandler; + private FlightDataHandler dataHandler; + + private final List listeners = new ArrayList(); + + public SingleSimulationHandler(OpenRocketDocument doc, DocumentLoadingContext context) { + this.doc = doc; + this.context = context; + } + + public OpenRocketDocument getDocument() { + return doc; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("name") || element.equals("simulator") || + element.equals("calculator") || element.equals("listener")) { + return PlainTextHandler.INSTANCE; + } else if (element.equals("conditions")) { + conditionHandler = new SimulationConditionsHandler(doc.getRocket(), context); + return conditionHandler; + } else if (element.equals("flightdata")) { + dataHandler = new FlightDataHandler(this, context); + return dataHandler; + } else { + warnings.add("Unknown element '" + element + "', ignoring."); + return null; + } + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("name")) { + name = content; + } else if (element.equals("simulator")) { + if (!content.trim().equals("RK4Simulator")) { + warnings.add("Unknown simulator '" + content.trim() + "' specified, ignoring."); + } + } else if (element.equals("calculator")) { + if (!content.trim().equals("BarrowmanCalculator")) { + warnings.add("Unknown calculator '" + content.trim() + "' specified, ignoring."); + } + } else if (element.equals("listener") && content.trim().length() > 0) { + listeners.add(content.trim()); + } + + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + + String s = attributes.get("status"); + Simulation.Status status = (Status) DocumentConfig.findEnum(s, Simulation.Status.class); + if (status == null) { + warnings.add("Simulation status unknown, assuming outdated."); + status = Simulation.Status.OUTDATED; + } + + SimulationOptions conditions; + if (conditionHandler != null) { + conditions = conditionHandler.getConditions(); + } else { + warnings.add("Simulation conditions not defined, using defaults."); + conditions = new SimulationOptions(doc.getRocket()); + } + + if (name == null) + name = "Simulation"; + + FlightData data; + if (dataHandler == null) + data = null; + else + data = dataHandler.getFlightData(); + + Simulation simulation = new Simulation(doc.getRocket(), status, name, + conditions, listeners, data); + + doc.addSimulation(simulation); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/StageSeparationConfigurationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/StageSeparationConfigurationHandler.java new file mode 100644 index 000000000..24728f64d --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/StageSeparationConfigurationHandler.java @@ -0,0 +1,59 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration.SeparationEvent; + +import org.xml.sax.SAXException; + +class StageSeparationConfigurationHandler extends AbstractElementHandler { + private final Stage stage; + private StageSeparationConfiguration config; + + public StageSeparationConfigurationHandler( Stage stage, DocumentLoadingContext context ) { + this.stage = stage; + config = new StageSeparationConfiguration(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) + throws SAXException { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, + WarningSet warnings) throws SAXException { + + content = content.trim(); + + if ( "separationevent".equals(element) ) { + SeparationEvent type = (SeparationEvent) DocumentConfig.findEnum(content, SeparationEvent.class); + if ( type == null ) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + config.setSeparationEvent( type ); + return; + } else if ( "separationdelay".equals(element) ) { + config.setSeparationDelay( Double.parseDouble(content)); + return; + } + super.closeElement(element, attributes, content, warnings); + + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + String configId = attributes.get("configid"); + stage.setFlightConfiguration(configId, config); + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/StringSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/StringSetter.java new file mode 100644 index 000000000..b1c6fc1c4 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/StringSetter.java @@ -0,0 +1,22 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// StringSetter - sets the value to the contained String +class StringSetter implements Setter { + private final Reflection.Method setMethod; + + public StringSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + setMethod.invoke(c, s); + } +} \ No newline at end of file