Merge commit '42b2e5ca519766e37ce6941ba4faecc9691cc403' into upstream
[debian/openrocket] / core / src / net / sf / openrocket / rocketcomponent / RocketComponent.java
index b1a57dbfc8e7445a75a8b30bd926f126b3bbc519..9000a5400fa2ef77672604f95c4409b2b9f6749b 100644 (file)
@@ -1,8 +1,6 @@
 package net.sf.openrocket.rocketcomponent;
 
-import java.util.ArrayDeque;
 import java.util.Collection;
-import java.util.Deque;
 import java.util.EventListener;
 import java.util.Iterator;
 import java.util.List;
@@ -21,13 +19,14 @@ import net.sf.openrocket.util.Invalidator;
 import net.sf.openrocket.util.LineStyle;
 import net.sf.openrocket.util.MathUtil;
 import net.sf.openrocket.util.SafetyMutex;
+import net.sf.openrocket.util.SimpleStack;
 import net.sf.openrocket.util.UniqueID;
 
 
 public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent> {
        private static final LogHelper log = Application.getLogger();
        private static final Translator trans = Application.getTranslator();
-       
+
        /*
         * Text is suitable to the form
         *    Position relative to:  <title>
@@ -48,38 +47,38 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                /** Specify an absolute X-coordinate position. */
                //// Tip of the nose cone
                ABSOLUTE(trans.get("RocketComponent.Position.ABSOLUTE"));
-               
+
                private String title;
-               
+
                Position(String title) {
                        this.title = title;
                }
-               
+
                @Override
                public String toString() {
                        return title;
                }
        }
-       
+
        /**
         * A safety mutex that can be used to prevent concurrent access to this component.
         */
        protected SafetyMutex mutex = SafetyMutex.newInstance();
-       
+
        ////////  Parent/child trees
        /**
         * Parent component of the current component, or null if none exists.
         */
        private RocketComponent parent = null;
-       
+
        /**
         * List of child components of this component.
         */
        private ArrayList<RocketComponent> children = new ArrayList<RocketComponent>();
-       
-       
+
+
        ////////  Parameters common to all components:
-       
+
        /**
         * Characteristic length of the component.  This is used in calculating the coordinate
         * transformations and positions of other components in reference to this component.
@@ -87,56 +86,56 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * By default it is zero, i.e. no translation.
         */
        protected double length = 0;
-       
+
        /**
         * Positioning of this component relative to the parent component.
         */
        protected Position relativePosition;
-       
+
        /**
         * Offset of the position of this component relative to the normal position given by
         * relativePosition.  By default zero, i.e. no position change.
         */
        protected double position = 0;
-       
-       
+
+
        // Color of the component, null means to use the default color
        private Color color = null;
        private LineStyle lineStyle = null;
-       
-       
+
+
        // Override mass/CG
        private double overrideMass = 0;
        private boolean massOverriden = false;
        private double overrideCGX = 0;
        private boolean cgOverriden = false;
-       
+
        private boolean overrideSubcomponents = false;
-       
-       
+
+
        // User-given name of the component
        private String name = null;
-       
+
        // User-specified comment
        private String comment = "";
-       
+
        // Unique ID of the component
        private String id = null;
-       
+
        // Preset component this component is based upon
        private ComponentPreset presetComponent = null;
-       
-       
+
+
        /**
         * Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}.
         */
        private Invalidator invalidator = new Invalidator(this);
-       
-       
+
+
        ////  NOTE !!!  All fields must be copied in the method copyFrom()!  ////
-       
-       
-       
+
+
+
        /**
         * Default constructor.  Sets the name of the component to the component's static name
         * and the relative position of the component.
@@ -147,26 +146,26 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                this.relativePosition = relativePosition;
                newID();
        }
-       
+
        ////////////  Methods that must be implemented  ////////////
-       
-       
+
+
        /**
         * Static component name.  The name may not vary of the parameters, it must be static.
         */
        public abstract String getComponentName(); // Static component type name
-       
+
        /**
         * Return the component mass (regardless of mass overriding).
         */
        public abstract double getComponentMass(); // Mass of non-overridden component
-       
+
        /**
         * Return the component CG and mass (regardless of CG or mass overriding).
         */
        public abstract Coordinate getComponentCG(); // CG of non-overridden component
-       
-       
+
+
        /**
         * Return the longitudinal (around the y- or z-axis) unitary moment of inertia.
         * The unitary moment of inertia is the moment of inertia with the assumption that
@@ -176,8 +175,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * @return   the longitudinal unitary moment of inertia of this component.
         */
        public abstract double getLongitudinalUnitInertia();
-       
-       
+
+
        /**
         * Return the rotational (around the x-axis) unitary moment of inertia.
         * The unitary moment of inertia is the moment of inertia with the assumption that
@@ -187,8 +186,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * @return   the rotational unitary moment of inertia of this component.
         */
        public abstract double getRotationalUnitInertia();
