create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / rocketcomponent / Rocket.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import java.util.Collection;
4 import java.util.Collections;
5 import java.util.EventListener;
6 import java.util.HashMap;
7 import java.util.Iterator;
8 import java.util.LinkedList;
9 import java.util.List;
10 import java.util.UUID;
11
12 import net.sf.openrocket.l10n.Translator;
13 import net.sf.openrocket.logging.LogHelper;
14 import net.sf.openrocket.motor.Motor;
15 import net.sf.openrocket.startup.Application;
16 import net.sf.openrocket.util.ArrayList;
17 import net.sf.openrocket.util.Chars;
18 import net.sf.openrocket.util.Coordinate;
19 import net.sf.openrocket.util.MathUtil;
20 import net.sf.openrocket.util.StateChangeListener;
21 import net.sf.openrocket.util.UniqueID;
22
23
24 /**
25  * Base for all rocket components.  This is the "starting point" for all rocket trees.
26  * It provides the actual implementations of several methods defined in RocketComponent
27  * (eg. the rocket listener lists) and the methods defined in RocketComponent call these.
28  * It also defines some other methods that concern the whole rocket, and helper methods
29  * that keep information about the program state.
30  *
31  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
32  */
33
34 public class Rocket extends RocketComponent {
35         private static final LogHelper log = Application.getLogger();
36         private static final Translator trans = Application.getTranslator();
37         
38         public static final double DEFAULT_REFERENCE_LENGTH = 0.01;
39         
40
41         /**
42          * List of component change listeners.
43          */
44         private List<EventListener> listenerList = new ArrayList<EventListener>();
45                 
46         /**
47          * When freezeList != null, events are not dispatched but stored in the list.
48          * When the structure is thawed, a single combined event will be fired.
49          */
50         private List<ComponentChangeEvent> freezeList = null;
51         
52
53         private int modID;
54         private int massModID;
55         private int aeroModID;
56         private int treeModID;
57         private int functionalModID;
58         
59
60         private ReferenceType refType = ReferenceType.MAXIMUM; // Set in constructor
61         private double customReferenceLength = DEFAULT_REFERENCE_LENGTH;
62         
63
64         // The default configuration used in dialogs
65         private final Configuration defaultConfiguration;
66         
67
68         private String designer = "";
69         private String revision = "";
70         
71
72         // Motor configuration list
73         private ArrayList<String> motorConfigurationIDs = new ArrayList<String>();
74         private HashMap<String, String> motorConfigurationNames = new HashMap<String, String>();
75         {
76                 motorConfigurationIDs.add(null);
77         }
78         
79
80         // Does the rocket have a perfect finish (a notable amount of laminar flow)
81         private boolean perfectFinish = false;
82         
83         
84
85         /////////////  Constructor  /////////////
86         
87         public Rocket() {
88                 super(RocketComponent.Position.AFTER);
89                 modID = UniqueID.next();
90                 massModID = modID;
91                 aeroModID = modID;
92                 treeModID = modID;
93                 functionalModID = modID;
94                 defaultConfiguration = new Configuration(this);
95         }
96         
97         
98
99         public String getDesigner() {
100                 checkState();
101                 return designer;
102         }
103         
104         public void setDesigner(String s) {
105                 if (s == null)
106                         s = "";
107                 designer = s;
108                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
109         }
110         
111         
112         public String getRevision() {
113                 checkState();
114                 return revision;
115         }
116         
117         public void setRevision(String s) {
118                 if (s == null)
119                         s = "";
120                 revision = s;
121                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
122         }
123         
124         /**
125          * Return the number of stages in this rocket.
126          *
127          * @return   the number of stages in this rocket.
128          */
129         public int getStageCount() {
130                 checkState();
131                 return this.getChildCount();
132         }
133         
134         
135         /**
136          * Return the non-negative modification ID of this rocket.  The ID is changed
137          * every time any change occurs in the rocket.  This can be used to check
138          * whether it is necessary to void cached data in cases where listeners can not
139          * or should not be used.
140          * <p>
141          * Three other modification IDs are also available, {@link #getMassModID()},
142          * {@link #getAerodynamicModID()} {@link #getTreeModID()}, which change every time
143          * a mass change, aerodynamic change, or tree change occur.  Even though the values
144          * of the different modification ID's may be equal, they should be treated totally
145          * separate.
146          * <p>
147          * Note that undo events restore the modification IDs that were in use at the
148          * corresponding undo level.  Subsequent modifications, however, produce modIDs
149          * distinct from those already used.
150          *
151          * @return   a unique ID number for this modification state.
152          */
153         public int getModID() {
154                 return modID;
155         }
156         
157         /**
158          * Return the non-negative mass modification ID of this rocket.  See
159          * {@link #getModID()} for details.
160          *
161          * @return   a unique ID number for this mass-modification state.
162          */
163         public int getMassModID() {
164                 return massModID;
165         }
166         
167         /**
168          * Return the non-negative aerodynamic modification ID of this rocket.  See
169          * {@link #getModID()} for details.
170          *
171          * @return   a unique ID number for this aerodynamic-modification state.
172          */
173         public int getAerodynamicModID() {
174                 return aeroModID;
175         }
176         
177         /**
178          * Return the non-negative tree modification ID of this rocket.  See
179          * {@link #getModID()} for details.
180          *
181          * @return   a unique ID number for this tree-modification state.
182          */
183         public int getTreeModID() {
184                 return treeModID;
185         }
186         
187         /**
188          * Return the non-negative functional modificationID of this rocket.
189          * This changes every time a functional change occurs.
190          *
191          * @return      a unique ID number for this functional modification state.
192          */
193         public int getFunctionalModID() {
194                 return functionalModID;
195         }
196         
197         
198
199
200         public ReferenceType getReferenceType() {
201                 checkState();
202                 return refType;
203         }
204         
205         public void setReferenceType(ReferenceType type) {
206                 if (refType == type)
207                         return;
208                 refType = type;
209                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
210         }
211         
212         
213         public double getCustomReferenceLength() {
214                 checkState();
215                 return customReferenceLength;
216         }
217         
218         public void setCustomReferenceLength(double length) {
219                 if (MathUtil.equals(customReferenceLength, length))
220                         return;
221                 
222                 this.customReferenceLength = Math.max(length, 0.001);
223                 
224                 if (refType == ReferenceType.CUSTOM) {
225                         fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
226                 }
227         }
228         
229         
230
231
232
233         /**
234          * Set whether the rocket has a perfect finish.  This will affect whether the
235          * boundary layer is assumed to be fully turbulent or not.
236          *
237          * @param perfectFinish         whether the finish is perfect.
238          */
239         public void setPerfectFinish(boolean perfectFinish) {
240                 if (this.perfectFinish == perfectFinish)
241                         return;
242                 this.perfectFinish = perfectFinish;
243                 fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
244         }
245         
246         
247
248         /**
249          * Get whether the rocket has a perfect finish.
250          *
251          * @return the perfectFinish
252          */
253         public boolean isPerfectFinish() {
254                 return perfectFinish;
255         }
256         
257         
258
259
260
261         /**
262          * Make a deep copy of the Rocket structure.  This method is exposed as public to allow
263          * for undo/redo system functionality.
264          */
265         @SuppressWarnings("unchecked")
266         @Override
267         public Rocket copyWithOriginalID() {
268                 Rocket copy = (Rocket) super.copyWithOriginalID();
269                 copy.motorConfigurationIDs = this.motorConfigurationIDs.clone();
270                 copy.motorConfigurationNames =
271                                 (HashMap<String, String>) this.motorConfigurationNames.clone();
272                 copy.resetListeners();
273                 
274                 return copy;
275         }
276         
277         /**
278          * Load the rocket structure from the source.  The method loads the fields of this
279          * Rocket object and copies the references to siblings from the <code>source</code>.
280          * The object <code>source</code> should not be used after this call, as it is in
281          * an illegal state!
282          * <p>
283          * This method is meant to be used in conjunction with undo/redo functionality,
284          * and therefore fires an UNDO_EVENT, masked with all applicable mass/aerodynamic/tree
285          * changes.
286          */
287         @SuppressWarnings("unchecked")
288         public void loadFrom(Rocket r) {
289                 
290                 // Store list of components to invalidate after event has been fired
291                 List<RocketComponent> toInvalidate = this.copyFrom(r);
292                 
293                 int type = ComponentChangeEvent.UNDO_CHANGE | ComponentChangeEvent.NONFUNCTIONAL_CHANGE;
294                 if (this.massModID != r.massModID)
295                         type |= ComponentChangeEvent.MASS_CHANGE;
296                 if (this.aeroModID != r.aeroModID)
297                         type |= ComponentChangeEvent.AERODYNAMIC_CHANGE;
298                 // Loading a rocket is always a tree change since the component objects change
299                 type |= ComponentChangeEvent.TREE_CHANGE;
300                 
301                 this.modID = r.modID;
302                 this.massModID = r.massModID;
303                 this.aeroModID = r.aeroModID;
304                 this.treeModID = r.treeModID;
305                 this.functionalModID = r.functionalModID;
306                 this.refType = r.refType;
307                 this.customReferenceLength = r.customReferenceLength;
308                 
309                 this.motorConfigurationIDs = r.motorConfigurationIDs.clone();
310                 this.motorConfigurationNames =
311                                 (HashMap<String, String>) r.motorConfigurationNames.clone();
312                 this.perfectFinish = r.perfectFinish;
313                 
314                 String id = defaultConfiguration.getMotorConfigurationID();
315                 if (!this.motorConfigurationIDs.contains(id))
316                         defaultConfiguration.setMotorConfigurationID(null);
317                 
318                 this.checkComponentStructure();
319                 
320                 fireComponentChangeEvent(type);
321                 
322                 // Invalidate obsolete components after event
323                 for (RocketComponent c : toInvalidate) {
324                         c.invalidate();
325                 }
326         }
327         
328         
329
330
331         ///////  Implement the ComponentChangeListener lists
332         
333         /**
334          * Creates a new EventListenerList for this component.  This is necessary when cloning
335          * the structure.
336          */
337         public void resetListeners() {
338                 //              System.out.println("RESETTING LISTENER LIST of Rocket "+this);
339                 listenerList = new ArrayList<EventListener>();
340         }
341         
342         
343         public void printListeners() {
344                 System.out.println("" + this + " has " + listenerList.size() + " listeners:");
345                 int i =0;
346                 for( EventListener l : listenerList ) {
347                         System.out.println("  " + (i) + ": " + l);
348                         i++;
349                 }
350         }
351         
352         @Override
353         public void addComponentChangeListener(ComponentChangeListener l) {
354                 checkState();
355                 listenerList.add(l);
356                 log.verbose("Added ComponentChangeListener " + l + ", current number of listeners is " +
357                                 listenerList.size());
358         }
359         
360         @Override
361         public void removeComponentChangeListener(ComponentChangeListener l) {
362                 listenerList.remove(l);
363                 log.verbose("Removed ComponentChangeListener " + l + ", current number of listeners is " +
364                                 listenerList.size());
365         }
366         
367         
368         @Override
369         public void addChangeListener(EventListener l) {
370                 checkState();
371                 listenerList.add(l);
372                 log.verbose("Added ChangeListener " + l + ", current number of listeners is " +
373                                 listenerList.size());
374         }
375         
376         @Override
377         public void removeChangeListener(EventListener l) {
378                 listenerList.remove(l);
379                 log.verbose("Removed ChangeListener " + l + ", current number of listeners is " +
380                                 listenerList.size());
381         }
382         
383         
384         @Override
385         protected void fireComponentChangeEvent(ComponentChangeEvent e) {
386                 mutex.lock("fireComponentChangeEvent");
387                 try {
388                         checkState();
389                         
390                         // Update modification ID's only for normal (not undo/redo) events
391                         if (!e.isUndoChange()) {
392                                 modID = UniqueID.next();
393                                 if (e.isMassChange())
394                                         massModID = modID;
395                                 if (e.isAerodynamicChange())
396                                         aeroModID = modID;
397                                 if (e.isTreeChange())
398                                         treeModID = modID;
399                                 if (e.getType() != ComponentChangeEvent.NONFUNCTIONAL_CHANGE)
400                                         functionalModID = modID;
401                         }
402                         
403                         // Check whether frozen
404                         if (freezeList != null) {
405                                 log.debug("Rocket is in frozen state, adding event " + e + " info freeze list");
406                                 freezeList.add(e);
407                                 return;
408                         }
409                         
410                         log.debug("Firing rocket change event " + e);
411                         
412                         // Notify all components first
413                         Iterator<RocketComponent> iterator = this.iterator(true);
414                         while (iterator.hasNext()) {
415                                 iterator.next().componentChanged(e);
416                         }
417                         
418                         // Notify all listeners
419                         // Copy the list before iterating to prevent concurrent modification exceptions.
420                         EventListener[] list = listenerList.toArray( new EventListener[0] );
421                         for ( EventListener l : list ) {
422                                 if ( l instanceof ComponentChangeListener ) {
423                                         ((ComponentChangeListener) l ).componentChanged(e);
424                                 } else if ( l instanceof StateChangeListener ) {
425                                         ((StateChangeListener) l ).stateChanged(e);
426                                 }
427                         }
428                 } finally {
429                         mutex.unlock("fireComponentChangeEvent");
430                 }
431         }
432         
433         
434         /**
435          * Freezes the rocket structure from firing any events.  This may be performed to
436          * combine several actions on the structure into a single large action.
437          * <code>thaw()</code> must always be called afterwards.
438          *
439          * NOTE:  Always use a try/finally to ensure <code>thaw()</code> is called:
440          * <pre>
441          *     Rocket r = c.getRocket();
442          *     try {
443          *         r.freeze();
444          *         // do stuff
445          *     } finally {
446          *         r.thaw();
447          *     }
448          * </pre>
449          *
450          * @see #thaw()
451          */
452         public void freeze() {
453                 checkState();
454                 if (freezeList == null) {
455                         freezeList = new LinkedList<ComponentChangeEvent>();
456                         log.debug("Freezing Rocket");
457                 } else {
458                         Application.getExceptionHandler().handleErrorCondition("Attempting to freeze Rocket when it is already frozen, " +
459                                         "freezeList=" + freezeList);
460                 }
461         }
462         
463         /**
464          * Thaws a frozen rocket structure and fires a combination of the events fired during
465          * the freeze.  The event type is a combination of those fired and the source is the
466          * last component to have been an event source.
467          *
468          * @see #freeze()
469          */
470         public void thaw() {
471                 checkState();
472                 if (freezeList == null) {
473                         Application.getExceptionHandler().handleErrorCondition("Attempting to thaw Rocket when it is not frozen");
474                         return;
475                 }
476                 if (freezeList.size() == 0) {
477                         log.warn("Thawing rocket with no changes made");
478                         freezeList = null;
479                         return;
480                 }
481                 
482                 log.debug("Thawing rocket, freezeList=" + freezeList);
483                 
484                 int type = 0;
485                 Object c = null;
486                 for (ComponentChangeEvent e : freezeList) {
487                         type = type | e.getType();
488                         c = e.getSource();
489                 }
490                 freezeList = null;
491                 
492                 fireComponentChangeEvent(new ComponentChangeEvent((RocketComponent) c, type));
493         }
494         
495         
496
497
498         ////////  Motor configurations  ////////
499         
500
501         /**
502          * Return the default configuration.  This should be used in the user interface
503          * to ensure a consistent rocket configuration between dialogs.  It should NOT
504          * be used in simulations not relating to the UI.
505          *
506          * @return   the default {@link Configuration}.
507          */
508         public Configuration getDefaultConfiguration() {
509                 checkState();
510                 return defaultConfiguration;
511         }
512         
513         
514         /**
515          * Return an array of the motor configuration IDs.  This array is guaranteed
516          * to contain the <code>null</code> ID as the first element.
517          *
518          * @return  an array of the motor configuration IDs.
519          */
520         public String[] getMotorConfigurationIDs() {
521                 checkState();
522                 return motorConfigurationIDs.toArray(new String[0]);
523         }
524         
525         /**
526          * Add a new motor configuration ID to the motor configurations.  The new ID
527          * is returned.
528          *
529          * @return  the new motor configuration ID.
530          */
531         public String newMotorConfigurationID() {
532                 checkState();
533                 String id = UUID.randomUUID().toString();
534                 motorConfigurationIDs.add(id);
535                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
536                 return id;
537         }
538         
539         /**
540          * Add a specified motor configuration ID to the motor configurations.
541          *
542          * @param id    the motor configuration ID.
543          * @return              true if successful, false if the ID was already used.
544          */
545         public boolean addMotorConfigurationID(String id) {
546                 checkState();
547                 if (id == null || motorConfigurationIDs.contains(id))
548                         return false;
549                 motorConfigurationIDs.add(id);
550                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
551                 return true;
552         }
553         
554         /**
555          * Remove a motor configuration ID from the configuration IDs.  The <code>null</code>
556          * ID cannot be removed, and an attempt to remove it will be silently ignored.
557          *
558          * @param id   the motor configuration ID to remove
559          */
560         public void removeMotorConfigurationID(String id) {
561                 checkState();
562                 if (id == null)
563                         return;
564                 motorConfigurationIDs.remove(id);
565                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
566         }
567         
568         
569         /**
570          * Check whether <code>id</code> is a valid motor configuration ID.
571          *
572          * @param id    the configuration ID.
573          * @return              whether a motor configuration with that ID exists.
574          */
575         public boolean isMotorConfigurationID(String id) {
576                 checkState();
577                 return motorConfigurationIDs.contains(id);
578         }
579         
580         
581
582         /**
583          * Check whether the given motor configuration ID has motors defined for it.
584          *
585          * @param id    the motor configuration ID (may be invalid).
586          * @return              whether any motors are defined for it.
587          */
588         public boolean hasMotors(String id) {
589                 checkState();
590                 if (id == null)
591                         return false;
592                 
593                 Iterator<RocketComponent> iterator = this.iterator();
594                 while (iterator.hasNext()) {
595                         RocketComponent c = iterator.next();
596                         
597                         if (c instanceof MotorMount) {
598                                 MotorMount mount = (MotorMount) c;
599                                 if (!mount.isMotorMount())
600                                         continue;
601                                 if (mount.getMotor(id) != null) {
602                                         return true;
603                                 }
604                         }
605                 }
606                 return false;
607         }
608         
609         
610         /**
611          * Return the user-set name of the motor configuration.  If no name has been set,
612          * returns an empty string (not null).
613          *
614          * @param id   the motor configuration id
615          * @return         the configuration name
616          */
617         public String getMotorConfigurationName(String id) {
618                 checkState();
619                 if (!isMotorConfigurationID(id))
620                         return "";
621                 String s = motorConfigurationNames.get(id);
622                 if (s == null)
623                         return "";
624                 return s;
625         }
626         
627         
628         /**
629          * Set the name of the motor configuration.  A name can be unset by passing
630          * <code>null</code> or an empty string.
631          *
632          * @param id    the motor configuration id
633          * @param name  the name for the motor configuration
634          */
635         public void setMotorConfigurationName(String id, String name) {
636                 checkState();
637                 motorConfigurationNames.put(id, name);
638                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
639         }
640         
641         
642         /**
643          * Return either the motor configuration name (if set) or its description.
644          *
645          * @param id  the motor configuration ID.
646          * @return    a textual representation of the configuration
647          */
648         public String getMotorConfigurationNameOrDescription(String id) {
649                 checkState();
650                 String name;
651                 
652                 name = getMotorConfigurationName(id);
653                 if (name != null && !name.equals(""))
654                         return name;
655                 
656                 return getMotorConfigurationDescription(id);
657         }
658         
659         /**
660          * Return a description for the motor configuration, generated from the motor
661          * designations of the components.
662          *
663          * @param id  the motor configuration ID.
664          * @return    a textual representation of the configuration
665          */
666         @SuppressWarnings("null")
667         public String getMotorConfigurationDescription(String id) {
668                 checkState();
669                 String name;
670                 int motorCount = 0;
671                 
672                 // Generate the description
673                 
674                 // First iterate over each stage and store the designations of each motor
675                 List<List<String>> list = new ArrayList<List<String>>();
676                 List<String> currentList = null;
677                 
678                 Iterator<RocketComponent> iterator = this.iterator();
679                 while (iterator.hasNext()) {
680                         RocketComponent c = iterator.next();
681                         
682                         if (c instanceof Stage) {
683                                 
684                                 currentList = new ArrayList<String>();
685                                 list.add(currentList);
686                                 
687                         } else if (c instanceof MotorMount) {
688                                 
689                                 MotorMount mount = (MotorMount) c;
690                                 Motor motor = mount.getMotor(id);
691                                 
692                                 if (mount.isMotorMount() && motor != null) {
693                                         String designation = motor.getDesignation(mount.getMotorDelay(id));
694                                         
695                                         for (int i = 0; i < mount.getMotorCount(); i++) {
696                                                 currentList.add(designation);
697                                                 motorCount++;
698                                         }
699                                 }
700                                 
701                         }
702                 }
703                 
704                 if (motorCount == 0) {
705                         //// [No motors]
706                         return trans.get("Rocket.motorCount.Nomotor");
707                 }
708                 
709                 // Change multiple occurrences of a motor to n x motor
710                 List<String> stages = new ArrayList<String>();
711                 
712                 for (List<String> stage : list) {
713                         String stageName = "";
714                         String previous = null;
715                         int count = 0;
716                         
717                         Collections.sort(stage);
718                         for (String current : stage) {
719                                 if (current.equals(previous)) {
720                                         
721                                         count++;
722                                         
723                                 } else {
724                                         
725                                         if (previous != null) {
726                                                 String s = "";
727                                                 if (count > 1) {
728                                                         s = "" + count + Chars.TIMES + previous;
729                                                 } else {
730                                                         s = previous;
731                                                 }
732                                                 
733                                                 if (stageName.equals(""))
734                                                         stageName = s;
735                                                 else
736                                                         stageName = stageName + "," + s;
737                                         }
738                                         
739                                         previous = current;
740                                         count = 1;
741                                         
742                                 }
743                         }
744                         if (previous != null) {
745                                 String s = "";
746                                 if (count > 1) {
747                                         s = "" + count + Chars.TIMES + previous;
748                                 } else {
749                                         s = previous;
750                                 }
751                                 
752                                 if (stageName.equals(""))
753                                         stageName = s;
754                                 else
755                                         stageName = stageName + "," + s;
756                         }
757                         
758                         stages.add(stageName);
759                 }
760                 
761                 name = "[";
762                 for (int i = 0; i < stages.size(); i++) {
763                         String s = stages.get(i);
764                         if (s.equals(""))
765                                 s = "None";
766                         if (i == 0)
767                                 name = name + s;
768                         else
769                                 name = name + "; " + s;
770                 }
771                 name += "]";
772                 return name;
773         }
774         
775         
776
777         ////////  Obligatory component information
778         
779
780         @Override
781         public String getComponentName() {
782                 //// Rocket
783                 return trans.get("Rocket.compname.Rocket");
784         }
785         
786         @Override
787         public Coordinate getComponentCG() {
788                 return new Coordinate(0, 0, 0, 0);
789         }
790         
791         @Override
792         public double getComponentMass() {
793                 return 0;
794         }
795         
796         @Override
797         public double getLongitudinalUnitInertia() {
798                 return 0;
799         }
800         
801         @Override
802         public double getRotationalUnitInertia() {
803                 return 0;
804         }
805         
806         @Override
807         public Collection<Coordinate> getComponentBounds() {
808                 return Collections.emptyList();
809         }
810         
811         @Override
812         public boolean isAerodynamic() {
813                 return false;
814         }
815         
816         @Override
817         public boolean isMassive() {
818                 return false;
819         }
820         
821         @Override
822         public boolean allowsChildren() {
823                 return true;
824         }
825         
826         /**
827          * Allows only <code>Stage</code> components to be added to the type Rocket.
828          */
829         @Override
830         public boolean isCompatible(Class<? extends RocketComponent> type) {
831                 return (Stage.class.isAssignableFrom(type));
832         }
833         
834 }