4 package net.sf.openrocket.file.rocksim.importt;
6 import net.sf.openrocket.aerodynamics.WarningSet;
7 import net.sf.openrocket.file.rocksim.RocksimCommonConstants;
8 import net.sf.openrocket.file.rocksim.RocksimDensityType;
9 import net.sf.openrocket.file.simplesax.AbstractElementHandler;
10 import net.sf.openrocket.material.Material;
11 import net.sf.openrocket.rocketcomponent.RocketComponent;
12 import org.xml.sax.SAXException;
14 import java.lang.reflect.InvocationTargetException;
15 import java.lang.reflect.Method;
16 import java.util.HashMap;
19 * An abstract base class that handles common parsing. All Rocksim component handlers are subclassed from here.
21 * @param <C> the specific RocketComponent subtype for which the concrete handler can create
23 public abstract class BaseHandler<C extends RocketComponent> extends AbstractElementHandler {
26 * Prepend rocksim materials.
28 public static final String ROCKSIM_MATERIAL_PREFIX = "RS: ";
30 * The overridden mass.
32 private Double mass = 0d;
36 private Double cg = 0d;
38 * The density of the material in the component.
40 private Double density = 0d;
42 * The internal Rocksim density type.
44 private RocksimDensityType densityType = RocksimDensityType.ROCKSIM_BULK;
49 private String materialName = "";
52 * The SAX method called when the closing element tag is reached.
54 * @param element the element name.
55 * @param attributes attributes of the element.
56 * @param content the textual content of the element.
57 * @param warnings the warning set to store warnings in.
59 * @throws SAXException
63 public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
65 final C component = getComponent();
67 if (RocksimCommonConstants.NAME.equals(element)) {
68 component.setName(content);
70 if (RocksimCommonConstants.KNOWN_MASS.equals(element)) {
71 mass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS);
73 if (RocksimCommonConstants.DENSITY.equals(element)) {
74 density = Math.max(0d, Double.parseDouble(content));
76 if (RocksimCommonConstants.KNOWN_CG.equals(element)) {
77 cg = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
79 if (RocksimCommonConstants.USE_KNOWN_CG.equals(element)) { //Rocksim sets UseKnownCG to true to control the override of both cg and mass
80 boolean override = "1".equals(content);
81 setOverride(component, override, mass, cg);
83 if (RocksimCommonConstants.DENSITY_TYPE.equals(element)) {
84 densityType = RocksimDensityType.fromCode(Integer.parseInt(content));
87 catch (NumberFormatException nfe) {
88 warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
96 public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
98 /* Because of the order of XML elements in Rocksim, not all information is known at the time it really needs
99 to be acted upon. So we keep temporary instance variables to be used here at the end of the parsing.
101 density = computeDensity(densityType, density);
102 RocketComponent component = getComponent();
103 updateComponentMaterial(component, materialName, getMaterialType(), density);
107 * Compute the density. Rocksim does strange things with densities. For some streamer material it's in cubic,
108 * rather than square, units. In those cases it needs to be converted to an appropriate SURFACE material density.
109 * Some G10 fiberglass materials are in cubic units, other G10 fiberglass is in square units. And due to a Rocksim
110 * bug, some densities are 0 when they clearly should not be.
112 * This may be overridden for specific component density computations.
114 * @param type the rocksim density
115 * @param rawDensity the density as specified in the Rocksim design file
117 * @return a value in OpenRocket SURFACE density units
119 protected double computeDensity(RocksimDensityType type, double rawDensity) {
120 return rawDensity / type.asOpenRocket();
124 * If the Rocksim component does not override the mass, then create a Material based upon the density defined for
125 * that component. This *should* result in a consistent representation of Cg between Rocksim and OpenRocket.
127 * @param component the component
128 * @param type the type of the material
129 * @param density the density in g/cm^3
130 * @param definedMaterial the material that is currently defined on the component; used only to get the name as it
133 public static void updateComponentMaterial(RocketComponent component, String definedMaterial, Material.Type type,
135 if (definedMaterial != null) {
136 Material custom = createCustomMaterial(type, definedMaterial, density);
137 setMaterial(component, custom);
142 * Override the mass and Cg of the component.
144 * @param component the component
145 * @param override true if any override should happen
146 * @param mass the override mass
147 * @param cg the override cg
149 public static void setOverride(RocketComponent component, boolean override, double mass, double cg) {
151 component.setCGOverridden(override);
152 component.setMassOverridden(override);
153 component.setOverrideSubcomponents(false); //Rocksim does not support this type of override
154 component.setOverrideMass(mass);
155 component.setOverrideCGX(cg);
160 * Get the component this handler is working upon.
162 * @return a component
164 protected abstract C getComponent();
167 * Get the required type of material for this component.
169 * @return the required material type
171 protected abstract Material.Type getMaterialType();
174 * Some CG positions in Rocksim do not correspond to the CG position reference in OpenRocket.
176 * @param theCG the CG value to really use when overriding CG on the OpenRocket component
178 protected void setCG(double theCG) {
183 * Set the material name as specified in the Rocksim design file.
185 * @param content the material name
187 protected void setMaterialName(String content) {
188 materialName = content;
192 * Get the Rocksim enum of the component's density type.
194 * @return a Rocksim density type
196 protected RocksimDensityType getDensityType() {
201 * Add child to parent only if the child is compatible. Otherwise add to warning set.
203 * @param parent the parent component
204 * @param child the child component
205 * @param warnings the warning set
207 * @return true if the child is compatible with parent
209 protected static boolean isCompatible(RocketComponent parent, Class<? extends RocketComponent> child, WarningSet warnings) {
210 return isCompatible(parent, child, warnings, false);
214 * Add child to parent only if the child is compatible. Otherwise add to warning set.
216 * @param parent the parent component
217 * @param child the child component
218 * @param warnings the warning set
219 * @param suppressWarnings suppress warnings, just return the boolean
221 * @return true if the child is compatible with parent
223 protected static boolean isCompatible(RocketComponent parent, Class<? extends RocketComponent> child,
225 boolean suppressWarnings) {
226 if (!parent.isCompatible(child)) {
227 if (!suppressWarnings) {
228 warnings.add(child.getName() + " can not be attached to "
229 + parent.getComponentName() + ", ignoring component.");
239 * Create a custom material based on the density. The name of the material is prepended with 'RS: ' to indicate it
240 * came from a RockSim material.
242 * @param type the type of the material
243 * @param name the name of the component
244 * @param density the density
246 * @return a Material instance
248 public static Material createCustomMaterial(Material.Type type, String name, double density) {
249 return Material.newUserMaterial(type, ROCKSIM_MATERIAL_PREFIX + name, density);
253 * Set the material onto an instance of RocketComponent. This is done because only some subtypes of RocketComponent
254 * have the setMaterial method. Unfortunately the supertype cannot be used.
256 * @param component the component who's material is to be set
257 * @param material the material to be set on the component (defined by getComponent())
259 private static void setMaterial(RocketComponent component, Material material) {
261 final Method method = getMethod(component, "setMaterial", new Class[]{Material.class});
262 if (method != null) {
263 method.invoke(component, material);
266 catch (IllegalAccessException ignored) {
268 catch (InvocationTargetException ignored) {
273 * Find a method by name and argument list.
275 * @param component the component who's material is to be set
276 * @param name the method name
277 * @param args the class types of the parameters
279 * @return the Method instance, or null
281 private static Method getMethod(RocketComponent component, String name, Class[] args) {
282 Method method = null;
284 method = component.getClass().getMethod(name, args);
286 catch (NoSuchMethodException ignored) {