updates for 0.9.4

This commit is contained in:
Sampo Niskanen 2009-10-04 15:46:32 +00:00
parent e3b951cb9e
commit 88139aadac
52 changed files with 1775 additions and 501 deletions

View File

@ -1,13 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="src"/> <classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="extra-src"/> <classpathentry kind="src" path="src-extra"/>
<classpathentry kind="src" path="test"/> <classpathentry kind="src" path="test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="/home/sampo/Projects/OpenRocket/lib/miglayout15-swing.jar"/> <classpathentry kind="lib" path="/home/sampo/Projects/OpenRocket/lib/miglayout15-swing.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JCommon 1.0.16"/> <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JCommon 1.0.16"/>
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JFreeChart 1.0.13"/> <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JFreeChart 1.0.13"/>
<classpathentry kind="lib" path="/home/sampo/Projects/OpenRocket/extra-lib/RXTXcomm.jar"/> <classpathentry kind="lib" path="lib-test/hamcrest-core-1.1.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="lib" path="lib-test/hamcrest-library-1.1.jar"/>
<classpathentry kind="lib" path="lib-test/jmock-2.5.1.jar"/>
<classpathentry kind="lib" path="lib-test/jmock-junit4-2.5.1.jar"/>
<classpathentry kind="lib" path="lib-extra/RXTXcomm.jar"/>
<classpathentry kind="lib" path="lib-test/junit-4.7.jar"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
</classpath> </classpath>

View File

@ -1,3 +1,12 @@
2009-10-04 Sampo Niskanen
* [BUG] Fixed too high configuration dialogs
2009-10-03 Sampo Niskanen
* Added debug information to ant build file compilation
* Implemented update information fetching (client side)
2009-09-26 Sampo Niskanen 2009-09-26 Sampo Niskanen
* Implemented custom material editing * Implemented custom material editing

13
TODO
View File

@ -4,7 +4,6 @@ Feature roadmap for OpenRocket 1.0
Must-have: Must-have:
- Allow editing user-defined materials
- Go through thrust curves and correct errors - Go through thrust curves and correct errors
- Add styrofoam and depron materials - Add styrofoam and depron materials
@ -12,12 +11,13 @@ Must-have:
Bugs: Bugs:
- Simulation plot dialog forces dialog one button row too high (All/None) - Simulation plot dialog forces dialog one button row too high (All/None)
- All configuration dialogs too high - Unit tests fail from ant script
Maybe: Maybe:
- Windows executable wrapper (launch4j) - Windows executable wrapper (launch4j)
- Inform user about software updates
Postponed: Postponed:
@ -31,18 +31,21 @@ Postponed:
- Simulate other branches - Simulate other branches
- Implement setDefaults() method for RocketComponent - Implement setDefaults() method for RocketComponent
- BUG: Inner tube cluster rotation, edit with spinner arrows, slider wrong - BUG: Inner tube cluster rotation, edit with spinner arrows, slider wrong
- Inform user about software updates
- Reading thrust curves from external directory - Reading thrust curves from external directory
- NAR/CNES/etc competition validity checking - NAR/CNES/etc competition validity checking
- Running from command line
- Print support
- Saving as SVG
Refactoring tasks: Refactoring tasks:
- Move startup class to src14 directory, remove reflection
- Remove database etc. initialization from class initialization, - Remove database etc. initialization from class initialization,
create separate set of test motors create separate set of test motors
- Extract event rules and data saving from Simulator into listeners - Extract event rules and data saving from Simulator into listeners
- Change SimulationStatus to include methods for obtaining basic - Change SimulationStatus to include methods for obtaining basic
position (maybe even an interface) position (maybe even change to an interface, implements Cloneable)
- Change Motor (immutable) to be a factory of MotorInstance (stateful) - Change Motor (immutable) to be a factory of MotorInstance (stateful)
@ -74,4 +77,6 @@ In 0.9.4:
- Save file as oldest OpenRocket format possible (for 0.9.4) - Save file as oldest OpenRocket format possible (for 0.9.4)
- Non-exception bug handling - Non-exception bug handling
- JTree text is cropped unnecessarily - JTree text is cropped unnecessarily
- Allow editing user-defined materials
- [BUG] All configuration dialogs too high

View File

@ -10,3 +10,7 @@ build.version=0.9.4pre
build.source=default build.source=default
# Whether checking for updates is enabled by default.
build.checkupdates=true

View File

@ -33,9 +33,11 @@
<path id="test-classpath"> <path id="test-classpath">
<path refid="classpath"/> <path refid="classpath"/>
<pathelement location="${basedir}"/>
<pathelement location="${build-test.dir}"/> <pathelement location="${build-test.dir}"/>
<pathelement location="${classes.dir}"/> <pathelement location="${classes.dir}"/>
<pathelement location="${ant.library.dir}/junit4.jar"/> <!-- <pathelement location="${ant.library.dir}/junit4.jar"/> -->
<pathelement location="lib-test/junit-4.7.jar"/>
</path> </path>
@ -51,9 +53,9 @@
<target name="build"> <target name="build">
<mkdir dir="${classes.dir}"/> <mkdir dir="${classes.dir}"/>
<echo>Compiling main classes</echo> <echo>Compiling main classes</echo>
<javac srcdir="${src.dir}" destdir="${classes.dir}" excludes="${main-dir}/*" classpathref="classpath"/> <javac debug="true" srcdir="${src.dir}" destdir="${classes.dir}" excludes="${main-dir}/*" classpathref="classpath"/>
<echo>Compiling startup classes</echo> <echo>Compiling startup classes</echo>
<javac srcdir="${src.dir}/${main-dir}" destdir="${classes.dir}" source="1.4" classpathref="classpath"/> <javac debug="true" srcdir="${src.dir}/${main-dir}" destdir="${classes.dir}" source="1.4" classpathref="classpath"/>
<copy file="build.properties" todir="${dist.dir}"/> <copy file="build.properties" todir="${dist.dir}"/>
</target> </target>
@ -150,7 +152,7 @@ ${criticaltodos}</fail>
<target name="unittest" description="Execute unit tests" depends="build"> <target name="unittest" description="Execute unit tests" depends="build">
<echo>Building unit tests</echo> <echo>Building unit tests</echo>
<mkdir dir="${build-test.dir}"/> <mkdir dir="${build-test.dir}"/>
<javac srcdir="${src-test.dir}" destdir="${build-test.dir}" classpathref="test-classpath"/> <javac debug="true" srcdir="${src-test.dir}" destdir="${build-test.dir}" classpathref="test-classpath"/>
<echo>Running unit tests</echo> <echo>Running unit tests</echo>
<mkdir dir="tmp/rawtestoutput"/> <mkdir dir="tmp/rawtestoutput"/>

Binary file not shown.

Binary file not shown.

BIN
lib-test/jmock-2.5.1.jar Normal file

Binary file not shown.

Binary file not shown.

BIN
lib-test/junit-4.7.jar Normal file

Binary file not shown.

View File

@ -0,0 +1,58 @@
package net.sf.openrocket.communication;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import net.sf.openrocket.util.Prefs;
public class BugReporter extends Communicator {
// Inhibit instantiation
private BugReporter() {
}
/**
* Send the provided report to the OpenRocket bug report URL. If the connection
* fails or the server does not respond with the correct response code, an
* exception is thrown.
*
* @param report the report to send.
* @throws IOException if an error occurs while connecting to the server or
* the server responds with a wrong response code.
*/
public static void sendBugReport(String report) throws IOException {
HttpURLConnection connection = connectionSource.getConnection(BUG_REPORT_URL);
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setRequestProperty("X-OpenRocket-Version", encode(Prefs.getVersion()));
String post;
post = (VERSION_PARAM + "=" + encode(Prefs.getVersion())
+ "&" + BUG_REPORT_PARAM + "=" + encode(report));
OutputStreamWriter wr = null;
try {
// Send post information
connection.setDoOutput(true);
wr = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
wr.write(post);
wr.flush();
if (connection.getResponseCode() != BUG_REPORT_RESPONSE_CODE) {
throw new IOException("Server responded with code " +
connection.getResponseCode() + ", expecting " + BUG_REPORT_RESPONSE_CODE);
}
} finally {
if (wr != null)
wr.close();
connection.disconnect();
}
}
}

View File

