Major refactoring for 1.1.1
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / RocketComponent.java
index 335a22311252e533098d12ad07cacb679a1d9f49..a945d7b2dd9ecf5a38d063c9a971a93f84216f79 100644 (file)
@@ -13,6 +13,7 @@ import java.util.UUID;
 
 import javax.swing.event.ChangeListener;
 
+import net.sf.openrocket.util.BugException;
 import net.sf.openrocket.util.ChangeSource;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.LineStyle;
@@ -307,7 +308,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
                try {
                        clone = (RocketComponent)this.clone();
                } catch (CloneNotSupportedException e) {
-                       throw new RuntimeException("CloneNotSupportedException encountered, " +
+                       throw new BugException("CloneNotSupportedException encountered, " +
                                        "report a bug!",e);
                }
 
@@ -316,10 +317,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
                clone.children = new ArrayList<RocketComponent>();
 
                // Add copied children to the structure without firing events.
-               for (RocketComponent c: this.children) {
-                       RocketComponent copy = c.copy();
-                       clone.children.add(copy);
-                       copy.parent = clone;
+               for (RocketComponent child: this.children) {
+                       RocketComponent childCopy = child.copy();
+                       // Don't use add method since it fires events
+                       clone.children.add(childCopy);
+                       childCopy.parent = clone;
                }
 
                return clone;
@@ -777,7 +779,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
                                break;
                                
                        default:
-                               throw new RuntimeException("Unknown relative positioning type of component"+
+                               throw new BugException("Unknown relative positioning type of component"+
                                                component+": "+component.relativePosition);
                        }
 
@@ -1079,6 +1081,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
                RocketComponent stage = this;
                while (!(stage instanceof Stage)) {
                        stage = stage.parent;
+                       if (stage == null || stage.parent == null) {
+                               throw new IllegalStateException("getStageNumber() could not find parent " +
+                                               "stage.");
+                       }
                }
                return stage.parent.getChildPosition(stage);
        }
@@ -1107,6 +1113,21 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
                if (parent == null)
                        return null;
                int pos = parent.getChildPosition(this);
+               if (pos < 0) {
+                       StringBuffer sb = new StringBuffer();
+                       sb.append("Inconsistent internal state: ");
+                       sb.append("this=").append(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);
+                               sb.append(c).append('[').append(System.identityHashCode(c)).append(']');
+                               if (i < parent.children.size()-1)
+                                       sb.append(", ");
+                       }
+                       sb.append(']');
+                       throw new IllegalStateException(sb.toString());
+               }
                assert(pos >= 0);
                if (pos == 0)
                        return parent;
@@ -1155,12 +1176,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
        /**
         * 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).
+        * Does nothing if the root component is not a Rocket.  (The asymmetry is so
+        * that listeners can always be removed just in case.)
         * 
         * @param l  Listener to remove
-        * @throws IllegalStateException - if the root component is not a Rocket
         */
        public void removeComponentChangeListener(ComponentChangeListener l) {
-               getRocket().removeComponentChangeListener(l);
+               if (parent != null) {
+                       getRoot().removeComponentChangeListener(l);
+               }
        }
        
 
@@ -1180,12 +1204,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
        /**
         * Removes a ChangeListener from the rocket tree.  This is identical to
         * removeComponentChangeListener() except it uses a ChangeListener.
+        * Does nothing if the root component is not a Rocket.  (The asymmetry is so
+        * that listeners can always be removed just in case.)
         * 
         * @param l  Listener to remove
-        * @throws IllegalStateException - if the root component is not a Rocket
         */
        public void removeChangeListener(ChangeListener l) {
-               getRocket().removeChangeListener(l);
+               if (this.parent != null) {
+                       getRoot().removeChangeListener(l);
+               }
        }
        
        
@@ -1402,16 +1429,23 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
        
        /**
         * Loads the RocketComponent fields from the given component.  This method is meant
-        * for use with the undo/redo mechanism.
-        *
+        * for in-place replacement of a component.  It is used with the undo/redo
+        * mechanism and when converting a finset into a freeform fin set.
+        * This component must not have a parent, otherwise this method will fail.
+        * <p>
         * The fields are copied by reference, and the supplied component must not be used
         * after the call, as it is in an undefined state.
         * 
         * TODO: MEDIUM: Make general to copy all private/protected fields...
         */
        protected void copyFrom(RocketComponent src) {
+               
+               if (this.parent != null) {
+                       throw new UnsupportedOperationException("copyFrom called for non-root component " 
+                                       + this);
+               }
+               
                // Set parents and children
-               this.parent = null;
                this.children = src.children;
                src.children = new ArrayList<RocketComponent>();