refactored file package
[debian/openrocket] / src / net / sf / openrocket / file / OpenRocketSaver.java
diff --git a/src/net/sf/openrocket/file/OpenRocketSaver.java b/src/net/sf/openrocket/file/OpenRocketSaver.java
deleted file mode 100644 (file)
index 312aa0b..0000000
+++ /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("<?xml version='1.0' encoding='utf-8'?>");
-               writeln("<openrocket version=\"" + fileVersionString + "\" creator=\"OpenRocket "
-                               + Prefs.getVersion() + "\">");
-               indent++;
-               
-               // Recursively save the rocket structure
-               saveComponent(document.getRocket());
-               
-               writeln("");
-               
-               // Save all simulations
-               writeln("<simulations>");
-               indent++;
-               boolean first = true;
-               for (Simulation s: document.getSimulations()) {
-                       if (!first)
-                               writeln("");
-                       first = false;
-                       saveSimulation(s, options.getSimulationTimeSkip());
-               }
-               indent--;
-               writeln("</simulations>");
-               
-               indent--;
-               writeln("</openrocket>");
-               
-               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<RocketComponent> 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<RocketComponent> 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<String> list = (List<String>) 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<length-1; i++) {
-                       writeln(list.get(i));
-               }
-               
-               // Recursively write subcomponents
-               if (component.getChildCount() > 0) {
-                       writeln("");
-                       writeln("<subcomponents>");
-                       indent++;
-                       boolean emptyline = false;
-                       for (RocketComponent subcomponent: component) {
-                               if (emptyline)
-                                       writeln("");
-                               emptyline = true;
-                               saveComponent(subcomponent);
-                       }
-                       indent--;
-                       writeln("</subcomponents>");
-               }
-               
-               // Close element
-               indent--;
-               writeln(list.get(length-1));
-       }
-
-       
-       
-       private void saveSimulation(Simulation simulation, double timeSkip) throws IOException {
-               SimulationConditions cond = simulation.getConditions();
-               
-               writeln("<simulation status=\"" + enumToXMLName(simulation.getStatus()) +"\">");
-               indent++;
-               
-               writeln("<name>" + escapeXML(simulation.getName()) + "</name>");
-               // TODO: MEDIUM: Other simulators/calculators
-               writeln("<simulator>RK4Simulator</simulator>");
-               writeln("<calculator>BarrowmanCalculator</calculator>");
-               writeln("<conditions>");
-               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("<atmosphere model=\"isa\"/>");
-               } else {
-                       writeln("<atmosphere model=\"extendedisa\">");
-                       indent++;
-                       writeElement("basetemperature", cond.getLaunchTemperature());
-                       writeElement("basepressure", cond.getLaunchPressure());
-                       indent--;
-                       writeln("</atmosphere>");
-               }
-
-               writeElement("timestep", cond.getTimeStep());
-               
-               indent--;
-               writeln("</conditions>");
-               
-               
-               for (String s: simulation.getSimulationListeners()) {
-                       writeElement("listener", escapeXML(s));
-               }
-               
-               
-               // Write basic simulation data
-               
-               FlightData data = simulation.getSimulatedData();
-               if (data != null) {
-                       String str = "<flightdata";
-                       if (!Double.isNaN(data.getMaxAltitude()))
-                               str += " maxaltitude=\"" + TextUtil.doubleToString(data.getMaxAltitude()) + "\"";
-                       if (!Double.isNaN(data.getMaxVelocity()))
-                               str += " maxvelocity=\"" + TextUtil.doubleToString(data.getMaxVelocity()) + "\"";
-                       if (!Double.isNaN(data.getMaxAcceleration()))
-                               str += " maxacceleration=\"" + TextUtil.doubleToString(data.getMaxAcceleration()) + "\"";
-                       if (!Double.isNaN(data.getMaxMachNumber()))
-                               str += " maxmach=\"" + TextUtil.doubleToString(data.getMaxMachNumber()) + "\"";
-                       if (!Double.isNaN(data.getTimeToApogee()))
-                               str += " timetoapogee=\"" + TextUtil.doubleToString(data.getTimeToApogee()) + "\"";
-                       if (!Double.isNaN(data.getFlightTime()))
-                               str += " flighttime=\"" + TextUtil.doubleToString(data.getFlightTime()) + "\"";
-                       if (!Double.isNaN(data.getGroundHitVelocity()))
-                               str += " groundhitvelocity=\"" + TextUtil.doubleToString(data.getGroundHitVelocity()) + "\"";
-                       str += ">";
-                       writeln(str);
-                       indent++;
-                       
-                       for (Warning w: data.getWarningSet()) {
-                               writeElement("warning", escapeXML(w.toString()));
-                       }
-                       
-                       // Check whether to store data
-                       if (simulation.getStatus() == Simulation.Status.EXTERNAL) // Always store external data
-                               timeSkip = 0;
-                       
-                       if (timeSkip != StorageOptions.SIMULATION_DATA_NONE) {
-                               for (int i=0; i<data.getBranchCount(); i++) {
-                                       FlightDataBranch branch = data.getBranch(i);
-                                       saveFlightDataBranch(branch, timeSkip);
-                               }
-                       }
-                       
-                       indent--;
-                       writeln("</flightdata>");
-               }
-               
-               indent--;
-               writeln("</simulation>");
-               
-       }
-       
-       
-       
-       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<List<Double>> data = new ArrayList<List<Double>>(types.length);
-               for (int i=0; i<types.length; i++) {
-                       data.add(branch.get(types[i]));
-               }
-               List<Double> 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 <databranch> tag
-               StringBuilder sb = new StringBuilder();
-               sb.append("<databranch name=\"");
-               sb.append(escapeXML(branch.getBranchName()));
-               sb.append("\" types=\"");
-               for (int i=0; i<types.length; i++) {
-                       if (i > 0)
-                               sb.append(",");
-                       sb.append(escapeXML(types[i].getName()));
-               }
-               sb.append("\">");
-               writeln(sb.toString());
-               indent++;
-               
-               // Write events
-               for (Pair<Double,FlightEvent> p: branch.getEvents()) {
-                       writeln("<event time=\"" + TextUtil.doubleToString(p.getU())
-                                       + "\" type=\"" + enumToXMLName(p.getV().getType()) + "\"/>");
-               }
-               
-               // 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("</databranch>");
-       }
-       
-       
-       
-       /* 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<Double> 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<List<Double>> data, int index, StringBuilder sb)
-       throws IOException {
-               sb.setLength(0);
-               sb.append("<datapoint>");
-               for (int j=0; j < data.size(); j++) {
-                       if (j > 0)
-                               sb.append(",");
-                       sb.append(TextUtil.doubleToString(data.get(j).get(index)));
-               }
-               sb.append("</datapoint>");
-               writeln(sb.toString());
-       }
-       
-       
-       
-       private void writeElement(String element, Object content) throws IOException {
-               if (content == null)
-                       content = "";
-               writeln("<"+element+">"+content+"</"+element+">");
-       }
-
-
-       
-       private void writeln(String str) throws IOException {
-               if (str.length() == 0) {
-                       dest.write("\n");
-                       return;
-               }
-               String s="";
-               for (int i=0; i<indent; i++)
-                       s=s+"  ";
-               s = s+str+"\n";
-               dest.write(s);
-       }
-       
-       
-       public static void main(String[] arg) {
-               double d = -0.000000123456789123;
-               
-               
-               for (int i=0; i< 20; i++) {
-                       String str = TextUtil.doubleToString(d);
-                       System.out.println(str + "   ->   " + 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("_", "");
-       }
-       
-}