4 package net.sf.openrocket.file.rocksim;
6 import net.sf.openrocket.aerodynamics.WarningSet;
7 import net.sf.openrocket.file.simplesax.ElementHandler;
8 import net.sf.openrocket.material.Material;
9 import net.sf.openrocket.rocketcomponent.RocketComponent;
10 import org.xml.sax.SAXException;
12 import java.lang.reflect.InvocationTargetException;
13 import java.lang.reflect.Method;
14 import java.util.HashMap;
17 * An abstract base class that handles common parsing. All Rocksim component handlers are subclassed from here.
19 public abstract class BaseHandler<C extends RocketComponent> extends ElementHandler {
22 * The overridden mass.
24 private Double mass = 0d;
28 private Double cg = 0d;
30 * The density of the material in the component.
32 private Double density = 0d;
36 private String materialName = "";
39 * The SAX method called when the closing element tag is reached.
41 * @param element the element name.
42 * @param attributes attributes of the element.
43 * @param content the textual content of the element.
44 * @param warnings the warning set to store warnings in.
45 * @throws SAXException
49 public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
51 final C component = getComponent();
53 if ("Name".equals(element)) {
54 component.setName(content);
56 if ("KnownMass".equals(element)) {
57 mass = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
59 if ("Density".equals(element)) {
60 density = Math.max(0d, Double.parseDouble(content) / getDensityConversion());
62 if ("KnownCG".equals(element)) {
63 cg = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
65 if ("UseKnownCG".equals(element)) {
66 boolean override = "1".equals(content);
67 setOverride(component, override, mass, cg);
70 catch (NumberFormatException nfe) {
71 warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
76 public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
78 /* Because of the order of XML elements in Rocksim, not all information is known at the time it really needs
79 to be acted upon. So we keep temporary instance variables to be used here at the end of the parsing.
81 RocketComponent component = getComponent();
82 updateComponentMaterial(component, materialName, getMaterialType(), density);
86 * If the Rocksim component does not override the mass, then create a Material based upon the density defined
87 * for that component. This *should* result in a consistent representation of Cg between Rocksim and OpenRocket.
89 * @param component the component
90 * @param type the type of the material
91 * @param density the density in g/cm^3
92 * @param definedMaterial the material that is currently defined on the component; used only to get the name
93 * as it appears in Rocksim
95 public static void updateComponentMaterial(RocketComponent component, String definedMaterial, Material.Type type,
97 if (definedMaterial != null) {
98 Material custom = createCustomMaterial(type, definedMaterial, density);
99 setMaterial(component, custom);
104 * Override the mass and Cg of the component.
106 * @param component the component
107 * @param override true if any override should happen
108 * @param mass the override mass
109 * @param cg the override cg
111 public static void setOverride(RocketComponent component, boolean override, double mass, double cg) {
113 component.setCGOverridden(override);
114 component.setMassOverridden(override);
115 component.setOverrideSubcomponents(false); //Rocksim does not support this type of override
116 component.setOverrideMass(mass);
117 component.setOverrideCGX(cg);
122 * Get the component this handler is working upon.
124 * @return a component
126 protected abstract C getComponent();
129 * Get the required type of material for this component.
131 * @return the required material type
133 protected abstract Material.Type getMaterialType();
136 * Some CG positions in Rocksim do not correspond to the CG position reference in OpenRocket.
138 * @param theCG the CG value to really use when overriding CG on the OpenRocket component
140 protected void setCG(double theCG) {
145 * Set the material name as specified in the Rocksim design file.
147 * @param content the material name
149 protected void setMaterialName(String content) {
150 materialName = content;
154 * Create a custom material based on the density.
156 * @param type the type of the material
157 * @param name the name of the component
158 * @param density the density in g/cm^3
160 * @return a Material instance
162 public static Material createCustomMaterial(Material.Type type, String name, double density) {
163 return Material.newMaterial(type, "RS: " + name, density, true);
167 * Get the appropriate density conversion for different types of materials.
169 * @return a conversion value that is assumed to be in Rocksim Units / OpenRocket Units
171 private double getDensityConversion() {
172 switch (getMaterialType()) {
174 return RocksimHandler.ROCKSIM_TO_OPENROCKET_LINE_DENSITY;
176 return RocksimHandler.ROCKSIM_TO_OPENROCKET_SURFACE_DENSITY;
179 return RocksimHandler.ROCKSIM_TO_OPENROCKET_BULK_DENSITY;
184 * Set the material onto an instance of RocketComponent. This is done because only some subtypes of RocketComponent
185 * have the setMaterial method. Unfortunately the supertype cannot be used.
187 * @param component the component who's material is to be set
188 * @param material the material to be set on the component (defined by getComponent())
190 private static void setMaterial(RocketComponent component, Material material) {
192 final Method method = getMethod(component, "setMaterial", new Class[]{Material.class});
193 if (method != null) {
194 method.invoke(component, material);
197 catch (IllegalAccessException ignored) {
199 catch (InvocationTargetException ignored) {
204 * Find a method by name and argument list.
206 * @param component the component who's material is to be seta
207 * @param name the method name
208 * @param args the class types of the parameters
210 * @return the Method instance, or null
212 private static Method getMethod(RocketComponent component, String name, Class[] args) {
213 Method method = null;
215 method = component.getClass().getMethod(name, args);
217 catch (NoSuchMethodException ignored) {