1 package net.sf.openrocket.preset;
3 import java.io.ByteArrayOutputStream;
4 import java.io.DataOutputStream;
5 import java.security.MessageDigest;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.Comparator;
10 import java.util.HashMap;
11 import java.util.List;
14 import net.sf.openrocket.material.Material;
15 import net.sf.openrocket.motor.Manufacturer;
16 import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
17 import net.sf.openrocket.rocketcomponent.Transition.Shape;
18 import net.sf.openrocket.unit.UnitGroup;
19 import net.sf.openrocket.util.BugException;
20 import net.sf.openrocket.util.TextUtil;
24 * A model for a preset component.
26 * A preset component contains a component class type, manufacturer information,
27 * part information, and a method that returns a prototype of the preset component.
29 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
31 // FIXME - Implement clone.
32 public class ComponentPreset implements Comparable<ComponentPreset> {
34 private final TypedPropertyMap properties = new TypedPropertyMap();
36 private String digest = "";
39 BODY_TUBE(new TypedKey<?>[] {
40 ComponentPreset.MANUFACTURER,
41 ComponentPreset.PARTNO,
42 ComponentPreset.DESCRIPTION,
43 ComponentPreset.INNER_DIAMETER,
44 ComponentPreset.OUTER_DIAMETER,
45 ComponentPreset.LENGTH }),
47 NOSE_CONE(new TypedKey<?>[] {
48 ComponentPreset.MANUFACTURER,
49 ComponentPreset.PARTNO,
50 ComponentPreset.DESCRIPTION,
51 ComponentPreset.SHAPE,
52 ComponentPreset.AFT_OUTER_DIAMETER,
53 ComponentPreset.AFT_SHOULDER_DIAMETER,
54 ComponentPreset.AFT_SHOULDER_LENGTH,
55 ComponentPreset.LENGTH }),
57 TRANSITION(new TypedKey<?>[] {
58 ComponentPreset.MANUFACTURER,
59 ComponentPreset.PARTNO,
60 ComponentPreset.DESCRIPTION,
61 ComponentPreset.SHAPE,
62 ComponentPreset.FORE_OUTER_DIAMETER,
63 ComponentPreset.FORE_SHOULDER_DIAMETER,
64 ComponentPreset.FORE_SHOULDER_LENGTH,
65 ComponentPreset.AFT_OUTER_DIAMETER,
66 ComponentPreset.AFT_SHOULDER_DIAMETER,
67 ComponentPreset.AFT_SHOULDER_LENGTH,
68 ComponentPreset.LENGTH }),
70 TUBE_COUPLER(new TypedKey<?>[] {
71 ComponentPreset.MANUFACTURER,
72 ComponentPreset.PARTNO,
73 ComponentPreset.DESCRIPTION,
74 ComponentPreset.OUTER_DIAMETER,
75 ComponentPreset.INNER_DIAMETER,
76 ComponentPreset.LENGTH }),
78 BULK_HEAD(new TypedKey<?>[] {
79 ComponentPreset.MANUFACTURER,
80 ComponentPreset.PARTNO,
81 ComponentPreset.DESCRIPTION,
82 ComponentPreset.OUTER_DIAMETER,
83 ComponentPreset.LENGTH }),
85 CENTERING_RING(new TypedKey<?>[] {
86 ComponentPreset.MANUFACTURER,
87 ComponentPreset.PARTNO,
88 ComponentPreset.DESCRIPTION,
89 ComponentPreset.INNER_DIAMETER,
90 ComponentPreset.OUTER_DIAMETER,
91 ComponentPreset.LENGTH }),
93 ENGINE_BLOCK(new TypedKey<?>[] {
94 ComponentPreset.MANUFACTURER,
95 ComponentPreset.PARTNO,
96 ComponentPreset.DESCRIPTION,
97 ComponentPreset.INNER_DIAMETER,
98 ComponentPreset.OUTER_DIAMETER,
99 ComponentPreset.LENGTH }),
101 LAUNCH_LUG(new TypedKey<?>[] {
102 ComponentPreset.MANUFACTURER,
103 ComponentPreset.PARTNO,
104 ComponentPreset.DESCRIPTION,
105 ComponentPreset.INNER_DIAMETER,
106 ComponentPreset.OUTER_DIAMETER,
107 ComponentPreset.LENGTH }),
109 STREAMER(new TypedKey<?>[] {
110 ComponentPreset.MANUFACTURER,
111 ComponentPreset.PARTNO,
112 ComponentPreset.DESCRIPTION,
113 ComponentPreset.LENGTH,
114 ComponentPreset.WIDTH,
115 ComponentPreset.THICKNESS,
116 ComponentPreset.MATERIAL }),
118 PARACHUTE(new TypedKey<?>[] {
119 ComponentPreset.MANUFACTURER,
120 ComponentPreset.PARTNO,
121 ComponentPreset.DESCRIPTION,
122 ComponentPreset.DIAMETER,
123 ComponentPreset.SIDES,
124 ComponentPreset.LINE_COUNT,
125 ComponentPreset.LINE_LENGTH,
126 ComponentPreset.LINE_MATERIAL,
127 ComponentPreset.MATERIAL });
129 TypedKey<?>[] displayedColumns;
131 Type(TypedKey<?>[] displayedColumns) {
132 this.displayedColumns = displayedColumns;
135 public List<Type> getCompatibleTypes() {
136 return compatibleTypeMap.get(Type.this);
139 public TypedKey<?>[] getDisplayedColumns() {
140 return displayedColumns;
143 private static Map<Type, List<Type>> compatibleTypeMap = new HashMap<Type, List<Type>>();
146 compatibleTypeMap.put(BODY_TUBE, Arrays.asList(BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG));
147 compatibleTypeMap.put(TUBE_COUPLER, Arrays.asList(BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG));
148 compatibleTypeMap.put(LAUNCH_LUG, Arrays.asList(BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG));
149 compatibleTypeMap.put(CENTERING_RING, Arrays.asList(CENTERING_RING, ENGINE_BLOCK));
150 compatibleTypeMap.put(NOSE_CONE, Arrays.asList(NOSE_CONE, TRANSITION));
155 public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
156 public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo", String.class);
157 public final static TypedKey<String> DESCRIPTION = new TypedKey<String>("Description", String.class);
158 public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type", Type.class);
159 public final static TypedKey<Double> LENGTH = new TypedKey<Double>("Length", Double.class, UnitGroup.UNITS_LENGTH);
160 public final static TypedKey<Double> WIDTH = new TypedKey<Double>("Width", Double.class, UnitGroup.UNITS_LENGTH);
161 public final static TypedKey<Double> INNER_DIAMETER = new TypedKey<Double>("InnerDiameter", Double.class, UnitGroup.UNITS_LENGTH);
162 public final static TypedKey<Double> OUTER_DIAMETER = new TypedKey<Double>("OuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
163 public final static TypedKey<Double> FORE_SHOULDER_LENGTH = new TypedKey<Double>("ForeShoulderLength", Double.class, UnitGroup.UNITS_LENGTH);
164 public final static TypedKey<Double> FORE_SHOULDER_DIAMETER = new TypedKey<Double>("ForeShoulderDiameter", Double.class, UnitGroup.UNITS_LENGTH);
165 public final static TypedKey<Double> FORE_OUTER_DIAMETER = new TypedKey<Double>("ForeOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
166 public final static TypedKey<Double> AFT_SHOULDER_LENGTH = new TypedKey<Double>("AftShoulderLength", Double.class, UnitGroup.UNITS_LENGTH);
167 public final static TypedKey<Double> AFT_SHOULDER_DIAMETER = new TypedKey<Double>("AftShoulderDiameter", Double.class, UnitGroup.UNITS_LENGTH);
168 public final static TypedKey<Double> AFT_OUTER_DIAMETER = new TypedKey<Double>("AftOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
169 public final static TypedKey<Shape> SHAPE = new TypedKey<Shape>("Shape", Shape.class);
170 public final static TypedKey<Material> MATERIAL = new TypedKey<Material>("Material", Material.class);
171 public final static TypedKey<Finish> FINISH = new TypedKey<Finish>("Finish", Finish.class);
172 public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
173 public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
174 public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
175 public final static TypedKey<Double> DIAMETER = new TypedKey<Double>("Diameter", Double.class, UnitGroup.UNITS_LENGTH);
176 public final static TypedKey<Integer> SIDES = new TypedKey<Integer>("Sides", Integer.class);
177 public final static TypedKey<Integer> LINE_COUNT = new TypedKey<Integer>("LineCount", Integer.class);
178 public final static TypedKey<Double> LINE_LENGTH = new TypedKey<Double>("LineLength", Double.class, UnitGroup.UNITS_LENGTH);
179 public final static TypedKey<Material> LINE_MATERIAL = new TypedKey<Material>("LineMaterial", Material.class);
180 public final static TypedKey<byte[]> IMAGE = new TypedKey<byte[]>("Image", byte[].class);
182 public final static List<TypedKey<?>> ORDERED_KEY_LIST = Collections.unmodifiableList(Arrays.<TypedKey<?>> asList(
192 AFT_SHOULDER_DIAMETER,
194 FORE_SHOULDER_DIAMETER,
195 FORE_SHOULDER_LENGTH,
210 // package scope constructor to encourage use of factory.
215 * Convenience method to retrieve the Type of this ComponentPreset.
219 public Type getType() {
220 return properties.get(TYPE);
224 * Convenience method to retrieve the Manufacturer of this ComponentPreset.
227 public Manufacturer getManufacturer() {
228 return properties.get(MANUFACTURER);
232 * Convenience method to retrieve the PartNo of this ComponentPreset.
235 public String getPartNo() {
236 return properties.get(PARTNO);
239 public String getDigest() {
243 public boolean has(Object key) {
244 return properties.containsKey(key);
248 * Package scope so the ComponentPresetFactory can call it.
251 void putAll(TypedPropertyMap other) {
255 properties.putAll(other);
259 * Package scope so the ComponentPresetFactory can call it.
263 <T> void put(TypedKey<T> key, T value) {
264 properties.put(key, value);
267 public <T> T get(TypedKey<T> key) {
268 T value = properties.get(key);
270 throw new BugException("Preset did not contain key " + key + " " + properties.toString());
276 public int compareTo(ComponentPreset p2) {
277 int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
278 if (manuCompare != 0)
281 int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
282 return partNoCompare;
286 public String toString() {
290 public String preferenceKey() {
291 return get(MANUFACTURER).toString() + "|" + get(PARTNO);
295 public boolean equals(final Object o) {
299 if (o == null || getClass() != o.getClass()) {
303 ComponentPreset that = (ComponentPreset) o;
305 if (digest != null ? !digest.equals(that.digest) : that.digest != null) {
313 public int hashCode() {
314 return digest != null ? digest.hashCode() : 0;
318 * Package scope so the factory can call it.
320 void computeDigest() {
323 ByteArrayOutputStream bos = new ByteArrayOutputStream();
324 DataOutputStream os = new DataOutputStream(bos);
326 List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>(properties.keySet());
328 Collections.sort(keys, new Comparator<TypedKey<?>>() {
330 public int compare(TypedKey<?> a, TypedKey<?> b) {
331 return a.getName().compareTo(b.getName());
335 for (TypedKey<?> key : keys) {
337 Object value = properties.get(key);
339 os.writeBytes(key.getName());
341 if (key.getType() == Double.class) {
342 Double d = (Double) value;
344 } else if (key.getType() == String.class) {
345 String s = (String) value;
347 } else if (key.getType() == Manufacturer.class) {
348 String s = ((Manufacturer) value).getSimpleName();
350 } else if (key.getType() == Finish.class) {
351 String s = ((Finish) value).name();
353 } else if (key.getType() == Type.class) {
354 String s = ((Type) value).name();
356 } else if (key.getType() == Boolean.class) {
357 Boolean b = (Boolean) value;
359 } else if (key.getType() == Material.class) {
360 double d = ((Material) value).getDensity();
362 } else if (key.getType() == Shape.class) {
363 // this is ugly to use the ordinal but what else?
364 int i = ((Shape) value).ordinal();
370 MessageDigest md5 = MessageDigest.getInstance("MD5");
371 digest = TextUtil.hexString(md5.digest(bos.toByteArray()));
372 } catch (Exception e) {
374 throw new BugException(e);