From d50821c2be7fff2aa18203bbbc0bf4e23028e853 Mon Sep 17 00:00:00 2001 From: plaa Date: Thu, 25 Aug 2011 19:29:50 +0000 Subject: [PATCH] bug fixes git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@160 180e2498-e6e9-4542-8430-84ac67f01cd8 --- ChangeLog | 11 ++ .../GeneralOptimizationDialog.java | 69 ++++++---- .../optimization/OptimizationPlotDialog.java | 31 ++++- .../openrocket/gui/main/ExceptionHandler.java | 23 ++++ .../gui/scalefigure/RocketPanel.java | 118 +++++++++--------- .../sf/openrocket/logging/TraceException.java | 42 ++++--- src/net/sf/openrocket/material/Material.java | 51 ++++---- ...ponentPreset.java => ComponentPreset.java} | 37 +++--- .../preset/ExternalComponentPreset.java | 29 ----- .../rocketcomponent/BodyComponent.java | 31 +++-- .../openrocket/rocketcomponent/BodyTube.java | 11 ++ .../rocketcomponent/ExternalComponent.java | 29 +++-- .../rocketcomponent/RocketComponent.java | 15 ++- .../rocketcomponent/SymmetricComponent.java | 24 +++- .../simulation/SimulationOptions.java | 3 +- .../logging/TraceExceptionTest.java | 89 +++++++++++++ 16 files changed, 407 insertions(+), 206 deletions(-) rename src/net/sf/openrocket/preset/{RocketComponentPreset.java => ComponentPreset.java} (59%) delete mode 100644 src/net/sf/openrocket/preset/ExternalComponentPreset.java create mode 100644 test/net/sf/openrocket/logging/TraceExceptionTest.java diff --git a/ChangeLog b/ChangeLog index 89e229f9..5399602f 100644 --- 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. diff --git a/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java b/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java index 0ef35e2e..1478a2b1 100644 --- a/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java @@ -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; diff --git a/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java b/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java index ab9c9c94..04885a82 100644 --- a/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java @@ -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(); diff --git a/src/net/sf/openrocket/gui/main/ExceptionHandler.java b/src/net/sf/openrocket/gui/main/ExceptionHandler.java index 74f30917..83301da7 100644 --- a/src/net/sf/openrocket/gui/main/ExceptionHandler.java +++ b/src/net/sf/openrocket/gui/main/ExceptionHandler.java @@ -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. */ diff --git a/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 920d4617..60cc67b8 100644 --- a/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -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"); - + //// Click to select    Shift+click to select other    Double-click to edit    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); } diff --git a/src/net/sf/openrocket/logging/TraceException.java b/src/net/sf/openrocket/logging/TraceException.java index 01029d7d..f3fc1714 100644 --- a/src/net/sf/openrocket/logging/TraceException.java +++ b/src/net/sf/openrocket/logging/TraceException.java @@ -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; diff --git a/src/net/sf/openrocket/material/Material.java b/src/net/sf/openrocket/material/Material.java index 8febc948..00be68b1 100644 --- a/src/net/sf/openrocket/material/Material.java +++ b/src/net/sf/openrocket/material/Material.java @@ -15,7 +15,7 @@ import net.sf.openrocket.util.MathUtil; */ public abstract class Material implements Comparable { - + 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 { 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 { 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 { 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 { } - + private final String name; private final double density; private final boolean userDefined; @@ -92,7 +95,7 @@ public abstract class Material implements Comparable { } - + public double getDensity() { return density; } @@ -116,7 +119,7 @@ public abstract class Material implements Comparable { 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 { 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 { /** * 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 { 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 { 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 { 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 { 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/RocketComponentPreset.java b/src/net/sf/openrocket/preset/ComponentPreset.java similarity index 59% rename from src/net/sf/openrocket/preset/RocketComponentPreset.java rename to src/net/sf/openrocket/preset/ComponentPreset.java index 34940b00..87343e93 100644 --- a/src/net/sf/openrocket/preset/RocketComponentPreset.java +++ b/src/net/sf/openrocket/preset/ComponentPreset.java @@ -7,26 +7,31 @@ import net.sf.openrocket.rocketcomponent.RocketComponent; * A model for a preset component. *

* A preset component contains a component class type, manufacturer information, - * part information, and getter methods for various properties of the component. + * part information, and a method that returns a prototype of the preset component. * * @author Sampo Niskanen */ -public abstract class RocketComponentPreset { +public abstract class ComponentPreset { - private final Class componentClass; private final Manufacturer manufacturer; - private final String partName; private final String partNo; private final String partDescription; + private final RocketComponent prototype; - public RocketComponentPreset(Class componentClass, Manufacturer manufacturer, - String partName, String partNo, String partDescription) { - this.componentClass = componentClass; + public ComponentPreset(Manufacturer manufacturer, String partNo, String partDescription, + RocketComponent prototype) { this.manufacturer = manufacturer; - this.partName = partName; 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"); + } } @@ -34,7 +39,7 @@ public abstract class RocketComponentPreset { * Return the component class that this preset defines. */ public Class getComponentClass() { - return componentClass; + return prototype.getClass(); } /** @@ -44,13 +49,6 @@ public abstract class RocketComponentPreset { 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"). */ @@ -65,4 +63,11 @@ public abstract class RocketComponentPreset { 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 index da37040e..00000000 --- a/src/net/sf/openrocket/preset/ExternalComponentPreset.java +++ /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 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/rocketcomponent/BodyComponent.java b/src/net/sf/openrocket/rocketcomponent/BodyComponent.java index 2bc112fb..a102e87f 100644 --- a/src/net/sf/openrocket/rocketcomponent/BodyComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/BodyComponent.java @@ -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. + *

+ * 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; } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/src/net/sf/openrocket/rocketcomponent/BodyTube.java index d3b6194e..64c1b4a9 100644 --- a/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -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); } diff --git a/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java b/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java index 23cea240..e710cb27 100644 --- a/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java @@ -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); } diff --git a/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index b0c7b47e..6b906589 100644 --- a/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -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 null 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 null 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 *

* 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. + *

+ * 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 } diff --git a/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java b/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java index 275de205..5dc18354 100644 --- a/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java @@ -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); diff --git a/src/net/sf/openrocket/simulation/SimulationOptions.java b/src/net/sf/openrocket/simulation/SimulationOptions.java index 05a22b1f..a2b39854 100644 --- a/src/net/sf/openrocket/simulation/SimulationOptions.java +++ b/src/net/sf/openrocket/simulation/SimulationOptions.java @@ -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 index 00000000..72f3c13a --- /dev/null +++ b/test/net/sf/openrocket/logging/TraceExceptionTest.java @@ -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); + } + } + +} -- 2.30.2