-       
-       
+
+
        /**
         * Test whether this component allows any children components.  This method must
         * return true if and only if {@link #isCompatible(Class)} returns true for any
@@ -197,7 +196,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * @return      <code>true</code> if children can be attached to this component, <code>false</code> otherwise.
         */
        public abstract boolean allowsChildren();
-       
+
        /**
         * Test whether the given component type can be added to this component.  This type safety
         * is enforced by the <code>addChild()</code> methods.  The return value of this method
@@ -208,8 +207,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * @return      Whether such a component can be added.
         */
        public abstract boolean isCompatible(Class<? extends RocketComponent> type);
-       
-       
+
+
        /* Non-abstract helper method */
        /**
         * Test whether the given component can be added to this component.  This is equivalent
@@ -223,9 +222,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return isCompatible(c.getClass());
        }
-       
-       
-       
+
+
+
        /**
         * Return a collection of bounding coordinates.  The coordinates must be such that
         * the component is fully enclosed in their convex hull.
@@ -233,24 +232,24 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * @return      a collection of coordinates that bound the component.
         */
        public abstract Collection<Coordinate> getComponentBounds();
-       
+
        /**
         * Return true if the component may have an aerodynamic effect on the rocket.
         */
        public abstract boolean isAerodynamic();
-       
+
        /**
         * Return true if the component may have an effect on the rocket's mass.
         */
        public abstract boolean isMassive();
-       
-       
-       
-       
-       
+
+
+
+
+
        ////////////  Methods that may be overridden  ////////////
-       
-       
+
+
        /**
         * Shift the coordinates in the array corresponding to radial movement.  A component
         * that has a radial position must shift the coordinates in this array suitably.
@@ -267,8 +266,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                return c;
        }
-       
-       
+
+
        /**
         * Called when any component in the tree fires a ComponentChangeEvent.  This is by
         * default a no-op, but subclasses may override this method to e.g. invalidate
@@ -281,10 +280,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                // No-op
                checkState();
        }
-       
-       
-       
-       
+
+
+
+
        /**
         * Return the user-provided name of the component, or the component base
         * name if the user-provided name is empty.  This can be used in the UI.
@@ -299,8 +298,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                else
                        return name;
        }
-       
-       
+
+
        /**
         * Create a string describing the basic component structure from this component downwards.
         * @return      a string containing the rocket structure
@@ -315,7 +314,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        mutex.unlock("toDebugString");
                }
        }
-       
+
        private void toDebugString(StringBuilder sb) {
                sb.append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this));
                sb.append("[\"").append(this.getName()).append('"');
@@ -325,8 +324,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                }
                sb.append(']');
        }
-       
-       
+
+
        /**
         * Make a deep copy of the rocket component tree structure from this component
         * downwards for copying purposes.  Each component in the copy will be assigned
@@ -336,16 +335,16 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         */
        public final RocketComponent copy() {
                RocketComponent clone = copyWithOriginalID();
-               
+
                Iterator<RocketComponent> iterator = clone.iterator(true);
                while (iterator.hasNext()) {
                        iterator.next().newID();
                }
                return clone;
        }
-       
-       
-       
+
+
+
        /**
         * Make a deep copy of the rocket component tree structure from this component
         * downwards while maintaining the component ID's.  The purpose of this method is
@@ -372,14 +371,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        } catch (CloneNotSupportedException e) {
                                throw new BugException("CloneNotSupportedException encountered, report a bug!", e);
                        }
-                       
+
                        // Reset the mutex
                        clone.mutex = SafetyMutex.newInstance();
-                       
+
                        // Reset all parent/child information
                        clone.parent = null;
                        clone.children = new ArrayList<RocketComponent>();
-                       
+
                        // Add copied children to the structure without firing events.
                        for (RocketComponent child : this.children) {
                                RocketComponent childCopy = child.copyWithOriginalID();
@@ -387,23 +386,23 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                                clone.children.add(childCopy);
                                childCopy.parent = clone;
                        }
-                       
+
                        this.checkComponentStructure();
                        clone.checkComponentStructure();
-                       
+
                        return clone;
                } finally {
                        mutex.unlock("copyWithOriginalID");
                }
        }
-       
-       
+
+
        //////////////  Methods that may not be overridden  ////////////
-       
-       
-       
+
+
+
        ////////// Common parameter setting/getting //////////
-       
+
        /**
         * Return the color of the object to use in 2D figures, or <code>null</code>
         * to use the default color.
@@ -412,7 +411,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return color;
        }
-       
+
        /**
         * Set the color of the object to use in 2D figures.
         */
@@ -420,18 +419,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                if ((color == null && c == null) ||
                                (color != null && color.equals(c)))
                        return;
-               
+
                checkState();
                this.color = c;
                fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
-       
-       
+
+
        public final LineStyle getLineStyle() {
                mutex.verify();
                return lineStyle;
        }
