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) / RocksimHandler.ROCKSIM_TO_OPENROCKET_DENSITY);
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 * Set the material onto an instance of RocketComponent. This is done because only some subtypes of RocketComponent
168 * have the setMaterial method. Unfortunately the supertype cannot be used.
170 * @param component the component who's material is to be set
171 * @param material the material to be set on the component (defined by getComponent())
173 private static void setMaterial(RocketComponent component, Material material) {
175 final Method method = getMethod(component, "setMaterial", new Class[]{Material.class});
176 if (method != null) {
177 method.invoke(component, material);
180 catch (IllegalAccessException ignored) {
182 catch (InvocationTargetException ignored) {
187 * Find a method by name and argument list.
189 * @param component the component who's material is to be seta
190 * @param name the method name
191 * @param args the class types of the parameters
193 * @return the Method instance, or null
195 private static Method getMethod(RocketComponent component, String name, Class[] args) {
196 Method method = null;
198 method = component.getClass().getMethod(name, args);
200 catch (NoSuchMethodException ignored) {