diff --git a/core/src/net/sf/openrocket/appearance/Appearance.java b/core/src/net/sf/openrocket/appearance/Appearance.java index 6d02c0b22..c52273f8d 100644 --- a/core/src/net/sf/openrocket/appearance/Appearance.java +++ b/core/src/net/sf/openrocket/appearance/Appearance.java @@ -36,9 +36,7 @@ public class Appearance { * @param shine shine of the appearance, will be clamped between 0 and 1 */ public Appearance(final Color paint, final double shine) { - this.paint = paint; - this.shine = MathUtil.clamp(shine, 0, 1); - this.texture = null; + this(paint,shine,null); } /** diff --git a/core/src/net/sf/openrocket/appearance/Decal.java b/core/src/net/sf/openrocket/appearance/Decal.java index 5e5461188..412cdcd3d 100644 --- a/core/src/net/sf/openrocket/appearance/Decal.java +++ b/core/src/net/sf/openrocket/appearance/Decal.java @@ -4,12 +4,17 @@ import net.sf.openrocket.startup.Application; 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 */ public class Decal { + /** + * enum to flag what happens on edge in a decal + * + * + */ public static enum EdgeMode { REPEAT("TextureWrap.Repeat"), MIRROR("TextureWrap.Mirror"), CLAMP("TextureWrap.Clamp"), STICKER("TextureWrap.Sticker"); private final String transName; @@ -29,6 +34,16 @@ public class Decal { private final DecalImage image; 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, final DecalImage image, final EdgeMode mode) { this.offset = offset; @@ -39,26 +54,56 @@ public class Decal { this.mode = mode; } + /** + * returns the offset, in coordinates object format + * + * @return offset coordinates of the decal + */ public Coordinate getOffset() { return offset; } + /** + * return the center, in coordinates object format + * + * @return The center coordinates of the decal + */ public Coordinate getCenter() { return center; } + /** + * return the scaling of the decal, in coordinate format + * + * @return the scale coordinates of the decal + */ public Coordinate getScale() { return scale; } + /** + * returns the rotation of the decal, in radians + * + * @return the rotation of the decal, in radians + */ public double getRotation() { return rotation; } + /** + * return the edge behaviour of the decal + * + * @return the edge behaviour of the decal + */ public EdgeMode getEdgeMode() { return mode; } + /** + * returns the image of the decal itself + * + * @return the image of the decal itself + */ public DecalImage getImage() { return image; } diff --git a/core/src/net/sf/openrocket/appearance/DecalImage.java b/core/src/net/sf/openrocket/appearance/DecalImage.java index 544eda8b3..77a74010f 100644 --- a/core/src/net/sf/openrocket/appearance/DecalImage.java +++ b/core/src/net/sf/openrocket/appearance/DecalImage.java @@ -7,13 +7,37 @@ import java.io.InputStream; import net.sf.openrocket.util.ChangeSource; +/** + * Interface to handle image files for declas + * + */ public interface DecalImage extends ChangeSource, Comparable { + /** + * returns the name of the file path of the image + * @return name of file path + */ 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; + /** + * exports an image into the File + * @param file The File handler object + * @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); } diff --git a/core/src/net/sf/openrocket/appearance/defaults/DefaultAppearance.java b/core/src/net/sf/openrocket/appearance/defaults/DefaultAppearance.java index 38806be53..bd57cc94a 100644 --- a/core/src/net/sf/openrocket/appearance/defaults/DefaultAppearance.java +++ b/core/src/net/sf/openrocket/appearance/defaults/DefaultAppearance.java @@ -23,8 +23,22 @@ import net.sf.openrocket.rocketcomponent.TubeFinSet; import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Coordinate; +/** + * + * Class defining the default images of the application + * + */ 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) { return new Appearance( new Color(1, 1, 1), @@ -37,6 +51,14 @@ public class DefaultAppearance { 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) { return new Appearance( base, @@ -69,6 +91,12 @@ public class DefaultAppearance { private static HashMap plastics = new HashMap(); + /** + * 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) { if (!plastics.containsKey(c)) { plastics.put(c, new Appearance(c, .3)); @@ -76,6 +104,12 @@ public class DefaultAppearance { 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) { if (c instanceof BodyTube) return ESTES_BT; @@ -100,6 +134,12 @@ public class DefaultAppearance { 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) { if (m instanceof ThrustCurveMotor) { ThrustCurveMotor tcm = (ThrustCurveMotor) m; diff --git a/core/src/net/sf/openrocket/appearance/defaults/ResourceDecalImage.java b/core/src/net/sf/openrocket/appearance/defaults/ResourceDecalImage.java index 630cf3639..6c70470ce 100644 --- a/core/src/net/sf/openrocket/appearance/defaults/ResourceDecalImage.java +++ b/core/src/net/sf/openrocket/appearance/defaults/ResourceDecalImage.java @@ -8,11 +8,20 @@ import java.io.InputStream; import net.sf.openrocket.appearance.DecalImage; import net.sf.openrocket.util.StateChangeListener; - +/** + * + * Default implementation class of DecalImage + * + */ public class ResourceDecalImage implements DecalImage { + /** File path to the image*/ final String resource; + /** + * main constructor, stores the file path given + * @param resource + */ public ResourceDecalImage(final String resource) { this.resource = resource; } diff --git a/core/src/net/sf/openrocket/communication/UpdateInfo.java b/core/src/net/sf/openrocket/communication/UpdateInfo.java index 78458b812..f4d296e75 100644 --- a/core/src/net/sf/openrocket/communication/UpdateInfo.java +++ b/core/src/net/sf/openrocket/communication/UpdateInfo.java @@ -6,18 +6,30 @@ import net.sf.openrocket.util.ArrayList; import net.sf.openrocket.util.BuildProperties; import net.sf.openrocket.util.ComparablePair; + /** + * + * class that stores the update information of the application + * + */ public class UpdateInfo { private final String latestVersion; private final ArrayList> updates; - + /** + * loads the default information + */ public UpdateInfo() { this.latestVersion = BuildProperties.getVersion(); this.updates = new ArrayList>(); } + /** + * 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> updates) { this.latestVersion = version; this.updates = new ArrayList>(updates); diff --git a/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java b/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java index 16d51456b..654ac0a93 100644 --- a/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java +++ b/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java @@ -41,10 +41,11 @@ public class UpdateInfoRetriever { * Check whether the update info fetching is still in progress. * * @return true if the communication is still in progress. + * @throws IllegalStateException if {@link #startFetchUpdateInfo()} has not been called */ public boolean isRunning() { if (fetcher == null) { - throw new IllegalStateException("startFetchUpdateInfo() has not been called"); + throw new IllegalStateException("startFetchUpdateInfo() has not been called"); } return fetcher.isAlive(); } @@ -81,43 +82,76 @@ public class UpdateInfoRetriever { */ /* package-private */ static UpdateInfo parseUpdateInput(Reader r) throws IOException { - BufferedReader reader; - if (r instanceof BufferedReader) { - reader = (BufferedReader) r; - } else { - reader = new BufferedReader(r); - } - - + BufferedReader reader = convertToBufferedReader(r); String version = null; + ArrayList> updates = new ArrayList>(); String str = reader.readLine(); 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(); - } 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(value, desc)); - } + } else if (isUpdateToken(str)) { + ComparablePair update = parseUpdateToken(str); + if(update != null) + updates.add(update); } - // Ignore anything else str = reader.readLine(); } - if (version != null) { - return new UpdateInfo(version, updates); - } else { + if (version == 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 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(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. * @@ -137,11 +171,193 @@ public class UpdateInfoRetriever { } } - + /** + * Establishes a connection with data of previous updates + * @throws IOException + */ private void doConnection() throws IOException { - String url = Communicator.UPDATE_INFO_URL + "?" + Communicator.VERSION_PARAM + "=" - + Communicator.encode(BuildProperties.getVersion()); + HttpURLConnection connection = getConnection(getUrl()); + 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> updates = + new ArrayList>(); + + 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 parseUpdateInfo(String line){ + String[] split = line.split(":", 2); + int n = Integer.parseInt(split[0]); + return new ComparablePair(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); connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT); @@ -165,80 +381,16 @@ public class UpdateInfoRetriever { connection.setRequestProperty("X-OpenRocket-Locale", Communicator.encode(Locale.getDefault().toString())); connection.setRequestProperty("X-OpenRocket-CPUs", "" + Runtime.getRuntime().availableProcessors()); - - InputStream is = null; - try { - connection.connect(); - - log.debug("Update response code: " + connection.getResponseCode()); - - if (connection.getResponseCode() == Communicator.UPDATE_INFO_NO_UPDATE_CODE) { - // No updates are available - log.info("No updates available"); - 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> updates = - new ArrayList>(); - - 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(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(); - } - } + return connection; + } + + /** + * builds the default url for fetching updates + * @return the string with an url for fethcing updates + */ + private String getUrl() { + return Communicator.UPDATE_INFO_URL + "?" + Communicator.VERSION_PARAM + "=" + + Communicator.encode(BuildProperties.getVersion()); } } } diff --git a/core/src/net/sf/openrocket/database/AsynchronousDatabaseLoader.java b/core/src/net/sf/openrocket/database/AsynchronousDatabaseLoader.java index 71bda4757..a8aed6f5c 100644 --- a/core/src/net/sf/openrocket/database/AsynchronousDatabaseLoader.java +++ b/core/src/net/sf/openrocket/database/AsynchronousDatabaseLoader.java @@ -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() { return endedLoading; @@ -86,10 +86,27 @@ public abstract class AsynchronousDatabaseLoader { } } - + /** + * + */ private void doLoad() { // 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; while (!inUse && System.currentTimeMillis() < startLoading) { 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(); diff --git a/core/src/net/sf/openrocket/database/ComponentPresetDao.java b/core/src/net/sf/openrocket/database/ComponentPresetDao.java index a27a7f5d4..aaa66bf1d 100644 --- a/core/src/net/sf/openrocket/database/ComponentPresetDao.java +++ b/core/src/net/sf/openrocket/database/ComponentPresetDao.java @@ -6,10 +6,23 @@ import net.sf.openrocket.preset.ComponentPreset; public interface ComponentPresetDao { + /** + * return a list all components + * @return list of all components + */ public List listAll(); + /** + * insert a component preset into a database + * @param preset the component to be inserted into the database + */ 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 listForType( ComponentPreset.Type type ); /** @@ -21,13 +34,35 @@ public interface ComponentPresetDao { * @return */ public List 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 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 listForTypes( List 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 ); + /** + * 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 find( String manufacturer, String partNo ); } \ No newline at end of file diff --git a/core/src/net/sf/openrocket/database/Database.java b/core/src/net/sf/openrocket/database/Database.java index 3f16e818a..df513ea21 100644 --- a/core/src/net/sf/openrocket/database/Database.java +++ b/core/src/net/sf/openrocket/database/Database.java @@ -19,6 +19,7 @@ import net.sf.openrocket.database.DatabaseListener; */ public class Database> extends AbstractSet { + /** the list that contains the data from the database itself*/ protected final List list = new ArrayList(); private final ArrayList> listeners = new ArrayList>(); @@ -33,6 +34,10 @@ public class Database> extends AbstractSet { return list.size(); } + /** + * {@inheritDoc} + * fires add event + */ @Override public boolean add(T element) { int index; @@ -71,17 +76,27 @@ public class Database> extends AbstractSet { return list.indexOf(m); } - + /** + * adds a listener for database changes + * @param listener the listener + */ public void addDatabaseListener(DatabaseListener listener) { listeners.add(listener); } + /** + * removes a listener from the list os listeners + * @param listener + */ public void removeChangeListener(DatabaseListener listener) { listeners.remove(listener); } - + /** + * wake up call for database listeners for when elements are added + * @param element the element added + */ @SuppressWarnings("unchecked") protected void fireAddEvent(T element) { Object[] array = listeners.toArray(); @@ -90,6 +105,10 @@ public class Database> extends AbstractSet { } } + /** + * wake up call for database listeners when elements are removed + * @param element the removed element + */ @SuppressWarnings("unchecked") protected void fireRemoveEvent(T element) { Object[] array = listeners.toArray(); @@ -98,10 +117,6 @@ public class Database> extends AbstractSet { } } - - - - /** * Iterator class implementation that fires changes if remove() is called. */ @@ -120,6 +135,10 @@ public class Database> extends AbstractSet { return current; } + /** + * {@inheritDoc} + * fires remove event + */ @Override public void remove() { iterator.remove(); diff --git a/core/src/net/sf/openrocket/database/DatabaseListener.java b/core/src/net/sf/openrocket/database/DatabaseListener.java index a24b2ddeb..54438f163 100644 --- a/core/src/net/sf/openrocket/database/DatabaseListener.java +++ b/core/src/net/sf/openrocket/database/DatabaseListener.java @@ -1,9 +1,24 @@ package net.sf.openrocket.database; +/** + * interface defining listeners for database + * + * @param type stored in the database + */ public interface DatabaseListener> { + /** + * 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 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 source); } diff --git a/core/src/net/sf/openrocket/database/Databases.java b/core/src/net/sf/openrocket/database/Databases.java index ffe3628eb..d28e92ac0 100644 --- a/core/src/net/sf/openrocket/database/Databases.java +++ b/core/src/net/sf/openrocket/database/Databases.java @@ -117,7 +117,13 @@ public class Databases { 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) { String name = trans.get("material", baseName); return Material.newMaterial(type, name, density, false); @@ -145,21 +151,7 @@ public class Databases { * @return the material, or null if not found. */ public static Material findMaterial(Material.Type type, String baseName) { - Database db; - 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); - } - + Database db = getDatabase(type); String name = trans.get("material", baseName); for (Material m : db) { @@ -170,6 +162,24 @@ public class Databases { 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 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 @@ -184,21 +194,7 @@ public class Databases { * @return the material object from the database or a new material. */ public static Material findMaterial(Material.Type type, String baseName, double density) { - Database db; - 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); - } - + Database db = getDatabase(type); String name = trans.get("material", baseName); for (Material m : db) { diff --git a/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java b/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java index fc9a0e24c..9abed3a19 100644 --- a/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java +++ b/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java @@ -47,32 +47,126 @@ public class ThrustCurveMotorSet implements Comparable { private String caseInfo = null; private boolean available = true; - + /** + * adds a motor into the set, + * uses digest and designation to determinate if a motor is present or not + * @param motor the motor to be added + */ public void addMotor(ThrustCurveMotor motor) { - // Check for first insertion - if (motors.isEmpty()) { - manufacturer = motor.getManufacturer(); - designation = motor.getDesignation(); - simplifiedDesignation = simplifyDesignation(designation); - diameter = motor.getDiameter(); - length = motor.getLength(); - totalImpulse = Math.round((motor.getTotalImpulseEstimate())); + checkFirstInsertion(motor); + verifyMotor(motor); + updateType(motor); + checkChangeSimplifiedDesignation(motor); + addStandardDelays(motor); + if(!checkMotorOverwrite(motor)){ + motors.add(motor); + digestMap.put(motor, motor.getDigest()); + Collections.sort(motors, comparator); + } + + } + + /** + * checks whether a motor is present, overwriting it if + * @param motor the motor to be checked + * @return if there was an overwrite or not, returns true if all is equals + */ + private boolean checkMotorOverwrite(ThrustCurveMotor motor) { + final String digest = motor.getDigest(); + for (int index = 0; index < motors.size(); index++) { + Motor m = motors.get(index); + + if (isMotorPresent(motor, digest, m)) { + + // Match found, check which one to keep (or both) based on comment + String newCmt = getFormattedDescription(motor); + String oldCmt = getFormattedDescription(m); + if (isNewDescriptionIrrelevant(newCmt, oldCmt)) { + return true; + } else if (oldCmt.length() == 0) { + motors.set(index, motor); + digestMap.put(motor, digest); + return true; + } + // else continue search and add both + + } + } + return false; + } + + /** + * checks if a motor is in the maps + * @param motor the motor to be checked + * @param digest the digest of the motor + * @param m the current motor being checked with + * @return wheter the motor is or no + */ + private boolean isMotorPresent(ThrustCurveMotor motor, final String digest, Motor m) { + return digest.equals(digestMap.get(m)) && + motor.getDesignation().equals(m.getDesignation()); + } + + /** + * get a description from the motor + * @param motor the motor + * @return the description of the motor + */ + private String getFormattedDescription(Motor motor) { + return motor.getDescription().replaceAll("\\s+", " ").trim(); + } + + + /** + * checks if the new commit message is empty or equals to the old commit + * @param newCmt the new commit message + * @param oldCmt the old commit message + * @return whether the new commit is empty or equals to the old commit + */ + private boolean isNewDescriptionIrrelevant(String newCmt, String oldCmt) { + return newCmt.length() == 0 || newCmt.equals(oldCmt); + } + + + /** + * adds the standard delay if aplicable + * @param motor the motor to be considered + */ + private void addStandardDelays(ThrustCurveMotor motor) { + // Add the standard delays + for (double d : motor.getStandardDelays()) { + d = Math.rint(d); + if (!delays.contains(d)) { + delays.add(d); + } + } + Collections.sort(delays); + } + + + /** + * checks if simplified designation should be changed with the given motor + * @param motor the motor to be checked with + */ + private void checkChangeSimplifiedDesignation(ThrustCurveMotor motor) { + // Change the simplified designation if necessary + if (!designation.equalsIgnoreCase(motor.getDesignation().trim())) { + designation = simplifiedDesignation; + } + + if (caseInfo == null) { caseInfo = motor.getCaseInfo(); - available = motor.isAvailable(); } - - // Verify that the motor can be added - if (!matches(motor)) { - throw new IllegalArgumentException("Motor does not match the set:" + - " manufacturer=" + manufacturer + - " designation=" + designation + - " diameter=" + diameter + - " length=" + length + - " set_size=" + motors.size() + - " motor=" + motor); - } - + } + + + /** + * checks if the cached type should be changed with the given motor + * if it's hybrid, delays will be added + * @param motor the motor to be checked with + */ + private void updateType(ThrustCurveMotor motor) { // Update the type if now known if (type == Motor.Type.UNKNOWN) { type = motor.getMotorType(); @@ -83,60 +177,50 @@ public class ThrustCurveMotorSet implements Comparable { } } } - - // Change the simplified designation if necessary - if (!designation.equalsIgnoreCase(motor.getDesignation().trim())) { - designation = simplifiedDesignation; + } + + + /** + * verifies if a motor is valid for this set + * @param motor the motor to be checked + */ + private void verifyMotor(ThrustCurveMotor motor) { + if (!matches(motor)) { + throw new IllegalArgumentException("Motor does not match the set:" + + " manufacturer=" + manufacturer + + " designation=" + designation + + " diameter=" + diameter + + " length=" + length + + " set_size=" + motors.size() + + " motor=" + motor); } - - if (caseInfo == null) { + } + + + /** + * checks if the given motor is the first one + * the ifrst motor inserted is what will difine the rest of the motors in the set + * @param motor the motor to be checked + */ + private void checkFirstInsertion(ThrustCurveMotor motor) { + if (motors.isEmpty()) { + manufacturer = motor.getManufacturer(); + designation = motor.getDesignation(); + simplifiedDesignation = simplifyDesignation(designation); + diameter = motor.getDiameter(); + length = motor.getLength(); + totalImpulse = Math.round((motor.getTotalImpulseEstimate())); caseInfo = motor.getCaseInfo(); + available = motor.isAvailable(); } - - // Add the standard delays - for (double d : motor.getStandardDelays()) { - d = Math.rint(d); - if (!delays.contains(d)) { - delays.add(d); - } - } - Collections.sort(delays); - - - // Check whether to add as new motor or overwrite existing - final String digest = motor.getDigest(); - for (int index = 0; index < motors.size(); index++) { - Motor m = motors.get(index); - - if (digest.equals(digestMap.get(m)) && - motor.getDesignation().equals(m.getDesignation())) { - - // Match found, check which one to keep (or both) based on comment - String newCmt = motor.getDescription().replaceAll("\\s+", " ").trim(); - String oldCmt = m.getDescription().replaceAll("\\s+", " ").trim(); - - if (newCmt.length() == 0 || newCmt.equals(oldCmt)) { - // Do not replace and do not add - return; - } else if (oldCmt.length() == 0) { - // Replace existing motor - motors.set(index, motor); - digestMap.put(motor, digest); - return; - } - // else continue search and add both - - } - } - - // Motor not present, add it - motors.add(motor); - digestMap.put(motor, digest); - Collections.sort(motors, comparator); - } - + /** + * Checks if a motor can be added with the set + * A set contains motors of same manufacturer, diameter, length and type + * @param m the motor to be checked with + * @return if the motor passed the test or not + */ public boolean matches(ThrustCurveMotor m) { if (motors.isEmpty()) return true; @@ -164,12 +248,19 @@ public class ThrustCurveMotorSet implements Comparable { return true; } - + /** + * returns a new list with the stored motors + * @return list + */ public List getMotors() { return motors.clone(); } + /** + * + * @return number of motor in the set + */ public int getMotorCount() { return motors.size(); } @@ -239,12 +330,18 @@ public class ThrustCurveMotorSet implements Comparable { return totalImpulse; } - + /** + * returns the case info of the motor + * @return the motor's case information + */ public String getCaseInfo() { return caseInfo; } - + /** + * checks if the motor is available for other calculations + * @return if the motor is available + */ public boolean isAvailable() { return available; } @@ -326,4 +423,4 @@ public class ThrustCurveMotorSet implements Comparable { } -} +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/document/Attachment.java b/core/src/net/sf/openrocket/document/Attachment.java index 6ac927495..a46d6300f 100644 --- a/core/src/net/sf/openrocket/document/Attachment.java +++ b/core/src/net/sf/openrocket/document/Attachment.java @@ -7,21 +7,44 @@ import java.io.InputStream; import net.sf.openrocket.util.AbstractChangeSource; import net.sf.openrocket.util.ChangeSource; +/** + * + * Class handler of documents attachments + * + */ public abstract class Attachment extends AbstractChangeSource implements Comparable, ChangeSource { private final String name; + /** + * default constructor + * @param name the attachment name + */ public Attachment(String name) { super(); this.name = name; } + /** + * returns the name of attachment + * @return name of attachment + */ public String getName() { 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; + /** + * {@inheritDoc} + * considers only the name to equals + */ @Override public int compareTo(Attachment o) { return this.name.compareTo(o.name); diff --git a/core/src/net/sf/openrocket/document/DecalRegistry.java b/core/src/net/sf/openrocket/document/DecalRegistry.java index b96cafba9..591676cdf 100644 --- a/core/src/net/sf/openrocket/document/DecalRegistry.java +++ b/core/src/net/sf/openrocket/document/DecalRegistry.java @@ -25,13 +25,28 @@ import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.FileUtils; import net.sf.openrocket.util.StateChangeListener; +/** + * + * Class that handles decal usage registration + * + */ public class DecalRegistry { + /** + * default constructor, does nothing + */ DecalRegistry() { } + /** the decal usage map*/ private Map registeredDecals = new HashMap(); + /** + * 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) { 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) { String decalName = attachment.getName(); DecalImageImpl d; @@ -166,6 +186,10 @@ public class DecalRegistry { } } + /** + * + * @return + */ File getFileSystemLocation() { return fileSystemLocation; } @@ -205,6 +229,11 @@ public class DecalRegistry { } + /** + * sercha + * @param file + * @return + */ private DecalImageImpl findDecalForFile(File file) { for (DecalImageImpl d : registeredDecals.values()) { @@ -240,28 +269,25 @@ public class DecalRegistry { private static final int NUMBER_INDEX = 3; 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) { - String newName = name; - if (!newName.startsWith("decals/")) { - newName = "decals/" + name; - } - String basename = ""; - String extension = ""; - Matcher nameMatcher = fileNamePattern.matcher(newName); - if (nameMatcher.matches()) { - basename = nameMatcher.group(BASE_NAME_INDEX); - extension = nameMatcher.group(EXTENSION_INDEX); - } + String newName = checkPathConsistency(name); + String basename = getGroup(BASE_NAME_INDEX,fileNamePattern.matcher(newName)); + String extension = getGroup(EXTENSION_INDEX,fileNamePattern.matcher(newName)); Set counts = new TreeSet(); boolean needsRewrite = false; - + for (DecalImageImpl d : registeredDecals.values()) { Matcher m = fileNamePattern.matcher(d.getName()); 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); if (intString != null) { Integer i = Integer.parseInt(intString); @@ -278,13 +304,54 @@ public class DecalRegistry { 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 counts) { Integer newIndex = 1; while (counts.contains(newIndex)) { newIndex++; } - - return MessageFormat.format("{0} ({1}).{2}", basename, newIndex, extension); + return newIndex; + } + + /** + * 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; } } diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocument.java b/core/src/net/sf/openrocket/document/OpenRocketDocument.java index 4feb45cac..8efe1460e 100644 --- a/core/src/net/sf/openrocket/document/OpenRocketDocument.java +++ b/core/src/net/sf/openrocket/document/OpenRocketDocument.java @@ -102,36 +102,56 @@ public class OpenRocketDocument implements ComponentChangeListener { private final List listeners = new ArrayList(); + /** + * main constructor, enable events in the rocket + * and initializes the document + * @param rocket the rocket to be used in the document + */ OpenRocketDocument(Rocket rocket) { this.rocket = rocket; rocket.enableEvents(); init(); } + /** + * initializes the document, clearing the undo cache and + * setting itself as a listener for changes in the rocket + */ private void init() { clearUndo(); - rocket.addComponentChangeListener(this); } + /** + * adds a customExpression into the list + * @param expression the expression to be added + */ public void addCustomExpression(CustomExpression 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."); - } else { - customExpressions.add(expression); - } + } + customExpressions.add(expression); } + /** + * remves + * @param expression + */ public void removeCustomExpression(CustomExpression expression) { customExpressions.remove(expression); } + //TODO:LOW:this leaves the object custom expression exposed, is it supposed to be like that? + /** + * + * @return + */ public List getCustomExpressions() { 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 getFlightDataTypes() { Set allTypes = new LinkedHashSet(); @@ -158,28 +178,50 @@ public class OpenRocketDocument implements ComponentChangeListener { return allTypes; } - + /** + * gets the rocket in the document + * @return the rocket in the document + */ public Rocket getRocket() { return rocket; } - + /** + * returns the selected configuration from the rocket + * @return selected configuration from the rocket + */ public FlightConfiguration getSelectedConfiguration() { return rocket.getSelectedConfiguration(); } + /** + * returns the File handler object for the document + * @return the File handler object for the document + */ public File getFile() { return file; } + /** + * set the file handler object for the document + * @param file the new file handler object + */ public void setFile(File file) { this.file = file; } + /** + * returns if the current rocket is saved + * @return if the current rocket is saved + */ public boolean isSaved() { 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) { if (saved == false) 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 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) { int count = 0; Iterator it = rocket.iterator(); while (it.hasNext()) { - RocketComponent comp = it.next(); - Appearance a = comp.getAppearance(); - if (a == null) { - continue; - } - Decal d = a.getTexture(); - if (d == null) { - continue; - } - if (img.equals(d.getImage())) { + if(hasDecal(it.next(),img)) 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) { if (countDecalUsage(img) <= 1) { return img; @@ -231,26 +297,54 @@ public class OpenRocketDocument implements ComponentChangeListener { 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) { return decalRegistry.getDecalImage(a); } + /** + * gets a list of simulations in the document + * @return the simulations in the document + */ public List getSimulations() { return simulations.clone(); } + /** + * gets the number of simulations in the document + * @return the number of simulations in the document + */ public int getSimulationCount() { 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) { 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) { 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) { simulations.add(simulation); FlightConfigurationId simId = simulation.getId(); @@ -260,33 +354,61 @@ public class OpenRocketDocument implements ComponentChangeListener { 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) { simulations.add(n, simulation); fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); } + /** + * removes the specific simulation from the list + * @param simulation the simulation to be removed + */ public void removeSimulation(Simulation simulation) { simulations.remove(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) { Simulation simulation = simulations.remove(n); fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); return simulation; } + /** + * removes the flight configuration and simulation with the specific id + * @param configId + */ public void removeFlightConfigurationAndSimulations(FlightConfigurationId configId) { if (configId == null) { 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()) { // Assumes modifiable collection - which it is if (configId.equals(s.getId())) { removeSimulation(s); } } - rocket.removeFlightConfiguration(configId); } @@ -331,42 +453,22 @@ public class OpenRocketDocument implements ComponentChangeListener { */ public void addUndoPosition(String description) { - if (storedDescription != null) { - logUndoError("addUndoPosition called while storedDescription=" + storedDescription + - " description=" + description); - } + checkDescription(description); // Check whether modifications have been done since last call - if (isCleanState()) { - // No modifications - log.info("Adding undo position '" + description + "' to " + this + ", document was in clean state"); - nextDescription = description; + if(isCheckNoModification(description)) return; - } - log.info("Adding undo position '" + description + "' to " + this + ", document is in unclean state"); + checkUndoPositionConsistency(); + addStateToUndoHistory(description); - /* - * 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"); - } - 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 + maintainMaximumUndoSize(); + } + + /** + * + */ + private void maintainMaximumUndoSize() { if (undoHistory.size() > UNDO_LEVELS + UNDO_MARGIN && undoPosition > UNDO_MARGIN) { for (int i = 0; i < UNDO_MARGIN; i++) { 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() + " isClean=" + isCleanState()); } - // Remove any redo information if available - while (undoPosition < undoHistory.size() - 1) { - undoHistory.removeLast(); - undoDescription.removeLast(); - } - - // Set the latest description - undoDescription.set(undoPosition, nextDescription); + removeRedoInfo(); + setLatestDescription(); } 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); } + private void fireUndoRedoChangeEvent() { UndoRedoListener[] array = undoRedoListeners.toArray(new UndoRedoListener[0]); for (UndoRedoListener l : array) { diff --git a/core/src/net/sf/openrocket/document/attachments/FileSystemAttachment.java b/core/src/net/sf/openrocket/document/attachments/FileSystemAttachment.java index 28d737e44..763aa92e2 100644 --- a/core/src/net/sf/openrocket/document/attachments/FileSystemAttachment.java +++ b/core/src/net/sf/openrocket/document/attachments/FileSystemAttachment.java @@ -8,19 +8,38 @@ import java.io.InputStream; import net.sf.openrocket.document.Attachment; +/** + * + * defines a file system attachment + * stores the attachment location + */ public class FileSystemAttachment extends Attachment { - + /** the 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) { super(name); this.location = location; } + /** + * + * @return the File object with the attachment location + */ public File getLocation() { return location; } + /** + * {@inheritDoc} + * creates the stream based on the location passed while building + */ @Override public InputStream getBytes() throws FileNotFoundException, IOException { return new FileInputStream(location); diff --git a/core/src/net/sf/openrocket/file/iterator/DirectoryIterator.java b/core/src/net/sf/openrocket/file/iterator/DirectoryIterator.java index f1a28a866..e80a1e2fd 100644 --- a/core/src/net/sf/openrocket/file/iterator/DirectoryIterator.java +++ b/core/src/net/sf/openrocket/file/iterator/DirectoryIterator.java @@ -35,6 +35,7 @@ public class DirectoryIterator extends FileIterator { * * @param directory the directory to read. * @param filter the filter for selecting files. + * @param recursive true for recursive search * @throws IOException if the directory cannot be read. */ public DirectoryIterator(File directory, FileFilter filter, boolean recursive) diff --git a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java index d27c07da1..4d481a676 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java @@ -12,16 +12,24 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC private static final Translator trans = Application.getTranslator(); //private static final Logger log = LoggerFactory.getLogger(AxialStage.class); + /** list of separations to be happening*/ protected FlightConfigurableParameterSet separations; - + /** number of stages */ protected int stageNumber; + /** + * default constructor, builds a rocket with zero stages + */ public AxialStage(){ this.separations = new FlightConfigurableParameterSet( new StageSeparationConfiguration()); this.relativePosition = Position.AFTER; this.stageNumber = 0; } + /** + * {@inheritDoc} + * AxialStage will always accept children + */ @Override public boolean allowsChildren() { return true; @@ -33,6 +41,10 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC return trans.get("Stage.Stage"); } + /** + * gets the separation configuration of the rocket + * @return the separation configuration of the rocket + */ public FlightConfigurableParameterSet getSeparationConfigurations() { return separations; } @@ -42,7 +54,10 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC 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 public Collection getComponentBounds() { Collection bounds = new ArrayList(8); @@ -110,16 +125,28 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC return this.stageNumber; } + /** + * {@inheritDoc} + * axialStage is always after + */ @Override public boolean isAfter(){ return true; } + /** + * returns if the object is a launch stage + * @return if the object is a launch stage + */ public boolean isLaunchStage(){ return ( this instanceof ParallelStage ) ||( getRocket().getBottomCoreStage().equals(this)); } + /** + * sets the stage number + * @param newStageNumber + */ public void setStageNumber(final int newStageNumber) { this.stageNumber = newStageNumber; } @@ -138,12 +165,21 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC return buf; } + /** + * method used for debugging separation + * @return a string that represents the debug message of separation + */ public String toDebugSeparation() { StringBuilder buff = new StringBuilder(); buff.append( this.separations.toDebug() ); 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() { if( this instanceof ParallelStage ){ return (AxialStage) this.parent; diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurationId.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurationId.java index 1e6ab1938..258fd7fe5 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurationId.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurationId.java @@ -2,7 +2,7 @@ package net.sf.openrocket.rocketcomponent; import java.util.UUID; -/* +/** * 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 * straight-up String Key in previous implementations. @@ -19,10 +19,17 @@ public final class FlightConfigurationId implements Comparable { @SuppressWarnings("unused") 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(); - /* + /** * Text is suitable to the form * Position relative to: */ @@ -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) { sb.append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this)); sb.append("[\"").append(this.getName()).append('"'); @@ -958,6 +966,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return result; } + /** + * returns the axial offset of the component + * @return + */ public double getAxialOffset() { mutex.verify(); return this.asPositionValue(this.relativePosition); @@ -2081,6 +2093,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } } + /// debug functions public String toDebugName(){ return this.getName()+"<"+this.getClass().getSimpleName()+">("+this.getID().substring(0,8)+")"; } diff --git a/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java b/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java index 2d1deb444..4ab2f4b15 100644 --- a/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java +++ b/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java @@ -19,6 +19,11 @@ import net.sf.openrocket.util.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * + * Loader that gets all component preset from the database in directory datafiles/preset + * + */ public class ComponentPresetDatabaseLoader extends AsynchronousDatabaseLoader { private final static Logger log = LoggerFactory.getLogger(ComponentPresetDatabaseLoader.class); @@ -27,6 +32,7 @@ public class ComponentPresetDatabaseLoader extends AsynchronousDatabaseLoader { private int fileCount = 0; private int presetCount = 0; + /** the database is immutable*/ private final ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(); public ComponentPresetDatabaseLoader() { @@ -47,51 +53,72 @@ public class ComponentPresetDatabaseLoader extends AsynchronousDatabaseLoader { @Override protected void loadDatabase() { long startTime = System.currentTimeMillis(); + loadPresetComponents(); + loadUserComponents(); + long end = System.currentTimeMillis(); + log.debug("Time to load presets: " + (end - startTime) + "ms " + presetCount + " loaded from " + fileCount + " files"); - log.info("Loading component presets from " + SYSTEM_PRESET_DIR); - - FileIterator iterator = DirectoryIterator.findDirectory(SYSTEM_PRESET_DIR, new SimpleFileFilter("", false, "ser")); - - if (iterator != null) { - while (iterator.hasNext()) { - Pair<String, InputStream> f = iterator.next(); - try { - ObjectInputStream ois = new ObjectInputStream(f.getV()); - List<ComponentPreset> list = (List<ComponentPreset>) ois.readObject(); - componentPresetDao.addAll(list); - fileCount++; - presetCount += list.size(); - } catch (Exception ex) { - throw new BugException(ex); - } - } - } - + } + + /** + * loads the user defined defined components into the database + * uses the directory defined in the preferences + */ + private void loadUserComponents() { SimpleFileFilter orcFilter = new SimpleFileFilter("", false, "orc"); + FileIterator iterator; try { iterator = new DirectoryIterator( ((SwingPreferences) Application.getPreferences()).getDefaultUserComponentDirectory(), orcFilter, true); } catch (IOException ioex) { - iterator = null; log.debug("Error opening UserComponentDirectory", ioex); + return; } - if (iterator != null) { - while (iterator.hasNext()) { - Pair<String, InputStream> f = iterator.next(); - Collection<ComponentPreset> presets = loadFile(f.getU(), f.getV()); - componentPresetDao.addAll(presets); + while (iterator.hasNext()) { + Pair<String, InputStream> f = iterator.next(); + Collection<ComponentPreset> presets = loadFile(f.getU(), f.getV()); + componentPresetDao.addAll(presets); + fileCount++; + presetCount += presets.size(); + } + } + + /** + * loads the default preset components into the database + * uses the file directory from "datafiles/presets" + */ + private void loadPresetComponents() { + log.info("Loading component presets from " + SYSTEM_PRESET_DIR); + FileIterator iterator = DirectoryIterator.findDirectory(SYSTEM_PRESET_DIR, new SimpleFileFilter("", false, "ser")); + + if(iterator == null) + return; + + while (iterator.hasNext()) { + Pair<String, InputStream> f = iterator.next(); + try { + ObjectInputStream ois = new ObjectInputStream(f.getV()); + @SuppressWarnings("unchecked") + List<ComponentPreset> list = (List<ComponentPreset>) ois.readObject(); + componentPresetDao.addAll(list); fileCount++; - presetCount += presets.size(); + presetCount += list.size(); + } catch (Exception ex) { + throw new BugException(ex); } } - - long end = System.currentTimeMillis(); - log.debug("Time to load presets: " + (end - startTime) + "ms " + presetCount + " loaded from " + fileCount + " files"); - } + /** + * load components from a custom component file + * uses an OpenRocketComponentLoader for the job + * + * @param fileName name of the file to be + * @param stream the input stream to the file + * @return a collection of components preset from the file + */ private Collection<ComponentPreset> loadFile(String fileName, InputStream stream) { log.debug("loading from file: " + fileName); OpenRocketComponentLoader loader = new OpenRocketComponentLoader(); diff --git a/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java b/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java index 19ef8bd18..47b1f3f28 100644 --- a/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java +++ b/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java @@ -17,7 +17,6 @@ import net.sf.openrocket.file.iterator.FileIterator; import net.sf.openrocket.file.motor.GeneralMotorLoader; import net.sf.openrocket.gui.util.SimpleFileFilter; import net.sf.openrocket.gui.util.SwingPreferences; -import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; @@ -40,7 +39,9 @@ public class MotorDatabaseLoader extends AsynchronousDatabaseLoader { private final ThrustCurveMotorSetDatabase database = new ThrustCurveMotorSetDatabase(); private int motorCount = 0; - + /** + * sole constructor, default startup delay = 0 + */ public MotorDatabaseLoader() { super(STARTUP_DELAY); } @@ -48,19 +49,18 @@ public class MotorDatabaseLoader extends AsynchronousDatabaseLoader { @Override protected void loadDatabase() { - + loadSerializedMotorDatabase(); + loadUserDefinedMotors(); + } + + + /** + * Loads the user defined motors + * the directories are defined in the preferences + */ + private void loadUserDefinedMotors() { GeneralMotorLoader loader = new GeneralMotorLoader(); SimpleFileFilter fileFilter = new SimpleFileFilter("", loader.getSupportedExtensions()); - - log.info("Starting reading serialized motor database"); - FileIterator iterator = DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY, new SimpleFileFilter("", false, "ser")); - while (iterator.hasNext()) { - Pair<String, InputStream> f = iterator.next(); - loadSerialized(f); - } - log.info("Ending reading serialized motor database, motorCount=" + motorCount); - - log.info("Starting reading user-defined motors"); for (File file : ((SwingPreferences) Application.getPreferences()).getUserThrustCurveFiles()) { if (file.isFile()) { @@ -72,11 +72,29 @@ public class MotorDatabaseLoader extends AsynchronousDatabaseLoader { } } log.info("Ending reading user-defined motors, motorCount=" + motorCount); - + } + + + /** + * Loads the default, with established serialized manufacturing and data + * uses directory "datafiles/thrustcurves" for data + */ + private void loadSerializedMotorDatabase() { + log.info("Starting reading serialized motor database"); + FileIterator iterator = DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY, new SimpleFileFilter("", false, "ser")); + while (iterator.hasNext()) { + Pair<String, InputStream> f = iterator.next(); + loadSerialized(f); + } + log.info("Ending reading serialized motor database, motorCount=" + motorCount); } - + /** + * loads a serailized motor data from an stream + * + * @param f the pair of a String with the filename (for logging) and the input stream + */ @SuppressWarnings("unchecked") private void loadSerialized(Pair<String, InputStream> f) { try { @@ -89,27 +107,52 @@ public class MotorDatabaseLoader extends AsynchronousDatabaseLoader { } } - + /** + * loads a single motor file into the database using a simple file handler object + * + * @param loader the motor loading handler object + * @param file the File to the file itself + */ private void loadFile(GeneralMotorLoader loader, File file) { - BufferedInputStream bis = null; try { log.debug("Loading motors from file " + file); - bis = new BufferedInputStream(new FileInputStream(file)); - List<ThrustCurveMotor.Builder> motors = loader.load(bis, file.getName()); - addMotorsFromBuilders(motors); - bis.close(); + loadFile( + loader, + new Pair<String,InputStream>( + file.getName(), + new BufferedInputStream(new FileInputStream(file)))); } catch (IOException e) { log.warn("IOException while reading " + file + ": " + e, e); - if (bis != null) { - try { - bis.close(); - } catch (IOException e1) { - - } + } + } + + /** + * loads a single motor file into the database using inputStream instead of file object + * + * @param loader an object to handle the loading + * @param f the pair of File name and its input stream + */ + private void loadFile(GeneralMotorLoader loader, Pair<String, InputStream> f) { + try { + List<ThrustCurveMotor.Builder> motors = loader.load(f.getV(), f.getU()); + addMotorsFromBuilders(motors); + f.getV().close(); + } catch (IOException e) { + log.warn("IOException while loading file " + f.getU() + ": " + e, e); + try { + f.getV().close(); + } catch (IOException e1) { } } } + /** + * loads an entire directory of motor files + * + * @param loader a motor loading handler object + * @param fileFilter the supported extensions of files + * @param file the directory file object + */ private void loadDirectory(GeneralMotorLoader loader, SimpleFileFilter fileFilter, File file) { FileIterator iterator; try { @@ -119,21 +162,17 @@ public class MotorDatabaseLoader extends AsynchronousDatabaseLoader { return; } while (iterator.hasNext()) { - Pair<String, InputStream> f = iterator.next(); - try { - List<ThrustCurveMotor.Builder> motors = loader.load(f.getV(), f.getU()); - addMotorsFromBuilders(motors); - f.getV().close(); - } catch (IOException e) { - log.warn("IOException while loading file " + f.getU() + ": " + e, e); - try { - f.getV().close(); - } catch (IOException e1) { - } - } + loadFile(loader, iterator.next()); } } + + + + /** + * adds a motor list into the database + * @param motors the list of motors to be added + */ private synchronized void addMotors(List<ThrustCurveMotor> motors) { for (ThrustCurveMotor m : motors) { motorCount++; @@ -141,8 +180,13 @@ public class MotorDatabaseLoader extends AsynchronousDatabaseLoader { } } - private synchronized void addMotorsFromBuilders(List<ThrustCurveMotor.Builder> motors) { - for (ThrustCurveMotor.Builder m : motors) { + /** + * builds the motors while building them + * + * @param motorBuilders List of motor builders to be used for adding motor into the database + */ + private synchronized void addMotorsFromBuilders(List<ThrustCurveMotor.Builder> motorBuilders) { + for (ThrustCurveMotor.Builder m : motorBuilders) { motorCount++; database.addMotor(m.build()); }