-       
+
        public final void setLineStyle(LineStyle style) {
                if (this.lineStyle == style)
                        return;
@@ -439,10 +438,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                this.lineStyle = style;
                fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
-       
-       
-       
-       
+
+
+
+
        /**
         * Get the current override mass.  The mass is not necessarily in use
         * at the moment.
@@ -453,7 +452,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return overrideMass;
        }
-       
+
        /**
         * Set the current override mass.  The mass is not set to use by this
         * method.
@@ -468,7 +467,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                if (massOverriden)
                        fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
+
        /**
         * Return whether mass override is active for this component.  This does NOT
         * take into account whether a parent component is overriding the mass.
@@ -479,7 +478,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return massOverriden;
        }
-       
+
        /**
         * Set whether the mass is currently overridden.
         *
@@ -493,11 +492,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                massOverriden = o;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
-       
-       
-       
+
+
+
+
+
        /**
         * Return the current override CG.  The CG is not necessarily overridden.
         *
@@ -507,7 +506,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return getComponentCG().setX(overrideCGX);
        }
-       
+
        /**
         * Return the x-coordinate of the current override CG.
         *
@@ -517,7 +516,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return overrideCGX;
        }
-       
+
        /**
         * Set the current override CG to (x,0,0).
         *
@@ -533,7 +532,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                else
                        fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
-       
+
        /**
         * Return whether the CG is currently overridden.
         *
@@ -543,7 +542,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return cgOverriden;
        }
-       
+
        /**
         * Set whether the CG is currently overridden.
         *
@@ -557,9 +556,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                cgOverriden = o;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
-       
+
+
+
        /**
         * Return whether the mass and/or CG override overrides all subcomponent values
         * as well.  The default implementation is a normal getter/setter implementation,
@@ -574,8 +573,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return overrideSubcomponents;
        }
-       
-       
+
+
        /**
         * Set whether the mass and/or CG override overrides all subcomponent values
         * as well.  See {@link #getOverrideSubcomponents()} for details.
@@ -590,7 +589,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                overrideSubcomponents = override;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
+
        /**
         * Return whether the option to override all subcomponents is enabled or not.
         * The default implementation returns <code>false</code> if neither mass nor
@@ -605,10 +604,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return isCGOverridden() || isMassOverridden();
        }
-       
-       
-       
-       
+
+
+
+
        /**
         * Get the user-defined name of the component.
         */
@@ -616,7 +615,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return name;
        }
-       
+
        /**
         * Set the user-defined name of the component.  If name==null, sets the name to
         * the default name, currently the component name.
@@ -632,8 +631,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        this.name = name;
                fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
-       
-       
+
+
        /**
         * Return the comment of the component.  The component may contain multiple lines
         * using \n as a newline separator.
@@ -644,7 +643,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return comment;
        }
-       
+
        /**
         * Set the comment of the component.
         *
@@ -660,9 +659,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        this.comment = comment;
                fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
-       
-       
-       
+
+
+
        /**
         * Return the preset component that this component is based upon.
         * 
@@ -671,7 +670,17 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        public final ComponentPreset getPresetComponent() {
                return presetComponent;
        }
-       
+
+       /**
+        * Return the most compatible preset type for this component.
+        * This method should be overridden by components which have presets
+        * 
+        * @return the most compatible ComponentPreset.Type or <code>null</code> if no presets are compatible.
+        */
+       public ComponentPreset.Type getPresetType() {
+               return null;
+       }
+
        /**
         * Set the preset component this component is based upon and load all of the 
         * preset values.
@@ -682,17 +691,20 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                if (presetComponent == preset) {
                        return;
                }
-               
+
                if (preset == null) {
                        clearPreset();
                        return;
                }
-               
+
+               // TODO - do we need to this compatibility check?
+               /*
                if (preset.getComponentClass() != this.getClass()) {
                        throw new IllegalArgumentException("Attempting to load preset of type " + preset.getComponentClass()
                                        + " into component of type " + this.getClass());
                }
-               
+                */
+
                RocketComponent root = getRoot();
                final Rocket rocket;
                if (root instanceof Rocket) {
@@ -700,25 +712,25 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                } else {
                        rocket = null;
                }
-               
+
                try {
                        if (rocket != null) {
                                rocket.freeze();
                        }
-                       
-                       loadFromPreset(preset.getPrototype());
-                       
+
+                       loadFromPreset(preset);
+
                        this.presetComponent = preset;
                        fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
-                       
+
                } finally {
                        if (rocket != null) {
                                rocket.thaw();
                        }
                }
        }
-       
-       
+
+
        /**
         * Load component properties from the specified preset.  The preset is guaranteed
         * to be of the correct type.
@@ -731,11 +743,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * 
         * @param preset        the preset to load from
         */
