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