bug fixes
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Thu, 25 Aug 2011 19:29:50 +0000 (19:29 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Thu, 25 Aug 2011 19:29:50 +0000 (19:29 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@160 180e2498-e6e9-4542-8430-84ac67f01cd8

17 files changed:
ChangeLog
src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java
src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java
src/net/sf/openrocket/gui/main/ExceptionHandler.java
src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
src/net/sf/openrocket/logging/TraceException.java
src/net/sf/openrocket/material/Material.java
src/net/sf/openrocket/preset/ComponentPreset.java [new file with mode: 0644]
src/net/sf/openrocket/preset/ExternalComponentPreset.java [deleted file]
src/net/sf/openrocket/preset/RocketComponentPreset.java [deleted file]
src/net/sf/openrocket/rocketcomponent/BodyComponent.java
src/net/sf/openrocket/rocketcomponent/BodyTube.java
src/net/sf/openrocket/rocketcomponent/ExternalComponent.java
src/net/sf/openrocket/rocketcomponent/RocketComponent.java
src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java
src/net/sf/openrocket/simulation/SimulationOptions.java
test/net/sf/openrocket/logging/TraceExceptionTest.java [new file with mode: 0644]

index 89e229f9a6a1e52fb7889dc48fffb896c890c660..5399602f15d473a2f3f2b7e2531ecccf5119321e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2011-08-25  Sampo Niskanen
+
+       * [BUG] Ignore synthetic methods in logging traces
+       * [BUG] Ignore JRE bug #6826104
+
+2011-08-24  Sampo Niskanen
+
+       * [BUG] NPE in SimulationOptions.equals
+       * [BUG] Exception in plotting optimization path
+       * [BUG] Exception in saving optimization path
+
 2011-08-17  Justin Seitz
 
        * Added Blue tube to materials database.
index 0ef35e2e7f242eac632fc9079dee62729805806b..1478a2b12847f6bf39f6d1251d444750363763d7 100644 (file)
@@ -606,8 +606,11 @@ public class GeneralOptimizationDialog extends JDialog {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                                log.user("Plotting optimization path, dimensionality=" + selectedModifiers.size());
-                               OptimizationPlotDialog dialog = new OptimizationPlotDialog(optimizationPath, evaluationHistory,
-                                               selectedModifiers, getSelectedParameter(),
+                               OptimizationPlotDialog dialog = new OptimizationPlotDialog(
+                                               Collections.unmodifiableList(optimizationPath),
+                                               Collections.unmodifiableMap(evaluationHistory),
+                                               Collections.unmodifiableList(selectedModifiers),
+                                               getSelectedParameter(),
                                                UnitGroup.stabilityUnits(getSelectedSimulation().getRocket()),
                                                GeneralOptimizationDialog.this);
                                dialog.setVisible(true);
@@ -803,6 +806,7 @@ public class GeneralOptimizationDialog extends JDialog {
                                });
                                timer.setRepeats(false);
                                timer.start();
+                               updateComponents();
                        }
                        
                        @Override
@@ -1125,16 +1129,21 @@ public class GeneralOptimizationDialog extends JDialog {
         * Update the enabled status of all components in the dialog.
         */
        private void updateComponents() {
+               boolean state;
                
                if (updating) {
+                       log.debug("Ignoring updateComponents");
                        return;
                }
                
+               log.debug("Running updateComponents()");
+               
                updating = true;
                
 
                // First enable all components if optimization not running
                if (!running) {
+                       log.debug("Initially enabling all components");
                        for (JComponent c : disableComponents) {
                                c.setEnabled(true);
                        }
@@ -1143,46 +1152,56 @@ public class GeneralOptimizationDialog extends JDialog {
 
                // "Add" button
                SimulationModifier mod = getSelectedAvailableModifier();
-               if (mod != null && !selectedModifiers.contains(mod)) {
-                       addButton.setEnabled(true);
-               } else {
-                       addButton.setEnabled(false);
-               }
+               state = (mod != null && !selectedModifiers.contains(mod));
+               log.debug("addButton enabled: " + state);
+               addButton.setEnabled(state);
                
                // "Remove" button
-               removeButton.setEnabled(selectedModifierTable.getSelectedRow() >= 0);
+               state = (selectedModifierTable.getSelectedRow() >= 0);
+               log.debug("removeButton enabled: " + state);
+               removeButton.setEnabled(state);
                
                // "Remove all" button
-               removeAllButton.setEnabled(!selectedModifiers.isEmpty());
+               state = (!selectedModifiers.isEmpty());
+               log.debug("removeAllButton enabled: " + state);
+               removeAllButton.setEnabled(state);
                
 
                // Optimization goal
                String selected = (String) optimizationGoalCombo.getSelectedItem();
-               if (GOAL_SEEK.equals(selected)) {
-                       optimizationGoalSpinner.setVisible(true);
-                       optimizationGoalUnitSelector.setVisible(true);
-               } else {
-                       optimizationGoalSpinner.setVisible(false);
-                       optimizationGoalUnitSelector.setVisible(false);
-               }
+               state = GOAL_SEEK.equals(selected);
+               log.debug("optimizationGoalSpinner & UnitSelector enabled: " + state);
+               optimizationGoalSpinner.setVisible(state);
+               optimizationGoalUnitSelector.setVisible(state);
                
 
                // Minimum/maximum stability options
-               minimumStabilitySpinner.setEnabled(minimumStabilitySelected.isSelected());
-               minimumStabilityUnitSelector.setEnabled(minimumStabilitySelected.isSelected());
-               maximumStabilitySpinner.setEnabled(maximumStabilitySelected.isSelected());
-               maximumStabilityUnitSelector.setEnabled(maximumStabilitySelected.isSelected());
+               state = minimumStabilitySelected.isSelected();
+               log.debug("minimumStabilitySpinner & UnitSelector enabled: " + state);
+               minimumStabilitySpinner.setEnabled(state);
+               minimumStabilityUnitSelector.setEnabled(state);
+               
+               state = maximumStabilitySelected.isSelected();
+               log.debug("maximumStabilitySpimmer & UnitSelector enabled: " + state);
+               maximumStabilitySpinner.setEnabled(state);
+               maximumStabilityUnitSelector.setEnabled(state);
                
 
                // Plot button (enabled if path exists and dimensionality is 1 or 2)
-               plotButton.setEnabled(!optimizationPath.isEmpty() && (selectedModifiers.size() == 1 || selectedModifiers.size() == 2));
+               state = (!optimizationPath.isEmpty() && (selectedModifiers.size() == 1 || selectedModifiers.size() == 2));
+               log.debug("plotButton enabled: " + state + " optimizationPath.isEmpty=" + optimizationPath.isEmpty() +
+                               " selectedModifiers.size=" + selectedModifiers.size());
+               plotButton.setEnabled(state);
                
                // Save button (enabled if path exists)
-               saveButton.setEnabled(!optimizationPath.isEmpty());
+               state = (!evaluationHistory.isEmpty());
+               log.debug("saveButton enabled: " + state);
+               saveButton.setEnabled(state);
                
 
                // Last disable all components if optimization is running
                if (running) {
+                       log.debug("Disabling all components because optimization is running");
                        for (JComponent c : disableComponents) {
                                c.setEnabled(false);
                        }
@@ -1445,6 +1464,12 @@ public class GeneralOptimizationDialog extends JDialog {
                @Override
                public void setValueAt(Object value, int row, int column) {
                        
+                       if (row >= selectedModifiers.size()) {
+                               throw new BugException("setValueAt with invalid row:  value=" + value + " row=" + row + " column=" + column +
+                                               " selectedModifiers.size=" + selectedModifiers.size() + " selectedModifiers=" + selectedModifiers +
+                                               " selectedModifierTable.getRowCount=" + selectedModifierTable.getRowCount());
+                       }
+                       
                        switch (column) {
                        case PARAMETER:
                                break;
index ab9c9c94c284f50349097b2d04a757e316dab6bf..04885a82cbaa441f06910279b231c67fe85c8f74 100644 (file)
@@ -162,7 +162,12 @@ public class OptimizationPlotDialog extends JDialog {
                double x1 = xUnit.toUnit(modX.getMinValue());
                double x2 = xUnit.toUnit(modX.getMaxValue());
                
-               chart.getXYPlot().getDomainAxis().setRange(x1, x2);
+               if (x1 < x2 - 0.0001) {
+                       log.debug("Setting 1D plot domain axis x1=" + x1 + " x2=" + x2);
+                       chart.getXYPlot().getDomainAxis().setRange(x1, x2);
+               } else {
+                       log.warn("1D plot domain singular x1=" + x1 + " x2=" + x2 + ", not setting");
+               }
                
                // Add lines to show optimization limits
                XYLineAnnotation line = new XYLineAnnotation(x1, -1e19, x1, 1e19);
@@ -296,8 +301,19 @@ public class OptimizationPlotDialog extends JDialog {
                double y1 = yUnit.toUnit(modY.getMinValue());
                double y2 = yUnit.toUnit(modY.getMaxValue());
                
-               chart.getXYPlot().getDomainAxis().setRange(x1, x2);
-               chart.getXYPlot().getRangeAxis().setRange(y1, y2);
+               if (x1 < x2 - 0.0001) {
+                       log.debug("Setting 2D plot domain axis to x1=" + x1 + " x2=" + x2);
+                       chart.getXYPlot().getDomainAxis().setRange(x1, x2);
+               } else {
+                       log.warn("2D plot has singular domain axis: x1=" + x1 + " x2=" + x2);
+               }
+               
+               if (y1 < y2 - 0.0001) {
+                       log.debug("Setting 2D plot range axis to y1=" + y1 + " y2=" + y2);
+                       chart.getXYPlot().getRangeAxis().setRange(y1, y2);
+               } else {
+                       log.warn("2D plot has singular range axis: y1=" + y1 + " y2=" + y2);
+               }
                
                XYBoxAnnotation box = new XYBoxAnnotation(x1, y1, x2, y2);
                chart.getXYPlot().addAnnotation(box);
@@ -308,6 +324,15 @@ public class OptimizationPlotDialog extends JDialog {
                text.setTextAnchor(TextAnchor.BASELINE_LEFT);
                chart.getXYPlot().addAnnotation(text);
                
+
+               if (min < max - 0.0001) {
+                       log.debug("Setting gradient scale range to min=" + min + " max=" + max);
+               } else {
+                       log.warn("2D plot has singular gradient scale, resetting to (0,1): min=" + min + " max=" + max);
+                       min = 0;
+                       max = 1;
+               }
+               
                PaintScale paintScale = new GradientScale(min, max);
                
                XYShapeRenderer shapeRenderer = new XYShapeRenderer();
index 74f3091757ad4dc1cbcf6136228668d489ae7549..83301da729d0645a2e3c26c398eb835002c55551 100644 (file)
@@ -301,6 +301,29 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
                
                // NOTE:  Calling method logs the entire throwable, so log only message here
                
+
+               /*
+                * Detect and ignore bug 6826104 in Sun JRE.
+                */
+               if (t instanceof NullPointerException) {
+                       StackTraceElement[] trace = t.getStackTrace();
+                       
+                       if (trace.length > 3 &&
+                                       trace[0].getClassName().equals("sun.awt.X11.XWindowPeer") &&
+                                       trace[0].getMethodName().equals("restoreTransientFor") &&
+
+                                       trace[1].getClassName().equals("sun.awt.X11.XWindowPeer") &&
+                                       trace[1].getMethodName().equals("removeFromTransientFors") &&
+
+                                       trace[2].getClassName().equals("sun.awt.X11.XWindowPeer") &&
+                                       trace[2].getMethodName().equals("setModalBlocked")) {
+                               log.warn("Ignoring Sun JRE bug (6826104): http://bugs.sun.com/view_bug.do?bug_id=6826104" + t);
+                               return true;
+                       }
+                       
+               }
+               
+
                /*
                 * Detect and ignore bug 6828938 in Sun JRE 1.6.0_14 - 1.6.0_16.
                 */
index 920d4617021b85a143ab1be8fb2c3ccca72d8193..60cc67b8ca209470a42811259ce4ec3adb21d7f8 100644 (file)
@@ -1,6 +1,35 @@
 package net.sf.openrocket.gui.scalefigure;
 
 
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.JToggleButton;
+import javax.swing.JViewport;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
 import net.miginfocom.swing.MigLayout;
 import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
 import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
@@ -40,34 +69,6 @@ import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 import net.sf.openrocket.util.Prefs;
 
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JSlider;
-import javax.swing.JToggleButton;
-import javax.swing.JViewport;
-import javax.swing.SwingUtilities;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import javax.swing.event.TreeSelectionEvent;
-import javax.swing.event.TreeSelectionListener;
-import javax.swing.tree.TreePath;
-import javax.swing.tree.TreeSelectionModel;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Point;
-import java.awt.event.ActionEvent;
-import java.awt.event.InputEvent;
-import java.awt.event.MouseEvent;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-
 /**
  * A JPanel that contains a RocketFigure and buttons to manipulate the figure. 
  * 
@@ -241,12 +242,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
                add(new BasicSlider(theta.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true),
                                "ax 50%, wrap, width " + (d.width + 6) + "px:null:null, growy");
                
-               
+
                //// <html>Click to select &nbsp;&nbsp; Shift+click to select other &nbsp;&nbsp; Double-click to edit &nbsp;&nbsp; Click+drag to move
                infoMessage = new JLabel(trans.get("RocketPanel.lbl.infoMessage"));
                infoMessage.setFont(new Font("Sans Serif", Font.PLAIN, 9));
                add(infoMessage, "skip, span, gapleft 25, wrap");
                
+
                addExtras();
        }
        
@@ -263,35 +265,35 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
        public Configuration getConfiguration() {
                return configuration;
        }
-
-    /**
-     * Get the center of pressure figure element.
-     * 
-     * @return center of pressure info
-     */
-    public Caret getExtraCP () {
-        return extraCP;
-    }
-
-    /**
-     * Get the center of gravity figure element.
-     * 
-     * @return center of gravity info
-     */
-    public Caret getExtraCG () {
-        return extraCG;
-    }
-
-    /**
-     * Get the extra text figure element.
-     * 
-     * @return extra text that contains info about the rocket design
-     */
-    public RocketInfo getExtraText () {
-        return extraText;
-    }
-
-    public void setSelectionModel(TreeSelectionModel m) {
+       
+       /**
+        * Get the center of pressure figure element.
+        
+        * @return center of pressure info
+        */
+       public Caret getExtraCP() {
+               return extraCP;
+       }
+       
+       /**
+        * Get the center of gravity figure element.
+        
+        * @return center of gravity info
+        */
+       public Caret getExtraCG() {
+               return extraCG;
+       }
+       
+       /**
+        * Get the extra text figure element.
+        
+        * @return extra text that contains info about the rocket design
+        */
+       public RocketInfo getExtraText() {
+               return extraText;
+       }
+       
+       public void setSelectionModel(TreeSelectionModel m) {
                if (selectionModel != null) {
                        selectionModel.removeTreeSelectionListener(this);
                }
index 01029d7d34e06493b316b6f4c1e7ff5973ca4912..f3fc1714ae9c0e05246cabd4458a377dbeb1a834 100644 (file)
@@ -95,31 +95,33 @@ public class TraceException extends Exception {
                        StackTraceElement[] elements = this.getStackTrace();
                        
                        StringBuilder sb = new StringBuilder();
-                       if (minLevel < elements.length) {
-                               
-                               sb.append("(");
-                               sb.append(toString(elements[minLevel]));
-                               for (int i = minLevel + 1; i <= maxLevel; i++) {
-                                       if (i < elements.length) {
-                                               sb.append(' ').append(toString(elements[i]));
-                                       }
-                               }
-                               sb.append(')');
-                               
-                       } else if (elements.length == 0) {
-                               
-                               sb.append("(no stack trace)");
-                               
+                       sb.append('(');
+                       
+                       if (elements == null || elements.length == 0) {
+                               sb.append("no stack trace");
                        } else {
                                
-                               sb.append('(');
-                               sb.append(toString(elements[0]));
-                               for (int i = 1; i < elements.length; i++) {
-                                       sb.append(' ').append(toString(elements[i]));
+                               int levelCount = 0;
+                               int position = minLevel;
+                               while (levelCount <= (maxLevel - minLevel) && position < elements.length) {
+                                       
+                                       // Ignore synthetic "access$0" methods generated by the JRE
+                                       if (elements[position].getMethodName().contains("$")) {
+                                               position++;
+                                               continue;
+                                       }
+                                       
+                                       if (levelCount > 0) {
+                                               sb.append(' ');
+                                       }
+                                       sb.append(toString(elements[position]));
+                                       levelCount++;
+                                       position++;
                                }
-                               sb.append(" level=").append(minLevel).append(')');
                                
                        }
+                       sb.append(')');
+                       
                        message = sb.toString();
                }
                return message;
index 8febc9480432cefa785ebc2c1b5a262ef51f9e7f..00be68b1c3cdce191dfe53b035d13d31fc086a60 100644 (file)
@@ -15,7 +15,7 @@ import net.sf.openrocket.util.MathUtil;
  */
 
 public abstract class Material implements Comparable<Material> {
-
+       
        public enum Type {
                LINE("Line", UnitGroup.UNITS_DENSITY_LINE),
                SURFACE("Surface", UnitGroup.UNITS_DENSITY_SURFACE),
@@ -23,13 +23,16 @@ public abstract class Material implements Comparable<Material> {
                
                private final String name;
                private final UnitGroup units;
+               
                private Type(String name, UnitGroup units) {
                        this.name = name;
                        this.units = units;
                }
+               
                public UnitGroup getUnitGroup() {
                        return units;
                }
+               
                @Override
                public String toString() {
                        return name;
@@ -43,7 +46,7 @@ public abstract class Material implements Comparable<Material> {
                public Line(String name, double density, boolean userDefined) {
                        super(name, density, userDefined);
                }
-
+               
                @Override
                public Type getType() {
                        return Type.LINE;
@@ -71,7 +74,7 @@ public abstract class Material implements Comparable<Material> {
                public Bulk(String name, double density, boolean userDefined) {
                        super(name, density, userDefined);
                }
-
+               
                @Override
                public Type getType() {
                        return Type.BULK;
@@ -79,7 +82,7 @@ public abstract class Material implements Comparable<Material> {
        }
        
        
-       
+
        private final String name;
        private final double density;
        private final boolean userDefined;
@@ -92,7 +95,7 @@ public abstract class Material implements Comparable<Material> {
        }
        
        
-       
+
        public double getDensity() {
                return density;
        }
@@ -116,7 +119,7 @@ public abstract class Material implements Comparable<Material> {
                return this.getName(this.getType().getUnitGroup().getDefaultUnit());
        }
        
-
+       
        /**
         * Compares this object to another object.  Material objects are equal if and only if
         * their types, names and densities are identical.
@@ -127,30 +130,30 @@ public abstract class Material implements Comparable<Material> {
                        return false;
                if (this.getClass() != o.getClass())
                        return false;
-               Material m = (Material)o;
-               return ((m.name.equals(this.name)) && 
-                               MathUtil.equals(m.density, this.density)); 
+               Material m = (Material) o;
+               return ((m.name.equals(this.name)) && MathUtil.equals(m.density, this.density));
        }
-
-
+       
+       
        /**
         * A hashCode() method giving a hash code compatible with the equals() method.
         */
        @Override
        public int hashCode() {
-               return name.hashCode() + (int)(density*1000);
+               return name.hashCode() + (int) (density * 1000);
        }
-
+       
        
        /**
         * Order the materials according to their name, secondarily according to density.
         */
+       @Override
        public int compareTo(Material o) {
                int c = this.name.compareTo(o.name);
                if (c != 0) {
                        return c;
                } else {
-                       return (int)((this.density - o.density)*1000);
+                       return (int) ((this.density - o.density) * 1000);
                }
        }
        
@@ -158,7 +161,7 @@ public abstract class Material implements Comparable<Material> {
        /**
         * Return a new material of the specified type.
         */
-       public static Material newMaterial(Type type, String name, double density, 
+       public static Material newMaterial(Type type, String name, double density,
                        boolean userDefined) {
                switch (type) {
                case LINE:
@@ -171,7 +174,7 @@ public abstract class Material implements Comparable<Material> {
                        return new Material.Bulk(name, density, userDefined);
                        
                default:
-                       throw new IllegalArgumentException("Unknown material type: "+type);
+                       throw new IllegalArgumentException("Unknown material type: " + type);
                }
        }
        
@@ -193,10 +196,10 @@ public abstract class Material implements Comparable<Material> {
                if (str == null)
                        throw new IllegalArgumentException("Material string is null");
                
-               String[] split = str.split("\\|",3);
+               String[] split = str.split("\\|", 3);
                if (split.length < 3)
-                       throw new IllegalArgumentException("Illegal material string: "+str);
-
+                       throw new IllegalArgumentException("Illegal material string: " + str);
+               
                Type type = null;
                String name;
                double density;
@@ -204,15 +207,15 @@ public abstract class Material implements Comparable<Material> {
                try {
                        type = Type.valueOf(split[0]);
                } catch (Exception e) {
-                       throw new IllegalArgumentException("Illegal material string: "+str, e);
+                       throw new IllegalArgumentException("Illegal material string: " + str, e);
                }
-
+               
                name = split[1];
                
                try {
                        density = Double.parseDouble(split[2]);
                } catch (NumberFormatException e) {
-                       throw new IllegalArgumentException("Illegal material string: "+str, e);
+                       throw new IllegalArgumentException("Illegal material string: " + str, e);
                }
                
                switch (type) {
@@ -226,8 +229,8 @@ public abstract class Material implements Comparable<Material> {
                        return new Material.Line(name, density, userDefined);
                        
                default:
-                       throw new IllegalArgumentException("Illegal material string: "+str);
+                       throw new IllegalArgumentException("Illegal material string: " + str);
                }
        }
-
+       
 }
diff --git a/src/net/sf/openrocket/preset/ComponentPreset.java b/src/net/sf/openrocket/preset/ComponentPreset.java
new file mode 100644 (file)
index 0000000..87343e9
--- /dev/null
@@ -0,0 +1,73 @@
+package net.sf.openrocket.preset;
+
+import net.sf.openrocket.motor.Manufacturer;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+/**
+ * A model for a preset component.
+ * <p>
+ * A preset component contains a component class type, manufacturer information,
+ * part information, and a method that returns a prototype of the preset component.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public abstract class ComponentPreset {
+       
+       private final Manufacturer manufacturer;
+       private final String partNo;
+       private final String partDescription;
+       private final RocketComponent prototype;
+       
+       
+       public ComponentPreset(Manufacturer manufacturer, String partNo, String partDescription,
+                       RocketComponent prototype) {
+               this.manufacturer = manufacturer;
+               this.partNo = partNo;
+               this.partDescription = partDescription;
+               this.prototype = prototype.copy();
+               
+               if (prototype.getParent() != null) {
+                       throw new IllegalArgumentException("Prototype component cannot have a parent");
+               }
+               if (prototype.getChildCount() > 0) {
+                       throw new IllegalArgumentException("Prototype component cannot have children");
+               }
+       }
+       
+       
+       /**
+        * Return the component class that this preset defines.
+        */
+       public Class<? extends RocketComponent> getComponentClass() {
+               return prototype.getClass();
+       }
+       
+       /**
+        * Return the manufacturer of this preset component.
+        */
+       public Manufacturer getManufacturer() {
+               return manufacturer;
+       }
+       
+       /**
+        * Return the part number.  This is the part identifier (e.g. "BT-50").
+        */
+       public String getPartNo() {
+               return partNo;
+       }
+       
+       /**
+        * Return the part description.  This is a longer description of the component.
+        */
+       public String getPartDescription() {
+               return partDescription;
+       }
+       
+       /**
+        * Return a prototype component.  This component may be modified freely.
+        */
+       public RocketComponent getPrototype() {
+               return prototype.copy();
+       }
+       
+}
diff --git a/src/net/sf/openrocket/preset/ExternalComponentPreset.java b/src/net/sf/openrocket/preset/ExternalComponentPreset.java
deleted file mode 100644 (file)
index da37040..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package net.sf.openrocket.preset;
-
-import net.sf.openrocket.motor.Manufacturer;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-
-public class ExternalComponentPreset extends RocketComponentPreset {
-       
-       private final double mass;
-       private final String materialName;
-       
-       public ExternalComponentPreset(Class<? extends RocketComponent> componentClass, Manufacturer manufacturer, String partName,
-                       String partNo, String partDescription, double mass, String materialName) {
-               super(componentClass, manufacturer, partName, partNo, partDescription);
-               
-               this.materialName = materialName;
-               this.mass = mass;
-       }
-       
-       
-       public String getMaterialName() {
-               return materialName;
-       }
-       
-       
-       public double getMass() {
-               return mass;
-       }
-       
-}
diff --git a/src/net/sf/openrocket/preset/RocketComponentPreset.java b/src/net/sf/openrocket/preset/RocketComponentPreset.java
deleted file mode 100644 (file)
index 34940b0..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-package net.sf.openrocket.preset;
-
-import net.sf.openrocket.motor.Manufacturer;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-
-/**
- * A model for a preset component.
- * <p>
- * A preset component contains a component class type, manufacturer information,
- * part information, and getter methods for various properties of the component.
- * 
- * @author Sampo Niskanen <sampo.niskanen@iki.fi>
- */
-public abstract class RocketComponentPreset {
-       
-       private final Class<? extends RocketComponent> componentClass;
-       private final Manufacturer manufacturer;
-       private final String partName;
-       private final String partNo;
-       private final String partDescription;
-       
-       
-       public RocketComponentPreset(Class<? extends RocketComponent> componentClass, Manufacturer manufacturer,
-                       String partName, String partNo, String partDescription) {
-               this.componentClass = componentClass;
-               this.manufacturer = manufacturer;
-               this.partName = partName;
-               this.partNo = partNo;
-               this.partDescription = partDescription;
-       }
-       
-       
-       /**
-        * Return the component class that this preset defines.
-        */
-       public Class<? extends RocketComponent> getComponentClass() {
-               return componentClass;
-       }
-       
-       /**
-        * Return the manufacturer of this preset component.
-        */
-       public Manufacturer getManufacturer() {
-               return manufacturer;
-       }
-       
-       /**
-        * Return the part name.  This is a short, human-readable name of the part.
-        */
-       public String getPartName() {
-               return partName;
-       }
-       
-       /**
-        * Return the part number.  This is the part identifier (e.g. "BT-50").
-        */
-       public String getPartNo() {
-               return partNo;
-       }
-       
-       /**
-        * Return the part description.  This is a longer description of the component.
-        */
-       public String getPartDescription() {
-               return partDescription;
-       }
-       
-}
index 2bc112fbb11624924fcd699307157bde8764c3b1..a102e87f720871ae46f1341a3f3f05280bb6c858 100644 (file)
@@ -1,6 +1,7 @@
 package net.sf.openrocket.rocketcomponent;
 
 
+
 /**
  * Class to represent a body object.  The object can be described as a function of
  * the cylindrical coordinates x and angle theta as  r = f(x,theta).  The component
@@ -13,7 +14,7 @@ package net.sf.openrocket.rocketcomponent;
  */
 
 public abstract class BodyComponent extends ExternalComponent {
-
+       
        /**
         * Default constructor.  Sets the relative position to POSITION_RELATIVE_AFTER,
         * i.e. body components come after one another.
@@ -21,8 +22,8 @@ public abstract class BodyComponent extends ExternalComponent {
        public BodyComponent() {
                super(RocketComponent.Position.AFTER);
        }
-
-
+       
+       
        /**
         * Get the outer radius of the component at cylindrical coordinate (x,theta).
         *
@@ -33,8 +34,8 @@ public abstract class BodyComponent extends ExternalComponent {
         * @return  Distance to the outer edge of the object
         */
        public abstract double getRadius(double x, double theta);
-
-
+       
+       
        /**
         * Get the inner radius of the component at cylindrical coordinate (x,theta).
         *
@@ -45,11 +46,23 @@ public abstract class BodyComponent extends ExternalComponent {
         * @return  Distance to the inner edge of the object
         */
        public abstract double getInnerRadius(double x, double theta);
-
-
+       
+       
+       @Override
+       protected void loadFromPreset(RocketComponent preset) {
+               BodyComponent c = (BodyComponent) preset;
+               this.setLength(c.getLength());
+               
+               super.loadFromPreset(preset);
+       }
+       
+       
 
        /**
         * Sets the length of the body component.
+        * <p>
+        * Note: This should be overridden by the subcomponents which need to call
+        * clearPreset().  (BodyTube allows changing length without resetting the preset.)
         */
        public void setLength(double length) {
                if (this.length == length)
@@ -57,10 +70,10 @@ public abstract class BodyComponent extends ExternalComponent {
                this.length = Math.max(length, 0);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
+       
        @Override
        public boolean allowsChildren() {
                return true;
        }
-
+       
 }
index d3b6194eb63832b52e84278048b2cb88c97c9d68..64c1b4a99c7608f602e676ce38909dad0cd22919 100644 (file)
@@ -108,6 +108,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
                if (this.thickness > this.outerRadius)
                        this.thickness = this.outerRadius;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
+               clearPreset();
        }
        
        
@@ -128,6 +129,16 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
                
                autoRadius = auto;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
+               clearPreset();
+       }
+       
+       
+       @Override
+       protected void loadFromPreset(RocketComponent preset) {
+               BodyTube c = (BodyTube) preset;
+               this.setOuterRadius(c.getOuterRadius());
+               
+               super.loadFromPreset(preset);
        }
        
        
index 23cea24074368f56983fd6fa6e20a2e8b8f122ab..e710cb2773d288a14b915e9335112a117a76bfb4 100644 (file)
@@ -5,8 +5,6 @@ import java.util.List;
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.material.Material;
 import net.sf.openrocket.material.Material.Type;
-import net.sf.openrocket.preset.ExternalComponentPreset;
-import net.sf.openrocket.preset.RocketComponentPreset;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.Prefs;
@@ -116,6 +114,7 @@ public abstract class ExternalComponent extends RocketComponent {
                material = mat;
                clearPreset();
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
+               clearPreset();
        }
        
        public Finish getFinish() {
@@ -131,22 +130,26 @@ public abstract class ExternalComponent extends RocketComponent {
        
        
        @Override
-       protected void loadFromPreset(RocketComponentPreset preset) {
+       protected void loadFromPreset(RocketComponent preset) {
                super.loadFromPreset(preset);
                
-               ExternalComponentPreset p = (ExternalComponentPreset) preset;
-               String materialName = p.getMaterialName();
-               double mass = p.getMass();
+               // Surface finish is left unchanged
+               
+               ExternalComponent c = (ExternalComponent) preset;
                
-               double volume = getComponentVolume();
-               double density;
-               if (volume > 0.00001) {
-                       density = mass / volume;
-               } else {
-                       density = 1000;
+               Material mat = c.getMaterial();
+               if (c.isMassOverridden()) {
+                       double mass = c.getOverrideMass();
+                       double volume = getComponentVolume();
+                       double density;
+                       if (volume > 0.00001) {
+                               density = mass / volume;
+                       } else {
+                               density = 1000;
+                       }
+                       mat = Material.newMaterial(Type.BULK, mat.getName(), density, true);
                }
                
-               Material mat = Material.newMaterial(Type.BULK, materialName, density, true);
                setMaterial(mat);
        }
        
index b0c7b47e32c97c2926825af079e08a51af4dbfa0..6b906589615dc0fc62ae807a0ebfaba743b1838c 100644 (file)
@@ -12,7 +12,7 @@ import javax.swing.event.ChangeListener;
 
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.logging.LogHelper;
-import net.sf.openrocket.preset.RocketComponentPreset;
+import net.sf.openrocket.preset.ComponentPreset;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.util.ArrayList;
 import net.sf.openrocket.util.BugException;
@@ -125,7 +125,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        private String id = null;
        
        // Preset component this component is based upon
-       private RocketComponentPreset presetComponent = null;
+       private ComponentPreset presetComponent = null;
        
 
        /**
@@ -669,7 +669,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * 
         * @return      the preset component, or <code>null</code> if this is not based on a preset.
         */
-       public final RocketComponentPreset getPresetComponent() {
+       public final ComponentPreset getPresetComponent() {
                return presetComponent;
        }
        
@@ -679,7 +679,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * 
         * @param preset        the preset component to load, or <code>null</code> to clear the preset.
         */
-       public final void loadPreset(RocketComponentPreset preset) {
+       public final void loadPreset(ComponentPreset preset) {
                if (presetComponent == preset) {
                        return;
                }
@@ -707,7 +707,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                                rocket.freeze();
                        }
                        
-                       loadFromPreset(preset);
+                       loadFromPreset(preset.getPrototype());
                        
                        this.presetComponent = preset;
                        fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
@@ -726,10 +726,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * <p>
         * This method should fire the appropriate events related to the changes.  The rocket
         * is frozen by the caller, so the events will be automatically combined.
+        * <p>
+        * This method must FIRST perform the preset loading and THEN call super.loadFromPreset().
+        * This is because mass setting requires the dimensions to be set beforehand.
         * 
         * @param preset        the preset to load from
         */
-       protected void loadFromPreset(RocketComponentPreset preset) {
+       protected void loadFromPreset(RocketComponent preset) {
                // No-op
        }
        
index 275de2051d5320b1466d36fc6a74b4f96504b256..5dc1835463e9ed3bf2ad72207c0835607e2b167c 100644 (file)
@@ -103,6 +103,7 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial
                this.thickness = MathUtil.clamp(thickness, 0, Math.max(getForeRadius(), getAftRadius()));
                filled = false;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
+               clearPreset();
        }
        
        
@@ -124,6 +125,7 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial
                        return;
                this.filled = filled;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
+               clearPreset();
        }
        
        
@@ -143,6 +145,18 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial
        
        
 
+       @Override
+       protected void loadFromPreset(RocketComponent preset) {
+               SymmetricComponent c = (SymmetricComponent) preset;
+               this.setThickness(c.getThickness());
+               this.setFilled(c.isFilled());
+               
+               super.loadFromPreset(preset);
+       }
+       
+       
+
+
        /**
         * Calculate volume of the component by integrating over the length of the component.
         * The method caches the result, so subsequent calls are instant.  Subclasses may
@@ -368,7 +382,7 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial
                longitudinalInertia = 0;
                rotationalInertia = 0;
                
-               double volume = 0;
+               double vol = 0;
                
                for (int n = 1; n <= DIVISIONS; n++) {
                        /*
@@ -399,20 +413,20 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial
                        longitudinalInertia += dV * ((3 * (pow2(outer) + pow2(inner)) + pow2(l)) / 12
                                        + pow2(x + l / 2));
                        
-                       volume += dV;
+                       vol += dV;
                        
                        // Update for next iteration
                        r1 = r2;
                        x += l;
                }
                
-               if (MathUtil.equals(volume, 0)) {
+               if (MathUtil.equals(vol, 0)) {
                        integrateInertiaSurface();
                        return;
                }
                
-               rotationalInertia /= volume;
-               longitudinalInertia /= volume;
+               rotationalInertia /= vol;
+               longitudinalInertia /= vol;
                
                // Shift longitudinal inertia to CG
                longitudinalInertia = Math.max(longitudinalInertia - pow2(getComponentCG().x), 0);
index 05a22b1fcab57d0a94d2914b7a18c2e81e12f6f4..a2b39854bbfed73b0ef8a93944b15a2aed59575d 100644 (file)
@@ -17,6 +17,7 @@ import net.sf.openrocket.rocketcomponent.Rocket;
 import net.sf.openrocket.util.BugException;
 import net.sf.openrocket.util.ChangeSource;
 import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.Utils;
 
 /**
  * A class holding simulation options in basic parameter form and which functions
@@ -415,7 +416,7 @@ public class SimulationOptions implements ChangeSource, Cloneable {
                        return false;
                SimulationOptions o = (SimulationOptions) other;
                return ((this.rocket == o.rocket) &&
-                               this.motorID.equals(o.motorID) &&
+                               Utils.equals(this.motorID, o.motorID) &&
                                MathUtil.equals(this.launchAltitude, o.launchAltitude) &&
                                MathUtil.equals(this.launchLatitude, o.launchLatitude) &&
                                MathUtil.equals(this.launchPressure, o.launchPressure) &&
diff --git a/test/net/sf/openrocket/logging/TraceExceptionTest.java b/test/net/sf/openrocket/logging/TraceExceptionTest.java
new file mode 100644 (file)
index 0000000..72f3c13
--- /dev/null
@@ -0,0 +1,89 @@
+package net.sf.openrocket.logging;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class TraceExceptionTest {
+       
+       private TraceException getViaAccess() {
+               
+               /*
+                * The SubClass test bases on the fact that getViaAccess method is defined on a row number < 20
+                * and the return statement is on a row number > 20.
+                * 
+                * The JRE sometimes adds an additional "access$NNN" method call between the calls with the
+                * row number equal to the method definition line.
+                * 
+                * 
+                * 
+                * 
+                * 
+                * 
+                * 
+                */
+
+               return new TraceException(0, 1);
+       }
+       
+       
+       @Test
+       public void testBasic() {
+               TraceException trace = new TraceException();
+               assertMatch("\\(TraceExceptionTest.java:[2-9][0-9]\\)", trace);
+       }
+       
+       
+       @Test
+       public void testOneLevelUp() {
+               // @formatter:off - these need to be on the same line number
+               TraceException trace = getOneLevelUp(); TraceException ref = new TraceException();
+               // @formatter:on
+               assertEquals(ref.getMessage(), trace.getMessage());
+       }
+       
+       private TraceException getOneLevelUp() {
+               return new TraceException(1);
+       }
+       
+       
+       @Test
+       public void testTwoLevels() {
+               TraceException trace = getTwoLevels();
+               assertMatch("\\(TraceExceptionTest.java:[2-9][0-9] TraceExceptionTest.java:[2-9][0-9]\\)", trace);
+       }
+       
+       private TraceException getTwoLevels() {
+               return new TraceException(0, 1);
+       }
+       
+       
+       @Test
+       public void testViaSubclass() {
+               /*
+                * This tests that TraceException.getMessage ignores the synthetic "access$0" method calls.
+                */
+
+               TraceException trace = new SubClass().getTrace();
+               assertMatch("\\(TraceExceptionTest.java:[2-9][0-9] TraceExceptionTest.java:[2-9][0-9]\\)", trace);
+       }
+       
+       private class SubClass {
+               private TraceException getTrace() {
+                       return getViaAccess();
+               }
+       }
+       
+       
+
+
+
+       private void assertMatch(String regex, TraceException trace) {
+               boolean match = trace.getMessage().matches(regex);
+               if (!match) {
+                       trace.printStackTrace();
+                       assertTrue("Was: " + trace.getMessage(), match);
+               }
+       }
+       
+}