-       protected void loadFromPreset(RocketComponent preset) {
-               // No-op
+       protected void loadFromPreset(ComponentPreset preset) {
+               if ( preset.has(ComponentPreset.LENGTH) ) {
+                       this.length = preset.get(ComponentPreset.LENGTH);
+               }
        }
-       
-       
+
+
        /**
         * Clear the current component preset.  This does not affect the component properties
         * otherwise.
@@ -746,9 +760,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                presetComponent = null;
                fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
-       
-       
-       
+
+
+
        /**
         * Returns the unique ID of the component.
         *
@@ -757,7 +771,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        public final String getID() {
                return id;
        }
-       
+
        /**
         * Generate a new ID for this component.
         */
@@ -765,10 +779,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                this.id = UniqueID.uuid();
        }
-       
-       
-       
-       
+
+
+
+
        /**
         * Get the characteristic length of the component, for example the length of a body tube
         * of the length of the root chord of a fin.  This is used in positioning the component
@@ -781,7 +795,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return length;
        }
-       
+
        /**
         * Get the positioning of the component relative to its parent component.
         * This is one of the enums of {@link Position}.  A setter method is not provided,
@@ -791,8 +805,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return relativePosition;
        }
-       
-       
+
+
        /**
         * Set the positioning of the component relative to its parent component.
         * The actual position of the component is maintained to the best ability.
@@ -808,38 +822,38 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                if (this.relativePosition == position)
                        return;
                checkState();
-               
+
                // Update position so as not to move the component
                if (this.parent != null) {
                        double thisPos = this.toRelative(Coordinate.NUL, this.parent)[0].x;
-                       
+
                        switch (position) {
                        case ABSOLUTE:
                                this.position = this.toAbsolute(Coordinate.NUL)[0].x;
                                break;
-                       
+
                        case TOP:
                                this.position = thisPos;
                                break;
-                       
+
                        case MIDDLE:
                                this.position = thisPos - (this.parent.length - this.length) / 2;
                                break;
-                       
+
                        case BOTTOM:
                                this.position = thisPos - (this.parent.length - this.length);
                                break;
-                       
+
                        default:
                                throw new BugException("Unknown position type: " + position);
                        }
                }
-               
+
                this.relativePosition = position;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
-       
+
+
        /**
         * Determine position relative to given position argument.  Note: This is a side-effect free method.  No state
         * is modified.
@@ -853,7 +867,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                double result = this.position;
                if (relativeTo != null) {
                        double thisPos = this.toRelative(Coordinate.NUL, relativeTo)[0].x;
-                       
+
                        switch (thePosition) {
                        case ABSOLUTE:
                                result = this.toAbsolute(Coordinate.NUL)[0].x;
@@ -873,7 +887,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                }
                return result;
        }
-       
+
        /**
         * Get the position value of the component.  The exact meaning of the value is
         * dependent on the current relative positioning.
@@ -884,8 +898,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                mutex.verify();
                return position;
        }
-       
-       
+
+
        /**
         * Set the position value of the component.  The exact meaning of the value
         * depends on the current relative positioning.
@@ -903,11 +917,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                this.position = value;
        }
-       
-       
-       
+
+
+
        ///////////  Coordinate changes  ///////////
-       
+
        /**
         * Returns coordinate c in absolute coordinates.  Equivalent to toComponent(c,null).
         */
@@ -915,8 +929,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                return toRelative(c, null);
        }
