Change nose cone preset to use Aft Outer Diameter, Aft Shoulder Length, and Aft Shoul...
[debian/openrocket] / core / src / net / sf / openrocket / preset / ComponentPreset.java
1 package net.sf.openrocket.preset;
2
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;
10
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;
20 import java.util.Map;
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.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.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                 TypedKey<?>[] displayedColumns;
101
102                 Type( TypedKey<?>[] displayedColumns) {
103                         this.displayedColumns = displayedColumns;
104                 }
105
106                 public List<Type> getCompatibleTypes() {
107                         return compatibleTypeMap.get(Type.this);
108                 }
109
110                 public TypedKey<?>[] getDisplayedColumns() {
111                         return displayedColumns;
112                 }
113
114                 private static Map<Type,List<Type>> compatibleTypeMap = new HashMap<Type,List<Type>>();
115
116                 static {
117                         compatibleTypeMap.put( BODY_TUBE, Arrays.asList( BODY_TUBE, TUBE_COUPLER) );
118                         compatibleTypeMap.put( TUBE_COUPLER, Arrays.asList( BODY_TUBE,TUBE_COUPLER) );
119                         compatibleTypeMap.put( CENTERING_RING, Arrays.asList( CENTERING_RING, ENGINE_BLOCK ) );
120                         compatibleTypeMap.put( NOSE_CONE, Arrays.asList( NOSE_CONE, TRANSITION));
121                 }
122
123         }
124
125         public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
126         public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo",String.class);
127         public final static TypedKey<String> DESCRIPTION = new TypedKey<String>("Description", String.class);
128         public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type",Type.class);
129         public final static TypedKey<Double> LENGTH = new TypedKey<Double>("Length", Double.class, UnitGroup.UNITS_LENGTH);
130         public final static TypedKey<Double> INNER_DIAMETER = new TypedKey<Double>("InnerDiameter", Double.class, UnitGroup.UNITS_LENGTH);
131         public final static TypedKey<Double> OUTER_DIAMETER = new TypedKey<Double>("OuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
132         public final static TypedKey<Double> FORE_SHOULDER_LENGTH = new TypedKey<Double>("ForeShoulderLength",Double.class, UnitGroup.UNITS_LENGTH);
133         public final static TypedKey<Double> FORE_SHOULDER_DIAMETER = new TypedKey<Double>("ForeShoulderDiameter",Double.class, UnitGroup.UNITS_LENGTH);
134         public final static TypedKey<Double> FORE_OUTER_DIAMETER = new TypedKey<Double>("ForeOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
135         public final static TypedKey<Double> AFT_SHOULDER_LENGTH = new TypedKey<Double>("AftShoulderLength",Double.class, UnitGroup.UNITS_LENGTH);
136         public final static TypedKey<Double> AFT_SHOULDER_DIAMETER = new TypedKey<Double>("AftShoulderDiameter",Double.class, UnitGroup.UNITS_LENGTH);
137         public final static TypedKey<Double> AFT_OUTER_DIAMETER = new TypedKey<Double>("AftOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
138         public final static TypedKey<Shape> SHAPE = new TypedKey<Shape>("Shape", Shape.class);
139         public final static TypedKey<Material> MATERIAL = new TypedKey<Material>("Material", Material.class);
140         public final static TypedKey<Finish> FINISH = new TypedKey<Finish>("Finish", Finish.class);
141         public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
142         public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
143         public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
144
145         public final static Map<String, TypedKey<?>> keyMap = new HashMap<String, TypedKey<?>>();
146         static {
147                 keyMap.put(MANUFACTURER.getName(), MANUFACTURER);
148                 keyMap.put(PARTNO.getName(), PARTNO);
149                 keyMap.put(TYPE.getName(), TYPE);
150                 keyMap.put(DESCRIPTION.getName(), DESCRIPTION);
151                 keyMap.put(LENGTH.getName(), LENGTH);
152                 keyMap.put(INNER_DIAMETER.getName(), INNER_DIAMETER);
153                 keyMap.put(OUTER_DIAMETER.getName(), OUTER_DIAMETER);
154                 keyMap.put(FORE_SHOULDER_LENGTH.getName(), FORE_SHOULDER_LENGTH);
155                 keyMap.put(FORE_SHOULDER_DIAMETER.getName(), FORE_SHOULDER_DIAMETER);
156                 keyMap.put(FORE_OUTER_DIAMETER.getName(), FORE_OUTER_DIAMETER);
157                 keyMap.put(AFT_SHOULDER_LENGTH.getName(), AFT_SHOULDER_LENGTH);
158                 keyMap.put(AFT_SHOULDER_DIAMETER.getName(), AFT_SHOULDER_DIAMETER);
159                 keyMap.put(AFT_OUTER_DIAMETER.getName(), AFT_OUTER_DIAMETER);
160                 keyMap.put(SHAPE.getName(), SHAPE);
161                 keyMap.put(MATERIAL.getName(), MATERIAL);
162                 keyMap.put(FINISH.getName(), FINISH);
163                 keyMap.put(THICKNESS.getName(), THICKNESS);
164                 keyMap.put(FILLED.getName(), FILLED);
165                 keyMap.put(MASS.getName(), MASS);
166         }
167
168         public final static List<TypedKey<?>> orderedKeyList = Arrays.<TypedKey<?>>asList(
169                         MANUFACTURER,
170                         PARTNO,
171                         DESCRIPTION,
172                         OUTER_DIAMETER,
173                         FORE_OUTER_DIAMETER,
174                         AFT_OUTER_DIAMETER,
175                         INNER_DIAMETER,
176                         LENGTH,
177                         AFT_SHOULDER_DIAMETER,
178                         AFT_SHOULDER_LENGTH,
179                         FORE_SHOULDER_DIAMETER,
180                         FORE_SHOULDER_LENGTH,
181                         SHAPE,
182                         THICKNESS,
183                         FILLED,
184                         MASS,
185                         FINISH,
186                         MATERIAL
187                         );
188
189
190         // package scope constructor to encourage use of factory.
191         ComponentPreset() {
192         }
193
194         /**
195          * Convenience method to retrieve the Type of this ComponentPreset.
196          *
197          * @return
198          */
199         public Type getType() {
200                 return properties.get(TYPE);
201         }
202
203         /**
204          * Convenience method to retrieve the Manufacturer of this ComponentPreset.
205          * @return
206          */
207         public Manufacturer getManufacturer() {
208                 return properties.get(MANUFACTURER);
209         }
210
211         /**
212          * Convenience method to retrieve the PartNo of this ComponentPreset.
213          * @return
214          */
215         public String getPartNo() {
216                 return properties.get(PARTNO);
217         }
218
219         public String getDigest() {
220                 return digest;
221         }
222
223         public boolean has(Object key) {
224                 return properties.containsKey(key);
225         }
226
227         /**
228          * Package scope so the ComponentPresetFactory can call it.
229          * @param other
230          */
231         void putAll(TypedPropertyMap other) {
232                 if (other == null) {
233                         return;
234                 }
235                 properties.putAll(other);
236         }
237
238         /**
239          * Package scope so the ComponentPresetFactory can call it.
240          * @param key
241          * @param value
242          */
243         <T> void put( TypedKey<T> key, T value ) {
244                 properties.put(key, value);
245         }
246
247         public <T> T get(TypedKey<T> key) {
248                 T value = properties.get(key);
249                 if (value == null) {
250                         throw new BugException("Preset did not contain key " + key + " " + properties.toString());
251                 }
252                 return (T) value;
253         }
254
255         public boolean isFavorite() {
256                 return favorite;
257         }
258
259         public void setFavorite(boolean favorite) {
260                 this.favorite = favorite;
261         }
262
263         @Override
264         public int compareTo(ComponentPreset p2) {
265                 int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
266                 if ( manuCompare != 0 )
267                         return manuCompare;
268
269                 int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
270                 return partNoCompare;
271         }
272
273         @Override
274         public String toString() {
275                 return get(MANUFACTURER).toString() + " " + get(PARTNO);
276         }
277
278         public String preferenceKey() {
279                 return get(MANUFACTURER).toString() + "|" + get(PARTNO);
280         }
281
282         /**
283          * Package scope so the factory can call it.
284          */
285         void computeDigest() {
286
287                 try {
288                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
289                         DataOutputStream os = new DataOutputStream(bos);
290
291                         List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>( properties.keySet());
292
293                         Collections.sort(keys, new Comparator<TypedKey<?>>() {
294                                 @Override
295                                 public int compare( TypedKey<?> a, TypedKey<?> b ) {
296                                         return a.getName().compareTo(b.getName());
297                                 }
298                         });
299
300                         for ( TypedKey<?> key : keys  ) {
301
302                                 Object value = properties.get(key);
303
304                                 os.writeBytes(key.getName());
305
306                                 if ( key.getType() == Double.class ) {
307                                         Double d = (Double) value;
308                                         os.writeDouble(d);
309                                 } else if (key.getType() == String.class ) {
310                                         String s = (String) value;
311                                         os.writeBytes(s);
312                                 } else if (key.getType() == Manufacturer.class ) {
313                                         String s = ((Manufacturer)value).getSimpleName();
314                                         os.writeBytes(s);
315                                 } else if ( key.getType() == Finish.class ) {
316                                         String s = ((Finish)value).name();
317                                         os.writeBytes(s);
318                                 } else if ( key.getType() == Type.class ) {
319                                         String s = ((Type)value).name();
320                                         os.writeBytes(s);
321                                 } else if ( key.getType() == Boolean.class ) {
322                                         Boolean b = (Boolean) value;
323                                         os.writeBoolean(b);
324                                 } else if ( key.getType() == Material.class ) {
325                                         double d = ((Material)value).getDensity();
326                                         os.writeDouble(d);
327                                 } else if ( key.getType() == Shape.class ) {
328                                         // FIXME - this is ugly to use the ordinal but what else?
329                                         int i = ((Shape)value).ordinal();
330                                         os.writeInt(i);
331                                 }
332
333                         }
334
335                         MessageDigest md5 = MessageDigest.getInstance("MD5");
336                         digest = TextUtil.hexString(md5.digest( bos.toByteArray() ));
337                 }
338                 catch ( Exception e ) {
339             e.printStackTrace();
340                         throw new BugException(e);
341                 }
342         }
343
344 }