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;
83 // check for tracksegment
84 if (!checkArrayHasField(fields, Field.NEW_SEGMENT) && fieldLooksLikeSegment(value, isHeader))
86 fields[f] = Field.NEW_SEGMENT;
89 // check for waypoint type
90 if (!checkArrayHasField(fields, Field.WAYPT_TYPE) && fieldLooksLikeWaypointType(value, isHeader))
92 fields[f] = Field.WAYPT_TYPE;
97 // Fill in the rest of the fields using just custom fields
98 // Could try to guess other fields (waypoint type, segment) or unguessed altitude, name, but keep simple for now
99 String customPrefix = I18nManager.getText("fieldname.prefix") + " ";
100 int customFieldNum = 0;
101 for (int f=0; f<numFields; f++) {
102 if (fields[f] == null)
104 // Make sure lat and long are filled in if not already
105 if (!checkArrayHasField(fields, Field.LATITUDE)) {
106 fields[f] = Field.LATITUDE;
108 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
109 fields[f] = Field.LONGITUDE;
113 fields[f] = new Field(customPrefix + (customFieldNum));
117 // Do a final check to make sure lat and long are in there
118 if (!checkArrayHasField(fields, Field.LATITUDE)) {
119 fields[0] = Field.LATITUDE;
121 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
122 fields[1] = Field.LONGITUDE;
124 // Longitude _could_ have overwritten latitude in position 1
125 if (!checkArrayHasField(fields, Field.LATITUDE)) {
126 fields[0] = Field.LATITUDE;
133 * Check whether the given field array has the specified field
135 * @param inCheckField
136 * @return true if Field is contained within the array
138 private static boolean checkArrayHasField(Field[] inFields, Field inCheckField)
140 for (int f=0; f<inFields.length; f++)
142 if (inFields[f] != null && inFields[f].equals(inCheckField)) {
152 * Check whether the given String looks like a Latitude value
153 * @param inValue value from file
154 * @param inIsHeader true if this is a header line, false for data
155 * @return true if it could be latitude
157 private static boolean fieldLooksLikeLatitude(String inValue, boolean inIsHeader)
159 if (inValue == null || inValue.equals("")) {return false;}
162 // This is a header line so look for english or local text
163 String upperValue = inValue.toUpperCase();
164 return (upperValue.equals("LATITUDE")
165 || upperValue.equals(I18nManager.getText("fieldname.latitude").toUpperCase()));
169 // Note this will also catch longitudes too
170 Latitude lat = new Latitude(inValue);
171 return lat.isValid();
176 * Check whether the given String looks like a Longitude value
177 * @param inValue value from file
178 * @param inIsHeader true if this is a header line, false for data
179 * @return true if it could be longitude
181 private static boolean fieldLooksLikeLongitude(String inValue, boolean inIsHeader)
183 if (inValue == null || inValue.equals("")) {return false;}
186 // This is a header line so look for english or local text
187 String upperValue = inValue.toUpperCase();
188 return (upperValue.equals("LONGITUDE")
189 || upperValue.equals(I18nManager.getText("fieldname.longitude").toUpperCase()));
193 // Note this will also catch latitudes too
194 Longitude lon = new Longitude(inValue);
195 return lon.isValid();
200 * Check whether the given String looks like an Altitude value
201 * @param inValue value from file
202 * @param inIsHeader true if this is a header line, false for data
203 * @return true if it could be altitude
205 private static boolean fieldLooksLikeAltitude(String inValue, boolean inIsHeader)
207 if (inValue == null || inValue.equals("")) {return false;}
210 // This is a header line so look for english or local text
211 String upperValue = inValue.toUpperCase();
212 return (upperValue.equals("ALTITUDE")
213 || upperValue.equals("ALT")
214 || upperValue.equals("HMSL") // height above mean sea level
215 || upperValue.equals(I18nManager.getText("fieldname.altitude").toUpperCase()));
219 // Look for a number less than 100000
222 int intValue = Integer.parseInt(inValue);
223 return (intValue > 0 && intValue < 100000);
225 catch (NumberFormatException nfe) {}
232 * Check whether the given String looks like a waypoint name
233 * @param inValue value from file
234 * @param inIsHeader true if this is a header line, false for data
235 * @return true if it could be a name
237 private static boolean fieldLooksLikeName(String inValue, boolean inIsHeader)
239 if (inValue == null || inValue.equals("")) {return false;}
242 // This is a header line so look for english or local text
243 String upperValue = inValue.toUpperCase();
244 return (upperValue.equals("NAME")
245 || upperValue.equals("LABEL")
246 || upperValue.equals(I18nManager.getText("fieldname.waypointname").toUpperCase()));
250 // Look for at least two letters in it
252 for (int i=0; i<inValue.length(); i++)
254 char currChar = inValue.charAt(i);
255 if (Character.isLetter(currChar)) {
258 // Not interested if it contains ":" or "."
259 if (currChar == ':' || currChar == '.') {return false;}
261 return numLetters >= 2;
266 * Check whether the given String looks like a timestamp
267 * @param inValue value from file
268 * @param inIsHeader true if this is a header line, false for data
269 * @return true if it could be a timestamp
271 private static boolean fieldLooksLikeTimestamp(String inValue, boolean inIsHeader)
273 if (inValue == null || inValue.equals("")) {return false;}
276 String upperValue = inValue.toUpperCase();
277 // This is a header line so look for english or local text
278 return (upperValue.equals("TIMESTAMP")
279 || upperValue.equals("TIME")
280 || upperValue.equals(I18nManager.getText("fieldname.timestamp").toUpperCase()));
284 // must be at least 7 characters long
285 if (inValue.length() < 7) {return false;}
286 Timestamp stamp = new Timestamp(inValue);
287 return stamp.isValid();
292 * Check whether the given String looks like a track segment field
293 * @param inValue value from file
294 * @param inIsHeader true if this is a header line, false for data
295 * @return true if it could be a track segment
297 private static boolean fieldLooksLikeSegment(String inValue, boolean inIsHeader)
299 if (inValue == null || inValue.equals("")) {return false;}
302 String upperValue = inValue.toUpperCase();
303 // This is a header line so look for english or local text
304 return upperValue.equals("SEGMENT")
305 || upperValue.equals(I18nManager.getText("fieldname.newsegment").toUpperCase());
309 // can't reliably identify it just using the value
315 * Check whether the given String looks like a waypoint type
316 * @param inValue value from file
317 * @param inIsHeader true if this is a header line, false for data
318 * @return true if it could be a waypoint type
320 private static boolean fieldLooksLikeWaypointType(String inValue, boolean inIsHeader)
322 if (inValue == null || inValue.equals("")) {return false;}
325 String upperValue = inValue.toUpperCase();
326 // This is a header line so look for english or local text
327 return (upperValue.equals("TYPE")
328 || upperValue.equals(I18nManager.getText("fieldname.waypointtype").toUpperCase()));
332 // can't reliably identify it just using the value