@ -1,262 +0,0 @@
package net.sf.openrocket.communication;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import net.sf.openrocket.util.ComparablePair;
import net.sf.openrocket.util.Prefs;
public class Communication {
private static final String BUG_REPORT_URL =
"http://openrocket.sourceforge.net/actions/reportbug";
private static final String UPDATE_INFO_URL =
"http://openrocket.sourceforge.net/actions/updates";
private static final String VERSION_PARAM = "version";
private static final String BUG_REPORT_PARAM = "content";
private static final int BUG_REPORT_RESPONSE_CODE = HttpURLConnection.HTTP_ACCEPTED;
private static final int CONNECTION_TIMEOUT = 10000; // in milliseconds
private static final int UPDATE_INFO_UPDATE_AVAILABLE = HttpURLConnection.HTTP_OK;
private static final int UPDATE_INFO_NO_UPDATE_CODE = HttpURLConnection.HTTP_NO_CONTENT;
private static final String UPDATE_INFO_CONTENT_TYPE = "text/plain";
private static UpdateInfoFetcher fetcher = null;
/**
* Send the provided report to the OpenRocket bug report URL. If the connection
* fails or the server does not respond with the correct response code, an
* exception is thrown.
*
* @param report the report to send.
* @throws IOException if an error occurs while connecting to the server or
* the server responds with a wrong response code.
*/
public static void sendBugReport(String report) throws IOException {
URL url = new URL(BUG_REPORT_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setRequestProperty("X-OpenRocket-Version", encode(Prefs.getVersion()));
String post;
post = (VERSION_PARAM + "=" + encode(Prefs.getVersion())
+ "&" + BUG_REPORT_PARAM + "=" + encode(report));
OutputStreamWriter wr = null;
try {
// Send post information
connection.setDoOutput(true);
wr = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
wr.write(post);
wr.flush();
if (connection.getResponseCode() != BUG_REPORT_RESPONSE_CODE) {
throw new IOException("Server responded with code " +
connection.getResponseCode() + ", expecting " + BUG_REPORT_RESPONSE_CODE);
}
} finally {
if (wr != null)
wr.close();
connection.disconnect();
}
}
/**
* Start an asynchronous task that will fetch information about the latest
* OpenRocket version. This will overwrite any previous fetching operation.
*/
public static void startFetchUpdateInfo() {
fetcher = new UpdateInfoFetcher();
fetcher.start();
}
/**
* Check whether the update info fetching is still in progress.
*
* @return <code>true</code> if the communication is still in progress.
*/
public static boolean isFetchUpdateInfoRunning() {
if (fetcher == null) {
throw new IllegalStateException("startFetchUpdateInfo() has not been called");
}
return fetcher.isAlive();
}
/**
* Retrieve the result of the background update info fetcher. This method returns
* the result of the previous call to {@link #startFetchUpdateInfo()}. It must be
* called before calling this method.
* <p>
* This method will return <code>null</code> if the info fetcher is still running or
* if it encountered a problem in communicating with the server. The difference can
* be checked using {@link #isFetchUpdateInfoRunning()}.
*
* @return the update result, or <code>null</code> if the fetching is still in progress
* or an error occurred while communicating with the server.
* @throws IllegalStateException if {@link #startFetchUpdateInfo()} has not been called.
*/
public static UpdateInfo getUpdateInfo() {
if (fetcher == null) {
throw new IllegalStateException("startFetchUpdateInfo() has not been called");
}
return fetcher.info;
}
/**
* Parse the data received from the server.
*
* @param r the Reader from which to read.
* @return an UpdateInfo construct, or <code>null</code> if the data was invalid.
* @throws IOException if an I/O exception occurs.
*/
/* package-private */
static UpdateInfo parseUpdateInput(Reader r) throws IOException {
BufferedReader reader;
if (r instanceof BufferedReader) {
reader = (BufferedReader)r;
} else {
reader = new BufferedReader(r);
}
String version = null;
ArrayList<ComparablePair<Integer,String>> updates =
new ArrayList<ComparablePair<Integer,String>>();
String str = reader.readLine();
while (str != null) {
if (str.matches("^Version: *[0-9]+\\.[0-9]+\\.[0-9]+[a-zA-Z0-9.-]* *$")) {
version = str.substring(8).trim();
} else if (str.matches("^[0-9]+:\\p{Print}+$")) {
int index = str.indexOf(':');
int value = Integer.parseInt(str.substring(0, index));
String desc = str.substring(index+1).trim();
if (!desc.equals("")) {
updates.add(new ComparablePair<Integer,String>(value, desc));
}
}
// Ignore anything else
str = reader.readLine();
}
if (version != null) {
return new UpdateInfo(version, updates);
} else {
return null;
}
}
private static class UpdateInfoFetcher extends Thread {
private volatile UpdateInfo info = null;
@Override
public void run() {
try {
doConnection();
} catch (IOException e) {
return;
}
}
private void doConnection() throws IOException {
URL url;
url = new URL(UPDATE_INFO_URL + "?" + VERSION_PARAM + "=" +
encode(Prefs.getVersion()));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("GET");
connection.setUseCaches(false);
connection.setRequestProperty("X-OpenRocket-Version", encode(Prefs.getVersion()));
connection.setRequestProperty("X-OpenRocket-ID", encode(Prefs.getUniqueID()));
connection.setRequestProperty("X-OpenRocket-OS", encode(
System.getProperty("os.name") + " " + System.getProperty("os.arch")));
connection.setRequestProperty("X-OpenRocket-Java", encode(
System.getProperty("java.vendor") + " " + System.getProperty("java.version")));
connection.setRequestProperty("X-OpenRocket-Country", encode(
System.getProperty("user.country")));
InputStream is = null;
try {
connection.connect();
if (connection.getResponseCode() == UPDATE_INFO_NO_UPDATE_CODE) {
// No updates are available
info = new UpdateInfo();
return;
}
if (connection.getResponseCode() != UPDATE_INFO_UPDATE_AVAILABLE) {
// Error communicating with server
return;
}
if (!UPDATE_INFO_CONTENT_TYPE.equalsIgnoreCase(connection.getContentType())) {
// Unknown response type
return;
}
// Update is available, parse input
is = connection.getInputStream();
String encoding = connection.getContentEncoding();
if (encoding == null)
encoding = "UTF-8";
BufferedReader reader = new BufferedReader(new InputStreamReader(is, encoding));
} finally {
if (is != null)
is.close();
connection.disconnect();
}
}
}
private static String encode(String str) {
if (str == null)
return "null";
try {
return URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported encoding UTF-8", e);
}
}
}

View File

@ -0,0 +1,72 @@
package net.sf.openrocket.communication;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
public abstract class Communicator {
protected static final String BUG_REPORT_URL;
protected static final String UPDATE_INFO_URL;
static {
String url;
url = System.getProperty("openrocket.debug.bugurl");
if (url == null)
url = "http://openrocket.sourceforge.net/actions/reportbug";
BUG_REPORT_URL = url;
url = System.getProperty("openrocket.debug.updateurl");
if (url == null)
url = "http://openrocket.sourceforge.net/actions/updates";
UPDATE_INFO_URL = url;
}
protected static final String VERSION_PARAM = "version";
protected static final String BUG_REPORT_PARAM = "content";
protected static final int BUG_REPORT_RESPONSE_CODE = HttpURLConnection.HTTP_ACCEPTED;
protected static final int CONNECTION_TIMEOUT = 10000; // in milliseconds
protected static final int UPDATE_INFO_UPDATE_AVAILABLE = HttpURLConnection.HTTP_OK;
protected static final int UPDATE_INFO_NO_UPDATE_CODE = HttpURLConnection.HTTP_NO_CONTENT;
protected static final String UPDATE_INFO_CONTENT_TYPE = "text/plain";
// Limit the number of bytes that can be read from the server
protected static final int MAX_INPUT_BYTES = 20000;
protected static ConnectionSource connectionSource = new DefaultConnectionSource();
/**
* Set the source of the network connections. This can be used for unit testing.
* By default the source is a DefaultConnectionSource.
*
* @param source the source of the connections.
*/
public static void setConnectionSource(ConnectionSource source) {
connectionSource = source;
}
/**
* URL-encode the specified string in UTF-8 encoding.
*
* @param str the string to encode (null ok)
* @return the encoded string or "null"
*/
public static String encode(String str) {
if (str == null)
return "null";
try {
return URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported encoding UTF-8", e);
}
}
}

View File

@ -0,0 +1,21 @@
package net.sf.openrocket.communication;
import java.io.IOException;
import java.net.HttpURLConnection;
/**
* A source for network connections. This interface exists to enable unit testing.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public interface ConnectionSource {
/**
* Return a connection to the specified url.
* @param url the URL to connect to.
* @return the corresponding HttpURLConnection
* @throws IOException if an IOException occurs
*/
public HttpURLConnection getConnection(String url) throws IOException;
}

View File

@ -0,0 +1,21 @@
package net.sf.openrocket.communication;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Default implementation of ConnectionSource, which simply opens a new
* HttpURLConnection from a URL object.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class DefaultConnectionSource implements ConnectionSource {
@Override
public HttpURLConnection getConnection(String urlString) throws IOException {
URL url = new URL(urlString);
return (HttpURLConnection) url.openConnection();
}
}

View File

