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