Added support for TubeCoupler.
[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.Collections;
8 import java.util.Comparator;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12
13 import net.sf.openrocket.material.Material;
14 import net.sf.openrocket.motor.Manufacturer;
15 import net.sf.openrocket.rocketcomponent.BodyTube;
16 import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
17 import net.sf.openrocket.rocketcomponent.NoseCone;
18 import net.sf.openrocket.rocketcomponent.Transition;
19 import net.sf.openrocket.rocketcomponent.Transition.Shape;
20 import net.sf.openrocket.unit.UnitGroup;
21 import net.sf.openrocket.util.BugException;
22 import net.sf.openrocket.util.TextUtil;
23
24
25 /**
26  * A model for a preset component.
27  * <p>
28  * A preset component contains a component class type, manufacturer information,
29  * part information, and a method that returns a prototype of the preset component.
30  * 
31  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
32  */
33 // FIXME - Implement clone.
34 public class ComponentPreset implements Comparable<ComponentPreset> {
35
36         private final TypedPropertyMap properties = new TypedPropertyMap();
37
38         private boolean favorite = false;
39         private String digest = "";
40
41         public enum Type {
42                 BODY_TUBE( new TypedKey<?>[] {
43                                 ComponentPreset.MANUFACTURER,
44                                 ComponentPreset.PARTNO,
45                                 ComponentPreset.OUTER_DIAMETER,
46                                 ComponentPreset.INNER_DIAMETER,
47                                 ComponentPreset.LENGTH} ),
48                                 
49                 NOSE_CONE( new TypedKey<?>[] {
50                                 ComponentPreset.MANUFACTURER,
51                                 ComponentPreset.PARTNO,
52                                 ComponentPreset.DESCRIPTION,
53                                 ComponentPreset.SHAPE,
54                                 ComponentPreset.OUTER_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.OUTER_DIAMETER,
64                                 ComponentPreset.LENGTH} ),
65                                 
66                 TUBE_COUPLER( new TypedKey<?>[] {
67                                 ComponentPreset.MANUFACTURER,
68                                 ComponentPreset.PARTNO,
69                                 ComponentPreset.OUTER_DIAMETER,
70                                 ComponentPreset.INNER_DIAMETER,
71                                 ComponentPreset.LENGTH} ),
72                                                 
73                 BULK_HEAD( new TypedKey<?>[] {
74                                 ComponentPreset.MANUFACTURER,
75                                 ComponentPreset.PARTNO,
76                                 ComponentPreset.DESCRIPTION,
77                                 ComponentPreset.OUTER_DIAMETER,
78                                 ComponentPreset.LENGTH} );
79
80                 Type[] compatibleTypes;
81                 TypedKey<?>[] displayedColumns;
82
83                 Type( TypedKey<?>[] displayedColumns) {
84                         compatibleTypes = new Type[1];
85                         compatibleTypes[0] = this;
86                         this.displayedColumns = displayedColumns;
87                 }
88
89                 Type( Type[] t, TypedKey<?>[] displayedColumns ) {
90
91                         compatibleTypes = new Type[t.length+1];
92                         compatibleTypes[0] = this;
93                         for( int i=0; i<t.length; i++ ) {
94                                 compatibleTypes[i+1] = t[i];
95                         }
96
97                         this.displayedColumns = displayedColumns;
98                 }
99
100                 public Type[] getCompatibleTypes() {
101                         return compatibleTypes;
102                 }
103
104                 public TypedKey<?>[] getDisplayedColumns() {
105                         return displayedColumns;
106                 }
107
108         }
109
110         public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
111         public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo",String.class);
112         public final static TypedKey<String> DESCRIPTION = new TypedKey<String>("Description", String.class);
113         public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type",Type.class);
114         public final static TypedKey<Double> LENGTH = new TypedKey<Double>("Length", Double.class, UnitGroup.UNITS_LENGTH);
115         public final static TypedKey<Double> INNER_DIAMETER = new TypedKey<Double>("InnerDiameter", Double.class, UnitGroup.UNITS_LENGTH);
116         public final static TypedKey<Double> OUTER_DIAMETER = new TypedKey<Double>("OuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
117         public final static TypedKey<Double> SHOULDER_LENGTH = new TypedKey<Double>("ShoulderLength", Double.class, UnitGroup.UNITS_LENGTH);
118         public final static TypedKey<Double> SHOULDER_DIAMETER = new TypedKey<Double>("ShoulderDiameter", Double.class, UnitGroup.UNITS_LENGTH);
119         public final static TypedKey<Double> FORE_SHOULDER_LENGTH = new TypedKey<Double>("ForeShoulderLength",Double.class, UnitGroup.UNITS_LENGTH);
120         public final static TypedKey<Double> FORE_SHOULDER_DIAMETER = new TypedKey<Double>("ForeShoulderDiameter",Double.class, UnitGroup.UNITS_LENGTH);
121         public final static TypedKey<Double> FORE_OUTER_DIAMETER = new TypedKey<Double>("ForeOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH);
122         public final static TypedKey<Shape> SHAPE = new TypedKey<Shape>("Shape", Shape.class);
123         public final static TypedKey<Material> MATERIAL = new TypedKey<Material>("Material", Material.class);
124         public final static TypedKey<Finish> FINISH = new TypedKey<Finish>("Finish", Finish.class);
125         public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
126         public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
127         public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
128
129         public final static Map<String, TypedKey<?>> keyMap = new HashMap<String, TypedKey<?>>();
130         static {
131                 keyMap.put(MANUFACTURER.getName(), MANUFACTURER);
132                 keyMap.put(PARTNO.getName(), PARTNO);
133                 keyMap.put(TYPE.getName(), TYPE);
134                 keyMap.put(DESCRIPTION.getName(), DESCRIPTION);
135                 keyMap.put(LENGTH.getName(), LENGTH);
136                 keyMap.put(INNER_DIAMETER.getName(), INNER_DIAMETER);
137                 keyMap.put(OUTER_DIAMETER.getName(), OUTER_DIAMETER);
138                 keyMap.put(SHOULDER_LENGTH.getName(), SHOULDER_LENGTH);
139                 keyMap.put(SHOULDER_DIAMETER.getName(), SHOULDER_DIAMETER);
140                 keyMap.put(FORE_SHOULDER_LENGTH.getName(), FORE_SHOULDER_LENGTH);
141                 keyMap.put(FORE_SHOULDER_DIAMETER.getName(), FORE_SHOULDER_DIAMETER);
142                 keyMap.put(FORE_OUTER_DIAMETER.getName(), FORE_OUTER_DIAMETER);
143                 keyMap.put(SHAPE.getName(), SHAPE);
144                 keyMap.put(MATERIAL.getName(), MATERIAL);
145                 keyMap.put(FINISH.getName(), FINISH);
146                 keyMap.put(THICKNESS.getName(), THICKNESS);
147                 keyMap.put(FILLED.getName(), FILLED);
148                 keyMap.put(MASS.getName(), MASS);
149         }
150
151         // package scope constructor to encourage use of factory.
152         ComponentPreset() {
153         }
154
155         /**
156          * Convenience method to retrieve the Type of this ComponentPreset.
157          * 
158          * @return
159          */
160         public Type getType() {
161                 return properties.get(TYPE);
162         }
163
164         /**
165          * Convenience method to retrieve the Manufacturer of this ComponentPreset.
166          * @return
167          */
168         public Manufacturer getManufacturer() {
169                 return properties.get(MANUFACTURER);
170         }
171
172         /**
173          * Convenience method to retrieve the PartNo of this ComponentPreset.
174          * @return
175          */
176         public String getPartNo() {
177                 return properties.get(PARTNO);
178         }
179
180         public String getDigest() {
181                 return digest;
182         }
183
184         public boolean has(Object key) {
185                 return properties.containsKey(key);
186         }
187
188         /**
189          * Package scope so the ComponentPresetFactory can call it.
190          * @param other
191          */
192         void putAll(TypedPropertyMap other) {
193                 if (other == null) {
194                         return;
195                 }
196                 properties.putAll(other);
197         }
198
199         /**
200          * Package scope so the ComponentPresetFactory can call it.
201          * @param key
202          * @param value
203          */
204         <T> void put( TypedKey<T> key, T value ) {
205                 properties.put(key, value);
206         }
207         
208         public <T> T get(TypedKey<T> key) {
209                 T value = properties.get(key);
210                 if (value == null) {
211                         throw new BugException("Preset did not contain key " + key + " " + properties.toString());
212                 }
213                 return (T) value;
214         }
215
216         public boolean isFavorite() {
217                 return favorite;
218         }
219
220         public void setFavorite(boolean favorite) {
221                 this.favorite = favorite;
222         }
223
224         @Override
225         public int compareTo(ComponentPreset p2) {
226                 int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
227                 if ( manuCompare != 0 )
228                         return manuCompare;
229
230                 int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
231                 return partNoCompare;
232         }
233
234         @Override
235         public String toString() {
236                 return get(MANUFACTURER).toString() + " " + get(PARTNO);
237         }
238
239         public String preferenceKey() {
240                 return get(MANUFACTURER).toString() + "|" + get(PARTNO);
241         }
242
243         /**
244          * Package scope so the factory can call it.
245          */
246         void computeDigest() {
247
248                 try {
249                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
250                         DataOutputStream os = new DataOutputStream(bos);
251
252                         List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>( properties.keySet());
253
254                         Collections.sort(keys, new Comparator<TypedKey<?>>() {
255                                 @Override
256                                 public int compare( TypedKey<?> a, TypedKey<?> b ) {
257                                         return a.getName().compareTo(b.getName());
258                                 }
259                         });
260
261                         for ( TypedKey<?> key : keys  ) {
262
263                                 Object value = properties.get(key);
264
265                                 os.writeBytes(key.getName());
266
267                                 if ( key.getType() == Double.class ) {
268                                         Double d = (Double) value;
269                                         os.writeDouble(d);
270                                 } else if (key.getType() == String.class ) {
271                                         String s = (String) value;
272                                         os.writeBytes(s);
273                                 } else if (key.getType() == Manufacturer.class ) {
274                                         String s = ((Manufacturer)value).getSimpleName();
275                                         os.writeBytes(s);
276                                 } else if ( key.getType() == Finish.class ) {
277                                         String s = ((Finish)value).name();
278                                         os.writeBytes(s);
279                                 } else if ( key.getType() == Type.class ) {
280                                         String s = ((Type)value).name();
281                                         os.writeBytes(s);
282                                 } else if ( key.getType() == Boolean.class ) {
283                                         Boolean b = (Boolean) value;
284                                         os.writeBoolean(b);
285                                 } else if ( key.getType() == Material.class ) {
286                                         double d = ((Material)value).getDensity();
287                                         os.writeDouble(d);
288                                 } else if ( key.getType() == Shape.class ) {
289                                         // FIXME - this is ugly to use the ordinal but what else?
290                                         int i = ((Shape)value).ordinal();
291                                         os.writeInt(i);
292                                 }
293
294                         }
295
296                         MessageDigest md5 = MessageDigest.getInstance("MD5");
297                         digest = TextUtil.hexString(md5.digest( bos.toByteArray() ));
298                 }
299                 catch ( Exception e ) {
300                         throw new BugException(e);
301                 }
302         }
303
304 }