]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/load/FieldGuesser.java
Version 11, August 2010
[GpsPrune.git] / tim / prune / load / FieldGuesser.java
1 package tim.prune.load;
2
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;
8
9 /**
10  * Class to try to match data with field names,
11  * using a variety of guessing techniques
12  */
13 public abstract class FieldGuesser
14 {
15         /**
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
19          */
20         private static boolean isHeaderRow(String[] inValues)
21         {
22                 // Loop over values looking for a Latitude value
23                 if (inValues != null)
24                 {
25                         for (int v=0; v<inValues.length; v++)
26                         {
27                                 Latitude lat = new Latitude(inValues[v]);
28                                 if (lat.isValid()) {return false;}
29                         }
30                 }
31                 // No valid Latitude value found so presume header
32                 return true;
33         }
34
35
36         /**
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
40          */
41         public static Field[] guessFields(String[] inValues)
42         {
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++)
50                 {
51                         if (inValues[f] != null) {
52                                 String value = inValues[f].trim();
53                                 // check for latitude
54                                 if (!checkArrayHasField(fields, Field.LATITUDE) && fieldLooksLikeLatitude(value, isHeader))
55                                 {
56                                         fields[f] = Field.LATITUDE;
57                                         continue;
58                                 }
59                                 // check for longitude
60                                 if (!checkArrayHasField(fields, Field.LONGITUDE) && fieldLooksLikeLongitude(value, isHeader))
61                                 {
62                                         fields[f] = Field.LONGITUDE;
63                                         continue;
64                                 }
65                                 // check for altitude
66                                 if (!checkArrayHasField(fields, Field.ALTITUDE) && fieldLooksLikeAltitude(value, isHeader))
67                                 {
68                                         fields[f] = Field.ALTITUDE;
69                                         continue;
70                                 }
71                                 // check for waypoint name
72                                 if (!checkArrayHasField(fields, Field.WAYPT_NAME) && fieldLooksLikeName(value, isHeader))
73                                 {
74                                         fields[f] = Field.WAYPT_NAME;
75                                         continue;
76                                 }
77                                 // check for timestamp
78                                 if (!checkArrayHasField(fields, Field.TIMESTAMP) && fieldLooksLikeTimestamp(value, isHeader))
79                                 {
80                                         fields[f] = Field.TIMESTAMP;
81                                         continue;
82                                 }
83                                 // check for tracksegment
84                                 if (!checkArrayHasField(fields, Field.NEW_SEGMENT) && fieldLooksLikeSegment(value, isHeader))
85                                 {
86                                         fields[f] = Field.NEW_SEGMENT;
87                                         continue;
88                                 }
89                                 // check for waypoint type
90                                 if (!checkArrayHasField(fields, Field.WAYPT_TYPE) && fieldLooksLikeWaypointType(value, isHeader))
91                                 {
92                                         fields[f] = Field.WAYPT_TYPE;
93                                         continue;
94                                 }
95                         }
96                 }
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)
103                         {
104                                 // Make sure lat and long are filled in if not already
105                                 if (!checkArrayHasField(fields, Field.LATITUDE)) {
106                                         fields[f] = Field.LATITUDE;
107                                 }
108                                 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
109                                         fields[f] = Field.LONGITUDE;
110                                 }
111                                 else {
112                                         customFieldNum++;
113                                         fields[f] = new Field(customPrefix + (customFieldNum));
114                                 }
115                         }
116                 }
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;
120                 }
121                 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
122                         fields[1] = Field.LONGITUDE;
123                 }
124                 // Longitude _could_ have overwritten latitude in position 1
125                 if (!checkArrayHasField(fields, Field.LATITUDE)) {
126                         fields[0] = Field.LATITUDE;
127                 }
128                 return fields;
129         }
130
131
132         /**
133          * Check whether the given field array has the specified field
134          * @param inFields
135          * @param inCheckField
136          * @return true if Field is contained within the array
137          */
138         private static boolean checkArrayHasField(Field[] inFields, Field inCheckField)
139         {
140                 for (int f=0; f<inFields.length; f++)
141                 {
142                         if (inFields[f] != null && inFields[f].equals(inCheckField)) {
143                                 return true;
144                         }
145                 }
146                 // not found
147                 return false;
148         }
149
150
151         /**
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
156          */
157         private static boolean fieldLooksLikeLatitude(String inValue, boolean inIsHeader)
158         {
159                 if (inValue == null || inValue.equals("")) {return false;}
160                 if (inIsHeader)
161                 {
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()));
166                 }
167                 else
168                 {
169                         // Note this will also catch longitudes too
170                         Latitude lat = new Latitude(inValue);
171                         return lat.isValid();
172                 }
173         }
174
175         /**
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
180          */
181         private static boolean fieldLooksLikeLongitude(String inValue, boolean inIsHeader)
182         {
183                 if (inValue == null || inValue.equals("")) {return false;}
184                 if (inIsHeader)
185                 {
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()));
190                 }
191                 else
192                 {
193                         // Note this will also catch latitudes too
194                         Longitude lon = new Longitude(inValue);
195                         return lon.isValid();
196                 }
197         }
198
199         /**
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
204          */
205         private static boolean fieldLooksLikeAltitude(String inValue, boolean inIsHeader)
206         {
207                 if (inValue == null || inValue.equals("")) {return false;}
208                 if (inIsHeader)
209                 {
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(I18nManager.getText("fieldname.altitude").toUpperCase()));
215                 }
216                 else
217                 {
218                         // Look for a number less than 100000
219                         try
220                         {
221                                 int intValue = Integer.parseInt(inValue);
222                                 return (intValue > 0 && intValue < 100000);
223                         }
224                         catch (NumberFormatException nfe) {}
225                         return false;
226                 }
227         }
228
229
230         /**
231          * Check whether the given String looks like a waypoint name
232          * @param inValue value from file
233          * @param inIsHeader true if this is a header line, false for data
234          * @return true if it could be a name
235          */
236         private static boolean fieldLooksLikeName(String inValue, boolean inIsHeader)
237         {
238                 if (inValue == null || inValue.equals("")) {return false;}
239                 if (inIsHeader)
240                 {
241                         // This is a header line so look for english or local text
242                         String upperValue = inValue.toUpperCase();
243                         return (upperValue.equals("NAME")
244                                 || upperValue.equals("LABEL")
245                                 || upperValue.equals(I18nManager.getText("fieldname.waypointname").toUpperCase()));
246                 }
247                 else
248                 {
249                         // Look for at least two letters in it
250                         int numLetters = 0;
251                         for (int i=0; i<inValue.length(); i++)
252                         {
253                                 char currChar = inValue.charAt(i);
254                                 if (Character.isLetter(currChar)) {
255                                         numLetters++;
256                                 }
257                                 // Not interested if it contains ":" or "."
258                                 if (currChar == ':' || currChar == '.') {return false;}
259                         }
260                         return numLetters >= 2;
261                 }
262         }
263
264         /**
265          * Check whether the given String looks like a timestamp
266          * @param inValue value from file
267          * @param inIsHeader true if this is a header line, false for data
268          * @return true if it could be a timestamp
269          */
270         private static boolean fieldLooksLikeTimestamp(String inValue, boolean inIsHeader)
271         {
272                 if (inValue == null || inValue.equals("")) {return false;}
273                 if (inIsHeader)
274                 {
275                         String upperValue = inValue.toUpperCase();
276                         // This is a header line so look for english or local text
277                         return (upperValue.equals("TIMESTAMP")
278                                 || upperValue.equals("TIME")
279                                 || upperValue.equals(I18nManager.getText("fieldname.timestamp").toUpperCase()));
280                 }
281                 else
282                 {
283                         // must be at least 7 characters long
284                         if (inValue.length() < 7) {return false;}
285                         Timestamp stamp = new Timestamp(inValue);
286                         return stamp.isValid();
287                 }
288         }
289
290         /**
291          * Check whether the given String looks like a track segment field
292          * @param inValue value from file
293          * @param inIsHeader true if this is a header line, false for data
294          * @return true if it could be a track segment
295          */
296         private static boolean fieldLooksLikeSegment(String inValue, boolean inIsHeader)
297         {
298                 if (inValue == null || inValue.equals("")) {return false;}
299                 if (inIsHeader)
300                 {
301                         String upperValue = inValue.toUpperCase();
302                         // This is a header line so look for english or local text
303                         return upperValue.equals("SEGMENT")
304                                 || upperValue.equals(I18nManager.getText("fieldname.newsegment").toUpperCase());
305                 }
306                 else
307                 {
308                         // can't reliably identify it just using the value
309                         return false;
310                 }
311         }
312
313         /**
314          * Check whether the given String looks like a waypoint type
315          * @param inValue value from file
316          * @param inIsHeader true if this is a header line, false for data
317          * @return true if it could be a waypoint type
318          */
319         private static boolean fieldLooksLikeWaypointType(String inValue, boolean inIsHeader)
320         {
321                 if (inValue == null || inValue.equals("")) {return false;}
322                 if (inIsHeader)
323                 {
324                         String upperValue = inValue.toUpperCase();
325                         // This is a header line so look for english or local text
326                         return (upperValue.equals("TYPE")
327                                 || upperValue.equals(I18nManager.getText("fieldname.waypointtype").toUpperCase()));
328                 }
329                 else
330                 {
331                         // can't reliably identify it just using the value
332                         return false;
333                 }
334         }
335 }