aad4a330ebddbd5ed546103e47496954227dd159
[debian/openrocket] / src / net / sf / openrocket / file / rocksim / BaseHandler.java
1 /*
2  * BaseHandler.java
3  */
4 package net.sf.openrocket.file.rocksim;
5
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;
11
12 import java.lang.reflect.InvocationTargetException;
13 import java.lang.reflect.Method;
14 import java.util.HashMap;
15
16 /**
17  * An abstract base class that handles common parsing.  All Rocksim component handlers are subclassed from here.
18  */
19 public abstract class BaseHandler<C extends RocketComponent> extends ElementHandler {
20
21     /**
22      * The overridden mass.
23      */
24     private Double mass = 0d;
25     /**
26      * The overridden Cg.
27      */
28     private Double cg = 0d;
29     /**
30      * The density of the material in the component.
31      */
32     private Double density = 0d;
33     /**
34      * The material name.
35      */
36     private String materialName = "";
37     
38     /**
39      * The SAX method called when the closing element tag is reached.
40      * 
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
46      */
47     
48     @Override
49     public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
50             throws SAXException {
51         final C component = getComponent();
52         try {
53             if ("Name".equals(element)) {
54                 component.setName(content);
55             }
56             if ("KnownMass".equals(element)) {
57                 mass = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
58             }
59             if ("Density".equals(element)) {
60                 density = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_DENSITY);
61             }
62             if ("KnownCG".equals(element)) {
63                 cg = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
64             }
65             if ("UseKnownCG".equals(element)) {
66                 boolean override = "1".equals(content);
67                 setOverride(component, override, mass, cg);
68             }
69         }
70         catch (NumberFormatException nfe) {
71             warnings.add("Could not convert " + element + " value of " + content + ".  It is expected to be a number.");
72         }
73     }
74
75     @Override
76     public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
77             throws SAXException {
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.
80          */
81         RocketComponent component = getComponent();
82         updateComponentMaterial(component, materialName, getMaterialType(), density);
83     }
84
85     /**
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.
88      *
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
94      */
95     public static void updateComponentMaterial(RocketComponent component, String definedMaterial, Material.Type type,
96                                                double density) {
97         if (definedMaterial != null) {
98             Material custom = createCustomMaterial(type, definedMaterial, density);
99             setMaterial(component, custom);
100         }
101     }
102
103     /**
104      * Override the mass and Cg of the component.
105      * 
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
110      */
111     public static void setOverride(RocketComponent component, boolean override, double mass, double cg) {
112         if (override) {
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);
118         }
119     }
120
121     /**
122      * Get the component this handler is working upon.
123      *
124      * @return a component
125      */
126     protected abstract C getComponent();
127
128     /**
129      * Get the required type of material for this component.
130      *
131      * @return the required material type
132      */
133     protected abstract Material.Type getMaterialType();
134
135     /**
136      * Some CG positions in Rocksim do not correspond to the CG position reference in OpenRocket.
137      * 
138      * @param theCG  the CG value to really use when overriding CG on the OpenRocket component
139      */
140     protected void setCG(double theCG) {
141         cg = theCG;
142     }
143     
144     /**
145      * Set the material name as specified in the Rocksim design file.
146      * 
147      * @param content  the material name
148      */
149     protected void setMaterialName(String content) {
150         materialName = content;
151     }
152     
153     /**
154      * Create a custom material based on the density.
155      *
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
159      * 
160      * @return a Material instance
161      */
162     public static Material createCustomMaterial(Material.Type type, String name, double density) {
163         return Material.newMaterial(type, "RS: " + name, density, true);
164     }
165
166     /**
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.
169      *
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())
172      */
173     private static void setMaterial(RocketComponent component, Material material) {
174         try {
175             final Method method = getMethod(component, "setMaterial", new Class[]{Material.class});
176             if (method != null) {
177                 method.invoke(component, material);
178             }
179         }
180         catch (IllegalAccessException ignored) {
181         }
182         catch (InvocationTargetException ignored) {
183         }
184     }
185
186     /**
187      * Find a method by name and argument list.
188      *
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
192      * 
193      * @return the Method instance, or null
194      */
195     private static Method getMethod(RocketComponent component, String name, Class[] args) {
196         Method method = null;
197         try {
198             method = component.getClass().getMethod(name, args);
199         }
200         catch (NoSuchMethodException ignored) {
201         }
202         return method;
203     }
204
205 }