adds documentation written before update

This commit is contained in:
Luiz Victor Linhares Rocha 2016-10-24 11:05:08 -02:00
commit f0ecd6f495
20 changed files with 974 additions and 242 deletions

View File

@ -36,9 +36,7 @@ public class Appearance {
* @param shine shine of the appearance, will be clamped between 0 and 1 * @param shine shine of the appearance, will be clamped between 0 and 1
*/ */
public Appearance(final Color paint, final double shine) { public Appearance(final Color paint, final double shine) {
this.paint = paint; this(paint,shine,null);
this.shine = MathUtil.clamp(shine, 0, 1);
this.texture = null;
} }
/** /**

View File

@ -4,12 +4,17 @@ import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
/** /**
* A texture that can be applied by an Appearance. This class is immutable. * A texture that can be applied by an Appearance. an object of this class is immutable.
* *
* @author Bill Kuker <bkuker@billkuker.com> * @author Bill Kuker <bkuker@billkuker.com>
*/ */
public class Decal { public class Decal {
/**
* enum to flag what happens on edge in a decal
*
*
*/
public static enum EdgeMode { public static enum EdgeMode {
REPEAT("TextureWrap.Repeat"), MIRROR("TextureWrap.Mirror"), CLAMP("TextureWrap.Clamp"), STICKER("TextureWrap.Sticker"); REPEAT("TextureWrap.Repeat"), MIRROR("TextureWrap.Mirror"), CLAMP("TextureWrap.Clamp"), STICKER("TextureWrap.Sticker");
private final String transName; private final String transName;
@ -29,6 +34,16 @@ public class Decal {
private final DecalImage image; private final DecalImage image;
private final EdgeMode mode; private final EdgeMode mode;
/**
* Builds a new decal with the given itens
*
* @param offset The offset of the decal, in coordinate obejct format
* @param center The position of the center of the decal, in coordinate object format
* @param scale The scale of the decal, in coordinate obejct format
* @param rotation Rotation of the decal, in radians
* @param image The image itself
* @param mode The description of Edge behaviour
*/
public Decal(final Coordinate offset, final Coordinate center, final Coordinate scale, final double rotation, public Decal(final Coordinate offset, final Coordinate center, final Coordinate scale, final double rotation,
final DecalImage image, final EdgeMode mode) { final DecalImage image, final EdgeMode mode) {
this.offset = offset; this.offset = offset;
@ -39,26 +54,56 @@ public class Decal {
this.mode = mode; this.mode = mode;
} }
/**
* returns the offset, in coordinates object format
*
* @return offset coordinates of the decal
*/
public Coordinate getOffset() { public Coordinate getOffset() {
return offset; return offset;
} }
/**
* return the center, in coordinates object format
*
* @return The center coordinates of the decal
*/
public Coordinate getCenter() { public Coordinate getCenter() {
return center; return center;
} }
/**
* return the scaling of the decal, in coordinate format
*
* @return the scale coordinates of the decal
*/
public Coordinate getScale() { public Coordinate getScale() {
return scale; return scale;
} }
/**
* returns the rotation of the decal, in radians
*
* @return the rotation of the decal, in radians
*/
public double getRotation() { public double getRotation() {
return rotation; return rotation;
} }
/**
* return the edge behaviour of the decal
*
* @return the edge behaviour of the decal
*/
public EdgeMode getEdgeMode() { public EdgeMode getEdgeMode() {
return mode; return mode;
} }
/**
* returns the image of the decal itself
*
* @return the image of the decal itself
*/
public DecalImage getImage() { public DecalImage getImage() {
return image; return image;
} }

View File

@ -7,13 +7,37 @@ import java.io.InputStream;
import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.ChangeSource;
/**
* Interface to handle image files for declas
*
*/
public interface DecalImage extends ChangeSource, Comparable<DecalImage> { public interface DecalImage extends ChangeSource, Comparable<DecalImage> {
/**
* returns the name of the file path of the image
* @return name of file path
*/
public String getName(); public String getName();
/**
* gets the Stream of bytes representing the image itself
*
* @return the Stream of bytes representing the image
* @throws FileNotFoundException
* @throws IOException
*/
public InputStream getBytes() throws FileNotFoundException, IOException; public InputStream getBytes() throws FileNotFoundException, IOException;
/**
* exports an image into the File
* @param file The File handler object
* @throws IOException
*/
public void exportImage(File file) throws IOException; public void exportImage(File file) throws IOException;
/**
* wake up call to listeners
* @param source The source of the wake up call
*/
public void fireChangeEvent(Object source); public void fireChangeEvent(Object source);
} }

View File

@ -23,8 +23,22 @@ import net.sf.openrocket.rocketcomponent.TubeFinSet;
import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Color;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
/**
*
* Class defining the default images of the application
*
*/
public class DefaultAppearance { public class DefaultAppearance {
/**
* returns a simple appearance with the image in the path with
* default color
* no shining
* no offset, origin center and scale 1
*
* @param resource the path file to the resource
* @return
*/
private static Appearance simple(String resource) { private static Appearance simple(String resource) {
return new Appearance( return new Appearance(
new Color(1, 1, 1), new Color(1, 1, 1),
@ -37,6 +51,14 @@ public class DefaultAppearance {
new ResourceDecalImage(resource), EdgeMode.REPEAT)); new ResourceDecalImage(resource), EdgeMode.REPEAT));
}; };
/**
* returns the image with custom color and shine
*
* @param base base color for the image
* @param shine the custom shine property
* @param resource the file path to the image
* @return The appearance with custom color and shine.
*/
private static Appearance simpleAlpha(Color base, float shine, String resource) { private static Appearance simpleAlpha(Color base, float shine, String resource) {
return new Appearance( return new Appearance(
base, base,
@ -69,6 +91,12 @@ public class DefaultAppearance {
private static HashMap<Color, Appearance> plastics = new HashMap<Color, Appearance>(); private static HashMap<Color, Appearance> plastics = new HashMap<Color, Appearance>();
/**
* gets the appearance correspondent to the plastic with the given color
* also caches the plastics
* @param c the color of the plastics
* @return The plastic appearance with the given color
*/
private static Appearance getPlastic(Color c) { private static Appearance getPlastic(Color c) {
if (!plastics.containsKey(c)) { if (!plastics.containsKey(c)) {
plastics.put(c, new Appearance(c, .3)); plastics.put(c, new Appearance(c, .3));
@ -76,6 +104,12 @@ public class DefaultAppearance {
return plastics.get(c); return plastics.get(c);
} }
/**
* gets the default based on the type of the rocket component
*
* @param c the rocket component
* @return the default appearance for that type of rocket component
*/
public static Appearance getDefaultAppearance(RocketComponent c) { public static Appearance getDefaultAppearance(RocketComponent c) {
if (c instanceof BodyTube) if (c instanceof BodyTube)
return ESTES_BT; return ESTES_BT;
@ -100,6 +134,12 @@ public class DefaultAppearance {
return Appearance.MISSING; return Appearance.MISSING;
} }
/**
* gets the default motor texture based on the manufacturer
* returns reusable motor texture as default
* @param m The motor object
* @return The default appearance for the motor
*/
public static Appearance getDefaultAppearance(Motor m) { public static Appearance getDefaultAppearance(Motor m) {
if (m instanceof ThrustCurveMotor) { if (m instanceof ThrustCurveMotor) {
ThrustCurveMotor tcm = (ThrustCurveMotor) m; ThrustCurveMotor tcm = (ThrustCurveMotor) m;

View File

@ -8,11 +8,20 @@ import java.io.InputStream;
import net.sf.openrocket.appearance.DecalImage; import net.sf.openrocket.appearance.DecalImage;
import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.util.StateChangeListener;
/**
*
* Default implementation class of DecalImage
*
*/
public class ResourceDecalImage implements DecalImage { public class ResourceDecalImage implements DecalImage {
/** File path to the image*/
final String resource; final String resource;
/**
* main constructor, stores the file path given
* @param resource
*/
public ResourceDecalImage(final String resource) { public ResourceDecalImage(final String resource) {
this.resource = resource; this.resource = resource;
} }

View File

@ -6,18 +6,30 @@ import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.BuildProperties; import net.sf.openrocket.util.BuildProperties;
import net.sf.openrocket.util.ComparablePair; import net.sf.openrocket.util.ComparablePair;
/**
*
* class that stores the update information of the application
*
*/
public class UpdateInfo { public class UpdateInfo {
private final String latestVersion; private final String latestVersion;
private final ArrayList<ComparablePair<Integer, String>> updates; private final ArrayList<ComparablePair<Integer, String>> updates;
/**
* loads the default information
*/
public UpdateInfo() { public UpdateInfo() {
this.latestVersion = BuildProperties.getVersion(); this.latestVersion = BuildProperties.getVersion();
this.updates = new ArrayList<ComparablePair<Integer, String>>(); this.updates = new ArrayList<ComparablePair<Integer, String>>();
} }
/**
* loads a custom update information into the cache
* @param version String with the version
* @param updates The list of updates contained in the version
*/
public UpdateInfo(String version, List<ComparablePair<Integer, String>> updates) { public UpdateInfo(String version, List<ComparablePair<Integer, String>> updates) {
this.latestVersion = version; this.latestVersion = version;
this.updates = new ArrayList<ComparablePair<Integer, String>>(updates); this.updates = new ArrayList<ComparablePair<Integer, String>>(updates);

View File

@ -41,10 +41,11 @@ public class UpdateInfoRetriever {
* Check whether the update info fetching is still in progress. * Check whether the update info fetching is still in progress.
* *
* @return <code>true</code> if the communication is still in progress. * @return <code>true</code> if the communication is still in progress.
* @throws IllegalStateException if {@link #startFetchUpdateInfo()} has not been called
*/ */
public boolean isRunning() { public boolean isRunning() {
if (fetcher == null) { if (fetcher == null) {
throw new IllegalStateException("startFetchUpdateInfo() has not been called"); throw new IllegalStateException("startFetchUpdateInfo() has not been called");
} }
return fetcher.isAlive(); return fetcher.isAlive();
} }
@ -81,43 +82,76 @@ public class UpdateInfoRetriever {
*/ */
/* package-private */ /* package-private */
static UpdateInfo parseUpdateInput(Reader r) throws IOException { static UpdateInfo parseUpdateInput(Reader r) throws IOException {
BufferedReader reader; BufferedReader reader = convertToBufferedReader(r);
if (r instanceof BufferedReader) {
reader = (BufferedReader) r;
} else {
reader = new BufferedReader(r);
}
String version = null; String version = null;
ArrayList<ComparablePair<Integer, String>> updates = ArrayList<ComparablePair<Integer, String>> updates =
new ArrayList<ComparablePair<Integer, String>>(); new ArrayList<ComparablePair<Integer, String>>();
String str = reader.readLine(); String str = reader.readLine();
while (str != null) { while (str != null) {
if (str.matches("^Version: *[0-9]+\\.[0-9]+\\.[0-9]+[a-zA-Z0-9.-]* *$")) { if (isHeader(str)) {
version = str.substring(8).trim(); version = str.substring(8).trim();
} else if (str.matches("^[0-9]+:\\p{Print}+$")) { } else if (isUpdateToken(str)) {
int index = str.indexOf(':'); ComparablePair<Integer, String> update = parseUpdateToken(str);
int value = Integer.parseInt(str.substring(0, index)); if(update != null)
String desc = str.substring(index + 1).trim(); updates.add(update);
if (!desc.equals("")) {
updates.add(new ComparablePair<Integer, String>(value, desc));
}
} }
// Ignore anything else
str = reader.readLine(); str = reader.readLine();
} }
if (version != null) { if (version == null)
return new UpdateInfo(version, updates);
} else {
return null; return null;
} return new UpdateInfo(version, updates);
} }
/**
* parses a line of a connection content into the information of an update
* @param str the line of the connection
* @return the update information
*/
private static ComparablePair<Integer, String> parseUpdateToken(String str){
int index = str.indexOf(':');
int value = Integer.parseInt(str.substring(0, index));
String desc = str.substring(index + 1).trim();
if (desc.equals(""))
return null;
return new ComparablePair<Integer, String>(value, desc);
}
/**
* checks if a string contains and update information
* @param str the string itself
* @return true for when the string has an update
* false otherwise
*/
private static boolean isUpdateToken(String str) {
return str.matches("^[0-9]+:\\p{Print}+$");
}
/**
* check if the string is formated as an update list header
* @param str the string to be checked
* @return true if str is a header, false otherwise
*/
private static boolean isHeader(String str) {
return str.matches("^Version: *[0-9]+\\.[0-9]+\\.[0-9]+[a-zA-Z0-9.-]* *$");
}
/**
* convert, if not yet converted, a Reader into a buffered reader
* @param r the Reader object
* @return the Reader as a BufferedReader Object
*/
private static BufferedReader convertToBufferedReader(Reader r) {
if (r instanceof BufferedReader)
return (BufferedReader) r;
return new BufferedReader(r);
}
/** /**
* An asynchronous task that fetches and parses the update info. * An asynchronous task that fetches and parses the update info.
* *
@ -137,11 +171,193 @@ public class UpdateInfoRetriever {
} }
} }
/**
* Establishes a connection with data of previous updates
* @throws IOException
*/
private void doConnection() throws IOException { private void doConnection() throws IOException {
String url = Communicator.UPDATE_INFO_URL + "?" + Communicator.VERSION_PARAM + "=" HttpURLConnection connection = getConnection(getUrl());
+ Communicator.encode(BuildProperties.getVersion()); InputStream is = null;
try {
connection.connect();
if(!checkConnection(connection))
return;
if(!checkContentType(connection))
return;
is = new LimitedInputStream(connection.getInputStream(), Communicator.MAX_INPUT_BYTES);
parseUpdateInput(buildBufferedReader(connection,is));
} finally {
try {
if (is != null)
is.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Parses the data received in a buffered reader
* @param reader The reader object
* @throws IOException If anything bad happens
*/
private void parseUpdateInput(BufferedReader reader) throws IOException{
String version = null;
ArrayList<ComparablePair<Integer, String>> updates =
new ArrayList<ComparablePair<Integer, String>>();
String line = reader.readLine();
while (line != null) {
if (isHeader(line)) {
version = parseHeader(line);
} else if (isUpdateInfo(line)) {
updates.add(parseUpdateInfo(line));
}
line = reader.readLine();
}
if (isInvalidVersion(version)) {
log.warn("Invalid version received, ignoring.");
return;
}
info = new UpdateInfo(version, updates);
log.info("Found update: " + info);
}
/**
* parses a line into it's version name
* @param line the string of the header
* @return the version in it's right format
*/
private String parseHeader(String line) {
return line.substring(8).trim();
}
/**
* parses a line into it's correspondent update information
* @param line the line to be parsed
* @return update information from the line
*/
private ComparablePair<Integer,String> parseUpdateInfo(String line){
String[] split = line.split(":", 2);
int n = Integer.parseInt(split[0]);
return new ComparablePair<Integer, String>(n, split[1].trim());
}
/**
* checks if a line contains an update information
* @param line the line to be checked
* @return true if the line caontain an update information
* false otherwise
*/
private boolean isUpdateInfo(String line) {
return line.matches("^[0-9]{1,9}:\\P{Cntrl}{1,300}$");
}
/**
* checks if a line is a header of an update list
* @param line the line to be checked
* @return true if line is a header, false otherwise
*/
private boolean isHeader(String line) {
return line.matches("^Version:[a-zA-Z0-9._ -]{1,30}$");
}
/**
* checks if a String is a valid version
* @param version the String to be checked
* @return true if it's valid, false otherwise
*/
private boolean isInvalidVersion(String version) {
return version == null || version.length() == 0 ||
version.equalsIgnoreCase(BuildProperties.getVersion());
}
/**
* builds a buffered reader from an open connection and a stream
* @param connection The connection
* @param is The input stream
* @return The Buffered reader created
* @throws IOException
*/
private BufferedReader buildBufferedReader(HttpURLConnection connection, InputStream is) throws IOException {
String encoding = connection.getContentEncoding();
if (encoding == null || encoding.equals(""))
encoding = "UTF-8";
return new BufferedReader(new InputStreamReader(is, encoding));
}
/**
* check if the content of a connection is valid
* @param connection the connection to be checked
* @return true if the content is valid, false otherwise
*/
private boolean checkContentType(HttpURLConnection connection) {
String contentType = connection.getContentType();
if (contentType == null ||
contentType.toLowerCase(Locale.ENGLISH).indexOf(Communicator.UPDATE_INFO_CONTENT_TYPE) < 0) {
// Unknown response type
log.warn("Unknown Content-type received:" + contentType);
return false;
}
return true;
}
/**
* check if a connection is responsive and valid
* @param connection the connection to be checked
* @return true if connection is ok, false otherwise
* @throws IOException
*/
private boolean checkConnection(HttpURLConnection connection) throws IOException{
log.debug("Update response code: " + connection.getResponseCode());
if (noUpdatesAvailable(connection)) {
log.info("No updates available");
info = new UpdateInfo();
return false;
}
if (!updateAvailable(connection)) {
// Error communicating with server
log.warn("Unknown server response code: " + connection.getResponseCode());
return false;
}
return true;
}
/**
* checks if a connection sent an update available flag
* @param connection the connection to be checked
* @return true if the response was an update available flag
* false otherwise
* @throws IOException if anything goes wrong
*/
private boolean updateAvailable(HttpURLConnection connection) throws IOException {
return connection.getResponseCode() == Communicator.UPDATE_INFO_UPDATE_AVAILABLE;
}
/**
* checks if a connection sent an update unavailable flag
* @param connection the connection to be checked
* @return true if the response was an no update available flag
* false otherwise
* @throws IOException if anything goes wrong
*/
private boolean noUpdatesAvailable(HttpURLConnection connection) throws IOException {
return connection.getResponseCode() == Communicator.UPDATE_INFO_NO_UPDATE_CODE;
}
/**
* Builds a connection with the given url
* @param url the url
* @return connection base on the url
* @throws IOException
*/
private HttpURLConnection getConnection(String url) throws IOException{
HttpURLConnection connection = Communicator.connectionSource.getConnection(url); HttpURLConnection connection = Communicator.connectionSource.getConnection(url);
connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT); connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT);
@ -165,80 +381,16 @@ public class UpdateInfoRetriever {
connection.setRequestProperty("X-OpenRocket-Locale", connection.setRequestProperty("X-OpenRocket-Locale",
Communicator.encode(Locale.getDefault().toString())); Communicator.encode(Locale.getDefault().toString()));
connection.setRequestProperty("X-OpenRocket-CPUs", "" + Runtime.getRuntime().availableProcessors()); connection.setRequestProperty("X-OpenRocket-CPUs", "" + Runtime.getRuntime().availableProcessors());
return connection;
InputStream is = null; }
try {
connection.connect(); /**
* builds the default url for fetching updates
log.debug("Update response code: " + connection.getResponseCode()); * @return the string with an url for fethcing updates
*/
if (connection.getResponseCode() == Communicator.UPDATE_INFO_NO_UPDATE_CODE) { private String getUrl() {
// No updates are available return Communicator.UPDATE_INFO_URL + "?" + Communicator.VERSION_PARAM + "="
log.info("No updates available"); + Communicator.encode(BuildProperties.getVersion());
info = new UpdateInfo();
return;
}
if (connection.getResponseCode() != Communicator.UPDATE_INFO_UPDATE_AVAILABLE) {
// Error communicating with server
log.warn("Unknown server response code: " + connection.getResponseCode());
return;
}
String contentType = connection.getContentType();
if (contentType == null ||
contentType.toLowerCase(Locale.ENGLISH).indexOf(Communicator.UPDATE_INFO_CONTENT_TYPE) < 0) {
// Unknown response type
log.warn("Unknown Content-type received:" + contentType);
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(BuildProperties.getVersion())) {
// Invalid response
log.warn("Invalid version received, ignoring.");
return;
}
info = new UpdateInfo(version, updates);
log.info("Found update: " + info);
} finally {
try {
if (is != null)
is.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
} }
} }
} }

View File

@ -43,7 +43,7 @@ public abstract class AsynchronousDatabaseLoader {
/** /**
* Return whether loading the database has ended. * @return whether loading the database has ended.
*/ */
public boolean isLoaded() { public boolean isLoaded() {
return endedLoading; return endedLoading;
@ -86,10 +86,27 @@ public abstract class AsynchronousDatabaseLoader {
} }
} }
/**
*
*/
private void doLoad() { private void doLoad() {
// Pause for indicated startup time // Pause for indicated startup time
pauseForStartupTime();
loadDatabase();
synchronized (this) {
endedLoading = true;
this.notifyAll();
}
}
/**
* waits the startup time before loading the database
*/
private void pauseForStartupTime() {
long startLoading = System.currentTimeMillis() + startupDelay; long startLoading = System.currentTimeMillis() + startupDelay;
while (!inUse && System.currentTimeMillis() < startLoading) { while (!inUse && System.currentTimeMillis() < startLoading) {
synchronized (this) { synchronized (this) {
@ -99,16 +116,11 @@ public abstract class AsynchronousDatabaseLoader {
} }
} }
} }
loadDatabase();
synchronized (this) {
endedLoading = true;
this.notifyAll();
}
} }
/**
* method that actually load the database
*/
protected abstract void loadDatabase(); protected abstract void loadDatabase();

View File

@ -6,10 +6,23 @@ import net.sf.openrocket.preset.ComponentPreset;
public interface ComponentPresetDao { public interface ComponentPresetDao {
/**
* return a list all components
* @return list of all components
*/
public List<ComponentPreset> listAll(); public List<ComponentPreset> listAll();
/**
* insert a component preset into a database
* @param preset the component to be inserted into the database
*/
public void insert( ComponentPreset preset ); public void insert( ComponentPreset preset );
/**
* return all components preset matching the given type
* @param type the searched type
* @return the list of components matching the type
*/
public List<ComponentPreset> listForType( ComponentPreset.Type type ); public List<ComponentPreset> listForType( ComponentPreset.Type type );
/** /**
@ -21,13 +34,35 @@ public interface ComponentPresetDao {
* @return * @return
*/ */
public List<ComponentPreset> listForType( ComponentPreset.Type type, boolean favorite ); public List<ComponentPreset> listForType( ComponentPreset.Type type, boolean favorite );
/**
* Returns a list of components presets of multiple types
* @param type the types to be searched for
* @return
*/
public List<ComponentPreset> listForTypes( ComponentPreset.Type ... type ); public List<ComponentPreset> listForTypes( ComponentPreset.Type ... type );
/**
* Returns a list of components preset of each type in the list
* @param types the list of types to be searched for
* @return
*/
public List<ComponentPreset> listForTypes( List<ComponentPreset.Type> types ); public List<ComponentPreset> listForTypes( List<ComponentPreset.Type> types );
/**
* set or reset a component preset as favorite
* @param preset the preset to be set as favorite
* @param type the type of the preset
* @param favorite true to set, false to reset as favorite
*/
public void setFavorite( ComponentPreset preset, ComponentPreset.Type type, boolean favorite ); public void setFavorite( ComponentPreset preset, ComponentPreset.Type type, boolean favorite );
/**
* returns a list of components preset based on manufacturer and part number
* @param manufacturer the manufacturer to be searched for
* @param partNo the part number of the component
* @return the resulting list of the search
*/
public List<ComponentPreset> find( String manufacturer, String partNo ); public List<ComponentPreset> find( String manufacturer, String partNo );
} }

View File

@ -19,6 +19,7 @@ import net.sf.openrocket.database.DatabaseListener;
*/ */
public class Database<T extends Comparable<T>> extends AbstractSet<T> { public class Database<T extends Comparable<T>> extends AbstractSet<T> {
/** the list that contains the data from the database itself*/
protected final List<T> list = new ArrayList<T>(); protected final List<T> list = new ArrayList<T>();
private final ArrayList<DatabaseListener<T>> listeners = new ArrayList<DatabaseListener<T>>(); private final ArrayList<DatabaseListener<T>> listeners = new ArrayList<DatabaseListener<T>>();
@ -33,6 +34,10 @@ public class Database<T extends Comparable<T>> extends AbstractSet<T> {
return list.size(); return list.size();
} }
/**
* {@inheritDoc}
* fires add event
*/
@Override @Override
public boolean add(T element) { public boolean add(T element) {
int index; int index;
@ -71,17 +76,27 @@ public class Database<T extends Comparable<T>> extends AbstractSet<T> {
return list.indexOf(m); return list.indexOf(m);
} }
/**
* adds a listener for database changes
* @param listener the listener
*/
public void addDatabaseListener(DatabaseListener<T> listener) { public void addDatabaseListener(DatabaseListener<T> listener) {
listeners.add(listener); listeners.add(listener);
} }
/**
* removes a listener from the list os listeners
* @param listener
*/
public void removeChangeListener(DatabaseListener<T> listener) { public void removeChangeListener(DatabaseListener<T> listener) {
listeners.remove(listener); listeners.remove(listener);
} }
/**
* wake up call for database listeners for when elements are added
* @param element the element added
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void fireAddEvent(T element) { protected void fireAddEvent(T element) {
Object[] array = listeners.toArray(); Object[] array = listeners.toArray();
@ -90,6 +105,10 @@ public class Database<T extends Comparable<T>> extends AbstractSet<T> {
} }
} }
/**
* wake up call for database listeners when elements are removed
* @param element the removed element
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void fireRemoveEvent(T element) { protected void fireRemoveEvent(T element) {
Object[] array = listeners.toArray(); Object[] array = listeners.toArray();
@ -98,10 +117,6 @@ public class Database<T extends Comparable<T>> extends AbstractSet<T> {
} }
} }
/** /**
* Iterator class implementation that fires changes if remove() is called. * Iterator class implementation that fires changes if remove() is called.
*/ */
@ -120,6 +135,10 @@ public class Database<T extends Comparable<T>> extends AbstractSet<T> {
return current; return current;
} }
/**
* {@inheritDoc}
* fires remove event
*/
@Override @Override
public void remove() { public void remove() {
iterator.remove(); iterator.remove();

View File

@ -1,9 +1,24 @@
package net.sf.openrocket.database; package net.sf.openrocket.database;
/**
* interface defining listeners for database
*
* @param <T> type stored in the database
*/
public interface DatabaseListener<T extends Comparable<T>> { public interface DatabaseListener<T extends Comparable<T>> {
/**
* action for when elements are added
* @param element the element added
* @param source the database of which the element was added
*/
public void elementAdded(T element, Database<T> source); public void elementAdded(T element, Database<T> source);
/**
* action for when elements are removed
* @param element the removed element
* @param source the database on which the element was removed
*/
public void elementRemoved(T element, Database<T> source); public void elementRemoved(T element, Database<T> source);
} }

View File

@ -117,7 +117,13 @@ public class Databases {
BULK_MATERIAL.addDatabaseListener(listener); BULK_MATERIAL.addDatabaseListener(listener);
} }
/**
* builds a new material based on the parameters given
* @param type The type of material
* @param baseName the name of material
* @param density density
* @return a new onejct withe the material data
*/
private static Material newMaterial(Type type, String baseName, double density) { private static Material newMaterial(Type type, String baseName, double density) {
String name = trans.get("material", baseName); String name = trans.get("material", baseName);
return Material.newMaterial(type, name, density, false); return Material.newMaterial(type, name, density, false);
@ -145,21 +151,7 @@ public class Databases {
* @return the material, or <code>null</code> if not found. * @return the material, or <code>null</code> if not found.
*/ */
public static Material findMaterial(Material.Type type, String baseName) { public static Material findMaterial(Material.Type type, String baseName) {
Database<Material> db; Database<Material> db = getDatabase(type);
switch (type) {
case BULK:
db = BULK_MATERIAL;
break;
case SURFACE:
db = SURFACE_MATERIAL;
break;
case LINE:
db = LINE_MATERIAL;
break;
default:
throw new IllegalArgumentException("Illegal material type: " + type);
}
String name = trans.get("material", baseName); String name = trans.get("material", baseName);
for (Material m : db) { for (Material m : db) {
@ -170,6 +162,24 @@ public class Databases {
return null; return null;
} }
/**
* gets the specific database with the given type
* @param type the desired type
* @return the database of the type given
*/
private static Database<Material> getDatabase(Material.Type type){
switch (type) {
case BULK:
return BULK_MATERIAL;
case SURFACE:
return SURFACE_MATERIAL;
case LINE:
return LINE_MATERIAL;
default:
throw new IllegalArgumentException("Illegal material type: " + type);
}
}
/** /**
* Find a material from the database or return a new user defined material if the specified * Find a material from the database or return a new user defined material if the specified
@ -184,21 +194,7 @@ public class Databases {
* @return the material object from the database or a new material. * @return the material object from the database or a new material.
*/ */
public static Material findMaterial(Material.Type type, String baseName, double density) { public static Material findMaterial(Material.Type type, String baseName, double density) {
Database<Material> db; Database<Material> db = getDatabase(type);
switch (type) {
case BULK:
db = BULK_MATERIAL;
break;
case SURFACE:
db = SURFACE_MATERIAL;
break;
case LINE:
db = LINE_MATERIAL;
break;
default:
throw new IllegalArgumentException("Illegal material type: " + type);
}
String name = trans.get("material", baseName); String name = trans.get("material", baseName);
for (Material m : db) { for (Material m : db) {

View File

@ -326,4 +326,4 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
} }
} }

View File

@ -7,21 +7,44 @@ import java.io.InputStream;
import net.sf.openrocket.util.AbstractChangeSource; import net.sf.openrocket.util.AbstractChangeSource;
import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.ChangeSource;
/**
*
* Class handler of documents attachments
*
*/
public abstract class Attachment extends AbstractChangeSource implements Comparable<Attachment>, ChangeSource { public abstract class Attachment extends AbstractChangeSource implements Comparable<Attachment>, ChangeSource {
private final String name; private final String name;
/**
* default constructor
* @param name the attachment name
*/
public Attachment(String name) { public Attachment(String name) {
super(); super();
this.name = name; this.name = name;
} }
/**
* returns the name of attachment
* @return name of attachment
*/
public String getName() { public String getName() {
return name; return name;
} }
/**
* returns the stream of bytes representing the attachment
* @return the stream of bytes representing the attachment
* @throws FileNotFoundException
* @throws IOException
*/
public abstract InputStream getBytes() throws FileNotFoundException, IOException; public abstract InputStream getBytes() throws FileNotFoundException, IOException;
/**
* {@inheritDoc}
* considers only the name to equals
*/
@Override @Override
public int compareTo(Attachment o) { public int compareTo(Attachment o) {
return this.name.compareTo(o.name); return this.name.compareTo(o.name);

View File

@ -25,13 +25,28 @@ import net.sf.openrocket.util.ChangeSource;
import net.sf.openrocket.util.FileUtils; import net.sf.openrocket.util.FileUtils;
import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.util.StateChangeListener;
/**
*
* Class that handles decal usage registration
*
*/
public class DecalRegistry { public class DecalRegistry {
/**
* default constructor, does nothing
*/
DecalRegistry() { DecalRegistry() {
} }
/** the decal usage map*/
private Map<String, DecalImageImpl> registeredDecals = new HashMap<String, DecalImageImpl>(); private Map<String, DecalImageImpl> registeredDecals = new HashMap<String, DecalImageImpl>();
/**
* returns a new decal with the same image but with unique names
* supports only classes and subclasses of DecalImageImpl
* @param original the decal to be made unique
* @return
*/
public DecalImage makeUniqueImage(DecalImage original) { public DecalImage makeUniqueImage(DecalImage original) {
if (!(original instanceof DecalImageImpl)) { if (!(original instanceof DecalImageImpl)) {
@ -57,6 +72,11 @@ public class DecalRegistry {
} }
/**
* get the image from an attachment
* @param attachment
* @return
*/
public DecalImage getDecalImage(Attachment attachment) { public DecalImage getDecalImage(Attachment attachment) {
String decalName = attachment.getName(); String decalName = attachment.getName();
DecalImageImpl d; DecalImageImpl d;
@ -166,6 +186,10 @@ public class DecalRegistry {
} }
} }
/**
*
* @return
*/
File getFileSystemLocation() { File getFileSystemLocation() {
return fileSystemLocation; return fileSystemLocation;
} }
@ -205,6 +229,11 @@ public class DecalRegistry {
} }
/**
* sercha
* @param file
* @return
*/
private DecalImageImpl findDecalForFile(File file) { private DecalImageImpl findDecalForFile(File file) {
for (DecalImageImpl d : registeredDecals.values()) { for (DecalImageImpl d : registeredDecals.values()) {
@ -240,28 +269,25 @@ public class DecalRegistry {
private static final int NUMBER_INDEX = 3; private static final int NUMBER_INDEX = 3;
private static final int EXTENSION_INDEX = 4; private static final int EXTENSION_INDEX = 4;
/**
* Makes a unique name for saving decal files in case the name already exists
* @param name the name of the decal
* @return the name formated and unique
*/
private String makeUniqueName(String name) { private String makeUniqueName(String name) {
String newName = name; String newName = checkPathConsistency(name);
if (!newName.startsWith("decals/")) { String basename = getGroup(BASE_NAME_INDEX,fileNamePattern.matcher(newName));
newName = "decals/" + name; String extension = getGroup(EXTENSION_INDEX,fileNamePattern.matcher(newName));
}
String basename = "";
String extension = "";
Matcher nameMatcher = fileNamePattern.matcher(newName);
if (nameMatcher.matches()) {
basename = nameMatcher.group(BASE_NAME_INDEX);
extension = nameMatcher.group(EXTENSION_INDEX);
}
Set<Integer> counts = new TreeSet<Integer>(); Set<Integer> counts = new TreeSet<Integer>();
boolean needsRewrite = false; boolean needsRewrite = false;
for (DecalImageImpl d : registeredDecals.values()) { for (DecalImageImpl d : registeredDecals.values()) {
Matcher m = fileNamePattern.matcher(d.getName()); Matcher m = fileNamePattern.matcher(d.getName());
if (m.matches()) { if (m.matches()) {
if (basename.equals(m.group(BASE_NAME_INDEX)) && extension.equals(m.group(EXTENSION_INDEX))) { if (isofSameBaseAndExtension(m, basename, extension)) {
String intString = m.group(NUMBER_INDEX); String intString = m.group(NUMBER_INDEX);
if (intString != null) { if (intString != null) {
Integer i = Integer.parseInt(intString); Integer i = Integer.parseInt(intString);
@ -278,13 +304,54 @@ public class DecalRegistry {
return newName; return newName;
} }
// find a missing integer; return MessageFormat.format("{0} ({1}).{2}", basename, findMissingInteger(counts),extension);
}
/**
* Searches the count for a new Integer
* @param counts the count set
* @return a unique integer in the count
*/
private Integer findMissingInteger(Set<Integer> counts) {
Integer newIndex = 1; Integer newIndex = 1;
while (counts.contains(newIndex)) { while (counts.contains(newIndex)) {
newIndex++; newIndex++;
} }
return newIndex;
return MessageFormat.format("{0} ({1}).{2}", basename, newIndex, extension); }
/**
* Tests if a matcher has the same basename and extension
* @param m the matcher being tested
* @param basename the basename
* @param extension the extension
* @return
*/
private boolean isofSameBaseAndExtension(Matcher m, String basename, String extension) {
return basename.equals(m.group(BASE_NAME_INDEX)) && extension.equals(m.group(EXTENSION_INDEX));
}
/**
* gets the String group from a matcher
* @param index the index of the group to
* @param matcher the matcher for the search
* @return the String according with the group, empty if there's no match
*/
private String getGroup(int index, Matcher matcher) {
if (matcher.matches())
return matcher.group(index);
return "";
}
/**
* checks if the name starts with "decals/"
* @param name the name being checked
* @return the name complete with the starting folder
*/
private String checkPathConsistency(String name){
if (name.startsWith("decals/"))
return name;
return "decals/" + name;
} }
} }

View File

@ -102,36 +102,56 @@ public class OpenRocketDocument implements ComponentChangeListener {
private final List<DocumentChangeListener> listeners = new ArrayList<DocumentChangeListener>(); private final List<DocumentChangeListener> listeners = new ArrayList<DocumentChangeListener>();
/**
* main constructor, enable events in the rocket
* and initializes the document
* @param rocket the rocket to be used in the document
*/
OpenRocketDocument(Rocket rocket) { OpenRocketDocument(Rocket rocket) {
this.rocket = rocket; this.rocket = rocket;
rocket.enableEvents(); rocket.enableEvents();
init(); init();
} }
/**
* initializes the document, clearing the undo cache and
* setting itself as a listener for changes in the rocket
*/
private void init() { private void init() {
clearUndo(); clearUndo();
rocket.addComponentChangeListener(this); rocket.addComponentChangeListener(this);
} }
/**
* adds a customExpression into the list
* @param expression the expression to be added
*/
public void addCustomExpression(CustomExpression expression) { public void addCustomExpression(CustomExpression expression) {
if (customExpressions.contains(expression)) { if (customExpressions.contains(expression)) {
log.info(Markers.USER_MARKER, "Could not add custom expression " + expression.getName() + " to document as document alerady has a matching expression."); log.info(Markers.USER_MARKER, "Could not add custom expression " + expression.getName() + " to document as document alerady has a matching expression.");
} else { }
customExpressions.add(expression); customExpressions.add(expression);
}
} }
/**
* remves
* @param expression
*/
public void removeCustomExpression(CustomExpression expression) { public void removeCustomExpression(CustomExpression expression) {
customExpressions.remove(expression); customExpressions.remove(expression);
} }
//TODO:LOW:this leaves the object custom expression exposed, is it supposed to be like that?
/**
*
* @return
*/
public List<CustomExpression> getCustomExpressions() { public List<CustomExpression> getCustomExpressions() {
return customExpressions; return customExpressions;
} }
/* /**
* Returns a set of all the flight data types defined or available in any way in the rocket document * @returns a set of all the flight data types defined or available in any way in the rocket document
*/ */
public Set<FlightDataType> getFlightDataTypes() { public Set<FlightDataType> getFlightDataTypes() {
Set<FlightDataType> allTypes = new LinkedHashSet<FlightDataType>(); Set<FlightDataType> allTypes = new LinkedHashSet<FlightDataType>();
@ -158,28 +178,50 @@ public class OpenRocketDocument implements ComponentChangeListener {
return allTypes; return allTypes;
} }
/**
* gets the rocket in the document
* @return the rocket in the document
*/
public Rocket getRocket() { public Rocket getRocket() {
return rocket; return rocket;
} }
/**
* returns the selected configuration from the rocket
* @return selected configuration from the rocket
*/
public FlightConfiguration getSelectedConfiguration() { public FlightConfiguration getSelectedConfiguration() {
return rocket.getSelectedConfiguration(); return rocket.getSelectedConfiguration();
} }
/**
* returns the File handler object for the document
* @return the File handler object for the document
*/
public File getFile() { public File getFile() {
return file; return file;
} }
/**
* set the file handler object for the document
* @param file the new file handler object
*/
public void setFile(File file) { public void setFile(File file) {
this.file = file; this.file = file;
} }
/**
* returns if the current rocket is saved
* @return if the current rocket is saved
*/
public boolean isSaved() { public boolean isSaved() {
return rocket.getModID() == savedID; return rocket.getModID() == savedID;
} }
/**
* sets the current rocket as saved, and none if false is given
* @param saved if the current rocket or none will be set to save
*/
public void setSaved(boolean saved) { public void setSaved(boolean saved) {
if (saved == false) if (saved == false)
this.savedID = -1; this.savedID = -1;
@ -197,33 +239,57 @@ public class OpenRocketDocument implements ComponentChangeListener {
} }
/**
* returns the decal list used in the document
* @return the decal list registered in the document
*/
public Collection<DecalImage> getDecalList() { public Collection<DecalImage> getDecalList() {
return decalRegistry.getDecalList(); return decalRegistry.getDecalList();
} }
/**
* returns the number of times the given decal was used
* @param img the decal to be counted
* @return the number of times
*/
public int countDecalUsage(DecalImage img) { public int countDecalUsage(DecalImage img) {
int count = 0; int count = 0;
Iterator<RocketComponent> it = rocket.iterator(); Iterator<RocketComponent> it = rocket.iterator();
while (it.hasNext()) { while (it.hasNext()) {
RocketComponent comp = it.next(); if(hasDecal(it.next(),img))
Appearance a = comp.getAppearance();
if (a == null) {
continue;
}
Decal d = a.getTexture();
if (d == null) {
continue;
}
if (img.equals(d.getImage())) {
count++; count++;
}
} }
return count; return count;
} }
//TODO: LOW: move this method to rocketComponent, Appearance and decal
//I see 3 layers of object accessed, seems unsafe
/**
* checks if a rocket component has the given decalImage
* @param comp the RocketComponent to be searched
* @param img the DecalImage to be checked
* @return if the comp has img
*/
private boolean hasDecal(RocketComponent comp, DecalImage img){
Appearance a = comp.getAppearance();
if(a == null)
return false;
Decal d = a.getTexture();
if(d == null)
return false;
if(img.equals(d.getImage()))
return true;
return false;
}
/**
* gets a unique identification for the given decal
* @param img the decal to be made unique
* @return the new unique decal
*/
public DecalImage makeUniqueDecal(DecalImage img) { public DecalImage makeUniqueDecal(DecalImage img) {
if (countDecalUsage(img) <= 1) { if (countDecalUsage(img) <= 1) {
return img; return img;
@ -231,26 +297,54 @@ public class OpenRocketDocument implements ComponentChangeListener {
return decalRegistry.makeUniqueImage(img); return decalRegistry.makeUniqueImage(img);
} }
/**
* gets the decal image from an attachment
* @param a the attachment
* @return the image from the attachment
*/
public DecalImage getDecalImage(Attachment a) { public DecalImage getDecalImage(Attachment a) {
return decalRegistry.getDecalImage(a); return decalRegistry.getDecalImage(a);
} }
/**
* gets a list of simulations in the document
* @return the simulations in the document
*/
public List<Simulation> getSimulations() { public List<Simulation> getSimulations() {
return simulations.clone(); return simulations.clone();
} }
/**
* gets the number of simulations in the document
* @return the number of simulations in the document
*/
public int getSimulationCount() { public int getSimulationCount() {
return simulations.size(); return simulations.size();
} }
/**
* the the Nth simulation from the document
* @param n simulation index
* @return the Nth simulation from the document, null if there's none
*/
public Simulation getSimulation(int n) { public Simulation getSimulation(int n) {
return simulations.get(n); return simulations.get(n);
} }
/**
* gets the index of the given simulation
* @param simulation the simulation being searched
* @return the index of the simulation in the document
*/
public int getSimulationIndex(Simulation simulation) { public int getSimulationIndex(Simulation simulation) {
return simulations.indexOf(simulation); return simulations.indexOf(simulation);
} }
/**
* adds simulation into the document
* fires document change event
* @param simulation the simulation to be added
*/
public void addSimulation(Simulation simulation) { public void addSimulation(Simulation simulation) {
simulations.add(simulation); simulations.add(simulation);
FlightConfigurationId simId = simulation.getId(); FlightConfigurationId simId = simulation.getId();
@ -260,33 +354,61 @@ public class OpenRocketDocument implements ComponentChangeListener {
fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
} }
/**
* adds the simulation to the Nth index, overwriting if there is already one
* fires change document event
* @param simulation the simulation to be added
* @param n the index to be added
*/
public void addSimulation(Simulation simulation, int n) { public void addSimulation(Simulation simulation, int n) {
simulations.add(n, simulation); simulations.add(n, simulation);
fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
} }
/**
* removes the specific simulation from the list
* @param simulation the simulation to be removed
*/
public void removeSimulation(Simulation simulation) { public void removeSimulation(Simulation simulation) {
simulations.remove(simulation); simulations.remove(simulation);
fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
} }
/**
* removes the Nth simulation from the document
* fires document change event
* @param n the Nth simulation
* @return the removed simulation
*/
public Simulation removeSimulation(int n) { public Simulation removeSimulation(int n) {
Simulation simulation = simulations.remove(n); Simulation simulation = simulations.remove(n);
fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
return simulation; return simulation;
} }
/**
* removes the flight configuration and simulation with the specific id
* @param configId
*/
public void removeFlightConfigurationAndSimulations(FlightConfigurationId configId) { public void removeFlightConfigurationAndSimulations(FlightConfigurationId configId) {
if (configId == null) { if (configId == null) {
return; return;
} }
removeSimulations(configId);
rocket.removeFlightConfiguration(configId);
}
/**
* removes all simulations with the specific configId
* @param configId the Flight Configuration Id that dictates which simulations shoul be removed
*/
private void removeSimulations(FlightConfigurationId configId) {
for (Simulation s : getSimulations()) { for (Simulation s : getSimulations()) {
// Assumes modifiable collection - which it is // Assumes modifiable collection - which it is
if (configId.equals(s.getId())) { if (configId.equals(s.getId())) {
removeSimulation(s); removeSimulation(s);
} }
} }
rocket.removeFlightConfiguration(configId);
} }
@ -331,42 +453,22 @@ public class OpenRocketDocument implements ComponentChangeListener {
*/ */
public void addUndoPosition(String description) { public void addUndoPosition(String description) {
if (storedDescription != null) { checkDescription(description);
logUndoError("addUndoPosition called while storedDescription=" + storedDescription +
" description=" + description);
}
// Check whether modifications have been done since last call // Check whether modifications have been done since last call
if (isCleanState()) { if(isCheckNoModification(description))
// No modifications
log.info("Adding undo position '" + description + "' to " + this + ", document was in clean state");
nextDescription = description;
return; return;
}
log.info("Adding undo position '" + description + "' to " + this + ", document is in unclean state"); log.info("Adding undo position '" + description + "' to " + this + ", document is in unclean state");
checkUndoPositionConsistency();
addStateToUndoHistory(description);
/* maintainMaximumUndoSize();
* Modifications have been made to the rocket. We should be at the end of the }
* undo history, but check for consistency and try to recover.
*/ /**
if (undoPosition != undoHistory.size() - 1) { *
logUndoError("undo position inconsistency"); */
} private void maintainMaximumUndoSize() {
while (undoPosition < undoHistory.size() - 1) {
undoHistory.removeLast();
undoDescription.removeLast();
}
// Add the current state to the undo history
undoHistory.add(rocket.copyWithOriginalID());
undoDescription.add(null);
nextDescription = description;
undoPosition++;
// Maintain maximum undo size
if (undoHistory.size() > UNDO_LEVELS + UNDO_MARGIN && undoPosition > UNDO_MARGIN) { if (undoHistory.size() > UNDO_LEVELS + UNDO_MARGIN && undoPosition > UNDO_MARGIN) {
for (int i = 0; i < UNDO_MARGIN; i++) { for (int i = 0; i < UNDO_MARGIN; i++) {
undoHistory.removeFirst(); undoHistory.removeFirst();
@ -375,6 +477,57 @@ public class OpenRocketDocument implements ComponentChangeListener {
} }
} }
} }
/**
* @param description
*/
private void addStateToUndoHistory(String description) {
// Add the current state to the undo history
undoHistory.add(rocket.copyWithOriginalID());
undoDescription.add(null);
nextDescription = description;
undoPosition++;
}
/**
* checks if there was or not modification, and logs
*
* @param description the description to be used in the log
* @return if there was or not modification
*/
private boolean isCheckNoModification(String description){
if (isCleanState()) {
// No modifications
log.info("Adding undo position '" + description + "' to " + this + ", document was in clean state");
nextDescription = description;
return true;
}
return false;
}
/**
* checks if the document already has a stored undo description
* logs if it has
*
* @param description undo description to be logged
*/
private void checkDescription(String description) {
if (storedDescription != null) {
logUndoError("addUndoPosition called while storedDescription=" + storedDescription +
" description=" + description);
}
}
/**
* If modifications have been made to the rocket. We should be at the end of the
* undo history, but check for consistency and try to recover.
*/
private void checkUndoPositionConsistency() {
if (undoPosition != undoHistory.size() - 1) {
logUndoError("undo position inconsistency");
}
removeRedoInfo();
}
/** /**
@ -436,18 +589,29 @@ public class OpenRocketDocument implements ComponentChangeListener {
" undoPosition=" + undoPosition + " undoHistory.size=" + undoHistory.size() + " undoPosition=" + undoPosition + " undoHistory.size=" + undoHistory.size() +
" isClean=" + isCleanState()); " isClean=" + isCleanState());
} }
// Remove any redo information if available removeRedoInfo();
while (undoPosition < undoHistory.size() - 1) { setLatestDescription();
undoHistory.removeLast();
undoDescription.removeLast();
}
// Set the latest description
undoDescription.set(undoPosition, nextDescription);
} }
fireUndoRedoChangeEvent(); fireUndoRedoChangeEvent();
} }
/**
* Sets the latest description
*/
private void setLatestDescription() {
undoDescription.set(undoPosition, nextDescription);
}
/**
* Removes any redo information if available
*/
private void removeRedoInfo() {
while (undoPosition < undoHistory.size() - 1) {
undoHistory.removeLast();
undoDescription.removeLast();
}
}
/** /**
@ -610,6 +774,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
undoRedoListeners.remove(listener); undoRedoListeners.remove(listener);
} }
private void fireUndoRedoChangeEvent() { private void fireUndoRedoChangeEvent() {
UndoRedoListener[] array = undoRedoListeners.toArray(new UndoRedoListener[0]); UndoRedoListener[] array = undoRedoListeners.toArray(new UndoRedoListener[0]);
for (UndoRedoListener l : array) { for (UndoRedoListener l : array) {

View File

@ -8,19 +8,38 @@ import java.io.InputStream;
import net.sf.openrocket.document.Attachment; import net.sf.openrocket.document.Attachment;
/**
*
* defines a file system attachment
* stores the attachment location
*/
public class FileSystemAttachment extends Attachment { public class FileSystemAttachment extends Attachment {
/** the file location*/
private final File location; private final File location;
/**
* main constructor,
*
* @param name name of attachment
* @param location File location of attachment
*/
public FileSystemAttachment(String name, File location) { public FileSystemAttachment(String name, File location) {
super(name); super(name);
this.location = location; this.location = location;
} }
/**
*
* @return the File object with the attachment location
*/
public File getLocation() { public File getLocation() {
return location; return location;
} }
/**
* {@inheritDoc}
* creates the stream based on the location passed while building
*/
@Override @Override
public InputStream getBytes() throws FileNotFoundException, IOException { public InputStream getBytes() throws FileNotFoundException, IOException {
return new FileInputStream(location); return new FileInputStream(location);

View File

@ -12,16 +12,24 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
//private static final Logger log = LoggerFactory.getLogger(AxialStage.class); //private static final Logger log = LoggerFactory.getLogger(AxialStage.class);
/** list of separations to be happening*/
protected FlightConfigurableParameterSet<StageSeparationConfiguration> separations; protected FlightConfigurableParameterSet<StageSeparationConfiguration> separations;
/** number of stages */
protected int stageNumber; protected int stageNumber;
/**
* default constructor, builds a rocket with zero stages
*/
public AxialStage(){ public AxialStage(){
this.separations = new FlightConfigurableParameterSet<StageSeparationConfiguration>( new StageSeparationConfiguration()); this.separations = new FlightConfigurableParameterSet<StageSeparationConfiguration>( new StageSeparationConfiguration());
this.relativePosition = Position.AFTER; this.relativePosition = Position.AFTER;
this.stageNumber = 0; this.stageNumber = 0;
} }
/**
* {@inheritDoc}
* AxialStage will always accept children
*/
@Override @Override
public boolean allowsChildren() { public boolean allowsChildren() {
return true; return true;
@ -33,6 +41,10 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
return trans.get("Stage.Stage"); return trans.get("Stage.Stage");
} }
/**
* gets the separation configuration of the rocket
* @return the separation configuration of the rocket
*/
public FlightConfigurableParameterSet<StageSeparationConfiguration> getSeparationConfigurations() { public FlightConfigurableParameterSet<StageSeparationConfiguration> getSeparationConfigurations() {
return separations; return separations;
} }
@ -42,7 +54,10 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
separations.reset(fcid); separations.reset(fcid);
} }
// not strictly accurate, but this should provide an acceptable estimate for total vehicle size /**
* {@inheritDoc}
* not strictly accurate, but this should provide an acceptable estimate for total vehicle size
*/
@Override @Override
public Collection<Coordinate> getComponentBounds() { public Collection<Coordinate> getComponentBounds() {
Collection<Coordinate> bounds = new ArrayList<Coordinate>(8); Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
@ -110,16 +125,28 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
return this.stageNumber; return this.stageNumber;
} }
/**
* {@inheritDoc}
* axialStage is always after
*/
@Override @Override
public boolean isAfter(){ public boolean isAfter(){
return true; return true;
} }
/**
* returns if the object is a launch stage
* @return if the object is a launch stage
*/
public boolean isLaunchStage(){ public boolean isLaunchStage(){
return ( this instanceof ParallelStage ) return ( this instanceof ParallelStage )
||( getRocket().getBottomCoreStage().equals(this)); ||( getRocket().getBottomCoreStage().equals(this));
} }
/**
* sets the stage number
* @param newStageNumber
*/
public void setStageNumber(final int newStageNumber) { public void setStageNumber(final int newStageNumber) {
this.stageNumber = newStageNumber; this.stageNumber = newStageNumber;
} }
@ -138,12 +165,21 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
return buf; return buf;
} }
/**
* method used for debugging separation
* @return a string that represents the debug message of separation
*/
public String toDebugSeparation() { public String toDebugSeparation() {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.append( this.separations.toDebug() ); buff.append( this.separations.toDebug() );
return buff.toString(); return buff.toString();
} }
/**
* gets the previous stage installed in the rockets
* returns null if this is the first stage
* @return the previous stage in the rocket
*/
public AxialStage getPreviousStage() { public AxialStage getPreviousStage() {
if( this instanceof ParallelStage ){ if( this instanceof ParallelStage ){
return (AxialStage) this.parent; return (AxialStage) this.parent;

View File

@ -2,7 +2,7 @@ package net.sf.openrocket.rocketcomponent;
import java.util.UUID; import java.util.UUID;
/* /**
* FlightConfigurationID is a very minimal wrapper class used to identify a given flight configuration for various components and options. * FlightConfigurationID is a very minimal wrapper class used to identify a given flight configuration for various components and options.
* It is intended to provide better visibility and traceability by more specific type safety -- this class replaces a * It is intended to provide better visibility and traceability by more specific type safety -- this class replaces a
* straight-up <code>String</code> Key in previous implementations. * straight-up <code>String</code> Key in previous implementations.
@ -19,10 +19,17 @@ public final class FlightConfigurationId implements Comparable<FlightConfigurati
public final static FlightConfigurationId ERROR_FCID = new FlightConfigurationId( FlightConfigurationId.ERROR_UUID); public final static FlightConfigurationId ERROR_FCID = new FlightConfigurationId( FlightConfigurationId.ERROR_UUID);
public final static FlightConfigurationId DEFAULT_VALUE_FCID = new FlightConfigurationId( FlightConfigurationId.DEFAULT_VALUE_UUID ); public final static FlightConfigurationId DEFAULT_VALUE_FCID = new FlightConfigurationId( FlightConfigurationId.DEFAULT_VALUE_UUID );
/**
* default constructor, builds with an unique random ID
*/
public FlightConfigurationId() { public FlightConfigurationId() {
this(UUID.randomUUID()); this(UUID.randomUUID());
} }
/**
* builds the id with the given String
* @param _str te string to be made into the id
*/
public FlightConfigurationId(final String _str) { public FlightConfigurationId(final String _str) {
UUID candidate; UUID candidate;
if(_str == null || "".equals(_str)){ if(_str == null || "".equals(_str)){
@ -37,6 +44,10 @@ public final class FlightConfigurationId implements Comparable<FlightConfigurati
this.key = candidate; this.key = candidate;
} }
/**
* builds he id with the given UUID object
* @param _val the UUID to be made into the id
*/
public FlightConfigurationId(final UUID _val) { public FlightConfigurationId(final UUID _val) {
if (null == _val){ if (null == _val){
this.key = FlightConfigurationId.ERROR_UUID; this.key = FlightConfigurationId.ERROR_UUID;
@ -45,6 +56,10 @@ public final class FlightConfigurationId implements Comparable<FlightConfigurati
} }
} }
/**
* {@inheritDoc}
* considers equals ids with the same key
*/
@Override @Override
public boolean equals(Object anObject) { public boolean equals(Object anObject) {
if (!(anObject instanceof FlightConfigurationId)) { if (!(anObject instanceof FlightConfigurationId)) {
@ -55,32 +70,65 @@ public final class FlightConfigurationId implements Comparable<FlightConfigurati
return this.key.equals(otherFCID.key); return this.key.equals(otherFCID.key);
} }
/**
*
* @return
*/
public String toShortKey(){ public String toShortKey(){
if( hasError() ){ if( hasError() )
return FlightConfigurationId.ERROR_KEY_NAME; return FlightConfigurationId.ERROR_KEY_NAME;
}else if( this.key == FlightConfigurationId.DEFAULT_VALUE_UUID){ if( isDefaultId())
return FlightConfigurationId.DEFAULT_VALUE_NAME; return FlightConfigurationId.DEFAULT_VALUE_NAME;
}else{ return this.key.toString().substring(0,8);
return this.key.toString().substring(0,8);
} }
//extracted this method because maybe, just maybe, this info could be used somewhere else
/**
* gets if the id is the default
* @return if the id is default
*/
private boolean isDefaultId() {
return this.key == FlightConfigurationId.DEFAULT_VALUE_UUID;
} }
/**
* returns the whole key in the id
* @return the full key of the id
*/
public String toFullKey(){ public String toFullKey(){
return this.key.toString(); return this.toString();
} }
/**
* {@inheritDoc}
* uses the key hash code
*/
@Override @Override
public int hashCode() { public int hashCode() {
return this.key.hashCode(); return this.key.hashCode();
} }
/**
* checks if the key is the ERROR_UUID flag
* @return if the id has error
*/
public boolean hasError(){ public boolean hasError(){
return (ERROR_UUID == this.key); return (ERROR_UUID == this.key);
} }
/**
* checks if the key from the id is valid
* @return if the id is valid or not
*/
public boolean isValid() { public boolean isValid() {
return !hasError(); return !hasError();
} }
/**
* {@inheritDoc}
* same as get full id
*/
@Override @Override
public String toString() { public String toString() {
return this.key.toString(); return this.key.toString();
@ -91,6 +139,10 @@ public final class FlightConfigurationId implements Comparable<FlightConfigurati
return this.key.compareTo( other.key); return this.key.compareTo( other.key);
} }
/**
* used for debuggin, gets the short key
* @return the short key version of the id
*/
public String toDebug(){ public String toDebug(){
return this.toShortKey(); return this.toShortKey();
} }

View File

@ -30,7 +30,11 @@ import net.sf.openrocket.util.SafetyMutex;
import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.util.StateChangeListener;
import net.sf.openrocket.util.UniqueID; import net.sf.openrocket.util.UniqueID;
/**
* Master class that defines components of rockets
* almost all hardware from the rocket extends from this abstract class
*
*/
public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent> { public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent> {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(RocketComponent.class); private static final Logger log = LoggerFactory.getLogger(RocketComponent.class);
@ -41,7 +45,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
//private static final Translator trans = Application.getTranslator(); //private static final Translator trans = Application.getTranslator();
/* /**
* Text is suitable to the form * Text is suitable to the form
* Position relative to: <title> * Position relative to: <title>
*/ */
@ -351,6 +355,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
} }
/**
* appends the debug string of the component into the passed builder
* @param sb String builder to be appended
*/
private void toDebugString(StringBuilder sb) { private void toDebugString(StringBuilder sb) {
sb.append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this)); sb.append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this));
sb.append("[\"").append(this.getName()).append('"'); sb.append("[\"").append(this.getName()).append('"');
@ -958,6 +966,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
return result; return result;
} }
/**
* returns the axial offset of the component
* @return
*/
public double getAxialOffset() { public double getAxialOffset() {
mutex.verify(); mutex.verify();
return this.asPositionValue(this.relativePosition); return this.asPositionValue(this.relativePosition);
@ -2081,6 +2093,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
} }
/// debug functions
public String toDebugName(){ public String toDebugName(){
return this.getName()+"<"+this.getClass().getSimpleName()+">("+this.getID().substring(0,8)+")"; return this.getName()+"<"+this.getClass().getSimpleName()+">("+this.getID().substring(0,8)+")";
} }