-       
-       
+
+
        /**
         * Return coordinate <code>c</code> described in the coordinate system of
         * <code>dest</code>.  If <code>dest</code> is <code>null</code> returns
@@ -941,33 +955,33 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        RocketComponent search = dest;
                        Coordinate[] array = new Coordinate[1];
                        array[0] = c;
-                       
+
                        RocketComponent component = this;
                        while ((component != search) && (component.parent != null)) {
-                               
+
                                array = component.shiftCoordinates(array);
-                               
+
                                switch (component.relativePosition) {
                                case TOP:
                                        for (int i = 0; i < array.length; i++) {
                                                array[i] = array[i].add(component.position, 0, 0);
                                        }
                                        break;
-                               
+
                                case MIDDLE:
                                        for (int i = 0; i < array.length; i++) {
                                                array[i] = array[i].add(component.position +
                                                                (component.parent.length - component.length) / 2, 0, 0);
                                        }
                                        break;
-                               
+
                                case BOTTOM:
                                        for (int i = 0; i < array.length; i++) {
                                                array[i] = array[i].add(component.position +
                                                                (component.parent.length - component.length), 0, 0);
                                        }
                                        break;
-                               
+
                                case AFTER:
                                        // Add length of all previous brother-components with POSITION_RELATIVE_AFTER
                                        int index = component.parent.children.indexOf(component);
@@ -983,28 +997,28 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                                                array[i] = array[i].add(component.position + component.parent.length, 0, 0);
                                        }
                                        break;
-                               
+
                                case ABSOLUTE:
                                        search = null; // Requires back-search if dest!=null
                                        if (Double.isNaN(absoluteX)) {
                                                absoluteX = component.position;
                                        }
                                        break;
-                               
+
                                default:
                                        throw new BugException("Unknown relative positioning type of component" +
                                                        component + ": " + component.relativePosition);
                                }
-                               
+
                                component = component.parent; // parent != null
                        }
-                       
+
                        if (!Double.isNaN(absoluteX)) {
                                for (int i = 0; i < array.length; i++) {
                                        array[i] = array[i].setX(absoluteX + c.x);
                                }
                        }
-                       
+
                        // Check whether destination has been found or whether to backtrack
                        // TODO: LOW: Backtracking into clustered components uses only one component
                        if ((dest != null) && (component != dest)) {
@@ -1013,14 +1027,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                                        array[i] = array[i].sub(origin[0]);
                                }
                        }
-                       
+
                        return array;
                } finally {
                        mutex.unlock("toRelative");
                }
        }
-       
-       
+
+
        /**
         * Recursively sum the lengths of all subcomponents that have position
         * Position.AFTER.
@@ -1042,11 +1056,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        mutex.unlock("getTotalLength");
                }
        }
-       
-       
-       
+
+
+
        /////////// Total mass and CG calculation ////////////
-       
+
        /**
         * Return the (possibly overridden) mass of component.
         *
@@ -1058,7 +1072,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        return overrideMass;
                return getComponentMass();
        }
-       
+
        /**
         * Return the (possibly overridden) center of gravity and mass.
         *
@@ -1071,14 +1085,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                if (cgOverriden)
                        return getOverrideCG().setWeight(getMass());
-               
+
                if (massOverriden)
                        return getComponentCG().setWeight(getMass());
-               
+
                return getComponentCG();
        }
-       
-       
+
+
        /**
         * Return the longitudinal (around the y- or z-axis) moment of inertia of this component.
         * The moment of inertia is scaled in reference to the (possibly overridden) mass
@@ -1090,7 +1104,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                return getLongitudinalUnitInertia() * getMass();
        }
-       
+
        /**
         * Return the rotational (around the y- or z-axis) moment of inertia of this component.
         * The moment of inertia is scaled in reference to the (possibly overridden) mass
@@ -1102,12 +1116,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                return getRotationalUnitInertia() * getMass();
        }
-       
-       
-       
+
+
+
        ///////////  Children handling  ///////////
-       
-       
+
+
        /**
         * Adds a child to the rocket component tree.  The component is added to the end
         * of the component's child list.  This is a helper method that calls
@@ -1122,8 +1136,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                addChild(component, children.size());
        }
-       
-       
+
+
        /**
         * Adds a child to the rocket component tree.  The component is added to
         * the given position of the component's child list.
@@ -1138,32 +1152,32 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         */
        public void addChild(RocketComponent component, int index) {
                checkState();
-               
+
                if (component.parent != null) {
                        throw new IllegalArgumentException("component " + component.getComponentName() +
                                        " is already in a tree");
                }
-               
+
                // Ensure that the no loops are created in component tree [A -> X -> Y -> B, B.addChild(A)]
                if (this.getRoot().equals(component)) {
                        throw new IllegalStateException("Component " + component.getComponentName() +
                                        " is a parent of " + this.getComponentName() + ", attempting to create cycle in tree.");
                }
-               
+
                if (!isCompatible(component)) {
                        throw new IllegalStateException("Component " + component.getComponentName() +
                                        " not currently compatible with component " + getComponentName());
                }
-               
+
                children.add(index, component);
                component.parent = this;
-               
+
                this.checkComponentStructure();
                component.checkComponentStructure();
-               
+
                fireAddRemoveEvent(component);
        }
-       
+
        /**
         * Removes a child from the rocket component tree.
         *
@@ -1174,13 +1188,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                RocketComponent component = children.remove(n);
                component.parent = null;
-               
+
                this.checkComponentStructure();
                component.checkComponentStructure();
-               
+
                fireAddRemoveEvent(component);
        }
-       
+
        /**
         * Removes a child from the rocket component tree.  Does nothing if the component
         * is not present as a child.
@@ -1190,24 +1204,24 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         */
        public final boolean removeChild(RocketComponent component) {
                checkState();
-               
+
                component.checkComponentStructure();
-               
+
                if (children.remove(component)) {
                        component.parent = null;
-                       
+
                        this.checkComponentStructure();
                        component.checkComponentStructure();
-                       
+
                        fireAddRemoveEvent(component);
                        return true;
                }
                return false;
        }
