1 package tim.prune.load;
3 import tim.prune.I18nManager;
4 import tim.prune.data.Field;
5 import tim.prune.data.Latitude;
6 import tim.prune.data.Longitude;
7 import tim.prune.data.Timestamp;
10 * Class to try to match data with field names,
11 * using a variety of guessing techniques
13 public abstract class FieldGuesser
16 * Try to guess whether the given line is a header line or data
17 * @param inValues array of values from first non-blank line of file
18 * @return true if it looks like a header row, false if it looks like data
20 private static boolean isHeaderRow(String[] inValues)
22 // Loop over values looking for a Latitude value
25 for (int v=0; v<inValues.length; v++)
27 Latitude lat = new Latitude(inValues[v]);
28 if (lat.isValid()) {return false;}
31 // No valid Latitude value found so presume header
37 * Try to guess the fields for the given values from the file
38 * @param inValues array of values from first non-blank line of file
39 * @return array of fields which hopefully match
41 public static Field[] guessFields(String[] inValues)
43 // Guess whether it's a header line or not
44 boolean isHeader = isHeaderRow(inValues);
45 // make array of Fields
46 int numFields = inValues.length;
47 Field[] fields = new Field[numFields];
48 // Loop over fields to try to guess the main ones
49 for (int f=0; f<numFields; f++)
51 if (inValues[f] != null) {
52 String value = inValues[f].trim();
54 if (!checkArrayHasField(fields, Field.LATITUDE) && fieldLooksLikeLatitude(value, isHeader))
56 fields[f] = Field.LATITUDE;
59 // check for longitude
60 if (!checkArrayHasField(fields, Field.LONGITUDE) && fieldLooksLikeLongitude(value, isHeader))
62 fields[f] = Field.LONGITUDE;
66 if (!checkArrayHasField(fields, Field.ALTITUDE) && fieldLooksLikeAltitude(value, isHeader))
68 fields[f] = Field.ALTITUDE;
71 // check for waypoint name
72 if (!checkArrayHasField(fields, Field.WAYPT_NAME) && fieldLooksLikeName(value, isHeader))
74 fields[f] = Field.WAYPT_NAME;
77 // check for timestamp
78 if (!checkArrayHasField(fields, Field.TIMESTAMP) && fieldLooksLikeTimestamp(value, isHeader))
80 fields[f] = Field.TIMESTAMP;
85 // Fill in the rest of the fields using just custom fields
86 // Could try to guess other fields (waypoint type, segment) or unguessed altitude, name, but keep simple for now
87 String customPrefix = I18nManager.getText("fieldname.prefix") + " ";
88 int customFieldNum = 0;
89 for (int f=0; f<numFields; f++) {
90 if (fields[f] == null)
92 // Make sure lat and long are filled in if not already
93 if (!checkArrayHasField(fields, Field.LATITUDE)) {
94 fields[f] = Field.LATITUDE;
96 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
97 fields[f] = Field.LONGITUDE;
101 fields[f] = new Field(customPrefix + (customFieldNum));
105 // Do a final check to make sure lat and long are in there
106 if (!checkArrayHasField(fields, Field.LATITUDE)) {
107 fields[0] = Field.LATITUDE;
109 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
110 fields[1] = Field.LONGITUDE;
117 * Check whether the given field array has the specified field
119 * @param inCheckField
120 * @return true if Field is contained within the array
122 private static boolean checkArrayHasField(Field[] inFields, Field inCheckField)
124 for (int f=0; f<inFields.length; f++)
126 if (inFields[f] != null && inFields[f].equals(inCheckField)) {
136 * Check whether the given String looks like a Latitude value
137 * @param inValue value from file
138 * @param inIsHeader true if this is a header line, false for data
139 * @return true if it could be latitude
141 private static boolean fieldLooksLikeLatitude(String inValue, boolean inIsHeader)
143 if (inValue == null || inValue.equals("")) {return false;}
146 // This is a header line so look for english or local text
147 String upperValue = inValue.toUpperCase();
148 return (upperValue.equals("LATITUDE")
149 || upperValue.equals(I18nManager.getText("fieldname.latitude").toUpperCase()));
153 // Note this will also catch longitudes too
154 Latitude lat = new Latitude(inValue);
155 return lat.isValid();
160 * Check whether the given String looks like a Longitude value
161 * @param inValue value from file
162 * @param inIsHeader true if this is a header line, false for data
163 * @return true if it could be longitude
165 private static boolean fieldLooksLikeLongitude(String inValue, boolean inIsHeader)
167 if (inValue == null || inValue.equals("")) {return false;}
170 // This is a header line so look for english or local text
171 String upperValue = inValue.toUpperCase();
172 return (upperValue.equals("LONGITUDE")
173 || upperValue.equals(I18nManager.getText("fieldname.longitude").toUpperCase()));
177 // Note this will also catch latitudes too
178 Longitude lon = new Longitude(inValue);
179 return lon.isValid();
184 * Check whether the given String looks like an Altitude value
185 * @param inValue value from file
186 * @param inIsHeader true if this is a header line, false for data
187 * @return true if it could be altitude
189 private static boolean fieldLooksLikeAltitude(String inValue, boolean inIsHeader)
191 if (inValue == null || inValue.equals("")) {return false;}
194 // This is a header line so look for english or local text
195 String upperValue = inValue.toUpperCase();
196 return (upperValue.equals("ALTITUDE")
197 || upperValue.equals("ALT")
198 || upperValue.equals(I18nManager.getText("fieldname.altitude").toUpperCase()));
202 // Look for a number less than 100000
205 int intValue = Integer.parseInt(inValue);
206 return (intValue > 0 && intValue < 100000);
208 catch (NumberFormatException nfe) {}
215 * Check whether the given String looks like a waypoint name
216 * @param inValue value from file
217 * @param inIsHeader true if this is a header line, false for data
218 * @return true if it could be a name
220 private static boolean fieldLooksLikeName(String inValue, boolean inIsHeader)
222 if (inValue == null || inValue.equals("")) {return false;}
225 // This is a header line so look for english or local text
226 String upperValue = inValue.toUpperCase();
227 return (upperValue.equals("NAME")
228 || upperValue.equals("LABEL")
229 || upperValue.equals(I18nManager.getText("fieldname.waypointname").toUpperCase()));
233 // Look for at least two letters in it
235 for (int i=0; i<inValue.length(); i++)
237 char currChar = inValue.charAt(i);
238 if (Character.isLetter(currChar)) {
241 // Not interested if it contains ":" or "."
242 if (currChar == ':' || currChar == '.') {return false;}
244 return numLetters >= 2;
249 * Check whether the given String looks like a timestamp
250 * @param inValue value from file
251 * @param inIsHeader true if this is a header line, false for data
252 * @return true if it could be a timestamp
254 private static boolean fieldLooksLikeTimestamp(String inValue, boolean inIsHeader)
256 if (inValue == null || inValue.equals("")) {return false;}
259 String upperValue = inValue.toUpperCase();
260 // This is a header line so look for english or local text
261 return (upperValue.equals("TIMESTAMP")
262 || upperValue.equals("TIME")
263 || upperValue.equals(I18nManager.getText("fieldname.timestamp").toUpperCase()));
267 // must be at least 7 characters long
268 if (inValue.length() < 7) {return false;}
269 Timestamp stamp = new Timestamp(inValue);
270 return stamp.isValid();