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