Merge commit '42b2e5ca519766e37ce6941ba4faecc9691cc403' into upstream
[debian/openrocket] / core / src / net / sf / openrocket / file / rocksim / importt / MassObjectHandler.java
index 53a33d8b027e23925ccbfaa41888c296c5cc5ca0..2d918af9a4c49bc5d7951c51967228b364e25014 100644 (file)
@@ -4,11 +4,16 @@
 package net.sf.openrocket.file.rocksim.importt;
 
 import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.rocksim.RocksimCommonConstants;
+import net.sf.openrocket.file.rocksim.RocksimDensityType;
 import net.sf.openrocket.file.simplesax.ElementHandler;
 import net.sf.openrocket.file.simplesax.PlainTextHandler;
 import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.Coaxial;
 import net.sf.openrocket.rocketcomponent.MassComponent;
+import net.sf.openrocket.rocketcomponent.MassObject;
 import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.ShockCord;
 import org.xml.sax.SAXException;
 
 import java.util.HashMap;
@@ -16,14 +21,14 @@ import java.util.HashMap;
 /**
  * A SAX handler for Rocksim's MassObject XML type.
  */
-class MassObjectHandler extends PositionDependentHandler<MassComponent> {
+class MassObjectHandler extends PositionDependentHandler<MassObject> {
 
-    /** 
+    /**
      * The Rocksim Mass length fudge factor.  Rocksim completely exaggerates the length of a mass object to the point
      * that it looks ridiculous in OpenRocket.  This fudge factor is here merely to get the typical mass object to
-     * render in the OpenRocket UI with it's bounds mostly inside it's parent.  The odd thing about it is that 
-     * Rocksim does not expose the length of a mass object in the UI and actually treats mass objects as point objects -
-     * not 3 or even 2 dimensional.
+     * render in the OpenRocket UI with it's bounds mostly inside it's parent.  The odd thing about it is that Rocksim
+     * does not expose the length of a mass object in the UI and actually treats mass objects as point objects - not 3
+     * or even 2 dimensional.
      */
     public static final int MASS_LEN_FUDGE_FACTOR = 100;
 
@@ -33,21 +38,35 @@ class MassObjectHandler extends PositionDependentHandler<MassComponent> {
     private final MassComponent mass;
 
     /**
-     * Constructor.
-     *l
-     * @param c the parent component
-     * @param warnings  the warning set
-     * 
-     * @throws IllegalArgumentException  thrown if <code>c</code> is null
+     * Reference to answer for getComponent().
+     */
+    private MassObject current;
+
+    /**
+     * Parent.
+     */
+    private RocketComponent parent;
+
+    /**
+     * 0 == General, 1 == Shock Cord
+     */
+    private int typeCode = 0;
+
+    /**
+     * Constructor. l
+     *
+     * @param c        the parent component
+     * @param warnings the warning set
+     *
+     * @throws IllegalArgumentException thrown if <code>c</code> is null
      */
     public MassObjectHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException {
         if (c == null) {
             throw new IllegalArgumentException("The parent component of a mass component may not be null.");
         }
         mass = new MassComponent();
-        if (isCompatible(c, MassComponent.class, warnings)) {
-            c.addChild(mass);
-        }
+        current = mass;
+        parent = c;
     }
 
     @Override
@@ -60,19 +79,25 @@ class MassObjectHandler extends PositionDependentHandler<MassComponent> {
             throws SAXException {
         super.closeElement(element, attributes, content, warnings);
         try {
-            if ("Len".equals(element)) {
-                mass.setLength(Double.parseDouble(content) / (RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH * MASS_LEN_FUDGE_FACTOR));
+            if (RocksimCommonConstants.LEN.equals(element)) {
+                mass.setLength(Double.parseDouble(content) / (RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH));
             }
-            if ("KnownMass".equals(element)) {
-                mass.setComponentMass(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
+            if (RocksimCommonConstants.KNOWN_MASS.equals(element)) {
+                mass.setComponentMass(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS);
             }
-            if ("KnownCG".equals(element)) {
+            if (RocksimCommonConstants.KNOWN_CG.equals(element)) {
                 //Setting the CG of the Mass Object to 0 is important because of the different ways that Rocksim and
                 //OpenRocket treat mass objects.  Rocksim treats them as points (even though the data file contains a
                 //length) and because Rocksim sets the CG of the mass object to really be relative to the front of
                 //the parent.  But that value is already assumed in the position and position value for the component.
                 //Thus it needs to be set to 0 to say that the mass object's CG is at the point of the mass object.
-                super.setCG(0); 
+                super.setCG(0);
+            }
+            if (RocksimCommonConstants.TYPE_CODE.equals(element)) {
+                typeCode = Integer.parseInt(content);
+            }
+            if (RocksimCommonConstants.MATERIAL.equals(element)) {
+                setMaterialName(content);
             }
         }
         catch (NumberFormatException nfe) {
@@ -80,14 +105,80 @@ class MassObjectHandler extends PositionDependentHandler<MassComponent> {
         }
     }
 
+    @Override
+    public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws
+                                                                                                                    SAXException {
+        if (inferAsShockCord(typeCode, warnings)) { //Shock Cord
+            mapMassObjectAsShockCord(element, attributes, content, warnings);
+        }
+        else { // typeCode == 0   General Mass Object
+            if (isCompatible(parent, MassComponent.class, warnings)) {
+                parent.addChild(mass);
+            }
+            super.endHandler(element, attributes, content, warnings);
+        }
+    }
+
+    /**
+     * Rocksim does not have a separate entity for Shock Cords.  It has to be inferred.  Sometimes the typeCode
+     * indicates it's a shock cord, but most times it does not.  This is due to bugs in the Rocksim Component and
+     * Material databases.  Try to infer a shock cord based on it's length and it's material type.  It's somewhat
+     * arbitrary, but if the mass object's length is more than twice the length of it's parent component and it's a LINE
+     * material, then assume a shock cord.
+     *
+     * @param theTypeCode the code from the RKT XML file
+     *
+     * @return true if we think it's a shock cord
+     */
+    private boolean inferAsShockCord(int theTypeCode, WarningSet warnings) {
+        return (theTypeCode == 1 || (mass.getLength() >= 2 * parent.getLength() && RocksimDensityType.ROCKSIM_LINE
+                .equals(getDensityType()))) && isCompatible(parent, ShockCord.class, warnings, true);
+    }
+
+    /**
+     * If it appears that the mass object is a shock cord, then create an OR shock cord instance.
+     *
+     * @param element    the element name
+     * @param attributes the attributes
+     * @param content    the content of the element
+     * @param warnings   the warning set to store warnings in.
+     *
+     * @throws org.xml.sax.SAXException not thrown
+     */
+    private void mapMassObjectAsShockCord(final String element, final HashMap<String, String> attributes,
+                                          final String content, final WarningSet warnings) throws SAXException {
+        ShockCord cord = new ShockCord();
+        current = cord;
+        if (isCompatible(parent, ShockCord.class, warnings)) {
+            parent.addChild(cord);
+        }
+        super.endHandler(element, attributes, content, warnings);
+        cord.setName(mass.getName());
+
+        setOverride(cord, mass.isMassOverridden(), mass.getOverrideMass(), mass.getOverrideCGX());
+
+        cord.setRadialDirection(mass.getRadialDirection());
+        cord.setRadialPosition(mass.getRadialPosition());
+        cord.setRadius(mass.getRadius());
+
+        //Rocksim does not distinguish between total length of the cord and the packed length.  Fudge the
+        //packed length and set the real length.
+        cord.setCordLength(mass.getLength());
+        cord.setLength(cord.getCordLength() / MASS_LEN_FUDGE_FACTOR);
+        if (parent instanceof Coaxial) {
+            Coaxial parentCoaxial = (Coaxial) parent;
+            cord.setRadius(parentCoaxial.getInnerRadius());
+        }
+    }
+
     /**
-     * Get the component this handler is working upon.
+     * Get the component this handler is working upon.  This changes depending upon the type of mass object.
      *
      * @return a component
      */
     @Override
-    public MassComponent getComponent() {
-        return mass;
+    public MassObject getComponent() {
+        return current;
     }
 
     /**
@@ -97,17 +188,18 @@ class MassObjectHandler extends PositionDependentHandler<MassComponent> {
      * @param position the OpenRocket position
      */
     public void setRelativePosition(RocketComponent.Position position) {
-        mass.setRelativePosition(position);
+        current.setRelativePosition(position);
     }
 
     /**
-     * Get the required type of material for this component.  Does not apply to MassComponents.
+     * Get the required type of material for this component.  Does not apply to MassComponents, but does apply to Shock
+     * Cords.
      *
-     * @return BULK
+     * @return LINE
      */
     @Override
     public Material.Type getMaterialType() {
-        return Material.Type.BULK;
+        return Material.Type.LINE;
     }
 
 }