/*
* RingHandler.java
*/
package net.sf.openrocket.file.rocksim.importt;
import java.util.HashMap;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.file.DocumentLoadingContext;
import net.sf.openrocket.file.rocksim.RocksimCommonConstants;
import net.sf.openrocket.file.simplesax.ElementHandler;
import net.sf.openrocket.file.simplesax.PlainTextHandler;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.Bulkhead;
import net.sf.openrocket.rocketcomponent.CenteringRing;
import net.sf.openrocket.rocketcomponent.EngineBlock;
import net.sf.openrocket.rocketcomponent.RingComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.TubeCoupler;
import org.xml.sax.SAXException;
/**
* A SAX handler for centering rings, tube couplers, and bulkheads.
*/
class RingHandler extends PositionDependentHandler<CenteringRing> {
/**
* The OpenRocket Ring.
*/
private final CenteringRing ring = new CenteringRing();
/**
* The parent component.
*/
private final RocketComponent parent;
/**
* The parsed Rocksim UsageCode.
*/
private int usageCode = 0;
/**
* Constructor.
*
* @param theParent the parent component
* @param warnings the warning set
* @throws IllegalArgumentException thrown if <code>c</code> is null
*/
public RingHandler(DocumentLoadingContext context, RocketComponent theParent, WarningSet warnings) throws IllegalArgumentException {
super(context);
if (theParent == null) {
throw new IllegalArgumentException("The parent of a ring may not be null.");
}
parent = theParent;
}
@Override
public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
return PlainTextHandler.INSTANCE;
}
@Override
public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
throws SAXException {
super.closeElement(element, attributes, content, warnings);
try {
if (RocksimCommonConstants.OD.equals(element)) {
ring.setOuterRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS);
}
if (RocksimCommonConstants.ID.equals(element)) {
ring.setInnerRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS);
}
if (RocksimCommonConstants.LEN.equals(element)) {
ring.setLength(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
}
if (RocksimCommonConstants.MATERIAL.equals(element)) {
setMaterialName(content);
}
if (RocksimCommonConstants.USAGE_CODE.equals(element)) {
usageCode = Integer.parseInt(content);
}
} catch (NumberFormatException nfe) {
warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
}
}
/**
* Get the ring component this handler is working upon.
*
* @return a component
*/
@Override
public CenteringRing getComponent() {
return ring;
}
/**
* This method adds the CenteringRing as a child of the parent rocket component.
*
* @param warnings the warning set
*/
public void asCenteringRing(WarningSet warnings) {
if (isCompatible(parent, CenteringRing.class, warnings)) {
parent.addChild(ring);
}
}
/**
* Convert the parsed Rocksim data values in this object to an instance of OpenRocket's Bulkhead.
* <p/>
* Side Effect Warning: This method adds the resulting Bulkhead as a child of the parent rocket component!
*
* @param warnings the warning set
*/
public void asBulkhead(WarningSet warnings) {
Bulkhead result = new Bulkhead();
copyValues(result);
if (isCompatible(parent, Bulkhead.class, warnings)) {
parent.addChild(result);
}
}
/**
* Convert the parsed Rocksim data values in this object to an instance of OpenRocket's TubeCoupler.
* <p/>
* Side Effect Warning: This method adds the resulting TubeCoupler as a child of the parent rocket component!
*
* @param warnings the warning set
*/
public void asTubeCoupler(WarningSet warnings) {
TubeCoupler result = new TubeCoupler();
copyValues(result);
if (isCompatible(parent, TubeCoupler.class, warnings)) {
parent.addChild(result);
}
}
/**
* Convert the parsed Rocksim data values in this object to an instance of OpenRocket's Engine Block.
* <p/>
* Side Effect Warning: This method adds the resulting EngineBlock as a child of the parent rocket component!
*
* @param warnings the warning set
*/
public void asEngineBlock(WarningSet warnings) {
EngineBlock result = new EngineBlock();
copyValues(result);
if (isCompatible(parent, EngineBlock.class, warnings)) {
parent.addChild(result);
}
}
/**
* Copy values from the base ring to the specific component.
*
* @param result the target to which ring values will be copied
*/
private void copyValues(RingComponent result) {
result.setOuterRadius(ring.getOuterRadius());
result.setInnerRadius(ring.getInnerRadius());
result.setLength(ring.getLength());
result.setName(ring.getName());
setOverride(result, ring.isOverrideSubcomponentsEnabled(), ring.getOverrideMass(), ring.getOverrideCGX());
result.setRelativePosition(ring.getRelativePosition());
result.setPositionValue(ring.getPositionValue());
result.setMaterial(ring.getMaterial());
result.setThickness(result.getThickness());
}
/**
* Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
* public in all components.
*
* @param position the OpenRocket position
*/
@Override
public void setRelativePosition(RocketComponent.Position position) {
ring.setRelativePosition(position);
}
@Override
public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws SAXException {
super.endHandler(element, attributes, content, warnings);
// The <Ring> XML element in Rocksim design file is used for many types of components, unfortunately.
// Additional subelements are used to indicate the type of the rocket component. When parsing using SAX
// this poses a problem because we can't "look ahead" to see what type is being represented at the start
// of parsing - something that would be nice to do so that we can instantiate the correct OR component
// at the start, then just call setters for the appropriate data.
// To overcome that, a CenteringRing is instantiated at the start of parsing, it's mutators are called,
// and then at the end (this method) converts the CenteringRing to a more appropriate type. CenteringRing
// is generic enough to support the representation of all similar types without loss of data.
//UsageCode
// 0 == Centering Ring
// 1 == Bulkhead
// 2 == Engine Block
// 3 == Sleeve
// 4 == Tube Coupler
if (usageCode == 1) {
//Bulkhead
asBulkhead(warnings);
} else if (usageCode == 2) {
asEngineBlock(warnings);
} else if (usageCode == 4) {
//TubeCoupler
asTubeCoupler(warnings);
} else {
//Default
asCenteringRing(warnings);
}
}
/**
* Get the required type of material for this component.
*
* @return BULK
*/
@Override
public Material.Type getMaterialType() {
return Material.Type.BULK;
}
}