]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/load/FieldGuesser.java
Version 5, May 2008
[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                         }
90                 }
91                 // Fill in the rest of the fields using just custom fields
92                 // Could try to guess other fields (waypoint type, segment) or unguessed altitude, name, but keep simple for now
93                 String customPrefix = I18nManager.getText("fieldname.prefix") + " ";
94                 int customFieldNum = 0;
95                 for (int f=0; f<numFields; f++) {
96                         if (fields[f] == null)
97                         {
98                                 // Make sure lat and long are filled in if not already
99                                 if (!checkArrayHasField(fields, Field.LATITUDE)) {
100                                         fields[f] = Field.LATITUDE;
101                                 }
102                                 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
103                                         fields[f] = Field.LONGITUDE;
104                                 }
105                                 else {
106                                         customFieldNum++;
107                                         fields[f] = new Field(customPrefix + (customFieldNum));
108                                 }
109                         }
110                 }
111                 // Do a final check to make sure lat and long are in there
112                 if (!checkArrayHasField(fields, Field.LATITUDE)) {
113                         fields[0] = Field.LATITUDE;
114                 }
115                 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
116                         fields[1] = Field.LONGITUDE;
117                 }
118                 return fields;
119         }
120
121
122         /**
123          * Check whether the given field array has the specified field
124          * @param inFields
125          * @param inCheckField
126          * @return true if Field is contained within the array
127          */
128         private static boolean checkArrayHasField(Field[] inFields, Field inCheckField)
129         {
130                 for (int f=0; f<inFields.length; f++)
131                 {
132                         if (inFields[f] != null && inFields[f].equals(inCheckField)) {
133                                 return true;
134                         }
135                 }
136                 // not found
137                 return false;
138         }
139
140
141         /**
142          * Check whether the given String looks like a Latitude value
143          * @param inValue value from file
144          * @param inIsHeader true if this is a header line, false for data
145          * @return true if it could be latitude
146          */
147         private static boolean fieldLooksLikeLatitude(String inValue, boolean inIsHeader)
148         {
149                 if (inValue == null || inValue.equals("")) {return false;}
150                 if (inIsHeader)
151                 {
152                         // This is a header line so look for english or local text
153                         String upperValue = inValue.toUpperCase();
154                         return (upperValue.equals("LATITUDE")
155                                 || upperValue.equals(I18nManager.getText("fieldname.latitude").toUpperCase()));
156                 }
157                 else
158                 {
159                         // Note this will also catch longitudes too
160                         Latitude lat = new Latitude(inValue);
161                         return lat.isValid();
162                 }
163         }
164
165         /**
166          * Check whether the given String looks like a Longitude value
167          * @param inValue value from file
168          * @param inIsHeader true if this is a header line, false for data
169          * @return true if it could be longitude
170          */
171         private static boolean fieldLooksLikeLongitude(String inValue, boolean inIsHeader)
172         {
173                 if (inValue == null || inValue.equals("")) {return false;}
174                 if (inIsHeader)
175                 {
176                         // This is a header line so look for english or local text
177                         String upperValue = inValue.toUpperCase();
178                         return (upperValue.equals("LONGITUDE")
179                                 || upperValue.equals(I18nManager.getText("fieldname.longitude").toUpperCase()));
180                 }
181                 else
182                 {
183                         // Note this will also catch latitudes too
184                         Longitude lon = new Longitude(inValue);
185                         return lon.isValid();
186                 }
187         }
188
189         /**
190          * Check whether the given String looks like an Altitude value
191          * @param inValue value from file
192          * @param inIsHeader true if this is a header line, false for data
193          * @return true if it could be altitude
194          */
195         private static boolean fieldLooksLikeAltitude(String inValue, boolean inIsHeader)
196         {
197                 if (inValue == null || inValue.equals("")) {return false;}
198                 if (inIsHeader)
199                 {
200                         // This is a header line so look for english or local text
201                         String upperValue = inValue.toUpperCase();
202                         return (upperValue.equals("ALTITUDE")
203                                 || upperValue.equals("ALT")
204                                 || upperValue.equals(I18nManager.getText("fieldname.altitude").toUpperCase()));
205                 }
206                 else
207                 {
208                         // Look for a number less than 100000
209                         try
210                         {
211                                 int intValue = Integer.parseInt(inValue);
212                                 return (intValue > 0 && intValue < 100000);
213                         }
214                         catch (NumberFormatException nfe) {}
215                         return false;
216                 }
217         }
218
219
220         /**
221          * Check whether the given String looks like a waypoint name
222          * @param inValue value from file
223          * @param inIsHeader true if this is a header line, false for data
224          * @return true if it could be a name
225          */
226         private static boolean fieldLooksLikeName(String inValue, boolean inIsHeader)
227         {
228                 if (inValue == null || inValue.equals("")) {return false;}
229                 if (inIsHeader)
230                 {
231                         // This is a header line so look for english or local text
232                         String upperValue = inValue.toUpperCase();
233                         return (upperValue.equals("NAME")
234                                 || upperValue.equals("LABEL")
235                                 || upperValue.equals(I18nManager.getText("fieldname.waypointname").toUpperCase()));
236                 }
237                 else
238                 {
239                         // Look for at least two letters in it
240                         int numLetters = 0;
241                         for (int i=0; i<inValue.length(); i++)
242                         {
243                                 char currChar = inValue.charAt(i);
244                                 if (Character.isLetter(currChar)) {
245                                         numLetters++;
246                                 }
247                                 // Not interested if it contains ":" or "."
248                                 if (currChar == ':' || currChar == '.') {return false;}
249                         }
250                         return numLetters >= 2;
251                 }
252         }
253
254         /**
255          * Check whether the given String looks like a timestamp
256          * @param inValue value from file
257          * @param inIsHeader true if this is a header line, false for data
258          * @return true if it could be a timestamp
259          */
260         private static boolean fieldLooksLikeTimestamp(String inValue, boolean inIsHeader)
261         {
262                 if (inValue == null || inValue.equals("")) {return false;}
263                 if (inIsHeader)
264                 {
265                         String upperValue = inValue.toUpperCase();
266                         // This is a header line so look for english or local text
267                         return (upperValue.equals("TIMESTAMP")
268                                 || upperValue.equals("TIME")
269                                 || upperValue.equals(I18nManager.getText("fieldname.timestamp").toUpperCase()));
270                 }
271                 else
272                 {
273                         // must be at least 7 characters long
274                         if (inValue.length() < 7) {return false;}
275                         Timestamp stamp = new Timestamp(inValue);
276                         return stamp.isValid();
277                 }
278         }
279
280         /**
281          * Check whether the given String looks like a track segment field
282          * @param inValue value from file
283          * @param inIsHeader true if this is a header line, false for data
284          * @return true if it could be a track segment
285          */
286         private static boolean fieldLooksLikeSegment(String inValue, boolean inIsHeader)
287         {
288                 if (inValue == null || inValue.equals("")) {return false;}
289                 if (inIsHeader)
290                 {
291                         String upperValue = inValue.toUpperCase();
292                         // This is a header line so look for english or local text
293                         return upperValue.equals("SEGMENT");
294                 }
295                 else
296                 {
297                         // can't reliably identify it just using the value
298                         return false;
299                 }
300         }
301 }