+2009-11-28 Sampo Niskanen
+
+ * Released version 0.9.5
+ * [BUG] NPE when clearing combo box selections
+ * Minor fixes to update checking
+ * Added a few guidance texts
+
+2009-11-27 Sampo Niskanen
+
+ * [BUG] Fixed computing inner radius of centering ring
+ * Removed RuntimeException instantiation from all classes
+
2009-11-24 Sampo Niskanen
* Released version 0.9.4
+OpenRocket 0.9.5 (2009-11-28):
+-------------------------------
+
+Fixed a serious defect which prevented adding a tube coupler and
+centering ring on the same body tube. Other minor improvements.
+
+
OpenRocket 0.9.4 (2009-11-24):
-------------------------------
Feature roadmap for OpenRocket 1.0
+----------------------------------
Must-have:
-- Go through thrust curves and correct errors
-or
-- Hide duplicate motors
-
-
-Bugs:
+- Go through thrust curves and select best ones
+- Updated splash screen
- Add slight randomness to yaw moment
+
Postponed:
+----------
+
+
+Motor support:
- Integration with thrustcurve.org (syncing?)
- Reading thrust curves from external directory
- Plot motor thrust curve
+- Water rocket modelling
-- Screw weights for nose cones / transitions
+
+Running:
- Windows executable wrapper (launch4j)
- Allow only one instance of OpenRocket running (RMI communication)
+- Running from command line
+
+
+UI issues:
+
- Only schedule rocket figure update instead of each time updating it
-- Reading (writing) .RKT format
- Importing flight data (file/altimeter)
-- Water rocket modelling
-- Landing scatter plots
-- Simulate other branches
+- Saving as SVG
- Implement setDefaults() method for RocketComponent
- BUG: Inner tube cluster rotation, edit with spinner arrows, slider wrong
- NAR/CNES/etc competition validity checking
-- Running from command line
- Print support
-- Saving as SVG
+
+
+Simulation:
+
+- Landing scatter plots
+- Simulate other branches
+
+
+Component support:
+
+- Screw weights for nose cones / transitions
+- Support for external pods
+- Support for tube fins
+
+
+Other:
+
+- Reading (writing) .RKT format
Refactoring tasks:
Done:
-
+-----
- Search field in motor selection dialog
- Motor selection/editing from Edit configurations dialog
- Change FreeformFinSet to throw checked exceptions
- Simulation plot dialog forces dialog one button row too high (All/None)
- Add styrofoam and depron materials
- Inform user about software updates
-
+In 0.9.5:
+- Add label to motor panel to tell current number of stages
# The OpenRocket build version
-build.version=0.9.5pre
+build.version=0.9.5
# The source of the package. When building a package for a specific
}
// Log the request
-if (strlen($orversion) > 0 || strlen($orid) > 0 || strlen($oros) > 0
- || strlen($orjava) > 0 || strlen($orcountry) > 0) {
+if ((strlen($orversion) > 0 || strlen($orid) > 0 || strlen($oros) > 0
+ || strlen($orjava) > 0 || strlen($orcountry) > 0) &&
+
+ (strlen($orversion) < 20 && strlen($orid) < 50 && strlen($oros) < 50
+ && strlen($orjava) < 50 && strlen($orcountry) < 50)) {
$file = $logfiles . gmdate("Y-m");
$line = gmdate("Y-m-d H:i:s") . ";" . $orid . ";" . $orversion .
// Set HTTP content-type header
-header("Content-type: text/plain; charset=utf-8");
+// No charset allowed for 0.9.4
+//header("Content-type: text/plain; charset=utf-8");
+header("Content-type: text/plain");
+/*
+ * Currently all old versions are handled manually.
+ * Update checking was introduced in OpenRocket 0.9.4
+ */
$version = $_GET["version"];
+$updates = "";
+
+if (preg_match("/^0\.9\.(4|5pre)/",$version)) {
+ $updates = "Version: 0.9.5\n" .
+ "5: Important bug fixes";
+}
+
+
+if (strlen($updates) == 0) {
-// No updates available
-header("HTTP/1.0 204 No Content");
+ // No updates available
+ header("HTTP/1.0 204 No Content");
+
+} else {
+
+ header("HTTP/1.0 200 OK");
+ echo $updates;
+
+}
?>
\ No newline at end of file
Steps for making a release:
-1. Update build.properties for the new version.
-2. Update ReleaseNotes
-3. Update ChangeLog
-4. ant dist
-5. Test new features (not in project directory)
-6. Copy distribution files into dists/
-7. Update Eclipse project and commit files to SVN
-8. Tag the version in SVN, URL:
- https://openrocket.svn.sourceforge.net/svnroot/openrocket/tags/Release_0.9.x
-9. Upload JAR and source distribution and ReleaseNotes to Sourceforge
- - Project Admin -> File Manager
- - create new version directory under /openrocket
- - upload JAR, ZIP and ReleaseNotes
- - select ReleaseNotes properties, set as release notes
- - select JAR properties, set release note file and default downloads
- - select ZIP properties, set release note file
-10. Update HTML: index.html (release notes) download.html (version number)
-11. Update HTML to web server:
+1. Update build.properties for the new version.
+2. Update ReleaseNotes
+3. Update ChangeLog
+4. ant dist
+5. Test new features (not in project directory)
+6. Copy distribution files into dists/
+7. Update Eclipse project and commit files to SVN
+8. Tag the version in SVN, URL:
+ https://openrocket.svn.sourceforge.net/svnroot/openrocket/tags/Release_0.9.x
+9. Update updates.php and test various versions using a different name:
+ scp updates.php plaa,openrocket@web.sourceforge.net:htdocs/actions/testupdates.php
+ java -Dopenrocket.debug.updateurl=http://openrocket.sourceforge.net/actions/testupdates.php -jar OpenRocket-0.9.4.jar
+10. Upload JAR and source distribution and ReleaseNotes to Sourceforge
+ - Project Admin -> File Manager
+ - create new version directory under /openrocket
+ - upload JAR, ZIP and ReleaseNotes
+ - select ReleaseNotes properties, set as release notes
+ - select JAR properties, set release note file and default downloads
+ - select ZIP properties, set release note file
+11. Update HTML: index.html (release notes) download.html (version number)
+12. Update HTML to web server:
scp * plaa,openrocket@web.sourceforge.net:htdocs/
-12. Test downloading from Sourceforge and web site
-13. Update build.properties to "pre" version + commit
-14. Send email about new release to openrocket-announce@lists.sourceforge.net
+13. Test downloading from Sourceforge and web site
+14. Update update.php to web server:
+ scp updates.php plaa,openrocket@web.sourceforge.net:htdocs/actions/
+15. Update build.properties to "pre" version + commit
+16. Send email about new release to openrocket-announce@lists.sourceforge.net
package net.sf.openrocket.aerodynamics;
import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
public class AerodynamicForces implements Cloneable {
try {
return (AerodynamicForces)super.clone();
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("CloneNotSupportedException?!?");
+ throw new BugException("CloneNotSupportedException?!?");
}
}
package net.sf.openrocket.aerodynamics;
+import net.sf.openrocket.util.BugException;
+
public class AtmosphericConditions implements Cloneable {
/** Specific gas constant of dry air. */
try {
return (AtmosphericConditions) super.clone();
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("BUG: CloneNotSupportedException encountered!");
+ throw new BugException("BUG: CloneNotSupportedException encountered!");
}
}
import javax.swing.event.ChangeListener;
import net.sf.openrocket.rocketcomponent.Configuration;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
import net.sf.openrocket.util.MathUtil;
cond.atmosphericConditions = atmosphericConditions.clone();
return cond;
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("BUG: clone not supported!",e);
+ throw new BugException("BUG: clone not supported!",e);
}
}
import java.util.ArrayList;
import java.util.Iterator;
+import net.sf.openrocket.util.BugException;
+
/**
* A set that contains multiple <code>Warning</code>s. When adding a
* {@link Warning} to this set, the contents is checked for a warning of the
return newSet;
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("CloneNotSupportedException occurred, report bug!",e);
+ throw new BugException("CloneNotSupportedException occurred, report bug!",e);
}
}
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.rocketcomponent.Transition;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LinearInterpolator;
import net.sf.openrocket.util.MathUtil;
return 0;
if (!(component instanceof Transition)) {
- throw new RuntimeException("Pressure calculation of unknown type: "+
+ throw new BugException("Pressure calculation of unknown type: "+
component.getComponentName());
}
import java.net.HttpURLConnection;
import java.net.URLEncoder;
+import net.sf.openrocket.util.BugException;
+
public abstract class Communicator {
protected static final String BUG_REPORT_URL;
try {
return URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
- throw new RuntimeException("Unsupported encoding UTF-8", e);
+ throw new BugException("Unsupported encoding UTF-8", e);
}
}
return (List<ComparablePair<Integer, String>>) updates.clone();
}
+ @Override
+ public String toString() {
+ return "UpdateInfo[version=" + latestVersion + "; updates=" + updates.toString() + "]";
+ }
+
}
System.getProperty("java.version")));
connection.setRequestProperty("X-OpenRocket-Country",
Communicator.encode(System.getProperty("user.country") + " " +
- Communicator.encode(System.getProperty("user.timezone"))));
+ System.getProperty("user.timezone")));
InputStream is = null;
try {
if (connection.getResponseCode() != Communicator.UPDATE_INFO_UPDATE_AVAILABLE) {
// Error communicating with server
+ System.out.println("Unknown response code: " + connection.getResponseCode());
return;
}
- if (!Communicator.UPDATE_INFO_CONTENT_TYPE.equalsIgnoreCase(
- connection.getContentType())) {
+ String contentType = connection.getContentType();
+ if (contentType == null ||
+ contentType.toLowerCase().indexOf(Communicator.UPDATE_INFO_CONTENT_TYPE) < 0) {
// Unknown response type
+ System.out.println("Unknown Content-type received:"+contentType);
return;
}
+ System.out.println("Update is available");
// Update is available, parse input
is = connection.getInputStream();
if (version == null || version.length() == 0 ||
version.equalsIgnoreCase(Prefs.getVersion())) {
// Invalid response
+ System.out.println("Invalid version received, ignoring.");
return;
}
info = new UpdateInfo(version, updates);
-
+ System.out.println("Found update: " + info);
} finally {
try {
if (is != null)
import net.sf.openrocket.material.Material;
import net.sf.openrocket.material.MaterialStorage;
import net.sf.openrocket.motor.Motor;
+import net.sf.openrocket.util.ConfigurationException;
import net.sf.openrocket.util.JarUtil;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Prefs;
MOTOR.loadDirectory(dir, ".*\\.[eE][nN][gG]$");
} catch (IOException e1) {
System.out.println("Could not read thrust curves from directory either.");
- throw new RuntimeException(e1);
+ throw new ConfigurationException("Couldn't read thrust curves from either " +
+ "JAR file or system resource directory", e1);
}
}
}
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Icons;
break;
default:
- throw new RuntimeException("EEEK!");
+ throw new BugException("illegal type="+type);
}
if (desc != null)
import net.sf.openrocket.simulation.SimulationListener;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.exception.SimulationListenerException;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
l = (SimulationListener)c.newInstance();
} catch (Exception e) {
throw new SimulationListenerException("Could not instantiate listener of " +
- "class: " + className);
+ "class: " + className, e);
}
simulator.addSimulationListener(l);
}
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("Clone not supported, BUG", e);
+ throw new BugException("Clone not supported, BUG", e);
}
}
package net.sf.openrocket.document;
+import net.sf.openrocket.util.BugException;
+
public class StorageOptions implements Cloneable {
public static final double SIMULATION_DATA_NONE = Double.POSITIVE_INFINITY;
try {
return (StorageOptions)super.clone();
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("CloneNotSupportedException?!?", e);
+ throw new BugException("CloneNotSupportedException?!?", e);
}
}
}
throw e;
} catch (IOException e) {
throw new RocketLoadException("I/O error: " + e.getMessage());
- } catch (Exception e) {
- throw new RocketLoadException("An unknown error occurred. Please report a bug.", e);
- } catch (Throwable e) {
- throw new RocketLoadException("A serious error occurred and the software may be "
- + "unstable. Save your designs and restart OpenRocket.", e);
}
}
import net.sf.openrocket.simulation.SimulationConditions;
import net.sf.openrocket.simulation.FlightEvent.Type;
import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LineStyle;
import net.sf.openrocket.util.Reflection;
constructors.put("stage", Stage.class.getConstructor(new Class<?>[0]));
} catch (NoSuchMethodException e) {
- throw new RuntimeException(
+ throw new BugException(
"Error in constructing the 'constructors' HashMap.");
}
}
try {
c = constructor.newInstance();
} catch (InstantiationException e) {
- throw new RuntimeException("Error constructing component.", e);
+ throw new BugException("Error constructing component.", e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("Error constructing component.", e);
+ throw new BugException("Error constructing component.", e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("Error constructing component.", e);
+ throw Reflection.handleInvocationTargetException(e);
}
parent.addChild(c);
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.SimulationConditions;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Pair;
import net.sf.openrocket.util.Prefs;
Reflection.Method m = Reflection.findMethod(METHOD_PACKAGE, component, METHOD_SUFFIX,
"getElements", RocketComponent.class);
if (m==null) {
- throw new RuntimeException("Unable to find saving class for component "+
+ throw new BugException("Unable to find saving class for component "+
component.getComponentName());
}
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.LineStyle;
str += " type=\"bulk\"";
break;
default:
- throw new RuntimeException("Unknown material type: " + mat.getType());
+ throw new BugException("Unknown material type: " + mat.getType());
}
return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(mat.getName()) + "</"+tag+">";
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.Reflection;
/**
try {
return (Boolean)getMethod.invoke(source);
} catch (IllegalAccessException e) {
- throw new RuntimeException("getMethod execution error for source "+source,e);
+ throw new BugException("getMethod execution error for source "+source,e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("getMethod execution error for source "+source,e);
+ throw Reflection.handleInvocationTargetException(e);
}
}
try {
setMethod.invoke(source, new Object[] { (Boolean)b });
} catch (IllegalAccessException e) {
- throw new RuntimeException("setMethod execution error for source "+source,e);
+ throw new BugException("setMethod execution error for source "+source,e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("setMethod execution error for source "+source,e);
+ throw Reflection.handleInvocationTargetException(e);
}
}
try {
return (Boolean)getEnabled.invoke(source);
} catch (IllegalAccessException e) {
- throw new RuntimeException("getEnabled execution error for source "+source,e);
+ throw new BugException("getEnabled execution error for source "+source,e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("getEnabled execution error for source "+source,e);
+ throw Reflection.handleInvocationTargetException(e);
}
}
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.Reflection;
/**
try {
return (Double)getMethod.invoke(source)*multiplier;
} catch (IllegalArgumentException e) {
- throw new RuntimeException("BUG: Unable to invoke getMethod of "+this, e);
+ throw new BugException("BUG: Unable to invoke getMethod of "+this, e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("BUG: Unable to invoke getMethod of "+this, e);
+ throw new BugException("BUG: Unable to invoke getMethod of "+this, e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("BUG: Unable to invoke getMethod of "+this, e);
+ throw Reflection.handleInvocationTargetException(e);
}
}
try {
setMethod.invoke(source, v/multiplier);
- return;
} catch (IllegalArgumentException e) {
- throw new RuntimeException("BUG: Unable to invoke setMethod of "+this, e);
+ throw new BugException("BUG: Unable to invoke setMethod of "+this, e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("BUG: Unable to invoke setMethod of "+this, e);
+ throw new BugException("BUG: Unable to invoke setMethod of "+this, e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("Setter method of "+this+" threw exception", e);
+ throw Reflection.handleInvocationTargetException(e);
}
}
try {
return (Boolean)getAutoMethod.invoke(source);
} catch (IllegalArgumentException e) {
- e.printStackTrace();
+ throw new BugException("Method call failed", e);
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ throw new BugException("Method call failed", e);
} catch (InvocationTargetException e) {
- e.printStackTrace();
+ throw Reflection.handleInvocationTargetException(e);
}
- return false; // Should not occur
}
/**
return;
}
+ lastAutomatic = auto;
try {
- lastAutomatic = auto;
setAutoMethod.invoke(source, auto);
- return;
} catch (IllegalArgumentException e) {
- e.printStackTrace();
+ throw new BugException(e);
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ throw new BugException(e);
} catch (InvocationTargetException e) {
- e.printStackTrace();
+ throw Reflection.handleInvocationTargetException(e);
}
- fireStateChanged(); // Should not occur
}
@Override
public void setSelectedItem(Object item) {
+ if (item == null) {
+ // Clear selection - huh?
+ return;
+ }
if (item instanceof String) {
if (currentValue != null)
setMethod.invoke(source, (Object)null);
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.Reflection;
public class IntegerModel implements ChangeListener {
try {
return (Integer)getMethod.invoke(source);
} catch (IllegalArgumentException e) {
- e.printStackTrace();
+ throw new BugException(e);
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ throw new BugException(e);
} catch (InvocationTargetException e) {
- e.printStackTrace();
+ throw Reflection.handleInvocationTargetException(e);
}
- return lastValue; // Should not occur
}
/**
public void setValue(int v) {
try {
setMethod.invoke(source, v);
- return;
} catch (IllegalArgumentException e) {
- e.printStackTrace();
+ throw new BugException(e);
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ throw new BugException(e);
} catch (InvocationTargetException e) {
- e.printStackTrace();
+ throw Reflection.handleInvocationTargetException(e);
}
- fireStateChanged(); // Should not occur
}
@Override
public void setSelectedItem(Object item) {
+ if (item == null) {
+ // Clear selection - huh?
+ return;
+ }
+
if (item == CUSTOM) {
// Open custom material dialog in the future, after combo box has closed
@Override
public void setSelectedItem(Object item) {
+ if (item == null) {
+ // Clear selection - huh?
+ return;
+ }
if (item == EDIT) {
// Open edit dialog in the future, after combo box has closed
return;
}
- if (!(item instanceof ID))
- return;
+ if (!(item instanceof ID)) {
+ throw new IllegalArgumentException("MotorConfigurationModel item="+item);
+ }
ID idObject = (ID) item;
config.setMotorConfigurationID(idObject.getID());
import java.util.HashMap;
import java.util.Map;
+import net.sf.openrocket.util.BugException;
+
/**
* A label of a URL that is clickable. Clicking the URL will launch the URL in
* the default browser if the Desktop class is supported.
try {
d.browse(new URI(url));
} catch (URISyntaxException e1) {
- throw new RuntimeException("BUG: Illegal URL: " + url, e1);
+ throw new BugException("BUG: Illegal URL: " + url, e1);
} catch (IOException e1) {
System.err.println("Unable to launch browser:");
e1.printStackTrace();
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Prefs;
+import net.sf.openrocket.util.Reflection;
/**
* A dialog that contains the configuration elements of one component.
try {
return (RocketComponentConfig) c.newInstance(component);
} catch (InstantiationException e) {
- throw new RuntimeException("BUG in constructor reflection",e);
+ throw new BugException("BUG in constructor reflection",e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("BUG in constructor reflection",e);
+ throw new BugException("BUG in constructor reflection",e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("BUG in constructor reflection",e);
+ throw Reflection.handleInvocationTargetException(e);
}
}
// Should never be reached, since RocketComponentConfig should catch all
// components without their own configurator.
- throw new RuntimeException("Unable to find any configurator for "+component);
+ throw new BugException("Unable to find any configurator for "+component);
}
/**
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.dialogs.MotorChooserDialog;
import net.sf.openrocket.motor.Motor;
spin.setEditor(new SpinnerEditor(spin));
panel.add(spin,"gap rel rel");
- panel.add(new JLabel("seconds"),"wrap paragraph");
+ panel.add(new JLabel("seconds"),"wrap unrel");
+ // Check stage count
+ RocketComponent c = (RocketComponent)mount;
+ c = c.getRocket();
+ int stages = c.getChildCount();
+
+ if (stages == 1) {
+ panel.add(new StyledLabel("The current design has only one stage. " +
+ "Stages can be added by clicking \"New stage\".", -1),
+ "spanx, right, wrap para");
+ } else {
+ panel.add(new StyledLabel("The current design has " + stages + " stages.", -1),
+ "skip 1, spanx, wrap para");
+ }
+
// Select etc. buttons
button = new JButton("Select motor");
((RingComponent) component).setRadialPosition(0.0);
}
});
- panel.add(button,"spanx, right");
+ panel.add(button,"spanx, right, wrap para");
+
+
+ DescriptionArea note = new DescriptionArea(2);
+ note.setText("Note: An inner tube will not affect the aerodynamics" +
+ " of the rocket even if it is located outside of the body tube.");
+ panel.add(note, "spanx, growx");
return panel;
import net.sf.openrocket.communication.BugReporter;
import net.sf.openrocket.gui.components.SelectableLabel;
import net.sf.openrocket.gui.components.StyledLabel;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.JarUtil;
import net.sf.openrocket.util.Prefs;
text = URLEncoder.encode(text, "UTF-8");
version = URLEncoder.encode(Prefs.getVersion(), "UTF-8");
} catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e);
+ throw new BugException(e);
}
import javax.swing.ListSelectionModel;
import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.JarUtil;
designs[i] = new ExampleDesign(files[i].toURI().toURL(),
name.substring(0, name.length()-4));
} catch (MalformedURLException e) {
- throw new RuntimeException(e);
+ throw new BugException(e);
}
}
return designs;
fileUrl = file.toURI().toURL();
} catch (MalformedURLException e1) {
e1.printStackTrace();
- throw new RuntimeException(e1);
+ throw new BugException(e1);
}
// Iterate over JAR entries searching for designs
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
+import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.JButton;
}
licenseText = sb.toString();
- } catch (Exception e) {
+ } catch (IOException e) {
licenseText = DEFAULT_LICENSE_TEXT;
import java.util.List;
import javax.swing.JButton;
+import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.sf.openrocket.util.Icons;
public class UpdateInfoDialog extends JDialog {
+
+ private final JCheckBox remind;
public UpdateInfoDialog(UpdateInfo info) {
super((Window)null, "OpenRocket update available", ModalityType.APPLICATION_MODAL);
"gaptop para, alignx 50%, wrap unrel");
panel.add(new URLLabel(AboutDialog.OPENROCKET_URL), "alignx 50%, wrap para");
+ remind = new JCheckBox("Remind me later");
+ remind.setToolTipText("Show this update also the next time you start OpenRocket");
+ remind.setSelected(true);
+ panel.add(remind);
+
JButton button = new JButton("Close");
button.addActionListener(new ActionListener() {
@Override
UpdateInfoDialog.this.dispose();
}
});
- panel.add(button, "right");
+ panel.add(button, "right, gapright para");
this.add(panel);
GUIUtil.setDisposableDialogOptions(this, button);
}
+
+ public boolean isReminderSelected() {
+ return remind.isSelected();
+ }
+
}
}
@Override
public void setSelectedItem(Object item) {
+ if (item == null) {
+ // Clear selection - huh?
+ return;
+ }
if (!(item instanceof Unit)) {
throw new IllegalArgumentException("Illegal argument "+item);
}
@Override
public void setSelectedItem(Object item) {
+ if (item == null) {
+ // Clear selection - huh?
+ return;
+ }
if (!(item instanceof String)) {
throw new IllegalArgumentException("Illegal argument "+item);
}
@Override
public void setSelectedItem(Object item) {
+ if (item == null) {
+ // Clear selection - huh?
+ return;
+ }
if (!(item instanceof String)) {
throw new IllegalArgumentException("Illegal argument "+item);
}
"You are running the latest version of OpenRocket.",
"No updates available", JOptionPane.INFORMATION_MESSAGE, null);
} else {
- new UpdateInfoDialog(info).setVisible(true);
+ UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
+ infoDialog.setVisible(true);
+ if (infoDialog.isReminderSelected()) {
+ Prefs.putString(Prefs.LAST_UPDATE, "");
+ } else {
+ Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
+ }
}
}
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Stage;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Icons;
import net.sf.openrocket.util.OpenFileWorker;
} else {
- throw new RuntimeException("Unknown error when opening file", e);
+ throw new BugException("Unknown error when opening file", e);
}
} catch (InterruptedException e) {
- throw new RuntimeException("EDT was interrupted", e);
+ throw new BugException("EDT was interrupted", e);
}
if (doc == null) {
- throw new RuntimeException("BUG: Document loader returned null");
+ throw new BugException("BUG: Document loader returned null");
}
e.getMessage() }, "Saving failed", JOptionPane.ERROR_MESSAGE);
return false;
} else {
- throw new RuntimeException("Unknown error when saving file", e);
+ throw new BugException("Unknown error when saving file", e);
}
} catch (InterruptedException e) {
- throw new RuntimeException("EDT was interrupted", e);
+ throw new BugException("EDT was interrupted", e);
}
return saved;
if (info != null && info.getLatestVersion() != null &&
!current.equals(info.getLatestVersion()) &&
!last.equals(info.getLatestVersion())) {
- Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
- new UpdateInfoDialog(info).setVisible(true);
+
+ UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
+ infoDialog.setVisible(true);
+ if (infoDialog.isReminderSelected()) {
+ Prefs.putString(Prefs.LAST_UPDATE, "");
+ } else {
+ Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
+ }
}
}
count--;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import javax.swing.Icon;
import javax.swing.JButton;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
import net.sf.openrocket.rocketcomponent.TubeCoupler;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Pair;
import net.sf.openrocket.util.Prefs;
+import net.sf.openrocket.util.Reflection;
/**
* A component that contains addition buttons to add different types of rocket components
RocketComponent component;
try {
component = (RocketComponent)constructor.newInstance();
- } catch (Exception e) {
- throw new RuntimeException("Could not construct new instance of class "+
+ } catch (InstantiationException e) {
+ throw new BugException("Could not construct new instance of class "+
constructor,e);
+ } catch (IllegalAccessException e) {
+ throw new BugException("Could not construct new instance of class "+
+ constructor,e);
+ } catch (InvocationTargetException e) {
+ throw Reflection.handleInvocationTargetException(e);
}
// Next undo position is set by opening the configuration dialog
} else if (t instanceof Exception) {
+ // TODO: MEDIUM: Check the exception handling here...
+
DetailDialog.showDetailedMessageDialog(SimulationRunDialog.this,
new Object[] {
"An exception occurred during the simulation:",
package net.sf.openrocket.gui.plot;
+import net.sf.openrocket.util.BugException;
+
public class Axis implements Cloneable {
private double minValue = Double.NaN;
return (Axis) super.clone();
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("BUG! Could not clone().");
+ throw new BugException("BUG! Could not clone().");
}
}
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.FlightDataBranch.Type;
import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Pair;
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("BUG! Could not clone().");
+ throw new BugException("BUG! Could not clone().");
}
}
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Pair;
double t2 = time.get(tindex+1);
if ((t1 > t) || (t2 < t)) {
- throw new RuntimeException("BUG: t1="+t1+" t2="+t2+" t="+t);
+ throw new BugException("BUG: t1="+t1+" t2="+t2+" t="+t);
}
if (MathUtil.equals(t1, t2)) {
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LineStyle;
import net.sf.openrocket.util.MathUtil;
break;
default:
- throw new RuntimeException("Unknown figure type = "+type);
+ throw new BugException("Unknown figure type = "+type);
}
if (m == null) {
return new Rectangle2D.Double(-maxR,-maxR,2*maxR,2*maxR);
default:
- throw new RuntimeException("Illegal figure type = "+type);
+ throw new BugException("Illegal figure type = "+type);
}
}
import net.sf.openrocket.unit.Tick;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.BugException;
public void setFitting(boolean fit) {
if (fit && !allowFit) {
- throw new RuntimeException("Attempting to fit figure not allowing fit.");
+ throw new BugException("Attempting to fit figure not allowing fit.");
}
this.fit = fit;
if (fit) {
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
avgTime *= time/(DIVISIONS+1);
if (Double.isNaN(avgTime))
- throw new RuntimeException("Calculated avg. time is NaN for motor "+this);
+ throw new BugException("Calculated avg. time is NaN for motor "+this);
}
return avgTime;
avgThrust /= points;
if (Double.isNaN(avgThrust))
- throw new RuntimeException("Calculated average thrust is NaN for motor "+this);
+ throw new BugException("Calculated average thrust is NaN for motor "+this);
}
return avgThrust;
}
}
if (Double.isNaN(totalImpulse))
- throw new RuntimeException("Calculated total impulse is NaN for motor "+this);
+ throw new BugException("Calculated total impulse is NaN for motor "+this);
}
return totalImpulse;
}
// Component can be parentless if disattached from rocket
if (this.getParent() != null) {
for (RocketComponent sibling: this.getParent().getChildren()) {
- if (!(sibling instanceof RadialParent)) // Excludes itself
+ /*
+ * Only InnerTubes are considered when determining the automatic
+ * inner radius (for now).
+ */
+ if (!(sibling instanceof InnerTube)) // Excludes itself
continue;
double pos1 = this.toRelative(Coordinate.NUL, sibling)[0].x;
if (pos2 < 0 || pos1 > sibling.getLength())
continue;
- // TODO: CRITICAL: ClassCastException below:
innerRadius = Math.max(innerRadius, ((InnerTube)sibling).getOuterRadius());
}
innerRadius = Math.min(innerRadius, getOuterRadius());
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
rocket.addComponentChangeListener(config);
return config;
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("BUG: clone not supported!",e);
+ throw new BugException("BUG: clone not supported!",e);
}
}
import javax.swing.event.ChangeListener;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LineStyle;
try {
clone = (RocketComponent)this.clone();
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("CloneNotSupportedException encountered, " +
+ throw new BugException("CloneNotSupportedException encountered, " +
"report a bug!",e);
}
break;
default:
- throw new RuntimeException("Unknown relative positioning type of component"+
+ throw new BugException("Unknown relative positioning type of component"+
component+": "+component.relativePosition);
}
return null;
int pos = parent.getChildPosition(this);
if (pos < 0) {
- throw new IllegalStateException("Inconsistent internal state: " +
- "this="+this+" parent="+parent+" parent.children="+
- parent.children.toString());
+ StringBuffer sb = new StringBuffer();
+ sb.append("Inconsistent internal state: ");
+ sb.append("this=").append(this).append('[').append(System.identityHashCode(this)).append(']');
+ sb.append(" parent.children=[");
+ for (int i=0; i < parent.children.size(); i++) {
+ RocketComponent c = parent.children.get(i);
+ sb.append(c).append('[').append(System.identityHashCode(c)).append(']');
+ if (i < parent.children.size()-1)
+ sb.append(", ");
+ }
+ throw new IllegalStateException(sb.toString());
}
assert(pos >= 0);
if (pos == 0)
package net.sf.openrocket.rocketcomponent;
import net.sf.openrocket.material.Material;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Prefs;
public void setMaterial(Material m) {
if (m.getType() != Material.Type.LINE)
- throw new RuntimeException("Attempting to set non-linear material.");
+ throw new BugException("Attempting to set non-linear material.");
if (material.equals(m))
return;
this.material = m;
import net.sf.openrocket.aerodynamics.AtmosphericModel;
import net.sf.openrocket.aerodynamics.ExtendedISAModel;
import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
import net.sf.openrocket.util.MathUtil;
copy.listeners = new ArrayList<ChangeListener>();
return copy;
} catch (CloneNotSupportedException e) {
- throw new RuntimeException(e);
+ throw new BugException(e);
}
}
import net.sf.openrocket.aerodynamics.WindSimulator;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
try {
return (SimulationStatus) super.clone();
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("BUG: CloneNotSupportedException?!?",e);
+ throw new BugException("BUG: CloneNotSupportedException?!?",e);
}
}
}
--- /dev/null
+package net.sf.openrocket.util;
+
+/**
+ * Thrown when a bug is noticed.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class BugException extends FatalException {
+
+ public BugException() {
+ }
+
+ public BugException(String message) {
+ super(message);
+ }
+
+ public BugException(Throwable cause) {
+ super(cause);
+ }
+
+ public BugException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.util;
+
+/**
+ * An exception to be thrown when a fatal problem with the environment
+ * is encountered (for example some file cannot be found).
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ConfigurationException extends FatalException {
+
+ public ConfigurationException() {
+ }
+
+ public ConfigurationException(String message) {
+ super(message);
+ }
+
+ public ConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ public ConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.util;
+
+/**
+ * A superclass for all types of fatal error conditions. This class is
+ * abstract so only subclasses can be used.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ * @see BugException
+ * @see ConfigurationException
+ */
+public abstract class FatalException extends RuntimeException {
+
+ public FatalException() {
+ }
+
+ public FatalException(String message) {
+ super(message);
+ }
+
+ public FatalException(Throwable cause) {
+ super(cause);
+ }
+
+ public FatalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
other.sortMap = (TreeMap<Double,Double>)this.sortMap.clone();
return other;
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("CloneNotSupportedException?!",e);
+ throw new BugException("CloneNotSupportedException?!",e);
}
}
return ((u != null) ? u.hashCode() : 0) + ((v != null) ? v.hashCode() : 0);
}
+
+ @Override
+ public String toString() {
+ return "[" + u + ";" + v + "]";
+ }
+
}
root.node(NODENAME).removeNode();
}
} catch (BackingStoreException e) {
- throw new RuntimeException("Unable to clear preference node",e);
+ throw new BugException("Unable to clear preference node",e);
}
}
PREFNODE = root.node(NODENAME);
try {
return (Quaternion) super.clone();
} catch (CloneNotSupportedException e) {
- throw new RuntimeException("CloneNotSupportedException encountered");
+ throw new BugException("CloneNotSupportedException encountered");
}
}
try {
return method.invoke(obj, args);
} catch (IllegalArgumentException e) {
- throw new RuntimeException("Error while invoking method '"+method+"'. "+
+ throw new BugException("Error while invoking method '"+method+"'. "+
"Please report this as a bug.",e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("Error while invoking method '"+method+"'. "+
+ throw new BugException("Error while invoking method '"+method+"'. "+
"Please report this as a bug.",e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("Error while invoking method '"+method+"'. "+
- "Please report this as a bug.",e);
+ throw Reflection.handleInvocationTargetException(e);
}
}
/**
}
+ /**
+ * Handles an InvocationTargetException gracefully. If the cause is an unchecked
+ * exception it is thrown, otherwise it is encapsulated in a BugException.
+ * <p>
+ * This method has a return type of Error in order to allow writing code like:
+ * <pre>throw Reflection.handleInvocationTargetException(e)</pre>
+ * This allows the compiler verifying that the call will never succeed correctly
+ * and ending that branch of execution.
+ *
+ * @param e the InvocationTargetException that occurred (not null).
+ * @return never returns normally.
+ */
+ public static Error handleInvocationTargetException(InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ throw new BugException("BUG: InvocationTargetException without cause", e);
+ }
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException)cause;
+ }
+ if (cause instanceof Error) {
+ throw (Error)cause;
+ }
+ throw new BugException("InvocationTargetException occurred", cause);
+ }
+
+
+
/**
* Throws an exception if method not found.
*/
Reflection.Method m = findMethod(ROCKETCOMPONENT_PACKAGE, componentClass,
"", method, params);
if (m == null) {
- throw new RuntimeException("Could not find method for componentClass="
+ throw new BugException("Could not find method for componentClass="
+componentClass+" method="+method);
}
return m;
}
} catch (ClassNotFoundException ignore) {
} catch (IllegalArgumentException e) {
- throw new RuntimeException("Construction of "+name+" failed",e);
+ throw new BugException("Construction of "+name+" failed",e);
} catch (InstantiationException e) {
- throw new RuntimeException("Construction of "+name+" failed",e);
+ throw new BugException("Construction of "+name+" failed",e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("Construction of "+name+" failed",e);
+ throw new BugException("Construction of "+name+" failed",e);
} catch (InvocationTargetException e) {
- throw new RuntimeException("Construction of "+name+" failed",e);
+ throw Reflection.handleInvocationTargetException(e);
}
currentclass = currentclass.getSuperclass();
}
- throw new RuntimeException("Suitable constructor for component "+component+
+ throw new BugException("Suitable constructor for component "+component+
" not found");
}
}
e.getMessage() }, "Saving failed", JOptionPane.ERROR_MESSAGE);
return false;
} else {
- throw new RuntimeException("Unknown error when saving file", e);
+ throw new BugException("Unknown error when saving file", e);
}
} catch (InterruptedException e) {
- throw new RuntimeException("EDT was interrupted", e);
+ throw new BugException("EDT was interrupted", e);
}
return true;
import java.util.List;
import java.util.Map;
+import net.sf.openrocket.util.BugException;
+
public class HttpURLConnectionMock extends HttpURLConnection {
private static final URL MOCK_URL;
try {
MOCK_URL = new URL("http://localhost/");
} catch (MalformedURLException e) {
- throw new RuntimeException(e);
+ throw new BugException(e);
}
}
--- /dev/null
+package net.sf.openrocket.util;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Test;
+
+public class ReflectionTest {
+
+ @Test
+ public void textHandleInvocationTargetException() {
+ Throwable cause = null;
+
+ try {
+ cause = new InvocationTargetException(null);
+ Reflection.handleInvocationTargetException((InvocationTargetException)cause);
+ fail();
+ } catch (BugException e) {
+ assertTrue(cause == e.getCause());
+ }
+
+ try {
+ cause = new IllegalStateException("Test");
+ Reflection.handleInvocationTargetException(new InvocationTargetException(cause));
+ fail();
+ } catch (IllegalStateException e) {
+ assertTrue(cause == e);
+ }
+
+ try {
+ cause = new AbstractMethodError();
+ Reflection.handleInvocationTargetException(new InvocationTargetException(cause));
+ fail();
+ } catch (AbstractMethodError e) {
+ assertTrue(cause == e);
+ }
+
+ try {
+ cause = new IOException();
+ Reflection.handleInvocationTargetException(new InvocationTargetException(cause));
+ fail();
+ } catch (BugException e) {
+ assertTrue(cause == e.getCause());
+ }
+
+ }
+
+}