I18 changes
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / Rocket.java
index 9ab110b49c75996e2a5efb337638d980a0cf43ef..0be57cd16b6dcb88b3274d5f3ddd50a0937f010e 100644 (file)
@@ -1,6 +1,5 @@
 package net.sf.openrocket.rocketcomponent;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -12,10 +11,16 @@ import java.util.UUID;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.EventListenerList;
 
+import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.logging.LogHelper;
 import net.sf.openrocket.motor.Motor;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.util.ArrayList;
 import net.sf.openrocket.util.Chars;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.UniqueID;
 
 
 /**
@@ -29,17 +34,11 @@ import net.sf.openrocket.util.MathUtil;
  */
 
 public class Rocket extends RocketComponent {
+       private static final LogHelper log = Application.getLogger();
+       private static final Translator trans = Application.getTranslator();
+       
        public static final double DEFAULT_REFERENCE_LENGTH = 0.01;
        
-       private static final boolean DEBUG_LISTENERS = false;
-
-       
-       /**
-        * The next modification ID to use.  This variable may only be accessed via
-        * the synchronized {@link #getNextModID()} method!
-        */
-       private static int nextModID = 1;
-
 
        /**
         * List of component change listeners.
@@ -52,26 +51,26 @@ public class Rocket extends RocketComponent {
         */
        private List<ComponentChangeEvent> freezeList = null;
        
-       
+
        private int modID;
        private int massModID;
        private int aeroModID;
        private int treeModID;
        private int functionalModID;
        
-       
-       private ReferenceType refType = ReferenceType.MAXIMUM;  // Set in constructor
+
+       private ReferenceType refType = ReferenceType.MAXIMUM; // Set in constructor
        private double customReferenceLength = DEFAULT_REFERENCE_LENGTH;
        
-       
+
        // The default configuration used in dialogs
        private final Configuration defaultConfiguration;
        
-       
+
        private String designer = "";
        private String revision = "";
        
-       
+
        // Motor configuration list
        private ArrayList<String> motorConfigurationIDs = new ArrayList<String>();
        private HashMap<String, String> motorConfigurationNames = new HashMap<String, String>();
@@ -79,17 +78,17 @@ public class Rocket extends RocketComponent {
                motorConfigurationIDs.add(null);
        }
        
-       
+
        // Does the rocket have a perfect finish (a notable amount of laminar flow)
        private boolean perfectFinish = false;
        
        
-       
+
        /////////////  Constructor  /////////////
        
        public Rocket() {
                super(RocketComponent.Position.AFTER);
-               modID = getNextModID();
+               modID = UniqueID.next();
                massModID = modID;
                aeroModID = modID;
                treeModID = modID;
@@ -98,8 +97,9 @@ public class Rocket extends RocketComponent {
        }
        
        
-       
+
        public String getDesigner() {
+               checkState();
                return designer;
        }
        
@@ -110,8 +110,9 @@ public class Rocket extends RocketComponent {
                fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
        
-
+       
        public String getRevision() {
+               checkState();
                return revision;
        }
        
@@ -123,7 +124,7 @@ public class Rocket extends RocketComponent {
        }
        
        
-       
+
 
        /**
         * Return the number of stages in this rocket.
@@ -131,11 +132,11 @@ public class Rocket extends RocketComponent {
         * @return   the number of stages in this rocket.
         */
        public int getStageCount() {
+               checkState();
                return this.getChildCount();
        }
        
        
-       
        /**
         * Return the non-negative modification ID of this rocket.  The ID is changed
         * every time any change occurs in the rocket.  This can be used to check 
@@ -199,9 +200,10 @@ public class Rocket extends RocketComponent {
        }
        
        
-       
-       
+
+
        public ReferenceType getReferenceType() {
+               checkState();
                return refType;
        }
        
@@ -214,6 +216,7 @@ public class Rocket extends RocketComponent {
        
        
        public double getCustomReferenceLength() {
+               checkState();
                return customReferenceLength;
        }
        
@@ -221,7 +224,7 @@ public class Rocket extends RocketComponent {
                if (MathUtil.equals(customReferenceLength, length))
                        return;
                
-               this.customReferenceLength = Math.max(length,0.001);
+               this.customReferenceLength = Math.max(length, 0.001);
                
                if (refType == ReferenceType.CUSTOM) {
                        fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
@@ -229,9 +232,9 @@ public class Rocket extends RocketComponent {
        }
        
        
-       
-       
-       
+
+
+
        /**
         * Set whether the rocket has a perfect finish.  This will affect whether the
         * boundary layer is assumed to be fully turbulent or not.
@@ -244,8 +247,8 @@ public class Rocket extends RocketComponent {
                this.perfectFinish = perfectFinish;
                fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
        }
-
-
+       
+       
 
        /**
         * Get whether the rocket has a perfect finish.
@@ -255,33 +258,22 @@ public class Rocket extends RocketComponent {
        public boolean isPerfectFinish() {
                return perfectFinish;
        }
+       
+       
 
 
 
        /**
-        * Return a new unique modification ID.  This method is thread-safe.
-        * 
-        * @return  a new modification ID unique to this session.
-        */
-       private synchronized int getNextModID() {
-               return nextModID++;
-       }
-       
-
-       
-       
-       
-       /**
-        * Make a deep copy of the Rocket structure.  This is a helper method which simply 
-        * casts the result of the superclass method to a Rocket.
+        * Make a deep copy of the Rocket structure.  This method is exposed as public to allow
+        * for undo/redo system functionality.
         */
        @SuppressWarnings("unchecked")
        @Override
-       public Rocket copy() {
-               Rocket copy = (Rocket)super.copy();
-               copy.motorConfigurationIDs = (ArrayList<String>) this.motorConfigurationIDs.clone();
-               copy.motorConfigurationNames = 
-                       (HashMap<String, String>) this.motorConfigurationNames.clone();
+       public Rocket copyWithOriginalID() {
+               Rocket copy = (Rocket) super.copyWithOriginalID();
+               copy.motorConfigurationIDs = this.motorConfigurationIDs.clone();
+               copy.motorConfigurationNames =
+                               (HashMap<String, String>) this.motorConfigurationNames.clone();
                copy.resetListeners();
                
                return copy;
@@ -299,15 +291,17 @@ public class Rocket extends RocketComponent {
         */
        @SuppressWarnings("unchecked")
        public void loadFrom(Rocket r) {
-               super.copyFrom(r);
+               
+               // Store list of components to invalidate after event has been fired
+               List<RocketComponent> toInvalidate = this.copyFrom(r);
                
                int type = ComponentChangeEvent.UNDO_CHANGE | ComponentChangeEvent.NONFUNCTIONAL_CHANGE;
                if (this.massModID != r.massModID)
                        type |= ComponentChangeEvent.MASS_CHANGE;
                if (this.aeroModID != r.aeroModID)
                        type |= ComponentChangeEvent.AERODYNAMIC_CHANGE;
-               if (this.treeModID != r.treeModID)
-                       type |= ComponentChangeEvent.TREE_CHANGE;
+               // Loading a rocket is always a tree change since the component objects change
+               type |= ComponentChangeEvent.TREE_CHANGE;
                
                this.modID = r.modID;
                this.massModID = r.massModID;
@@ -317,21 +311,28 @@ public class Rocket extends RocketComponent {
                this.refType = r.refType;
                this.customReferenceLength = r.customReferenceLength;
                
-               this.motorConfigurationIDs = (ArrayList<String>) r.motorConfigurationIDs.clone();
-               this.motorConfigurationNames = 
-                       (HashMap<String, String>) r.motorConfigurationNames.clone();
+               this.motorConfigurationIDs = r.motorConfigurationIDs.clone();
+               this.motorConfigurationNames =
+                               (HashMap<String, String>) r.motorConfigurationNames.clone();
                this.perfectFinish = r.perfectFinish;
                
                String id = defaultConfiguration.getMotorConfigurationID();
                if (!this.motorConfigurationIDs.contains(id))
                        defaultConfiguration.setMotorConfigurationID(null);
                
+               this.checkComponentStructure();
+               
                fireComponentChangeEvent(type);
+               
+               // Invalidate obsolete components after event
+               for (RocketComponent c : toInvalidate) {
+                       c.invalidate();
+               }
        }
-
-       
        
        
+
+
        ///////  Implement the ComponentChangeListener lists
        
        /**
@@ -339,93 +340,99 @@ public class Rocket extends RocketComponent {
         * the structure.
         */
        public void resetListeners() {
-//             System.out.println("RESETTING LISTENER LIST of Rocket "+this);
+               //              System.out.println("RESETTING LISTENER LIST of Rocket "+this);
                listenerList = new EventListenerList();
        }
        
        
        public void printListeners() {
-               System.out.println(""+this+" has "+listenerList.getListenerCount()+" listeners:");
+               System.out.println("" + this + " has " + listenerList.getListenerCount() + " listeners:");
                Object[] list = listenerList.getListenerList();
-               for (int i=1; i<list.length; i+=2)
-                       System.out.println("  "+((i+1)/2)+": "+list[i]);
+               for (int i = 1; i < list.length; i += 2)
+                       System.out.println("  " + ((i + 1) / 2) + ": " + list[i]);
        }
        
        @Override
        public void addComponentChangeListener(ComponentChangeListener l) {
-               listenerList.add(ComponentChangeListener.class,l);
-               if (DEBUG_LISTENERS)
-                       System.out.println(this+": Added listner (now "+listenerList.getListenerCount()+
-                                       " listeners): "+l);
+               checkState();
+               listenerList.add(ComponentChangeListener.class, l);
+               log.verbose("Added ComponentChangeListener " + l + ", current number of listeners is " +
+                               listenerList.getListenerCount());
        }
+       
        @Override
        public void removeComponentChangeListener(ComponentChangeListener l) {
                listenerList.remove(ComponentChangeListener.class, l);
-               if (DEBUG_LISTENERS)
-                       System.out.println(this+": Removed listner (now "+listenerList.getListenerCount()+
-                                       " listeners): "+l);
+               log.verbose("Removed ComponentChangeListener " + l + ", current number of listeners is " +
+                               listenerList.getListenerCount());
        }
        
-
+       
        @Override
        public void addChangeListener(ChangeListener l) {
-               listenerList.add(ChangeListener.class,l);
-               if (DEBUG_LISTENERS)
-                       System.out.println(this+": Added listner (now "+listenerList.getListenerCount()+
-                                       " listeners): "+l);
+               checkState();
+               listenerList.add(ChangeListener.class, l);
+               log.verbose("Added ChangeListener " + l + ", current number of listeners is " +
+                               listenerList.getListenerCount());
        }
+       
        @Override
        public void removeChangeListener(ChangeListener l) {
                listenerList.remove(ChangeListener.class, l);
-               if (DEBUG_LISTENERS)
-                       System.out.println(this+": Removed listner (now "+listenerList.getListenerCount()+
-                                       " listeners): "+l);
+               log.verbose("Removed ChangeListener " + l + ", current number of listeners is " +
+                               listenerList.getListenerCount());
        }
-
+       
        
        @Override
        protected void fireComponentChangeEvent(ComponentChangeEvent e) {
-
-               // Update modification ID's only for normal (not undo/redo) events
-               if (!e.isUndoChange()) {
-                       modID = getNextModID();
-                       if (e.isMassChange())
-                               massModID = modID;
-                       if (e.isAerodynamicChange())
-                               aeroModID = modID;
-                       if (e.isTreeChange())
-                               treeModID = modID;
-                       if (e.getType() != ComponentChangeEvent.NONFUNCTIONAL_CHANGE)
-                               functionalModID = modID;
-               }
-               
-               if (DEBUG_LISTENERS)
-                       System.out.println("FIRING "+e);
-               
-               // Check whether frozen
-               if (freezeList != null) {
-                       freezeList.add(e);
-                       return;
-               }
-               
-               // Notify all components first
-               Iterator<RocketComponent> iterator = this.deepIterator(true);
-               while (iterator.hasNext()) {
-                       iterator.next().componentChanged(e);
-               }
-
-               // Notify all listeners
-               Object[] listeners = listenerList.getListenerList();
-               for (int i = listeners.length-2; i>=0; i-=2) {
-                       if (listeners[i] == ComponentChangeListener.class) {
-                               ((ComponentChangeListener) listeners[i+1]).componentChanged(e);
-                       } else if (listeners[i] == ChangeListener.class) {
-                               ((ChangeListener) listeners[i+1]).stateChanged(e);
+               mutex.lock("fireComponentChangeEvent");
+               try {
+                       checkState();
+                       
+                       // Update modification ID's only for normal (not undo/redo) events
+                       if (!e.isUndoChange()) {
+                               modID = UniqueID.next();
+                               if (e.isMassChange())
+                                       massModID = modID;
+                               if (e.isAerodynamicChange())
+                                       aeroModID = modID;
+                               if (e.isTreeChange())
+                                       treeModID = modID;
+                               if (e.getType() != ComponentChangeEvent.NONFUNCTIONAL_CHANGE)
+                                       functionalModID = modID;
+                       }
+                       
+                       // Check whether frozen
+                       if (freezeList != null) {
+                               log.debug("Rocket is in frozen state, adding event " + e + " info freeze list");
+                               freezeList.add(e);
+                               return;
                        }
+                       
+                       log.debug("Firing rocket change event " + e);
+                       
+                       // Notify all components first
+                       Iterator<RocketComponent> iterator = this.iterator(true);
+                       while (iterator.hasNext()) {
+                               iterator.next().componentChanged(e);
+                       }
+                       
+                       // Notify all listeners
+                       Object[] listeners = listenerList.getListenerList();
+                       for (int i = listeners.length - 2; i >= 0; i -= 2) {
+                               if (listeners[i] == ComponentChangeListener.class) {
+                                       ((ComponentChangeListener) listeners[i + 1]).componentChanged(e);
+                               } else if (listeners[i] == ChangeListener.class) {
+                                       ((ChangeListener) listeners[i + 1]).stateChanged(e);
+                               }
+                       }
+               } finally {
+                       mutex.unlock("fireComponentChangeEvent");
                }
        }
        
-               
+       
        /**
         * Freezes the rocket structure from firing any events.  This may be performed to
         * combine several actions on the structure into a single large action.
@@ -445,8 +452,14 @@ public class Rocket extends RocketComponent {
         * @see #thaw()
         */
        public void freeze() {
-               if (freezeList == null)
+               checkState();
+               if (freezeList == null) {
                        freezeList = new LinkedList<ComponentChangeEvent>();
+                       log.debug("Freezing Rocket");
+               } else {
+                       ExceptionHandler.handleErrorCondition("Attempting to freeze Rocket when it is already frozen, " +
+                                       "freezeList=" + freezeList);
+               }
        }
        
        /**
@@ -457,30 +470,36 @@ public class Rocket extends RocketComponent {
         * @see #freeze()
         */
        public void thaw() {
-               if (freezeList == null)
+               checkState();
+               if (freezeList == null) {
+                       ExceptionHandler.handleErrorCondition("Attempting to thaw Rocket when it is not frozen");
                        return;
-               if (freezeList.size()==0) {
+               }
+               if (freezeList.size() == 0) {
+                       log.warn("Thawing rocket with no changes made");
                        freezeList = null;
                        return;
                }
                
+               log.debug("Thawing rocket, freezeList=" + freezeList);
+               
                int type = 0;
                Object c = null;
-               for (ComponentChangeEvent e: freezeList) {
+               for (ComponentChangeEvent e : freezeList) {
                        type = type | e.getType();
                        c = e.getSource();
                }
                freezeList = null;
                
-               fireComponentChangeEvent(new ComponentChangeEvent((RocketComponent)c,type));
+               fireComponentChangeEvent(new ComponentChangeEvent((RocketComponent) c, type));
        }
        
        
 
-       
+
        ////////  Motor configurations  ////////
        
-       
+
        /**
         * Return the default configuration.  This should be used in the user interface
         * to ensure a consistent rocket configuration between dialogs.  It should NOT
@@ -489,6 +508,7 @@ public class Rocket extends RocketComponent {
         * @return   the default {@link Configuration}.
         */
        public Configuration getDefaultConfiguration() {
+               checkState();
                return defaultConfiguration;
        }
        
@@ -500,6 +520,7 @@ public class Rocket extends RocketComponent {
         * @return  an array of the motor configuration IDs.
         */
        public String[] getMotorConfigurationIDs() {
+               checkState();
                return motorConfigurationIDs.toArray(new String[0]);
        }
        
@@ -510,6 +531,7 @@ public class Rocket extends RocketComponent {
         * @return  the new motor configuration ID.
         */
        public String newMotorConfigurationID() {
+               checkState();
                String id = UUID.randomUUID().toString();
                motorConfigurationIDs.add(id);
                fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
@@ -523,13 +545,14 @@ public class Rocket extends RocketComponent {
         * @return              true if successful, false if the ID was already used.
         */
        public boolean addMotorConfigurationID(String id) {
+               checkState();
                if (id == null || motorConfigurationIDs.contains(id))
                        return false;
                motorConfigurationIDs.add(id);
                fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
                return true;
        }
-
+       
        /**
         * Remove a motor configuration ID from the configuration IDs.  The <code>null</code>
         * ID cannot be removed, and an attempt to remove it will be silently ignored.
@@ -537,12 +560,13 @@ public class Rocket extends RocketComponent {
         * @param id   the motor configuration ID to remove
         */
        public void removeMotorConfigurationID(String id) {
+               checkState();
                if (id == null)
                        return;
                motorConfigurationIDs.remove(id);
                fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
        }
-
+       
        
        /**
         * Check whether <code>id</code> is a valid motor configuration ID.
@@ -551,11 +575,12 @@ public class Rocket extends RocketComponent {
         * @return              whether a motor configuration with that ID exists.
         */
        public boolean isMotorConfigurationID(String id) {
+               checkState();
                return motorConfigurationIDs.contains(id);
        }
        
        
-       
+
        /**
         * Check whether the given motor configuration ID has motors defined for it.
         * 
@@ -563,13 +588,14 @@ public class Rocket extends RocketComponent {
         * @return              whether any motors are defined for it.
         */
        public boolean hasMotors(String id) {
+               checkState();
                if (id == null)
                        return false;
                
-               Iterator<RocketComponent> iterator = this.deepIterator();
+               Iterator<RocketComponent> iterator = this.iterator();
                while (iterator.hasNext()) {
                        RocketComponent c = iterator.next();
-
+                       
                        if (c instanceof MotorMount) {
                                MotorMount mount = (MotorMount) c;
                                if (!mount.isMotorMount())
@@ -591,6 +617,7 @@ public class Rocket extends RocketComponent {
         * @return         the configuration name
         */
        public String getMotorConfigurationName(String id) {
+               checkState();
                if (!isMotorConfigurationID(id))
                        return "";
                String s = motorConfigurationNames.get(id);
@@ -608,7 +635,8 @@ public class Rocket extends RocketComponent {
         * @param name  the name for the motor configuration
         */
        public void setMotorConfigurationName(String id, String name) {
-               motorConfigurationNames.put(id,name);
+               checkState();
+               motorConfigurationNames.put(id, name);
                fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
        
@@ -620,12 +648,13 @@ public class Rocket extends RocketComponent {
         * @return    a textual representation of the configuration
         */
        public String getMotorConfigurationNameOrDescription(String id) {
+               checkState();
                String name;
                
                name = getMotorConfigurationName(id);
-               if (name != null  &&  !name.equals(""))
+               if (name != null && !name.equals(""))
                        return name;
-
+               
                return getMotorConfigurationDescription(id);
        }
        
@@ -638,6 +667,7 @@ public class Rocket extends RocketComponent {
         */
        @SuppressWarnings("null")
        public String getMotorConfigurationDescription(String id) {
+               checkState();
                String name;
                int motorCount = 0;
                
@@ -647,7 +677,7 @@ public class Rocket extends RocketComponent {
                List<List<String>> list = new ArrayList<List<String>>();
                List<String> currentList = null;
                
-               Iterator<RocketComponent> iterator = this.deepIterator();
+               Iterator<RocketComponent> iterator = this.iterator();
                while (iterator.hasNext()) {
                        RocketComponent c = iterator.next();
                        
@@ -664,7 +694,7 @@ public class Rocket extends RocketComponent {
                                if (mount.isMotorMount() && motor != null) {
                                        String designation = motor.getDesignation(mount.getMotorDelay(id));
                                        
-                                       for (int i=0; i < mount.getMotorCount(); i++) {
+                                       for (int i = 0; i < mount.getMotorCount(); i++) {
                                                currentList.add(designation);
                                                motorCount++;
                                        }
@@ -674,19 +704,20 @@ public class Rocket extends RocketComponent {
                }
                
                if (motorCount == 0) {
-                       return "[No motors]";
+                       //// [No motors]
+                       return trans.get("Rocket.motorCount.Nomotor");
                }
                
                // Change multiple occurrences of a motor to n x motor 
                List<String> stages = new ArrayList<String>();
                
-               for (List<String> stage: list) {
+               for (List<String> stage : list) {
                        String stageName = "";
                        String previous = null;
                        int count = 0;
                        
                        Collections.sort(stage);
-                       for (String current: stage) {
+                       for (String current : stage) {
                                if (current.equals(previous)) {
                                        
                                        count++;
@@ -730,11 +761,11 @@ public class Rocket extends RocketComponent {
                }
                
                name = "[";
-               for (int i=0; i < stages.size(); i++) {
+               for (int i = 0; i < stages.size(); i++) {
                        String s = stages.get(i);
                        if (s.equals(""))
                                s = "None";
-                       if (i==0)
+                       if (i == 0)
                                name = name + s;
                        else
                                name = name + "; " + s;
@@ -743,31 +774,32 @@ public class Rocket extends RocketComponent {
                return name;
        }
        
-
+       
 
        ////////  Obligatory component information
        
-       
+
        @Override
        public String getComponentName() {
-               return "Rocket";
+               //// Rocket
+               return trans.get("Rocket.compname.Rocket");
        }
-
+       
        @Override
        public Coordinate getComponentCG() {
-               return new Coordinate(0,0,0,0);
+               return new Coordinate(0, 0, 0, 0);
        }
-
+       
        @Override
        public double getComponentMass() {
                return 0;
        }
-
+       
        @Override
-       public double getLongitudalUnitInertia() {
+       public double getLongitudinalUnitInertia() {
                return 0;
        }
-
+       
        @Override
        public double getRotationalUnitInertia() {
                return 0;
@@ -777,17 +809,22 @@ public class Rocket extends RocketComponent {
        public Collection<Coordinate> getComponentBounds() {
                return Collections.emptyList();
        }
-
+       
        @Override
        public boolean isAerodynamic() {
                return false;
        }
-
+       
        @Override
        public boolean isMassive() {
                return false;
        }
-
+       
+       @Override
+       public boolean allowsChildren() {
+               return true;
+       }
+       
        /**
         * Allows only <code>Stage</code> components to be added to the type Rocket.
         */
@@ -795,4 +832,14 @@ public class Rocket extends RocketComponent {
        public boolean isCompatible(Class<? extends RocketComponent> type) {
                return (Stage.class.isAssignableFrom(type));
        }
+       
+       /**
+        * Accept a visitor to this Rocket in the component hierarchy.
+        * 
+        * @param theVisitor  the visitor that will be called back with a reference to this Rocket
+        */
+       @Override
+       public void accept(final ComponentVisitor theVisitor) {
+               theVisitor.visit(this);
+       }
 }