1 package net.sf.openrocket.preset;
3 import net.sf.openrocket.material.Material;
4 import net.sf.openrocket.motor.Manufacturer;
5 import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
6 import net.sf.openrocket.rocketcomponent.Transition.Shape;
7 import net.sf.openrocket.unit.UnitGroup;
8 import net.sf.openrocket.util.BugException;
9 import net.sf.openrocket.util.TextUtil;
11 import java.io.ByteArrayOutputStream;
12 import java.io.DataOutputStream;
13 import java.security.MessageDigest;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.HashMap;
19 import java.util.List;
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 boolean favorite = false;
37 private String digest = "";
40 BODY_TUBE( new TypedKey<?>[] {
41 ComponentPreset.MANUFACTURER,
42 ComponentPreset.PARTNO,
43 ComponentPreset.DESCRIPTION,
44 ComponentPreset.INNER_DIAMETER,
45 ComponentPreset.OUTER_DIAMETER,
46 ComponentPreset.LENGTH} ),
48 NOSE_CONE( new TypedKey<?>[] {
49 ComponentPreset.MANUFACTURER,
50 ComponentPreset.PARTNO,
51 ComponentPreset.DESCRIPTION,
52 ComponentPreset.SHAPE,
53 ComponentPreset.AFT_OUTER_DIAMETER,
54 ComponentPreset.AFT_SHOULDER_DIAMETER,
55 ComponentPreset.AFT_SHOULDER_LENGTH,
56 ComponentPreset.LENGTH} ),
58 TRANSITION( new TypedKey<?>[] {
59 ComponentPreset.MANUFACTURER,
60 ComponentPreset.PARTNO,
61 ComponentPreset.DESCRIPTION,
62 ComponentPreset.SHAPE,
63 ComponentPreset.FORE_OUTER_DIAMETER,
64 ComponentPreset.FORE_SHOULDER_DIAMETER,
65 ComponentPreset.FORE_SHOULDER_LENGTH,
66 ComponentPreset.AFT_OUTER_DIAMETER,
67 ComponentPreset.AFT_SHOULDER_DIAMETER,
68 ComponentPreset.AFT_SHOULDER_LENGTH,
69 ComponentPreset.LENGTH} ),
71 TUBE_COUPLER( new TypedKey<?>[] {
72 ComponentPreset.MANUFACTURER,
73 ComponentPreset.PARTNO,
74 ComponentPreset.DESCRIPTION,
75 ComponentPreset.OUTER_DIAMETER,
76 ComponentPreset.INNER_DIAMETER,
77 ComponentPreset.LENGTH} ),
79 BULK_HEAD( new TypedKey<?>[] {
80 ComponentPreset.MANUFACTURER,
81 ComponentPreset.PARTNO,
82 ComponentPreset.DESCRIPTION,
83 ComponentPreset.OUTER_DIAMETER,
84 ComponentPreset.LENGTH} ),
86 CENTERING_RING( new TypedKey<?>[] {
87 ComponentPreset.MANUFACTURER,
88 ComponentPreset.PARTNO,
89 ComponentPreset.DESCRIPTION,
90 ComponentPreset.INNER_DIAMETER,
91 ComponentPreset.OUTER_DIAMETER,
92 ComponentPreset.LENGTH} ),
94 ENGINE_BLOCK( new TypedKey<?>[] {
95 ComponentPreset.MANUFACTURER,
96 ComponentPreset.PARTNO,
97 ComponentPreset.DESCRIPTION,
98 ComponentPreset.INNER_DIAMETER,
99 ComponentPreset.OUTER_DIAMETER,
100 ComponentPreset.LENGTH} ),
102 LAUNCH_LUG( new TypedKey<?>[] {
103 ComponentPreset.MANUFACTURER,
104 ComponentPreset.PARTNO,
105 ComponentPreset.DESCRIPTION,
106 ComponentPreset.INNER_DIAMETER,
107 ComponentPreset.OUTER_DIAMETER,
108 ComponentPreset.LENGTH} ),
110 STREAMER( new TypedKey<?>[] {
111 ComponentPreset.MANUFACTURER,
112 ComponentPreset.PARTNO,
113 ComponentPreset.DESCRIPTION,
114 ComponentPreset.LENGTH,
115 ComponentPreset.WIDTH,
116 ComponentPreset.THICKNESS,
117 ComponentPreset.MATERIAL} ),
119 PARACHUTE( new TypedKey<?>[] {
120 ComponentPreset.MANUFACTURER,
121 ComponentPreset.PARTNO,
122 ComponentPreset.DESCRIPTION,
123 ComponentPreset.DIAMETER,
124 ComponentPreset.SIDES,
125 ComponentPreset.LINE_COUNT,
126 ComponentPreset.LINE_LENGTH,
127 ComponentPreset.LINE_MATERIAL,
128 ComponentPreset.MATERIAL} );
130 TypedKey<?>[] displayedColumns;
132 Type( TypedKey<?>[] displayedColumns) {
133 this.displayedColumns = displayedColumns;
136 public List<Type> getCompatibleTypes() {
137 return compatibleTypeMap.get(Type.this);
140 public TypedKey<?>[] getDisplayedColumns() {
141 return displayedColumns;
144 private static Map<Type,List<Type>> compatibleTypeMap = new HashMap<Type,List<Type>>();
147 compatibleTypeMap.put( BODY_TUBE, Arrays.asList( BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG ) );
148 compatibleTypeMap.put( TUBE_COUPLER, Arrays.asList( BODY_TUBE,TUBE_COUPLER, LAUNCH_LUG ) );
149 compatibleTypeMap.put( LAUNCH_LUG, Arrays.asList( BODY_TUBE,TUBE_COUPLER, LAUNCH_LUG ) );
150 compatibleTypeMap.put( CENTERING_RING, Arrays.asList( CENTERING_RING, ENGINE_BLOCK ) );
151 compatibleTypeMap.put( NOSE_CONE, Arrays.asList( NOSE_CONE, TRANSITION));
156 public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
157 public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo",String.class);
158 public final static TypedKey<String> DESCRIPTION = new TypedKey<String>("Description", String.class);
159 public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type",Type.class);
160 public final static TypedKey<Double> LENGTH = new TypedKey<Double>("Length", Double.class, UnitGroup.UNITS_LENGTH);
161 public final static TypedKey<Double> WIDTH = new TypedKey<Double>("Width", Double.class, UnitGroup.UNITS_LENGTH);
162 public final static TypedKey<Double> INNER_DIAMETER = new TypedKey<Double>("InnerDiameter", Double.class, UnitGroup.UNITS_LENGTH);
163 public final static TypedKey<Double> OUTER_DIAMETER = new TypedKey<Double>("OuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
164 public final static TypedKey<Double> FORE_SHOULDER_LENGTH = new TypedKey<Double>("ForeShoulderLength",Double.class, UnitGroup.UNITS_LENGTH);
165 public final static TypedKey<Double> FORE_SHOULDER_DIAMETER = new TypedKey<Double>("ForeShoulderDiameter",Double.class, UnitGroup.UNITS_LENGTH);
166 public final static TypedKey<Double> FORE_OUTER_DIAMETER = new TypedKey<Double>("ForeOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
167 public final static TypedKey<Double> AFT_SHOULDER_LENGTH = new TypedKey<Double>("AftShoulderLength",Double.class, UnitGroup.UNITS_LENGTH);
168 public final static TypedKey<Double> AFT_SHOULDER_DIAMETER = new TypedKey<Double>("AftShoulderDiameter",Double.class, UnitGroup.UNITS_LENGTH);
169 public final static TypedKey<Double> AFT_OUTER_DIAMETER = new TypedKey<Double>("AftOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
170 public final static TypedKey<Shape> SHAPE = new TypedKey<Shape>("Shape", Shape.class);
171 public final static TypedKey<Material> MATERIAL = new TypedKey<Material>("Material", Material.class);
172 public final static TypedKey<Finish> FINISH = new TypedKey<Finish>("Finish", Finish.class);
173 public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
174 public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
175 public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
176 public final static TypedKey<Double> DIAMETER = new TypedKey<Double>("Diameter", Double.class, UnitGroup.UNITS_LENGTH);
177 public final static TypedKey<Integer> SIDES = new TypedKey<Integer>("Sides", Integer.class);
178 public final static TypedKey<Integer> LINE_COUNT = new TypedKey<Integer>("LineCount", Integer.class);
179 public final static TypedKey<Double> LINE_LENGTH = new TypedKey<Double>("LineLength", Double.class, UnitGroup.UNITS_LENGTH);
180 public final static TypedKey<Material> LINE_MATERIAL = new TypedKey<Material>("LineMaterial", Material.class);
181 public final static TypedKey<byte[]> IMAGE = new TypedKey<byte[]>("Image", byte[].class);
183 public final static List<TypedKey<?>> orderedKeyList = Arrays.<TypedKey<?>>asList(
193 AFT_SHOULDER_DIAMETER,
195 FORE_SHOULDER_DIAMETER,
196 FORE_SHOULDER_LENGTH,
211 // package scope constructor to encourage use of factory.
216 * Convenience method to retrieve the Type of this ComponentPreset.
220 public Type getType() {
221 return properties.get(TYPE);
225 * Convenience method to retrieve the Manufacturer of this ComponentPreset.
228 public Manufacturer getManufacturer() {
229 return properties.get(MANUFACTURER);
233 * Convenience method to retrieve the PartNo of this ComponentPreset.
236 public String getPartNo() {
237 return properties.get(PARTNO);
240 public String getDigest() {
244 public boolean has(Object key) {
245 return properties.containsKey(key);
249 * Package scope so the ComponentPresetFactory can call it.
252 void putAll(TypedPropertyMap other) {
256 properties.putAll(other);
260 * Package scope so the ComponentPresetFactory can call it.
264 <T> void put( TypedKey<T> key, T value ) {
265 properties.put(key, value);
268 public <T> T get(TypedKey<T> key) {
269 T value = properties.get(key);
271 throw new BugException("Preset did not contain key " + key + " " + properties.toString());
276 public boolean isFavorite() {
280 public void setFavorite(boolean favorite) {
281 this.favorite = favorite;
285 public int compareTo(ComponentPreset p2) {
286 int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
287 if ( manuCompare != 0 )
290 int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
291 return partNoCompare;
295 public String toString() {
296 return get(MANUFACTURER).toString() + " " + get(PARTNO);
299 public String preferenceKey() {
300 return get(MANUFACTURER).toString() + "|" + get(PARTNO);
304 public boolean equals(final Object o) {
308 if (o == null || getClass() != o.getClass()) {
312 ComponentPreset that = (ComponentPreset) o;
314 if (digest != null ? !digest.equals(that.digest) : that.digest != null) {
322 public int hashCode() {
323 return digest != null ? digest.hashCode() : 0;
327 * Package scope so the factory can call it.
329 void computeDigest() {
332 ByteArrayOutputStream bos = new ByteArrayOutputStream();
333 DataOutputStream os = new DataOutputStream(bos);
335 List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>( properties.keySet());
337 Collections.sort(keys, new Comparator<TypedKey<?>>() {
339 public int compare( TypedKey<?> a, TypedKey<?> b ) {
340 return a.getName().compareTo(b.getName());
344 for ( TypedKey<?> key : keys ) {
346 Object value = properties.get(key);
348 os.writeBytes(key.getName());
350 if ( key.getType() == Double.class ) {
351 Double d = (Double) value;
353 } else if (key.getType() == String.class ) {
354 String s = (String) value;
356 } else if (key.getType() == Manufacturer.class ) {
357 String s = ((Manufacturer)value).getSimpleName();
359 } else if ( key.getType() == Finish.class ) {
360 String s = ((Finish)value).name();
362 } else if ( key.getType() == Type.class ) {
363 String s = ((Type)value).name();
365 } else if ( key.getType() == Boolean.class ) {
366 Boolean b = (Boolean) value;
368 } else if ( key.getType() == Material.class ) {
369 double d = ((Material)value).getDensity();
371 } else if ( key.getType() == Shape.class ) {
372 // this is ugly to use the ordinal but what else?
373 int i = ((Shape)value).ordinal();
379 MessageDigest md5 = MessageDigest.getInstance("MD5");
380 digest = TextUtil.hexString(md5.digest( bos.toByteArray() ));
382 catch ( Exception e ) {
384 throw new BugException(e);