2 * MassObjectHandler.java
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.ElementHandler;
10 import net.sf.openrocket.file.simplesax.PlainTextHandler;
11 import net.sf.openrocket.material.Material;
12 import net.sf.openrocket.rocketcomponent.Coaxial;
13 import net.sf.openrocket.rocketcomponent.MassComponent;
14 import net.sf.openrocket.rocketcomponent.MassObject;
15 import net.sf.openrocket.rocketcomponent.RocketComponent;
16 import net.sf.openrocket.rocketcomponent.ShockCord;
17 import org.xml.sax.SAXException;
19 import java.util.HashMap;
22 * A SAX handler for Rocksim's MassObject XML type.
24 class MassObjectHandler extends PositionDependentHandler<MassObject> {
27 * The Rocksim Mass length fudge factor. Rocksim completely exaggerates the length of a mass object to the point
28 * that it looks ridiculous in OpenRocket. This fudge factor is here merely to get the typical mass object to
29 * render in the OpenRocket UI with it's bounds mostly inside it's parent. The odd thing about it is that Rocksim
30 * does not expose the length of a mass object in the UI and actually treats mass objects as point objects - not 3
31 * or even 2 dimensional.
33 public static final int MASS_LEN_FUDGE_FACTOR = 100;
36 * The OpenRocket MassComponent - counterpart to the RS MassObject.
38 private final MassComponent mass;
41 * Reference to answer for getComponent().
43 private MassObject current;
48 private RocketComponent parent;
51 * 0 == General, 1 == Shock Cord
53 private int typeCode = 0;
58 * @param c the parent component
59 * @param warnings the warning set
61 * @throws IllegalArgumentException thrown if <code>c</code> is null
63 public MassObjectHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException {
65 throw new IllegalArgumentException("The parent component of a mass component may not be null.");
67 mass = new MassComponent();
73 public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
74 return PlainTextHandler.INSTANCE;
78 public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
80 super.closeElement(element, attributes, content, warnings);
82 if (RocksimCommonConstants.LEN.equals(element)) {
83 mass.setLength(Double.parseDouble(content) / (RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH));
85 if (RocksimCommonConstants.KNOWN_MASS.equals(element)) {
86 mass.setComponentMass(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS);
88 if (RocksimCommonConstants.KNOWN_CG.equals(element)) {
89 //Setting the CG of the Mass Object to 0 is important because of the different ways that Rocksim and
90 //OpenRocket treat mass objects. Rocksim treats them as points (even though the data file contains a
91 //length) and because Rocksim sets the CG of the mass object to really be relative to the front of
92 //the parent. But that value is already assumed in the position and position value for the component.
93 //Thus it needs to be set to 0 to say that the mass object's CG is at the point of the mass object.
96 if (RocksimCommonConstants.TYPE_CODE.equals(element)) {
97 typeCode = Integer.parseInt(content);
99 if (RocksimCommonConstants.MATERIAL.equals(element)) {
100 setMaterialName(content);
103 catch (NumberFormatException nfe) {
104 warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
109 public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws
111 if (inferAsShockCord(typeCode, warnings)) { //Shock Cord
112 mapMassObjectAsShockCord(element, attributes, content, warnings);
114 else { // typeCode == 0 General Mass Object
115 if (isCompatible(parent, MassComponent.class, warnings)) {
116 parent.addChild(mass);
118 super.endHandler(element, attributes, content, warnings);
123 * Rocksim does not have a separate entity for Shock Cords. It has to be inferred. Sometimes the typeCode
124 * indicates it's a shock cord, but most times it does not. This is due to bugs in the Rocksim Component and
125 * Material databases. Try to infer a shock cord based on it's length and it's material type. It's somewhat
126 * arbitrary, but if the mass object's length is more than twice the length of it's parent component and it's a LINE
127 * material, then assume a shock cord.
129 * @param theTypeCode the code from the RKT XML file
131 * @return true if we think it's a shock cord
133 private boolean inferAsShockCord(int theTypeCode, WarningSet warnings) {
134 return (theTypeCode == 1 || (mass.getLength() >= 2 * parent.getLength() && RocksimDensityType.ROCKSIM_LINE
135 .equals(getDensityType()))) && isCompatible(parent, ShockCord.class, warnings, true);
139 * If it appears that the mass object is a shock cord, then create an OR shock cord instance.
141 * @param element the element name
142 * @param attributes the attributes
143 * @param content the content of the element
144 * @param warnings the warning set to store warnings in.
146 * @throws org.xml.sax.SAXException not thrown
148 private void mapMassObjectAsShockCord(final String element, final HashMap<String, String> attributes,
149 final String content, final WarningSet warnings) throws SAXException {
150 ShockCord cord = new ShockCord();
152 if (isCompatible(parent, ShockCord.class, warnings)) {
153 parent.addChild(cord);
155 super.endHandler(element, attributes, content, warnings);
156 cord.setName(mass.getName());
158 setOverride(cord, mass.isMassOverridden(), mass.getOverrideMass(), mass.getOverrideCGX());
160 cord.setRadialDirection(mass.getRadialDirection());
161 cord.setRadialPosition(mass.getRadialPosition());
162 cord.setRadius(mass.getRadius());
164 //Rocksim does not distinguish between total length of the cord and the packed length. Fudge the
165 //packed length and set the real length.
166 cord.setCordLength(mass.getLength());
167 cord.setLength(cord.getCordLength() / MASS_LEN_FUDGE_FACTOR);
168 if (parent instanceof Coaxial) {
169 Coaxial parentCoaxial = (Coaxial) parent;
170 cord.setRadius(parentCoaxial.getInnerRadius());
175 * Get the component this handler is working upon. This changes depending upon the type of mass object.
177 * @return a component
180 public MassObject getComponent() {
185 * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
186 * public in all components.
188 * @param position the OpenRocket position
190 public void setRelativePosition(RocketComponent.Position position) {
191 current.setRelativePosition(position);
195 * Get the required type of material for this component. Does not apply to MassComponents, but does apply to Shock
201 public Material.Type getMaterialType() {
202 return Material.Type.LINE;