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.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.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;
class DocumentConfig {
/* Remember to update OpenRocketSaver as well! */
- public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4" };
+ public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5" };
/**
* Divisor used in converting an integer version to the point-represented version.
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<Finish>(
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<Stage.SeparationEvent>(
+ Reflection.findMethod(Stage.class, "setSeparationEvent", Stage.SeparationEvent.class),
+ Stage.SeparationEvent.class));
+ setters.put("Stage:separationdelay", new DoubleSetter(
+ Reflection.findMethod(Stage.class, "setSeparationDelay", double.class)));
+
}
return null;
name = name.trim();
for (Enum<T> e : enumClass.getEnumConstants()) {
- if (e.name().toLowerCase().replace("_", "").equals(name)) {
+ if (e.name().toLowerCase(Locale.ENGLISH).replace("_", "").equals(name)) {
return e;
}
}
* The starting point of the handlers. Accepts a single <openrocket> element and hands
* the contents to be read by a OpenRocketContentsHandler.
*/
-class OpenRocketHandler extends ElementHandler {
+class OpenRocketHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private OpenRocketContentHandler handler = null;
/**
* Handles the content of the <openrocket> tag.
*/
-class OpenRocketContentHandler extends ElementHandler {
+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.doc = new OpenRocketDocument(rocket);
}
-
public OpenRocketDocument getDocument() {
if (!rocketDefined)
return null;
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
}
}
+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<String, String> 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<String, String> 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 {
+ 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<String, String> attributes, WarningSet warnings)
+ throws SAXException {
+
+ return this;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> 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 ElementHandler {
+class ComponentHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private final RocketComponent parent;
* This uses the setters, or delegates the handling to another handler for specific
* elements.
*/
-class ComponentParameterHandler extends ElementHandler {
+class ComponentParameterHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private final RocketComponent component;
* A handler that reads the <point> specifications within the freeformfinset's
* <finpoints> elements.
*/
-class FinSetPointHandler extends ElementHandler {
+class FinSetPointHandler extends AbstractElementHandler {
@SuppressWarnings("unused")
private final DocumentLoadingContext context;
private final FreeformFinSet finset;
}
-class MotorMountHandler extends ElementHandler {
+class MotorMountHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private final MotorMount mount;
private MotorHandler motorHandler;
if (element.equals("ignitionevent")) {
MotorMount.IgnitionEvent event = null;
for (MotorMount.IgnitionEvent e : MotorMount.IgnitionEvent.values()) {
- if (e.name().toLowerCase().replaceAll("_", "").equals(content)) {
+ if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) {
event = e;
break;
}
-class MotorConfigurationHandler extends ElementHandler {
+class MotorConfigurationHandler extends AbstractElementHandler {
@SuppressWarnings("unused")
private final DocumentLoadingContext context;
private final Rocket rocket;
}
-class MotorHandler extends ElementHandler {
+class MotorHandler extends AbstractElementHandler {
/** File version where latest digest format was introduced */
private static final int MOTOR_DIGEST_VERSION = 104;
// Motor type
type = null;
for (Motor.Type t : Motor.Type.values()) {
- if (t.name().toLowerCase().equals(content.trim())) {
+ if (t.name().toLowerCase(Locale.ENGLISH).equals(content.trim())) {
type = t;
break;
}
-class SimulationsHandler extends ElementHandler {
+class SimulationsHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private final OpenRocketDocument doc;
private SingleSimulationHandler handler;
public void closeElement(String element, HashMap<String, String> 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 ElementHandler {
+class SingleSimulationHandler extends AbstractElementHandler {
+
private final DocumentLoadingContext context;
private final OpenRocketDocument doc;
this.context = context;
}
-
+ public OpenRocketDocument getDocument() {
+ return doc;
+ }
@Override
public ElementHandler openElement(String element, HashMap<String, String> attributes,
conditionHandler = new SimulationConditionsHandler(doc.getRocket(), context);
return conditionHandler;
} else if (element.equals("flightdata")) {
- dataHandler = new FlightDataHandler(context);
+ dataHandler = new FlightDataHandler(this, context);
return dataHandler;
} else {
warnings.add("Unknown element '" + element + "', ignoring.");
}
}
-
-
-class SimulationConditionsHandler extends ElementHandler {
+class SimulationConditionsHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private SimulationOptions conditions;
private AtmosphereHandler atmosphereHandler;
}
-class AtmosphereHandler extends ElementHandler {
+class AtmosphereHandler extends AbstractElementHandler {
@SuppressWarnings("unused")
private final DocumentLoadingContext context;
private final String model;
}
-class FlightDataHandler extends ElementHandler {
+class FlightDataHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private FlightDataBranchHandler dataHandler;
private WarningSet warningSet = new WarningSet();
private List<FlightDataBranch> branches = new ArrayList<FlightDataBranch>();
+ private SingleSimulationHandler simHandler;
private FlightData data;
- public FlightDataHandler(DocumentLoadingContext context) {
+ public FlightDataHandler(SingleSimulationHandler simHandler, DocumentLoadingContext context) {
this.context = context;
+ this.simHandler = simHandler;
}
public FlightData getFlightData() {
return null;
}
dataHandler = new FlightDataBranchHandler(attributes.get("name"),
- attributes.get("types"), context);
+ attributes.get("types"),
+ simHandler, context);
return dataHandler;
}
}
-class FlightDataBranchHandler extends ElementHandler {
+class FlightDataBranchHandler extends AbstractElementHandler {
@SuppressWarnings("unused")
private final DocumentLoadingContext context;
private final FlightDataType[] types;
private final FlightDataBranch branch;
- public FlightDataBranchHandler(String name, String typeList, DocumentLoadingContext context) {
+ 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++) {
- types[i] = FlightDataType.getType(split[i], UnitGroup.UNITS_NONE);
+ 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;
}
}
+////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<String, String> 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<ComponentPreset> 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;
// Check type if specified
str = attributes.remove("type");
- if (str != null && !type.name().toLowerCase().equals(str)) {
+ 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, false);
+ mat = Databases.findMaterial(type, name, density);
setMethod.invoke(c, mat);
}
+
class PositionSetter implements Setter {
@Override