28afb30db9f83b94924b8bddc6c3d5ecdadf4ae9
[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 // FIXME - Implement clone.
32 public class ComponentPreset implements Comparable<ComponentPreset> {
33         
34         private final TypedPropertyMap properties = new TypedPropertyMap();
35         
36         private boolean favorite = false;
37         private String digest = "";
38         
39         public enum Type {
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 }),
47                 
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 }),
57                 
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 }),
70                 
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 }),
78                 
79                 BULK_HEAD(new TypedKey<?>[] {
80                                 ComponentPreset.MANUFACTURER,
81                                 ComponentPreset.PARTNO,
82                                 ComponentPreset.DESCRIPTION,
83                                 ComponentPreset.OUTER_DIAMETER,
84                                 ComponentPreset.LENGTH }),
85                 
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 }),
93                 
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 }),
101                 
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 }),
109                 
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 }),
118                 
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 });
129                 
130                 TypedKey<?>[] displayedColumns;
131                 
132                 Type(TypedKey<?>[] displayedColumns) {
133                         this.displayedColumns = displayedColumns;
134                 }
135                 
136                 public List<Type> getCompatibleTypes() {
137                         return compatibleTypeMap.get(Type.this);
138                 }
139                 
140                 public TypedKey<?>[] getDisplayedColumns() {
141                         return displayedColumns;
142                 }
143                 
144                 private static Map<Type, List<Type>> compatibleTypeMap = new HashMap<Type, List<Type>>();
145                 
146                 static {
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));
152                 }
153                 
154         }
155         
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);
182         
183         public final static List<TypedKey<?>> ORDERED_KEY_LIST = Collections.unmodifiableList(Arrays.<TypedKey<?>> asList(
184                         MANUFACTURER,
185                         PARTNO,
186                         DESCRIPTION,
187                         OUTER_DIAMETER,
188                         FORE_OUTER_DIAMETER,
189                         AFT_OUTER_DIAMETER,
190                         INNER_DIAMETER,
191                         LENGTH,
192                         WIDTH,
193                         AFT_SHOULDER_DIAMETER,
194                         AFT_SHOULDER_LENGTH,
195                         FORE_SHOULDER_DIAMETER,
196                         FORE_SHOULDER_LENGTH,
197                         SHAPE,
198                         THICKNESS,
199                         FILLED,
200                         DIAMETER,
201                         SIDES,
202                         LINE_COUNT,
203                         LINE_LENGTH,
204                         LINE_MATERIAL,
205                         MASS,
206                         FINISH,
207                         MATERIAL
208                         ));
209         
210         
211         // package scope constructor to encourage use of factory.
212         ComponentPreset() {
213         }
214         
215         /**
216          * Convenience method to retrieve the Type of this ComponentPreset.
217          *
218          * @return
219          */
220         public Type getType() {
221                 return properties.get(TYPE);
222         }
223         
224         /**
225          * Convenience method to retrieve the Manufacturer of this ComponentPreset.
226          * @return
227          */
228         public Manufacturer getManufacturer() {
229                 return properties.get(MANUFACTURER);
230         }
231         
232         /**
233          * Convenience method to retrieve the PartNo of this ComponentPreset.
234          * @return
235          */
236         public String getPartNo() {
237                 return properties.get(PARTNO);
238         }
239         
240         public String getDigest() {
241                 return digest;
242         }
243         
244         public boolean has(Object key) {
245                 return properties.containsKey(key);
246         }
247         
248         /**
249          * Package scope so the ComponentPresetFactory can call it.
250          * @param other
251          */
252         void putAll(TypedPropertyMap other) {
253                 if (other == null) {
254                         return;
255                 }
256                 properties.putAll(other);
257         }
258         
259         /**
260          * Package scope so the ComponentPresetFactory can call it.
261          * @param key
262          * @param value
263          */
264         <T> void put(TypedKey<T> key, T value) {
265                 properties.put(key, value);
266         }
267         
268         public <T> T get(TypedKey<T> key) {
269                 T value = properties.get(key);
270                 if (value == null) {
271                         throw new BugException("Preset did not contain key " + key + " " + properties.toString());
272                 }
273                 return value;
274         }
275         
276         public boolean isFavorite() {
277                 return favorite;
278         }
279         
280         public void setFavorite(boolean favorite) {
281                 this.favorite = favorite;
282         }
283         
284         @Override
285         public int compareTo(ComponentPreset p2) {
286                 int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
287                 if (manuCompare != 0)
288                         return manuCompare;
289                 
290                 int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
291                 return partNoCompare;
292         }
293         
294         @Override
295         public String toString() {
296                 return get(PARTNO);
297         }
298         
299         public String preferenceKey() {
300                 return get(MANUFACTURER).toString() + "|" + get(PARTNO);
301         }
302         
303         @Override
304         public boolean equals(final Object o) {
305                 if (this == o) {
306                         return true;
307                 }
308                 if (o == null || getClass() != o.getClass()) {
309                         return false;
310                 }
311                 
312                 ComponentPreset that = (ComponentPreset) o;
313                 
314                 if (digest != null ? !digest.equals(that.digest) : that.digest != null) {
315                         return false;
316                 }
317                 
318                 return true;
319         }
320         
321         @Override
322         public int hashCode() {
323                 return digest != null ? digest.hashCode() : 0;
324         }
325         
326         /**
327          * Package scope so the factory can call it.
328          */
329         void computeDigest() {
330                 
331                 try {
332                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
333                         DataOutputStream os = new DataOutputStream(bos);
334                         
335                         List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>(properties.keySet());
336                         
337                         Collections.sort(keys, new Comparator<TypedKey<?>>() {
338                                 @Override
339                                 public int compare(TypedKey<?> a, TypedKey<?> b) {
340                                         return a.getName().compareTo(b.getName());
341                                 }
342                         });
343                         
344                         for (TypedKey<?> key : keys) {
345                                 
346                                 Object value = properties.get(key);
347                                 
348                                 os.writeBytes(key.getName());
349                                 
350                                 if (key.getType() == Double.class) {
351                                         Double d = (Double) value;
352                                         os.writeDouble(d);
353                                 } else if (key.getType() == String.class) {
354                                         String s = (String) value;
355                                         os.writeBytes(s);
356                                 } else if (key.getType() == Manufacturer.class) {
357                                         String s = ((Manufacturer) value).getSimpleName();
358                                         os.writeBytes(s);
359                                 } else if (key.getType() == Finish.class) {
360                                         String s = ((Finish) value).name();
361                                         os.writeBytes(s);
362                                 } else if (key.getType() == Type.class) {
363                                         String s = ((Type) value).name();
364                                         os.writeBytes(s);
365                                 } else if (key.getType() == Boolean.class) {
366                                         Boolean b = (Boolean) value;
367                                         os.writeBoolean(b);
368                                 } else if (key.getType() == Material.class) {
369                                         double d = ((Material) value).getDensity();
370                                         os.writeDouble(d);
371                                 } else if (key.getType() == Shape.class) {
372                                         // this is ugly to use the ordinal but what else?
373                                         int i = ((Shape) value).ordinal();
374                                         os.writeInt(i);
375                                 }
376                                 
377                         }
378                         
379                         MessageDigest md5 = MessageDigest.getInstance("MD5");
380                         digest = TextUtil.hexString(md5.digest(bos.toByteArray()));
381                 } catch (Exception e) {
382                         e.printStackTrace();
383                         throw new BugException(e);
384                 }
385         }
386         
387 }