Merge commit '42b2e5ca519766e37ce6941ba4faecc9691cc403' into upstream
[debian/openrocket] / core / src / net / sf / openrocket / file / openrocket / importt / OpenRocketLoader.java
index 313fa981a158d96206c6f9d2aa410e85e706020f..4efdf9dbd5f962b15ce2bccbec136884c2796d63 100644 (file)
@@ -7,6 +7,7 @@ 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;
 
@@ -27,6 +28,7 @@ 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;
@@ -71,6 +73,7 @@ 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;
@@ -163,7 +166,7 @@ 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" };
+       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.
@@ -235,6 +238,8 @@ class DocumentConfig {
                                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>(
@@ -508,7 +513,7 @@ class DocumentConfig {
                        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;
                        }
                }
@@ -644,6 +649,7 @@ class OpenRocketContentHandler extends AbstractElementHandler {
        
        private boolean rocketDefined = false;
        private boolean simulationsDefined = false;
+       private boolean datatypesDefined = false;
        
        public OpenRocketContentHandler(DocumentLoadingContext context) {
                this.context = context;
@@ -651,7 +657,6 @@ class OpenRocketContentHandler extends AbstractElementHandler {
                this.doc = new OpenRocketDocument(rocket);
        }
        
-       
        public OpenRocketDocument getDocument() {
                if (!rocketDefined)
                        return null;
@@ -673,6 +678,15 @@ class OpenRocketContentHandler extends AbstractElementHandler {
                        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
@@ -690,8 +704,90 @@ class OpenRocketContentHandler extends AbstractElementHandler {
        }
 }
 
+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
@@ -926,7 +1022,7 @@ class MotorMountHandler extends AbstractElementHandler {
                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;
                                }
@@ -1084,7 +1180,7 @@ class MotorHandler extends AbstractElementHandler {
                        // 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;
                                }
@@ -1190,13 +1286,18 @@ class SimulationsHandler extends AbstractElementHandler {
        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 AbstractElementHandler {
+       
        private final DocumentLoadingContext context;
        
        private final OpenRocketDocument doc;
@@ -1213,7 +1314,9 @@ class SingleSimulationHandler extends AbstractElementHandler {
                this.context = context;
        }
        
-       
+       public OpenRocketDocument getDocument() {
+               return doc;
+       }
        
        @Override
        public ElementHandler openElement(String element, HashMap<String, String> attributes,
@@ -1226,7 +1329,7 @@ class SingleSimulationHandler extends AbstractElementHandler {
                        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.");
@@ -1289,8 +1392,6 @@ class SingleSimulationHandler extends AbstractElementHandler {
        }
 }
 
-
-
 class SimulationConditionsHandler extends AbstractElementHandler {
        private final DocumentLoadingContext context;
        private SimulationOptions conditions;
@@ -1475,11 +1576,13 @@ class FlightDataHandler extends AbstractElementHandler {
        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() {
@@ -1499,7 +1602,8 @@ class FlightDataHandler extends AbstractElementHandler {
                                return null;
                        }
                        dataHandler = new FlightDataBranchHandler(attributes.get("name"),
-                                       attributes.get("types"), context);
+                                       attributes.get("types"),
+                                       simHandler, context);
                        return dataHandler;
                }
                
@@ -1596,18 +1700,59 @@ class FlightDataBranchHandler extends AbstractElementHandler {
        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;
@@ -1933,8 +2078,71 @@ class ColorSetter implements Setter {
        }
 }
 
+////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;
@@ -1985,12 +2193,12 @@ class MaterialSetter implements Setter {
                
                // 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);
        }
@@ -1999,6 +2207,7 @@ class MaterialSetter implements Setter {
 
 
 
+
 class PositionSetter implements Setter {
        
        @Override