From: plaa Date: Thu, 26 Nov 2009 15:56:35 +0000 (+0000) Subject: refactored file package X-Git-Tag: upstream/1.0.0~9 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=d23932f311312abb73801262a80ef2f6bc66818d;p=debian%2Fopenrocket refactored file package git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@33 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/TODO b/TODO index 67f030c7..ec071f93 100644 --- a/TODO +++ b/TODO @@ -26,6 +26,7 @@ Postponed: - Reading thrust curves from external directory - Plot motor thrust curve +- Screw weights for nose cones / transitions - Windows executable wrapper (launch4j) - Allow only one instance of OpenRocket running (RMI communication) diff --git a/html/actions/updates.php b/html/actions/updates.php index e4981450..55e3e147 100644 --- a/html/actions/updates.php +++ b/html/actions/updates.php @@ -60,6 +60,6 @@ header("Content-type: text/plain; charset=utf-8"); $version = $_GET["version"]; // No updates available -header("HTTP/1.0 202 No Content"); +header("HTTP/1.0 204 No Content"); ?> \ No newline at end of file diff --git a/html/download.html b/html/download.html index d3497cac..856c2386 100644 --- a/html/download.html +++ b/html/download.html @@ -56,7 +56,7 @@ later. The Sun JRE is recommended.

- Download OpenRocket 0.9.3

+ Download OpenRocket 0.9.4

OpenRocket is still considered beta software. If you encounter any problems, please @@ -65,7 +65,7 @@

OpenRocket can be started in graphical environments (such as Windows) by double-clicking the package icon. No installation is required. From the command line it can be started by

-
$ java -jar OpenRocket-0.9.3.jar
+
$ java -jar OpenRocket-0.9.4.jar

Older packages and source code are available from the SourceForge repository.

diff --git a/html/index.html b/html/index.html index c2d39944..864a2736 100644 --- a/html/index.html +++ b/html/index.html @@ -90,6 +90,13 @@

News

+

24.11.2009: Version 0.9.4 is + released!

+ +

This version adds support for through-the-wall fin tabs, + attaching components to coupler tubes, material editing, automatic + update checking, in addition to fixing numerous bugs.

+

1.9.2009: Version 0.9.3 is released!

diff --git a/releasing.txt b/releasing.txt index 389c310c..63164c4f 100644 --- a/releasing.txt +++ b/releasing.txt @@ -6,13 +6,21 @@ Steps for making a release: 3. Update ChangeLog 4. ant dist 5. Test new features (not in project directory) -6. Copy distribution files into dist/ +6. Copy distribution files into dists/ 7. Update Eclipse project and commit files to SVN -8. Tag the version in SVN +8. Tag the version in SVN, URL: + https://openrocket.svn.sourceforge.net/svnroot/openrocket/tags/Release_0.9.x 9. Upload JAR and source distribution and ReleaseNotes to Sourceforge - (Project Admin -> File Manager, create new version directory + upload) + - Project Admin -> File Manager + - create new version directory under /openrocket + - upload JAR, ZIP and ReleaseNotes + - select ReleaseNotes properties, set as release notes + - select JAR properties, set release note file and default downloads + - select ZIP properties, set release note file 10. Update HTML: index.html (release notes) download.html (version number) -11. Update HTML to web server -12. Send email about new release +11. Update HTML to web server: + scp * plaa,openrocket@web.sourceforge.net:htdocs/ +12. Test downloading from Sourceforge and web site 13. Update build.properties to "pre" version + commit +14. Send email about new release to openrocket-announce@lists.sourceforge.net diff --git a/src/net/sf/openrocket/communication/UpdateInfoRetriever.java b/src/net/sf/openrocket/communication/UpdateInfoRetriever.java index d67ea335..082f2b9f 100644 --- a/src/net/sf/openrocket/communication/UpdateInfoRetriever.java +++ b/src/net/sf/openrocket/communication/UpdateInfoRetriever.java @@ -124,6 +124,7 @@ public class UpdateInfoRetriever { try { doConnection(); } catch (IOException e) { + System.out.println("fetching update failed: " + e); return; } } @@ -158,6 +159,8 @@ public class UpdateInfoRetriever { try { connection.connect(); + System.out.println("response code: " + connection.getResponseCode()); + if (connection.getResponseCode() == Communicator.UPDATE_INFO_NO_UPDATE_CODE) { // No updates are available info = new UpdateInfo(); diff --git a/src/net/sf/openrocket/file/GeneralMotorLoader.java b/src/net/sf/openrocket/file/GeneralMotorLoader.java index 5981c2d0..117672f5 100644 --- a/src/net/sf/openrocket/file/GeneralMotorLoader.java +++ b/src/net/sf/openrocket/file/GeneralMotorLoader.java @@ -6,6 +6,8 @@ import java.io.Reader; import java.nio.charset.Charset; import java.util.List; +import net.sf.openrocket.file.motor.RASPMotorLoader; +import net.sf.openrocket.file.motor.RockSimMotorLoader; import net.sf.openrocket.motor.Motor; /** diff --git a/src/net/sf/openrocket/file/GeneralRocketLoader.java b/src/net/sf/openrocket/file/GeneralRocketLoader.java index ce997dfe..1dc3329a 100644 --- a/src/net/sf/openrocket/file/GeneralRocketLoader.java +++ b/src/net/sf/openrocket/file/GeneralRocketLoader.java @@ -7,6 +7,7 @@ import java.nio.charset.Charset; import java.util.zip.GZIPInputStream; import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.openrocket.OpenRocketLoader; /** diff --git a/src/net/sf/openrocket/file/OpenRocketLoader.java b/src/net/sf/openrocket/file/OpenRocketLoader.java deleted file mode 100644 index c5d5f54c..00000000 --- a/src/net/sf/openrocket/file/OpenRocketLoader.java +++ /dev/null @@ -1,1976 +0,0 @@ -package net.sf.openrocket.file; - -import java.awt.Color; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import net.sf.openrocket.aerodynamics.Warning; -import net.sf.openrocket.aerodynamics.WarningSet; -import net.sf.openrocket.database.Databases; -import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.document.StorageOptions; -import net.sf.openrocket.document.Simulation.Status; -import net.sf.openrocket.file.simplesax.ElementHandler; -import net.sf.openrocket.file.simplesax.PlainTextHandler; -import net.sf.openrocket.file.simplesax.SimpleSAX; -import net.sf.openrocket.material.Material; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.rocketcomponent.BodyComponent; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.Bulkhead; -import net.sf.openrocket.rocketcomponent.CenteringRing; -import net.sf.openrocket.rocketcomponent.ClusterConfiguration; -import net.sf.openrocket.rocketcomponent.Clusterable; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.EngineBlock; -import net.sf.openrocket.rocketcomponent.ExternalComponent; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.IllegalFinPointException; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.InternalComponent; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.MassComponent; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.Parachute; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; -import net.sf.openrocket.rocketcomponent.RecoveryDevice; -import net.sf.openrocket.rocketcomponent.ReferenceType; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.ShockCord; -import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.rocketcomponent.Streamer; -import net.sf.openrocket.rocketcomponent.StructuralComponent; -import net.sf.openrocket.rocketcomponent.SymmetricComponent; -import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; -import net.sf.openrocket.rocketcomponent.TubeCoupler; -import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; -import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; -import net.sf.openrocket.rocketcomponent.RocketComponent.Position; -import net.sf.openrocket.simulation.FlightData; -import net.sf.openrocket.simulation.FlightDataBranch; -import net.sf.openrocket.simulation.FlightEvent; -import net.sf.openrocket.simulation.SimulationConditions; -import net.sf.openrocket.simulation.FlightEvent.Type; -import net.sf.openrocket.unit.UnitGroup; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.LineStyle; -import net.sf.openrocket.util.Reflection; - -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - - -/** - * Class that loads a rocket definition from an OpenRocket rocket file. - *

