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