package net.sf.openrocket.preset;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.motor.Manufacturer;
+import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
+import net.sf.openrocket.rocketcomponent.Transition.Shape;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.BugException;
+import net.sf.openrocket.util.TextUtil;
+
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import net.sf.openrocket.material.Material;
-import net.sf.openrocket.motor.Manufacturer;
-import net.sf.openrocket.rocketcomponent.BodyTube;
-import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
-import net.sf.openrocket.unit.UnitGroup;
-import net.sf.openrocket.util.BugException;
-import net.sf.openrocket.util.TextUtil;
-
/**
* A model for a preset component.
* <p>
* A preset component contains a component class type, manufacturer information,
* part information, and a method that returns a prototype of the preset component.
- *
+ *
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
// FIXME - Implement clone.
private String digest = "";
public enum Type {
- BODY_TUBE,
- NOSE_CONE;
-
- Type[] compatibleTypes;
-
- Type () {
- compatibleTypes = new Type[1];
- compatibleTypes[0] = this;
+ BODY_TUBE( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.INNER_DIAMETER,
+ ComponentPreset.OUTER_DIAMETER,
+ ComponentPreset.LENGTH} ),
+
+ NOSE_CONE( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.SHAPE,
+ ComponentPreset.AFT_OUTER_DIAMETER,
+ ComponentPreset.AFT_SHOULDER_DIAMETER,
+ ComponentPreset.AFT_SHOULDER_LENGTH,
+ ComponentPreset.LENGTH} ),
+
+ TRANSITION( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.SHAPE,
+ ComponentPreset.FORE_OUTER_DIAMETER,
+ ComponentPreset.FORE_SHOULDER_DIAMETER,
+ ComponentPreset.FORE_SHOULDER_LENGTH,
+ ComponentPreset.AFT_OUTER_DIAMETER,
+ ComponentPreset.AFT_SHOULDER_DIAMETER,
+ ComponentPreset.AFT_SHOULDER_LENGTH,
+ ComponentPreset.LENGTH} ),
+
+ TUBE_COUPLER( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.OUTER_DIAMETER,
+ ComponentPreset.INNER_DIAMETER,
+ ComponentPreset.LENGTH} ),
+
+ BULK_HEAD( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.OUTER_DIAMETER,
+ ComponentPreset.LENGTH} ),
+
+ CENTERING_RING( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.INNER_DIAMETER,
+ ComponentPreset.OUTER_DIAMETER,
+ ComponentPreset.LENGTH} ),
+
+ ENGINE_BLOCK( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.INNER_DIAMETER,
+ ComponentPreset.OUTER_DIAMETER,
+ ComponentPreset.LENGTH} ),
+
+ LAUNCH_LUG( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.INNER_DIAMETER,
+ ComponentPreset.OUTER_DIAMETER,
+ ComponentPreset.LENGTH} ),
+
+ STREAMER( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.LENGTH,
+ ComponentPreset.WIDTH,
+ ComponentPreset.THICKNESS,
+ ComponentPreset.MATERIAL} ),
+
+ PARACHUTE( new TypedKey<?>[] {
+ ComponentPreset.MANUFACTURER,
+ ComponentPreset.PARTNO,
+ ComponentPreset.DESCRIPTION,
+ ComponentPreset.DIAMETER,
+ ComponentPreset.SIDES,
+ ComponentPreset.LINE_COUNT,
+ ComponentPreset.LINE_LENGTH,
+ ComponentPreset.LINE_MATERIAL,
+ ComponentPreset.MATERIAL} );
+
+ TypedKey<?>[] displayedColumns;
+
+ Type( TypedKey<?>[] displayedColumns) {
+ this.displayedColumns = displayedColumns;
}
- Type( Type ... t ) {
+ public List<Type> getCompatibleTypes() {
+ return compatibleTypeMap.get(Type.this);
+ }
- compatibleTypes = new Type[t.length+1];
- compatibleTypes[0] = this;
- for( int i=0; i<t.length; i++ ) {
- compatibleTypes[i+1] = t[i];
- }
+ public TypedKey<?>[] getDisplayedColumns() {
+ return displayedColumns;
}
- public Type[] getCompatibleTypes() {
- return compatibleTypes;
+ private static Map<Type,List<Type>> compatibleTypeMap = new HashMap<Type,List<Type>>();
+
+ static {
+ compatibleTypeMap.put( BODY_TUBE, Arrays.asList( BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG ) );
+ compatibleTypeMap.put( TUBE_COUPLER, Arrays.asList( BODY_TUBE,TUBE_COUPLER, LAUNCH_LUG ) );
+ compatibleTypeMap.put( LAUNCH_LUG, Arrays.asList( BODY_TUBE,TUBE_COUPLER, LAUNCH_LUG ) );
+ compatibleTypeMap.put( CENTERING_RING, Arrays.asList( CENTERING_RING, ENGINE_BLOCK ) );
+ compatibleTypeMap.put( NOSE_CONE, Arrays.asList( NOSE_CONE, TRANSITION));
}
}
public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo",String.class);
+ public final static TypedKey<String> DESCRIPTION = new TypedKey<String>("Description", String.class);
public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type",Type.class);
public final static TypedKey<Double> LENGTH = new TypedKey<Double>("Length", Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Double> WIDTH = new TypedKey<Double>("Width", Double.class, UnitGroup.UNITS_LENGTH);
public final static TypedKey<Double> INNER_DIAMETER = new TypedKey<Double>("InnerDiameter", Double.class, UnitGroup.UNITS_LENGTH);
public final static TypedKey<Double> OUTER_DIAMETER = new TypedKey<Double>("OuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Double> FORE_SHOULDER_LENGTH = new TypedKey<Double>("ForeShoulderLength",Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Double> FORE_SHOULDER_DIAMETER = new TypedKey<Double>("ForeShoulderDiameter",Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Double> FORE_OUTER_DIAMETER = new TypedKey<Double>("ForeOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Double> AFT_SHOULDER_LENGTH = new TypedKey<Double>("AftShoulderLength",Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Double> AFT_SHOULDER_DIAMETER = new TypedKey<Double>("AftShoulderDiameter",Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Double> AFT_OUTER_DIAMETER = new TypedKey<Double>("AftOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Shape> SHAPE = new TypedKey<Shape>("Shape", Shape.class);
public final static TypedKey<Material> MATERIAL = new TypedKey<Material>("Material", Material.class);
public final static TypedKey<Finish> FINISH = new TypedKey<Finish>("Finish", Finish.class);
public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
-
- public final static Map<String, TypedKey<?>> keyMap = new HashMap<String, TypedKey<?>>();
- static {
- keyMap.put(MANUFACTURER.getName(), MANUFACTURER);
- keyMap.put(PARTNO.getName(), PARTNO);
- keyMap.put(TYPE.getName(), TYPE);
- keyMap.put(LENGTH.getName(), LENGTH);
- keyMap.put(INNER_DIAMETER.getName(), INNER_DIAMETER);
- keyMap.put(OUTER_DIAMETER.getName(), OUTER_DIAMETER);
- keyMap.put(MATERIAL.getName(), MATERIAL);
- keyMap.put(FINISH.getName(), FINISH);
- keyMap.put(THICKNESS.getName(), THICKNESS);
- keyMap.put(FILLED.getName(), FILLED);
- keyMap.put(MASS.getName(), MASS);
- }
-
- public static ComponentPreset create( TypedPropertyMap props ) throws InvalidComponentPresetException {
-
- ComponentPreset preset = new ComponentPreset();
- // First do validation.
- if ( !props.containsKey(TYPE)) {
- throw new InvalidComponentPresetException("No Type specified " + props.toString() );
- }
-
- if (!props.containsKey(MANUFACTURER)) {
- throw new InvalidComponentPresetException("No Manufacturer specified " + props.toString() );
- }
-
- if (!props.containsKey(PARTNO)) {
- throw new InvalidComponentPresetException("No PartNo specified " + props.toString() );
- }
-
- preset.properties.putAll(props);
-
- // Should check for various bits of each of the types.
- Type t = props.get(TYPE);
- switch ( t ) {
- case BODY_TUBE: {
-
- if ( !props.containsKey(LENGTH) ) {
- throw new InvalidComponentPresetException( "No Length specified for body tube preset " + props.toString());
- }
-
- BodyTube bt = new BodyTube();
-
- bt.setLength(props.get(LENGTH));
-
- // Need to verify contains 2 of OD, thickness, ID. Compute the third.
- boolean hasOd = props.containsKey(OUTER_DIAMETER);
- boolean hasId = props.containsKey(INNER_DIAMETER);
- boolean hasThickness = props.containsKey(THICKNESS);
-
- if ( hasOd ) {
- double outerRadius = props.get(OUTER_DIAMETER)/2.0;
- double thickness = 0;
- bt.setOuterRadius( outerRadius );
- if ( hasId ) {
- thickness = outerRadius - props.get(INNER_DIAMETER)/2.0;
- } else if ( hasThickness ) {
- thickness = props.get(THICKNESS);
- } else {
- throw new InvalidComponentPresetException("Body tube preset underspecified " + props.toString());
- }
- bt.setThickness( thickness );
- } else {
- if ( ! hasId && ! hasThickness ) {
- throw new InvalidComponentPresetException("Body tube preset underspecified " + props.toString());
- }
- double innerRadius = props.get(INNER_DIAMETER)/2.0;
- double thickness = props.get(THICKNESS);
- bt.setOuterRadius(innerRadius + thickness);
- bt.setThickness(thickness);
- }
-
- preset.properties.put(OUTER_DIAMETER, bt.getOuterRadius() *2.0);
- preset.properties.put(INNER_DIAMETER, bt.getInnerRadius() *2.0);
- preset.properties.put(THICKNESS, bt.getThickness());
-
- // Need to translate Mass to Density.
- if ( props.containsKey(MASS) ) {
- String materialName = "TubeCustom";
- if ( props.containsKey(MATERIAL) ) {
- materialName = props.get(MATERIAL).getName();
- }
- Material m = Material.newMaterial(Material.Type.BULK, materialName, props.get(MASS)/bt.getComponentVolume(), false);
- preset.properties.put(MATERIAL, m);
- }
-
- break;
- }
- case NOSE_CONE: {
- break;
- }
- }
-
- preset.computeDigest();
-
- return preset;
-
- }
-
- // Private constructor to encourage use of factory.
- private ComponentPreset() {
+ public final static TypedKey<Double> DIAMETER = new TypedKey<Double>("Diameter", Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Integer> SIDES = new TypedKey<Integer>("Sides", Integer.class);
+ public final static TypedKey<Integer> LINE_COUNT = new TypedKey<Integer>("LineCount", Integer.class);
+ public final static TypedKey<Double> LINE_LENGTH = new TypedKey<Double>("LineLength", Double.class, UnitGroup.UNITS_LENGTH);
+ public final static TypedKey<Material> LINE_MATERIAL = new TypedKey<Material>("LineMaterial", Material.class);
+ public final static TypedKey<byte[]> IMAGE = new TypedKey<byte[]>("Image", byte[].class);
+
+ public final static List<TypedKey<?>> orderedKeyList = Arrays.<TypedKey<?>>asList(
+ MANUFACTURER,
+ PARTNO,
+ DESCRIPTION,
+ OUTER_DIAMETER,
+ FORE_OUTER_DIAMETER,
+ AFT_OUTER_DIAMETER,
+ INNER_DIAMETER,
+ LENGTH,
+ WIDTH,
+ AFT_SHOULDER_DIAMETER,
+ AFT_SHOULDER_LENGTH,
+ FORE_SHOULDER_DIAMETER,
+ FORE_SHOULDER_LENGTH,
+ SHAPE,
+ THICKNESS,
+ FILLED,
+ DIAMETER,
+ SIDES,
+ LINE_COUNT,
+ LINE_LENGTH,
+ LINE_MATERIAL,
+ MASS,
+ FINISH,
+ MATERIAL
+ );
+
+
+ // package scope constructor to encourage use of factory.
+ ComponentPreset() {
}
/**
* Convenience method to retrieve the Type of this ComponentPreset.
- *
+ *
* @return
*/
public Type getType() {
return properties.containsKey(key);
}
+ /**
+ * Package scope so the ComponentPresetFactory can call it.
+ * @param other
+ */
+ void putAll(TypedPropertyMap other) {
+ if (other == null) {
+ return;
+ }
+ properties.putAll(other);
+ }
+
+ /**
+ * Package scope so the ComponentPresetFactory can call it.
+ * @param key
+ * @param value
+ */
+ <T> void put( TypedKey<T> key, T value ) {
+ properties.put(key, value);
+ }
+
public <T> T get(TypedKey<T> key) {
T value = properties.get(key);
if (value == null) {
return get(MANUFACTURER).toString() + "|" + get(PARTNO);
}
- private void computeDigest() {
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ComponentPreset that = (ComponentPreset) o;
+
+ if (digest != null ? !digest.equals(that.digest) : that.digest != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return digest != null ? digest.hashCode() : 0;
+ }
+
+ /**
+ * Package scope so the factory can call it.
+ */
+ void computeDigest() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
} else if ( key.getType() == Material.class ) {
double d = ((Material)value).getDensity();
os.writeDouble(d);
+ } else if ( key.getType() == Shape.class ) {
+ // this is ugly to use the ordinal but what else?
+ int i = ((Shape)value).ordinal();
+ os.writeInt(i);
}
}
digest = TextUtil.hexString(md5.digest( bos.toByteArray() ));
}
catch ( Exception e ) {
+ e.printStackTrace();
throw new BugException(e);
}
}