-       
-       
-       
-       
+
+
+
+
        /**
         * Move a child to another position.
         *
@@ -1219,15 +1233,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                if (children.remove(component)) {
                        children.add(index, component);
-                       
+
                        this.checkComponentStructure();
                        component.checkComponentStructure();
-                       
+
                        fireAddRemoveEvent(component);
                }
        }
-       
-       
+
+
        /**
         * Fires an AERODYNAMIC_CHANGE, MASS_CHANGE or OTHER_CHANGE event depending on the
         * type of component removed.
@@ -1242,30 +1256,30 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        if (c.isMassive())
                                type |= ComponentChangeEvent.MASS_CHANGE;
                }
-               
+
                fireComponentChangeEvent(type);
        }
-       
-       
+
+
        public final int getChildCount() {
                checkState();
                this.checkComponentStructure();
                return children.size();
        }
-       
+
        public final RocketComponent getChild(int n) {
                checkState();
                this.checkComponentStructure();
                return children.get(n);
        }
-       
+
        public final List<RocketComponent> getChildren() {
                checkState();
                this.checkComponentStructure();
                return children.clone();
        }
-       
-       
+
+
        /**
         * Returns the position of the child in this components child list, or -1 if the
         * component is not a child of this component.
@@ -1278,7 +1292,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                this.checkComponentStructure();
                return children.indexOf(child);
        }
-       
+
        /**
         * Get the parent component of this component.  Returns <code>null</code> if the component
         * has no parent.
@@ -1289,7 +1303,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                return parent;
        }
-       
+
        /**
         * Get the root component of the component tree.
         *
@@ -1302,7 +1316,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        gp = gp.parent;
                return gp;
        }
-       
+
        /**
         * Returns the root Rocket component of this component tree.  Throws an
         * IllegalStateException if the root component is not a Rocket.
@@ -1318,8 +1332,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                throw new IllegalStateException("getRocket() called with root component "
                                + r.getComponentName());
        }
-       
-       
+
+
        /**
         * Return the Stage component that this component belongs to.  Throws an
         * IllegalStateException if a Stage is not in the parentage of this component.
@@ -1337,7 +1351,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                }
                throw new IllegalStateException("getStage() called without Stage as a parent.");
        }
-       
+
        /**
         * Return the stage number of the stage this component belongs to.  The stages
         * are numbered from zero upwards.
@@ -1349,7 +1363,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                if (parent == null) {
                        throw new IllegalArgumentException("getStageNumber() called for root component");
                }
-               
+
                RocketComponent stage = this;
                while (!(stage instanceof Stage)) {
                        stage = stage.parent;
@@ -1360,8 +1374,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                }
                return stage.parent.getChildPosition(stage);
        }
-       
-       
+
+
        /**
         * Find a component with the given ID.  The component tree is searched from this component
         * down (including this component) for the ID and the corresponding component is returned,
@@ -1380,8 +1394,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                }
                return null;
        }
-       
-       
+
+
        // TODO: Move these methods elsewhere (used only in SymmetricComponent)
        public final RocketComponent getPreviousComponent() {
                checkState();
@@ -1393,7 +1407,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        StringBuffer sb = new StringBuffer();
                        sb.append("Inconsistent internal state: ");
                        sb.append("this=").append(this).append('[')
-                                       .append(System.identityHashCode(this)).append(']');
+                       .append(System.identityHashCode(this)).append(']');
                        sb.append(" parent.children=[");
                        for (int i = 0; i < parent.children.size(); i++) {
                                RocketComponent c = parent.children.get(i);
@@ -1412,34 +1426,34 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        c = c.getChild(c.getChildCount() - 1);
                return c;
        }
-       
+
        // TODO: Move these methods elsewhere (used only in SymmetricComponent)
        public final RocketComponent getNextComponent() {
                checkState();
                if (getChildCount() > 0)
                        return getChild(0);
-               
+
                RocketComponent current = this;
                RocketComponent nextParent = this.parent;
-               
+
                while (nextParent != null) {
                        int pos = nextParent.getChildPosition(current);
                        if (pos < nextParent.getChildCount() - 1)
                                return nextParent.getChild(pos + 1);
-                       
+
                        current = nextParent;
                        nextParent = current.parent;
                }
                return null;
        }
-       
-       
+
+
        ///////////  Event handling  //////////
        //
        // Listener lists are provided by the root Rocket component,
        // a single listener list for the whole rocket.
        //
-       
+
        /**
         * Adds a ComponentChangeListener to the rocket tree.  The listener is added to the root
         * component, which must be of type Rocket (which overrides this method).  Events of all
@@ -1451,7 +1465,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                getRocket().addComponentChangeListener(l);
        }
-       
+
        /**
         * Removes a ComponentChangeListener from the rocket tree.  The listener is removed from
         * the root component, which must be of type Rocket (which overrides this method).
@@ -1465,8 +1479,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        getRoot().removeComponentChangeListener(l);
                }
        }
-       
-       
+
+
        /**
         * Adds a <code>ChangeListener</code> to the rocket tree.  This is identical to
         * <code>addComponentChangeListener()</code> except that it uses a
@@ -1481,7 +1495,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                getRocket().addChangeListener(l);
        }
-       
+
        /**
         * Removes a ChangeListener from the rocket tree.  This is identical to
         * removeComponentChangeListener() except it uses a ChangeListener.
@@ -1496,8 +1510,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        getRoot().removeChangeListener(l);
                }
        }
-       
-       
+
+
        /**
         * Fires a ComponentChangeEvent on the rocket structure.  The call is passed to the
         * root component, which must be of type Rocket (which overrides this method).
@@ -1518,8 +1532,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                }
                getRoot().fireComponentChangeEvent(e);
        }
-       
-       
+
+
        /**
         * Fires a ComponentChangeEvent of the given type.  The source of the event is set to
         * this component.
@@ -1530,8 +1544,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        protected void fireComponentChangeEvent(int type) {
                fireComponentChangeEvent(new ComponentChangeEvent(this, type));
        }
-       
-       
+
+
        /**
         * Checks whether this component has been invalidated and should no longer be used.
         * This is a safety check that in-place replaced components are no longer used.
@@ -1544,8 +1558,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                invalidator.check(true);
                mutex.verify();
        }
-       
-       
+
+
        /**
         * Check that the local component structure is correct.  This can be called after changing
         * the component structure in order to verify the integrity.
@@ -1568,7 +1582,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        }
                }
        }
-       
+
        // Check whether the list contains exactly the searched-for component (with == operator)
        private boolean containsExact(List<RocketComponent> haystack, RocketComponent needle) {
                for (RocketComponent c : haystack) {
@@ -1578,10 +1592,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                }
                return false;
        }
-       
-       
+
+
        ///////////  Iterators  //////////
-       
+
        /**
         * Returns an iterator that iterates over all children and sub-children.
         * <p>
@@ -1603,8 +1617,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        public final Iterator<RocketComponent> deepIterator(boolean returnSelf) {
                return iterator(returnSelf);
        }
-       
-       
+
+
        /**
         * Returns an iterator that iterates over all children and sub-children, including itself.
         * <p>
@@ -1619,9 +1633,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        public final Iterator<RocketComponent> deepIterator() {
                return iterator();
        }
-       
-       
-       
+
+
+
        /**
         * Returns an iterator that iterates over all children and sub-children.
         * <p>
@@ -1640,8 +1654,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                checkState();
                return new RocketComponentIterator(this, returnSelf);
        }
-       
-       
+
+
        /**
         * Returns an iterator that iterates over this component, its children and sub-children.
         * <p>
@@ -1653,11 +1667,30 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        public final Iterator<RocketComponent> iterator() {
                return iterator(true);
        }
-       
-       
-       
-       
-       
+
+       /**
+        * Retrieve the List of MotorMounts in the Rocket.
+        * 
+        * Each element returned will a RocketComponent which implements MotorMount.  Further isMotorMount()
+        * returns true.
+        * 
+        * @return List<MotorMount>
+        */
+       public final List<MotorMount> getMotorMounts() {
+               Iterator<RocketComponent> it = iterator();
+               List<MotorMount> mmts = new ArrayList<MotorMount>();
+
+               while (it.hasNext()) {
+                       RocketComponent c = it.next();
+                       if (c instanceof MotorMount) {
+                               if ( ((MotorMount)c).isMotorMount() ) {
+                                       mmts.add((MotorMount) c);
+                               }
+                       }
+               }
+               return mmts;
+       }
+
        /**
         * Compare component equality based on the ID of this component.  Only the
         * ID and class type is used for a basis of comparison.
@@ -1673,21 +1706,21 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                RocketComponent other = (RocketComponent) obj;
                return this.id.equals(other.id);
        }
-       
-       
-       
+
+
+
        @Override
        public int hashCode() {
                return id.hashCode();
        }
-       
-       
-       
+
+
+
        ////////////  Helper methods for subclasses
-       
-       
-       
-       
+
+
+
+
        /**
         * Helper method to add rotationally symmetric bounds at the specified coordinates.
         * The X-axis value is <code>x</code> and the radius at the specified position is
@@ -1699,37 +1732,41 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                bounds.add(new Coordinate(x, r, r));
                bounds.add(new Coordinate(x, -r, r));
        }
-       
-       
+
+
        protected static final Coordinate ringCG(double outerRadius, double innerRadius,
                        double x1, double x2, double density) {
                return new Coordinate((x1 + x2) / 2, 0, 0,
                                ringMass(outerRadius, innerRadius, x2 - x1, density));
        }
-       
+
+       protected static final double ringVolume( double outerRadius, double innerRadius, double length ) {
+               return ringMass( outerRadius, innerRadius, length, 1.0 );
+       }
+
        protected static final double ringMass(double outerRadius, double innerRadius,
                        double length, double density) {
                return Math.PI * (MathUtil.pow2(outerRadius) - MathUtil.pow2(innerRadius)) *
                                length * density;
        }
-       
+
        protected static final double ringLongitudinalUnitInertia(double outerRadius,
                        double innerRadius, double length) {
                // 1/12 * (3 * (r1^2 + r2^2) + h^2)
                return (3 * (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) + MathUtil.pow2(length)) / 12;
        }
-       
+
        protected static final double ringRotationalUnitInertia(double outerRadius,
                        double innerRadius) {
                // 1/2 * (r1^2 + r2^2)
                return (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) / 2;
        }
-       
-       
-       
+
+
+
        ////////////  OTHER
-       
-       
+
+
        /**
         * Loads the RocketComponent fields from the given component.  This method is meant
         * for in-place replacement of a component.  It is used with the undo/redo
@@ -1750,34 +1787,34 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        protected List<RocketComponent> copyFrom(RocketComponent src) {
                checkState();
                List<RocketComponent> toInvalidate = new ArrayList<RocketComponent>();
-               
+
                if (this.parent != null) {
                        throw new UnsupportedOperationException("copyFrom called for non-root component, parent=" +
                                        this.parent.toDebugString() + ", this=" + this.toDebugString());
                }
-               
+
                // Add current structure to be invalidated
                Iterator<RocketComponent> iterator = this.iterator(false);
                while (iterator.hasNext()) {
                        toInvalidate.add(iterator.next());
                }
-               
+
                // Remove previous components
                for (RocketComponent child : this.children) {
                        child.parent = null;
                }
                this.children.clear();
-               
+
                // Copy new children to this component
                for (RocketComponent c : src.children) {
                        RocketComponent copy = c.copyWithOriginalID();
                        this.children.add(copy);
                        copy.parent = this;
                }
-               
+
                this.checkComponentStructure();
                src.checkComponentStructure();
-               
+
                // Set all parameters
                this.length = src.length;
                this.relativePosition = src.relativePosition;
@@ -1792,22 +1829,22 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                this.name = src.name;
                this.comment = src.comment;
                this.id = src.id;
-               
+
                // Add source components to invalidation tree
                for (RocketComponent c : src) {
                        toInvalidate.add(c);
                }
-               
+
                return toInvalidate;
        }
-       
+
        protected void invalidate() {
                invalidator.invalidate();
        }
-       
-       
+
+
        //////////  Iterator implementation  ///////////
-       
+
        /**
         * Private inner class to implement the Iterator.
         *
@@ -1815,17 +1852,17 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         */
        private static class RocketComponentIterator implements Iterator<RocketComponent> {
                // Stack holds iterators which still have some components left.
-               private final Deque<Iterator<RocketComponent>> iteratorStack = new ArrayDeque<Iterator<RocketComponent>>();
-               
+               private final SimpleStack<Iterator<RocketComponent>> iteratorStack = new SimpleStack<Iterator<RocketComponent>>();
+
                private final Rocket root;
                private final int treeModID;
-               
+
                private final RocketComponent original;
                private boolean returnSelf = false;
-               
+
                // Construct iterator with component's child's iterator, if it has elements
                public RocketComponentIterator(RocketComponent c, boolean returnSelf) {
-                       
+
                        RocketComponent gp = c.getRoot();
                        if (gp instanceof Rocket) {
                                root = (Rocket) gp;
@@ -1834,15 +1871,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                                root = null;
                                treeModID = -1;
                        }
-                       
+
                        Iterator<RocketComponent> i = c.children.iterator();
                        if (i.hasNext())
                                iteratorStack.push(i);
-                       
+
                        this.original = c;
                        this.returnSelf = returnSelf;
                }
-               
+
                @Override
                public boolean hasNext() {
                        checkID();
@@ -1850,38 +1887,38 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                                return true;
                        return !iteratorStack.isEmpty(); // Elements remain if stack is not empty
                }
-               
+
                @Override
                public RocketComponent next() {
                        Iterator<RocketComponent> i;
-                       
+
                        checkID();
-                       
+
                        // Return original component first
                        if (returnSelf) {
                                returnSelf = false;
                                return original;
                        }
-                       
+
                        // Peek first iterator from stack, throw exception if empty
                        i = iteratorStack.peek();
                        if (i == null) {
                                throw new NoSuchElementException("No further elements in RocketComponent iterator");
                        }
-                       
+
                        // Retrieve next component of the iterator, remove iterator from stack if empty
                        RocketComponent c = i.next();
                        if (!i.hasNext())
                                iteratorStack.pop();
-                       
+
                        // Add iterator of component children to stack if it has children
                        i = c.children.iterator();
                        if (i.hasNext())
                                iteratorStack.push(i);
-                       
+
                        return c;
                }
-               
+
                private void checkID() {
                        if (root != null) {
                                if (root.getTreeModID() != treeModID) {
@@ -1889,12 +1926,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                                }
                        }
                }
-               
+
                @Override
                public void remove() {
                        throw new UnsupportedOperationException("remove() not supported by " +
                                        "RocketComponent iterator");
                }
        }
-       
+
 }