- * This class uses SAX to read the XML file format. The - * {@link #loadFromStream(InputStream)} method simply sets the system up and - * starts the parsing, while the actual logic is in the private inner class - * OpenRocketHandler. - * - * @author Sampo Niskanen - */ - -public class OpenRocketLoader extends RocketLoader { - - @Override - public OpenRocketDocument loadFromStream(InputStream source) throws RocketLoadException, - IOException { - InputSource xmlSource = new InputSource(source); - OpenRocketHandler handler = new OpenRocketHandler(); - - - try { - SimpleSAX.readXML(xmlSource, handler, warnings); - } catch (SAXException e) { - throw new RocketLoadException("Malformed XML in input.", e); - } - - - OpenRocketDocument doc = handler.getDocument(); - doc.getDefaultConfiguration().setAllStages(); - - // Deduce suitable time skip - double timeSkip = StorageOptions.SIMULATION_DATA_NONE; - for (Simulation s: doc.getSimulations()) { - if (s.getStatus() == Simulation.Status.EXTERNAL || - s.getStatus() == Simulation.Status.NOT_SIMULATED) - continue; - if (s.getSimulatedData() == null) - continue; - if (s.getSimulatedData().getBranchCount() == 0) - continue; - FlightDataBranch branch = s.getSimulatedData().getBranch(0); - if (branch == null) - continue; - List list = branch.get(FlightDataBranch.TYPE_TIME); - if (list == null) - continue; - - double previousTime = Double.NaN; - for (double time: list) { - if (time - previousTime < timeSkip) - timeSkip = time-previousTime; - previousTime = time; - } - } - // Round value - timeSkip = Math.rint(timeSkip*100)/100; - - doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip); - doc.getDefaultStorageOptions().setCompressionEnabled(false); // Set by caller if compressed - doc.getDefaultStorageOptions().setExplicitlySet(false); - - doc.clearUndo(); - return doc; - } - -} - - - -class DocumentConfig { - - /* Remember to update OpenRocketSaver as well! */ - public static final String[] SUPPORTED_VERSIONS = { "0.9", "1.0", "1.1" }; - - - //////// Component constructors - static final HashMap> constructors = new HashMap>(); - static { - try { - // External components - constructors.put("bodytube", BodyTube.class.getConstructor(new Class[0])); - constructors.put("transition", Transition.class.getConstructor(new Class[0])); - constructors.put("nosecone", NoseCone.class.getConstructor(new Class[0])); - constructors.put("trapezoidfinset", TrapezoidFinSet.class.getConstructor(new Class[0])); - constructors.put("ellipticalfinset", EllipticalFinSet.class.getConstructor(new Class[0])); - constructors.put("freeformfinset", FreeformFinSet.class.getConstructor(new Class[0])); - constructors.put("launchlug", LaunchLug.class.getConstructor(new Class[0])); - - // Internal components - constructors.put("engineblock", EngineBlock.class.getConstructor(new Class[0])); - constructors.put("innertube", InnerTube.class.getConstructor(new Class[0])); - constructors.put("tubecoupler", TubeCoupler.class.getConstructor(new Class[0])); - constructors.put("bulkhead", Bulkhead.class.getConstructor(new Class[0])); - constructors.put("centeringring", CenteringRing.class.getConstructor(new Class[0])); - - constructors.put("masscomponent", MassComponent.class.getConstructor(new Class[0])); - constructors.put("shockcord", ShockCord.class.getConstructor(new Class[0])); - constructors.put("parachute", Parachute.class.getConstructor(new Class[0])); - constructors.put("streamer", Streamer.class.getConstructor(new Class[0])); - - // Other - constructors.put("stage", Stage.class.getConstructor(new Class[0])); - - } catch (NoSuchMethodException e) { - throw new RuntimeException( - "Error in constructing the 'constructors' HashMap."); - } - } - - - //////// Parameter setters - /* - * The keys are of the form Class:param, where Class is the class name and param - * the element name. Setters are searched for in descending class order. - * A setter of null means setting the parameter is not allowed. - */ - static final HashMap setters = new HashMap(); - static { - // RocketComponent - setters.put("RocketComponent:name", new StringSetter( - Reflection.findMethodStatic(RocketComponent.class, "setName", String.class))); - setters.put("RocketComponent:color", new ColorSetter( - Reflection.findMethodStatic(RocketComponent.class, "setColor", Color.class))); - setters.put("RocketComponent:linestyle", new EnumSetter( - Reflection.findMethodStatic(RocketComponent.class, "setLineStyle", LineStyle.class), - LineStyle.class)); - setters.put("RocketComponent:position", new PositionSetter()); - setters.put("RocketComponent:overridemass", new OverrideSetter( - Reflection.findMethodStatic(RocketComponent.class, "setOverrideMass", double.class), - Reflection.findMethodStatic(RocketComponent.class, "setMassOverridden", boolean.class))); - setters.put("RocketComponent:overridecg", new OverrideSetter( - Reflection.findMethodStatic(RocketComponent.class, "setOverrideCGX", double.class), - Reflection.findMethodStatic(RocketComponent.class, "setCGOverridden", boolean.class))); - setters.put("RocketComponent:overridesubcomponents", new BooleanSetter( - Reflection.findMethodStatic(RocketComponent.class, "setOverrideSubcomponents", boolean.class))); - setters.put("RocketComponent:comment", new StringSetter( - Reflection.findMethodStatic(RocketComponent.class, "setComment", String.class))); - - // ExternalComponent - setters.put("ExternalComponent:finish", new EnumSetter( - Reflection.findMethodStatic(ExternalComponent.class, "setFinish", Finish.class), - Finish.class)); - setters.put("ExternalComponent:material", new MaterialSetter( - Reflection.findMethodStatic(ExternalComponent.class, "setMaterial", Material.class), - Material.Type.BULK)); - - // BodyComponent - setters.put("BodyComponent:length", new DoubleSetter( - Reflection.findMethodStatic(BodyComponent.class, "setLength", double.class))); - - // SymmetricComponent - setters.put("SymmetricComponent:thickness", new DoubleSetter( - Reflection.findMethodStatic(SymmetricComponent.class,"setThickness", double.class), - "filled", - Reflection.findMethodStatic(SymmetricComponent.class,"setFilled", boolean.class))); - - // BodyTube - setters.put("BodyTube:radius", new DoubleSetter( - Reflection.findMethodStatic(BodyTube.class, "setRadius", double.class), - "auto", - Reflection.findMethodStatic(BodyTube.class,"setRadiusAutomatic", boolean.class))); - - // Transition - setters.put("Transition:shape", new EnumSetter( - Reflection.findMethodStatic(Transition.class, "setType", Transition.Shape.class), - Transition.Shape.class)); - setters.put("Transition:shapeclipped", new BooleanSetter( - Reflection.findMethodStatic(Transition.class, "setClipped", boolean.class))); - setters.put("Transition:shapeparameter", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setShapeParameter", double.class))); - - setters.put("Transition:foreradius", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setForeRadius", double.class), - "auto", - Reflection.findMethodStatic(Transition.class, "setForeRadiusAutomatic", boolean.class))); - setters.put("Transition:aftradius", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setAftRadius", double.class), - "auto", - Reflection.findMethodStatic(Transition.class, "setAftRadiusAutomatic", boolean.class))); - - setters.put("Transition:foreshoulderradius", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setForeShoulderRadius", double.class))); - setters.put("Transition:foreshoulderlength", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setForeShoulderLength", double.class))); - setters.put("Transition:foreshoulderthickness", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setForeShoulderThickness", double.class))); - setters.put("Transition:foreshouldercapped", new BooleanSetter( - Reflection.findMethodStatic(Transition.class, "setForeShoulderCapped", boolean.class))); - - setters.put("Transition:aftshoulderradius", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setAftShoulderRadius", double.class))); - setters.put("Transition:aftshoulderlength", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setAftShoulderLength", double.class))); - setters.put("Transition:aftshoulderthickness", new DoubleSetter( - Reflection.findMethodStatic(Transition.class, "setAftShoulderThickness", double.class))); - setters.put("Transition:aftshouldercapped", new BooleanSetter( - Reflection.findMethodStatic(Transition.class, "setAftShoulderCapped", boolean.class))); - - // NoseCone - disable disallowed elements - setters.put("NoseCone:foreradius", null); - setters.put("NoseCone:foreshoulderradius", null); - setters.put("NoseCone:foreshoulderlength", null); - setters.put("NoseCone:foreshoulderthickness", null); - setters.put("NoseCone:foreshouldercapped", null); - - // FinSet - setters.put("FinSet:fincount", new IntSetter( - Reflection.findMethodStatic(FinSet.class, "setFinCount", int.class))); - setters.put("FinSet:rotation", new DoubleSetter( - Reflection.findMethodStatic(FinSet.class, "setBaseRotation", double.class), Math.PI/180.0)); - setters.put("FinSet:thickness", new DoubleSetter( - Reflection.findMethodStatic(FinSet.class, "setThickness", double.class))); - setters.put("FinSet:crosssection", new EnumSetter( - Reflection.findMethodStatic(FinSet.class, "setCrossSection", FinSet.CrossSection.class), - FinSet.CrossSection.class)); - setters.put("FinSet:cant", new DoubleSetter( - Reflection.findMethodStatic(FinSet.class, "setCantAngle", double.class), Math.PI/180.0)); - setters.put("FinSet:tabheight", new DoubleSetter( - Reflection.findMethodStatic(FinSet.class, "setTabHeight", double.class))); - setters.put("FinSet:tablength", new DoubleSetter( - Reflection.findMethodStatic(FinSet.class, "setTabLength", double.class))); - setters.put("FinSet:tabposition", new FinTabPositionSetter()); - - // TrapezoidFinSet - setters.put("TrapezoidFinSet:rootchord", new DoubleSetter( - Reflection.findMethodStatic(TrapezoidFinSet.class, "setRootChord", double.class))); - setters.put("TrapezoidFinSet:tipchord", new DoubleSetter( - Reflection.findMethodStatic(TrapezoidFinSet.class, "setTipChord", double.class))); - setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter( - Reflection.findMethodStatic(TrapezoidFinSet.class, "setSweep", double.class))); - setters.put("TrapezoidFinSet:height", new DoubleSetter( - Reflection.findMethodStatic(TrapezoidFinSet.class, "setHeight", double.class))); - - // EllipticalFinSet - setters.put("EllipticalFinSet:rootchord", new DoubleSetter( - Reflection.findMethodStatic(EllipticalFinSet.class, "setLength", double.class))); - setters.put("EllipticalFinSet:height", new DoubleSetter( - Reflection.findMethodStatic(EllipticalFinSet.class, "setHeight", double.class))); - - // FreeformFinSet points handled as a special handler - - // LaunchLug - setters.put("LaunchLug:radius", new DoubleSetter( - Reflection.findMethodStatic(LaunchLug.class, "setRadius", double.class))); - setters.put("LaunchLug:length", new DoubleSetter( - Reflection.findMethodStatic(LaunchLug.class, "setLength", double.class))); - setters.put("LaunchLug:thickness", new DoubleSetter( - Reflection.findMethodStatic(LaunchLug.class, "setThickness", double.class))); - setters.put("LaunchLug:radialdirection", new DoubleSetter( - Reflection.findMethodStatic(LaunchLug.class, "setRadialDirection", double.class), - Math.PI/180.0)); - - // InternalComponent - nothing - - // StructuralComponent - setters.put("StructuralComponent:material", new MaterialSetter( - Reflection.findMethodStatic(StructuralComponent.class, "setMaterial", Material.class), - Material.Type.BULK)); - - // RingComponent - setters.put("RingComponent:length", new DoubleSetter( - Reflection.findMethodStatic(RingComponent.class, "setLength", double.class))); - setters.put("RingComponent:radialposition", new DoubleSetter( - Reflection.findMethodStatic(RingComponent.class, "setRadialPosition", double.class))); - setters.put("RingComponent:radialdirection", new DoubleSetter( - Reflection.findMethodStatic(RingComponent.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // ThicknessRingComponent - radius on separate components due to differing automatics - setters.put("ThicknessRingComponent:thickness", new DoubleSetter( - Reflection.findMethodStatic(ThicknessRingComponent.class, "setThickness", double.class))); - - // EngineBlock - setters.put("EngineBlock:outerradius", new DoubleSetter( - Reflection.findMethodStatic(EngineBlock.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethodStatic(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class))); - - // TubeCoupler - setters.put("TubeCoupler:outerradius", new DoubleSetter( - Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class))); - - // InnerTube - setters.put("InnerTube:outerradius", new DoubleSetter( - Reflection.findMethodStatic(InnerTube.class, "setOuterRadius", double.class))); - setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter()); - setters.put("InnerTube:clusterscale", new DoubleSetter( - Reflection.findMethodStatic(InnerTube.class, "setClusterScale", double.class))); - setters.put("InnerTube:clusterrotation", new DoubleSetter( - Reflection.findMethodStatic(InnerTube.class, "setClusterRotation", double.class), - Math.PI / 180.0)); - - // RadiusRingComponent - - // Bulkhead - setters.put("RadiusRingComponent:innerradius", new DoubleSetter( - Reflection.findMethodStatic(RadiusRingComponent.class, "setInnerRadius", double.class))); - setters.put("Bulkhead:outerradius", new DoubleSetter( - Reflection.findMethodStatic(Bulkhead.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethodStatic(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class))); - - // CenteringRing - setters.put("CenteringRing:innerradius", new DoubleSetter( - Reflection.findMethodStatic(CenteringRing.class, "setInnerRadius", double.class), - "auto", - Reflection.findMethodStatic(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class))); - setters.put("CenteringRing:outerradius", new DoubleSetter( - Reflection.findMethodStatic(CenteringRing.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethodStatic(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class))); - - - // MassObject - setters.put("MassObject:packedlength", new DoubleSetter( - Reflection.findMethodStatic(MassObject.class, "setLength", double.class))); - setters.put("MassObject:packedradius", new DoubleSetter( - Reflection.findMethodStatic(MassObject.class, "setRadius", double.class))); - setters.put("MassObject:radialposition", new DoubleSetter( - Reflection.findMethodStatic(MassObject.class, "setRadialPosition", double.class))); - setters.put("MassObject:radialdirection", new DoubleSetter( - Reflection.findMethodStatic(MassObject.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // MassComponent - setters.put("MassComponent:mass", new DoubleSetter( - Reflection.findMethodStatic(MassComponent.class, "setComponentMass", double.class))); - - // ShockCord - setters.put("ShockCord:cordlength", new DoubleSetter( - Reflection.findMethodStatic(ShockCord.class, "setCordLength", double.class))); - setters.put("ShockCord:material", new MaterialSetter( - Reflection.findMethodStatic(ShockCord.class, "setMaterial", Material.class), - Material.Type.LINE)); - - // RecoveryDevice - setters.put("RecoveryDevice:cd", new DoubleSetter( - Reflection.findMethodStatic(RecoveryDevice.class, "setCD", double.class), - "auto", - Reflection.findMethodStatic(RecoveryDevice.class, "setCDAutomatic", boolean.class))); - setters.put("RecoveryDevice:deployevent", new EnumSetter( - Reflection.findMethodStatic(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class), - RecoveryDevice.DeployEvent.class)); - setters.put("RecoveryDevice:deployaltitude", new DoubleSetter( - Reflection.findMethodStatic(RecoveryDevice.class, "setDeployAltitude", double.class))); - setters.put("RecoveryDevice:deploydelay", new DoubleSetter( - Reflection.findMethodStatic(RecoveryDevice.class, "setDeployDelay", double.class))); - setters.put("RecoveryDevice:material", new MaterialSetter( - Reflection.findMethodStatic(RecoveryDevice.class, "setMaterial", Material.class), - Material.Type.SURFACE)); - - // Parachute - setters.put("Parachute:diameter", new DoubleSetter( - Reflection.findMethodStatic(Parachute.class, "setDiameter", double.class))); - setters.put("Parachute:linecount", new IntSetter( - Reflection.findMethodStatic(Parachute.class, "setLineCount", int.class))); - setters.put("Parachute:linelength", new DoubleSetter( - Reflection.findMethodStatic(Parachute.class, "setLineLength", double.class))); - setters.put("Parachute:linematerial", new MaterialSetter( - Reflection.findMethodStatic(Parachute.class, "setLineMaterial", Material.class), - Material.Type.LINE)); - - // Streamer - setters.put("Streamer:striplength", new DoubleSetter( - Reflection.findMethodStatic(Streamer.class, "setStripLength", double.class))); - setters.put("Streamer:stripwidth", new DoubleSetter( - Reflection.findMethodStatic(Streamer.class, "setStripWidth", double.class))); - - // Rocket - // handled by separate handler - setters.put("Rocket:referencetype", new EnumSetter( - Reflection.findMethodStatic(Rocket.class, "setReferenceType", ReferenceType.class), - ReferenceType.class)); - setters.put("Rocket:customreference", new DoubleSetter( - Reflection.findMethodStatic(Rocket.class, "setCustomReferenceLength", double.class))); - setters.put("Rocket:designer", new StringSetter( - Reflection.findMethodStatic(Rocket.class, "setDesigner", String.class))); - setters.put("Rocket:revision", new StringSetter( - Reflection.findMethodStatic(Rocket.class, "setRevision", String.class))); - } - - - /** - * Search for a enum value that has the corresponding name as an XML value. The current - * conversion from enum name to XML value is to lowercase the name and strip out all - * underscore characters. This method returns a match to these criteria, or null - * if no such enum exists. - * - * @param then enum type. - * @param name the XML value, null ok. - * @param enumClass the class of the enum. - * @return the found enum value, or null. - */ - public static > Enum findEnum(String name, - Class> enumClass) { - - if (name == null) - return null; - name = name.trim(); - for (Enum e: enumClass.getEnumConstants()) { - if (e.name().toLowerCase().replace("_", "").equals(name)) { - return e; - } - } - return null; - } - - - /** - * Convert a string to a double including formatting specifications of the OpenRocket - * file format. This accepts all formatting that is valid for - * Double.parseDouble(s) and a few others as well ("Inf", "-Inf"). - * - * @param s the string to parse. - * @return the numerical value. - * @throws NumberFormatException the the string cannot be parsed. - */ - public static double stringToDouble(String s) throws NumberFormatException { - if (s == null) - throw new NumberFormatException("null string"); - if (s.equalsIgnoreCase("NaN")) - return Double.NaN; - if (s.equalsIgnoreCase("Inf")) - return Double.POSITIVE_INFINITY; - if (s.equalsIgnoreCase("-Inf")) - return Double.NEGATIVE_INFINITY; - return Double.parseDouble(s); - } -} - - - - - -/** - * The starting point of the handlers. Accepts a single element and hands - * the contents to be read by a OpenRocketContentsHandler. - */ -class OpenRocketHandler extends ElementHandler { - private OpenRocketContentHandler handler = null; - - /** - * Return the OpenRocketDocument read from the file, or null if a document - * has not been read yet. - * - * @return the document read, or null. - */ - public OpenRocketDocument getDocument() { - return handler.getDocument(); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Check for unknown elements - if (!element.equals("openrocket")) { - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - return null; - } - - // Check for first call - if (handler != null) { - warnings.add(Warning.fromString("Multiple document elements found, ignoring later " - + "ones.")); - return null; - } - - // Check version number - String version = null; - String creator = attributes.remove("creator"); - String docVersion = attributes.remove("version"); - for (String v : DocumentConfig.SUPPORTED_VERSIONS) { - if (v.equals(docVersion)) { - version = v; - break; - } - } - if (version == null) { - String str = "Unsupported document version"; - if (docVersion != null) - str += " " + docVersion; - if (creator != null && !creator.trim().equals("")) - str += " (written using '" + creator.trim() + "')"; - str += ", attempting to read file anyway."; - warnings.add(str); - } - - handler = new OpenRocketContentHandler(); - return handler; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - attributes.remove("version"); - attributes.remove("creator"); - super.closeElement(element, attributes, content, warnings); - } - - -} - - -/** - * Handles the content of the tag. - */ -class OpenRocketContentHandler extends ElementHandler { - private final OpenRocketDocument doc; - private final Rocket rocket; - - private boolean rocketDefined = false; - private boolean simulationsDefined = false; - - public OpenRocketContentHandler() { - this.rocket = new Rocket(); - this.doc = new OpenRocketDocument(rocket); - } - - - public OpenRocketDocument getDocument() { - if (!rocketDefined) - return null; - return doc; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("rocket")) { - if (rocketDefined) { - warnings.add(Warning - .fromString("Multiple rocket designs within one document, " - + "ignoring later ones.")); - return null; - } - rocketDefined = true; - return new ComponentParameterHandler(rocket); - } - - if (element.equals("simulations")) { - if (simulationsDefined) { - warnings.add(Warning - .fromString("Multiple simulation definitions within one document, " - + "ignoring later ones.")); - return null; - } - simulationsDefined = true; - return new SimulationsHandler(doc); - } - - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - - return null; - } -} - - - - -/** - * A handler that creates components from the corresponding elements. The control of the - * contents is passed on to ComponentParameterHandler. - */ -class ComponentHandler extends ElementHandler { - private final RocketComponent parent; - - public ComponentHandler(RocketComponent parent) { - this.parent = parent; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Attempt to construct new component - Constructor constructor = DocumentConfig.constructors - .get(element); - if (constructor == null) { - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - return null; - } - - RocketComponent c; - try { - c = constructor.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException("Error constructing component.", e); - } catch (IllegalAccessException e) { - throw new RuntimeException("Error constructing component.", e); - } catch (InvocationTargetException e) { - throw new RuntimeException("Error constructing component.", e); - } - - parent.addChild(c); - - return new ComponentParameterHandler(c); - } -} - - -/** - * A handler that populates the parameters of a previously constructed rocket component. - * This uses the setters, or delegates the handling to another handler for specific - * elements. - */ -class ComponentParameterHandler extends ElementHandler { - private final RocketComponent component; - - public ComponentParameterHandler(RocketComponent c) { - this.component = c; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Check for specific elements that contain other elements - if (element.equals("subcomponents")) { - return new ComponentHandler(component); - } - if (element.equals("motormount")) { - if (!(component instanceof MotorMount)) { - warnings.add(Warning.fromString("Illegal component defined as motor mount.")); - return null; - } - return new MotorMountHandler((MotorMount)component); - } - if (element.equals("finpoints")) { - if (!(component instanceof FreeformFinSet)) { - warnings.add(Warning.fromString("Illegal component defined for fin points.")); - return null; - } - return new FinSetPointHandler((FreeformFinSet)component); - } - if (element.equals("motorconfiguration")) { - if (!(component instanceof Rocket)) { - warnings.add(Warning.fromString("Illegal component defined for motor configuration.")); - return null; - } - return new MotorConfigurationHandler((Rocket)component); - } - - - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("subcomponents") || element.equals("motormount") || - element.equals("finpoints") || element.equals("motorconfiguration")) { - return; - } - - // Search for the correct setter class - - Class c; - for (c = component.getClass(); c != null; c = c.getSuperclass()) { - String setterKey = c.getSimpleName() + ":" + element; - Setter s = DocumentConfig.setters.get(setterKey); - if (s != null) { - // Setter found - System.out.println("Calling with key "+setterKey); - s.set(component, content, attributes, warnings); - break; - } - if (DocumentConfig.setters.containsKey(setterKey)) { - // Key exists but is null -> invalid parameter - c = null; - break; - } - } - if (c == null) { - warnings.add(Warning.fromString("Unknown parameter type '" + element + "' for " - + component.getComponentName() + ", ignoring.")); - } - } -} - - -/** - * A handler that reads the specifications within the freeformfinset's - * elements. - */ -class FinSetPointHandler extends ElementHandler { - private final FreeformFinSet finset; - private final ArrayList coordinates = new ArrayList(); - - public FinSetPointHandler(FreeformFinSet finset) { - this.finset = finset; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - String strx = attributes.remove("x"); - String stry = attributes.remove("y"); - if (strx == null || stry == null) { - warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); - return; - } - try { - double x = Double.parseDouble(strx); - double y = Double.parseDouble(stry); - coordinates.add(new Coordinate(x,y)); - } catch (NumberFormatException e) { - warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); - return; - } - - super.closeElement(element, attributes, content, warnings); - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - try { - finset.setPoints(coordinates.toArray(new Coordinate[0])); - } catch (IllegalFinPointException e) { - warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring.")); - } - } -} - - -class MotorMountHandler extends ElementHandler { - private final MotorMount mount; - private MotorHandler motorHandler; - - public MotorMountHandler(MotorMount mount) { - this.mount = mount; - mount.setMotorMount(true); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("motor")) { - motorHandler = new MotorHandler(); - return motorHandler; - } - - if (element.equals("ignitionevent") || - element.equals("ignitiondelay") || - element.equals("overhang")) { - return PlainTextHandler.INSTANCE; - } - - warnings.add(Warning.fromString("Unknown element '"+element+"' encountered, ignoring.")); - return null; - } - - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - if (element.equals("motor")) { - String id = attributes.get("configid"); - if (id == null || id.equals("")) { - warnings.add(Warning.fromString("Illegal motor specification, ignoring.")); - return; - } - - Motor motor = motorHandler.getMotor(warnings); - mount.setMotor(id, motor); - mount.setMotorDelay(id, motorHandler.getDelay(warnings)); - return; - } - - if (element.equals("ignitionevent")) { - MotorMount.IgnitionEvent event = null; - for (MotorMount.IgnitionEvent e : MotorMount.IgnitionEvent.values()) { - if (e.name().toLowerCase().replaceAll("_", "").equals(content)) { - event = e; - break; - } - } - if (event == null) { - warnings.add(Warning.fromString("Unknown ignition event type '"+content+"', ignoring.")); - return; - } - mount.setIgnitionEvent(event); - return; - } - - if (element.equals("ignitiondelay")) { - double d; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException nfe) { - warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); - return; - } - mount.setIgnitionDelay(d); - return; - } - - if (element.equals("overhang")) { - double d; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException nfe) { - warnings.add(Warning.fromString("Illegal overhang specified, ignoring.")); - return; - } - mount.setMotorOverhang(d); - return; - } - - super.closeElement(element, attributes, content, warnings); - } -} - - - - -class MotorConfigurationHandler extends ElementHandler { - private final Rocket rocket; - private String name = null; - private boolean inNameElement = false; - - public MotorConfigurationHandler(Rocket rocket) { - this.rocket = rocket; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (inNameElement || !element.equals("name")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return null; - } - inNameElement = true; - - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - name = content; - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - String configid = attributes.remove("configid"); - if (configid == null || configid.equals("")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (!rocket.addMotorConfigurationID(configid)) { - warnings.add("Duplicate motor configuration ID used."); - return; - } - - if (name != null && name.trim().length() > 0) { - rocket.setMotorConfigurationName(configid, name); - } - - if ("true".equals(attributes.remove("default"))) { - rocket.getDefaultConfiguration().setMotorConfigurationID(configid); - } - - super.closeElement(element, attributes, content, warnings); - } -} - - -class MotorHandler extends ElementHandler { - private Motor.Type type = null; - private String manufacturer = null; - private String designation = null; - private double diameter = Double.NaN; - private double length = Double.NaN; - private double delay = Double.NaN; - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - - /** - * Return the motor to use, or null. - */ - public Motor getMotor(WarningSet warnings) { - if (designation == null) { - warnings.add(Warning.fromString("No motor specified, ignoring.")); - return null; - } - Motor[] motors = Databases.findMotors(type, manufacturer, designation, diameter, length); - if (motors.length == 0) { - String str = "No motor with designation '"+designation+"'"; - if (manufacturer != null) - str += " for manufacturer '" + manufacturer + "'"; - warnings.add(Warning.fromString(str + " found.")); - return null; - } - if (motors.length > 1) { - String str = "Multiple motors with designation '"+designation+"'"; - if (manufacturer != null) - str += " for manufacturer '" + manufacturer + "'"; - warnings.add(Warning.fromString(str + " found, one chosen arbitrarily.")); - } - return motors[0]; - } - - - /** - * Return the delay to use for the motor. - */ - public double getDelay(WarningSet warnings) { - if (Double.isNaN(delay)) { - warnings.add(Warning.fromString("Motor delay not specified, assuming no ejection charge.")); - return Motor.PLUGGED; - } - return delay; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - content = content.trim(); - - if (element.equals("type")) { - - // Motor type - type = null; - for (Motor.Type t: Motor.Type.values()) { - if (t.name().toLowerCase().equals(content)) { - type = t; - break; - } - } - if (type == null) { - warnings.add(Warning.fromString("Unknown motor type '"+content+"', ignoring.")); - } - - } else if (element.equals("manufacturer")) { - - // Manufacturer - manufacturer = content; - - } else if (element.equals("designation")) { - - // Designation - designation = content; - - } else if (element.equals("diameter")) { - - // Diameter - diameter = Double.NaN; - try { - diameter = Double.parseDouble(content); - } catch (NumberFormatException e) { - // Ignore - } - if (Double.isNaN(diameter)) { - warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); - } - - } else if (element.equals("length")) { - - // Length - length = Double.NaN; - try { - length = Double.parseDouble(content); - } catch (NumberFormatException ignore) { } - - if (Double.isNaN(length)) { - warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); - } - - } else if (element.equals("delay")) { - - // Delay - delay = Double.NaN; - if (content.equals("none")) { - delay = Motor.PLUGGED; - } else { - try { - delay = Double.parseDouble(content); - } catch (NumberFormatException ignore) { } - - if (Double.isNaN(delay)) { - warnings.add(Warning.fromString("Illegal motor delay specified, ignoring.")); - } - - } - - } else { - super.closeElement(element, attributes, content, warnings); - } - } - -} - - - -class SimulationsHandler extends ElementHandler { - private final OpenRocketDocument doc; - private SingleSimulationHandler handler; - - public SimulationsHandler(OpenRocketDocument doc) { - this.doc = doc; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (!element.equals("simulation")) { - warnings.add("Unknown element '"+element+"', ignoring."); - return null; - } - - handler = new SingleSimulationHandler(doc); - return handler; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - attributes.remove("status"); - super.closeElement(element, attributes, content, warnings); - } - - -} - -class SingleSimulationHandler extends ElementHandler { - - private final OpenRocketDocument doc; - - private String name; - - private SimulationConditionsHandler conditionHandler; - private FlightDataHandler dataHandler; - - private final List listeners = new ArrayList(); - - public SingleSimulationHandler(OpenRocketDocument doc) { - this.doc = doc; - } - - - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("name") || element.equals("simulator") || - element.equals("calculator") || element.equals("listener")) { - return PlainTextHandler.INSTANCE; - } else if (element.equals("conditions")) { - conditionHandler = new SimulationConditionsHandler(doc.getRocket()); - return conditionHandler; - } else if (element.equals("flightdata")) { - dataHandler = new FlightDataHandler(); - return dataHandler; - } else { - warnings.add("Unknown element '"+element+"', ignoring."); - return null; - } - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("name")) { - name = content; - } else if (element.equals("simulator")) { - if (!content.trim().equals("RK4Simulator")) { - warnings.add("Unknown simulator '" + content.trim() + "' specified, ignoring."); - } - } else if (element.equals("calculator")) { - if (!content.trim().equals("BarrowmanCalculator")) { - warnings.add("Unknown calculator '" + content.trim() + "' specified, ignoring."); - } - } else if (element.equals("listener") && content.trim().length() > 0) { - listeners.add(content.trim()); - } - - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - - String s = attributes.get("status"); - Simulation.Status status = (Status) DocumentConfig.findEnum(s, Simulation.Status.class); - if (status == null) { - warnings.add("Simulation status unknown, assuming outdated."); - status = Simulation.Status.OUTDATED; - } - - SimulationConditions conditions; - if (conditionHandler != null) { - conditions = conditionHandler.getConditions(); - } else { - warnings.add("Simulation conditions not defined, using defaults."); - conditions = new SimulationConditions(doc.getRocket()); - } - - if (name == null) - name = "Simulation"; - - FlightData data; - if (dataHandler == null) - data = null; - else - data = dataHandler.getFlightData(); - - Simulation simulation = new Simulation(doc.getRocket(), status, name, - conditions, listeners, data); - - doc.addSimulation(simulation); - } -} - - - -class SimulationConditionsHandler extends ElementHandler { - private SimulationConditions conditions; - private AtmosphereHandler atmosphereHandler; - - public SimulationConditionsHandler(Rocket rocket) { - conditions = new SimulationConditions(rocket); - } - - public SimulationConditions getConditions() { - return conditions; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - if (element.equals("atmosphere")) { - atmosphereHandler = new AtmosphereHandler(attributes.get("model")); - return atmosphereHandler; - } - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - double d = Double.NaN; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException ignore) { } - - - if (element.equals("configid")) { - if (content.equals("")) { - conditions.setMotorConfigurationID(null); - } else { - conditions.setMotorConfigurationID(content); - } - } else if (element.equals("launchrodlength")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod length defined, ignoring."); - } else { - conditions.setLaunchRodLength(d); - } - } else if (element.equals("launchrodangle")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod angle defined, ignoring."); - } else { - conditions.setLaunchRodAngle(d*Math.PI/180); - } - } else if (element.equals("launchroddirection")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod direction defined, ignoring."); - } else { - conditions.setLaunchRodDirection(d*Math.PI/180); - } - } else if (element.equals("windaverage")) { - if (Double.isNaN(d)) { - warnings.add("Illegal average windspeed defined, ignoring."); - } else { - conditions.setWindSpeedAverage(d); - } - } else if (element.equals("windturbulence")) { - if (Double.isNaN(d)) { - warnings.add("Illegal wind turbulence intensity defined, ignoring."); - } else { - conditions.setWindTurbulenceIntensity(d); - } - } else if (element.equals("launchaltitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch altitude defined, ignoring."); - } else { - conditions.setLaunchAltitude(d); - } - } else if (element.equals("launchlatitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch latitude defined, ignoring."); - } else { - conditions.setLaunchLatitude(d); - } - } else if (element.equals("atmosphere")) { - atmosphereHandler.storeSettings(conditions, warnings); - } else if (element.equals("timestep")) { - if (Double.isNaN(d)) { - warnings.add("Illegal time step defined, ignoring."); - } else { - conditions.setTimeStep(d); - } - } - } -} - - -class AtmosphereHandler extends ElementHandler { - private final String model; - private double temperature = Double.NaN; - private double pressure = Double.NaN; - - public AtmosphereHandler(String model) { - this.model = model; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - double d = Double.NaN; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException ignore) { } - - if (element.equals("basetemperature")) { - if (Double.isNaN(d)) { - warnings.add("Illegal base temperature specified, ignoring."); - } - temperature = d; - } else if (element.equals("basepressure")) { - if (Double.isNaN(d)) { - warnings.add("Illegal base pressure specified, ignoring."); - } - pressure = d; - } else { - super.closeElement(element, attributes, content, warnings); - } - } - - - public void storeSettings(SimulationConditions cond, WarningSet warnings) { - if (!Double.isNaN(pressure)) { - cond.setLaunchPressure(pressure); - } - if (!Double.isNaN(temperature)) { - cond.setLaunchTemperature(temperature); - } - - if ("isa".equals(model)) { - cond.setISAAtmosphere(true); - } else if ("extendedisa".equals(model)){ - cond.setISAAtmosphere(false); - } else { - cond.setISAAtmosphere(true); - warnings.add("Unknown atmospheric model, using ISA."); - } - } - -} - - -class FlightDataHandler extends ElementHandler { - - private FlightDataBranchHandler dataHandler; - private WarningSet warningSet = new WarningSet(); - private List branches = new ArrayList(); - - private FlightData data; - - public FlightData getFlightData() { - return data; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("warning")) { - return PlainTextHandler.INSTANCE; - } - if (element.equals("databranch")) { - if (attributes.get("name") == null || attributes.get("types")==null) { - warnings.add("Illegal flight data definition, ignoring."); - return null; - } - dataHandler = new FlightDataBranchHandler(attributes.get("name"), - attributes.get("types")); - return dataHandler; - } - - warnings.add("Unknown element '"+element+"' encountered, ignoring."); - return null; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("databranch")) { - FlightDataBranch branch = dataHandler.getBranch(); - if (branch.getLength() > 0) { - branches.add(branch); - } - } else if (element.equals("warning")) { - warningSet.add(Warning.fromString(content)); - } - } - - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (branches.size() > 0) { - data = new FlightData(branches.toArray(new FlightDataBranch[0])); - } else { - double maxAltitude = Double.NaN; - double maxVelocity = Double.NaN; - double maxAcceleration = Double.NaN; - double maxMach = Double.NaN; - double timeToApogee = Double.NaN; - double flightTime = Double.NaN; - double groundHitVelocity = Double.NaN; - - try { - maxAltitude = DocumentConfig.stringToDouble(attributes.get("maxaltitude")); - } catch (NumberFormatException ignore) { } - try { - maxVelocity = DocumentConfig.stringToDouble(attributes.get("maxvelocity")); - } catch (NumberFormatException ignore) { } - try { - maxAcceleration = DocumentConfig.stringToDouble(attributes.get("maxacceleration")); - } catch (NumberFormatException ignore) { } - try { - maxMach = DocumentConfig.stringToDouble(attributes.get("maxmach")); - } catch (NumberFormatException ignore) { } - try { - timeToApogee = DocumentConfig.stringToDouble(attributes.get("timetoapogee")); - } catch (NumberFormatException ignore) { } - try { - flightTime = DocumentConfig.stringToDouble(attributes.get("flighttime")); - } catch (NumberFormatException ignore) { } - try { - groundHitVelocity = - DocumentConfig.stringToDouble(attributes.get("groundhitvelocity")); - } catch (NumberFormatException ignore) { } - - data = new FlightData(maxAltitude, maxVelocity, maxAcceleration, maxMach, - timeToApogee, flightTime, groundHitVelocity); - } - - data.getWarningSet().addAll(warningSet); - } - - -} - - -class FlightDataBranchHandler extends ElementHandler { - private final FlightDataBranch.Type[] types; - private final FlightDataBranch branch; - - public FlightDataBranchHandler(String name, String typeList) { - String[] split = typeList.split(","); - types = new FlightDataBranch.Type[split.length]; - for (int i=0; i < split.length; i++) { - types[i] = FlightDataBranch.getType(split[i], UnitGroup.UNITS_NONE); - } - - // TODO: LOW: May throw an IllegalArgumentException - branch = new FlightDataBranch(name, types); - } - - public FlightDataBranch getBranch() { - branch.immute(); - return branch; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("datapoint")) - return PlainTextHandler.INSTANCE; - if (element.equals("event")) - return PlainTextHandler.INSTANCE; - - warnings.add("Unknown element '"+element+"' encountered, ignoring."); - return null; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("event")) { - double time; - FlightEvent.Type type; - - try { - time = DocumentConfig.stringToDouble(attributes.get("time")); - } catch (NumberFormatException e) { - warnings.add("Illegal event specification, ignoring."); - return; - } - - type = (Type) DocumentConfig.findEnum(attributes.get("type"), FlightEvent.Type.class); - if (type == null) { - warnings.add("Illegal event specification, ignoring."); - return; - } - - branch.addEvent(time, new FlightEvent(type, time)); - return; - } - - if (!element.equals("datapoint")) { - warnings.add("Unknown element '"+element+"' encountered, ignoring."); - return; - } - - // element == "datapoint" - - - // Check line format - String[] split = content.split(","); - if (split.length != types.length) { - warnings.add("Data point did not contain correct amount of values, ignoring point."); - return; - } - - // Parse the doubles - double[] values = new double[split.length]; - for (int i=0; i < values.length; i++) { - try { - values[i] = DocumentConfig.stringToDouble(split[i]); - } catch (NumberFormatException e) { - warnings.add("Data point format error, ignoring point."); - return; - } - } - - // Add point to branch - branch.addPoint(); - for (int i=0; i < types.length; i++) { - branch.setValue(types[i], values[i]); - } - } -} - - - - - - -///////////////// Setters implementation - - -//// Interface -interface Setter { - /** - * Set the specified value to the given component. - * - * @param component the component to which to set. - * @param value the value within the element. - * @param attributes attributes for the element. - * @param warnings the warning set to use. - */ - public void set(RocketComponent component, String value, - HashMap attributes, WarningSet warnings); -} - - -//// StringSetter - sets the value to the contained String -class StringSetter implements Setter { - private final Reflection.Method setMethod; - - public StringSetter(Reflection.Method set) { - setMethod = set; - } - - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - setMethod.invoke(c, s); - } -} - -//// IntSetter - set an integer value -class IntSetter implements Setter { - private final Reflection.Method setMethod; - - public IntSetter(Reflection.Method set) { - setMethod = set; - } - - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - try { - int n = Integer.parseInt(s); - setMethod.invoke(c, n); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - -//// BooleanSetter - set a boolean value -class BooleanSetter implements Setter { - private final Reflection.Method setMethod; - - public BooleanSetter(Reflection.Method set) { - setMethod = set; - } - - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - s = s.trim(); - if (s.equalsIgnoreCase("true")) { - setMethod.invoke(c, true); - } else if (s.equalsIgnoreCase("false")) { - setMethod.invoke(c, false); - } else { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - - -//// DoubleSetter - sets a double value or (alternatively) if a specific string is encountered -//// calls a setXXX(boolean) method. -class DoubleSetter implements Setter { - private final Reflection.Method setMethod; - private final String specialString; - private final Reflection.Method specialMethod; - private final double multiplier; - - /** - * Set only the double value. - * @param set set method for the double value. - */ - public DoubleSetter(Reflection.Method set) { - this.setMethod = set; - this.specialString = null; - this.specialMethod = null; - this.multiplier = 1.0; - } - - /** - * Multiply with the given multiplier and set the double value. - * @param set set method for the double value. - * @param mul multiplier. - */ - public DoubleSetter(Reflection.Method set, double mul) { - this.setMethod = set; - this.specialString = null; - this.specialMethod = null; - this.multiplier = mul; - } - - /** - * Set the double value, or if the value equals the special string, use the - * special setter and set it to true. - * - * @param set double setter. - * @param special special string - * @param specialMethod boolean setter. - */ - public DoubleSetter(Reflection.Method set, String special, - Reflection.Method specialMethod) { - this.setMethod = set; - this.specialString = special; - this.specialMethod = specialMethod; - this.multiplier = 1.0; - } - - - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - s = s.trim(); - - // Check for special case - if (specialMethod != null && s.equalsIgnoreCase(specialString)) { - specialMethod.invoke(c, true); - return; - } - - // Normal case - try { - double d = Double.parseDouble(s); - setMethod.invoke(c, d * multiplier); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - -class OverrideSetter implements Setter { - private final Reflection.Method setMethod; - private final Reflection.Method enabledMethod; - - public OverrideSetter(Reflection.Method set, Reflection.Method enabledMethod) { - this.setMethod = set; - this.enabledMethod = enabledMethod; - } - - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - try { - double d = Double.parseDouble(s); - setMethod.invoke(c, d); - enabledMethod.invoke(c, true); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - -//// EnumSetter - sets a generic enum type -class EnumSetter> implements Setter { - private final Reflection.Method setter; - private final Class enumClass; - - public EnumSetter(Reflection.Method set, Class enumClass) { - this.setter = set; - this.enumClass = enumClass; - } - - @Override - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - - Enum setEnum = DocumentConfig.findEnum(name, enumClass); - if (setEnum == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - setter.invoke(c, setEnum); - } -} - - -//// ColorSetter - sets a Color value -class ColorSetter implements Setter { - private final Reflection.Method setMethod; - - public ColorSetter(Reflection.Method set) { - setMethod = set; - } - - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - String red = attributes.get("red"); - String green = attributes.get("green"); - String blue = attributes.get("blue"); - - if (red == null || green == null || blue == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - int r, g, b; - try { - r = Integer.parseInt(red); - g = Integer.parseInt(green); - b = Integer.parseInt(blue); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - Color color = new Color(r, g, b); - setMethod.invoke(c, color); - - if (!s.trim().equals("")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - - -class MaterialSetter implements Setter { - private final Reflection.Method setMethod; - private final Material.Type type; - - public MaterialSetter(Reflection.Method set, Material.Type type) { - this.setMethod = set; - this.type = type; - } - - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - - Material mat; - - // Check name != "" - name = name.trim(); - if (name.equals("")) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - - // Parse density - double density; - String str; - str = attributes.remove("density"); - if (str == null) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - try { - density = Double.parseDouble(str); - } catch (NumberFormatException e) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - - // Parse thickness -// double thickness = 0; -// str = attributes.remove("thickness"); -// try { -// if (str != null) -// thickness = Double.parseDouble(str); -// } catch (NumberFormatException e){ -// warnings.add(Warning.fromString("Illegal material specification, ignoring.")); -// return; -// } - - // Check type if specified - str = attributes.remove("type"); - if (str != null && !type.name().toLowerCase().equals(str)) { - warnings.add(Warning.fromString("Illegal material type specified, ignoring.")); - return; - } - - mat = Databases.findMaterial(type, name, density, false); - - setMethod.invoke(c, mat); - } -} - - - - -class PositionSetter implements Setter { - - public void set(RocketComponent c, String value, HashMap attributes, - WarningSet warnings) { - - RocketComponent.Position type = (Position) DocumentConfig.findEnum(attributes.get("type"), - RocketComponent.Position.class); - if (type == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - double pos; - try { - pos = Double.parseDouble(value); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (c instanceof FinSet) { - ((FinSet)c).setRelativePosition(type); - c.setPositionValue(pos); - } else if (c instanceof LaunchLug) { - ((LaunchLug)c).setRelativePosition(type); - c.setPositionValue(pos); - } else if (c instanceof InternalComponent) { - ((InternalComponent)c).setRelativePosition(type); - c.setPositionValue(pos); - } else { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - - } -} - - -class FinTabPositionSetter extends DoubleSetter { - - public FinTabPositionSetter() { - super(Reflection.findMethodStatic(FinSet.class, "setTabShift", double.class)); - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - if (!(c instanceof FinSet)) { - throw new IllegalStateException("FinTabPositionSetter called for component " + c); - } - - String relative = attributes.get("relativeto"); - FinSet.TabRelativePosition position = - (TabRelativePosition) DocumentConfig.findEnum(relative, - FinSet.TabRelativePosition.class); - - if (position != null) { - - ((FinSet)c).setTabRelativePosition(position); - - } else { - if (relative == null) { - warnings.add("Required attribute 'relativeto' not found for fin tab position."); - } else { - warnings.add("Illegal attribute value '" + relative + "' encountered."); - } - } - - super.set(c, s, attributes, warnings); - } - - -} - - -class ClusterConfigurationSetter implements Setter { - - public void set(RocketComponent component, String value, HashMap attributes, - WarningSet warnings) { - - if (!(component instanceof Clusterable)) { - warnings.add("Illegal component defined as cluster."); - return; - } - - ClusterConfiguration config = null; - for (ClusterConfiguration c: ClusterConfiguration.CONFIGURATIONS) { - if (c.getXMLName().equals(value)) { - config = c; - break; - } - } - - if (config == null) { - warnings.add("Illegal cluster configuration specified."); - return; - } - - ((Clusterable)component).setClusterConfiguration(config); - } -} - - diff --git a/src/net/sf/openrocket/file/OpenRocketSaver.java b/src/net/sf/openrocket/file/OpenRocketSaver.java deleted file mode 100644 index 312aa0bb..00000000 --- a/src/net/sf/openrocket/file/OpenRocketSaver.java +++ /dev/null @@ -1,531 +0,0 @@ -package net.sf.openrocket.file; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.zip.GZIPOutputStream; - -import net.sf.openrocket.aerodynamics.Warning; -import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.document.StorageOptions; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.TubeCoupler; -import net.sf.openrocket.simulation.FlightData; -import net.sf.openrocket.simulation.FlightDataBranch; -import net.sf.openrocket.simulation.FlightEvent; -import net.sf.openrocket.simulation.SimulationConditions; -import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.Pair; -import net.sf.openrocket.util.Prefs; -import net.sf.openrocket.util.Reflection; -import net.sf.openrocket.util.TextUtil; - -public class OpenRocketSaver extends RocketSaver { - - /** - * Divisor used in converting an integer version to the point-represented version. - * The integer version divided by this value is the major version and the remainder is - * the minor version. For example 101 corresponds to file version "1.1". - */ - public static final int FILE_VERSION_DIVISOR = 100; - - - private static final String OPENROCKET_CHARSET = "UTF-8"; - - private static final String METHOD_PACKAGE = "net.sf.openrocket.file.openrocket"; - private static final String METHOD_SUFFIX = "Saver"; - - - // Estimated storage used by different portions - // These have been hand-estimated from saved files - private static final int BYTES_PER_COMPONENT_UNCOMPRESSED = 590; - private static final int BYTES_PER_COMPONENT_COMPRESSED = 80; - private static final int BYTES_PER_SIMULATION_UNCOMPRESSED = 1000; - private static final int BYTES_PER_SIMULATION_COMPRESSED = 100; - private static final int BYTES_PER_DATAPOINT_UNCOMPRESSED = 350; - private static final int BYTES_PER_DATAPOINT_COMPRESSED = 100; - - - private int indent; - private Writer dest; - - @Override - public void save(OutputStream output, OpenRocketDocument document, StorageOptions options) - throws IOException { - - if (options.isCompressionEnabled()) { - output = new GZIPOutputStream(output); - } - - dest = new BufferedWriter(new OutputStreamWriter(output, OPENROCKET_CHARSET)); - - final int fileVersion = calculateNecessaryFileVersion(document, options); - final String fileVersionString = - (fileVersion / FILE_VERSION_DIVISOR) + "." + (fileVersion % FILE_VERSION_DIVISOR); - - - this.indent = 0; - - System.out.println("Writing..."); - - writeln(""); - writeln(""); - indent++; - - // Recursively save the rocket structure - saveComponent(document.getRocket()); - - writeln(""); - - // Save all simulations - writeln(""); - indent++; - boolean first = true; - for (Simulation s: document.getSimulations()) { - if (!first) - writeln(""); - first = false; - saveSimulation(s, options.getSimulationTimeSkip()); - } - indent--; - writeln(""); - - indent--; - writeln(""); - - dest.flush(); - if (options.isCompressionEnabled()) { - ((GZIPOutputStream)output).finish(); - } - } - - - - @Override - public long estimateFileSize(OpenRocketDocument doc, StorageOptions options) { - - long size = 0; - - // Size per component - int componentCount = 0; - Rocket rocket = doc.getRocket(); - Iterator iterator = rocket.deepIterator(true); - while (iterator.hasNext()) { - iterator.next(); - componentCount++; - } - - if (options.isCompressionEnabled()) - size += componentCount * BYTES_PER_COMPONENT_COMPRESSED; - else - size += componentCount * BYTES_PER_COMPONENT_UNCOMPRESSED; - - - // Size per simulation - if (options.isCompressionEnabled()) - size += doc.getSimulationCount() * BYTES_PER_SIMULATION_COMPRESSED; - else - size += doc.getSimulationCount() * BYTES_PER_SIMULATION_UNCOMPRESSED; - - - // Size per flight data point - int pointCount = 0; - double timeSkip = options.getSimulationTimeSkip(); - if (timeSkip != StorageOptions.SIMULATION_DATA_NONE) { - for (Simulation s: doc.getSimulations()) { - FlightData data = s.getSimulatedData(); - if (data != null) { - for (int i=0; i < data.getBranchCount(); i++) { - pointCount += countFlightDataBranchPoints(data.getBranch(i), timeSkip); - } - } - } - } - - if (options.isCompressionEnabled()) - size += pointCount * BYTES_PER_DATAPOINT_COMPRESSED; - else - size += pointCount * BYTES_PER_DATAPOINT_UNCOMPRESSED; - - return size; - } - - - /** - * Determine which file version is required in order to store all the features of the - * current design. By default the oldest version that supports all the necessary features - * will be used. - * - * @param document the document to output. - * @param opts the storage options. - * @return the integer file version to use. - */ - private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) { - /* - * File version 1.1 is required for: - * - fin tabs - * - components attached to tube coupler - * - * Otherwise use version 1.0. - */ - - // Check for fin tabs (version 1.1) - Iterator iterator = document.getRocket().deepIterator(); - while (iterator.hasNext()) { - RocketComponent c = iterator.next(); - - // Check for fin tabs - if (c instanceof FinSet) { - FinSet fin = (FinSet)c; - if (!MathUtil.equals(fin.getTabHeight(),0) && - !MathUtil.equals(fin.getTabLength(), 0)) { - return FILE_VERSION_DIVISOR + 1; - } - } - - // Check for components attached to tube coupler - if (c instanceof TubeCoupler) { - if (c.getChildCount() > 0) { - return FILE_VERSION_DIVISOR + 1; - } - } - } - - // Default (version 1.0) - return FILE_VERSION_DIVISOR + 0; - } - - - - @SuppressWarnings("unchecked") - private void saveComponent(RocketComponent component) throws IOException { - - Reflection.Method m = Reflection.findMethod(METHOD_PACKAGE, component, METHOD_SUFFIX, - "getElements", RocketComponent.class); - if (m==null) { - throw new RuntimeException("Unable to find saving class for component "+ - component.getComponentName()); - } - - // Get the strings to save - List list = (List) m.invokeStatic(component); - int length = list.size(); - - if (length == 0) // Nothing to do - return; - - if (length < 2) { - throw new RuntimeException("BUG, component data length less than two lines."); - } - - // Open element - writeln(list.get(0)); - indent++; - - // Write parameters - for (int i=1; i 0) { - writeln(""); - writeln(""); - indent++; - boolean emptyline = false; - for (RocketComponent subcomponent: component) { - if (emptyline) - writeln(""); - emptyline = true; - saveComponent(subcomponent); - } - indent--; - writeln(""); - } - - // Close element - indent--; - writeln(list.get(length-1)); - } - - - - private void saveSimulation(Simulation simulation, double timeSkip) throws IOException { - SimulationConditions cond = simulation.getConditions(); - - writeln(""); - indent++; - - writeln("" + escapeXML(simulation.getName()) + ""); - // TODO: MEDIUM: Other simulators/calculators - writeln("RK4Simulator"); - writeln("BarrowmanCalculator"); - writeln(""); - indent++; - - writeElement("configid", cond.getMotorConfigurationID()); - writeElement("launchrodlength", cond.getLaunchRodLength()); - writeElement("launchrodangle", cond.getLaunchRodAngle() * 180.0/Math.PI); - writeElement("launchroddirection", cond.getLaunchRodDirection() * 180.0/Math.PI); - writeElement("windaverage", cond.getWindSpeedAverage()); - writeElement("windturbulence", cond.getWindTurbulenceIntensity()); - writeElement("launchaltitude", cond.getLaunchAltitude()); - writeElement("launchlatitude", cond.getLaunchLatitude()); - - if (cond.isISAAtmosphere()) { - writeln(""); - } else { - writeln(""); - indent++; - writeElement("basetemperature", cond.getLaunchTemperature()); - writeElement("basepressure", cond.getLaunchPressure()); - indent--; - writeln(""); - } - - writeElement("timestep", cond.getTimeStep()); - - indent--; - writeln(""); - - - for (String s: simulation.getSimulationListeners()) { - writeElement("listener", escapeXML(s)); - } - - - // Write basic simulation data - - FlightData data = simulation.getSimulatedData(); - if (data != null) { - String str = ""); - } - - indent--; - writeln(""); - - } - - - - private void saveFlightDataBranch(FlightDataBranch branch, double timeSkip) - throws IOException { - double previousTime = -100000; - - if (branch == null) - return; - - // Retrieve the types from the branch - FlightDataBranch.Type[] types = branch.getTypes(); - - if (types.length == 0) - return; - - // Retrieve the data from the branch - List> data = new ArrayList>(types.length); - for (int i=0; i timeData = branch.get(FlightDataBranch.TYPE_TIME); - if (timeData == null) { - // TODO: MEDIUM: External data may not have time data - throw new IllegalArgumentException("Data did not contain time data"); - } - - // Build the tag - StringBuilder sb = new StringBuilder(); - sb.append(" 0) - sb.append(","); - sb.append(escapeXML(types[i].getName())); - } - sb.append("\">"); - writeln(sb.toString()); - indent++; - - // Write events - for (Pair p: branch.getEvents()) { - writeln(""); - } - - // Write the data - int length = branch.getLength(); - if (length > 0) { - writeDataPointString(data, 0, sb); - previousTime = timeData.get(0); - } - - for (int i=1; i < length-1; i++) { - if (Math.abs(timeData.get(i) - previousTime - timeSkip) < - Math.abs(timeData.get(i+1) - previousTime - timeSkip)) { - writeDataPointString(data, i, sb); - previousTime = timeData.get(i); - } - } - - if (length > 1) { - writeDataPointString(data, length-1, sb); - } - - indent--; - writeln(""); - } - - - - /* TODO: LOW: This is largely duplicated from above! */ - private int countFlightDataBranchPoints(FlightDataBranch branch, double timeSkip) { - int count = 0; - - double previousTime = -100000; - - if (branch == null) - return 0; - - // Retrieve the types from the branch - FlightDataBranch.Type[] types = branch.getTypes(); - - if (types.length == 0) - return 0; - - List timeData = branch.get(FlightDataBranch.TYPE_TIME); - if (timeData == null) { - // TODO: MEDIUM: External data may not have time data - throw new IllegalArgumentException("Data did not contain time data"); - } - - // Write the data - int length = branch.getLength(); - if (length > 0) { - count++; - previousTime = timeData.get(0); - } - - for (int i=1; i < length-1; i++) { - if (Math.abs(timeData.get(i) - previousTime - timeSkip) < - Math.abs(timeData.get(i+1) - previousTime - timeSkip)) { - count++; - previousTime = timeData.get(i); - } - } - - if (length > 1) { - count++; - } - - return count; - } - - - - private void writeDataPointString(List> data, int index, StringBuilder sb) - throws IOException { - sb.setLength(0); - sb.append(""); - for (int j=0; j < data.size(); j++) { - if (j > 0) - sb.append(","); - sb.append(TextUtil.doubleToString(data.get(j).get(index))); - } - sb.append(""); - writeln(sb.toString()); - } - - - - private void writeElement(String element, Object content) throws IOException { - if (content == null) - content = ""; - writeln("<"+element+">"+content+""); - } - - - - private void writeln(String str) throws IOException { - if (str.length() == 0) { - dest.write("\n"); - return; - } - String s=""; - for (int i=0; i " + Double.parseDouble(str)); - d *= 10; - } - - - System.out.println("Value: "+ Double.parseDouble("1.2345e9")); - - } - - - /** - * Return the XML equivalent of an enum name. - * - * @param e the enum to save. - * @return the corresponding XML name. - */ - public static String enumToXMLName(Enum e) { - return e.name().toLowerCase().replace("_", ""); - } - -} diff --git a/src/net/sf/openrocket/file/RASPMotorLoader.java b/src/net/sf/openrocket/file/RASPMotorLoader.java deleted file mode 100644 index 409d2188..00000000 --- a/src/net/sf/openrocket/file/RASPMotorLoader.java +++ /dev/null @@ -1,221 +0,0 @@ -package net.sf.openrocket.file; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import net.sf.openrocket.motor.Manufacturer; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.motor.MotorDigest; -import net.sf.openrocket.motor.ThrustCurveMotor; -import net.sf.openrocket.motor.MotorDigest.DataType; -import net.sf.openrocket.util.Coordinate; - -public class RASPMotorLoader extends MotorLoader { - - public static final String CHARSET_NAME = "ISO-8859-1"; - - public static final Charset CHARSET = Charset.forName(CHARSET_NAME); - - - - - @Override - protected Charset getDefaultCharset() { - return CHARSET; - } - - - /** - * Load a Motor from a RASP file specified by the Reader. - * The Reader is responsible for using the correct charset. - *

- * The CG is assumed to be located at the center of the motor casing and the mass - * is calculated from the thrust curve by assuming a constant exhaust velocity. - * - * @param reader the source of the file. - * @return a list of the {@link Motor} objects defined in the file. - * @throws IOException if an I/O error occurs or if the file format is illegal. - */ - @Override - public List load(Reader reader, String filename) throws IOException { - List motors = new ArrayList(); - BufferedReader in = new BufferedReader(reader); - - String manufacturer = ""; - String designation = ""; - String comment = ""; - - double length = 0; - double diameter = 0; - ArrayList delays = null; - - List time = new ArrayList(); - List thrust = new ArrayList(); - - double propW = 0; - double totalW = 0; - - try { - String line; - String[] pieces, buf; - - line = in.readLine(); - main: while (line != null) { // Until EOF - - manufacturer = ""; - designation = ""; - comment = ""; - length = 0; - diameter = 0; - delays = new ArrayList(); - propW = 0; - totalW = 0; - time.clear(); - thrust .clear(); - - // Read comment - while (line.length()==0 || line.charAt(0)==';') { - if (line.length() > 0) { - comment += line.substring(1).trim() + "\n"; - } - line = in.readLine(); - if (line == null) - break main; - } - comment = comment.trim(); - - // Parse header line, example: - // F32 24 124 5-10-15-P .0377 .0695 RV - // desig diam len delays prop.w tot.w manufacturer - pieces = split(line); - if (pieces.length != 7) { - throw new IOException("Illegal file format."); - } - - designation = pieces[0]; - diameter = Double.parseDouble(pieces[1]) / 1000.0; - length = Double.parseDouble(pieces[2]) / 1000.0; - - if (pieces[3].equalsIgnoreCase("None")) { - - } else { - buf = split(pieces[3],"[-,]+"); - for (int i=0; i < buf.length; i++) { - if (buf[i].equalsIgnoreCase("P") || - buf[i].equalsIgnoreCase("plugged")) { - delays.add(Motor.PLUGGED); - } else { - // Many RASP files have "100" as an only delay - double d = Double.parseDouble(buf[i]); - if (d < 99) - delays.add(d); - } - } - Collections.sort(delays); - } - - propW = Double.parseDouble(pieces[4]); - totalW = Double.parseDouble(pieces[5]); - manufacturer = pieces[6]; - - if (propW > totalW) { - throw new IOException("Propellant weight exceeds total weight in " + - "RASP file"); - } - - // Read the data - for (line = in.readLine(); - (line != null) && (line.length()==0 || line.charAt(0) != ';'); - line = in.readLine()) { - - buf = split(line); - if (buf.length == 0) { - continue; - } else if (buf.length == 2) { - - time.add(Double.parseDouble(buf[0])); - thrust .add(Double.parseDouble(buf[1])); - - } else { - throw new IOException("Illegal file format."); - } - } - - // Comment of EOF encountered, marks the start of the next motor - if (time.size() < 2) { - throw new IOException("Illegal file format, too short thrust-curve."); - } - double[] delayArray = new double[delays.size()]; - for (int i=0; i time, List thrust) - throws IOException { - - // Add zero time/thrust if necessary - sortLists(time, thrust); - finalizeThrustCurve(time, thrust); - List mass = calculateMass(time,thrust,totalW,propW); - - double[] timeArray = new double[time.size()]; - double[] thrustArray = new double[time.size()]; - Coordinate[] cgArray = new Coordinate[time.size()]; - for (int i=0; i < time.size(); i++) { - timeArray[i] = time.get(i); - thrustArray[i] = thrust.get(i); - cgArray[i] = new Coordinate(length/2,0,0,mass.get(i)); - } - - designation = removeDelay(designation); - - // Create the motor digest from data available in RASP files - MotorDigest motorDigest = new MotorDigest(); - motorDigest.update(DataType.TIME_ARRAY, timeArray); - motorDigest.update(DataType.MASS_SPECIFIC, totalW, totalW-propW); - motorDigest.update(DataType.FORCE_PER_TIME, thrustArray); - final String digest = motorDigest.getDigest(); - - - try { - - return new ThrustCurveMotor(Manufacturer.getManufacturer(manufacturer), - designation, comment, Motor.Type.UNKNOWN, - delays, diameter, length, timeArray, thrustArray, cgArray, digest); - - } catch (IllegalArgumentException e) { - - // Bad data read from file. - throw new IOException("Illegal file format.", e); - - } - } -} diff --git a/src/net/sf/openrocket/file/RockSimMotorLoader.java b/src/net/sf/openrocket/file/RockSimMotorLoader.java deleted file mode 100644 index 48a15906..00000000 --- a/src/net/sf/openrocket/file/RockSimMotorLoader.java +++ /dev/null @@ -1,461 +0,0 @@ -package net.sf.openrocket.file; - -import java.io.IOException; -import java.io.Reader; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import net.sf.openrocket.aerodynamics.WarningSet; -import net.sf.openrocket.file.simplesax.ElementHandler; -import net.sf.openrocket.file.simplesax.NullElementHandler; -import net.sf.openrocket.file.simplesax.PlainTextHandler; -import net.sf.openrocket.file.simplesax.SimpleSAX; -import net.sf.openrocket.motor.Manufacturer; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.motor.MotorDigest; -import net.sf.openrocket.motor.ThrustCurveMotor; -import net.sf.openrocket.motor.MotorDigest.DataType; -import net.sf.openrocket.util.Coordinate; - -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -public class RockSimMotorLoader extends MotorLoader { - - public static final String CHARSET_NAME = "UTF-8"; - - public static final Charset CHARSET = Charset.forName(CHARSET_NAME); - - - /** Any delay longed than this will be interpreted as a plugged motor. */ - private static final int DELAY_LIMIT = 90; - - - - @Override - protected Charset getDefaultCharset() { - return CHARSET; - } - - - - /** - * Load a Motor from a RockSim motor definition file specified by the - * Reader. The Reader is responsible for using the correct - * charset. - *

- * If automatic CG/mass calculation is used, then the CG is assumed to be located at - * the center of the motor casing and the mass is calculated from the thrust curve - * by assuming a constant exhaust velocity. - * - * @param reader the source of the file. - * @return a list of the {@link Motor} objects defined in the file. - * @throws IOException if an I/O error occurs or if the file format is invalid. - */ - @Override - public List load(Reader reader, String filename) throws IOException { - InputSource source = new InputSource(reader); - RSEHandler handler = new RSEHandler(); - WarningSet warnings = new WarningSet(); - - try { - SimpleSAX.readXML(source, handler, warnings); - return handler.getMotors(); - } catch (SAXException e) { - throw new IOException(e.getMessage(), e); - } - } - - - - /** - * Initial handler for the RockSim engine files. - */ - private static class RSEHandler extends ElementHandler { - private final List motors = new ArrayList(); - - private RSEMotorHandler motorHandler; - - public List getMotors() { - return motors; - } - - @Override - public ElementHandler openElement(String element, - HashMap attributes, WarningSet warnings) throws SAXException { - - if (element.equals("engine-database") || - element.equals("engine-list")) { - // Ignore and elements - return this; - } - - if (element.equals("version")) { - // Ignore elements completely - return null; - } - - if (element.equals("engine")) { - motorHandler = new RSEMotorHandler(attributes); - return motorHandler; - } - - return null; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - if (element.equals("engine")) { - Motor motor = motorHandler.getMotor(); - motors.add(motor); - } - } - } - - - /** - * Handler for a RockSim engine file element. - */ - private static class RSEMotorHandler extends ElementHandler { - - private final String manufacturer; - private final String designation; - private final double[] delays; - private final double diameter; - private final double length; - private final double initMass; - private final double propMass; - private final Motor.Type type; - private boolean calculateMass; - private boolean calculateCG; - - private String description = ""; - - private List time; - private List force; - private List mass; - private List cg; - - private RSEMotorDataHandler dataHandler = null; - - - public RSEMotorHandler(HashMap attributes) throws SAXException { - String str; - - // Manufacturer - str = attributes.get("mfg"); - if (str == null) - throw new SAXException("Manufacturer missing"); - manufacturer = str; - - // Designation - str = attributes.get("code"); - if (str == null) - throw new SAXException("Designation missing"); - designation = removeDelay(str); - - // Delays - ArrayList delayList = new ArrayList(); - str = attributes.get("delays"); - if (str != null) { - String[] split = str.split(","); - for (String delay: split) { - try { - - double d = Double.parseDouble(delay); - if (d >= DELAY_LIMIT) - d = Motor.PLUGGED; - delayList.add(d); - - } catch (NumberFormatException e) { - if (str.equalsIgnoreCase("P") || str.equalsIgnoreCase("plugged")) { - delayList.add(Motor.PLUGGED); - } - } - } - } - delays = new double[delayList.size()]; - for (int i=0; i initMass) { - throw new SAXException("Propellant weight exceeds total weight in " + - "RockSim engine format"); - } - - // Motor type - str = attributes.get("Type"); - if (str != null && str.equalsIgnoreCase("single-use")) { - type = Motor.Type.SINGLE; - } else if (str != null && str.equalsIgnoreCase("hybrid")) { - type = Motor.Type.HYBRID; - } else if (str != null && str.equalsIgnoreCase("reloadable")) { - type = Motor.Type.RELOAD; - } else { - type = Motor.Type.UNKNOWN; - } - - // Calculate mass - str = attributes.get("auto-calc-mass"); - if ("0".equals(str) || "false".equalsIgnoreCase(str)) { - calculateMass = false; - } else { - calculateMass = true; - } - - // Calculate CG - str = attributes.get("auto-calc-cg"); - if ("0".equals(str) || "false".equalsIgnoreCase(str)) { - calculateCG = false; - } else { - calculateCG = true; - } - } - - @Override - public ElementHandler openElement(String element, - HashMap attributes, WarningSet warnings) throws SAXException { - - if (element.equals("comments")) { - return PlainTextHandler.INSTANCE; - } - - if (element.equals("data")) { - if (dataHandler != null) { - throw new SAXException("Multiple data elements encountered in motor " + - "definition"); - } - dataHandler = new RSEMotorDataHandler(); - return dataHandler; - } - - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return null; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("comments")) { - if (description.length() > 0) { - description = description + "\n\n" + content.trim(); - } else { - description = content.trim(); - } - return; - } - - if (element.equals("data")) { - time = dataHandler.getTime(); - force = dataHandler.getForce(); - mass = dataHandler.getMass(); - cg = dataHandler.getCG(); - - sortLists(time, force, mass, cg); - - for (double d: mass) { - if (Double.isNaN(d)) { - calculateMass = true; - break; - } - } - for (double d: cg) { - if (Double.isNaN(d)) { - calculateCG = true; - break; - } - } - return; - } - } - - public Motor getMotor() throws SAXException { - if (time == null || time.size() == 0) - throw new SAXException("Illegal motor data"); - - - finalizeThrustCurve(time, force, mass, cg); - final int n = time.size(); - - if (hasIllegalValue(mass)) - calculateMass = true; - if (hasIllegalValue(cg)) - calculateCG = true; - - if (calculateMass) { - mass = calculateMass(time, force, initMass, propMass); - } - if (calculateCG) { - for (int i=0; i < n; i++) { - cg.set(i, length/2); - } - } - - double[] timeArray = toArray(time); - double[] thrustArray = toArray(force); - Coordinate[] cgArray = new Coordinate[n]; - for (int i=0; i < n; i++) { - cgArray[i] = new Coordinate(cg.get(i),0,0,mass.get(i)); - } - - - // Create the motor digest from all data available in the file - MotorDigest motorDigest = new MotorDigest(); - motorDigest.update(DataType.TIME_ARRAY, timeArray); - if (!calculateMass) { - motorDigest.update(DataType.MASS_PER_TIME, toArray(mass)); - } else { - motorDigest.update(DataType.MASS_SPECIFIC, initMass, initMass-propMass); - } - if (!calculateCG) { - motorDigest.update(DataType.CG_PER_TIME, toArray(cg)); - } - motorDigest.update(DataType.FORCE_PER_TIME, thrustArray); - final String digest = motorDigest.getDigest(); - - - try { - return new ThrustCurveMotor(Manufacturer.getManufacturer(manufacturer), - designation, description, type, - delays, diameter, length, timeArray, thrustArray, cgArray, digest); - } catch (IllegalArgumentException e) { - throw new SAXException("Illegal motor data", e); - } - } - } - - - /** - * Handler for the element in a RockSim engine file motor definition. - */ - private static class RSEMotorDataHandler extends ElementHandler { - - private final List time = new ArrayList(); - private final List force = new ArrayList(); - private final List mass = new ArrayList(); - private final List cg = new ArrayList(); - - - public List getTime() { - return time; - } - public List getForce() { - return force; - } - public List getMass() { - return mass; - } - public List getCG() { - return cg; - } - - - @Override - public ElementHandler openElement(String element, - HashMap attributes, WarningSet warnings) { - - if (element.equals("eng-data")) { - return NullElementHandler.INSTANCE; - } - - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return null; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - double t = parseDouble(attributes.get("t")); - double f = parseDouble(attributes.get("f")); - double m = parseDouble(attributes.get("m")) / 1000.0; - double g = parseDouble(attributes.get("cg")) / 1000.0; - - if (Double.isNaN(t) || Double.isNaN(f)) { - throw new SAXException("Illegal motor data point encountered"); - } - - time.add(t); - force.add(f); - mass.add(m); - cg.add(g); - } - - - private double parseDouble(String str) { - if (str == null) - return Double.NaN; - try { - return Double.parseDouble(str); - } catch (NumberFormatException e) { - return Double.NaN; - } - } - } - - - - private static boolean hasIllegalValue(List list) { - for (Double d: list) { - if (d == null || d.isNaN() || d.isInfinite()) { - return true; - } - } - return false; - } - - private static double[] toArray(List list) { - final int n = list.size(); - double[] array = new double[n]; - for (int i=0; i < n; i++) { - array[i] = list.get(i); - } - return array; - } -} diff --git a/src/net/sf/openrocket/file/motor/RASPMotorLoader.java b/src/net/sf/openrocket/file/motor/RASPMotorLoader.java new file mode 100644 index 00000000..02feb9b2 --- /dev/null +++ b/src/net/sf/openrocket/file/motor/RASPMotorLoader.java @@ -0,0 +1,222 @@ +package net.sf.openrocket.file.motor; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sf.openrocket.file.MotorLoader; +import net.sf.openrocket.motor.Manufacturer; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorDigest; +import net.sf.openrocket.motor.ThrustCurveMotor; +import net.sf.openrocket.motor.MotorDigest.DataType; +import net.sf.openrocket.util.Coordinate; + +public class RASPMotorLoader extends MotorLoader { + + public static final String CHARSET_NAME = "ISO-8859-1"; + + public static final Charset CHARSET = Charset.forName(CHARSET_NAME); + + + + + @Override + protected Charset getDefaultCharset() { + return CHARSET; + } + + + /** + * Load a Motor from a RASP file specified by the Reader. + * The Reader is responsible for using the correct charset. + *

+ * The CG is assumed to be located at the center of the motor casing and the mass + * is calculated from the thrust curve by assuming a constant exhaust velocity. + * + * @param reader the source of the file. + * @return a list of the {@link Motor} objects defined in the file. + * @throws IOException if an I/O error occurs or if the file format is illegal. + */ + @Override + public List load(Reader reader, String filename) throws IOException { + List motors = new ArrayList(); + BufferedReader in = new BufferedReader(reader); + + String manufacturer = ""; + String designation = ""; + String comment = ""; + + double length = 0; + double diameter = 0; + ArrayList delays = null; + + List time = new ArrayList(); + List thrust = new ArrayList(); + + double propW = 0; + double totalW = 0; + + try { + String line; + String[] pieces, buf; + + line = in.readLine(); + main: while (line != null) { // Until EOF + + manufacturer = ""; + designation = ""; + comment = ""; + length = 0; + diameter = 0; + delays = new ArrayList(); + propW = 0; + totalW = 0; + time.clear(); + thrust .clear(); + + // Read comment + while (line.length()==0 || line.charAt(0)==';') { + if (line.length() > 0) { + comment += line.substring(1).trim() + "\n"; + } + line = in.readLine(); + if (line == null) + break main; + } + comment = comment.trim(); + + // Parse header line, example: + // F32 24 124 5-10-15-P .0377 .0695 RV + // desig diam len delays prop.w tot.w manufacturer + pieces = split(line); + if (pieces.length != 7) { + throw new IOException("Illegal file format."); + } + + designation = pieces[0]; + diameter = Double.parseDouble(pieces[1]) / 1000.0; + length = Double.parseDouble(pieces[2]) / 1000.0; + + if (pieces[3].equalsIgnoreCase("None")) { + + } else { + buf = split(pieces[3],"[-,]+"); + for (int i=0; i < buf.length; i++) { + if (buf[i].equalsIgnoreCase("P") || + buf[i].equalsIgnoreCase("plugged")) { + delays.add(Motor.PLUGGED); + } else { + // Many RASP files have "100" as an only delay + double d = Double.parseDouble(buf[i]); + if (d < 99) + delays.add(d); + } + } + Collections.sort(delays); + } + + propW = Double.parseDouble(pieces[4]); + totalW = Double.parseDouble(pieces[5]); + manufacturer = pieces[6]; + + if (propW > totalW) { + throw new IOException("Propellant weight exceeds total weight in " + + "RASP file"); + } + + // Read the data + for (line = in.readLine(); + (line != null) && (line.length()==0 || line.charAt(0) != ';'); + line = in.readLine()) { + + buf = split(line); + if (buf.length == 0) { + continue; + } else if (buf.length == 2) { + + time.add(Double.parseDouble(buf[0])); + thrust .add(Double.parseDouble(buf[1])); + + } else { + throw new IOException("Illegal file format."); + } + } + + // Comment of EOF encountered, marks the start of the next motor + if (time.size() < 2) { + throw new IOException("Illegal file format, too short thrust-curve."); + } + double[] delayArray = new double[delays.size()]; + for (int i=0; i time, List thrust) + throws IOException { + + // Add zero time/thrust if necessary + sortLists(time, thrust); + finalizeThrustCurve(time, thrust); + List mass = calculateMass(time,thrust,totalW,propW); + + double[] timeArray = new double[time.size()]; + double[] thrustArray = new double[time.size()]; + Coordinate[] cgArray = new Coordinate[time.size()]; + for (int i=0; i < time.size(); i++) { + timeArray[i] = time.get(i); + thrustArray[i] = thrust.get(i); + cgArray[i] = new Coordinate(length/2,0,0,mass.get(i)); + } + + designation = removeDelay(designation); + + // Create the motor digest from data available in RASP files + MotorDigest motorDigest = new MotorDigest(); + motorDigest.update(DataType.TIME_ARRAY, timeArray); + motorDigest.update(DataType.MASS_SPECIFIC, totalW, totalW-propW); + motorDigest.update(DataType.FORCE_PER_TIME, thrustArray); + final String digest = motorDigest.getDigest(); + + + try { + + return new ThrustCurveMotor(Manufacturer.getManufacturer(manufacturer), + designation, comment, Motor.Type.UNKNOWN, + delays, diameter, length, timeArray, thrustArray, cgArray, digest); + + } catch (IllegalArgumentException e) { + + // Bad data read from file. + throw new IOException("Illegal file format.", e); + + } + } +} diff --git a/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java b/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java new file mode 100644 index 00000000..0596ba1d --- /dev/null +++ b/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java @@ -0,0 +1,462 @@ +package net.sf.openrocket.file.motor; + +import java.io.IOException; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.MotorLoader; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.NullElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.file.simplesax.SimpleSAX; +import net.sf.openrocket.motor.Manufacturer; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorDigest; +import net.sf.openrocket.motor.ThrustCurveMotor; +import net.sf.openrocket.motor.MotorDigest.DataType; +import net.sf.openrocket.util.Coordinate; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +public class RockSimMotorLoader extends MotorLoader { + + public static final String CHARSET_NAME = "UTF-8"; + + public static final Charset CHARSET = Charset.forName(CHARSET_NAME); + + + /** Any delay longed than this will be interpreted as a plugged motor. */ + private static final int DELAY_LIMIT = 90; + + + + @Override + protected Charset getDefaultCharset() { + return CHARSET; + } + + + + /** + * Load a Motor from a RockSim motor definition file specified by the + * Reader. The Reader is responsible for using the correct + * charset. + *

+ * If automatic CG/mass calculation is used, then the CG is assumed to be located at + * the center of the motor casing and the mass is calculated from the thrust curve + * by assuming a constant exhaust velocity. + * + * @param reader the source of the file. + * @return a list of the {@link Motor} objects defined in the file. + * @throws IOException if an I/O error occurs or if the file format is invalid. + */ + @Override + public List load(Reader reader, String filename) throws IOException { + InputSource source = new InputSource(reader); + RSEHandler handler = new RSEHandler(); + WarningSet warnings = new WarningSet(); + + try { + SimpleSAX.readXML(source, handler, warnings); + return handler.getMotors(); + } catch (SAXException e) { + throw new IOException(e.getMessage(), e); + } + } + + + + /** + * Initial handler for the RockSim engine files. + */ + private static class RSEHandler extends ElementHandler { + private final List motors = new ArrayList(); + + private RSEMotorHandler motorHandler; + + public List getMotors() { + return motors; + } + + @Override + public ElementHandler openElement(String element, + HashMap attributes, WarningSet warnings) throws SAXException { + + if (element.equals("engine-database") || + element.equals("engine-list")) { + // Ignore and elements + return this; + } + + if (element.equals("version")) { + // Ignore elements completely + return null; + } + + if (element.equals("engine")) { + motorHandler = new RSEMotorHandler(attributes); + return motorHandler; + } + + return null; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + if (element.equals("engine")) { + Motor motor = motorHandler.getMotor(); + motors.add(motor); + } + } + } + + + /** + * Handler for a RockSim engine file element. + */ + private static class RSEMotorHandler extends ElementHandler { + + private final String manufacturer; + private final String designation; + private final double[] delays; + private final double diameter; + private final double length; + private final double initMass; + private final double propMass; + private final Motor.Type type; + private boolean calculateMass; + private boolean calculateCG; + + private String description = ""; + + private List time; + private List force; + private List mass; + private List cg; + + private RSEMotorDataHandler dataHandler = null; + + + public RSEMotorHandler(HashMap attributes) throws SAXException { + String str; + + // Manufacturer + str = attributes.get("mfg"); + if (str == null) + throw new SAXException("Manufacturer missing"); + manufacturer = str; + + // Designation + str = attributes.get("code"); + if (str == null) + throw new SAXException("Designation missing"); + designation = removeDelay(str); + + // Delays + ArrayList delayList = new ArrayList(); + str = attributes.get("delays"); + if (str != null) { + String[] split = str.split(","); + for (String delay: split) { + try { + + double d = Double.parseDouble(delay); + if (d >= DELAY_LIMIT) + d = Motor.PLUGGED; + delayList.add(d); + + } catch (NumberFormatException e) { + if (str.equalsIgnoreCase("P") || str.equalsIgnoreCase("plugged")) { + delayList.add(Motor.PLUGGED); + } + } + } + } + delays = new double[delayList.size()]; + for (int i=0; i initMass) { + throw new SAXException("Propellant weight exceeds total weight in " + + "RockSim engine format"); + } + + // Motor type + str = attributes.get("Type"); + if (str != null && str.equalsIgnoreCase("single-use")) { + type = Motor.Type.SINGLE; + } else if (str != null && str.equalsIgnoreCase("hybrid")) { + type = Motor.Type.HYBRID; + } else if (str != null && str.equalsIgnoreCase("reloadable")) { + type = Motor.Type.RELOAD; + } else { + type = Motor.Type.UNKNOWN; + } + + // Calculate mass + str = attributes.get("auto-calc-mass"); + if ("0".equals(str) || "false".equalsIgnoreCase(str)) { + calculateMass = false; + } else { + calculateMass = true; + } + + // Calculate CG + str = attributes.get("auto-calc-cg"); + if ("0".equals(str) || "false".equalsIgnoreCase(str)) { + calculateCG = false; + } else { + calculateCG = true; + } + } + + @Override + public ElementHandler openElement(String element, + HashMap attributes, WarningSet warnings) throws SAXException { + + if (element.equals("comments")) { + return PlainTextHandler.INSTANCE; + } + + if (element.equals("data")) { + if (dataHandler != null) { + throw new SAXException("Multiple data elements encountered in motor " + + "definition"); + } + dataHandler = new RSEMotorDataHandler(); + return dataHandler; + } + + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return null; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("comments")) { + if (description.length() > 0) { + description = description + "\n\n" + content.trim(); + } else { + description = content.trim(); + } + return; + } + + if (element.equals("data")) { + time = dataHandler.getTime(); + force = dataHandler.getForce(); + mass = dataHandler.getMass(); + cg = dataHandler.getCG(); + + sortLists(time, force, mass, cg); + + for (double d: mass) { + if (Double.isNaN(d)) { + calculateMass = true; + break; + } + } + for (double d: cg) { + if (Double.isNaN(d)) { + calculateCG = true; + break; + } + } + return; + } + } + + public Motor getMotor() throws SAXException { + if (time == null || time.size() == 0) + throw new SAXException("Illegal motor data"); + + + finalizeThrustCurve(time, force, mass, cg); + final int n = time.size(); + + if (hasIllegalValue(mass)) + calculateMass = true; + if (hasIllegalValue(cg)) + calculateCG = true; + + if (calculateMass) { + mass = calculateMass(time, force, initMass, propMass); + } + if (calculateCG) { + for (int i=0; i < n; i++) { + cg.set(i, length/2); + } + } + + double[] timeArray = toArray(time); + double[] thrustArray = toArray(force); + Coordinate[] cgArray = new Coordinate[n]; + for (int i=0; i < n; i++) { + cgArray[i] = new Coordinate(cg.get(i),0,0,mass.get(i)); + } + + + // Create the motor digest from all data available in the file + MotorDigest motorDigest = new MotorDigest(); + motorDigest.update(DataType.TIME_ARRAY, timeArray); + if (!calculateMass) { + motorDigest.update(DataType.MASS_PER_TIME, toArray(mass)); + } else { + motorDigest.update(DataType.MASS_SPECIFIC, initMass, initMass-propMass); + } + if (!calculateCG) { + motorDigest.update(DataType.CG_PER_TIME, toArray(cg)); + } + motorDigest.update(DataType.FORCE_PER_TIME, thrustArray); + final String digest = motorDigest.getDigest(); + + + try { + return new ThrustCurveMotor(Manufacturer.getManufacturer(manufacturer), + designation, description, type, + delays, diameter, length, timeArray, thrustArray, cgArray, digest); + } catch (IllegalArgumentException e) { + throw new SAXException("Illegal motor data", e); + } + } + } + + + /** + * Handler for the element in a RockSim engine file motor definition. + */ + private static class RSEMotorDataHandler extends ElementHandler { + + private final List time = new ArrayList(); + private final List force = new ArrayList(); + private final List mass = new ArrayList(); + private final List cg = new ArrayList(); + + + public List getTime() { + return time; + } + public List getForce() { + return force; + } + public List getMass() { + return mass; + } + public List getCG() { + return cg; + } + + + @Override + public ElementHandler openElement(String element, + HashMap attributes, WarningSet warnings) { + + if (element.equals("eng-data")) { + return NullElementHandler.INSTANCE; + } + + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return null; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + double t = parseDouble(attributes.get("t")); + double f = parseDouble(attributes.get("f")); + double m = parseDouble(attributes.get("m")) / 1000.0; + double g = parseDouble(attributes.get("cg")) / 1000.0; + + if (Double.isNaN(t) || Double.isNaN(f)) { + throw new SAXException("Illegal motor data point encountered"); + } + + time.add(t); + force.add(f); + mass.add(m); + cg.add(g); + } + + + private double parseDouble(String str) { + if (str == null) + return Double.NaN; + try { + return Double.parseDouble(str); + } catch (NumberFormatException e) { + return Double.NaN; + } + } + } + + + + private static boolean hasIllegalValue(List list) { + for (Double d: list) { + if (d == null || d.isNaN() || d.isInfinite()) { + return true; + } + } + return false; + } + + private static double[] toArray(List list) { + final int n = list.size(); + double[] array = new double[n]; + for (int i=0; i < n; i++) { + array[i] = list.get(i); + } + return array; + } +} diff --git a/src/net/sf/openrocket/file/openrocket/BodyComponentSaver.java b/src/net/sf/openrocket/file/openrocket/BodyComponentSaver.java deleted file mode 100644 index 97dc5eb5..00000000 --- a/src/net/sf/openrocket/file/openrocket/BodyComponentSaver.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -public class BodyComponentSaver extends ExternalComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - // Body components have a natural length, store it now - elements.add(""+((net.sf.openrocket.rocketcomponent.BodyComponent)c).getLength()+""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/BodyTubeSaver.java b/src/net/sf/openrocket/file/openrocket/BodyTubeSaver.java deleted file mode 100644 index 865c6c80..00000000 --- a/src/net/sf/openrocket/file/openrocket/BodyTubeSaver.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class BodyTubeSaver extends SymmetricComponentSaver { - - private static final BodyTubeSaver instance = new BodyTubeSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - net.sf.openrocket.rocketcomponent.BodyTube tube = (net.sf.openrocket.rocketcomponent.BodyTube) c; - - if (tube.isRadiusAutomatic()) - elements.add("auto"); - else - elements.add("" + tube.getRadius() + ""); - - if (tube.isMotorMount()) { - elements.addAll(motorMountParams(tube)); - } - } - - -} diff --git a/src/net/sf/openrocket/file/openrocket/BulkheadSaver.java b/src/net/sf/openrocket/file/openrocket/BulkheadSaver.java deleted file mode 100644 index c4e5b6a0..00000000 --- a/src/net/sf/openrocket/file/openrocket/BulkheadSaver.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class BulkheadSaver extends RadiusRingComponentSaver { - - private static final BulkheadSaver instance = new BulkheadSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/CenteringRingSaver.java b/src/net/sf/openrocket/file/openrocket/CenteringRingSaver.java deleted file mode 100644 index 9014fb0a..00000000 --- a/src/net/sf/openrocket/file/openrocket/CenteringRingSaver.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class CenteringRingSaver extends RadiusRingComponentSaver { - - private static final CenteringRingSaver instance = new CenteringRingSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/ComponentAssemblySaver.java b/src/net/sf/openrocket/file/openrocket/ComponentAssemblySaver.java deleted file mode 100644 index 8a73471f..00000000 --- a/src/net/sf/openrocket/file/openrocket/ComponentAssemblySaver.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -public class ComponentAssemblySaver extends RocketComponentSaver { - - // No-op - -} diff --git a/src/net/sf/openrocket/file/openrocket/EllipticalFinSetSaver.java b/src/net/sf/openrocket/file/openrocket/EllipticalFinSetSaver.java deleted file mode 100644 index 8874a7ae..00000000 --- a/src/net/sf/openrocket/file/openrocket/EllipticalFinSetSaver.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class EllipticalFinSetSaver extends FinSetSaver { - - private static final EllipticalFinSetSaver instance = new EllipticalFinSetSaver(); - - public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - ArrayList list = new ArrayList(); - - list.add(""); - instance.addParams(c,list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - net.sf.openrocket.rocketcomponent.EllipticalFinSet fins = (net.sf.openrocket.rocketcomponent.EllipticalFinSet)c; - elements.add(""+fins.getLength()+""); - elements.add(""+fins.getHeight()+""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/EngineBlockSaver.java b/src/net/sf/openrocket/file/openrocket/EngineBlockSaver.java deleted file mode 100644 index 49323225..00000000 --- a/src/net/sf/openrocket/file/openrocket/EngineBlockSaver.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class EngineBlockSaver extends ThicknessRingComponentSaver { - - private static final EngineBlockSaver instance = new EngineBlockSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/ExternalComponentSaver.java b/src/net/sf/openrocket/file/openrocket/ExternalComponentSaver.java deleted file mode 100644 index 7ca8b8f9..00000000 --- a/src/net/sf/openrocket/file/openrocket/ExternalComponentSaver.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.rocketcomponent.ExternalComponent; - - -public class ExternalComponentSaver extends RocketComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - ExternalComponent ext = (ExternalComponent)c; - - // Finish enum names are currently the same except for case - elements.add("" + ext.getFinish().name().toLowerCase() + ""); - - // Material - elements.add(materialParam(ext.getMaterial())); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/FinSetSaver.java b/src/net/sf/openrocket/file/openrocket/FinSetSaver.java deleted file mode 100644 index 756115e4..00000000 --- a/src/net/sf/openrocket/file/openrocket/FinSetSaver.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.util.MathUtil; - -public class FinSetSaver extends ExternalComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - net.sf.openrocket.rocketcomponent.FinSet fins = (net.sf.openrocket.rocketcomponent.FinSet) c; - elements.add("" + fins.getFinCount() + ""); - elements.add("" + (fins.getBaseRotation() * 180.0 / Math.PI) + ""); - elements.add("" + fins.getThickness() + ""); - elements.add("" + fins.getCrossSection().name().toLowerCase() - + ""); - elements.add("" + (fins.getCantAngle() * 180.0 / Math.PI) + ""); - - // Save fin tabs only if they exist (compatibility with file version < 1.1) - if (!MathUtil.equals(fins.getTabHeight(),0) && - !MathUtil.equals(fins.getTabLength(), 0)) { - - elements.add("" + fins.getTabHeight() + ""); - elements.add("" + fins.getTabLength() + ""); - elements.add("" + - fins.getTabShift() + ""); - - } - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/FreeformFinSetSaver.java b/src/net/sf/openrocket/file/openrocket/FreeformFinSetSaver.java deleted file mode 100644 index 6aa17782..00000000 --- a/src/net/sf/openrocket/file/openrocket/FreeformFinSetSaver.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.util.Coordinate; - - -public class FreeformFinSetSaver extends FinSetSaver { - - private static final FreeformFinSetSaver instance = new FreeformFinSetSaver(); - - public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - ArrayList list = new ArrayList(); - - list.add(""); - instance.addParams(c,list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - FreeformFinSet fins = (FreeformFinSet)c; - elements.add(""); - for (Coordinate p: fins.getFinPoints()) { - elements.add(" "); - } - elements.add(""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/InnerTubeSaver.java b/src/net/sf/openrocket/file/openrocket/InnerTubeSaver.java deleted file mode 100644 index f7069177..00000000 --- a/src/net/sf/openrocket/file/openrocket/InnerTubeSaver.java +++ /dev/null @@ -1,43 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.InnerTube; - - -public class InnerTubeSaver extends ThicknessRingComponentSaver { - - private static final InnerTubeSaver instance = new InnerTubeSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - InnerTube tube = (InnerTube) c; - - elements.add("" + tube.getClusterConfiguration().getXMLName() - + ""); - elements.add("" + tube.getClusterScale() + ""); - elements.add("" + (tube.getClusterRotation() * 180.0 / Math.PI) - + ""); - - if (tube.isMotorMount()) { - elements.addAll(motorMountParams(tube)); - } - - - } - - -} diff --git a/src/net/sf/openrocket/file/openrocket/InternalComponentSaver.java b/src/net/sf/openrocket/file/openrocket/InternalComponentSaver.java deleted file mode 100644 index 45c0a563..00000000 --- a/src/net/sf/openrocket/file/openrocket/InternalComponentSaver.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -public class InternalComponentSaver extends RocketComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - // Nothing to save - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/LaunchLugSaver.java b/src/net/sf/openrocket/file/openrocket/LaunchLugSaver.java deleted file mode 100644 index 06d3da59..00000000 --- a/src/net/sf/openrocket/file/openrocket/LaunchLugSaver.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.LaunchLug; - - -public class LaunchLugSaver extends ExternalComponentSaver { - - private static final LaunchLugSaver instance = new LaunchLugSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - LaunchLug lug = (LaunchLug) c; - - elements.add("" + lug.getRadius() + ""); - elements.add("" + lug.getLength() + ""); - elements.add("" + lug.getThickness() + ""); - elements.add("" + (lug.getRadialDirection()*180.0/Math.PI) + ""); - } - - -} diff --git a/src/net/sf/openrocket/file/openrocket/MassComponentSaver.java b/src/net/sf/openrocket/file/openrocket/MassComponentSaver.java deleted file mode 100644 index 11a5ce65..00000000 --- a/src/net/sf/openrocket/file/openrocket/MassComponentSaver.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.MassComponent; - - -public class MassComponentSaver extends MassObjectSaver { - - private static final MassComponentSaver instance = new MassComponentSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - MassComponent mass = (MassComponent) c; - - elements.add("" + mass.getMass() + ""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/MassObjectSaver.java b/src/net/sf/openrocket/file/openrocket/MassObjectSaver.java deleted file mode 100644 index f1ee440c..00000000 --- a/src/net/sf/openrocket/file/openrocket/MassObjectSaver.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.rocketcomponent.MassObject; - - -public class MassObjectSaver extends InternalComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - MassObject mass = (MassObject) c; - - elements.add("" + mass.getLength() + ""); - elements.add("" + mass.getRadius() + ""); - elements.add("" + mass.getRadialPosition() + ""); - elements.add("" + (mass.getRadialDirection() * 180.0 / Math.PI) - + ""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/NoseConeSaver.java b/src/net/sf/openrocket/file/openrocket/NoseConeSaver.java deleted file mode 100644 index 95feafb7..00000000 --- a/src/net/sf/openrocket/file/openrocket/NoseConeSaver.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class NoseConeSaver extends TransitionSaver { - - private static final NoseConeSaver instance = new NoseConeSaver(); - - public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - ArrayList list = new ArrayList(); - - list.add(""); - instance.addParams(c,list); - list.add(""); - - return list; - } - - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - // Transition handles nose cone saving as well - } -} diff --git a/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java new file mode 100644 index 00000000..3b7b23d9 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java @@ -0,0 +1,1978 @@ +package net.sf.openrocket.file.openrocket; + +import java.awt.Color; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.database.Databases; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.document.StorageOptions; +import net.sf.openrocket.document.Simulation.Status; +import net.sf.openrocket.file.RocketLoadException; +import net.sf.openrocket.file.RocketLoader; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.file.simplesax.SimpleSAX; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.BodyComponent; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.Bulkhead; +import net.sf.openrocket.rocketcomponent.CenteringRing; +import net.sf.openrocket.rocketcomponent.ClusterConfiguration; +import net.sf.openrocket.rocketcomponent.Clusterable; +import net.sf.openrocket.rocketcomponent.EllipticalFinSet; +import net.sf.openrocket.rocketcomponent.EngineBlock; +import net.sf.openrocket.rocketcomponent.ExternalComponent; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.IllegalFinPointException; +import net.sf.openrocket.rocketcomponent.InnerTube; +import net.sf.openrocket.rocketcomponent.InternalComponent; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.MassComponent; +import net.sf.openrocket.rocketcomponent.MassObject; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.Parachute; +import net.sf.openrocket.rocketcomponent.RadiusRingComponent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.ReferenceType; +import net.sf.openrocket.rocketcomponent.RingComponent; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.ShockCord; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.Streamer; +import net.sf.openrocket.rocketcomponent.StructuralComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.rocketcomponent.TubeCoupler; +import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; +import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; +import net.sf.openrocket.rocketcomponent.RocketComponent.Position; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.SimulationConditions; +import net.sf.openrocket.simulation.FlightEvent.Type; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.LineStyle; +import net.sf.openrocket.util.Reflection; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + + +/** + * Class that loads a rocket definition from an OpenRocket rocket file. + *

+ * This class uses SAX to read the XML file format. The + * {@link #loadFromStream(InputStream)} method simply sets the system up and + * starts the parsing, while the actual logic is in the private inner class + * OpenRocketHandler. + * + * @author Sampo Niskanen + */ + +public class OpenRocketLoader extends RocketLoader { + + @Override + public OpenRocketDocument loadFromStream(InputStream source) throws RocketLoadException, + IOException { + InputSource xmlSource = new InputSource(source); + OpenRocketHandler handler = new OpenRocketHandler(); + + + try { + SimpleSAX.readXML(xmlSource, handler, warnings); + } catch (SAXException e) { + throw new RocketLoadException("Malformed XML in input.", e); + } + + + OpenRocketDocument doc = handler.getDocument(); + doc.getDefaultConfiguration().setAllStages(); + + // Deduce suitable time skip + double timeSkip = StorageOptions.SIMULATION_DATA_NONE; + for (Simulation s: doc.getSimulations()) { + if (s.getStatus() == Simulation.Status.EXTERNAL || + s.getStatus() == Simulation.Status.NOT_SIMULATED) + continue; + if (s.getSimulatedData() == null) + continue; + if (s.getSimulatedData().getBranchCount() == 0) + continue; + FlightDataBranch branch = s.getSimulatedData().getBranch(0); + if (branch == null) + continue; + List list = branch.get(FlightDataBranch.TYPE_TIME); + if (list == null) + continue; + + double previousTime = Double.NaN; + for (double time: list) { + if (time - previousTime < timeSkip) + timeSkip = time-previousTime; + previousTime = time; + } + } + // Round value + timeSkip = Math.rint(timeSkip*100)/100; + + doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip); + doc.getDefaultStorageOptions().setCompressionEnabled(false); // Set by caller if compressed + doc.getDefaultStorageOptions().setExplicitlySet(false); + + doc.clearUndo(); + return doc; + } + +} + + + +class DocumentConfig { + + /* Remember to update OpenRocketSaver as well! */ + public static final String[] SUPPORTED_VERSIONS = { "0.9", "1.0", "1.1" }; + + + //////// Component constructors + static final HashMap> constructors = new HashMap>(); + static { + try { + // External components + constructors.put("bodytube", BodyTube.class.getConstructor(new Class[0])); + constructors.put("transition", Transition.class.getConstructor(new Class[0])); + constructors.put("nosecone", NoseCone.class.getConstructor(new Class[0])); + constructors.put("trapezoidfinset", TrapezoidFinSet.class.getConstructor(new Class[0])); + constructors.put("ellipticalfinset", EllipticalFinSet.class.getConstructor(new Class[0])); + constructors.put("freeformfinset", FreeformFinSet.class.getConstructor(new Class[0])); + constructors.put("launchlug", LaunchLug.class.getConstructor(new Class[0])); + + // Internal components + constructors.put("engineblock", EngineBlock.class.getConstructor(new Class[0])); + constructors.put("innertube", InnerTube.class.getConstructor(new Class[0])); + constructors.put("tubecoupler", TubeCoupler.class.getConstructor(new Class[0])); + constructors.put("bulkhead", Bulkhead.class.getConstructor(new Class[0])); + constructors.put("centeringring", CenteringRing.class.getConstructor(new Class[0])); + + constructors.put("masscomponent", MassComponent.class.getConstructor(new Class[0])); + constructors.put("shockcord", ShockCord.class.getConstructor(new Class[0])); + constructors.put("parachute", Parachute.class.getConstructor(new Class[0])); + constructors.put("streamer", Streamer.class.getConstructor(new Class[0])); + + // Other + constructors.put("stage", Stage.class.getConstructor(new Class[0])); + + } catch (NoSuchMethodException e) { + throw new RuntimeException( + "Error in constructing the 'constructors' HashMap."); + } + } + + + //////// Parameter setters + /* + * The keys are of the form Class:param, where Class is the class name and param + * the element name. Setters are searched for in descending class order. + * A setter of null means setting the parameter is not allowed. + */ + static final HashMap setters = new HashMap(); + static { + // RocketComponent + setters.put("RocketComponent:name", new StringSetter( + Reflection.findMethodStatic(RocketComponent.class, "setName", String.class))); + setters.put("RocketComponent:color", new ColorSetter( + Reflection.findMethodStatic(RocketComponent.class, "setColor", Color.class))); + setters.put("RocketComponent:linestyle", new EnumSetter( + Reflection.findMethodStatic(RocketComponent.class, "setLineStyle", LineStyle.class), + LineStyle.class)); + setters.put("RocketComponent:position", new PositionSetter()); + setters.put("RocketComponent:overridemass", new OverrideSetter( + Reflection.findMethodStatic(RocketComponent.class, "setOverrideMass", double.class), + Reflection.findMethodStatic(RocketComponent.class, "setMassOverridden", boolean.class))); + setters.put("RocketComponent:overridecg", new OverrideSetter( + Reflection.findMethodStatic(RocketComponent.class, "setOverrideCGX", double.class), + Reflection.findMethodStatic(RocketComponent.class, "setCGOverridden", boolean.class))); + setters.put("RocketComponent:overridesubcomponents", new BooleanSetter( + Reflection.findMethodStatic(RocketComponent.class, "setOverrideSubcomponents", boolean.class))); + setters.put("RocketComponent:comment", new StringSetter( + Reflection.findMethodStatic(RocketComponent.class, "setComment", String.class))); + + // ExternalComponent + setters.put("ExternalComponent:finish", new EnumSetter( + Reflection.findMethodStatic(ExternalComponent.class, "setFinish", Finish.class), + Finish.class)); + setters.put("ExternalComponent:material", new MaterialSetter( + Reflection.findMethodStatic(ExternalComponent.class, "setMaterial", Material.class), + Material.Type.BULK)); + + // BodyComponent + setters.put("BodyComponent:length", new DoubleSetter( + Reflection.findMethodStatic(BodyComponent.class, "setLength", double.class))); + + // SymmetricComponent + setters.put("SymmetricComponent:thickness", new DoubleSetter( + Reflection.findMethodStatic(SymmetricComponent.class,"setThickness", double.class), + "filled", + Reflection.findMethodStatic(SymmetricComponent.class,"setFilled", boolean.class))); + + // BodyTube + setters.put("BodyTube:radius", new DoubleSetter( + Reflection.findMethodStatic(BodyTube.class, "setRadius", double.class), + "auto", + Reflection.findMethodStatic(BodyTube.class,"setRadiusAutomatic", boolean.class))); + + // Transition + setters.put("Transition:shape", new EnumSetter( + Reflection.findMethodStatic(Transition.class, "setType", Transition.Shape.class), + Transition.Shape.class)); + setters.put("Transition:shapeclipped", new BooleanSetter( + Reflection.findMethodStatic(Transition.class, "setClipped", boolean.class))); + setters.put("Transition:shapeparameter", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setShapeParameter", double.class))); + + setters.put("Transition:foreradius", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setForeRadius", double.class), + "auto", + Reflection.findMethodStatic(Transition.class, "setForeRadiusAutomatic", boolean.class))); + setters.put("Transition:aftradius", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setAftRadius", double.class), + "auto", + Reflection.findMethodStatic(Transition.class, "setAftRadiusAutomatic", boolean.class))); + + setters.put("Transition:foreshoulderradius", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setForeShoulderRadius", double.class))); + setters.put("Transition:foreshoulderlength", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setForeShoulderLength", double.class))); + setters.put("Transition:foreshoulderthickness", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setForeShoulderThickness", double.class))); + setters.put("Transition:foreshouldercapped", new BooleanSetter( + Reflection.findMethodStatic(Transition.class, "setForeShoulderCapped", boolean.class))); + + setters.put("Transition:aftshoulderradius", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setAftShoulderRadius", double.class))); + setters.put("Transition:aftshoulderlength", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setAftShoulderLength", double.class))); + setters.put("Transition:aftshoulderthickness", new DoubleSetter( + Reflection.findMethodStatic(Transition.class, "setAftShoulderThickness", double.class))); + setters.put("Transition:aftshouldercapped", new BooleanSetter( + Reflection.findMethodStatic(Transition.class, "setAftShoulderCapped", boolean.class))); + + // NoseCone - disable disallowed elements + setters.put("NoseCone:foreradius", null); + setters.put("NoseCone:foreshoulderradius", null); + setters.put("NoseCone:foreshoulderlength", null); + setters.put("NoseCone:foreshoulderthickness", null); + setters.put("NoseCone:foreshouldercapped", null); + + // FinSet + setters.put("FinSet:fincount", new IntSetter( + Reflection.findMethodStatic(FinSet.class, "setFinCount", int.class))); + setters.put("FinSet:rotation", new DoubleSetter( + Reflection.findMethodStatic(FinSet.class, "setBaseRotation", double.class), Math.PI/180.0)); + setters.put("FinSet:thickness", new DoubleSetter( + Reflection.findMethodStatic(FinSet.class, "setThickness", double.class))); + setters.put("FinSet:crosssection", new EnumSetter( + Reflection.findMethodStatic(FinSet.class, "setCrossSection", FinSet.CrossSection.class), + FinSet.CrossSection.class)); + setters.put("FinSet:cant", new DoubleSetter( + Reflection.findMethodStatic(FinSet.class, "setCantAngle", double.class), Math.PI/180.0)); + setters.put("FinSet:tabheight", new DoubleSetter( + Reflection.findMethodStatic(FinSet.class, "setTabHeight", double.class))); + setters.put("FinSet:tablength", new DoubleSetter( + Reflection.findMethodStatic(FinSet.class, "setTabLength", double.class))); + setters.put("FinSet:tabposition", new FinTabPositionSetter()); + + // TrapezoidFinSet + setters.put("TrapezoidFinSet:rootchord", new DoubleSetter( + Reflection.findMethodStatic(TrapezoidFinSet.class, "setRootChord", double.class))); + setters.put("TrapezoidFinSet:tipchord", new DoubleSetter( + Reflection.findMethodStatic(TrapezoidFinSet.class, "setTipChord", double.class))); + setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter( + Reflection.findMethodStatic(TrapezoidFinSet.class, "setSweep", double.class))); + setters.put("TrapezoidFinSet:height", new DoubleSetter( + Reflection.findMethodStatic(TrapezoidFinSet.class, "setHeight", double.class))); + + // EllipticalFinSet + setters.put("EllipticalFinSet:rootchord", new DoubleSetter( + Reflection.findMethodStatic(EllipticalFinSet.class, "setLength", double.class))); + setters.put("EllipticalFinSet:height", new DoubleSetter( + Reflection.findMethodStatic(EllipticalFinSet.class, "setHeight", double.class))); + + // FreeformFinSet points handled as a special handler + + // LaunchLug + setters.put("LaunchLug:radius", new DoubleSetter( + Reflection.findMethodStatic(LaunchLug.class, "setRadius", double.class))); + setters.put("LaunchLug:length", new DoubleSetter( + Reflection.findMethodStatic(LaunchLug.class, "setLength", double.class))); + setters.put("LaunchLug:thickness", new DoubleSetter( + Reflection.findMethodStatic(LaunchLug.class, "setThickness", double.class))); + setters.put("LaunchLug:radialdirection", new DoubleSetter( + Reflection.findMethodStatic(LaunchLug.class, "setRadialDirection", double.class), + Math.PI/180.0)); + + // InternalComponent - nothing + + // StructuralComponent + setters.put("StructuralComponent:material", new MaterialSetter( + Reflection.findMethodStatic(StructuralComponent.class, "setMaterial", Material.class), + Material.Type.BULK)); + + // RingComponent + setters.put("RingComponent:length", new DoubleSetter( + Reflection.findMethodStatic(RingComponent.class, "setLength", double.class))); + setters.put("RingComponent:radialposition", new DoubleSetter( + Reflection.findMethodStatic(RingComponent.class, "setRadialPosition", double.class))); + setters.put("RingComponent:radialdirection", new DoubleSetter( + Reflection.findMethodStatic(RingComponent.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // ThicknessRingComponent - radius on separate components due to differing automatics + setters.put("ThicknessRingComponent:thickness", new DoubleSetter( + Reflection.findMethodStatic(ThicknessRingComponent.class, "setThickness", double.class))); + + // EngineBlock + setters.put("EngineBlock:outerradius", new DoubleSetter( + Reflection.findMethodStatic(EngineBlock.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethodStatic(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class))); + + // TubeCoupler + setters.put("TubeCoupler:outerradius", new DoubleSetter( + Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class))); + + // InnerTube + setters.put("InnerTube:outerradius", new DoubleSetter( + Reflection.findMethodStatic(InnerTube.class, "setOuterRadius", double.class))); + setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter()); + setters.put("InnerTube:clusterscale", new DoubleSetter( + Reflection.findMethodStatic(InnerTube.class, "setClusterScale", double.class))); + setters.put("InnerTube:clusterrotation", new DoubleSetter( + Reflection.findMethodStatic(InnerTube.class, "setClusterRotation", double.class), + Math.PI / 180.0)); + + // RadiusRingComponent + + // Bulkhead + setters.put("RadiusRingComponent:innerradius", new DoubleSetter( + Reflection.findMethodStatic(RadiusRingComponent.class, "setInnerRadius", double.class))); + setters.put("Bulkhead:outerradius", new DoubleSetter( + Reflection.findMethodStatic(Bulkhead.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethodStatic(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class))); + + // CenteringRing + setters.put("CenteringRing:innerradius", new DoubleSetter( + Reflection.findMethodStatic(CenteringRing.class, "setInnerRadius", double.class), + "auto", + Reflection.findMethodStatic(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class))); + setters.put("CenteringRing:outerradius", new DoubleSetter( + Reflection.findMethodStatic(CenteringRing.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethodStatic(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class))); + + + // MassObject + setters.put("MassObject:packedlength", new DoubleSetter( + Reflection.findMethodStatic(MassObject.class, "setLength", double.class))); + setters.put("MassObject:packedradius", new DoubleSetter( + Reflection.findMethodStatic(MassObject.class, "setRadius", double.class))); + setters.put("MassObject:radialposition", new DoubleSetter( + Reflection.findMethodStatic(MassObject.class, "setRadialPosition", double.class))); + setters.put("MassObject:radialdirection", new DoubleSetter( + Reflection.findMethodStatic(MassObject.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // MassComponent + setters.put("MassComponent:mass", new DoubleSetter( + Reflection.findMethodStatic(MassComponent.class, "setComponentMass", double.class))); + + // ShockCord + setters.put("ShockCord:cordlength", new DoubleSetter( + Reflection.findMethodStatic(ShockCord.class, "setCordLength", double.class))); + setters.put("ShockCord:material", new MaterialSetter( + Reflection.findMethodStatic(ShockCord.class, "setMaterial", Material.class), + Material.Type.LINE)); + + // RecoveryDevice + setters.put("RecoveryDevice:cd", new DoubleSetter( + Reflection.findMethodStatic(RecoveryDevice.class, "setCD", double.class), + "auto", + Reflection.findMethodStatic(RecoveryDevice.class, "setCDAutomatic", boolean.class))); + setters.put("RecoveryDevice:deployevent", new EnumSetter( + Reflection.findMethodStatic(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class), + RecoveryDevice.DeployEvent.class)); + setters.put("RecoveryDevice:deployaltitude", new DoubleSetter( + Reflection.findMethodStatic(RecoveryDevice.class, "setDeployAltitude", double.class))); + setters.put("RecoveryDevice:deploydelay", new DoubleSetter( + Reflection.findMethodStatic(RecoveryDevice.class, "setDeployDelay", double.class))); + setters.put("RecoveryDevice:material", new MaterialSetter( + Reflection.findMethodStatic(RecoveryDevice.class, "setMaterial", Material.class), + Material.Type.SURFACE)); + + // Parachute + setters.put("Parachute:diameter", new DoubleSetter( + Reflection.findMethodStatic(Parachute.class, "setDiameter", double.class))); + setters.put("Parachute:linecount", new IntSetter( + Reflection.findMethodStatic(Parachute.class, "setLineCount", int.class))); + setters.put("Parachute:linelength", new DoubleSetter( + Reflection.findMethodStatic(Parachute.class, "setLineLength", double.class))); + setters.put("Parachute:linematerial", new MaterialSetter( + Reflection.findMethodStatic(Parachute.class, "setLineMaterial", Material.class), + Material.Type.LINE)); + + // Streamer + setters.put("Streamer:striplength", new DoubleSetter( + Reflection.findMethodStatic(Streamer.class, "setStripLength", double.class))); + setters.put("Streamer:stripwidth", new DoubleSetter( + Reflection.findMethodStatic(Streamer.class, "setStripWidth", double.class))); + + // Rocket + // handled by separate handler + setters.put("Rocket:referencetype", new EnumSetter( + Reflection.findMethodStatic(Rocket.class, "setReferenceType", ReferenceType.class), + ReferenceType.class)); + setters.put("Rocket:customreference", new DoubleSetter( + Reflection.findMethodStatic(Rocket.class, "setCustomReferenceLength", double.class))); + setters.put("Rocket:designer", new StringSetter( + Reflection.findMethodStatic(Rocket.class, "setDesigner", String.class))); + setters.put("Rocket:revision", new StringSetter( + Reflection.findMethodStatic(Rocket.class, "setRevision", String.class))); + } + + + /** + * Search for a enum value that has the corresponding name as an XML value. The current + * conversion from enum name to XML value is to lowercase the name and strip out all + * underscore characters. This method returns a match to these criteria, or null + * if no such enum exists. + * + * @param then enum type. + * @param name the XML value, null ok. + * @param enumClass the class of the enum. + * @return the found enum value, or null. + */ + public static > Enum findEnum(String name, + Class> enumClass) { + + if (name == null) + return null; + name = name.trim(); + for (Enum e: enumClass.getEnumConstants()) { + if (e.name().toLowerCase().replace("_", "").equals(name)) { + return e; + } + } + return null; + } + + + /** + * Convert a string to a double including formatting specifications of the OpenRocket + * file format. This accepts all formatting that is valid for + * Double.parseDouble(s) and a few others as well ("Inf", "-Inf"). + * + * @param s the string to parse. + * @return the numerical value. + * @throws NumberFormatException the the string cannot be parsed. + */ + public static double stringToDouble(String s) throws NumberFormatException { + if (s == null) + throw new NumberFormatException("null string"); + if (s.equalsIgnoreCase("NaN")) + return Double.NaN; + if (s.equalsIgnoreCase("Inf")) + return Double.POSITIVE_INFINITY; + if (s.equalsIgnoreCase("-Inf")) + return Double.NEGATIVE_INFINITY; + return Double.parseDouble(s); + } +} + + + + + +/** + * The starting point of the handlers. Accepts a single element and hands + * the contents to be read by a OpenRocketContentsHandler. + */ +class OpenRocketHandler extends ElementHandler { + private OpenRocketContentHandler handler = null; + + /** + * Return the OpenRocketDocument read from the file, or null if a document + * has not been read yet. + * + * @return the document read, or null. + */ + public OpenRocketDocument getDocument() { + return handler.getDocument(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Check for unknown elements + if (!element.equals("openrocket")) { + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + return null; + } + + // Check for first call + if (handler != null) { + warnings.add(Warning.fromString("Multiple document elements found, ignoring later " + + "ones.")); + return null; + } + + // Check version number + String version = null; + String creator = attributes.remove("creator"); + String docVersion = attributes.remove("version"); + for (String v : DocumentConfig.SUPPORTED_VERSIONS) { + if (v.equals(docVersion)) { + version = v; + break; + } + } + if (version == null) { + String str = "Unsupported document version"; + if (docVersion != null) + str += " " + docVersion; + if (creator != null && !creator.trim().equals("")) + str += " (written using '" + creator.trim() + "')"; + str += ", attempting to read file anyway."; + warnings.add(str); + } + + handler = new OpenRocketContentHandler(); + return handler; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + attributes.remove("version"); + attributes.remove("creator"); + super.closeElement(element, attributes, content, warnings); + } + + +} + + +/** + * Handles the content of the tag. + */ +class OpenRocketContentHandler extends ElementHandler { + private final OpenRocketDocument doc; + private final Rocket rocket; + + private boolean rocketDefined = false; + private boolean simulationsDefined = false; + + public OpenRocketContentHandler() { + this.rocket = new Rocket(); + this.doc = new OpenRocketDocument(rocket); + } + + + public OpenRocketDocument getDocument() { + if (!rocketDefined) + return null; + return doc; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("rocket")) { + if (rocketDefined) { + warnings.add(Warning + .fromString("Multiple rocket designs within one document, " + + "ignoring later ones.")); + return null; + } + rocketDefined = true; + return new ComponentParameterHandler(rocket); + } + + if (element.equals("simulations")) { + if (simulationsDefined) { + warnings.add(Warning + .fromString("Multiple simulation definitions within one document, " + + "ignoring later ones.")); + return null; + } + simulationsDefined = true; + return new SimulationsHandler(doc); + } + + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + + return null; + } +} + + + + +/** + * A handler that creates components from the corresponding elements. The control of the + * contents is passed on to ComponentParameterHandler. + */ +class ComponentHandler extends ElementHandler { + private final RocketComponent parent; + + public ComponentHandler(RocketComponent parent) { + this.parent = parent; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Attempt to construct new component + Constructor constructor = DocumentConfig.constructors + .get(element); + if (constructor == null) { + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + return null; + } + + RocketComponent c; + try { + c = constructor.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException("Error constructing component.", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Error constructing component.", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Error constructing component.", e); + } + + parent.addChild(c); + + return new ComponentParameterHandler(c); + } +} + + +/** + * A handler that populates the parameters of a previously constructed rocket component. + * This uses the setters, or delegates the handling to another handler for specific + * elements. + */ +class ComponentParameterHandler extends ElementHandler { + private final RocketComponent component; + + public ComponentParameterHandler(RocketComponent c) { + this.component = c; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Check for specific elements that contain other elements + if (element.equals("subcomponents")) { + return new ComponentHandler(component); + } + if (element.equals("motormount")) { + if (!(component instanceof MotorMount)) { + warnings.add(Warning.fromString("Illegal component defined as motor mount.")); + return null; + } + return new MotorMountHandler((MotorMount)component); + } + if (element.equals("finpoints")) { + if (!(component instanceof FreeformFinSet)) { + warnings.add(Warning.fromString("Illegal component defined for fin points.")); + return null; + } + return new FinSetPointHandler((FreeformFinSet)component); + } + if (element.equals("motorconfiguration")) { + if (!(component instanceof Rocket)) { + warnings.add(Warning.fromString("Illegal component defined for motor configuration.")); + return null; + } + return new MotorConfigurationHandler((Rocket)component); + } + + + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("subcomponents") || element.equals("motormount") || + element.equals("finpoints") || element.equals("motorconfiguration")) { + return; + } + + // Search for the correct setter class + + Class c; + for (c = component.getClass(); c != null; c = c.getSuperclass()) { + String setterKey = c.getSimpleName() + ":" + element; + Setter s = DocumentConfig.setters.get(setterKey); + if (s != null) { + // Setter found + System.out.println("Calling with key "+setterKey); + s.set(component, content, attributes, warnings); + break; + } + if (DocumentConfig.setters.containsKey(setterKey)) { + // Key exists but is null -> invalid parameter + c = null; + break; + } + } + if (c == null) { + warnings.add(Warning.fromString("Unknown parameter type '" + element + "' for " + + component.getComponentName() + ", ignoring.")); + } + } +} + + +/** + * A handler that reads the specifications within the freeformfinset's + * elements. + */ +class FinSetPointHandler extends ElementHandler { + private final FreeformFinSet finset; + private final ArrayList coordinates = new ArrayList(); + + public FinSetPointHandler(FreeformFinSet finset) { + this.finset = finset; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + String strx = attributes.remove("x"); + String stry = attributes.remove("y"); + if (strx == null || stry == null) { + warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); + return; + } + try { + double x = Double.parseDouble(strx); + double y = Double.parseDouble(stry); + coordinates.add(new Coordinate(x,y)); + } catch (NumberFormatException e) { + warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); + return; + } + + super.closeElement(element, attributes, content, warnings); + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + try { + finset.setPoints(coordinates.toArray(new Coordinate[0])); + } catch (IllegalFinPointException e) { + warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring.")); + } + } +} + + +class MotorMountHandler extends ElementHandler { + private final MotorMount mount; + private MotorHandler motorHandler; + + public MotorMountHandler(MotorMount mount) { + this.mount = mount; + mount.setMotorMount(true); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("motor")) { + motorHandler = new MotorHandler(); + return motorHandler; + } + + if (element.equals("ignitionevent") || + element.equals("ignitiondelay") || + element.equals("overhang")) { + return PlainTextHandler.INSTANCE; + } + + warnings.add(Warning.fromString("Unknown element '"+element+"' encountered, ignoring.")); + return null; + } + + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + if (element.equals("motor")) { + String id = attributes.get("configid"); + if (id == null || id.equals("")) { + warnings.add(Warning.fromString("Illegal motor specification, ignoring.")); + return; + } + + Motor motor = motorHandler.getMotor(warnings); + mount.setMotor(id, motor); + mount.setMotorDelay(id, motorHandler.getDelay(warnings)); + return; + } + + if (element.equals("ignitionevent")) { + MotorMount.IgnitionEvent event = null; + for (MotorMount.IgnitionEvent e : MotorMount.IgnitionEvent.values()) { + if (e.name().toLowerCase().replaceAll("_", "").equals(content)) { + event = e; + break; + } + } + if (event == null) { + warnings.add(Warning.fromString("Unknown ignition event type '"+content+"', ignoring.")); + return; + } + mount.setIgnitionEvent(event); + return; + } + + if (element.equals("ignitiondelay")) { + double d; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); + return; + } + mount.setIgnitionDelay(d); + return; + } + + if (element.equals("overhang")) { + double d; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal overhang specified, ignoring.")); + return; + } + mount.setMotorOverhang(d); + return; + } + + super.closeElement(element, attributes, content, warnings); + } +} + + + + +class MotorConfigurationHandler extends ElementHandler { + private final Rocket rocket; + private String name = null; + private boolean inNameElement = false; + + public MotorConfigurationHandler(Rocket rocket) { + this.rocket = rocket; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (inNameElement || !element.equals("name")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return null; + } + inNameElement = true; + + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + name = content; + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + String configid = attributes.remove("configid"); + if (configid == null || configid.equals("")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (!rocket.addMotorConfigurationID(configid)) { + warnings.add("Duplicate motor configuration ID used."); + return; + } + + if (name != null && name.trim().length() > 0) { + rocket.setMotorConfigurationName(configid, name); + } + + if ("true".equals(attributes.remove("default"))) { + rocket.getDefaultConfiguration().setMotorConfigurationID(configid); + } + + super.closeElement(element, attributes, content, warnings); + } +} + + +class MotorHandler extends ElementHandler { + private Motor.Type type = null; + private String manufacturer = null; + private String designation = null; + private double diameter = Double.NaN; + private double length = Double.NaN; + private double delay = Double.NaN; + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + + /** + * Return the motor to use, or null. + */ + public Motor getMotor(WarningSet warnings) { + if (designation == null) { + warnings.add(Warning.fromString("No motor specified, ignoring.")); + return null; + } + Motor[] motors = Databases.findMotors(type, manufacturer, designation, diameter, length); + if (motors.length == 0) { + String str = "No motor with designation '"+designation+"'"; + if (manufacturer != null) + str += " for manufacturer '" + manufacturer + "'"; + warnings.add(Warning.fromString(str + " found.")); + return null; + } + if (motors.length > 1) { + String str = "Multiple motors with designation '"+designation+"'"; + if (manufacturer != null) + str += " for manufacturer '" + manufacturer + "'"; + warnings.add(Warning.fromString(str + " found, one chosen arbitrarily.")); + } + return motors[0]; + } + + + /** + * Return the delay to use for the motor. + */ + public double getDelay(WarningSet warnings) { + if (Double.isNaN(delay)) { + warnings.add(Warning.fromString("Motor delay not specified, assuming no ejection charge.")); + return Motor.PLUGGED; + } + return delay; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + content = content.trim(); + + if (element.equals("type")) { + + // Motor type + type = null; + for (Motor.Type t: Motor.Type.values()) { + if (t.name().toLowerCase().equals(content)) { + type = t; + break; + } + } + if (type == null) { + warnings.add(Warning.fromString("Unknown motor type '"+content+"', ignoring.")); + } + + } else if (element.equals("manufacturer")) { + + // Manufacturer + manufacturer = content; + + } else if (element.equals("designation")) { + + // Designation + designation = content; + + } else if (element.equals("diameter")) { + + // Diameter + diameter = Double.NaN; + try { + diameter = Double.parseDouble(content); + } catch (NumberFormatException e) { + // Ignore + } + if (Double.isNaN(diameter)) { + warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); + } + + } else if (element.equals("length")) { + + // Length + length = Double.NaN; + try { + length = Double.parseDouble(content); + } catch (NumberFormatException ignore) { } + + if (Double.isNaN(length)) { + warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); + } + + } else if (element.equals("delay")) { + + // Delay + delay = Double.NaN; + if (content.equals("none")) { + delay = Motor.PLUGGED; + } else { + try { + delay = Double.parseDouble(content); + } catch (NumberFormatException ignore) { } + + if (Double.isNaN(delay)) { + warnings.add(Warning.fromString("Illegal motor delay specified, ignoring.")); + } + + } + + } else { + super.closeElement(element, attributes, content, warnings); + } + } + +} + + + +class SimulationsHandler extends ElementHandler { + private final OpenRocketDocument doc; + private SingleSimulationHandler handler; + + public SimulationsHandler(OpenRocketDocument doc) { + this.doc = doc; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (!element.equals("simulation")) { + warnings.add("Unknown element '"+element+"', ignoring."); + return null; + } + + handler = new SingleSimulationHandler(doc); + return handler; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + attributes.remove("status"); + super.closeElement(element, attributes, content, warnings); + } + + +} + +class SingleSimulationHandler extends ElementHandler { + + private final OpenRocketDocument doc; + + private String name; + + private SimulationConditionsHandler conditionHandler; + private FlightDataHandler dataHandler; + + private final List listeners = new ArrayList(); + + public SingleSimulationHandler(OpenRocketDocument doc) { + this.doc = doc; + } + + + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("name") || element.equals("simulator") || + element.equals("calculator") || element.equals("listener")) { + return PlainTextHandler.INSTANCE; + } else if (element.equals("conditions")) { + conditionHandler = new SimulationConditionsHandler(doc.getRocket()); + return conditionHandler; + } else if (element.equals("flightdata")) { + dataHandler = new FlightDataHandler(); + return dataHandler; + } else { + warnings.add("Unknown element '"+element+"', ignoring."); + return null; + } + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("name")) { + name = content; + } else if (element.equals("simulator")) { + if (!content.trim().equals("RK4Simulator")) { + warnings.add("Unknown simulator '" + content.trim() + "' specified, ignoring."); + } + } else if (element.equals("calculator")) { + if (!content.trim().equals("BarrowmanCalculator")) { + warnings.add("Unknown calculator '" + content.trim() + "' specified, ignoring."); + } + } else if (element.equals("listener") && content.trim().length() > 0) { + listeners.add(content.trim()); + } + + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + + String s = attributes.get("status"); + Simulation.Status status = (Status) DocumentConfig.findEnum(s, Simulation.Status.class); + if (status == null) { + warnings.add("Simulation status unknown, assuming outdated."); + status = Simulation.Status.OUTDATED; + } + + SimulationConditions conditions; + if (conditionHandler != null) { + conditions = conditionHandler.getConditions(); + } else { + warnings.add("Simulation conditions not defined, using defaults."); + conditions = new SimulationConditions(doc.getRocket()); + } + + if (name == null) + name = "Simulation"; + + FlightData data; + if (dataHandler == null) + data = null; + else + data = dataHandler.getFlightData(); + + Simulation simulation = new Simulation(doc.getRocket(), status, name, + conditions, listeners, data); + + doc.addSimulation(simulation); + } +} + + + +class SimulationConditionsHandler extends ElementHandler { + private SimulationConditions conditions; + private AtmosphereHandler atmosphereHandler; + + public SimulationConditionsHandler(Rocket rocket) { + conditions = new SimulationConditions(rocket); + } + + public SimulationConditions getConditions() { + return conditions; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + if (element.equals("atmosphere")) { + atmosphereHandler = new AtmosphereHandler(attributes.get("model")); + return atmosphereHandler; + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + double d = Double.NaN; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException ignore) { } + + + if (element.equals("configid")) { + if (content.equals("")) { + conditions.setMotorConfigurationID(null); + } else { + conditions.setMotorConfigurationID(content); + } + } else if (element.equals("launchrodlength")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod length defined, ignoring."); + } else { + conditions.setLaunchRodLength(d); + } + } else if (element.equals("launchrodangle")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod angle defined, ignoring."); + } else { + conditions.setLaunchRodAngle(d*Math.PI/180); + } + } else if (element.equals("launchroddirection")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod direction defined, ignoring."); + } else { + conditions.setLaunchRodDirection(d*Math.PI/180); + } + } else if (element.equals("windaverage")) { + if (Double.isNaN(d)) { + warnings.add("Illegal average windspeed defined, ignoring."); + } else { + conditions.setWindSpeedAverage(d); + } + } else if (element.equals("windturbulence")) { + if (Double.isNaN(d)) { + warnings.add("Illegal wind turbulence intensity defined, ignoring."); + } else { + conditions.setWindTurbulenceIntensity(d); + } + } else if (element.equals("launchaltitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch altitude defined, ignoring."); + } else { + conditions.setLaunchAltitude(d); + } + } else if (element.equals("launchlatitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch latitude defined, ignoring."); + } else { + conditions.setLaunchLatitude(d); + } + } else if (element.equals("atmosphere")) { + atmosphereHandler.storeSettings(conditions, warnings); + } else if (element.equals("timestep")) { + if (Double.isNaN(d)) { + warnings.add("Illegal time step defined, ignoring."); + } else { + conditions.setTimeStep(d); + } + } + } +} + + +class AtmosphereHandler extends ElementHandler { + private final String model; + private double temperature = Double.NaN; + private double pressure = Double.NaN; + + public AtmosphereHandler(String model) { + this.model = model; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + double d = Double.NaN; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException ignore) { } + + if (element.equals("basetemperature")) { + if (Double.isNaN(d)) { + warnings.add("Illegal base temperature specified, ignoring."); + } + temperature = d; + } else if (element.equals("basepressure")) { + if (Double.isNaN(d)) { + warnings.add("Illegal base pressure specified, ignoring."); + } + pressure = d; + } else { + super.closeElement(element, attributes, content, warnings); + } + } + + + public void storeSettings(SimulationConditions cond, WarningSet warnings) { + if (!Double.isNaN(pressure)) { + cond.setLaunchPressure(pressure); + } + if (!Double.isNaN(temperature)) { + cond.setLaunchTemperature(temperature); + } + + if ("isa".equals(model)) { + cond.setISAAtmosphere(true); + } else if ("extendedisa".equals(model)){ + cond.setISAAtmosphere(false); + } else { + cond.setISAAtmosphere(true); + warnings.add("Unknown atmospheric model, using ISA."); + } + } + +} + + +class FlightDataHandler extends ElementHandler { + + private FlightDataBranchHandler dataHandler; + private WarningSet warningSet = new WarningSet(); + private List branches = new ArrayList(); + + private FlightData data; + + public FlightData getFlightData() { + return data; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("warning")) { + return PlainTextHandler.INSTANCE; + } + if (element.equals("databranch")) { + if (attributes.get("name") == null || attributes.get("types")==null) { + warnings.add("Illegal flight data definition, ignoring."); + return null; + } + dataHandler = new FlightDataBranchHandler(attributes.get("name"), + attributes.get("types")); + return dataHandler; + } + + warnings.add("Unknown element '"+element+"' encountered, ignoring."); + return null; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("databranch")) { + FlightDataBranch branch = dataHandler.getBranch(); + if (branch.getLength() > 0) { + branches.add(branch); + } + } else if (element.equals("warning")) { + warningSet.add(Warning.fromString(content)); + } + } + + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (branches.size() > 0) { + data = new FlightData(branches.toArray(new FlightDataBranch[0])); + } else { + double maxAltitude = Double.NaN; + double maxVelocity = Double.NaN; + double maxAcceleration = Double.NaN; + double maxMach = Double.NaN; + double timeToApogee = Double.NaN; + double flightTime = Double.NaN; + double groundHitVelocity = Double.NaN; + + try { + maxAltitude = DocumentConfig.stringToDouble(attributes.get("maxaltitude")); + } catch (NumberFormatException ignore) { } + try { + maxVelocity = DocumentConfig.stringToDouble(attributes.get("maxvelocity")); + } catch (NumberFormatException ignore) { } + try { + maxAcceleration = DocumentConfig.stringToDouble(attributes.get("maxacceleration")); + } catch (NumberFormatException ignore) { } + try { + maxMach = DocumentConfig.stringToDouble(attributes.get("maxmach")); + } catch (NumberFormatException ignore) { } + try { + timeToApogee = DocumentConfig.stringToDouble(attributes.get("timetoapogee")); + } catch (NumberFormatException ignore) { } + try { + flightTime = DocumentConfig.stringToDouble(attributes.get("flighttime")); + } catch (NumberFormatException ignore) { } + try { + groundHitVelocity = + DocumentConfig.stringToDouble(attributes.get("groundhitvelocity")); + } catch (NumberFormatException ignore) { } + + data = new FlightData(maxAltitude, maxVelocity, maxAcceleration, maxMach, + timeToApogee, flightTime, groundHitVelocity); + } + + data.getWarningSet().addAll(warningSet); + } + + +} + + +class FlightDataBranchHandler extends ElementHandler { + private final FlightDataBranch.Type[] types; + private final FlightDataBranch branch; + + public FlightDataBranchHandler(String name, String typeList) { + String[] split = typeList.split(","); + types = new FlightDataBranch.Type[split.length]; + for (int i=0; i < split.length; i++) { + types[i] = FlightDataBranch.getType(split[i], UnitGroup.UNITS_NONE); + } + + // TODO: LOW: May throw an IllegalArgumentException + branch = new FlightDataBranch(name, types); + } + + public FlightDataBranch getBranch() { + branch.immute(); + return branch; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("datapoint")) + return PlainTextHandler.INSTANCE; + if (element.equals("event")) + return PlainTextHandler.INSTANCE; + + warnings.add("Unknown element '"+element+"' encountered, ignoring."); + return null; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("event")) { + double time; + FlightEvent.Type type; + + try { + time = DocumentConfig.stringToDouble(attributes.get("time")); + } catch (NumberFormatException e) { + warnings.add("Illegal event specification, ignoring."); + return; + } + + type = (Type) DocumentConfig.findEnum(attributes.get("type"), FlightEvent.Type.class); + if (type == null) { + warnings.add("Illegal event specification, ignoring."); + return; + } + + branch.addEvent(time, new FlightEvent(type, time)); + return; + } + + if (!element.equals("datapoint")) { + warnings.add("Unknown element '"+element+"' encountered, ignoring."); + return; + } + + // element == "datapoint" + + + // Check line format + String[] split = content.split(","); + if (split.length != types.length) { + warnings.add("Data point did not contain correct amount of values, ignoring point."); + return; + } + + // Parse the doubles + double[] values = new double[split.length]; + for (int i=0; i < values.length; i++) { + try { + values[i] = DocumentConfig.stringToDouble(split[i]); + } catch (NumberFormatException e) { + warnings.add("Data point format error, ignoring point."); + return; + } + } + + // Add point to branch + branch.addPoint(); + for (int i=0; i < types.length; i++) { + branch.setValue(types[i], values[i]); + } + } +} + + + + + + +///////////////// Setters implementation + + +//// Interface +interface Setter { + /** + * Set the specified value to the given component. + * + * @param component the component to which to set. + * @param value the value within the element. + * @param attributes attributes for the element. + * @param warnings the warning set to use. + */ + public void set(RocketComponent component, String value, + HashMap attributes, WarningSet warnings); +} + + +//// StringSetter - sets the value to the contained String +class StringSetter implements Setter { + private final Reflection.Method setMethod; + + public StringSetter(Reflection.Method set) { + setMethod = set; + } + + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + setMethod.invoke(c, s); + } +} + +//// IntSetter - set an integer value +class IntSetter implements Setter { + private final Reflection.Method setMethod; + + public IntSetter(Reflection.Method set) { + setMethod = set; + } + + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + try { + int n = Integer.parseInt(s); + setMethod.invoke(c, n); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} + + +//// BooleanSetter - set a boolean value +class BooleanSetter implements Setter { + private final Reflection.Method setMethod; + + public BooleanSetter(Reflection.Method set) { + setMethod = set; + } + + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + s = s.trim(); + if (s.equalsIgnoreCase("true")) { + setMethod.invoke(c, true); + } else if (s.equalsIgnoreCase("false")) { + setMethod.invoke(c, false); + } else { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} + + + +//// DoubleSetter - sets a double value or (alternatively) if a specific string is encountered +//// calls a setXXX(boolean) method. +class DoubleSetter implements Setter { + private final Reflection.Method setMethod; + private final String specialString; + private final Reflection.Method specialMethod; + private final double multiplier; + + /** + * Set only the double value. + * @param set set method for the double value. + */ + public DoubleSetter(Reflection.Method set) { + this.setMethod = set; + this.specialString = null; + this.specialMethod = null; + this.multiplier = 1.0; + } + + /** + * Multiply with the given multiplier and set the double value. + * @param set set method for the double value. + * @param mul multiplier. + */ + public DoubleSetter(Reflection.Method set, double mul) { + this.setMethod = set; + this.specialString = null; + this.specialMethod = null; + this.multiplier = mul; + } + + /** + * Set the double value, or if the value equals the special string, use the + * special setter and set it to true. + * + * @param set double setter. + * @param special special string + * @param specialMethod boolean setter. + */ + public DoubleSetter(Reflection.Method set, String special, + Reflection.Method specialMethod) { + this.setMethod = set; + this.specialString = special; + this.specialMethod = specialMethod; + this.multiplier = 1.0; + } + + + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + s = s.trim(); + + // Check for special case + if (specialMethod != null && s.equalsIgnoreCase(specialString)) { + specialMethod.invoke(c, true); + return; + } + + // Normal case + try { + double d = Double.parseDouble(s); + setMethod.invoke(c, d * multiplier); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} + + +class OverrideSetter implements Setter { + private final Reflection.Method setMethod; + private final Reflection.Method enabledMethod; + + public OverrideSetter(Reflection.Method set, Reflection.Method enabledMethod) { + this.setMethod = set; + this.enabledMethod = enabledMethod; + } + + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + try { + double d = Double.parseDouble(s); + setMethod.invoke(c, d); + enabledMethod.invoke(c, true); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} + +//// EnumSetter - sets a generic enum type +class EnumSetter> implements Setter { + private final Reflection.Method setter; + private final Class enumClass; + + public EnumSetter(Reflection.Method set, Class enumClass) { + this.setter = set; + this.enumClass = enumClass; + } + + @Override + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + + Enum setEnum = DocumentConfig.findEnum(name, enumClass); + if (setEnum == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + setter.invoke(c, setEnum); + } +} + + +//// ColorSetter - sets a Color value +class ColorSetter implements Setter { + private final Reflection.Method setMethod; + + public ColorSetter(Reflection.Method set) { + setMethod = set; + } + + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + String red = attributes.get("red"); + String green = attributes.get("green"); + String blue = attributes.get("blue"); + + if (red == null || green == null || blue == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + int r, g, b; + try { + r = Integer.parseInt(red); + g = Integer.parseInt(green); + b = Integer.parseInt(blue); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + Color color = new Color(r, g, b); + setMethod.invoke(c, color); + + if (!s.trim().equals("")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} + + + +class MaterialSetter implements Setter { + private final Reflection.Method setMethod; + private final Material.Type type; + + public MaterialSetter(Reflection.Method set, Material.Type type) { + this.setMethod = set; + this.type = type; + } + + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + + Material mat; + + // Check name != "" + name = name.trim(); + if (name.equals("")) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + + // Parse density + double density; + String str; + str = attributes.remove("density"); + if (str == null) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + try { + density = Double.parseDouble(str); + } catch (NumberFormatException e) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + + // Parse thickness +// double thickness = 0; +// str = attributes.remove("thickness"); +// try { +// if (str != null) +// thickness = Double.parseDouble(str); +// } catch (NumberFormatException e){ +// warnings.add(Warning.fromString("Illegal material specification, ignoring.")); +// return; +// } + + // Check type if specified + str = attributes.remove("type"); + if (str != null && !type.name().toLowerCase().equals(str)) { + warnings.add(Warning.fromString("Illegal material type specified, ignoring.")); + return; + } + + mat = Databases.findMaterial(type, name, density, false); + + setMethod.invoke(c, mat); + } +} + + + + +class PositionSetter implements Setter { + + public void set(RocketComponent c, String value, HashMap attributes, + WarningSet warnings) { + + RocketComponent.Position type = (Position) DocumentConfig.findEnum(attributes.get("type"), + RocketComponent.Position.class); + if (type == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + double pos; + try { + pos = Double.parseDouble(value); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (c instanceof FinSet) { + ((FinSet)c).setRelativePosition(type); + c.setPositionValue(pos); + } else if (c instanceof LaunchLug) { + ((LaunchLug)c).setRelativePosition(type); + c.setPositionValue(pos); + } else if (c instanceof InternalComponent) { + ((InternalComponent)c).setRelativePosition(type); + c.setPositionValue(pos); + } else { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + + } +} + + +class FinTabPositionSetter extends DoubleSetter { + + public FinTabPositionSetter() { + super(Reflection.findMethodStatic(FinSet.class, "setTabShift", double.class)); + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + if (!(c instanceof FinSet)) { + throw new IllegalStateException("FinTabPositionSetter called for component " + c); + } + + String relative = attributes.get("relativeto"); + FinSet.TabRelativePosition position = + (TabRelativePosition) DocumentConfig.findEnum(relative, + FinSet.TabRelativePosition.class); + + if (position != null) { + + ((FinSet)c).setTabRelativePosition(position); + + } else { + if (relative == null) { + warnings.add("Required attribute 'relativeto' not found for fin tab position."); + } else { + warnings.add("Illegal attribute value '" + relative + "' encountered."); + } + } + + super.set(c, s, attributes, warnings); + } + + +} + + +class ClusterConfigurationSetter implements Setter { + + public void set(RocketComponent component, String value, HashMap attributes, + WarningSet warnings) { + + if (!(component instanceof Clusterable)) { + warnings.add("Illegal component defined as cluster."); + return; + } + + ClusterConfiguration config = null; + for (ClusterConfiguration c: ClusterConfiguration.CONFIGURATIONS) { + if (c.getXMLName().equals(value)) { + config = c; + break; + } + } + + if (config == null) { + warnings.add("Illegal cluster configuration specified."); + return; + } + + ((Clusterable)component).setClusterConfiguration(config); + } +} + + diff --git a/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java b/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java new file mode 100644 index 00000000..6a13069b --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java @@ -0,0 +1,532 @@ +package net.sf.openrocket.file.openrocket; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.zip.GZIPOutputStream; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.document.StorageOptions; +import net.sf.openrocket.file.RocketSaver; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.TubeCoupler; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.SimulationConditions; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.Pair; +import net.sf.openrocket.util.Prefs; +import net.sf.openrocket.util.Reflection; +import net.sf.openrocket.util.TextUtil; + +public class OpenRocketSaver extends RocketSaver { + + /** + * Divisor used in converting an integer version to the point-represented version. + * The integer version divided by this value is the major version and the remainder is + * the minor version. For example 101 corresponds to file version "1.1". + */ + public static final int FILE_VERSION_DIVISOR = 100; + + + private static final String OPENROCKET_CHARSET = "UTF-8"; + + private static final String METHOD_PACKAGE = "net.sf.openrocket.file.openrocket.savers"; + private static final String METHOD_SUFFIX = "Saver"; + + + // Estimated storage used by different portions + // These have been hand-estimated from saved files + private static final int BYTES_PER_COMPONENT_UNCOMPRESSED = 590; + private static final int BYTES_PER_COMPONENT_COMPRESSED = 80; + private static final int BYTES_PER_SIMULATION_UNCOMPRESSED = 1000; + private static final int BYTES_PER_SIMULATION_COMPRESSED = 100; + private static final int BYTES_PER_DATAPOINT_UNCOMPRESSED = 350; + private static final int BYTES_PER_DATAPOINT_COMPRESSED = 100; + + + private int indent; + private Writer dest; + + @Override + public void save(OutputStream output, OpenRocketDocument document, StorageOptions options) + throws IOException { + + if (options.isCompressionEnabled()) { + output = new GZIPOutputStream(output); + } + + dest = new BufferedWriter(new OutputStreamWriter(output, OPENROCKET_CHARSET)); + + final int fileVersion = calculateNecessaryFileVersion(document, options); + final String fileVersionString = + (fileVersion / FILE_VERSION_DIVISOR) + "." + (fileVersion % FILE_VERSION_DIVISOR); + + + this.indent = 0; + + System.out.println("Writing..."); + + writeln(""); + writeln(""); + indent++; + + // Recursively save the rocket structure + saveComponent(document.getRocket()); + + writeln(""); + + // Save all simulations + writeln(""); + indent++; + boolean first = true; + for (Simulation s: document.getSimulations()) { + if (!first) + writeln(""); + first = false; + saveSimulation(s, options.getSimulationTimeSkip()); + } + indent--; + writeln(""); + + indent--; + writeln(""); + + dest.flush(); + if (options.isCompressionEnabled()) { + ((GZIPOutputStream)output).finish(); + } + } + + + + @Override + public long estimateFileSize(OpenRocketDocument doc, StorageOptions options) { + + long size = 0; + + // Size per component + int componentCount = 0; + Rocket rocket = doc.getRocket(); + Iterator iterator = rocket.deepIterator(true); + while (iterator.hasNext()) { + iterator.next(); + componentCount++; + } + + if (options.isCompressionEnabled()) + size += componentCount * BYTES_PER_COMPONENT_COMPRESSED; + else + size += componentCount * BYTES_PER_COMPONENT_UNCOMPRESSED; + + + // Size per simulation + if (options.isCompressionEnabled()) + size += doc.getSimulationCount() * BYTES_PER_SIMULATION_COMPRESSED; + else + size += doc.getSimulationCount() * BYTES_PER_SIMULATION_UNCOMPRESSED; + + + // Size per flight data point + int pointCount = 0; + double timeSkip = options.getSimulationTimeSkip(); + if (timeSkip != StorageOptions.SIMULATION_DATA_NONE) { + for (Simulation s: doc.getSimulations()) { + FlightData data = s.getSimulatedData(); + if (data != null) { + for (int i=0; i < data.getBranchCount(); i++) { + pointCount += countFlightDataBranchPoints(data.getBranch(i), timeSkip); + } + } + } + } + + if (options.isCompressionEnabled()) + size += pointCount * BYTES_PER_DATAPOINT_COMPRESSED; + else + size += pointCount * BYTES_PER_DATAPOINT_UNCOMPRESSED; + + return size; + } + + + /** + * Determine which file version is required in order to store all the features of the + * current design. By default the oldest version that supports all the necessary features + * will be used. + * + * @param document the document to output. + * @param opts the storage options. + * @return the integer file version to use. + */ + private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) { + /* + * File version 1.1 is required for: + * - fin tabs + * - components attached to tube coupler + * + * Otherwise use version 1.0. + */ + + // Check for fin tabs (version 1.1) + Iterator iterator = document.getRocket().deepIterator(); + while (iterator.hasNext()) { + RocketComponent c = iterator.next(); + + // Check for fin tabs + if (c instanceof FinSet) { + FinSet fin = (FinSet)c; + if (!MathUtil.equals(fin.getTabHeight(),0) && + !MathUtil.equals(fin.getTabLength(), 0)) { + return FILE_VERSION_DIVISOR + 1; + } + } + + // Check for components attached to tube coupler + if (c instanceof TubeCoupler) { + if (c.getChildCount() > 0) { + return FILE_VERSION_DIVISOR + 1; + } + } + } + + // Default (version 1.0) + return FILE_VERSION_DIVISOR + 0; + } + + + + @SuppressWarnings("unchecked") + private void saveComponent(RocketComponent component) throws IOException { + + Reflection.Method m = Reflection.findMethod(METHOD_PACKAGE, component, METHOD_SUFFIX, + "getElements", RocketComponent.class); + if (m==null) { + throw new RuntimeException("Unable to find saving class for component "+ + component.getComponentName()); + } + + // Get the strings to save + List list = (List) m.invokeStatic(component); + int length = list.size(); + + if (length == 0) // Nothing to do + return; + + if (length < 2) { + throw new RuntimeException("BUG, component data length less than two lines."); + } + + // Open element + writeln(list.get(0)); + indent++; + + // Write parameters + for (int i=1; i 0) { + writeln(""); + writeln(""); + indent++; + boolean emptyline = false; + for (RocketComponent subcomponent: component) { + if (emptyline) + writeln(""); + emptyline = true; + saveComponent(subcomponent); + } + indent--; + writeln(""); + } + + // Close element + indent--; + writeln(list.get(length-1)); + } + + + + private void saveSimulation(Simulation simulation, double timeSkip) throws IOException { + SimulationConditions cond = simulation.getConditions(); + + writeln(""); + indent++; + + writeln("" + escapeXML(simulation.getName()) + ""); + // TODO: MEDIUM: Other simulators/calculators + writeln("RK4Simulator"); + writeln("BarrowmanCalculator"); + writeln(""); + indent++; + + writeElement("configid", cond.getMotorConfigurationID()); + writeElement("launchrodlength", cond.getLaunchRodLength()); + writeElement("launchrodangle", cond.getLaunchRodAngle() * 180.0/Math.PI); + writeElement("launchroddirection", cond.getLaunchRodDirection() * 180.0/Math.PI); + writeElement("windaverage", cond.getWindSpeedAverage()); + writeElement("windturbulence", cond.getWindTurbulenceIntensity()); + writeElement("launchaltitude", cond.getLaunchAltitude()); + writeElement("launchlatitude", cond.getLaunchLatitude()); + + if (cond.isISAAtmosphere()) { + writeln(""); + } else { + writeln(""); + indent++; + writeElement("basetemperature", cond.getLaunchTemperature()); + writeElement("basepressure", cond.getLaunchPressure()); + indent--; + writeln(""); + } + + writeElement("timestep", cond.getTimeStep()); + + indent--; + writeln(""); + + + for (String s: simulation.getSimulationListeners()) { + writeElement("listener", escapeXML(s)); + } + + + // Write basic simulation data + + FlightData data = simulation.getSimulatedData(); + if (data != null) { + String str = ""); + } + + indent--; + writeln(""); + + } + + + + private void saveFlightDataBranch(FlightDataBranch branch, double timeSkip) + throws IOException { + double previousTime = -100000; + + if (branch == null) + return; + + // Retrieve the types from the branch + FlightDataBranch.Type[] types = branch.getTypes(); + + if (types.length == 0) + return; + + // Retrieve the data from the branch + List> data = new ArrayList>(types.length); + for (int i=0; i timeData = branch.get(FlightDataBranch.TYPE_TIME); + if (timeData == null) { + // TODO: MEDIUM: External data may not have time data + throw new IllegalArgumentException("Data did not contain time data"); + } + + // Build the tag + StringBuilder sb = new StringBuilder(); + sb.append(" 0) + sb.append(","); + sb.append(escapeXML(types[i].getName())); + } + sb.append("\">"); + writeln(sb.toString()); + indent++; + + // Write events + for (Pair p: branch.getEvents()) { + writeln(""); + } + + // Write the data + int length = branch.getLength(); + if (length > 0) { + writeDataPointString(data, 0, sb); + previousTime = timeData.get(0); + } + + for (int i=1; i < length-1; i++) { + if (Math.abs(timeData.get(i) - previousTime - timeSkip) < + Math.abs(timeData.get(i+1) - previousTime - timeSkip)) { + writeDataPointString(data, i, sb); + previousTime = timeData.get(i); + } + } + + if (length > 1) { + writeDataPointString(data, length-1, sb); + } + + indent--; + writeln(""); + } + + + + /* TODO: LOW: This is largely duplicated from above! */ + private int countFlightDataBranchPoints(FlightDataBranch branch, double timeSkip) { + int count = 0; + + double previousTime = -100000; + + if (branch == null) + return 0; + + // Retrieve the types from the branch + FlightDataBranch.Type[] types = branch.getTypes(); + + if (types.length == 0) + return 0; + + List timeData = branch.get(FlightDataBranch.TYPE_TIME); + if (timeData == null) { + // TODO: MEDIUM: External data may not have time data + throw new IllegalArgumentException("Data did not contain time data"); + } + + // Write the data + int length = branch.getLength(); + if (length > 0) { + count++; + previousTime = timeData.get(0); + } + + for (int i=1; i < length-1; i++) { + if (Math.abs(timeData.get(i) - previousTime - timeSkip) < + Math.abs(timeData.get(i+1) - previousTime - timeSkip)) { + count++; + previousTime = timeData.get(i); + } + } + + if (length > 1) { + count++; + } + + return count; + } + + + + private void writeDataPointString(List> data, int index, StringBuilder sb) + throws IOException { + sb.setLength(0); + sb.append(""); + for (int j=0; j < data.size(); j++) { + if (j > 0) + sb.append(","); + sb.append(TextUtil.doubleToString(data.get(j).get(index))); + } + sb.append(""); + writeln(sb.toString()); + } + + + + private void writeElement(String element, Object content) throws IOException { + if (content == null) + content = ""; + writeln("<"+element+">"+content+""); + } + + + + private void writeln(String str) throws IOException { + if (str.length() == 0) { + dest.write("\n"); + return; + } + String s=""; + for (int i=0; i " + Double.parseDouble(str)); + d *= 10; + } + + + System.out.println("Value: "+ Double.parseDouble("1.2345e9")); + + } + + + /** + * Return the XML equivalent of an enum name. + * + * @param e the enum to save. + * @return the corresponding XML name. + */ + public static String enumToXMLName(Enum e) { + return e.name().toLowerCase().replace("_", ""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/ParachuteSaver.java b/src/net/sf/openrocket/file/openrocket/ParachuteSaver.java deleted file mode 100644 index 64e44ff8..00000000 --- a/src/net/sf/openrocket/file/openrocket/ParachuteSaver.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.Parachute; - - -public class ParachuteSaver extends RecoveryDeviceSaver { - - private static final ParachuteSaver instance = new ParachuteSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - Parachute para = (Parachute) c; - - elements.add("" + para.getDiameter() + ""); - elements.add("" + para.getLineCount() + ""); - elements.add("" + para.getLineLength() + ""); - elements.add(materialParam("linematerial", para.getLineMaterial())); - } - - -} diff --git a/src/net/sf/openrocket/file/openrocket/RadiusRingComponentSaver.java b/src/net/sf/openrocket/file/openrocket/RadiusRingComponentSaver.java deleted file mode 100644 index 02cf0159..00000000 --- a/src/net/sf/openrocket/file/openrocket/RadiusRingComponentSaver.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.rocketcomponent.Bulkhead; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; - - -public class RadiusRingComponentSaver extends RingComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - RadiusRingComponent comp = (RadiusRingComponent)c; - if (comp.isOuterRadiusAutomatic()) - elements.add("auto"); - else - elements.add("" + comp.getOuterRadius() + ""); - if (!(comp instanceof Bulkhead)) { - if (comp.isInnerRadiusAutomatic()) - elements.add("auto"); - else - elements.add("" + comp.getInnerRadius() + ""); - } - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/RecoveryDeviceSaver.java b/src/net/sf/openrocket/file/openrocket/RecoveryDeviceSaver.java deleted file mode 100644 index a57747e2..00000000 --- a/src/net/sf/openrocket/file/openrocket/RecoveryDeviceSaver.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.rocketcomponent.RecoveryDevice; - - -public class RecoveryDeviceSaver extends MassObjectSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - RecoveryDevice dev = (RecoveryDevice) c; - - if (dev.isCDAutomatic()) - elements.add("auto"); - else - elements.add("" + dev.getCD() + ""); - - elements.add("" + dev.getDeployEvent().name().toLowerCase() + ""); - elements.add("" + dev.getDeployAltitude() + ""); - elements.add("" + dev.getDeployDelay() + ""); - elements.add(materialParam(dev.getMaterial())); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/RingComponentSaver.java b/src/net/sf/openrocket/file/openrocket/RingComponentSaver.java deleted file mode 100644 index 53d8177f..00000000 --- a/src/net/sf/openrocket/file/openrocket/RingComponentSaver.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.rocketcomponent.RingComponent; - - -public class RingComponentSaver extends StructuralComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - RingComponent ring = (RingComponent) c; - - elements.add("" + ring.getLength() + ""); - elements.add("" + ring.getRadialPosition() + ""); - elements.add("" + (ring.getRadialDirection() * 180.0 / Math.PI) - + ""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/RocketComponentSaver.java b/src/net/sf/openrocket/file/openrocket/RocketComponentSaver.java deleted file mode 100644 index a9da9b2f..00000000 --- a/src/net/sf/openrocket/file/openrocket/RocketComponentSaver.java +++ /dev/null @@ -1,151 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.awt.Color; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import net.sf.openrocket.file.RocketSaver; -import net.sf.openrocket.material.Material; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.rocketcomponent.ComponentAssembly; -import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.util.LineStyle; - - -public class RocketComponentSaver { - - protected RocketComponentSaver() { - // Prevent instantiation from outside the package - } - - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - elements.add("" + RocketSaver.escapeXML(c.getName()) + ""); - - - // Save color and line style if significant - if (!(c instanceof Rocket || c instanceof ComponentAssembly)) { - Color color = c.getColor(); - if (color != null) { - elements.add(""); - } - - LineStyle style = c.getLineStyle(); - if (style != null) { - // Type names currently equivalent to the enum names except for case. - elements.add("" + style.name().toLowerCase() + ""); - } - } - - - // Save position unless "AFTER" - if (c.getRelativePosition() != RocketComponent.Position.AFTER) { - // The type names are currently equivalent to the enum names except for case. - String type = c.getRelativePosition().name().toLowerCase(); - elements.add("" + c.getPositionValue() + ""); - } - - - // Overrides - boolean overridden = false; - if (c.isMassOverridden()) { - elements.add("" + c.getOverrideMass() + ""); - overridden = true; - } - if (c.isCGOverridden()) { - elements.add("" + c.getOverrideCGX() + ""); - overridden = true; - } - if (overridden) { - elements.add("" + c.getOverrideSubcomponents() - + ""); - } - - - // Comment - if (c.getComment().length() > 0) { - elements.add("" + RocketSaver.escapeXML(c.getComment()) + ""); - } - - } - - - - - protected final String materialParam(Material mat) { - return materialParam("material", mat); - } - - - protected final String materialParam(String tag, Material mat) { - String str = "<" + tag; - - switch (mat.getType()) { - case LINE: - str += " type=\"line\""; - break; - case SURFACE: - str += " type=\"surface\""; - break; - case BULK: - str += " type=\"bulk\""; - break; - default: - throw new RuntimeException("Unknown material type: " + mat.getType()); - } - - return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(mat.getName()) + ""; - } - - - protected final List motorMountParams(MotorMount mount) { - if (!mount.isMotorMount()) - return Collections.emptyList(); - - String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getMotorConfigurationIDs(); - List elements = new ArrayList(); - - elements.add(""); - - for (String id : motorConfigIDs) { - Motor motor = mount.getMotor(id); - - // Nothing is stored if no motor loaded - if (motor == null) - continue; - - elements.add(" "); - if (motor.getMotorType() != Motor.Type.UNKNOWN) { - elements.add(" " + motor.getMotorType().name().toLowerCase() + ""); - } - elements.add(" " + RocketSaver.escapeXML(motor.getManufacturer().getSimpleName()) + ""); - elements.add(" " + RocketSaver.escapeXML(motor.getDesignation()) + ""); - elements.add(" " + motor.getDiameter() + ""); - elements.add(" " + motor.getLength() + ""); - - // Motor delay - if (mount.getMotorDelay(id) == Motor.PLUGGED) { - elements.add(" none"); - } else { - elements.add(" " + mount.getMotorDelay(id) + ""); - } - - elements.add(" "); - } - - elements.add(" " - + mount.getIgnitionEvent().name().toLowerCase().replace("_", "") - + ""); - - elements.add(" " + mount.getIgnitionDelay() + ""); - elements.add(" " + mount.getMotorOverhang() + ""); - - elements.add(""); - - return elements; - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/RocketSaver.java b/src/net/sf/openrocket/file/openrocket/RocketSaver.java deleted file mode 100644 index e27fa76c..00000000 --- a/src/net/sf/openrocket/file/openrocket/RocketSaver.java +++ /dev/null @@ -1,73 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.ReferenceType; -import net.sf.openrocket.rocketcomponent.Rocket; - - -public class RocketSaver extends RocketComponentSaver { - - private static final RocketSaver instance = new RocketSaver(); - - public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - ArrayList list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - Rocket rocket = (Rocket) c; - - if (rocket.getDesigner().length() > 0) { - elements.add("" - + net.sf.openrocket.file.RocketSaver.escapeXML(rocket.getDesigner()) - + ""); - } - if (rocket.getRevision().length() > 0) { - elements.add("" - + net.sf.openrocket.file.RocketSaver.escapeXML(rocket.getRevision()) - + ""); - } - - - // Motor configurations - String defId = rocket.getDefaultConfiguration().getMotorConfigurationID(); - for (String id : rocket.getMotorConfigurationIDs()) { - if (id == null) - continue; - - String str = ""; - } - elements.add(str); - } - - // Reference diameter - elements.add("" + rocket.getReferenceType().name().toLowerCase() - + ""); - if (rocket.getReferenceType() == ReferenceType.CUSTOM) { - elements.add("" + rocket.getCustomReferenceLength() - + ""); - } - - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/ShockCordSaver.java b/src/net/sf/openrocket/file/openrocket/ShockCordSaver.java deleted file mode 100644 index 0f8746e2..00000000 --- a/src/net/sf/openrocket/file/openrocket/ShockCordSaver.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.ShockCord; - - -public class ShockCordSaver extends MassObjectSaver { - - private static final ShockCordSaver instance = new ShockCordSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - ShockCord mass = (ShockCord) c; - - elements.add("" + mass.getCordLength() + ""); - elements.add(materialParam(mass.getMaterial())); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/StageSaver.java b/src/net/sf/openrocket/file/openrocket/StageSaver.java deleted file mode 100644 index 2c837728..00000000 --- a/src/net/sf/openrocket/file/openrocket/StageSaver.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; - -public class StageSaver extends ComponentAssemblySaver { - - private static final StageSaver instance = new StageSaver(); - - public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - ArrayList list = new ArrayList(); - - list.add(""); - instance.addParams(c,list); - list.add(""); - - return list; - } - - -} diff --git a/src/net/sf/openrocket/file/openrocket/StreamerSaver.java b/src/net/sf/openrocket/file/openrocket/StreamerSaver.java deleted file mode 100644 index d3e936b5..00000000 --- a/src/net/sf/openrocket/file/openrocket/StreamerSaver.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.Streamer; - - -public class StreamerSaver extends RecoveryDeviceSaver { - - private static final StreamerSaver instance = new StreamerSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - Streamer st = (Streamer) c; - - elements.add("" + st.getStripLength() + ""); - elements.add("" + st.getStripWidth() + ""); - } - - -} diff --git a/src/net/sf/openrocket/file/openrocket/StructuralComponentSaver.java b/src/net/sf/openrocket/file/openrocket/StructuralComponentSaver.java deleted file mode 100644 index 4d7f6059..00000000 --- a/src/net/sf/openrocket/file/openrocket/StructuralComponentSaver.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.rocketcomponent.StructuralComponent; - - -public class StructuralComponentSaver extends InternalComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - StructuralComponent comp = (StructuralComponent)c; - elements.add(materialParam(comp.getMaterial())); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/SymmetricComponentSaver.java b/src/net/sf/openrocket/file/openrocket/SymmetricComponentSaver.java deleted file mode 100644 index 741c5135..00000000 --- a/src/net/sf/openrocket/file/openrocket/SymmetricComponentSaver.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -public class SymmetricComponentSaver extends BodyComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - net.sf.openrocket.rocketcomponent.SymmetricComponent comp = (net.sf.openrocket.rocketcomponent.SymmetricComponent)c; - if (comp.isFilled()) - elements.add("filled"); - else - elements.add(""+comp.getThickness()+""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/ThicknessRingComponentSaver.java b/src/net/sf/openrocket/file/openrocket/ThicknessRingComponentSaver.java deleted file mode 100644 index d52df559..00000000 --- a/src/net/sf/openrocket/file/openrocket/ThicknessRingComponentSaver.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.List; - -import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; - - -public class ThicknessRingComponentSaver extends RingComponentSaver { - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - ThicknessRingComponent comp = (ThicknessRingComponent)c; - if (comp.isOuterRadiusAutomatic()) - elements.add("auto"); - else - elements.add("" + comp.getOuterRadius() + ""); - elements.add("" + comp.getThickness() + ""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/TransitionSaver.java b/src/net/sf/openrocket/file/openrocket/TransitionSaver.java deleted file mode 100644 index 461087f6..00000000 --- a/src/net/sf/openrocket/file/openrocket/TransitionSaver.java +++ /dev/null @@ -1,79 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.Transition; - - -public class TransitionSaver extends SymmetricComponentSaver { - - private static final TransitionSaver instance = new TransitionSaver(); - - public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - ArrayList list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - - - /* - * Note: This method must be capable of handling nose cones as well. - */ - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - net.sf.openrocket.rocketcomponent.Transition trans = (net.sf.openrocket.rocketcomponent.Transition) c; - boolean nosecone = (trans instanceof NoseCone); - - - Transition.Shape shape = trans.getType(); - elements.add("" + shape.name().toLowerCase() + ""); - if (shape.isClippable()) { - elements.add("" + trans.isClipped() + ""); - } - if (shape.usesParameter()) { - elements.add("" + trans.getShapeParameter() + ""); - } - - - if (!nosecone) { - if (trans.isForeRadiusAutomatic()) - elements.add("auto"); - else - elements.add("" + trans.getForeRadius() + ""); - } - - if (trans.isAftRadiusAutomatic()) - elements.add("auto"); - else - elements.add("" + trans.getAftRadius() + ""); - - - if (!nosecone) { - elements.add("" + trans.getForeShoulderRadius() - + ""); - elements.add("" + trans.getForeShoulderLength() - + ""); - elements.add("" + trans.getForeShoulderThickness() - + ""); - elements.add("" + trans.isForeShoulderCapped() - + ""); - } - - elements.add("" + trans.getAftShoulderRadius() - + ""); - elements.add("" + trans.getAftShoulderLength() - + ""); - elements.add("" + trans.getAftShoulderThickness() - + ""); - elements.add("" + trans.isAftShoulderCapped() - + ""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/TrapezoidFinSetSaver.java b/src/net/sf/openrocket/file/openrocket/TrapezoidFinSetSaver.java deleted file mode 100644 index fc244cc1..00000000 --- a/src/net/sf/openrocket/file/openrocket/TrapezoidFinSetSaver.java +++ /dev/null @@ -1,31 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class TrapezoidFinSetSaver extends FinSetSaver { - - private static final TrapezoidFinSetSaver instance = new TrapezoidFinSetSaver(); - - public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - ArrayList list = new ArrayList(); - - list.add(""); - instance.addParams(c,list); - list.add(""); - - return list; - } - - @Override - protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - super.addParams(c, elements); - - net.sf.openrocket.rocketcomponent.TrapezoidFinSet fins = (net.sf.openrocket.rocketcomponent.TrapezoidFinSet)c; - elements.add(""+fins.getRootChord()+""); - elements.add(""+fins.getTipChord()+""); - elements.add(""+fins.getSweep()+""); - elements.add(""+fins.getHeight()+""); - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/TubeCouplerSaver.java b/src/net/sf/openrocket/file/openrocket/TubeCouplerSaver.java deleted file mode 100644 index 09a79c7f..00000000 --- a/src/net/sf/openrocket/file/openrocket/TubeCouplerSaver.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.openrocket.file.openrocket; - -import java.util.ArrayList; -import java.util.List; - -public class TubeCouplerSaver extends ThicknessRingComponentSaver { - - private static final TubeCouplerSaver instance = new TubeCouplerSaver(); - - public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { - List list = new ArrayList(); - - list.add(""); - instance.addParams(c, list); - list.add(""); - - return list; - } - -} diff --git a/src/net/sf/openrocket/file/openrocket/savers/BodyComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/BodyComponentSaver.java new file mode 100644 index 00000000..1a65676b --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/BodyComponentSaver.java @@ -0,0 +1,15 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +public class BodyComponentSaver extends ExternalComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + // Body components have a natural length, store it now + elements.add(""+((net.sf.openrocket.rocketcomponent.BodyComponent)c).getLength()+""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/BodyTubeSaver.java b/src/net/sf/openrocket/file/openrocket/savers/BodyTubeSaver.java new file mode 100644 index 00000000..5caf2896 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/BodyTubeSaver.java @@ -0,0 +1,36 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class BodyTubeSaver extends SymmetricComponentSaver { + + private static final BodyTubeSaver instance = new BodyTubeSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + net.sf.openrocket.rocketcomponent.BodyTube tube = (net.sf.openrocket.rocketcomponent.BodyTube) c; + + if (tube.isRadiusAutomatic()) + elements.add("auto"); + else + elements.add("" + tube.getRadius() + ""); + + if (tube.isMotorMount()) { + elements.addAll(motorMountParams(tube)); + } + } + + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/BulkheadSaver.java b/src/net/sf/openrocket/file/openrocket/savers/BulkheadSaver.java new file mode 100644 index 00000000..9c0ef2d8 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/BulkheadSaver.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class BulkheadSaver extends RadiusRingComponentSaver { + + private static final BulkheadSaver instance = new BulkheadSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/CenteringRingSaver.java b/src/net/sf/openrocket/file/openrocket/savers/CenteringRingSaver.java new file mode 100644 index 00000000..bb428a3d --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/CenteringRingSaver.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class CenteringRingSaver extends RadiusRingComponentSaver { + + private static final CenteringRingSaver instance = new CenteringRingSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/ComponentAssemblySaver.java b/src/net/sf/openrocket/file/openrocket/savers/ComponentAssemblySaver.java new file mode 100644 index 00000000..9e9d609d --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/ComponentAssemblySaver.java @@ -0,0 +1,7 @@ +package net.sf.openrocket.file.openrocket.savers; + +public class ComponentAssemblySaver extends RocketComponentSaver { + + // No-op + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/EllipticalFinSetSaver.java b/src/net/sf/openrocket/file/openrocket/savers/EllipticalFinSetSaver.java new file mode 100644 index 00000000..31b9dfb0 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/EllipticalFinSetSaver.java @@ -0,0 +1,29 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class EllipticalFinSetSaver extends FinSetSaver { + + private static final EllipticalFinSetSaver instance = new EllipticalFinSetSaver(); + + public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + ArrayList list = new ArrayList(); + + list.add(""); + instance.addParams(c,list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + net.sf.openrocket.rocketcomponent.EllipticalFinSet fins = (net.sf.openrocket.rocketcomponent.EllipticalFinSet)c; + elements.add(""+fins.getLength()+""); + elements.add(""+fins.getHeight()+""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/EngineBlockSaver.java b/src/net/sf/openrocket/file/openrocket/savers/EngineBlockSaver.java new file mode 100644 index 00000000..921ddde5 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/EngineBlockSaver.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class EngineBlockSaver extends ThicknessRingComponentSaver { + + private static final EngineBlockSaver instance = new EngineBlockSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/ExternalComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/ExternalComponentSaver.java new file mode 100644 index 00000000..2f1b2a4e --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/ExternalComponentSaver.java @@ -0,0 +1,23 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.rocketcomponent.ExternalComponent; + + +public class ExternalComponentSaver extends RocketComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + ExternalComponent ext = (ExternalComponent)c; + + // Finish enum names are currently the same except for case + elements.add("" + ext.getFinish().name().toLowerCase() + ""); + + // Material + elements.add(materialParam(ext.getMaterial())); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java b/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java new file mode 100644 index 00000000..8756d89f --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java @@ -0,0 +1,34 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.util.MathUtil; + +public class FinSetSaver extends ExternalComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + net.sf.openrocket.rocketcomponent.FinSet fins = (net.sf.openrocket.rocketcomponent.FinSet) c; + elements.add("" + fins.getFinCount() + ""); + elements.add("" + (fins.getBaseRotation() * 180.0 / Math.PI) + ""); + elements.add("" + fins.getThickness() + ""); + elements.add("" + fins.getCrossSection().name().toLowerCase() + + ""); + elements.add("" + (fins.getCantAngle() * 180.0 / Math.PI) + ""); + + // Save fin tabs only if they exist (compatibility with file version < 1.1) + if (!MathUtil.equals(fins.getTabHeight(),0) && + !MathUtil.equals(fins.getTabLength(), 0)) { + + elements.add("" + fins.getTabHeight() + ""); + elements.add("" + fins.getTabLength() + ""); + elements.add("" + + fins.getTabShift() + ""); + + } + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/FreeformFinSetSaver.java b/src/net/sf/openrocket/file/openrocket/savers/FreeformFinSetSaver.java new file mode 100644 index 00000000..c310e4d9 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/FreeformFinSetSaver.java @@ -0,0 +1,36 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.util.Coordinate; + + +public class FreeformFinSetSaver extends FinSetSaver { + + private static final FreeformFinSetSaver instance = new FreeformFinSetSaver(); + + public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + ArrayList list = new ArrayList(); + + list.add(""); + instance.addParams(c,list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + FreeformFinSet fins = (FreeformFinSet)c; + elements.add(""); + for (Coordinate p: fins.getFinPoints()) { + elements.add(" "); + } + elements.add(""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/InnerTubeSaver.java b/src/net/sf/openrocket/file/openrocket/savers/InnerTubeSaver.java new file mode 100644 index 00000000..7eb0a538 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/InnerTubeSaver.java @@ -0,0 +1,43 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.InnerTube; + + +public class InnerTubeSaver extends ThicknessRingComponentSaver { + + private static final InnerTubeSaver instance = new InnerTubeSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + InnerTube tube = (InnerTube) c; + + elements.add("" + tube.getClusterConfiguration().getXMLName() + + ""); + elements.add("" + tube.getClusterScale() + ""); + elements.add("" + (tube.getClusterRotation() * 180.0 / Math.PI) + + ""); + + if (tube.isMotorMount()) { + elements.addAll(motorMountParams(tube)); + } + + + } + + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/InternalComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/InternalComponentSaver.java new file mode 100644 index 00000000..0a2dbe72 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/InternalComponentSaver.java @@ -0,0 +1,14 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +public class InternalComponentSaver extends RocketComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + // Nothing to save + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/LaunchLugSaver.java b/src/net/sf/openrocket/file/openrocket/savers/LaunchLugSaver.java new file mode 100644 index 00000000..3008bcce --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/LaunchLugSaver.java @@ -0,0 +1,35 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.LaunchLug; + + +public class LaunchLugSaver extends ExternalComponentSaver { + + private static final LaunchLugSaver instance = new LaunchLugSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + LaunchLug lug = (LaunchLug) c; + + elements.add("" + lug.getRadius() + ""); + elements.add("" + lug.getLength() + ""); + elements.add("" + lug.getThickness() + ""); + elements.add("" + (lug.getRadialDirection()*180.0/Math.PI) + ""); + } + + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/MassComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/MassComponentSaver.java new file mode 100644 index 00000000..093303c2 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/MassComponentSaver.java @@ -0,0 +1,32 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.MassComponent; + + +public class MassComponentSaver extends MassObjectSaver { + + private static final MassComponentSaver instance = new MassComponentSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + MassComponent mass = (MassComponent) c; + + elements.add("" + mass.getMass() + ""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/MassObjectSaver.java b/src/net/sf/openrocket/file/openrocket/savers/MassObjectSaver.java new file mode 100644 index 00000000..298cb263 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/MassObjectSaver.java @@ -0,0 +1,23 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.rocketcomponent.MassObject; + + +public class MassObjectSaver extends InternalComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + MassObject mass = (MassObject) c; + + elements.add("" + mass.getLength() + ""); + elements.add("" + mass.getRadius() + ""); + elements.add("" + mass.getRadialPosition() + ""); + elements.add("" + (mass.getRadialDirection() * 180.0 / Math.PI) + + ""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/NoseConeSaver.java b/src/net/sf/openrocket/file/openrocket/savers/NoseConeSaver.java new file mode 100644 index 00000000..9733254f --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/NoseConeSaver.java @@ -0,0 +1,27 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class NoseConeSaver extends TransitionSaver { + + private static final NoseConeSaver instance = new NoseConeSaver(); + + public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + ArrayList list = new ArrayList(); + + list.add(""); + instance.addParams(c,list); + list.add(""); + + return list; + } + + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + // Transition handles nose cone saving as well + } +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/ParachuteSaver.java b/src/net/sf/openrocket/file/openrocket/savers/ParachuteSaver.java new file mode 100644 index 00000000..7d906e67 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/ParachuteSaver.java @@ -0,0 +1,35 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.Parachute; + + +public class ParachuteSaver extends RecoveryDeviceSaver { + + private static final ParachuteSaver instance = new ParachuteSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + Parachute para = (Parachute) c; + + elements.add("" + para.getDiameter() + ""); + elements.add("" + para.getLineCount() + ""); + elements.add("" + para.getLineLength() + ""); + elements.add(materialParam("linematerial", para.getLineMaterial())); + } + + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/RadiusRingComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/RadiusRingComponentSaver.java new file mode 100644 index 00000000..c1b90204 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/RadiusRingComponentSaver.java @@ -0,0 +1,28 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.rocketcomponent.Bulkhead; +import net.sf.openrocket.rocketcomponent.RadiusRingComponent; + + +public class RadiusRingComponentSaver extends RingComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + RadiusRingComponent comp = (RadiusRingComponent)c; + if (comp.isOuterRadiusAutomatic()) + elements.add("auto"); + else + elements.add("" + comp.getOuterRadius() + ""); + if (!(comp instanceof Bulkhead)) { + if (comp.isInnerRadiusAutomatic()) + elements.add("auto"); + else + elements.add("" + comp.getInnerRadius() + ""); + } + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/RecoveryDeviceSaver.java b/src/net/sf/openrocket/file/openrocket/savers/RecoveryDeviceSaver.java new file mode 100644 index 00000000..22dcaa9c --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/RecoveryDeviceSaver.java @@ -0,0 +1,27 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.rocketcomponent.RecoveryDevice; + + +public class RecoveryDeviceSaver extends MassObjectSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + RecoveryDevice dev = (RecoveryDevice) c; + + if (dev.isCDAutomatic()) + elements.add("auto"); + else + elements.add("" + dev.getCD() + ""); + + elements.add("" + dev.getDeployEvent().name().toLowerCase() + ""); + elements.add("" + dev.getDeployAltitude() + ""); + elements.add("" + dev.getDeployDelay() + ""); + elements.add(materialParam(dev.getMaterial())); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/RingComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/RingComponentSaver.java new file mode 100644 index 00000000..c1999fba --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/RingComponentSaver.java @@ -0,0 +1,22 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.rocketcomponent.RingComponent; + + +public class RingComponentSaver extends StructuralComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + RingComponent ring = (RingComponent) c; + + elements.add("" + ring.getLength() + ""); + elements.add("" + ring.getRadialPosition() + ""); + elements.add("" + (ring.getRadialDirection() * 180.0 / Math.PI) + + ""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java new file mode 100644 index 00000000..64e55895 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java @@ -0,0 +1,151 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sf.openrocket.file.RocketSaver; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.ComponentAssembly; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.LineStyle; + + +public class RocketComponentSaver { + + protected RocketComponentSaver() { + // Prevent instantiation from outside the package + } + + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + elements.add("" + RocketSaver.escapeXML(c.getName()) + ""); + + + // Save color and line style if significant + if (!(c instanceof Rocket || c instanceof ComponentAssembly)) { + Color color = c.getColor(); + if (color != null) { + elements.add(""); + } + + LineStyle style = c.getLineStyle(); + if (style != null) { + // Type names currently equivalent to the enum names except for case. + elements.add("" + style.name().toLowerCase() + ""); + } + } + + + // Save position unless "AFTER" + if (c.getRelativePosition() != RocketComponent.Position.AFTER) { + // The type names are currently equivalent to the enum names except for case. + String type = c.getRelativePosition().name().toLowerCase(); + elements.add("" + c.getPositionValue() + ""); + } + + + // Overrides + boolean overridden = false; + if (c.isMassOverridden()) { + elements.add("" + c.getOverrideMass() + ""); + overridden = true; + } + if (c.isCGOverridden()) { + elements.add("" + c.getOverrideCGX() + ""); + overridden = true; + } + if (overridden) { + elements.add("" + c.getOverrideSubcomponents() + + ""); + } + + + // Comment + if (c.getComment().length() > 0) { + elements.add("" + RocketSaver.escapeXML(c.getComment()) + ""); + } + + } + + + + + protected final String materialParam(Material mat) { + return materialParam("material", mat); + } + + + protected final String materialParam(String tag, Material mat) { + String str = "<" + tag; + + switch (mat.getType()) { + case LINE: + str += " type=\"line\""; + break; + case SURFACE: + str += " type=\"surface\""; + break; + case BULK: + str += " type=\"bulk\""; + break; + default: + throw new RuntimeException("Unknown material type: " + mat.getType()); + } + + return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(mat.getName()) + ""; + } + + + protected final List motorMountParams(MotorMount mount) { + if (!mount.isMotorMount()) + return Collections.emptyList(); + + String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getMotorConfigurationIDs(); + List elements = new ArrayList(); + + elements.add(""); + + for (String id : motorConfigIDs) { + Motor motor = mount.getMotor(id); + + // Nothing is stored if no motor loaded + if (motor == null) + continue; + + elements.add(" "); + if (motor.getMotorType() != Motor.Type.UNKNOWN) { + elements.add(" " + motor.getMotorType().name().toLowerCase() + ""); + } + elements.add(" " + RocketSaver.escapeXML(motor.getManufacturer().getSimpleName()) + ""); + elements.add(" " + RocketSaver.escapeXML(motor.getDesignation()) + ""); + elements.add(" " + motor.getDiameter() + ""); + elements.add(" " + motor.getLength() + ""); + + // Motor delay + if (mount.getMotorDelay(id) == Motor.PLUGGED) { + elements.add(" none"); + } else { + elements.add(" " + mount.getMotorDelay(id) + ""); + } + + elements.add(" "); + } + + elements.add(" " + + mount.getIgnitionEvent().name().toLowerCase().replace("_", "") + + ""); + + elements.add(" " + mount.getIgnitionDelay() + ""); + elements.add(" " + mount.getMotorOverhang() + ""); + + elements.add(""); + + return elements; + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/RocketSaver.java b/src/net/sf/openrocket/file/openrocket/savers/RocketSaver.java new file mode 100644 index 00000000..e8b7a345 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/RocketSaver.java @@ -0,0 +1,73 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.ReferenceType; +import net.sf.openrocket.rocketcomponent.Rocket; + + +public class RocketSaver extends RocketComponentSaver { + + private static final RocketSaver instance = new RocketSaver(); + + public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + ArrayList list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + Rocket rocket = (Rocket) c; + + if (rocket.getDesigner().length() > 0) { + elements.add("" + + net.sf.openrocket.file.RocketSaver.escapeXML(rocket.getDesigner()) + + ""); + } + if (rocket.getRevision().length() > 0) { + elements.add("" + + net.sf.openrocket.file.RocketSaver.escapeXML(rocket.getRevision()) + + ""); + } + + + // Motor configurations + String defId = rocket.getDefaultConfiguration().getMotorConfigurationID(); + for (String id : rocket.getMotorConfigurationIDs()) { + if (id == null) + continue; + + String str = ""; + } + elements.add(str); + } + + // Reference diameter + elements.add("" + rocket.getReferenceType().name().toLowerCase() + + ""); + if (rocket.getReferenceType() == ReferenceType.CUSTOM) { + elements.add("" + rocket.getCustomReferenceLength() + + ""); + } + + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/ShockCordSaver.java b/src/net/sf/openrocket/file/openrocket/savers/ShockCordSaver.java new file mode 100644 index 00000000..8b8ae01f --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/ShockCordSaver.java @@ -0,0 +1,33 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.ShockCord; + + +public class ShockCordSaver extends MassObjectSaver { + + private static final ShockCordSaver instance = new ShockCordSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + ShockCord mass = (ShockCord) c; + + elements.add("" + mass.getCordLength() + ""); + elements.add(materialParam(mass.getMaterial())); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java b/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java new file mode 100644 index 00000000..0fd0f6f3 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; + +public class StageSaver extends ComponentAssemblySaver { + + private static final StageSaver instance = new StageSaver(); + + public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + ArrayList list = new ArrayList(); + + list.add(""); + instance.addParams(c,list); + list.add(""); + + return list; + } + + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/StreamerSaver.java b/src/net/sf/openrocket/file/openrocket/savers/StreamerSaver.java new file mode 100644 index 00000000..5b92852e --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/StreamerSaver.java @@ -0,0 +1,33 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.Streamer; + + +public class StreamerSaver extends RecoveryDeviceSaver { + + private static final StreamerSaver instance = new StreamerSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + Streamer st = (Streamer) c; + + elements.add("" + st.getStripLength() + ""); + elements.add("" + st.getStripWidth() + ""); + } + + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/StructuralComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/StructuralComponentSaver.java new file mode 100644 index 00000000..5cf5ce70 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/StructuralComponentSaver.java @@ -0,0 +1,18 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.rocketcomponent.StructuralComponent; + + +public class StructuralComponentSaver extends InternalComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + StructuralComponent comp = (StructuralComponent)c; + elements.add(materialParam(comp.getMaterial())); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/SymmetricComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/SymmetricComponentSaver.java new file mode 100644 index 00000000..bab5eccb --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/SymmetricComponentSaver.java @@ -0,0 +1,18 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +public class SymmetricComponentSaver extends BodyComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + net.sf.openrocket.rocketcomponent.SymmetricComponent comp = (net.sf.openrocket.rocketcomponent.SymmetricComponent)c; + if (comp.isFilled()) + elements.add("filled"); + else + elements.add(""+comp.getThickness()+""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/ThicknessRingComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/ThicknessRingComponentSaver.java new file mode 100644 index 00000000..d5a3c1b3 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/ThicknessRingComponentSaver.java @@ -0,0 +1,22 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.List; + +import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; + + +public class ThicknessRingComponentSaver extends RingComponentSaver { + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + ThicknessRingComponent comp = (ThicknessRingComponent)c; + if (comp.isOuterRadiusAutomatic()) + elements.add("auto"); + else + elements.add("" + comp.getOuterRadius() + ""); + elements.add("" + comp.getThickness() + ""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/TransitionSaver.java b/src/net/sf/openrocket/file/openrocket/savers/TransitionSaver.java new file mode 100644 index 00000000..d7bb1ed7 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/TransitionSaver.java @@ -0,0 +1,79 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.Transition; + + +public class TransitionSaver extends SymmetricComponentSaver { + + private static final TransitionSaver instance = new TransitionSaver(); + + public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + ArrayList list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + + + /* + * Note: This method must be capable of handling nose cones as well. + */ + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + net.sf.openrocket.rocketcomponent.Transition trans = (net.sf.openrocket.rocketcomponent.Transition) c; + boolean nosecone = (trans instanceof NoseCone); + + + Transition.Shape shape = trans.getType(); + elements.add("" + shape.name().toLowerCase() + ""); + if (shape.isClippable()) { + elements.add("" + trans.isClipped() + ""); + } + if (shape.usesParameter()) { + elements.add("" + trans.getShapeParameter() + ""); + } + + + if (!nosecone) { + if (trans.isForeRadiusAutomatic()) + elements.add("auto"); + else + elements.add("" + trans.getForeRadius() + ""); + } + + if (trans.isAftRadiusAutomatic()) + elements.add("auto"); + else + elements.add("" + trans.getAftRadius() + ""); + + + if (!nosecone) { + elements.add("" + trans.getForeShoulderRadius() + + ""); + elements.add("" + trans.getForeShoulderLength() + + ""); + elements.add("" + trans.getForeShoulderThickness() + + ""); + elements.add("" + trans.isForeShoulderCapped() + + ""); + } + + elements.add("" + trans.getAftShoulderRadius() + + ""); + elements.add("" + trans.getAftShoulderLength() + + ""); + elements.add("" + trans.getAftShoulderThickness() + + ""); + elements.add("" + trans.isAftShoulderCapped() + + ""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/TrapezoidFinSetSaver.java b/src/net/sf/openrocket/file/openrocket/savers/TrapezoidFinSetSaver.java new file mode 100644 index 00000000..21ac2aa8 --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/TrapezoidFinSetSaver.java @@ -0,0 +1,31 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class TrapezoidFinSetSaver extends FinSetSaver { + + private static final TrapezoidFinSetSaver instance = new TrapezoidFinSetSaver(); + + public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + ArrayList list = new ArrayList(); + + list.add(""); + instance.addParams(c,list); + list.add(""); + + return list; + } + + @Override + protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { + super.addParams(c, elements); + + net.sf.openrocket.rocketcomponent.TrapezoidFinSet fins = (net.sf.openrocket.rocketcomponent.TrapezoidFinSet)c; + elements.add(""+fins.getRootChord()+""); + elements.add(""+fins.getTipChord()+""); + elements.add(""+fins.getSweep()+""); + elements.add(""+fins.getHeight()+""); + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/TubeCouplerSaver.java b/src/net/sf/openrocket/file/openrocket/savers/TubeCouplerSaver.java new file mode 100644 index 00000000..0e19fd8d --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/savers/TubeCouplerSaver.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.file.openrocket.savers; + +import java.util.ArrayList; +import java.util.List; + +public class TubeCouplerSaver extends ThicknessRingComponentSaver { + + private static final TubeCouplerSaver instance = new TubeCouplerSaver(); + + public static List getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { + List list = new ArrayList(); + + list.add(""); + instance.addParams(c, list); + list.add(""); + + return list; + } + +} diff --git a/src/net/sf/openrocket/gui/StorageOptionChooser.java b/src/net/sf/openrocket/gui/StorageOptionChooser.java index 7e4b956c..dc9901b0 100644 --- a/src/net/sf/openrocket/gui/StorageOptionChooser.java +++ b/src/net/sf/openrocket/gui/StorageOptionChooser.java @@ -20,8 +20,8 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.StorageOptions; -import net.sf.openrocket.file.OpenRocketSaver; import net.sf.openrocket.file.RocketSaver; +import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightDataBranch; diff --git a/src/net/sf/openrocket/gui/main/BasicFrame.java b/src/net/sf/openrocket/gui/main/BasicFrame.java index c2d0d336..bd6e1552 100644 --- a/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -66,10 +66,10 @@ import net.sf.openrocket.communication.UpdateInfoRetriever; import net.sf.openrocket.database.Databases; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.file.GeneralRocketLoader; -import net.sf.openrocket.file.OpenRocketSaver; import net.sf.openrocket.file.RocketLoadException; import net.sf.openrocket.file.RocketLoader; import net.sf.openrocket.file.RocketSaver; +import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.gui.StorageOptionChooser; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; import net.sf.openrocket.gui.dialogs.AboutDialog; diff --git a/src/net/sf/openrocket/rocketcomponent/CenteringRing.java b/src/net/sf/openrocket/rocketcomponent/CenteringRing.java index 560cc260..3d70493d 100644 --- a/src/net/sf/openrocket/rocketcomponent/CenteringRing.java +++ b/src/net/sf/openrocket/rocketcomponent/CenteringRing.java @@ -28,6 +28,7 @@ public class CenteringRing extends RadiusRingComponent { if (pos2 < 0 || pos1 > sibling.getLength()) continue; + // TODO: CRITICAL: ClassCastException below: innerRadius = Math.max(innerRadius, ((InnerTube)sibling).getOuterRadius()); } innerRadius = Math.min(innerRadius, getOuterRadius());