1 package net.sf.openrocket.preset.loader;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.InputStreamReader;
11 import net.sf.openrocket.gui.print.PrintUnit;
12 import net.sf.openrocket.preset.TypedPropertyMap;
13 import net.sf.openrocket.unit.Unit;
14 import net.sf.openrocket.unit.UnitGroup;
15 import net.sf.openrocket.util.ArrayList;
16 import au.com.bytecode.opencsv.CSVReader;
19 * Primary entry point for parsing component CSV files that are in Rocksim format.
21 public abstract class RocksimComponentFileLoader {
23 public static String basePath = "";
25 protected List<RocksimComponentFileColumnParser> fileColumns;
27 public RocksimComponentFileLoader() {
29 fileColumns = new ArrayList<RocksimComponentFileColumnParser>();
32 protected abstract RocksimComponentFileType getFileType();
35 load( getFileType() );
38 * Read a comma separated component file and return the parsed contents as a list of string arrays. Not for
39 * production use - just here for smoke testing.
41 * @param type the type of component file to read; uses the default file name
42 * @return a list (guaranteed never to be null) of string arrays. Each element of the list represents a row in the
43 * component data file; the element in the list itself is an array of String, where each item in the array
44 * is a column (cell) in the row. The string array is in sequential order as it appeared in the file.
46 private void load(RocksimComponentFileType type) {
47 File dir = new File(basePath);
48 if ( !dir.exists() ) {
49 throw new IllegalArgumentException( basePath + " does not exist" );
51 if ( !dir.isDirectory() ) {
52 throw new IllegalArgumentException( basePath + " is not directory" );
54 if ( !dir.canRead() ) {
55 throw new IllegalArgumentException ( basePath + " is not readable" );
58 FileInputStream fis = new FileInputStream( new File(dir, type.getDefaultFileName()));
60 } catch (FileNotFoundException ex) {
66 * Read a comma separated component file and return the parsed contents as a list of string arrays.
68 * @param file the file to read and parse
69 * @return a list (guaranteed never to be null) of string arrays. Each element of the list represents a row in the
70 * component data file; the element in the list itself is an array of String, where each item in the array
71 * is a column (cell) in the row. The string array is in sequential order as it appeared in the file.
73 private void load(File file) throws FileNotFoundException {
74 load(new FileInputStream(file));
78 * Read a comma separated component file and return the parsed contents as a list of string arrays.
80 * @param is the stream to read and parse
81 * @return a list (guaranteed never to be null) of string arrays. Each element of the list represents a row in the
82 * component data file; the element in the list itself is an array of String, where each item in the array
83 * is a column (cell) in the row. The string array is in sequential order as it appeared in the file.
85 private void load(InputStream is) {
89 InputStreamReader r = null;
91 r = new InputStreamReader(is);
93 // Create the CSV reader. Use comma separator.
94 CSVReader reader = new CSVReader(r, ',', '\'', '\\');
96 //Read and throw away the header row.
97 parseHeaders(reader.readNext());
100 while( (data = reader.readNext()) != null ) {
101 // detect empty lines and skip:
102 if ( data.length == 0 ) {
105 if ( data.length == 1 && "".equals(data[0].trim())) {
110 //Read the rest of the file as data rows.
113 catch (IOException e) {
120 catch (IOException e) {
127 protected void parseHeaders( String[] headers ) {
128 for( RocksimComponentFileColumnParser column : fileColumns ) {
129 column.configure(headers);
133 protected void parseData( String[] data ) {
134 if ( data == null || data.length == 0 ) {
137 TypedPropertyMap props = new TypedPropertyMap();
141 for( RocksimComponentFileColumnParser column : fileColumns ) {
142 column.parse(data, props);
144 postProcess( props );
147 protected void preProcess( String[] data ) {
148 for( int i = 0; i< data.length; i++ ) {
154 d = stripAll(d, '"');
160 protected abstract void postProcess( TypedPropertyMap props );
163 * Rocksim CSV units are either inches or mm. A value of 0 or "in." indicate inches. A value of 1 or "mm" indicate
166 * @param units the value from the file
167 * @return true if it's inches
169 protected static boolean isInches(String units) {
170 String tmp = units.trim().toLowerCase();
171 return "0".equals(tmp) || tmp.startsWith("in");
175 * Convert inches or millimeters to meters.
177 * @param units a Rocksim CSV string representing the kind of units.
178 * @param value the original value within the CSV file
179 * @return the value in meters
181 protected static double convertLength(String units, double value) {
182 if (isInches(units)) {
183 return PrintUnit.INCHES.toMeters(value);
186 return PrintUnit.MILLIMETERS.toMeters(value);
190 protected static double convertMass(String units, double value ) {
191 if ( "oz".equals(units) ) {
192 Unit u = UnitGroup.UNITS_MASS.getUnit(2);
193 return u.fromUnit(value);
199 * Remove all occurrences of the given character. Note: this is done because some manufacturers embed double
200 * quotes in their descriptions or material names. Those are stripped away because they cause all sorts of
201 * matching/lookup issues.
203 * @param target the target string to be operated upon
204 * @param toBeRemoved the character to remove
205 * @return target, minus every occurrence of toBeRemoved
207 protected static String stripAll(String target, Character toBeRemoved) {
208 StringBuilder sb = new StringBuilder();
209 for (int i = 0; i < target.length(); i++) {
210 Character c = target.charAt(i);
211 if (!c.equals(toBeRemoved)) {
215 return sb.toString();
219 * Convert all words in a given string to Camel Case (first letter capitalized). Words are assumed to be
220 * separated by a space. Note: this is done because some manufacturers define their material name in Camel Case
221 * but the component part references the material in lower case. That causes matching/lookup issues that's
222 * easiest handled this way (rather than converting everything to lower case.
224 * @param target the target string to be operated upon
225 * @return target, with the first letter of each word in uppercase
227 protected static String toCamelCase(String target) {
228 StringBuilder sb = new StringBuilder();
229 String[] t = target.split("[ ]");
230 if (t != null && t.length > 0) {
231 for (String aT : t) {
233 s = s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
234 sb.append(s).append(" ");
236 return sb.toString().trim();
246 //The oddities I've found thus far in the stock Rocksim data:
247 //1. BTDATA.CSV - Totally Tubular goofed up their part no. and description columns (They messed up TCDATA also)
248 //2. NCDATA.CSV - Estes Balsa nose cones are classified as G10 Fiberglass
249 //3. TRDATA.CSV - Apogee Saturn LEM Transition has no part number; Balsa Machining transitions have blank diameter