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