bug fixes
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / RocketComponent.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import java.awt.Color;
4 import java.util.ArrayDeque;
5 import java.util.Collection;
6 import java.util.Deque;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.NoSuchElementException;
10
11 import javax.swing.event.ChangeListener;
12
13 import net.sf.openrocket.l10n.Translator;
14 import net.sf.openrocket.logging.LogHelper;
15 import net.sf.openrocket.preset.ComponentPreset;
16 import net.sf.openrocket.startup.Application;
17 import net.sf.openrocket.util.ArrayList;
18 import net.sf.openrocket.util.BugException;
19 import net.sf.openrocket.util.ChangeSource;
20 import net.sf.openrocket.util.Coordinate;
21 import net.sf.openrocket.util.Invalidator;
22 import net.sf.openrocket.util.LineStyle;
23 import net.sf.openrocket.util.MathUtil;
24 import net.sf.openrocket.util.SafetyMutex;
25 import net.sf.openrocket.util.UniqueID;
26
27
28 public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent> {
29         private static final LogHelper log = Application.getLogger();
30         private static final Translator trans = Application.getTranslator();
31         
32         /*
33          * Text is suitable to the form
34          *    Position relative to:  <title>
35          */
36         public enum Position {
37                 /** Position relative to the top of the parent component. */
38                 //// Top of the parent component
39                 TOP(trans.get("RocketComponent.Position.TOP")),
40                 /** Position relative to the middle of the parent component. */
41                 //// Middle of the parent component
42                 MIDDLE(trans.get("RocketComponent.Position.MIDDLE")),
43                 /** Position relative to the bottom of the parent component. */
44                 //// Bottom of the parent component
45                 BOTTOM(trans.get("RocketComponent.Position.BOTTOM")),
46                 /** Position after the parent component (for body components). */
47                 //// After the parent component
48                 AFTER(trans.get("RocketComponent.Position.AFTER")),
49                 /** Specify an absolute X-coordinate position. */
50                 //// Tip of the nose cone
51                 ABSOLUTE(trans.get("RocketComponent.Position.ABSOLUTE"));
52                 
53                 private String title;
54                 
55                 Position(String title) {
56                         this.title = title;
57                 }
58                 
59                 @Override
60                 public String toString() {
61                         return title;
62                 }
63         }
64         
65         /**
66          * A safety mutex that can be used to prevent concurrent access to this component.
67          */
68         protected SafetyMutex mutex = SafetyMutex.newInstance();
69         
70         ////////  Parent/child trees
71         /**
72          * Parent component of the current component, or null if none exists.
73          */
74         private RocketComponent parent = null;
75         
76         /**
77          * List of child components of this component.
78          */
79         private ArrayList<RocketComponent> children = new ArrayList<RocketComponent>();
80         
81
82         ////////  Parameters common to all components:
83         
84         /**
85          * Characteristic length of the component.  This is used in calculating the coordinate
86          * transformations and positions of other components in reference to this component.
87          * This may and should be used as the "true" length of the component, where applicable.
88          * By default it is zero, i.e. no translation.
89          */
90         protected double length = 0;
91         
92         /**
93          * Positioning of this component relative to the parent component.
94          */
95         protected Position relativePosition;
96         
97         /**
98          * Offset of the position of this component relative to the normal position given by
99          * relativePosition.  By default zero, i.e. no position change.
100          */
101         protected double position = 0;
102         
103
104         // Color of the component, null means to use the default color
105         private Color color = null;
106         private LineStyle lineStyle = null;
107         
108
109         // Override mass/CG
110         private double overrideMass = 0;
111         private boolean massOverriden = false;
112         private double overrideCGX = 0;
113         private boolean cgOverriden = false;
114         
115         private boolean overrideSubcomponents = false;
116         
117
118         // User-given name of the component
119         private String name = null;
120         
121         // User-specified comment
122         private String comment = "";
123         
124         // Unique ID of the component
125         private String id = null;
126         
127         // Preset component this component is based upon
128         private ComponentPreset presetComponent = null;
129         
130
131         /**
132          * Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}.
133          */
134         private Invalidator invalidator = new Invalidator(this);
135         
136         
137         ////  NOTE !!!  All fields must be copied in the method copyFrom()!  ////
138         
139
140
141         /**
142          * Default constructor.  Sets the name of the component to the component's static name
143          * and the relative position of the component.
144          */
145         public RocketComponent(Position relativePosition) {
146                 // These must not fire any events, due to Rocket undo system initialization
147                 this.name = getComponentName();
148                 this.relativePosition = relativePosition;
149                 newID();
150         }
151         
152         ////////////  Methods that must be implemented  ////////////
153         
154
155         /**
156          * Static component name.  The name may not vary of the parameters, it must be static.
157          */
158         public abstract String getComponentName(); // Static component type name
159         
160         /**
161          * Return the component mass (regardless of mass overriding).
162          */
163         public abstract double getComponentMass(); // Mass of non-overridden component
164         
165         /**
166          * Return the component CG and mass (regardless of CG or mass overriding).
167          */
168         public abstract Coordinate getComponentCG(); // CG of non-overridden component
169         
170
171         /**
172          * Return the longitudinal (around the y- or z-axis) unitary moment of inertia.
173          * The unitary moment of inertia is the moment of inertia with the assumption that
174          * the mass of the component is one kilogram.  The inertia is measured in
175          * respect to the non-overridden CG.
176          *
177          * @return   the longitudinal unitary moment of inertia of this component.
178          */
179         public abstract double getLongitudinalUnitInertia();
180         
181         
182         /**
183          * Return the rotational (around the x-axis) unitary moment of inertia.
184          * The unitary moment of inertia is the moment of inertia with the assumption that
185          * the mass of the component is one kilogram.  The inertia is measured in
186          * respect to the non-overridden CG.
187          *
188          * @return   the rotational unitary moment of inertia of this component.
189          */
190         public abstract double getRotationalUnitInertia();
191         
192         
193         /**
194          * Test whether this component allows any children components.  This method must
195          * return true if and only if {@link #isCompatible(Class)} returns true for any
196          * rocket component class.
197          *
198          * @return      <code>true</code> if children can be attached to this component, <code>false</code> otherwise.
199          */
200         public abstract boolean allowsChildren();
201         
202         /**
203          * Test whether the given component type can be added to this component.  This type safety
204          * is enforced by the <code>addChild()</code> methods.  The return value of this method
205          * may change to reflect the current state of this component (e.g. two components of some
206          * type cannot be placed as children).
207          *
208          * @param type  The RocketComponent class type to add.
209          * @return      Whether such a component can be added.
210          */
211         public abstract boolean isCompatible(Class<? extends RocketComponent> type);
212         
213         
214         /* Non-abstract helper method */
215         /**
216          * Test whether the given component can be added to this component.  This is equivalent
217          * to calling <code>isCompatible(c.getClass())</code>.
218          *
219          * @param c  Component to test.
220          * @return   Whether the component can be added.
221          * @see #isCompatible(Class)
222          */
223         public final boolean isCompatible(RocketComponent c) {
224                 mutex.verify();
225                 return isCompatible(c.getClass());
226         }
227         
228         
229
230         /**
231          * Return a collection of bounding coordinates.  The coordinates must be such that
232          * the component is fully enclosed in their convex hull.
233          *
234          * @return      a collection of coordinates that bound the component.
235          */
236         public abstract Collection<Coordinate> getComponentBounds();
237         
238         /**
239          * Return true if the component may have an aerodynamic effect on the rocket.
240          */
241         public abstract boolean isAerodynamic();
242         
243         /**
244          * Return true if the component may have an effect on the rocket's mass.
245          */
246         public abstract boolean isMassive();
247         
248         
249
250
251
252         ////////////  Methods that may be overridden  ////////////
253         
254
255         /**
256          * Shift the coordinates in the array corresponding to radial movement.  A component
257          * that has a radial position must shift the coordinates in this array suitably.
258          * If the component is clustered, then a new array must be returned with a
259          * coordinate for each cluster.
260          * <p>
261          * The default implementation simply returns the array, and thus produces no shift.
262          *
263          * @param c   an array of coordinates to shift.
264          * @return    an array of shifted coordinates.  The method may modify the contents
265          *                        of the passed array and return the array itself.
266          */
267         public Coordinate[] shiftCoordinates(Coordinate[] c) {
268                 checkState();
269                 return c;
270         }
271         
272         
273         /**
274          * Called when any component in the tree fires a ComponentChangeEvent.  This is by
275          * default a no-op, but subclasses may override this method to e.g. invalidate
276          * cached data.  The overriding method *must* call
277          * <code>super.componentChanged(e)</code> at some point.
278          *
279          * @param e  The event fired
280          */
281         protected void componentChanged(ComponentChangeEvent e) {
282                 // No-op
283                 checkState();
284         }
285         
286         
287
288
289         /**
290          * Return the user-provided name of the component, or the component base
291          * name if the user-provided name is empty.  This can be used in the UI.
292          *
293          * @return A string describing the component.
294          */
295         @Override
296         public final String toString() {
297                 mutex.verify();
298                 if (name.length() == 0)
299                         return getComponentName();
300                 else
301                         return name;
302         }
303         
304         
305         /**
306          * Create a string describing the basic component structure from this component downwards.
307          * @return      a string containing the rocket structure
308          */
309         public final String toDebugString() {
310                 mutex.lock("toDebugString");
311                 try {
312                         StringBuilder sb = new StringBuilder();
313                         toDebugString(sb);
314                         return sb.toString();
315                 } finally {
316                         mutex.unlock("toDebugString");
317                 }
318         }
319         
320         private void toDebugString(StringBuilder sb) {
321                 sb.append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this));
322                 sb.append("[\"").append(this.getName()).append('"');
323                 for (RocketComponent c : this.children) {
324                         sb.append("; ");
325                         c.toDebugString(sb);
326                 }
327                 sb.append(']');
328         }
329         
330         
331         /**
332          * Make a deep copy of the rocket component tree structure from this component
333          * downwards for copying purposes.  Each component in the copy will be assigned
334          * a new component ID, making it a safe copy.  This method does not fire any events.
335          *
336          * @return A deep copy of the structure.
337          */
338         public final RocketComponent copy() {
339                 RocketComponent clone = copyWithOriginalID();
340                 
341                 Iterator<RocketComponent> iterator = clone.iterator(true);
342                 while (iterator.hasNext()) {
343                         iterator.next().newID();
344                 }
345                 return clone;
346         }
347         
348         
349
350         /**
351          * Make a deep copy of the rocket component tree structure from this component
352          * downwards while maintaining the component ID's.  The purpose of this method is
353          * to allow copies to be created with the original ID's for the purpose of the
354          * undo/redo mechanism.  This method should not be used for other purposes,
355          * such as copy/paste.  This method does not fire any events.
356          * <p>
357          * This method must be overridden by any component that refers to mutable objects,
358          * or if some fields should not be copied.  This should be performed by
359          * <code>RocketComponent c = super.copyWithOriginalID();</code> and then cloning/modifying
360          * the appropriate fields.
361          * <p>
362          * This is not performed as serializing/deserializing for performance reasons.
363          *
364          * @return A deep copy of the structure.
365          */
366         protected RocketComponent copyWithOriginalID() {
367                 mutex.lock("copyWithOriginalID");
368                 try {
369                         checkState();
370                         RocketComponent clone;
371                         try {
372                                 clone = (RocketComponent) this.clone();
373                         } catch (CloneNotSupportedException e) {
374                                 throw new BugException("CloneNotSupportedException encountered, report a bug!", e);
375                         }
376                         
377                         // Reset the mutex
378                         clone.mutex = SafetyMutex.newInstance();
379                         
380                         // Reset all parent/child information
381                         clone.parent = null;
382                         clone.children = new ArrayList<RocketComponent>();
383                         
384                         // Add copied children to the structure without firing events.
385                         for (RocketComponent child : this.children) {
386                                 RocketComponent childCopy = child.copyWithOriginalID();
387                                 // Don't use add method since it fires events
388                                 clone.children.add(childCopy);
389                                 childCopy.parent = clone;
390                         }
391                         
392                         this.checkComponentStructure();
393                         clone.checkComponentStructure();
394                         
395                         return clone;
396                 } finally {
397                         mutex.unlock("copyWithOriginalID");
398                 }
399         }
400         
401         
402         //////////////  Methods that may not be overridden  ////////////
403         
404
405
406         ////////// Common parameter setting/getting //////////
407         
408         /**
409          * Return the color of the object to use in 2D figures, or <code>null</code>
410          * to use the default color.
411          */
412         public final Color getColor() {
413                 mutex.verify();
414                 return color;
415         }
416         
417         /**
418          * Set the color of the object to use in 2D figures.
419          */
420         public final void setColor(Color c) {
421                 if ((color == null && c == null) ||
422                                 (color != null && color.equals(c)))
423                         return;
424                 
425                 checkState();
426                 this.color = c;
427                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
428         }
429         
430         
431         public final LineStyle getLineStyle() {
432                 mutex.verify();
433                 return lineStyle;
434         }
435         
436         public final void setLineStyle(LineStyle style) {
437                 if (this.lineStyle == style)
438                         return;
439                 checkState();
440                 this.lineStyle = style;
441                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
442         }
443         
444         
445
446
447         /**
448          * Get the current override mass.  The mass is not necessarily in use
449          * at the moment.
450          *
451          * @return  the override mass
452          */
453         public final double getOverrideMass() {
454                 mutex.verify();
455                 return overrideMass;
456         }
457         
458         /**
459          * Set the current override mass.  The mass is not set to use by this
460          * method.
461          *
462          * @param m  the override mass
463          */
464         public final void setOverrideMass(double m) {
465                 if (MathUtil.equals(m, overrideMass))
466                         return;
467                 checkState();
468                 overrideMass = Math.max(m, 0);
469                 if (massOverriden)
470                         fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
471         }
472         
473         /**
474          * Return whether mass override is active for this component.  This does NOT
475          * take into account whether a parent component is overriding the mass.
476          *
477          * @return  whether the mass is overridden
478          */
479         public final boolean isMassOverridden() {
480                 mutex.verify();
481                 return massOverriden;
482         }
483         
484         /**
485          * Set whether the mass is currently overridden.
486          *
487          * @param o  whether the mass is overridden
488          */
489         public final void setMassOverridden(boolean o) {
490                 if (massOverriden == o) {
491                         return;
492                 }
493                 checkState();
494                 massOverriden = o;
495                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
496         }
497         
498         
499
500
501
502         /**
503          * Return the current override CG.  The CG is not necessarily overridden.
504          *
505          * @return  the override CG
506          */
507         public final Coordinate getOverrideCG() {
508                 mutex.verify();
509                 return getComponentCG().setX(overrideCGX);
510         }
511         
512         /**
513          * Return the x-coordinate of the current override CG.
514          *
515          * @return      the x-coordinate of the override CG.
516          */
517         public final double getOverrideCGX() {
518                 mutex.verify();
519                 return overrideCGX;
520         }
521         
522         /**
523          * Set the current override CG to (x,0,0).
524          *
525          * @param x  the x-coordinate of the override CG to set.
526          */
527         public final void setOverrideCGX(double x) {
528                 if (MathUtil.equals(overrideCGX, x))
529                         return;
530                 checkState();
531                 this.overrideCGX = x;
532                 if (isCGOverridden())
533                         fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
534                 else
535                         fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
536         }
537         
538         /**
539          * Return whether the CG is currently overridden.
540          *
541          * @return  whether the CG is overridden
542          */
543         public final boolean isCGOverridden() {
544                 mutex.verify();
545                 return cgOverriden;
546         }
547         
548         /**
549          * Set whether the CG is currently overridden.
550          *
551          * @param o  whether the CG is overridden
552          */
553         public final void setCGOverridden(boolean o) {
554                 if (cgOverriden == o) {
555                         return;
556                 }
557                 checkState();
558                 cgOverriden = o;
559                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
560         }
561         
562         
563
564         /**
565          * Return whether the mass and/or CG override overrides all subcomponent values
566          * as well.  The default implementation is a normal getter/setter implementation,
567          * however, subclasses are allowed to override this behavior if some subclass
568          * always or never overrides subcomponents.  In this case the subclass should
569          * also override {@link #isOverrideSubcomponentsEnabled()} to return
570          * <code>false</code>.
571          *
572          * @return      whether the current mass and/or CG override overrides subcomponents as well.
573          */
574         public boolean getOverrideSubcomponents() {
575                 mutex.verify();
576                 return overrideSubcomponents;
577         }
578         
579         
580         /**
581          * Set whether the mass and/or CG override overrides all subcomponent values
582          * as well.  See {@link #getOverrideSubcomponents()} for details.
583          *
584          * @param override      whether the mass and/or CG override overrides all subcomponent.
585          */
586         public void setOverrideSubcomponents(boolean override) {
587                 if (overrideSubcomponents == override) {
588                         return;
589                 }
590                 checkState();
591                 overrideSubcomponents = override;
592                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
593         }
594         
595         /**
596          * Return whether the option to override all subcomponents is enabled or not.
597          * The default implementation returns <code>false</code> if neither mass nor
598          * CG is overridden, <code>true</code> otherwise.
599          * <p>
600          * This method may be overridden if the setting of overriding subcomponents
601          * cannot be set.
602          *
603          * @return      whether the option to override subcomponents is currently enabled.
604          */
605         public boolean isOverrideSubcomponentsEnabled() {
606                 mutex.verify();
607                 return isCGOverridden() || isMassOverridden();
608         }
609         
610         
611
612
613         /**
614          * Get the user-defined name of the component.
615          */
616         public final String getName() {
617                 mutex.verify();
618                 return name;
619         }
620         
621         /**
622          * Set the user-defined name of the component.  If name==null, sets the name to
623          * the default name, currently the component name.
624          */
625         public final void setName(String name) {
626                 if (this.name.equals(name)) {
627                         return;
628                 }
629                 checkState();
630                 if (name == null || name.matches("^\\s*$"))
631                         this.name = getComponentName();
632                 else
633                         this.name = name;
634                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
635         }
636         
637         
638         /**
639          * Return the comment of the component.  The component may contain multiple lines
640          * using \n as a newline separator.
641          *
642          * @return  the comment of the component.
643          */
644         public final String getComment() {
645                 mutex.verify();
646                 return comment;
647         }
648         
649         /**
650          * Set the comment of the component.
651          *
652          * @param comment  the comment of the component.
653          */
654         public final void setComment(String comment) {
655                 if (this.comment.equals(comment))
656                         return;
657                 checkState();
658                 if (comment == null)
659                         this.comment = "";
660                 else
661                         this.comment = comment;
662                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
663         }
664         
665         
666
667         /**
668          * Return the preset component that this component is based upon.
669          * 
670          * @return      the preset component, or <code>null</code> if this is not based on a preset.
671          */
672         public final ComponentPreset getPresetComponent() {
673                 return presetComponent;
674         }
675         
676         /**
677          * Set the preset component this component is based upon and load all of the 
678          * preset values.
679          * 
680          * @param preset        the preset component to load, or <code>null</code> to clear the preset.
681          */
682         public final void loadPreset(ComponentPreset preset) {
683                 if (presetComponent == preset) {
684                         return;
685                 }
686                 
687                 if (preset == null) {
688                         clearPreset();
689                         return;
690                 }
691                 
692                 if (preset.getComponentClass() != this.getClass()) {
693                         throw new IllegalArgumentException("Attempting to load preset of type " + preset.getComponentClass()
694                                                 + " into component of type " + this.getClass());
695                 }
696                 
697                 RocketComponent root = getRoot();
698                 final Rocket rocket;
699                 if (root instanceof Rocket) {
700                         rocket = (Rocket) root;
701                 } else {
702                         rocket = null;
703                 }
704                 
705                 try {
706                         if (rocket != null) {
707                                 rocket.freeze();
708                         }
709                         
710                         loadFromPreset(preset.getPrototype());
711                         
712                         this.presetComponent = preset;
713                         fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
714                         
715                 } finally {
716                         if (rocket != null) {
717                                 rocket.thaw();
718                         }
719                 }
720         }
721         
722         
723         /**
724          * Load component properties from the specified preset.  The preset is guaranteed
725          * to be of the correct type.
726          * <p>
727          * This method should fire the appropriate events related to the changes.  The rocket
728          * is frozen by the caller, so the events will be automatically combined.
729          * <p>
730          * This method must FIRST perform the preset loading and THEN call super.loadFromPreset().
731          * This is because mass setting requires the dimensions to be set beforehand.
732          * 
733          * @param preset        the preset to load from
734          */
735         protected void loadFromPreset(RocketComponent preset) {
736                 // No-op
737         }
738         
739         
740         /**
741          * Clear the current component preset.  This does not affect the component properties
742          * otherwise.
743          */
744         public final void clearPreset() {
745                 if (presetComponent == null)
746                         return;
747                 presetComponent = null;
748                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
749         }
750         
751         
752
753         /**
754          * Returns the unique ID of the component.
755          *
756          * @return      the ID of the component.
757          */
758         public final String getID() {
759                 return id;
760         }
761         
762         /**
763          * Generate a new ID for this component.
764          */
765         private final void newID() {
766                 mutex.verify();
767                 this.id = UniqueID.uuid();
768         }
769         
770         
771
772
773         /**
774          * Get the characteristic length of the component, for example the length of a body tube
775          * of the length of the root chord of a fin.  This is used in positioning the component
776          * relative to its parent.
777          *
778          * If the length of a component is settable, the class must define the setter method
779          * itself.
780          */
781         public final double getLength() {
782                 mutex.verify();
783                 return length;
784         }
785         
786         /**
787          * Get the positioning of the component relative to its parent component.
788          * This is one of the enums of {@link Position}.  A setter method is not provided,
789          * but can be provided by a subclass.
790          */
791         public final Position getRelativePosition() {
792                 mutex.verify();
793                 return relativePosition;
794         }
795         
796         
797         /**
798          * Set the positioning of the component relative to its parent component.
799          * The actual position of the component is maintained to the best ability.
800          * <p>
801          * The default implementation is of protected visibility, since many components
802          * do not support setting the relative position.  A component that does support
803          * it should override this with a public method that simply calls this
804          * supermethod AND fire a suitable ComponentChangeEvent.
805          *
806          * @param position      the relative positioning.
807          */
808         protected void setRelativePosition(RocketComponent.Position position) {
809                 if (this.relativePosition == position)
810                         return;
811                 checkState();
812                 
813                 // Update position so as not to move the component
814                 if (this.parent != null) {
815                         double thisPos = this.toRelative(Coordinate.NUL, this.parent)[0].x;
816                         
817                         switch (position) {
818                         case ABSOLUTE:
819                                 this.position = this.toAbsolute(Coordinate.NUL)[0].x;
820                                 break;
821                         
822                         case TOP:
823                                 this.position = thisPos;
824                                 break;
825                         
826                         case MIDDLE:
827                                 this.position = thisPos - (this.parent.length - this.length) / 2;
828                                 break;
829                         
830                         case BOTTOM:
831                                 this.position = thisPos - (this.parent.length - this.length);
832                                 break;
833                         
834                         default:
835                                 throw new BugException("Unknown position type: " + position);
836                         }
837                 }
838                 
839                 this.relativePosition = position;
840                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
841         }
842         
843         
844
845
846         /**
847          * Get the position value of the component.  The exact meaning of the value is
848          * dependent on the current relative positioning.
849          *
850          * @return  the positional value.
851          */
852         public final double getPositionValue() {
853                 mutex.verify();
854                 return position;
855         }
856         
857         
858         /**
859          * Set the position value of the component.  The exact meaning of the value
860          * depends on the current relative positioning.
861          * <p>
862          * The default implementation is of protected visibility, since many components
863          * do not support setting the relative position.  A component that does support
864          * it should override this with a public method that simply calls this
865          * supermethod AND fire a suitable ComponentChangeEvent.
866          *
867          * @param value         the position value of the component.
868          */
869         public void setPositionValue(double value) {
870                 if (MathUtil.equals(this.position, value))
871                         return;
872                 checkState();
873                 this.position = value;
874         }
875         
876         
877
878         ///////////  Coordinate changes  ///////////
879         
880         /**
881          * Returns coordinate c in absolute coordinates.  Equivalent to toComponent(c,null).
882          */
883         public Coordinate[] toAbsolute(Coordinate c) {
884                 checkState();
885                 return toRelative(c, null);
886         }
887         
888         
889         /**
890          * Return coordinate <code>c</code> described in the coordinate system of
891          * <code>dest</code>.  If <code>dest</code> is <code>null</code> returns
892          * absolute coordinates.
893          * <p>
894          * This method returns an array of coordinates, each of which represents a
895          * position of the coordinate in clustered cases.  The array is guaranteed
896          * to contain at least one element.
897          * <p>
898          * The current implementation does not support rotating components.
899          *
900          * @param c    Coordinate in the component's coordinate system.
901          * @param dest Destination component coordinate system.
902          * @return     an array of coordinates describing <code>c</code> in coordinates
903          *                         relative to <code>dest</code>.
904          */
905         public final Coordinate[] toRelative(Coordinate c, RocketComponent dest) {
906                 checkState();
907                 mutex.lock("toRelative");
908                 try {
909                         double absoluteX = Double.NaN;
910                         RocketComponent search = dest;
911                         Coordinate[] array = new Coordinate[1];
912                         array[0] = c;
913                         
914                         RocketComponent component = this;
915                         while ((component != search) && (component.parent != null)) {
916                                 
917                                 array = component.shiftCoordinates(array);
918                                 
919                                 switch (component.relativePosition) {
920                                 case TOP:
921                                         for (int i = 0; i < array.length; i++) {
922                                                 array[i] = array[i].add(component.position, 0, 0);
923                                         }
924                                         break;
925                                 
926                                 case MIDDLE:
927                                         for (int i = 0; i < array.length; i++) {
928                                                 array[i] = array[i].add(component.position +
929                                                                 (component.parent.length - component.length) / 2, 0, 0);
930                                         }
931                                         break;
932                                 
933                                 case BOTTOM:
934                                         for (int i = 0; i < array.length; i++) {
935                                                 array[i] = array[i].add(component.position +
936                                                                 (component.parent.length - component.length), 0, 0);
937                                         }
938                                         break;
939                                 
940                                 case AFTER:
941                                         // Add length of all previous brother-components with POSITION_RELATIVE_AFTER
942                                         int index = component.parent.children.indexOf(component);
943                                         assert (index >= 0);
944                                         for (index--; index >= 0; index--) {
945                                                 RocketComponent comp = component.parent.children.get(index);
946                                                 double componentLength = comp.getTotalLength();
947                                                 for (int i = 0; i < array.length; i++) {
948                                                         array[i] = array[i].add(componentLength, 0, 0);
949                                                 }
950                                         }
951                                         for (int i = 0; i < array.length; i++) {
952                                                 array[i] = array[i].add(component.position + component.parent.length, 0, 0);
953                                         }
954                                         break;
955                                 
956                                 case ABSOLUTE:
957                                         search = null; // Requires back-search if dest!=null
958                                         if (Double.isNaN(absoluteX)) {
959                                                 absoluteX = component.position;
960                                         }
961                                         break;
962                                 
963                                 default:
964                                         throw new BugException("Unknown relative positioning type of component" +
965                                                         component + ": " + component.relativePosition);
966                                 }
967                                 
968                                 component = component.parent; // parent != null
969                         }
970                         
971                         if (!Double.isNaN(absoluteX)) {
972                                 for (int i = 0; i < array.length; i++) {
973                                         array[i] = array[i].setX(absoluteX + c.x);
974                                 }
975                         }
976                         
977                         // Check whether destination has been found or whether to backtrack
978                         // TODO: LOW: Backtracking into clustered components uses only one component
979                         if ((dest != null) && (component != dest)) {
980                                 Coordinate[] origin = dest.toAbsolute(Coordinate.NUL);
981                                 for (int i = 0; i < array.length; i++) {
982                                         array[i] = array[i].sub(origin[0]);
983                                 }
984                         }
985                         
986                         return array;
987                 } finally {
988                         mutex.unlock("toRelative");
989                 }
990         }
991         
992         
993         /**
994          * Recursively sum the lengths of all subcomponents that have position
995          * Position.AFTER.
996          *
997          * @return  Sum of the lengths.
998          */
999         private final double getTotalLength() {
1000                 checkState();
1001                 this.checkComponentStructure();
1002                 mutex.lock("getTotalLength");
1003                 try {
1004                         double l = 0;
1005                         if (relativePosition == Position.AFTER)
1006                                 l = length;
1007                         for (int i = 0; i < children.size(); i++)
1008                                 l += children.get(i).getTotalLength();
1009                         return l;
1010                 } finally {
1011                         mutex.unlock("getTotalLength");
1012                 }
1013         }
1014         
1015         
1016
1017         /////////// Total mass and CG calculation ////////////
1018         
1019         /**
1020          * Return the (possibly overridden) mass of component.
1021          *
1022          * @return The mass of the component or the given override mass.
1023          */
1024         public final double getMass() {
1025                 mutex.verify();
1026                 if (massOverriden)
1027                         return overrideMass;
1028                 return getComponentMass();
1029         }
1030         
1031         /**
1032          * Return the (possibly overridden) center of gravity and mass.
1033          *
1034          * Returns the CG with the weight of the coordinate set to the weight of the component.
1035          * Both CG and mass may be separately overridden.
1036          *
1037          * @return The CG of the component or the given override CG.
1038          */
1039         public final Coordinate getCG() {
1040                 checkState();
1041                 if (cgOverriden)
1042                         return getOverrideCG().setWeight(getMass());
1043                 
1044                 if (massOverriden)
1045                         return getComponentCG().setWeight(getMass());
1046                 
1047                 return getComponentCG();
1048         }
1049         
1050         
1051         /**
1052          * Return the longitudinal (around the y- or z-axis) moment of inertia of this component.
1053          * The moment of inertia is scaled in reference to the (possibly overridden) mass
1054          * and is relative to the non-overridden CG.
1055          *
1056          * @return    the longitudinal moment of inertia of this component.
1057          */
1058         public final double getLongitudinalInertia() {
1059                 checkState();
1060                 return getLongitudinalUnitInertia() * getMass();
1061         }
1062         
1063         /**
1064          * Return the rotational (around the y- or z-axis) moment of inertia of this component.
1065          * The moment of inertia is scaled in reference to the (possibly overridden) mass
1066          * and is relative to the non-overridden CG.
1067          *
1068          * @return    the rotational moment of inertia of this component.
1069          */
1070         public final double getRotationalInertia() {
1071                 checkState();
1072                 return getRotationalUnitInertia() * getMass();
1073         }
1074         
1075         
1076
1077         ///////////  Children handling  ///////////
1078         
1079
1080         /**
1081          * Adds a child to the rocket component tree.  The component is added to the end
1082          * of the component's child list.  This is a helper method that calls
1083          * {@link #addChild(RocketComponent,int)}.
1084          *
1085          * @param component  The component to add.
1086          * @throws IllegalArgumentException  if the component is already part of some
1087          *                                                                       component tree.
1088          * @see #addChild(RocketComponent,int)
1089          */
1090         public final void addChild(RocketComponent component) {
1091                 checkState();
1092                 addChild(component, children.size());
1093         }
1094         
1095         
1096         /**
1097          * Adds a child to the rocket component tree.  The component is added to
1098          * the given position of the component's child list.
1099          * <p>
1100          * This method may be overridden to enforce more strict component addition rules.
1101          * The tests should be performed first and then this method called.
1102          *
1103          * @param component     The component to add.
1104          * @param index         Position to add component to.
1105          * @throws IllegalArgumentException  If the component is already part of
1106          *                                                                       some component tree.
1107          */
1108         public void addChild(RocketComponent component, int index) {
1109                 checkState();
1110                 if (component.parent != null) {
1111                         throw new IllegalArgumentException("component " + component.getComponentName() +
1112                                         " is already in a tree");
1113                 }
1114                 if (!isCompatible(component)) {
1115                         throw new IllegalStateException("Component " + component.getComponentName() +
1116                                         " not currently compatible with component " + getComponentName());
1117                 }
1118                 
1119                 children.add(index, component);
1120                 component.parent = this;
1121                 
1122                 this.checkComponentStructure();
1123                 component.checkComponentStructure();
1124                 
1125                 fireAddRemoveEvent(component);
1126         }
1127         
1128         
1129         /**
1130          * Removes a child from the rocket component tree.
1131          *
1132          * @param n  remove the n'th child.
1133          * @throws IndexOutOfBoundsException  if n is out of bounds
1134          */
1135         public final void removeChild(int n) {
1136                 checkState();
1137                 RocketComponent component = children.remove(n);
1138                 component.parent = null;
1139                 
1140                 this.checkComponentStructure();
1141                 component.checkComponentStructure();
1142                 
1143                 fireAddRemoveEvent(component);
1144         }
1145         
1146         /**
1147          * Removes a child from the rocket component tree.  Does nothing if the component
1148          * is not present as a child.
1149          *
1150          * @param component             the component to remove
1151          * @return                              whether the component was a child
1152          */
1153         public final boolean removeChild(RocketComponent component) {
1154                 checkState();
1155                 
1156                 component.checkComponentStructure();
1157                 
1158                 if (children.remove(component)) {
1159                         component.parent = null;
1160                         
1161                         this.checkComponentStructure();
1162                         component.checkComponentStructure();
1163                         
1164                         fireAddRemoveEvent(component);
1165                         return true;
1166                 }
1167                 return false;
1168         }
1169         
1170         
1171
1172
1173         /**
1174          * Move a child to another position.
1175          *
1176          * @param component     the component to move
1177          * @param index the component's new position
1178          * @throws IllegalArgumentException If an illegal placement was attempted.
1179          */
1180         public final void moveChild(RocketComponent component, int index) {
1181                 checkState();
1182                 if (children.remove(component)) {
1183                         children.add(index, component);
1184                         
1185                         this.checkComponentStructure();
1186                         component.checkComponentStructure();
1187                         
1188                         fireAddRemoveEvent(component);
1189                 }
1190         }
1191         
1192         
1193         /**
1194          * Fires an AERODYNAMIC_CHANGE, MASS_CHANGE or OTHER_CHANGE event depending on the
1195          * type of component removed.
1196          */
1197         private void fireAddRemoveEvent(RocketComponent component) {
1198                 Iterator<RocketComponent> iter = component.iterator(true);
1199                 int type = ComponentChangeEvent.TREE_CHANGE;
1200                 while (iter.hasNext()) {
1201                         RocketComponent c = iter.next();
1202                         if (c.isAerodynamic())
1203                                 type |= ComponentChangeEvent.AERODYNAMIC_CHANGE;
1204                         if (c.isMassive())
1205                                 type |= ComponentChangeEvent.MASS_CHANGE;
1206                 }
1207                 
1208                 fireComponentChangeEvent(type);
1209         }
1210         
1211         
1212         public final int getChildCount() {
1213                 checkState();
1214                 this.checkComponentStructure();
1215                 return children.size();
1216         }
1217         
1218         public final RocketComponent getChild(int n) {
1219                 checkState();
1220                 this.checkComponentStructure();
1221                 return children.get(n);
1222         }
1223         
1224         public final List<RocketComponent> getChildren() {
1225                 checkState();
1226                 this.checkComponentStructure();
1227                 return children.clone();
1228         }
1229         
1230         
1231         /**
1232          * Returns the position of the child in this components child list, or -1 if the
1233          * component is not a child of this component.
1234          *
1235          * @param child  The child to search for.
1236          * @return  Position in the list or -1 if not found.
1237          */
1238         public final int getChildPosition(RocketComponent child) {
1239                 checkState();
1240                 this.checkComponentStructure();
1241                 return children.indexOf(child);
1242         }
1243         
1244         /**
1245          * Get the parent component of this component.  Returns <code>null</code> if the component
1246          * has no parent.
1247          *
1248          * @return  The parent of this component or <code>null</code>.
1249          */
1250         public final RocketComponent getParent() {
1251                 checkState();
1252                 return parent;
1253         }
1254         
1255         /**
1256          * Get the root component of the component tree.
1257          *
1258          * @return  The root component of the component tree.
1259          */
1260         public final RocketComponent getRoot() {
1261                 checkState();
1262                 RocketComponent gp = this;
1263                 while (gp.parent != null)
1264                         gp = gp.parent;
1265                 return gp;
1266         }
1267         
1268         /**
1269          * Returns the root Rocket component of this component tree.  Throws an
1270          * IllegalStateException if the root component is not a Rocket.
1271          *
1272          * @return  The root Rocket component of the component tree.
1273          * @throws  IllegalStateException  If the root component is not a Rocket.
1274          */
1275         public final Rocket getRocket() {
1276                 checkState();
1277                 RocketComponent r = getRoot();
1278                 if (r instanceof Rocket)
1279                         return (Rocket) r;
1280                 throw new IllegalStateException("getRocket() called with root component "
1281                                 + r.getComponentName());
1282         }
1283         
1284         
1285         /**
1286          * Return the Stage component that this component belongs to.  Throws an
1287          * IllegalStateException if a Stage is not in the parentage of this component.
1288          *
1289          * @return      The Stage component this component belongs to.
1290          * @throws      IllegalStateException   if a Stage component is not in the parentage.
1291          */
1292         public final Stage getStage() {
1293                 checkState();
1294                 RocketComponent c = this;
1295                 while (c != null) {
1296                         if (c instanceof Stage)
1297                                 return (Stage) c;
1298                         c = c.getParent();
1299                 }
1300                 throw new IllegalStateException("getStage() called without Stage as a parent.");
1301         }
1302         
1303         /**
1304          * Return the stage number of the stage this component belongs to.  The stages
1305          * are numbered from zero upwards.
1306          *
1307          * @return   the stage number this component belongs to.
1308          */
1309         public final int getStageNumber() {
1310                 checkState();
1311                 if (parent == null) {
1312                         throw new IllegalArgumentException("getStageNumber() called for root component");
1313                 }
1314                 
1315                 RocketComponent stage = this;
1316                 while (!(stage instanceof Stage)) {
1317                         stage = stage.parent;
1318                         if (stage == null || stage.parent == null) {
1319                                 throw new IllegalStateException("getStageNumber() could not find parent " +
1320                                                 "stage.");
1321                         }
1322                 }
1323                 return stage.parent.getChildPosition(stage);
1324         }
1325         
1326         
1327         /**
1328          * Find a component with the given ID.  The component tree is searched from this component
1329          * down (including this component) for the ID and the corresponding component is returned,
1330          * or null if not found.
1331          *
1332          * @param idToFind  ID to search for.
1333          * @return    The component with the ID, or null if not found.
1334          */
1335         public final RocketComponent findComponent(String idToFind) {
1336                 checkState();
1337                 Iterator<RocketComponent> iter = this.iterator(true);
1338                 while (iter.hasNext()) {
1339                         RocketComponent c = iter.next();
1340                         if (c.getID().equals(idToFind))
1341                                 return c;
1342                 }
1343                 return null;
1344         }
1345         
1346         
1347         // TODO: Move these methods elsewhere (used only in SymmetricComponent)
1348         public final RocketComponent getPreviousComponent() {
1349                 checkState();
1350                 this.checkComponentStructure();
1351                 if (parent == null)
1352                         return null;
1353                 int pos = parent.getChildPosition(this);
1354                 if (pos < 0) {
1355                         StringBuffer sb = new StringBuffer();
1356                         sb.append("Inconsistent internal state: ");
1357                         sb.append("this=").append(this).append('[')
1358                                         .append(System.identityHashCode(this)).append(']');
1359                         sb.append(" parent.children=[");
1360                         for (int i = 0; i < parent.children.size(); i++) {
1361                                 RocketComponent c = parent.children.get(i);
1362                                 sb.append(c).append('[').append(System.identityHashCode(c)).append(']');
1363                                 if (i < parent.children.size() - 1)
1364                                         sb.append(", ");
1365                         }
1366                         sb.append(']');
1367                         throw new IllegalStateException(sb.toString());
1368                 }
1369                 assert (pos >= 0);
1370                 if (pos == 0)
1371                         return parent;
1372                 RocketComponent c = parent.getChild(pos - 1);
1373                 while (c.getChildCount() > 0)
1374                         c = c.getChild(c.getChildCount() - 1);
1375                 return c;
1376         }
1377         
1378         // TODO: Move these methods elsewhere (used only in SymmetricComponent)
1379         public final RocketComponent getNextComponent() {
1380                 checkState();
1381                 if (getChildCount() > 0)
1382                         return getChild(0);
1383                 
1384                 RocketComponent current = this;
1385                 RocketComponent nextParent = this.parent;
1386                 
1387                 while (nextParent != null) {
1388                         int pos = nextParent.getChildPosition(current);
1389                         if (pos < nextParent.getChildCount() - 1)
1390                                 return nextParent.getChild(pos + 1);
1391                         
1392                         current = nextParent;
1393                         nextParent = current.parent;
1394                 }
1395                 return null;
1396         }
1397         
1398         
1399         ///////////  Event handling  //////////
1400         //
1401         // Listener lists are provided by the root Rocket component,
1402         // a single listener list for the whole rocket.
1403         //
1404         
1405         /**
1406          * Adds a ComponentChangeListener to the rocket tree.  The listener is added to the root
1407          * component, which must be of type Rocket (which overrides this method).  Events of all
1408          * subcomponents are sent to all listeners.
1409          *
1410          * @throws IllegalStateException - if the root component is not a Rocket
1411          */
1412         public void addComponentChangeListener(ComponentChangeListener l) {
1413                 checkState();
1414                 getRocket().addComponentChangeListener(l);
1415         }
1416         
1417         /**
1418          * Removes a ComponentChangeListener from the rocket tree.  The listener is removed from
1419          * the root component, which must be of type Rocket (which overrides this method).
1420          * Does nothing if the root component is not a Rocket.  (The asymmetry is so
1421          * that listeners can always be removed just in case.)
1422          *
1423          * @param l  Listener to remove
1424          */
1425         public void removeComponentChangeListener(ComponentChangeListener l) {
1426                 if (parent != null) {
1427                         getRoot().removeComponentChangeListener(l);
1428                 }
1429         }
1430         
1431         
1432         /**
1433          * Adds a <code>ChangeListener</code> to the rocket tree.  This is identical to
1434          * <code>addComponentChangeListener()</code> except that it uses a
1435          * <code>ChangeListener</code>.  The same events are dispatched to the
1436          * <code>ChangeListener</code>, as <code>ComponentChangeEvent</code> is a subclass
1437          * of <code>ChangeEvent</code>.
1438          *
1439          * @throws IllegalStateException - if the root component is not a <code>Rocket</code>
1440          */
1441         @Override
1442         public void addChangeListener(ChangeListener l) {
1443                 checkState();
1444                 getRocket().addChangeListener(l);
1445         }
1446         
1447         /**
1448          * Removes a ChangeListener from the rocket tree.  This is identical to
1449          * removeComponentChangeListener() except it uses a ChangeListener.
1450          * Does nothing if the root component is not a Rocket.  (The asymmetry is so
1451          * that listeners can always be removed just in case.)
1452          *
1453          * @param l  Listener to remove
1454          */
1455         @Override
1456         public void removeChangeListener(ChangeListener l) {
1457                 if (this.parent != null) {
1458                         getRoot().removeChangeListener(l);
1459                 }
1460         }
1461         
1462         
1463         /**
1464          * Fires a ComponentChangeEvent on the rocket structure.  The call is passed to the
1465          * root component, which must be of type Rocket (which overrides this method).
1466          * Events of all subcomponents are sent to all listeners.
1467          *
1468          * If the component tree root is not a Rocket, the event is ignored.  This is the
1469          * case when constructing components not in any Rocket tree.  In this case it
1470          * would be impossible for the component to have listeners in any case.
1471          *
1472          * @param e  Event to send
1473          */
1474         protected void fireComponentChangeEvent(ComponentChangeEvent e) {
1475                 checkState();
1476                 if (parent == null) {
1477                         /* Ignore if root invalid. */
1478                         log.debug("Attempted firing event " + e + " with root " + this.getComponentName() + ", ignoring event");
1479                         return;
1480                 }
1481                 getRoot().fireComponentChangeEvent(e);
1482         }
1483         
1484         
1485         /**
1486          * Fires a ComponentChangeEvent of the given type.  The source of the event is set to
1487          * this component.
1488          *
1489          * @param type  Type of event
1490          * @see #fireComponentChangeEvent(ComponentChangeEvent)
1491          */
1492         protected void fireComponentChangeEvent(int type) {
1493                 fireComponentChangeEvent(new ComponentChangeEvent(this, type));
1494         }
1495         
1496         
1497         /**
1498          * Checks whether this component has been invalidated and should no longer be used.
1499          * This is a safety check that in-place replaced components are no longer used.
1500          * All non-trivial methods (with the exception of methods simply getting a property)
1501          * should call this method before changing or computing anything.
1502          *
1503          * @throws      BugException    if this component has been invalidated by {@link #copyFrom(RocketComponent)}.
1504          */
1505         protected void checkState() {
1506                 invalidator.check(true);
1507                 mutex.verify();
1508         }
1509         
1510         
1511         /**
1512          * Check that the local component structure is correct.  This can be called after changing
1513          * the component structure in order to verify the integrity.
1514          * <p>
1515          * TODO: Remove this after the "inconsistent internal state" bug has been corrected
1516          */
1517         public void checkComponentStructure() {
1518                 if (this.parent != null) {
1519                         // Test that this component is found in parent's children with == operator
1520                         if (!containsExact(this.parent.children, this)) {
1521                                 throw new BugException("Inconsistent component structure detected, parent does not contain this " +
1522                                                 "component as a child, parent=" + parent.toDebugString() + " this=" + this.toDebugString());
1523                         }
1524                 }
1525                 for (RocketComponent child : this.children) {
1526                         if (child.parent != this) {
1527                                 throw new BugException("Inconsistent component structure detected, child does not have this component " +
1528                                                 "as the parent, this=" + this.toDebugString() + " child=" + child.toDebugString() +
1529                                                 " child.parent=" + (child.parent == null ? "null" : child.parent.toDebugString()));
1530                         }
1531                 }
1532         }
1533         
1534         // Check whether the list contains exactly the searched-for component (with == operator)
1535         private boolean containsExact(List<RocketComponent> haystack, RocketComponent needle) {
1536                 for (RocketComponent c : haystack) {
1537                         if (needle == c) {
1538                                 return true;
1539                         }
1540                 }
1541                 return false;
1542         }
1543         
1544         
1545         ///////////  Iterators  //////////
1546         
1547         /**
1548          * Returns an iterator that iterates over all children and sub-children.
1549          * <p>
1550          * The iterator iterates through all children below this object, including itself if
1551          * <code>returnSelf</code> is true.  The order of the iteration is not specified
1552          * (it may be specified in the future).
1553          * <p>
1554          * If an iterator iterating over only the direct children of the component is required,
1555          * use <code>component.getChildren().iterator()</code>.
1556          *
1557          * TODO: HIGH: Remove this after merges have been done
1558          *
1559          * @param returnSelf boolean value specifying whether the component itself should be
1560          *                                       returned
1561          * @return An iterator for the children and sub-children.
1562          * @deprecated Use {@link #iterator(boolean)} instead
1563          */
1564         @Deprecated
1565         public final Iterator<RocketComponent> deepIterator(boolean returnSelf) {
1566                 return iterator(returnSelf);
1567         }
1568         
1569         
1570         /**
1571          * Returns an iterator that iterates over all children and sub-children, including itself.
1572          * <p>
1573          * This method is equivalent to <code>deepIterator(true)</code>.
1574          *
1575          * TODO: HIGH: Remove this after merges have been done
1576          *
1577          * @return An iterator for this component, its children and sub-children.
1578          * @deprecated Use {@link #iterator()} instead
1579          */
1580         @Deprecated
1581         public final Iterator<RocketComponent> deepIterator() {
1582                 return iterator();
1583         }
1584         
1585         
1586
1587         /**
1588          * Returns an iterator that iterates over all children and sub-children.
1589          * <p>
1590          * The iterator iterates through all children below this object, including itself if
1591          * <code>returnSelf</code> is true.  The order of the iteration is not specified
1592          * (it may be specified in the future).
1593          * <p>
1594          * If an iterator iterating over only the direct children of the component is required,
1595          * use <code>component.getChildren().iterator()</code>.
1596          *
1597          * @param returnSelf boolean value specifying whether the component itself should be
1598          *                                       returned
1599          * @return An iterator for the children and sub-children.
1600          */
1601         public final Iterator<RocketComponent> iterator(boolean returnSelf) {
1602                 checkState();
1603                 return new RocketComponentIterator(this, returnSelf);
1604         }
1605         
1606         
1607         /**
1608          * Returns an iterator that iterates over this component, its children and sub-children.
1609          * <p>
1610          * This method is equivalent to <code>iterator(true)</code>.
1611          *
1612          * @return An iterator for this component, its children and sub-children.
1613          */
1614         @Override
1615         public final Iterator<RocketComponent> iterator() {
1616                 return iterator(true);
1617         }
1618         
1619         
1620
1621
1622
1623         /**
1624          * Compare component equality based on the ID of this component.  Only the
1625          * ID and class type is used for a basis of comparison.
1626          */
1627         @Override
1628         public boolean equals(Object obj) {
1629                 if (this == obj)
1630                         return true;
1631                 if (obj == null)
1632                         return false;
1633                 if (this.getClass() != obj.getClass())
1634                         return false;
1635                 RocketComponent other = (RocketComponent) obj;
1636                 return this.id.equals(other.id);
1637         }
1638         
1639         
1640
1641         @Override
1642         public int hashCode() {
1643                 return id.hashCode();
1644         }
1645         
1646         
1647
1648         ////////////  Helper methods for subclasses
1649         
1650
1651
1652
1653         /**
1654          * Helper method to add rotationally symmetric bounds at the specified coordinates.
1655          * The X-axis value is <code>x</code> and the radius at the specified position is
1656          * <code>r</code>.
1657          */
1658         protected static final void addBound(Collection<Coordinate> bounds, double x, double r) {
1659                 bounds.add(new Coordinate(x, -r, -r));
1660                 bounds.add(new Coordinate(x, r, -r));
1661                 bounds.add(new Coordinate(x, r, r));
1662                 bounds.add(new Coordinate(x, -r, r));
1663         }
1664         
1665         
1666         protected static final Coordinate ringCG(double outerRadius, double innerRadius,
1667                         double x1, double x2, double density) {
1668                 return new Coordinate((x1 + x2) / 2, 0, 0,
1669                                 ringMass(outerRadius, innerRadius, x2 - x1, density));
1670         }
1671         
1672         protected static final double ringMass(double outerRadius, double innerRadius,
1673                         double length, double density) {
1674                 return Math.PI * (MathUtil.pow2(outerRadius) - MathUtil.pow2(innerRadius)) *
1675                                         length * density;
1676         }
1677         
1678         protected static final double ringLongitudinalUnitInertia(double outerRadius,
1679                         double innerRadius, double length) {
1680                 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
1681                 return (3 * (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) + MathUtil.pow2(length)) / 12;
1682         }
1683         
1684         protected static final double ringRotationalUnitInertia(double outerRadius,
1685                         double innerRadius) {
1686                 // 1/2 * (r1^2 + r2^2)
1687                 return (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) / 2;
1688         }
1689         
1690         
1691
1692         ////////////  OTHER
1693         
1694
1695         /**
1696          * Loads the RocketComponent fields from the given component.  This method is meant
1697          * for in-place replacement of a component.  It is used with the undo/redo
1698          * mechanism and when converting a finset into a freeform fin set.
1699          * This component must not have a parent, otherwise this method will fail.
1700          * <p>
1701          * The child components in the source tree are copied into the current tree, however,
1702          * the original components should not be used since they represent old copies of the
1703          * components.  It is recommended to invalidate them by calling {@link #invalidate()}.
1704          * <p>
1705          * This method returns a list of components that should be invalidated after references
1706          * to them have been removed (for example by firing appropriate events).  The list contains
1707          * all children and sub-children of the current component and the entire component
1708          * tree of <code>src</code>.
1709          *
1710          * @return      a list of components that should not be used after this call.
1711          */
1712         protected List<RocketComponent> copyFrom(RocketComponent src) {
1713                 checkState();
1714                 List<RocketComponent> toInvalidate = new ArrayList<RocketComponent>();
1715                 
1716                 if (this.parent != null) {
1717                         throw new UnsupportedOperationException("copyFrom called for non-root component, parent=" +
1718                                         this.parent.toDebugString() + ", this=" + this.toDebugString());
1719                 }
1720                 
1721                 // Add current structure to be invalidated
1722                 Iterator<RocketComponent> iterator = this.iterator(false);
1723                 while (iterator.hasNext()) {
1724                         toInvalidate.add(iterator.next());
1725                 }
1726                 
1727                 // Remove previous components
1728                 for (RocketComponent child : this.children) {
1729                         child.parent = null;
1730                 }
1731                 this.children.clear();
1732                 
1733                 // Copy new children to this component
1734                 for (RocketComponent c : src.children) {
1735                         RocketComponent copy = c.copyWithOriginalID();
1736                         this.children.add(copy);
1737                         copy.parent = this;
1738                 }
1739                 
1740                 this.checkComponentStructure();
1741                 src.checkComponentStructure();
1742                 
1743                 // Set all parameters
1744                 this.length = src.length;
1745                 this.relativePosition = src.relativePosition;
1746                 this.position = src.position;
1747                 this.color = src.color;
1748                 this.lineStyle = src.lineStyle;
1749                 this.overrideMass = src.overrideMass;
1750                 this.massOverriden = src.massOverriden;
1751                 this.overrideCGX = src.overrideCGX;
1752                 this.cgOverriden = src.cgOverriden;
1753                 this.overrideSubcomponents = src.overrideSubcomponents;
1754                 this.name = src.name;
1755                 this.comment = src.comment;
1756                 this.id = src.id;
1757                 
1758                 // Add source components to invalidation tree
1759                 for (RocketComponent c : src) {
1760                         toInvalidate.add(c);
1761                 }
1762                 
1763                 return toInvalidate;
1764         }
1765         
1766         protected void invalidate() {
1767                 invalidator.invalidate();
1768         }
1769         
1770         
1771         //////////  Iterator implementation  ///////////
1772         
1773         /**
1774          * Private inner class to implement the Iterator.
1775          *
1776          * This iterator is fail-fast if the root of the structure is a Rocket.
1777          */
1778         private static class RocketComponentIterator implements Iterator<RocketComponent> {
1779                 // Stack holds iterators which still have some components left.
1780                 private final Deque<Iterator<RocketComponent>> iteratorStack = new ArrayDeque<Iterator<RocketComponent>>();
1781                 
1782                 private final Rocket root;
1783                 private final int treeModID;
1784                 
1785                 private final RocketComponent original;
1786                 private boolean returnSelf = false;
1787                 
1788                 // Construct iterator with component's child's iterator, if it has elements
1789                 public RocketComponentIterator(RocketComponent c, boolean returnSelf) {
1790                         
1791                         RocketComponent gp = c.getRoot();
1792                         if (gp instanceof Rocket) {
1793                                 root = (Rocket) gp;
1794                                 treeModID = root.getTreeModID();
1795                         } else {
1796                                 root = null;
1797                                 treeModID = -1;
1798                         }
1799                         
1800                         Iterator<RocketComponent> i = c.children.iterator();
1801                         if (i.hasNext())
1802                                 iteratorStack.push(i);
1803                         
1804                         this.original = c;
1805                         this.returnSelf = returnSelf;
1806                 }
1807                 
1808                 @Override
1809                 public boolean hasNext() {
1810                         checkID();
1811                         if (returnSelf)
1812                                 return true;
1813                         return !iteratorStack.isEmpty(); // Elements remain if stack is not empty
1814                 }
1815                 
1816                 @Override
1817                 public RocketComponent next() {
1818                         Iterator<RocketComponent> i;
1819                         
1820                         checkID();
1821                         
1822                         // Return original component first
1823                         if (returnSelf) {
1824                                 returnSelf = false;
1825                                 return original;
1826                         }
1827                         
1828                         // Peek first iterator from stack, throw exception if empty
1829                         i = iteratorStack.peek();
1830                         if (i == null) {
1831                                 throw new NoSuchElementException("No further elements in RocketComponent iterator");
1832                         }
1833                         
1834                         // Retrieve next component of the iterator, remove iterator from stack if empty
1835                         RocketComponent c = i.next();
1836                         if (!i.hasNext())
1837                                 iteratorStack.pop();
1838                         
1839                         // Add iterator of component children to stack if it has children
1840                         i = c.children.iterator();
1841                         if (i.hasNext())
1842                                 iteratorStack.push(i);
1843                         
1844                         return c;
1845                 }
1846                 
1847                 private void checkID() {
1848                         if (root != null) {
1849                                 if (root.getTreeModID() != treeModID) {
1850                                         throw new IllegalStateException("Rocket modified while being iterated");
1851                                 }
1852                         }
1853                 }
1854                 
1855                 @Override
1856                 public void remove() {
1857                         throw new UnsupportedOperationException("remove() not supported by " +
1858                                         "RocketComponent iterator");
1859                 }
1860         }
1861         
1862 }