Removed FIXME comment.
[debian/openrocket] / core / src / net / sf / openrocket / preset / ComponentPreset.java
1 package net.sf.openrocket.preset;
2
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;
12 import java.util.Map;
13
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;
21
22
23 /**
24  * A model for a preset component.
25  * <p>
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.
28  *
29  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
30  */
31 public class ComponentPreset implements Comparable<ComponentPreset> {
32         
33         private final TypedPropertyMap properties = new TypedPropertyMap();
34         
35         private String digest = "";
36         
37         public enum Type {
38                 BODY_TUBE(new TypedKey<?>[] {
39                                 ComponentPreset.MANUFACTURER,
40                                 ComponentPreset.PARTNO,
41                                 ComponentPreset.DESCRIPTION,
42                                 ComponentPreset.INNER_DIAMETER,
43                                 ComponentPreset.OUTER_DIAMETER,
44                                 ComponentPreset.LENGTH }),
45                 
46                 NOSE_CONE(new TypedKey<?>[] {
47                                 ComponentPreset.MANUFACTURER,
48                                 ComponentPreset.PARTNO,
49                                 ComponentPreset.DESCRIPTION,
50                                 ComponentPreset.SHAPE,
51                                 ComponentPreset.AFT_OUTER_DIAMETER,
52                                 ComponentPreset.AFT_SHOULDER_DIAMETER,
53                                 ComponentPreset.AFT_SHOULDER_LENGTH,
54                                 ComponentPreset.LENGTH }),
55                 
56                 TRANSITION(new TypedKey<?>[] {
57                                 ComponentPreset.MANUFACTURER,
58                                 ComponentPreset.PARTNO,
59                                 ComponentPreset.DESCRIPTION,
60                                 ComponentPreset.SHAPE,
61                                 ComponentPreset.FORE_OUTER_DIAMETER,
62                                 ComponentPreset.FORE_SHOULDER_DIAMETER,
63                                 ComponentPreset.FORE_SHOULDER_LENGTH,
64                                 ComponentPreset.AFT_OUTER_DIAMETER,
65                                 ComponentPreset.AFT_SHOULDER_DIAMETER,
66                                 ComponentPreset.AFT_SHOULDER_LENGTH,
67                                 ComponentPreset.LENGTH }),
68                 
69                 TUBE_COUPLER(new TypedKey<?>[] {
70                                 ComponentPreset.MANUFACTURER,
71                                 ComponentPreset.PARTNO,
72                                 ComponentPreset.DESCRIPTION,
73                                 ComponentPreset.OUTER_DIAMETER,
74                                 ComponentPreset.INNER_DIAMETER,
75                                 ComponentPreset.LENGTH }),
76                 
77                 BULK_HEAD(new TypedKey<?>[] {
78                                 ComponentPreset.MANUFACTURER,
79                                 ComponentPreset.PARTNO,
80                                 ComponentPreset.DESCRIPTION,
81                                 ComponentPreset.OUTER_DIAMETER,
82                                 ComponentPreset.LENGTH }),
83                 
84                 CENTERING_RING(new TypedKey<?>[] {
85                                 ComponentPreset.MANUFACTURER,
86                                 ComponentPreset.PARTNO,
87                                 ComponentPreset.DESCRIPTION,
88                                 ComponentPreset.INNER_DIAMETER,
89                                 ComponentPreset.OUTER_DIAMETER,
90                                 ComponentPreset.LENGTH }),
91                 
92                 ENGINE_BLOCK(new TypedKey<?>[] {
93                                 ComponentPreset.MANUFACTURER,
94                                 ComponentPreset.PARTNO,
95                                 ComponentPreset.DESCRIPTION,
96                                 ComponentPreset.INNER_DIAMETER,
97                                 ComponentPreset.OUTER_DIAMETER,
98                                 ComponentPreset.LENGTH }),
99                 
100                 LAUNCH_LUG(new TypedKey<?>[] {
101                                 ComponentPreset.MANUFACTURER,
102                                 ComponentPreset.PARTNO,
103                                 ComponentPreset.DESCRIPTION,
104                                 ComponentPreset.INNER_DIAMETER,
105                                 ComponentPreset.OUTER_DIAMETER,
106                                 ComponentPreset.LENGTH }),
107                 
108                 STREAMER(new TypedKey<?>[] {
109                                 ComponentPreset.MANUFACTURER,
110                                 ComponentPreset.PARTNO,
111                                 ComponentPreset.DESCRIPTION,
112                                 ComponentPreset.LENGTH,
113                                 ComponentPreset.WIDTH,
114                                 ComponentPreset.THICKNESS,
115                                 ComponentPreset.MATERIAL }),
116                 
117                 PARACHUTE(new TypedKey<?>[] {
118                                 ComponentPreset.MANUFACTURER,
119                                 ComponentPreset.PARTNO,
120                                 ComponentPreset.DESCRIPTION,
121                                 ComponentPreset.DIAMETER,
122                                 ComponentPreset.SIDES,
123                                 ComponentPreset.LINE_COUNT,
124                                 ComponentPreset.LINE_LENGTH,
125                                 ComponentPreset.LINE_MATERIAL,
126                                 ComponentPreset.MATERIAL });
127                 
128                 TypedKey<?>[] displayedColumns;
129                 
130                 Type(TypedKey<?>[] displayedColumns) {
131                         this.displayedColumns = displayedColumns;
132                 }
133                 
134                 public List<Type> getCompatibleTypes() {
135                         return compatibleTypeMap.get(Type.this);
136                 }
137                 
138                 public TypedKey<?>[] getDisplayedColumns() {
139                         return displayedColumns;
140                 }
141                 
142                 private static Map<Type, List<Type>> compatibleTypeMap = new HashMap<Type, List<Type>>();
143                 
144                 static {
145                         compatibleTypeMap.put(BODY_TUBE, Arrays.asList(BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG));
146                         compatibleTypeMap.put(TUBE_COUPLER, Arrays.asList(BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG));
147                         compatibleTypeMap.put(LAUNCH_LUG, Arrays.asList(BODY_TUBE, TUBE_COUPLER, LAUNCH_LUG));
148                         compatibleTypeMap.put(CENTERING_RING, Arrays.asList(CENTERING_RING, ENGINE_BLOCK));
149                         compatibleTypeMap.put(NOSE_CONE, Arrays.asList(NOSE_CONE, TRANSITION));
150                 }
151                 
152         }
153         
154         public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
155         public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo", String.class);
156         public final static TypedKey<String> DESCRIPTION = new TypedKey<String>("Description", String.class);
157         public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type", Type.class);
158         public final static TypedKey<Double> LENGTH = new TypedKey<Double>("Length", Double.class, UnitGroup.UNITS_LENGTH);
159         public final static TypedKey<Double> WIDTH = new TypedKey<Double>("Width", Double.class, UnitGroup.UNITS_LENGTH);
160         public final static TypedKey<Double> INNER_DIAMETER = new TypedKey<Double>("InnerDiameter", Double.class, UnitGroup.UNITS_LENGTH);
161         public final static TypedKey<Double> OUTER_DIAMETER = new TypedKey<Double>("OuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
162         public final static TypedKey<Double> FORE_SHOULDER_LENGTH = new TypedKey<Double>("ForeShoulderLength", Double.class, UnitGroup.UNITS_LENGTH);
163         public final static TypedKey<Double> FORE_SHOULDER_DIAMETER = new TypedKey<Double>("ForeShoulderDiameter", Double.class, UnitGroup.UNITS_LENGTH);
164         public final static TypedKey<Double> FORE_OUTER_DIAMETER = new TypedKey<Double>("ForeOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
165         public final static TypedKey<Double> AFT_SHOULDER_LENGTH = new TypedKey<Double>("AftShoulderLength", Double.class, UnitGroup.UNITS_LENGTH);
166         public final static TypedKey<Double> AFT_SHOULDER_DIAMETER = new TypedKey<Double>("AftShoulderDiameter", Double.class, UnitGroup.UNITS_LENGTH);
167         public final static TypedKey<Double> AFT_OUTER_DIAMETER = new TypedKey<Double>("AftOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
168         public final static TypedKey<Shape> SHAPE = new TypedKey<Shape>("Shape", Shape.class);
169         public final static TypedKey<Material> MATERIAL = new TypedKey<Material>("Material", Material.class);
170         public final static TypedKey<Finish> FINISH = new TypedKey<Finish>("Finish", Finish.class);
171         public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
172         public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
173         public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
174         public final static TypedKey<Double> DIAMETER = new TypedKey<Double>("Diameter", Double.class, UnitGroup.UNITS_LENGTH);
175         public final static TypedKey<Integer> SIDES = new TypedKey<Integer>("Sides", Integer.class);
176         public final static TypedKey<Integer> LINE_COUNT = new TypedKey<Integer>("LineCount", Integer.class);
177         public final static TypedKey<Double> LINE_LENGTH = new TypedKey<Double>("LineLength", Double.class, UnitGroup.UNITS_LENGTH);
178         public final static TypedKey<Material> LINE_MATERIAL = new TypedKey<Material>("LineMaterial", Material.class);
179         public final static TypedKey<byte[]> IMAGE = new TypedKey<byte[]>("Image", byte[].class);
180         
181         public final static List<TypedKey<?>> ORDERED_KEY_LIST = Collections.unmodifiableList(Arrays.<TypedKey<?>> asList(
182                         MANUFACTURER,
183                         PARTNO,
184                         DESCRIPTION,
185                         OUTER_DIAMETER,
186                         FORE_OUTER_DIAMETER,
187                         AFT_OUTER_DIAMETER,
188                         INNER_DIAMETER,
189                         LENGTH,
190                         WIDTH,
191                         AFT_SHOULDER_DIAMETER,
192                         AFT_SHOULDER_LENGTH,
193                         FORE_SHOULDER_DIAMETER,
194                         FORE_SHOULDER_LENGTH,
195                         SHAPE,
196                         THICKNESS,
197                         FILLED,
198                         DIAMETER,
199                         SIDES,
200                         LINE_COUNT,
201                         LINE_LENGTH,
202                         LINE_MATERIAL,
203                         MASS,
204                         FINISH,
205                         MATERIAL
206                         ));
207         
208         
209         // package scope constructor to encourage use of factory.
210         ComponentPreset() {
211         }
212         
213         /**
214          * Convenience method to retrieve the Type of this ComponentPreset.
215          *
216          * @return
217          */
218         public Type getType() {
219                 return properties.get(TYPE);
220         }
221         
222         /**
223          * Convenience method to retrieve the Manufacturer of this ComponentPreset.
224          * @return
225          */
226         public Manufacturer getManufacturer() {
227                 return properties.get(MANUFACTURER);
228         }
229         
230         /**
231          * Convenience method to retrieve the PartNo of this ComponentPreset.
232          * @return
233          */
234         public String getPartNo() {
235                 return properties.get(PARTNO);
236         }
237         
238         public String getDigest() {
239                 return digest;
240         }
241         
242         public boolean has(Object key) {
243                 return properties.containsKey(key);
244         }
245         
246         /**
247          * Package scope so the ComponentPresetFactory can call it.
248          * @param other
249          */
250         void putAll(TypedPropertyMap other) {
251                 if (other == null) {
252                         return;
253                 }
254                 properties.putAll(other);
255         }
256         
257         /**
258          * Package scope so the ComponentPresetFactory can call it.
259          * @param key
260          * @param value
261          */
262         <T> void put(TypedKey<T> key, T value) {
263                 properties.put(key, value);
264         }
265         
266         public <T> T get(TypedKey<T> key) {
267                 T value = properties.get(key);
268                 if (value == null) {
269                         throw new BugException("Preset did not contain key " + key + " " + properties.toString());
270                 }
271                 return value;
272         }
273         
274         @Override
275         public int compareTo(ComponentPreset p2) {
276                 int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
277                 if (manuCompare != 0)
278                         return manuCompare;
279                 
280                 int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
281                 return partNoCompare;
282         }
283         
284         @Override
285         public String toString() {
286                 return get(PARTNO);
287         }
288         
289         public String preferenceKey() {
290                 return get(MANUFACTURER).toString() + "|" + get(PARTNO);
291         }
292         
293         @Override
294         public boolean equals(final Object o) {
295                 if (this == o) {
296                         return true;
297                 }
298                 if (o == null || getClass() != o.getClass()) {
299                         return false;
300                 }
301                 
302                 ComponentPreset that = (ComponentPreset) o;
303                 
304                 if (digest != null ? !digest.equals(that.digest) : that.digest != null) {
305                         return false;
306                 }
307                 
308                 return true;
309         }
310         
311         @Override
312         public int hashCode() {
313                 return digest != null ? digest.hashCode() : 0;
314         }
315         
316         /**
317          * Package scope so the factory can call it.
318          */
319         void computeDigest() {
320                 
321                 try {
322                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
323                         DataOutputStream os = new DataOutputStream(bos);
324                         
325                         List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>(properties.keySet());
326                         
327                         Collections.sort(keys, new Comparator<TypedKey<?>>() {
328                                 @Override
329                                 public int compare(TypedKey<?> a, TypedKey<?> b) {
330                                         return a.getName().compareTo(b.getName());
331                                 }
332                         });
333                         
334                         for (TypedKey<?> key : keys) {
335                                 
336                                 Object value = properties.get(key);
337                                 
338                                 os.writeBytes(key.getName());
339                                 
340                                 if (key.getType() == Double.class) {
341                                         Double d = (Double) value;
342                                         os.writeDouble(d);
343                                 } else if (key.getType() == String.class) {
344                                         String s = (String) value;
345                                         os.writeBytes(s);
346                                 } else if (key.getType() == Manufacturer.class) {
347                                         String s = ((Manufacturer) value).getSimpleName();
348                                         os.writeBytes(s);
349                                 } else if (key.getType() == Finish.class) {
350                                         String s = ((Finish) value).name();
351                                         os.writeBytes(s);
352                                 } else if (key.getType() == Type.class) {
353                                         String s = ((Type) value).name();
354                                         os.writeBytes(s);
355                                 } else if (key.getType() == Boolean.class) {
356                                         Boolean b = (Boolean) value;
357                                         os.writeBoolean(b);
358                                 } else if (key.getType() == Material.class) {
359                                         double d = ((Material) value).getDensity();
360                                         os.writeDouble(d);
361                                 } else if (key.getType() == Shape.class) {
362                                         // this is ugly to use the ordinal but what else?
363                                         int i = ((Shape) value).ordinal();
364                                         os.writeInt(i);
365                                 }
366                                 
367                         }
368                         
369                         MessageDigest md5 = MessageDigest.getInstance("MD5");
370                         digest = TextUtil.hexString(md5.digest(bos.toByteArray()));
371                 } catch (Exception e) {
372                         e.printStackTrace();
373                         throw new BugException(e);
374                 }
375         }
376         
377 }