5202f864582494182d42ca4363705be9aeef2681
[debian/openrocket] / core / src / net / sf / openrocket / preset / loader / RocksimComponentFileLoader.java
1 package net.sf.openrocket.preset.loader;
2
3 import au.com.bytecode.opencsv.CSVReader;
4 import net.sf.openrocket.database.Databases;
5 import net.sf.openrocket.file.preset.ColumnDefinition;
6 import net.sf.openrocket.file.rocksim.RocksimNoseConeCode;
7 import net.sf.openrocket.gui.print.PrintUnit;
8 import net.sf.openrocket.gui.util.SwingPreferences;
9 import net.sf.openrocket.material.Material;
10 import net.sf.openrocket.preset.ComponentPreset;
11 import net.sf.openrocket.preset.ComponentPresetFactory;
12 import net.sf.openrocket.preset.InvalidComponentPresetException;
13 import net.sf.openrocket.preset.TypedKey;
14 import net.sf.openrocket.preset.TypedPropertyMap;
15 import net.sf.openrocket.startup.Application;
16 import net.sf.openrocket.unit.UnitGroup;
17 import net.sf.openrocket.util.ArrayList;
18 import net.sf.openrocket.util.BugException;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 /**
34  * Primary entry point for parsing component CSV files that are in Rocksim format.
35  */
36 public class RocksimComponentFileLoader {
37
38     /**
39      * Common unit of measure key.  Rocksim format allows different types of units.
40      */
41     public final static TypedKey<String> UNITS_OF_MEASURE = new TypedKey<String>("Units", String.class);
42
43
44     public static List<String[]> load(RocksimComponentFileType type) {
45         return load(RocksimComponentFileLoader.class.getResourceAsStream("/performancerocketry/" + type.getDefaultFileName()));
46     }
47
48     public static List<String[]> load(File file) throws FileNotFoundException {
49         return load(new FileInputStream(file));
50     }
51
52     public static List<String[]> load(InputStream is) {
53         if (is == null) {
54             return new ArrayList<String[]>();
55         }
56         InputStreamReader r = null;
57         try {
58             r = new InputStreamReader(is);
59
60             // Create the CSV reader.  Use comma separator.
61             CSVReader reader = new CSVReader(r, ',', '\'', '\\');
62
63             //Read and throw away the header row.
64             reader.readNext();
65
66             //Read the rest of the file as data rows.
67             return reader.readAll();
68         }
69         catch (IOException e) {
70         }
71         finally {
72             if (r != null) {
73                 try {
74                     r.close();
75                 }
76                 catch (IOException e) {
77                 }
78             }
79         }
80
81         return new ArrayList<String[]>();
82     }
83
84     /**
85      * Rocksim CSV units are either inches or mm.  A value of 0 or "in." indicate inches.  A value of 1 or "mm" indicate
86      * millimeters.
87      *
88      * @param units the value from the file
89      * @return true if it's inches
90      */
91     private static boolean isInches(String units) {
92         String tmp = units.trim().toLowerCase();
93         return "0".equals(tmp) || tmp.startsWith("in");
94     }
95
96     /**
97      * Convert inches or millimeters to meters.
98      *
99      * @param units a Rocksim CSV string representing the kind of units.
100      * @param value the original value within the CSV file
101      * @return the value in meters
102      */
103     private static double convertLength(String units, double value) {
104         if (isInches(units)) {
105             return PrintUnit.INCHES.toMeters(value);
106         }
107         else {
108             return PrintUnit.MILLIMETERS.toMeters(value);
109         }
110     }
111
112     /**
113      * Remove all occurrences of the given character.  Note: this is done because some manufacturers embed double
114      * quotes in their descriptions or material names.  Those are stripped away because they cause all sorts of
115      * matching/lookup issues.
116      *
117      * @param target      the target string to be operated upon
118      * @param toBeRemoved the character to remove
119      * @return target, minus every occurrence of toBeRemoved
120      */
121     private static String stripAll(String target, Character toBeRemoved) {
122         StringBuilder sb = new StringBuilder();
123         for (int i = 0; i < target.length(); i++) {
124             Character c = target.charAt(i);
125             if (!c.equals(toBeRemoved)) {
126                 sb.append(c);
127             }
128         }
129         return sb.toString();
130     }
131
132     /**
133      * Convert all words in a given string to Camel Case (first letter capitalized). Words are assumed to be
134      * separated by a space.  Note: this is done because some manufacturers define their material name in Camel Case
135      * but the component part references the material in lower case.  That causes matching/lookup issues that's
136      * easiest handled this way (rather than converting everything to lower case.
137      *
138      * @param target the target string to be operated upon
139      * @return target, with the first letter of each word in uppercase
140      */
141     private static String toCamelCase(String target) {
142         StringBuilder sb = new StringBuilder();
143         String[] t = target.split("[ ]");
144         if (t != null && t.length > 0) {
145             for (int i = 0; i < t.length; i++) {
146                 String s = t[i];
147                 s = s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
148                 sb.append(s).append(" ");
149             }
150             return sb.toString().trim();
151         }
152         else {
153             return target;
154         }
155     }
156
157     private static Collection<ComponentPreset> commonLoader(final List<String[]> theData,
158                                                             final List<TypedKey<?>> keyMap,
159                                                             final Map<String, Material> materialMap,
160                                                             final ComponentPreset.Type type) {
161         Collection<ComponentPreset> result = new ArrayList<ComponentPreset>();
162         List<TypedPropertyMap> templates = new java.util.ArrayList<TypedPropertyMap>();
163         Set<String> favorites = Application.getPreferences().getComponentFavorites();
164         Integer uom = null;
165
166         ColumnDefinition[] columns = new ColumnDefinition[keyMap.size()];
167         for (int i = 0; i < keyMap.size(); i++) {
168             TypedKey key = keyMap.get(i);
169             if (key != null) {
170                 columns[i] = new ColumnDefinition(key);
171             }
172             if (key.getName().equals("Units")) {
173                 uom = i;
174             }
175         }
176
177         for (int i = 0; i < theData.size(); i++) {
178             String[] item = theData.get(i);
179             TypedPropertyMap preset = new TypedPropertyMap();
180
181             for (int j = 0; j < columns.length; j++) {
182                 if (j < item.length) {
183                     String value = item[j];
184                     if (value == null) {
185                         continue;
186                     }
187                     value = value.trim();
188                     value = stripAll(value, '"');
189                     if (value.length() == 0) {
190                         continue;
191                     }
192                     final TypedKey typedKey = columns[j].getKey();
193                     if (typedKey.equals(ComponentPreset.MATERIAL)) {
194                         preset.put(ComponentPreset.MATERIAL, materialMap.get(value));
195                     }
196                     else if (typedKey.equals(ComponentPreset.SHAPE)) {
197                         preset.put(ComponentPreset.SHAPE, RocksimNoseConeCode.fromShapeNameOrCode(value).asOpenRocket());
198                     }
199                     else {
200                         final UnitGroup unitGroup = typedKey.getUnitGroup();
201                         if (unitGroup != null && unitGroup.equals(UnitGroup.UNITS_LENGTH)) {
202                             columns[j].setProperty(preset, convertLength(item[uom], Double.valueOf(value)));
203                         }
204                         else {
205                             columns[j].setProperty(preset, value);
206                         }
207                     }
208                 }
209             }
210             preset.put(ComponentPreset.TYPE, type);
211             templates.add(preset);
212         }
213
214         for (TypedPropertyMap o : templates) {
215             try {
216                 ComponentPreset preset = ComponentPresetFactory.create(o);
217                 if (favorites.contains(preset.preferenceKey())) {
218                     preset.setFavorite(true);
219                 }
220                 result.add(preset);
221             }
222             catch (InvalidComponentPresetException ex) {
223                 throw new BugException(ex);
224             }
225         }
226
227         return result;
228     }
229
230     static class BodyTubeLoader {
231         private final static int MFG_INDEX = 0;
232         private final static int PART_NO_INDEX = 1;
233         private final static int DESCRIPTION_INDEX = 2;
234         private final static int UNITS_INDEX = 3;
235         private final static int ID_INDEX = 4;
236         private final static int OD_INDEX = 5;
237         private final static int LENGTH_INDEX = 6;
238         private final static int MATERIAL_INDEX = 7;
239
240         public final static List<TypedKey<?>> keyMap = new ArrayList<TypedKey<?>>(8);
241
242         static {
243             keyMap.add(MFG_INDEX, ComponentPreset.MANUFACTURER);
244             keyMap.add(PART_NO_INDEX, ComponentPreset.PARTNO);
245             keyMap.add(DESCRIPTION_INDEX, ComponentPreset.DESCRIPTION);
246             keyMap.add(UNITS_INDEX, UNITS_OF_MEASURE);
247             keyMap.add(ID_INDEX, ComponentPreset.INNER_DIAMETER);
248             keyMap.add(OD_INDEX, ComponentPreset.OUTER_DIAMETER);
249             keyMap.add(LENGTH_INDEX, ComponentPreset.LENGTH);
250             keyMap.add(MATERIAL_INDEX, ComponentPreset.MATERIAL);
251         }
252
253         public Collection<ComponentPreset> load(Map<String, Material> materialMap) {
254             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.BODY_TUBE);
255             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.BODY_TUBE);
256         }
257
258         public Collection<ComponentPreset> load(Map<String, Material> materialMap, File file) throws
259                                                                                               FileNotFoundException {
260             List<String[]> data = RocksimComponentFileLoader.load(file);
261             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.BODY_TUBE);
262         }
263
264     }
265
266     /**
267      * Tube coupler parser.  Although there are additional fields in the file, they are not used by
268      * most (any?) manufacturers so we ignore them entirely.
269      */
270     static class TubeCouplerLoader extends BodyTubeLoader {
271         public Collection<ComponentPreset> load(Map<String, Material> materialMap) {
272             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.TUBE_COUPLER);
273             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.TUBE_COUPLER);
274         }
275
276         public Collection<ComponentPreset> load(Map<String, Material> materialMap, File file) throws
277                                                                                               FileNotFoundException {
278             List<String[]> data = RocksimComponentFileLoader.load(file);
279             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.TUBE_COUPLER);
280         }
281     }
282
283     /**
284      * Engine block parser.  Although there are additional fields in the file, they are not used by
285      * most (any?) manufacturers so we ignore them entirely.
286      */
287     static class EngineBlockLoader extends BodyTubeLoader {
288         public Collection<ComponentPreset> load(Map<String, Material> materialMap) {
289             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.ENGINE_BLOCK);
290             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.ENGINE_BLOCK);
291         }
292
293         public Collection<ComponentPreset> load(Map<String, Material> materialMap, File file) throws
294                                                                                               FileNotFoundException {
295             List<String[]> data = RocksimComponentFileLoader.load(file);
296             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.ENGINE_BLOCK);
297         }
298     }
299
300
301     static class BulkheadLoader extends BodyTubeLoader {
302         public Collection<ComponentPreset> load(Map<String, Material> materialMap) {
303             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.BULKHEAD);
304             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.BULK_HEAD);
305         }
306
307         public Collection<ComponentPreset> load(Map<String, Material> materialMap, File file) throws
308                                                                                               FileNotFoundException {
309             List<String[]> data = RocksimComponentFileLoader.load(file);
310             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.BULK_HEAD);
311         }
312     }
313
314     static class CenteringRingLoader extends BodyTubeLoader {
315         public Collection<ComponentPreset> load(Map<String, Material> materialMap) {
316             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.CENTERING_RING);
317             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.CENTERING_RING);
318         }
319
320         public Collection<ComponentPreset> load(Map<String, Material> materialMap, File file) throws
321                                                                                               FileNotFoundException {
322             List<String[]> data = RocksimComponentFileLoader.load(file);
323             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.CENTERING_RING);
324         }
325     }
326
327     static class NoseConeLoader {
328         public static final int MFG_INDEX = 0;
329         public static final int PART_NO_INDEX = 1;
330         public static final int DESCRIPTION_INDEX = 2;
331         public static final int UNITS_INDEX = 3;
332         public static final int LENGTH_INDEX = 4;
333         public static final int OUTER_DIA_INDEX = 5;
334         public static final int LD_RATIO_INDEX = 6;
335         public static final int INSERT_LENGTH_INDEX = 7;
336         public static final int INSERT_OD_INDEX = 8;
337         public static final int THICKNESS_INDEX = 9;
338         public static final int SHAPE_INDEX = 10;
339         public static final int CONFIG_INDEX = 11;
340         public static final int MATERIAL_INDEX = 12;
341         public static final int CG_LOC_INDEX = 13;
342         public static final int MASS_UNITS_INDEX = 14;
343         public static final int MASS_INDEX = 15;
344         public static final int BASE_EXT_LEN_INDEX = 16;
345
346         public final static TypedKey<Double> LD_RATIO = new TypedKey<Double>("Len/Dia Ratio", Double.class);
347         public final static TypedKey<Double> BASE_EXT_LEN = new TypedKey<Double>("Base Ext Len", Double.class, UnitGroup.UNITS_LENGTH);
348         public final static TypedKey<String> CONFIG = new TypedKey<String>("Config", String.class);
349         public final static TypedKey<Double> CG_LOC = new TypedKey<Double>("CG Loc", Double.class, UnitGroup.UNITS_LENGTH);
350         public final static List<TypedKey<?>> keyMap = new ArrayList<TypedKey<?>>(17);
351
352         static {
353             keyMap.add(MFG_INDEX, ComponentPreset.MANUFACTURER);
354             keyMap.add(PART_NO_INDEX, ComponentPreset.PARTNO);
355             keyMap.add(DESCRIPTION_INDEX, ComponentPreset.DESCRIPTION);
356             keyMap.add(UNITS_INDEX, UNITS_OF_MEASURE);
357             keyMap.add(LENGTH_INDEX, ComponentPreset.LENGTH);
358             keyMap.add(OUTER_DIA_INDEX, ComponentPreset.OUTER_DIAMETER);
359             keyMap.add(LD_RATIO_INDEX, LD_RATIO);
360             keyMap.add(INSERT_LENGTH_INDEX, ComponentPreset.SHOULDER_LENGTH);
361             keyMap.add(INSERT_OD_INDEX, ComponentPreset.SHOULDER_DIAMETER);
362             keyMap.add(THICKNESS_INDEX, ComponentPreset.THICKNESS);
363             keyMap.add(SHAPE_INDEX, ComponentPreset.SHAPE);
364             keyMap.add(CONFIG_INDEX, CONFIG);
365             keyMap.add(MATERIAL_INDEX, ComponentPreset.MATERIAL);
366             keyMap.add(CG_LOC_INDEX, CG_LOC);
367             keyMap.add(MASS_UNITS_INDEX, UNITS_OF_MEASURE);
368             keyMap.add(MASS_INDEX, ComponentPreset.MASS);
369             keyMap.add(BASE_EXT_LEN_INDEX, BASE_EXT_LEN);
370         }
371
372         public Collection<ComponentPreset> load(Map<String, Material> materialMap) {
373             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.NOSE_CONE);
374             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.NOSE_CONE);
375         }
376
377         public Collection<ComponentPreset> load(Map<String, Material> materialMap, File file) throws
378                                                                                               FileNotFoundException {
379             List<String[]> data = RocksimComponentFileLoader.load(file);
380             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.NOSE_CONE);
381         }
382     }
383
384     static class TransitionLoader {
385         public static final int MFG_INDEX = 0;
386         public static final int PART_NO_INDEX = 1;
387         public static final int DESCRIPTION_INDEX = 2;
388         public static final int UNITS_INDEX = 3;
389         public static final int FRONT_INSERT_LENGTH_INDEX = 4;
390         public static final int FRONT_INSERT_OD_INDEX = 5;
391         public static final int FRONT_OD_INDEX = 6;
392         public static final int LENGTH_INDEX = 7;
393         public static final int REAR_OD_INDEX = 8;
394         public static final int CORE_DIA_INDEX = 9;
395         public static final int REAR_INSERT_LENGTH_INDEX = 10;
396         public static final int REAR_INSERT_OD_INDEX = 11;
397         public static final int THICKNESS_INDEX = 12;
398         public static final int CONFIG_INDEX = 13;
399         public static final int MATERIAL_INDEX = 14;
400         public static final int CG_LOC_INDEX = 15;
401         public static final int MASS_UNITS_INDEX = 16;
402         public static final int MASS_INDEX = 17;
403         public static final int SHAPE_INDEX = 18;
404
405         public final static TypedKey<String> CONFIG = new TypedKey<String>("Config", String.class);
406         public final static TypedKey<String> IGNORE = new TypedKey<String>("Ignore", String.class);
407         public final static TypedKey<Double> CG_LOC = new TypedKey<Double>("CG Loc", Double.class, UnitGroup.UNITS_LENGTH);
408         public final static List<TypedKey<?>> keyMap = new ArrayList<TypedKey<?>>(19);
409
410         static {
411             keyMap.add(MFG_INDEX, ComponentPreset.MANUFACTURER);
412             keyMap.add(PART_NO_INDEX, ComponentPreset.PARTNO);
413             keyMap.add(DESCRIPTION_INDEX, ComponentPreset.DESCRIPTION);
414             keyMap.add(UNITS_INDEX, UNITS_OF_MEASURE);
415             keyMap.add(FRONT_INSERT_LENGTH_INDEX, ComponentPreset.FORE_SHOULDER_LENGTH);
416             keyMap.add(FRONT_INSERT_OD_INDEX, ComponentPreset.FORE_SHOULDER_DIAMETER);
417             keyMap.add(FRONT_OD_INDEX, ComponentPreset.FORE_OUTER_DIAMETER);
418             keyMap.add(LENGTH_INDEX, ComponentPreset.LENGTH);
419             keyMap.add(REAR_OD_INDEX, ComponentPreset.OUTER_DIAMETER);
420             keyMap.add(CORE_DIA_INDEX, IGNORE);
421             keyMap.add(REAR_INSERT_LENGTH_INDEX, ComponentPreset.SHOULDER_LENGTH);
422             keyMap.add(REAR_INSERT_OD_INDEX, ComponentPreset.SHOULDER_DIAMETER);
423             keyMap.add(THICKNESS_INDEX, ComponentPreset.THICKNESS);
424             keyMap.add(CONFIG_INDEX, CONFIG);
425             keyMap.add(MATERIAL_INDEX, ComponentPreset.MATERIAL);
426             keyMap.add(CG_LOC_INDEX, CG_LOC);
427             keyMap.add(MASS_UNITS_INDEX, UNITS_OF_MEASURE);
428             keyMap.add(MASS_INDEX, ComponentPreset.MASS);
429             keyMap.add(SHAPE_INDEX, ComponentPreset.SHAPE);
430         }
431
432         public Collection<ComponentPreset> load(Map<String, Material> materialMap) {
433             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.TRANSITION);
434             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.TRANSITION);
435         }
436
437         public Collection<ComponentPreset> load(Map<String, Material> materialMap, File file) throws
438                                                                                               FileNotFoundException {
439             List<String[]> data = RocksimComponentFileLoader.load(file);
440             return commonLoader(data, keyMap, materialMap, ComponentPreset.Type.TRANSITION);
441         }
442     }
443
444     static class MaterialLoader {
445         private final static int MATERIAL_INDEX = 0;
446         private final static int UNITS_INDEX = 1;
447         private final static int DENSITY_INDEX = 2;
448         private final static int LOW_INDEX = 3;
449         private final static int HIGH_INDEX = 4;
450         private final static int CLASS_INDEX = 5;
451         private final static int ROCKETRY_USE_INDEX = 6;
452         private final static int BODY_TUBES_INDEX = 7;
453         public static final int FIN_SETS_INDEX = 8;
454         public static final int LAUNCH_LUGS_INDEX = 9;
455         public static final int CORDS_INDEX = 10;
456         public static final int NOSE_INDEX = 11;
457         public static final int PARACHUTE_INDEX = 12;
458         public static final int STREAMER_INDEX = 13;
459         public static final int TRANSITION_INDEX = 14;
460         public static final int RING_INDEX = 15;
461         public static final int BULKHEAD_INDEX = 16;
462         public static final int ENGINE_BLOCK_INDEX = 17;
463         public static final int SLEEVE_INDEX = 18;
464         public static final int TUBE_COUPLER_INDEX = 19;
465         public static final int KNOWN_DIM_TYPE_INDEX = 27;
466         public static final int KNOWN_DIM_UNITS_INDEX = 28;
467         public static final int KNOWN_DIM_VALUE_INDEX = 29;
468
469         public final static List<TypedKey<?>> keyMap = new ArrayList<TypedKey<?>>(8);
470         public final static TypedKey<String> MATERIAL_NAME = new TypedKey<String>("Material Name", String.class);
471         public final static TypedKey<Double> DENSITY = new TypedKey<Double>("Density", Double.class);
472
473         static class MaterialAdapter {
474             Material.Type type;
475             double conversionFactor;
476
477             MaterialAdapter(Material.Type theType, double cf) {
478                 type = theType;
479                 conversionFactor = cf;
480             }
481         }
482
483         private final static Map<String, MaterialAdapter> materialAdapterMap = new HashMap<String, MaterialAdapter>();
484
485         static {
486             materialAdapterMap.put("g/cm", new MaterialAdapter(Material.Type.LINE, 0.1d));
487             materialAdapterMap.put("g/cm2", new MaterialAdapter(Material.Type.SURFACE, 10.0d));
488             materialAdapterMap.put("g/cm3", new MaterialAdapter(Material.Type.BULK, 1000.0d));
489             materialAdapterMap.put("kg/m3", new MaterialAdapter(Material.Type.BULK, 1d));
490             materialAdapterMap.put("lb/ft3", new MaterialAdapter(Material.Type.BULK, 16.0184634d));
491             materialAdapterMap.put("oz/in", new MaterialAdapter(Material.Type.LINE, 1.11612296d));
492             materialAdapterMap.put("oz/in2", new MaterialAdapter(Material.Type.SURFACE, 43.9418487));
493
494             keyMap.add(MATERIAL_INDEX, MATERIAL_NAME);
495             keyMap.add(UNITS_INDEX, UNITS_OF_MEASURE);
496             keyMap.add(DENSITY_INDEX, DENSITY);
497         }
498
499         static Map<String, Material> load() {
500             List<String[]> data = RocksimComponentFileLoader.load(RocksimComponentFileType.MATERIAL);
501             Map<String, Material> materialMap = new HashMap<String, Material>();
502
503             for (int i = 0; i < data.size(); i++) {
504                 try {
505                     String[] strings = data.get(i);
506                     MaterialAdapter ma = materialAdapterMap.get(strings[UNITS_INDEX]);
507                     double metricDensity = ma.conversionFactor * Double.parseDouble(strings[DENSITY_INDEX]);
508                     final String cleanedMaterialName = stripAll(strings[MATERIAL_INDEX], '"').trim();
509                     final Material material = Databases.findMaterial(ma.type, cleanedMaterialName,
510                             metricDensity, true);
511                     materialMap.put(cleanedMaterialName, material);
512                     materialMap.put(cleanedMaterialName.toLowerCase(), material);
513                     materialMap.put(toCamelCase(cleanedMaterialName), material);
514                 }
515                 catch (Exception e) {
516                     //Trap a bad row and move on
517                     //TODO: log it?  Display to user?
518                 }
519             }
520             return materialMap;
521         }
522     }
523
524     public static void main(String[] args) {
525         Application.setPreferences(new SwingPreferences());
526         Map<String, Material> materialMap = MaterialLoader.load();
527         Collection<ComponentPreset> presetNC = new NoseConeLoader().load(materialMap);
528         Collection<ComponentPreset> presetBC = new BodyTubeLoader().load(materialMap);
529         Collection<ComponentPreset> presetBH = new BulkheadLoader().load(materialMap);
530         Collection<ComponentPreset> presetCR = new CenteringRingLoader().load(materialMap);
531         Collection<ComponentPreset> presetTC = new TubeCouplerLoader().load(materialMap);
532         Collection<ComponentPreset> presetTR = new TransitionLoader().load(materialMap);
533         Collection<ComponentPreset> presetEB = new EngineBlockLoader().load(materialMap);
534
535         for (Iterator<ComponentPreset> iterator = presetNC.iterator(); iterator.hasNext(); ) {
536             ComponentPreset next = iterator.next();
537             System.err.println(next);
538         }
539         for (Iterator<ComponentPreset> iterator = presetBC.iterator(); iterator.hasNext(); ) {
540             ComponentPreset next = iterator.next();
541             System.err.println(next);
542         }
543         for (Iterator<ComponentPreset> iterator = presetBH.iterator(); iterator.hasNext(); ) {
544             ComponentPreset next = iterator.next();
545             System.err.println(next);
546         }
547         for (Iterator<ComponentPreset> iterator = presetCR.iterator(); iterator.hasNext(); ) {
548             ComponentPreset next = iterator.next();
549             System.err.println(next);
550         }
551         for (Iterator<ComponentPreset> iterator = presetTC.iterator(); iterator.hasNext(); ) {
552             ComponentPreset next = iterator.next();
553             System.err.println(next);
554         }
555         for (Iterator<ComponentPreset> iterator = presetTR.iterator(); iterator.hasNext(); ) {
556             ComponentPreset next = iterator.next();
557             System.err.println(next);
558         }
559         for (Iterator<ComponentPreset> iterator = presetEB.iterator(); iterator.hasNext(); ) {
560             ComponentPreset next = iterator.next();
561             System.err.println(next);
562         }
563     }
564 }
565
566 //Errata:
567 //The oddities I've found thus far in the stock Rocksim data:
568 //1. BTDATA.CSV - Totally Tubular goofed up their part no. and description columns (They messed up TCDATA also)
569 //2. NCDATA.CSV - Estes Balsa nose cones are classified as G10 Fiberglass
570 //3. TRDATA.CSV - Apogee Saturn LEM Transition has no part number; Balsa Machining transitions have blank diameter