@ -0,0 +1,226 @@
package net.sf.openrocket.communication;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import net.sf.openrocket.util.ComparablePair;
import net.sf.openrocket.util.LimitedInputStream;
import net.sf.openrocket.util.Prefs;
public class UpdateInfoRetriever {
private UpdateInfoFetcher fetcher = null;
/**
* Start an asynchronous task that will fetch information about the latest
* OpenRocket version. This will overwrite any previous fetching operation.
* This call will return immediately.
*/
public void start() {
fetcher = new UpdateInfoFetcher();
fetcher.setDaemon(true);
fetcher.start();
}
/**
* Check whether the update info fetching is still in progress.
*
* @return <code>true</code> if the communication is still in progress.
*/
public boolean isRunning() {
if (fetcher == null) {
throw new IllegalStateException("startFetchUpdateInfo() has not been called");
}
return fetcher.isAlive();
}
/**
* Retrieve the result of the background update info fetcher. This method returns
* the result of the previous call to {@link #start()}. It must be
* called before calling this method.
* <p>
* This method will return <code>null</code> if the info fetcher is still running or
* if it encountered a problem in communicating with the server. The difference can
* be checked using {@link #isRunning()}.
*
* @return the update result, or <code>null</code> if the fetching is still in progress
* or an error occurred while communicating with the server.
* @throws IllegalStateException if {@link #start()} has not been called.
*/
public UpdateInfo getUpdateInfo() {
if (fetcher == null) {
throw new IllegalStateException("start() has not been called");
}
return fetcher.info;
}
/**
* Parse the data received from the server.
*
* @param r the Reader from which to read.
* @return an UpdateInfo construct, or <code>null</code> if the data was invalid.
* @throws IOException if an I/O exception occurs.
*/
/* package-private */
static UpdateInfo parseUpdateInput(Reader r) throws IOException {
BufferedReader reader;
if (r instanceof BufferedReader) {
reader = (BufferedReader)r;
} else {
reader = new BufferedReader(r);
}
String version = null;
ArrayList<ComparablePair<Integer,String>> updates =
new ArrayList<ComparablePair<Integer,String>>();
String str = reader.readLine();
while (str != null) {
if (str.matches("^Version: *[0-9]+\\.[0-9]+\\.[0-9]+[a-zA-Z0-9.-]* *$")) {
version = str.substring(8).trim();
} else if (str.matches("^[0-9]+:\\p{Print}+$")) {
int index = str.indexOf(':');
int value = Integer.parseInt(str.substring(0, index));
String desc = str.substring(index+1).trim();
if (!desc.equals("")) {
updates.add(new ComparablePair<Integer,String>(value, desc));
}
}
// Ignore anything else
str = reader.readLine();
}
if (version != null) {
return new UpdateInfo(version, updates);
} else {
return null;
}
}
/**
* An asynchronous task that fetches and parses the update info.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
private class UpdateInfoFetcher extends Thread {
private volatile UpdateInfo info = null;
@Override
public void run() {
try {
doConnection();
} catch (IOException e) {
return;
}
}
private void doConnection() throws IOException {
String url = Communicator.UPDATE_INFO_URL + "?" + Communicator.VERSION_PARAM + "="
+ Communicator.encode(Prefs.getVersion());
HttpURLConnection connection = Communicator.connectionSource.getConnection(url);
connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("GET");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setRequestProperty("X-OpenRocket-Version",
Communicator.encode(Prefs.getVersion()));
connection.setRequestProperty("X-OpenRocket-ID",
Communicator.encode(Prefs.getUniqueID()));
connection.setRequestProperty("X-OpenRocket-OS",
Communicator.encode(System.getProperty("os.name") + " " +
System.getProperty("os.arch")));
connection.setRequestProperty("X-OpenRocket-Java",
Communicator.encode(System.getProperty("java.vendor") + " " +
System.getProperty("java.version")));
connection.setRequestProperty("X-OpenRocket-Country",
Communicator.encode(System.getProperty("user.country") + " " +
Communicator.encode(System.getProperty("user.timezone"))));
InputStream is = null;
try {
connection.connect();
if (connection.getResponseCode() == Communicator.UPDATE_INFO_NO_UPDATE_CODE) {
// No updates are available
info = new UpdateInfo();
return;
}
if (connection.getResponseCode() != Communicator.UPDATE_INFO_UPDATE_AVAILABLE) {
// Error communicating with server
return;
}
if (!Communicator.UPDATE_INFO_CONTENT_TYPE.equalsIgnoreCase(
connection.getContentType())) {
// Unknown response type
return;
}
// Update is available, parse input
is = connection.getInputStream();
is = new LimitedInputStream(is, Communicator.MAX_INPUT_BYTES);
String encoding = connection.getContentEncoding();
if (encoding == null || encoding.equals(""))
encoding = "UTF-8";
BufferedReader reader = new BufferedReader(new InputStreamReader(is, encoding));
String version = null;
ArrayList<ComparablePair<Integer, String>> updates =
new ArrayList<ComparablePair<Integer, String>>();
String line = reader.readLine();
while (line != null) {
if (line.matches("^Version:[a-zA-Z0-9._ -]{1,30}$")) {
version = line.substring(8).trim();
} else if (line.matches("^[0-9]{1,9}:\\P{Cntrl}{1,300}$")) {
String[] split = line.split(":", 2);
int n = Integer.parseInt(split[0]);
updates.add(new ComparablePair<Integer,String>(n, split[1].trim()));
}
// Ignore line otherwise
line = reader.readLine();
}
// Check version input
if (version == null || version.length() == 0 ||
version.equalsIgnoreCase(Prefs.getVersion())) {
// Invalid response
return;
}
info = new UpdateInfo(version, updates);
} finally {
try {
if (is != null)
is.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -79,6 +79,7 @@ public class Databases {
BULK_MATERIAL.add(new Material.Bulk("Cardboard", 680, false)); BULK_MATERIAL.add(new Material.Bulk("Cardboard", 680, false));
BULK_MATERIAL.add(new Material.Bulk("Carbon fiber", 1780, false)); BULK_MATERIAL.add(new Material.Bulk("Carbon fiber", 1780, false));
BULK_MATERIAL.add(new Material.Bulk("Cork", 240, false)); BULK_MATERIAL.add(new Material.Bulk("Cork", 240, false));
BULK_MATERIAL.add(new Material.Bulk("Depron", 40, false));
BULK_MATERIAL.add(new Material.Bulk("Fiberglass", 1850, false)); BULK_MATERIAL.add(new Material.Bulk("Fiberglass", 1850, false));
BULK_MATERIAL.add(new Material.Bulk("Kraft phenolic",950, false)); BULK_MATERIAL.add(new Material.Bulk("Kraft phenolic",950, false));
BULK_MATERIAL.add(new Material.Bulk("Maple", 755, false)); BULK_MATERIAL.add(new Material.Bulk("Maple", 755, false));
@ -89,6 +90,7 @@ public class Databases {
BULK_MATERIAL.add(new Material.Bulk("Polystyrene", 1050, false)); BULK_MATERIAL.add(new Material.Bulk("Polystyrene", 1050, false));
BULK_MATERIAL.add(new Material.Bulk("PVC", 1390, false)); BULK_MATERIAL.add(new Material.Bulk("PVC", 1390, false));
BULK_MATERIAL.add(new Material.Bulk("Spruce", 450, false)); BULK_MATERIAL.add(new Material.Bulk("Spruce", 450, false));
// TODO: CRITICAL: Add styrofoam
BULK_MATERIAL.add(new Material.Bulk("Quantum tubing",1050, false)); BULK_MATERIAL.add(new Material.Bulk("Quantum tubing",1050, false));
SURFACE_MATERIAL.add(new Material.Surface("Ripstop nylon", 0.067, false)); SURFACE_MATERIAL.add(new Material.Surface("Ripstop nylon", 0.067, false));
@ -141,6 +143,14 @@ public class Databases {
} }
/*
* Used just for ensuring initialization of the class.
*/
public static void fakeMethod() {
}
/** /**
* Find a material from the database with the specified type and name. Returns * Find a material from the database with the specified type and name. Returns
* <code>null</code> if the specified material could not be found. * <code>null</code> if the specified material could not be found.

View File

@ -0,0 +1,38 @@
package net.sf.openrocket.gui.components;
import java.awt.Dimension;
import javax.swing.JLabel;
/**
* A JLabel that limits the minimum and maximum height of the label to the
* initial preferred height of the label. This is required in labels that use HTML
* since these often cause the panels to expand too much in height.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class HtmlLabel extends JLabel {
public HtmlLabel() {
super();
limitSize();
}
public HtmlLabel(String text) {
super(text);
limitSize();
}
public HtmlLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
limitSize();
}
private void limitSize() {
Dimension dim = this.getPreferredSize();
this.setMinimumSize(new Dimension(0, dim.height));
this.setMaximumSize(new Dimension(Integer.MAX_VALUE, dim.height));
}
}

View File

@ -1,47 +0,0 @@
package net.sf.openrocket.gui.components;
import java.awt.Font;
import javax.swing.JLabel;
/**
* A resizeable JLabel. The method resizeFont(float) changes the current font size by the
* given (positive or negative) amount. The change is relative to the current font size.
* <p>
* A nice small text is achievable by <code>new ResizeLabel("My text", -2);</code>
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class ResizeLabel extends JLabel {
public ResizeLabel() {
super();
}
public ResizeLabel(String text) {
super(text);
}
public ResizeLabel(float size) {
super();
resizeFont(size);
}
public ResizeLabel(String text, float size) {
super(text);
resizeFont(size);
}
public ResizeLabel(String text, int horizontalAlignment, float size) {
super(text, horizontalAlignment);
resizeFont(size);
}
public void resizeFont(float size) {
Font font = this.getFont();
font = font.deriveFont(font.getSize2D()+size);
this.setFont(font);
}
}

View File

@ -0,0 +1,111 @@
package net.sf.openrocket.gui.components;
import java.awt.Font;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
/**
* A resizeable and styleable JLabel. The method {@link #resizeFont(float)} changes the
* current font size by the given (positive or negative) amount. The change is relative
* to the current font size. The method {@link #setFontStyle(Style)} sets the style
* (bold/italic) of the font.
* <p>
* A nice small text is achievable by <code>new ResizeLabel("My text", -2);</code>
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class StyledLabel extends JLabel {
public enum Style {
PLAIN(Font.PLAIN),
BOLD(Font.BOLD),
ITALIC(Font.ITALIC),
BOLD_ITALIC(Font.BOLD | Font.ITALIC);
private int style;
Style(int fontStyle) {
this.style = fontStyle;
}
public int getFontStyle() {
return style;
}
}
public StyledLabel() {
this("", SwingConstants.LEADING, 0f);
}
public StyledLabel(String text) {
this(text, SwingConstants.LEADING, 0f);
}
public StyledLabel(float size) {
this("", SwingConstants.LEADING, size);
}
public StyledLabel(String text, float size) {
this(text, SwingConstants.LEADING, size);
}
public StyledLabel(String text, int horizontalAlignment, float size) {
super(text, horizontalAlignment);
resizeFont(size);
checkPreferredSize(size, Style.PLAIN);
}
public StyledLabel(Style style) {
this("", SwingConstants.LEADING, 0f, style);
}
public StyledLabel(String text, Style style) {
this(text, SwingConstants.LEADING, 0f, style);
}
public StyledLabel(float size, Style style) {
this("", SwingConstants.LEADING, size, style);
}
public StyledLabel(String text, float size, Style style) {
this(text, SwingConstants.LEADING, size, style);
}
public StyledLabel(String text, int horizontalAlignment, float size, Style style) {
super(text, horizontalAlignment);
resizeFont(size);
setFontStyle(style);
checkPreferredSize(size, style);
}
private void checkPreferredSize(float size, Style style) {
String str = this.getText();
if (str.startsWith("<html>") && str.indexOf("<br") < 0) {
StyledLabel label = new StyledLabel("plaintext", size, style);
label.validate();
System.out.println("Plain-text label: " + label.getPreferredSize());
System.out.println("HTML label: " + this.getPreferredSize());
}
}
public void resizeFont(float size) {
Font font = this.getFont();
font = font.deriveFont(font.getSize2D()+size);
this.setFont(font);
}
public void setFontStyle(Style style) {
Font font = this.getFont();
font = font.deriveFont(style.getFontStyle());
this.setFont(font);
}
}

View File

@ -36,7 +36,7 @@ import net.sf.openrocket.unit.UnitGroup;
* @author Sampo Niskanen <sampo.niskanen@iki.fi> * @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/ */
public class UnitSelector extends ResizeLabel implements ChangeListener, MouseListener, public class UnitSelector extends StyledLabel implements ChangeListener, MouseListener,
ItemSelectable { ItemSelectable {
private DoubleModel model; private DoubleModel model;

View File

@ -15,7 +15,9 @@ import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.components.BasicSlider; 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.components.UnitSelector;
import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FreeformFinSet; import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
@ -107,7 +109,8 @@ public abstract class FinSetConfig extends RocketComponentConfig {
// JPanel panel = new JPanel(new MigLayout("fillx, align 20% 20%, gap rel unrel", // JPanel panel = new JPanel(new MigLayout("fillx, align 20% 20%, gap rel unrel",
// "[40lp][80lp::][30lp::][100lp::]","")); // "[40lp][80lp::][30lp::][100lp::]",""));
panel.add(new JLabel("<html><b>Through-the-wall fin tabs:</b>"), "spanx, wrap 30lp"); panel.add(new StyledLabel("Through-the-wall fin tabs:", Style.BOLD),
"spanx, wrap 30lp");
JLabel label; JLabel label;
DoubleModel m; DoubleModel m;

View File

@ -22,7 +22,7 @@ import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.IntegerModel; import net.sf.openrocket.gui.adaptors.IntegerModel;
import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.scalefigure.FinPointFigure; import net.sf.openrocket.gui.scalefigure.FinPointFigure;
import net.sf.openrocket.gui.scalefigure.ScaleScrollPane; import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
@ -212,14 +212,14 @@ public class FreeformFinSetConfig extends FinSetConfig {
panel.add(tablePane,"growy, width 100lp:100lp:, height 100lp:250lp:"); panel.add(tablePane,"growy, width 100lp:100lp:, height 100lp:250lp:");
panel.add(figurePane,"gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap"); panel.add(figurePane,"gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
panel.add(new ResizeLabel("Double-click", -2), "alignx 50%"); panel.add(new StyledLabel("Double-click", -2), "alignx 50%");
panel.add(new ScaleSelector(figurePane),"spany 2"); panel.add(new ScaleSelector(figurePane),"spany 2");
panel.add(new ResizeLabel("Click+drag: Add and move points " + panel.add(new StyledLabel("Click+drag: Add and move points " +
"Ctrl+click: Remove point", -2), "spany 2, right, wrap"); "Ctrl+click: Remove point", -2), "spany 2, right, wrap");
panel.add(new ResizeLabel("to edit", -2), "alignx 50%"); panel.add(new StyledLabel("to edit", -2), "alignx 50%");
return panel; return panel;
} }

View File

@ -17,7 +17,10 @@ import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.IntegerModel; import net.sf.openrocket.gui.adaptors.IntegerModel;
import net.sf.openrocket.gui.adaptors.MaterialModel; import net.sf.openrocket.gui.adaptors.MaterialModel;
import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.HtmlLabel;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.Parachute; import net.sf.openrocket.rocketcomponent.Parachute;
@ -36,7 +39,7 @@ public class ParachuteConfig extends RecoveryDeviceConfig {
//// Canopy //// Canopy
panel.add(new JLabel("<html><b>Canopy:</b>"), "wrap unrel"); panel.add(new StyledLabel("Canopy:", Style.BOLD), "wrap unrel");
panel.add(new JLabel("Diameter:")); panel.add(new JLabel("Diameter:"));
@ -62,7 +65,7 @@ public class ParachuteConfig extends RecoveryDeviceConfig {
// CD // CD
JLabel label = new JLabel("<html>Drag coefficient C<sub>D</sub>:"); JLabel label = new HtmlLabel("<html>Drag coefficient C<sub>D</sub>:");
String tip = "<html>The drag coefficient relative to the total area of the parachute.<br>" + String tip = "<html>The drag coefficient relative to the total area of the parachute.<br>" +
"A larger drag coefficient yields a slowed descent rate. " + "A larger drag coefficient yields a slowed descent rate. " +
"A typical value for parachutes is 0.8."; "A typical value for parachutes is 0.8.";
@ -89,7 +92,7 @@ public class ParachuteConfig extends RecoveryDeviceConfig {
//// Shroud lines //// Shroud lines
panel.add(new JLabel("<html><b>Shroud lines:</b>"), "wrap unrel"); panel.add(new StyledLabel("Shroud lines:", Style.BOLD), "wrap unrel");
panel.add(new JLabel("Number of lines:")); panel.add(new JLabel("Number of lines:"));

View File

@ -17,7 +17,8 @@ import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.MaterialModel; import net.sf.openrocket.gui.adaptors.MaterialModel;
import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.HtmlLabel;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.MassComponent;
@ -93,7 +94,7 @@ public class StreamerConfig extends RecoveryDeviceConfig {
// CD // CD
JLabel label = new JLabel("<html>Drag coefficient C<sub>D</sub>:"); JLabel label = new HtmlLabel("<html>Drag coefficient C<sub>D</sub>:");
String tip = "<html>The drag coefficient relative to the total area of the streamer.<br>" + String tip = "<html>The drag coefficient relative to the total area of the streamer.<br>" +
"A larger drag coefficient yields a slowed descent rate."; "A larger drag coefficient yields a slowed descent rate.";
label.setToolTipText(tip); label.setToolTipText(tip);
@ -110,7 +111,7 @@ public class StreamerConfig extends RecoveryDeviceConfig {
check.setText("Automatic"); check.setText("Automatic");
panel.add(check,"skip, span, wrap"); panel.add(check,"skip, span, wrap");
panel.add(new ResizeLabel("The drag coefficient is relative to the area of the streamer.", panel.add(new StyledLabel("The drag coefficient is relative to the area of the streamer.",
-2), "span, wrap"); -2), "span, wrap");

View File

@ -10,7 +10,7 @@ import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.URLLabel; import net.sf.openrocket.gui.components.URLLabel;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Icons; import net.sf.openrocket.util.Icons;
@ -31,18 +31,18 @@ public class AboutDialog extends JDialog {
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")), panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")),
"spany 5, top"); "spany 5, top");
panel.add(new ResizeLabel("OpenRocket", 20), "ax 50%, growy, wrap para"); panel.add(new StyledLabel("OpenRocket", 20), "ax 50%, growy, wrap para");
panel.add(new ResizeLabel("Version " + version, 3), "ax 50%, growy, wrap rel"); panel.add(new StyledLabel("Version " + version, 3), "ax 50%, growy, wrap rel");
String source = Prefs.getBuildSource(); String source = Prefs.getBuildSource();
if (!Prefs.DEFAULT_BUILD_SOURCE.equalsIgnoreCase(source)) { if (!Prefs.DEFAULT_BUILD_SOURCE.equalsIgnoreCase(source)) {
panel.add(new ResizeLabel("Distributed by " + source, -1), panel.add(new StyledLabel("Distributed by " + source, -1),
"ax 50%, growy, wrap para"); "ax 50%, growy, wrap para");
} else { } else {
panel.add(new ResizeLabel(" ", -1), "ax 50%, growy, wrap para"); panel.add(new StyledLabel(" ", -1), "ax 50%, growy, wrap para");
} }
panel.add(new ResizeLabel("Copyright \u00A9 2007-2009 Sampo Niskanen"), panel.add(new StyledLabel("Copyright \u00A9 2007-2009 Sampo Niskanen"),
"ax 50%, growy, wrap para"); "ax 50%, growy, wrap para");
panel.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para"); panel.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para");

View File

@ -25,8 +25,8 @@ import javax.swing.JScrollPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.communication.Communication; import net.sf.openrocket.communication.BugReporter;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.SelectableLabel; import net.sf.openrocket.gui.components.SelectableLabel;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.JarUtil; import net.sf.openrocket.util.JarUtil;
@ -66,7 +66,7 @@ public class BugReportDialog extends JDialog {
panel.add(new JScrollPane(textArea), "grow, wrap"); panel.add(new JScrollPane(textArea), "grow, wrap");
panel.add(new ResizeLabel("The information above may be included in a public " + panel.add(new StyledLabel("The information above may be included in a public " +
"bug report. Make sure it does not contain any sensitive information you " + "bug report. Make sure it does not contain any sensitive information you " +
"do not want to be made public.", -1), "wrap para"); "do not want to be made public.", -1), "wrap para");
@ -107,7 +107,7 @@ public class BugReportDialog extends JDialog {
String text = textArea.getText(); String text = textArea.getText();
try { try {
Communication.sendBugReport(text); BugReporter.sendBugReport(text);
// Success if we came here // Success if we came here
JOptionPane.showMessageDialog(BugReportDialog.this, JOptionPane.showMessageDialog(BugReportDialog.this,

View File

@ -45,7 +45,7 @@ import net.sf.openrocket.gui.adaptors.ColumnTableModel;
import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.MotorConfigurationModel; import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.StageSelector; import net.sf.openrocket.gui.components.StageSelector;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.scalefigure.RocketPanel;
@ -370,14 +370,14 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener {
}); });
panel.add(new ResizeLabel("Reference length: ", -1), panel.add(new StyledLabel("Reference length: ", -1),
"span, split, gapleft para, gapright rel"); "span, split, gapleft para, gapright rel");
DoubleModel dm = new DoubleModel(conditions, "RefLength", UnitGroup.UNITS_LENGTH); DoubleModel dm = new DoubleModel(conditions, "RefLength", UnitGroup.UNITS_LENGTH);
UnitSelector sel = new UnitSelector(dm, true); UnitSelector sel = new UnitSelector(dm, true);
sel.resizeFont(-1); sel.resizeFont(-1);
panel.add(sel, "gapright para"); panel.add(sel, "gapright para");
panel.add(new ResizeLabel("Reference area: ", -1), "gapright rel"); panel.add(new StyledLabel("Reference area: ", -1), "gapright rel");
dm = new DoubleModel(conditions, "RefArea", UnitGroup.UNITS_AREA); dm = new DoubleModel(conditions, "RefArea", UnitGroup.UNITS_AREA);
sel = new UnitSelector(dm, true); sel = new UnitSelector(dm, true);
sel.resizeFont(-1); sel.resizeFont(-1);

View File

@ -16,7 +16,7 @@ import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
@ -54,7 +54,7 @@ public class CustomMaterialDialog extends JDialog {
"gapleft para, span, wrap" + (note == null ? " para":"")); "gapleft para, span, wrap" + (note == null ? " para":""));
} }
if (note != null) { if (note != null) {
panel.add(new ResizeLabel(note, -1), "span, wrap para"); panel.add(new StyledLabel(note, -1), "span, wrap para");
} }

View File

@ -14,7 +14,7 @@ import javax.swing.JScrollPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
public class LicenseDialog extends JDialog { public class LicenseDialog extends JDialog {
@ -32,7 +32,7 @@ public class LicenseDialog extends JDialog {
JPanel panel = new JPanel(new MigLayout("fill")); JPanel panel = new JPanel(new MigLayout("fill"));
panel.add(new ResizeLabel("OpenRocket license", 10), "ax 50%, wrap para"); panel.add(new StyledLabel("OpenRocket license", 10), "ax 50%, wrap para");
String licenseText; String licenseText;
try { try {

View File

@ -37,7 +37,7 @@ import javax.swing.table.TableRowSorter;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.database.Databases; import net.sf.openrocket.database.Databases;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
@ -251,7 +251,7 @@ public class MotorChooserDialog extends JDialog {
} }
}); });
panel.add(delayBox,"gapright unrel"); panel.add(delayBox,"gapright unrel");
panel.add(new ResizeLabel("(Number of seconds or \"None\")", -1), "wrap para"); panel.add(new StyledLabel("(Number of seconds or \"None\")", -1), "wrap para");
setDelays(false); setDelays(false);

View File

@ -0,0 +1,73 @@
package net.sf.openrocket.gui.dialogs;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.communication.UpdateInfo;
import net.sf.openrocket.gui.components.URLLabel;
import net.sf.openrocket.util.ComparablePair;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Icons;
public class UpdateInfoDialog extends JDialog {
public UpdateInfoDialog(UpdateInfo info) {
super((Window)null, "OpenRocket update available", ModalityType.APPLICATION_MODAL);
JPanel panel = new JPanel(new MigLayout("fill"));
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")),
"spany 100, top");
panel.add(new JLabel("<html><b>OpenRocket version " + info.getLatestVersion() +
" is available!"), "wrap para");
List<ComparablePair<Integer, String>> updates = info.getUpdates();
if (updates.size() > 0) {
panel.add(new JLabel("Updates include:"), "wrap rel");
Collections.sort(updates);
int count = 0;
int n = -1;
for (int i=updates.size()-1; i>=0; i--) {
// Add only specific number of top features
if (count >= 4 && n != updates.get(i).getU())
break;
n = updates.get(i).getU();
panel.add(new JLabel(" \u2022 " + updates.get(i).getV()), "wrap 0px");
count++;
}
}
panel.add(new JLabel("Download the new version from:"),
"gaptop para, alignx 50%, wrap unrel");
panel.add(new URLLabel(AboutDialog.OPENROCKET_URL), "alignx 50%, wrap para");
JButton button = new JButton("Close");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
UpdateInfoDialog.this.dispose();
}
});
panel.add(button, "right");
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
GUIUtil.setDisposableDialogOptions(this, button);
}
}

View File

@ -19,7 +19,7 @@ import javax.swing.JPanel;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
@ -204,7 +204,7 @@ public class PreferencesDialog extends JDialog {
panel.add(button, "grow, wrap para"); panel.add(button, "grow, wrap para");
panel.add(new ResizeLabel("The effects will take place the next time you open a window.",-2), panel.add(new StyledLabel("The effects will take place the next time you open a window.",-2),
"spanx, wrap"); "spanx, wrap");

View File

@ -48,6 +48,7 @@ import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager; import javax.swing.ToolTipManager;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.TitledBorder; import javax.swing.border.TitledBorder;
@ -60,6 +61,9 @@ import javax.swing.tree.TreeSelectionModel;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.communication.UpdateInfo;
import net.sf.openrocket.communication.UpdateInfoRetriever;
import net.sf.openrocket.database.Databases;
import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.GeneralRocketLoader; import net.sf.openrocket.file.GeneralRocketLoader;
import net.sf.openrocket.file.OpenRocketSaver; import net.sf.openrocket.file.OpenRocketSaver;
@ -74,6 +78,7 @@ import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
import net.sf.openrocket.gui.dialogs.ExampleDesignDialog; import net.sf.openrocket.gui.dialogs.ExampleDesignDialog;
import net.sf.openrocket.gui.dialogs.LicenseDialog; import net.sf.openrocket.gui.dialogs.LicenseDialog;
import net.sf.openrocket.gui.dialogs.SwingWorkerDialog; import net.sf.openrocket.gui.dialogs.SwingWorkerDialog;
import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
import net.sf.openrocket.gui.dialogs.WarningDialog; import net.sf.openrocket.gui.dialogs.WarningDialog;
import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog; import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog;
import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.scalefigure.RocketPanel;
@ -1151,7 +1156,18 @@ public class BasicFrame extends JFrame {
private static void runMain(String[] args) { private static void runMain(String[] args) {
// Start update info fetching
final UpdateInfoRetriever updateInfo;
if (Prefs.getCheckUpdates()) {
updateInfo = new UpdateInfoRetriever();
updateInfo.start();
} else {
updateInfo = null;
}
/* /*
* Set the look-and-feel. On Linux, Motif/Metal is sometimes incorrectly used * Set the look-and-feel. On Linux, Motif/Metal is sometimes incorrectly used
* which is butt-ugly, so if the system l&f is Motif/Metal, we search for a few * which is butt-ugly, so if the system l&f is Motif/Metal, we search for a few
@ -1200,12 +1216,51 @@ public class BasicFrame extends JFrame {
// Load defaults // Load defaults
Prefs.loadDefaultUnits(); Prefs.loadDefaultUnits();
// Starting action
// Load motors etc.
Databases.fakeMethod();
// Starting action (load files or open new document)
if (!handleCommandLine(args)) { if (!handleCommandLine(args)) {
newAction(); newAction();
} }
// Check whether update info has been fetched or whether it needs more time
checkUpdateStatus(updateInfo);
}
private static void checkUpdateStatus(final UpdateInfoRetriever updateInfo) {
if (updateInfo == null)
return;
int delay = 1000;
if (!updateInfo.isRunning())
delay = 100;
final Timer timer = new Timer(delay, null);
ActionListener listener = new ActionListener() {
private int count = 5;
@Override
public void actionPerformed(ActionEvent e) {
if (!updateInfo.isRunning()) {
timer.stop();
UpdateInfo info = updateInfo.getUpdateInfo();
if (info != null && !Prefs.getVersion().equals(info.getLatestVersion())) {
new UpdateInfoDialog(info).setVisible(true);
}
}
count--;
if (count <= 0)
timer.stop();
}
};
timer.addActionListener(listener);
timer.start();
} }

View File

@ -26,7 +26,7 @@ import javax.swing.tree.TreeSelectionModel;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.rocketcomponent.BodyComponent; import net.sf.openrocket.rocketcomponent.BodyComponent;
import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.BodyTube;
@ -269,7 +269,7 @@ public class ComponentAddButtons extends JPanel implements Scrollable {
// Add labels // Add labels
String[] l = text.split("\n"); String[] l = text.split("\n");
for (int i=0; i<l.length; i++) { for (int i=0; i<l.length; i++) {
add(new ResizeLabel(l[i],SwingConstants.CENTER,-3.0f),"growx"); add(new StyledLabel(l[i],SwingConstants.CENTER,-3.0f),"growx");
} }
add(new JLabel(),"push, sizegroup spacing"); add(new JLabel(),"push, sizegroup spacing");
@ -502,7 +502,7 @@ public class ComponentAddButtons extends JPanel implements Scrollable {
JPanel panel = new JPanel(new MigLayout()); JPanel panel = new JPanel(new MigLayout());
JCheckBox check = new JCheckBox("Do not ask me again"); JCheckBox check = new JCheckBox("Do not ask me again");
panel.add(check,"wrap"); panel.add(check,"wrap");
panel.add(new ResizeLabel("You can change the default operation in the " + panel.add(new StyledLabel("You can change the default operation in the " +
"preferences.",-2)); "preferences.",-2));
int sel = JOptionPane.showOptionDialog(null, // parent component int sel = JOptionPane.showOptionDialog(null, // parent component

View File

@ -15,7 +15,7 @@ import javax.swing.KeyStroke;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
@ -209,7 +209,7 @@ public class RocketActions {
JPanel panel = new JPanel(new MigLayout()); JPanel panel = new JPanel(new MigLayout());
JCheckBox dontAsk = new JCheckBox("Do not ask me again"); JCheckBox dontAsk = new JCheckBox("Do not ask me again");
panel.add(dontAsk,"wrap"); panel.add(dontAsk,"wrap");
panel.add(new ResizeLabel("You can change the default operation in the " + panel.add(new StyledLabel("You can change the default operation in the " +
"preferences.",-2)); "preferences.",-2));
int ret = JOptionPane.showConfirmDialog( int ret = JOptionPane.showConfirmDialog(

View File

@ -33,7 +33,7 @@ import net.sf.openrocket.document.events.DocumentChangeListener;
import net.sf.openrocket.document.events.SimulationChangeEvent; import net.sf.openrocket.document.events.SimulationChangeEvent;
import net.sf.openrocket.gui.adaptors.Column; import net.sf.openrocket.gui.adaptors.Column;
import net.sf.openrocket.gui.adaptors.ColumnTableModel; import net.sf.openrocket.gui.adaptors.ColumnTableModel;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightData;
@ -146,7 +146,7 @@ public class SimulationPanel extends JPanel {
JPanel panel = new JPanel(new MigLayout()); JPanel panel = new JPanel(new MigLayout());
JCheckBox dontAsk = new JCheckBox("Do not ask me again"); JCheckBox dontAsk = new JCheckBox("Do not ask me again");
panel.add(dontAsk,"wrap"); panel.add(dontAsk,"wrap");
panel.add(new ResizeLabel("You can change the default operation in the " + panel.add(new StyledLabel("You can change the default operation in the " +
"preferences.",-2)); "preferences.",-2));
int ret = JOptionPane.showConfirmDialog(SimulationPanel.this, int ret = JOptionPane.showConfirmDialog(SimulationPanel.this,
@ -215,7 +215,7 @@ public class SimulationPanel extends JPanel {
// Initialize the label // Initialize the label
if (label == null) { if (label == null) {
label = new ResizeLabel(2f); label = new StyledLabel(2f);
label.setIconTextGap(1); label.setIconTextGap(1);
// label.setFont(label.getFont().deriveFont(Font.BOLD)); // label.setFont(label.getFont().deriveFont(Font.BOLD));
} }

View File

@ -21,7 +21,7 @@ import javax.swing.table.TableColumnModel;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.simulation.FlightEvent;
@ -160,7 +160,7 @@ public class PlotPanel extends JPanel {
this.add(domainUnitSelector, "width 40lp, gapright para"); this.add(domainUnitSelector, "width 40lp, gapright para");
ResizeLabel desc = new ResizeLabel("<html><p>The data will be plotted in time order " + StyledLabel desc = new StyledLabel("<html><p>The data will be plotted in time order " +
"even if the X axis type is not time.", -2); "even if the X axis type is not time.", -2);
this.add(desc, "width :0px:, growx, wrap para"); this.add(desc, "width :0px:, growx, wrap para");

View File

@ -0,0 +1,83 @@
package net.sf.openrocket.util;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* A filtering InputStream that limits the number of bytes that can be
* read from a stream. This can be used to enforce security, so that overlong
* input is ignored.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class LimitedInputStream extends FilterInputStream {
private int remaining;
public LimitedInputStream(InputStream is, int limit) {
super(is);
this.remaining = limit;
}
@Override
public int available() throws IOException {
int available = super.available();
return Math.min(available, remaining);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (remaining <= 0)
return -1;
int result = super.read(b, off, Math.min(len, remaining));
if (result >= 0)
remaining -= result;
return result;
}
@Override
public long skip(long n) throws IOException {
if (n > remaining)
n = remaining;
long result = super.skip(n);
remaining -= result;
return result;
}
@Override
public int read() throws IOException {
if (remaining <= 0)
return -1;
int result = super.read();
if (result >= 0)
remaining--;
return result;
}
// Disable mark support
@Override
public void mark(int readlimit) {
}
@Override
public boolean markSupported() {
return false;
}
@Override
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
}

View File

@ -57,6 +57,7 @@ public class Prefs {
private static final String BUILD_VERSION; private static final String BUILD_VERSION;
private static final String BUILD_SOURCE; private static final String BUILD_SOURCE;
public static final String DEFAULT_BUILD_SOURCE = "default"; public static final String DEFAULT_BUILD_SOURCE = "default";
private static final boolean DEFAULT_CHECK_UPDATES;
static { static {
try { try {
@ -81,6 +82,12 @@ public class Prefs {
BUILD_SOURCE = props.getProperty("build.source"); BUILD_SOURCE = props.getProperty("build.source");
String value = props.getProperty("build.checkupdates");
if (value != null)
DEFAULT_CHECK_UPDATES = Boolean.parseBoolean(value);
else
DEFAULT_CHECK_UPDATES = true;
} catch (IOException e) { } catch (IOException e) {
throw new MissingResourceException( throw new MissingResourceException(
"Error reading build.properties", "Error reading build.properties",
@ -103,6 +110,8 @@ public class Prefs {
public static final String PLOT_SHOW_POINTS = "ShowPlotPoints"; public static final String PLOT_SHOW_POINTS = "ShowPlotPoints";
private static final String CHECK_UPDATES = "CheckUpdates";
/** /**
* Node to this application's preferences. * Node to this application's preferences.
* @deprecated Use the static methods instead. * @deprecated Use the static methods instead.
@ -152,14 +161,18 @@ public class Prefs {
} }
private static final Material DEFAULT_LINE_MATERIAL = /*
Databases.findMaterial(Material.Type.LINE, "Elastic cord (round 2mm, 1/16 in)", * Within a holder class so they will load only when needed.
0.0018, false); */
private static final Material DEFAULT_SURFACE_MATERIAL = private static class DefaultMaterialHolder {
Databases.findMaterial(Material.Type.SURFACE, "Ripstop nylon", 0.067, false); private static final Material DEFAULT_LINE_MATERIAL =
private static final Material DEFAULT_BULK_MATERIAL = Databases.findMaterial(Material.Type.LINE, "Elastic cord (round 2mm, 1/16 in)",
Databases.findMaterial(Material.Type.BULK, "Cardboard", 680, false); 0.0018, false);
private static final Material DEFAULT_SURFACE_MATERIAL =
Databases.findMaterial(Material.Type.SURFACE, "Ripstop nylon", 0.067, false);
private static final Material DEFAULT_BULK_MATERIAL =
Databases.findMaterial(Material.Type.BULK, "Cardboard", 680, false);
}
////////////////////// //////////////////////
@ -242,6 +255,16 @@ public class Prefs {
public static boolean getCheckUpdates() {
return PREFNODE.getBoolean(CHECK_UPDATES, DEFAULT_CHECK_UPDATES);
}
public static void setCheckUpdates(boolean check) {
PREFNODE.putBoolean(CHECK_UPDATES, check);
storeVersion();
}
////////////////// //////////////////
public static File getDefaultDirectory() { public static File getDefaultDirectory() {
@ -360,11 +383,11 @@ public class Prefs {
switch (type) { switch (type) {
case LINE: case LINE:
return DEFAULT_LINE_MATERIAL; return DefaultMaterialHolder.DEFAULT_LINE_MATERIAL;
case SURFACE: case SURFACE:
return DEFAULT_SURFACE_MATERIAL; return DefaultMaterialHolder.DEFAULT_SURFACE_MATERIAL;
case BULK: case BULK:
return DEFAULT_BULK_MATERIAL; return DefaultMaterialHolder.DEFAULT_BULK_MATERIAL;
} }
throw new IllegalArgumentException("Unknown material type: "+type); throw new IllegalArgumentException("Unknown material type: "+type);
} }

View File

@ -14,17 +14,24 @@ import net.sf.openrocket.motor.ThrustCurveMotor;
public class MotorCompare { public class MotorCompare {
/** Maximum allowed difference in maximum thrust */
private static final double MAX_THRUST_MARGIN = 0.20; private static final double MAX_THRUST_MARGIN = 0.20;
/** Maximum allowed difference in total impulse */
private static final double TOTAL_IMPULSE_MARGIN = 0.10; private static final double TOTAL_IMPULSE_MARGIN = 0.10;
/** Maximum allowed difference in mass values */
private static final double MASS_MARGIN = 0.10; private static final double MASS_MARGIN = 0.10;
private static final double THRUST_MARGIN = 0.15; /** Number of time points in thrust curve to compare */
private static final int DIVISIONS = 100; private static final int DIVISIONS = 100;
/** Maximum difference in thrust for a time point to be considered invalid */
private static final double THRUST_MARGIN = 0.15;
/** Number of invalid time points allowed */
private static final int ALLOWED_INVALID_POINTS = 15; private static final int ALLOWED_INVALID_POINTS = 15;
/** Minimum number of thrust curve points allowed (incl. start and end points) */
private static final int MIN_POINTS = 7; private static final int MIN_POINTS = 7;
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
final double maxThrust; final double maxThrust;
final double maxTime; final double maxTime;

View File

@ -0,0 +1,72 @@
package net.sf.openrocket.communication;
import static org.junit.Assert.*;
import java.io.IOException;
import net.sf.openrocket.util.Prefs;
import org.junit.Test;
public class BugReportTest {
private HttpURLConnectionMock setup() {
HttpURLConnectionMock connection = new HttpURLConnectionMock();
Communicator.setConnectionSource(new ConnectionSourceStub(connection));
connection.setUseCaches(true);
return connection;
}
private void check(HttpURLConnectionMock connection) {
assertEquals(Communicator.BUG_REPORT_URL, connection.getTrueUrl());
assertTrue(connection.getConnectTimeout() > 0);
assertEquals(Prefs.getVersion(), connection.getRequestProperty("X-OpenRocket-Version"));
assertTrue(connection.getInstanceFollowRedirects());
assertEquals("POST", connection.getRequestMethod());
assertFalse(connection.getUseCaches());
}
@Test
public void testBugReportSuccess() throws IOException {
HttpURLConnectionMock connection = setup();
connection.setResponseCode(Communicator.BUG_REPORT_RESPONSE_CODE);
String message =
"MyMessage\n"+
"is important\n"+
"h\u00e4h?";
BugReporter.sendBugReport(message);
check(connection);
String msg = connection.getOutputStreamString();
assertTrue(msg.indexOf("version=" + Prefs.getVersion()) >= 0);
assertTrue(msg.indexOf(Communicator.encode(message)) >= 0);
}
@Test
public void testBugReportFailure() throws IOException {
HttpURLConnectionMock connection = setup();
connection.setResponseCode(200);
String message =
"MyMessage\n"+
"is important\n"+
"h\u00e4h?";
try {
BugReporter.sendBugReport(message);
fail("Exception did not occur");
} catch (IOException e) {
// Success
}
check(connection);
}
}

View File

@ -1,120 +0,0 @@
package net.sf.openrocket.communication;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.StringReader;
import java.util.Random;
import org.junit.Test;
public class CommunicationTest {
@Test
public void testIllegalInputUpdateParsing() throws IOException {
UpdateInfo info;
info = Communication.parseUpdateInput(new StringReader(""));
assertNull(info);
info = Communication.parseUpdateInput(new StringReader("vj\u00e4avdsads"));
assertNull(info);
info = Communication.parseUpdateInput(new StringReader("\u0000\u0001\u0002"));
assertNull(info);
info = Communication.parseUpdateInput(new StringReader("Version: 1.2"));
assertNull(info);
info = Communication.parseUpdateInput(new StringReader("Version: 1.2pre"));
assertNull(info);
info = Communication.parseUpdateInput(new StringReader("Version: 1.2.x"));
assertNull(info);
info = Communication.parseUpdateInput(new StringReader("\u0000\u0001\u0002"));
assertNull(info);
// Feed random bad input
Random rnd = new Random();
StringBuilder sb = new StringBuilder(10000);
for (int i=0; i<100; i++) {
int length = rnd.nextInt(10000);
sb.delete(0, sb.length());
for (int j=0; j<length; j++) {
sb.append((char)rnd.nextInt());
}
info = Communication.parseUpdateInput(new StringReader(sb.toString()));
assertNull(info);
}
}
@Test
public void testValidInputUpdateParsing() throws IOException {
UpdateInfo info;
info = Communication.parseUpdateInput(new StringReader("Version: 1.2.3"));
assertNotNull(info);
assertEquals("1.2.3", info.getLatestVersion());
assertEquals(0, info.getUpdates().size());
info = Communication.parseUpdateInput(new StringReader("Version: 1.2.3pre"));
assertNotNull(info);
assertEquals("1.2.3pre", info.getLatestVersion());
assertEquals(0, info.getUpdates().size());
info = Communication.parseUpdateInput(new StringReader("Version: 1.2.3-build-3"));
assertNotNull(info);
assertEquals("1.2.3-build-3", info.getLatestVersion());
assertEquals(0, info.getUpdates().size());
info = Communication.parseUpdateInput(new StringReader("Version: 1.2.3x\n\n"));
assertNotNull(info);
assertEquals("1.2.3x", info.getLatestVersion());
assertEquals(0, info.getUpdates().size());
info = Communication.parseUpdateInput(new StringReader("Version:1.2.3\nfdsacd\u00e4fdsa"));
assertNotNull(info);
assertEquals("1.2.3", info.getLatestVersion());
assertEquals(0, info.getUpdates().size());
info = Communication.parseUpdateInput(new StringReader(
"Version: 1.2.3 \n" +
"15: Fifteen\n" +
"3: Three1 \r\n" +
"3: Three2\r" +
"1:One"));
assertNotNull(info);
assertEquals("1.2.3", info.getLatestVersion());
assertEquals(4, info.getUpdates().size());
assertEquals(15, info.getUpdates().get(0).getU());
assertEquals(3, info.getUpdates().get(1).getU());
assertEquals(3, info.getUpdates().get(2).getU());
assertEquals(1, info.getUpdates().get(3).getU());
assertEquals("Fifteen", info.getUpdates().get(0).getV());
assertEquals("Three1", info.getUpdates().get(1).getV());
assertEquals("Three2", info.getUpdates().get(2).getV());
assertEquals("One", info.getUpdates().get(3).getV());
info = Communication.parseUpdateInput(new StringReader(
"Version: 1.2.3\n" +
"15: (C) 1234 A&B %23 \\o/ \r\r\n" +
"5: m\u00e4c\n" +
"3: Invalid\u0000value\n" +
"1: One\u0019two"));
assertNotNull(info);
assertEquals("1.2.3", info.getLatestVersion());
assertEquals(1, info.getUpdates().size());
assertEquals(15, info.getUpdates().get(0).getU());
assertEquals("(C) 1234 A&B %23 \\o/", info.getUpdates().get(0).getV());
}
}

View File

@ -0,0 +1,22 @@
package net.sf.openrocket.communication;
import java.io.IOException;
import java.net.HttpURLConnection;
public class ConnectionSourceStub implements ConnectionSource {
private final HttpURLConnection connection;
public ConnectionSourceStub(HttpURLConnection connection) {
this.connection = connection;
}
@Override
public HttpURLConnection getConnection(String url) throws IOException {
if (connection instanceof HttpURLConnectionMock) {
((HttpURLConnectionMock)connection).setTrueUrl(url);
}
return connection;
}
}

View File

@ -0,0 +1,548 @@
package net.sf.openrocket.communication;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.security.Permission;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpURLConnectionMock extends HttpURLConnection {
private static final URL MOCK_URL;
static {
try {
MOCK_URL = new URL("http://localhost/");
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
private volatile boolean instanceFollowRedirects = false;
private volatile String requestMethod = "";
private volatile int responseCode;
private Map<String, String> requestProperties = new HashMap<String, String>();
private volatile int connectTimeout = -1;
private volatile String contentEncoding = "";
private volatile boolean doInput = false;
private volatile boolean doOutput = false;
private volatile byte[] content = null;
private volatile String contentType = null;
private volatile boolean useCaches = false;
private volatile InputStream inputStream = null;
private volatile ByteArrayOutputStream outputStream = null;
private volatile String trueUrl = null;
private volatile boolean connected = false;
private volatile int connectionDelay = 0;
private volatile boolean failed = false;
public HttpURLConnectionMock() {
super(MOCK_URL);
}
public HttpURLConnectionMock(URL u) {
super(u);
}
public String getTrueUrl() {
return trueUrl;
}
public void setTrueUrl(String url) {
assertNull(this.trueUrl);
this.trueUrl = url;
}
public boolean hasFailed() {
return failed;
}
public void setConnectionDelay(int delay) {
this.connectionDelay = delay;
}
@Override
public void connect() {
if (!connected) {
try {
Thread.sleep(connectionDelay);
} catch (InterruptedException e) {
}
connected = true;
}
}
@Override
public void disconnect() {
}
@Override
public boolean usingProxy() {
return false;
}
@Override
public boolean getInstanceFollowRedirects() {
return this.instanceFollowRedirects;
}
@Override
public void setInstanceFollowRedirects(boolean followRedirects) {
assertFalse(connected);
this.instanceFollowRedirects = followRedirects;
}
@Override
public String getRequestMethod() {
return this.requestMethod;
}
@Override
public void setRequestMethod(String method) throws ProtocolException {
assertFalse(connected);
this.requestMethod = method;
}
@Override
public int getResponseCode() throws IOException {
connect();
return this.responseCode;
}
public void setResponseCode(int code) {
this.responseCode = code;
}
@Override
public void addRequestProperty(String key, String value) {
assertFalse(connected);
assertFalse(this.requestProperties.containsKey(key.toLowerCase()));
this.requestProperties.put(key.toLowerCase(), value);
}
@Override
public void setRequestProperty(String key, String value) {
assertFalse(connected);
this.requestProperties.put(key.toLowerCase(), value);
}
@Override
public String getRequestProperty(String key) {
return this.requestProperties.get(key.toLowerCase());
}
@Override
public int getConnectTimeout() {
return this.connectTimeout;
}
@Override
public void setConnectTimeout(int timeout) {
assertFalse(connected);
this.connectTimeout = timeout;
}
@Override
public String getContentEncoding() {
connect();
return this.contentEncoding;
}
public void setContentEncoding(String encoding) {
this.contentEncoding = encoding;
}
@Override
public int getContentLength() {
connect();
if (content == null)
return 0;
return content.length;
}
public void setContent(byte[] content) {
this.content = content;
}
public void setContent(String content) {
try {
this.content = content.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
fail("UTF-8");
}
}
@Override
public String getContentType() {
connect();
return this.contentType;
}
public void setContentType(String type) {
this.contentType = type;
}
@Override
public boolean getDoInput() {
return this.doInput;
}
@Override
public void setDoInput(boolean doinput) {
assertFalse(connected);
this.doInput = doinput;
}
@Override
public boolean getDoOutput() {
return this.doOutput;
}
@Override
public void setDoOutput(boolean dooutput) {
assertFalse(connected);
this.doOutput = dooutput;
}
@Override
public InputStream getInputStream() throws IOException {
assertTrue(doInput);
assertNull(inputStream);
assertNotNull(content);
connect();
inputStream = new ByteArrayInputStream(content);
return inputStream;
}
@Override
public OutputStream getOutputStream() throws IOException {
assertTrue(doOutput);
assertNull(outputStream);
outputStream = new ByteArrayOutputStream();
return outputStream;
}
public byte[] getOutputStreamData() {
return outputStream.toByteArray();
}
public String getOutputStreamString() {
try {
return outputStream.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
fail("UTF-8");
return null;
}
}
@Override
public void setUseCaches(boolean usecaches) {
assertFalse(connected);
this.useCaches = usecaches;
}
@Override
public boolean getUseCaches() {
return this.useCaches;
}
private void assertNull(Object o) {
try {
org.junit.Assert.assertNull(o);
} catch (AssertionError e) {
failed = true;
throw e;
}
}
private void assertNotNull(Object o) {
try {
org.junit.Assert.assertNotNull(o);
} catch (AssertionError e) {
failed = true;
throw e;
}
}
private void assertTrue(boolean o) {
try {
org.junit.Assert.assertTrue(o);
} catch (AssertionError e) {
failed = true;
throw e;
}
}
private void assertFalse(boolean o) {
try {
org.junit.Assert.assertFalse(o);
} catch (AssertionError e) {
failed = true;
throw e;
}
}
private void fail(String msg) {
failed = true;
org.junit.Assert.fail(msg);
}
@Override
public InputStream getErrorStream() {
throw new UnsupportedOperationException();
}
@Override
public String getHeaderField(int n) {
throw new UnsupportedOperationException();
}
@Override
public long getHeaderFieldDate(String name, long Default) {
throw new UnsupportedOperationException();
}
@Override
public String getHeaderFieldKey(int n) {
throw new UnsupportedOperationException();
}
@Override
public Permission getPermission() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public String getResponseMessage() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void setChunkedStreamingMode(int chunklen) {
throw new UnsupportedOperationException();
}
@Override
public void setFixedLengthStreamingMode(int contentLength) {
throw new UnsupportedOperationException();
}
@Override
public boolean getAllowUserInteraction() {
throw new UnsupportedOperationException();
}
@Override
public Object getContent() throws IOException {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
@Override
public Object getContent(Class[] classes) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long getDate() {
throw new UnsupportedOperationException();
}
@Override
public boolean getDefaultUseCaches() {
throw new UnsupportedOperationException();
}
@Override
public long getExpiration() {
throw new UnsupportedOperationException();
}
@Override
public String getHeaderField(String name) {
throw new UnsupportedOperationException();
}
@Override
public int getHeaderFieldInt(String name, int Default) {
throw new UnsupportedOperationException();
}
@Override
public Map<String, List<String>> getHeaderFields() {
throw new UnsupportedOperationException();
}
@Override
public long getIfModifiedSince() {
throw new UnsupportedOperationException();
}
@Override
public long getLastModified() {
throw new UnsupportedOperationException();
}
@Override
public int getReadTimeout() {
throw new UnsupportedOperationException();
}
@Override
public Map<String, List<String>> getRequestProperties() {
throw new UnsupportedOperationException();
}
@Override
public URL getURL() {
throw new UnsupportedOperationException();
}
@Override
public void setAllowUserInteraction(boolean allowuserinteraction) {
throw new UnsupportedOperationException();
}
@Override
public void setDefaultUseCaches(boolean defaultusecaches) {
throw new UnsupportedOperationException();
}
@Override
public void setIfModifiedSince(long ifmodifiedsince) {
throw new UnsupportedOperationException();
}
@Override
public void setReadTimeout(int timeout) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,229 @@
package net.sf.openrocket.communication;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import net.sf.openrocket.util.ComparablePair;
import net.sf.openrocket.util.Prefs;
import org.junit.Test;
public class UpdateInfoTest {
/** The connection delay */
private static final int DELAY = 100;
/** How much long does the test allow it to take */
private static final int ALLOWANCE = 2000;
private HttpURLConnectionMock setup() {
HttpURLConnectionMock connection = new HttpURLConnectionMock();
Communicator.setConnectionSource(new ConnectionSourceStub(connection));
connection.setConnectionDelay(DELAY);
connection.setUseCaches(true);
connection.setContentType("text/plain");
return connection;
}
private void check(HttpURLConnectionMock connection) {
assertEquals(Communicator.UPDATE_INFO_URL + "?version=" + Prefs.getVersion(),
connection.getTrueUrl());
assertTrue(connection.getConnectTimeout() > 0);
assertEquals(Prefs.getVersion(), connection.getRequestProperty("X-OpenRocket-Version"));
assertNotNull(connection.getRequestProperty("X-OpenRocket-Country"));
assertNotNull(connection.getRequestProperty("X-OpenRocket-ID"));
assertNotNull(connection.getRequestProperty("X-OpenRocket-OS"));
assertNotNull(connection.getRequestProperty("X-OpenRocket-Java"));
assertTrue(connection.getInstanceFollowRedirects());
assertEquals("GET", connection.getRequestMethod());
assertFalse(connection.getUseCaches());
}
@Test
public void testUpdateAvailable() throws IOException {
HttpURLConnectionMock connection = setup();
connection.setResponseCode(Communicator.UPDATE_INFO_UPDATE_AVAILABLE);
String content =
"Version: 6.6.6pre A \n" +
"Extra: information\n" +
"100:hundred\n" +
"50: m\u00e4 \n\n" +
"1: one\n" +
"-2: none";
connection.setContent(content);
UpdateInfoRetriever retriever = new UpdateInfoRetriever();
retriever.start();
// Info is null while processing
assertNull(retriever.getUpdateInfo());
waitfor(retriever);
assertFalse(connection.hasFailed());
UpdateInfo info = retriever.getUpdateInfo();
assertNotNull(info);
check(connection);
assertEquals("6.6.6pre A", info.getLatestVersion());
List<ComparablePair<Integer, String>> updates = info.getUpdates();
assertEquals(3, updates.size());
Collections.sort(updates);
assertEquals(1, (int)updates.get(0).getU());
assertEquals("one", updates.get(0).getV());
assertEquals(50, (int)updates.get(1).getU());
assertEquals("m\u00e4", updates.get(1).getV());
assertEquals(100, (int)updates.get(2).getU());
assertEquals("hundred", updates.get(2).getV());
}
@Test
public void testUpdateNotAvailable() throws IOException {
HttpURLConnectionMock connection = setup();
connection.setResponseCode(Communicator.UPDATE_INFO_NO_UPDATE_CODE);
String content =
"Version: 6.6.6pre A \n" +
"Extra: information\n" +
"100:hundred\n" +
"50: m\u00e4 \n\n" +
"1: one\n" +
"-2: none";
connection.setContent(content);
UpdateInfoRetriever retriever = new UpdateInfoRetriever();
retriever.start();
// Info is null while processing
assertNull(retriever.getUpdateInfo());
waitfor(retriever);
assertFalse(connection.hasFailed());
UpdateInfo info = retriever.getUpdateInfo();
assertNotNull(info);
check(connection);
assertEquals(Prefs.getVersion(), info.getLatestVersion());
assertEquals(0, info.getUpdates().size());
}
@Test
public void testInvalidResponses() {
HttpURLConnectionMock connection = setup();
connection.setResponseCode(404);
connection.setContent("Version: 1.2.3");
UpdateInfoRetriever retriever = new UpdateInfoRetriever();
retriever.start();
assertNull(retriever.getUpdateInfo());
waitfor(retriever);
assertFalse(connection.hasFailed());
assertNull(retriever.getUpdateInfo());
check(connection);
connection = setup();
connection.setResponseCode(Communicator.UPDATE_INFO_UPDATE_AVAILABLE);
connection.setContentType("text/xml");
retriever = new UpdateInfoRetriever();
retriever.start();
assertNull(retriever.getUpdateInfo());
waitfor(retriever);
assertFalse(connection.hasFailed());
assertNull(retriever.getUpdateInfo());
check(connection);
connection = setup();
connection.setResponseCode(Communicator.UPDATE_INFO_UPDATE_AVAILABLE);
String content =
"100:hundred\n" +
"50: m\u00e4 \n\n" +
"1: one\n";
connection.setContent(content);
retriever = new UpdateInfoRetriever();
retriever.start();
assertNull(retriever.getUpdateInfo());
waitfor(retriever);
assertFalse(connection.hasFailed());
assertNull(retriever.getUpdateInfo());
check(connection);
connection = setup();
connection.setResponseCode(Communicator.UPDATE_INFO_UPDATE_AVAILABLE);
connection.setContent(new byte[0]);
retriever = new UpdateInfoRetriever();
retriever.start();
assertNull(retriever.getUpdateInfo());
waitfor(retriever);
assertFalse(connection.hasFailed());
assertNull(retriever.getUpdateInfo());
check(connection);
}
@Test
public void testRandomInputData() {
Random rnd = new Random();
for (int i=0; i<10; i++) {
int size = (int) ((1 + 0.3 * rnd.nextGaussian()) * Math.pow(i, 6));
byte[] buf = new byte[size];
rnd.nextBytes(buf);
HttpURLConnectionMock connection = setup();
connection.setResponseCode(Communicator.UPDATE_INFO_UPDATE_AVAILABLE);
connection.setContent(buf);
UpdateInfoRetriever retriever = new UpdateInfoRetriever();
retriever.start();
assertNull(retriever.getUpdateInfo());
waitfor(retriever);
assertFalse(connection.hasFailed());
assertNull(retriever.getUpdateInfo());
check(connection);
}
}
private void waitfor(UpdateInfoRetriever retriever) {
long t = System.currentTimeMillis();
while (retriever.isRunning()) {
if (System.currentTimeMillis() >= t+ALLOWANCE) {
fail("retriever took too long to respond");
}
try {
Thread.sleep(10);
} catch (InterruptedException e) { }
}
System.out.println("Waiting took " + (System.currentTimeMillis()-t) + " ms");
}
}

View File

@ -13,6 +13,9 @@ public class ComponentCompareTest {
@Test @Test
public void testComponentEquality() { public void testComponentEquality() {
System.out.println("TEST CLASSPATH: " + System.getProperty("java.class.path"));
Rocket r1 = net.sf.openrocket.util.TestRockets.makeBigBlue(); Rocket r1 = net.sf.openrocket.util.TestRockets.makeBigBlue();
Rocket r2 = net.sf.openrocket.util.TestRockets.makeBigBlue(); Rocket r2 = net.sf.openrocket.util.TestRockets.makeBigBlue();