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