+++ /dev/null
-package tim.prune.load;
-
-import tim.prune.I18nManager;
-import tim.prune.data.Field;
-import tim.prune.data.Latitude;
-import tim.prune.data.Longitude;
-import tim.prune.data.TimestampUtc;
-
-/**
- * Class to try to match data with field names,
- * using a variety of guessing techniques
- */
-public abstract class FieldGuesser
-{
- /**
- * Try to guess whether the given line is a header line or data
- * @param inValues array of values from first non-blank line of file
- * @return true if it looks like a header row, false if it looks like data
- */
- private static boolean isHeaderRow(String[] inValues)
- {
- // Loop over values seeing if any are mostly numeric
- if (inValues != null)
- {
- for (String value : inValues)
- {
- if (fieldLooksNumeric(value)) {return false;}
- }
- }
- // No (mostly) numeric values found so presume header
- return true;
- }
-
-
- /**
- * See if a field looks numeric or not by comparing the number of numeric vs non-numeric characters
- * @param inValue field value to check
- * @return true if there are more numeric characters than not
- */
- private static boolean fieldLooksNumeric(String inValue)
- {
- if (inValue == null) {
- return false;
- }
- final int numChars = inValue.length();
- if (numChars < 3) {return false;} // Don't care about one or two character values
- // Loop through characters seeing which ones are numeric and which not
- int numNums = 0;
- for (int i=0; i<numChars; i++)
- {
- char currChar = inValue.charAt(i);
- if (currChar >= '0' && currChar <= '9') {numNums++;}
- }
- // Return true if more than half the characters are numeric
- return numNums > (numChars/2);
- }
-
- /**
- * Try to guess the fields for the given values from the file
- * @param inValues array of values from first non-blank line of file
- * @return array of fields which hopefully match
- */
- public static Field[] guessFields(String[] inValues)
- {
- // Guess whether it's a header line or not
- boolean isHeader = isHeaderRow(inValues);
- // make array of Fields
- int numFields = inValues.length;
- Field[] fields = new Field[numFields];
- // Loop over fields to try to guess the main ones
- for (int f=0; f<numFields; f++)
- {
- if (inValues[f] != null) {
- String value = inValues[f].trim();
- // check for latitude
- if (!checkArrayHasField(fields, Field.LATITUDE) && fieldLooksLikeLatitude(value, isHeader))
- {
- fields[f] = Field.LATITUDE;
- continue;
- }
- // check for longitude
- if (!checkArrayHasField(fields, Field.LONGITUDE) && fieldLooksLikeLongitude(value, isHeader))
- {
- fields[f] = Field.LONGITUDE;
- continue;
- }
- // check for altitude
- if (!checkArrayHasField(fields, Field.ALTITUDE) && fieldLooksLikeAltitude(value, isHeader))
- {
- fields[f] = Field.ALTITUDE;
- continue;
- }
- // check for waypoint name
- if (!checkArrayHasField(fields, Field.WAYPT_NAME) && fieldLooksLikeName(value, isHeader))
- {
- fields[f] = Field.WAYPT_NAME;
- continue;
- }
- // check for timestamp
- if (!checkArrayHasField(fields, Field.TIMESTAMP) && fieldLooksLikeTimestamp(value, isHeader))
- {
- fields[f] = Field.TIMESTAMP;
- continue;
- }
- // check for tracksegment
- if (!checkArrayHasField(fields, Field.NEW_SEGMENT) && fieldLooksLikeSegment(value, isHeader))
- {
- fields[f] = Field.NEW_SEGMENT;
- continue;
- }
- // check for waypoint type
- if (!checkArrayHasField(fields, Field.WAYPT_TYPE) && fieldLooksLikeWaypointType(value, isHeader))
- {
- fields[f] = Field.WAYPT_TYPE;
- continue;
- }
- }
- }
- // Fill in the rest of the fields using just custom fields
- // Could try to guess other fields (waypoint type, segment) or unguessed altitude, name, but keep simple for now
- String customPrefix = I18nManager.getText("fieldname.prefix") + " ";
- int customFieldNum = 0;
- for (int f=0; f<numFields; f++) {
- if (fields[f] == null)
- {
- // Make sure lat and long are filled in if not already
- if (!checkArrayHasField(fields, Field.LATITUDE)) {
- fields[f] = Field.LATITUDE;
- }
- else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
- fields[f] = Field.LONGITUDE;
- }
- else
- {
- // Can we use the field name given?
- Field customField = null;
- if (isHeader && inValues[f] != null && inValues[f].length() > 0) {
- customField = new Field(inValues[f]);
- }
- // Find an unused field number
- while (customField == null || checkArrayHasField(fields, customField))
- {
- customFieldNum++;
- customField = new Field(customPrefix + (customFieldNum));
- }
- fields[f] = customField;
- }
- }
- }
-
- // Do a final check to make sure lat and long are in there
- if (!checkArrayHasField(fields, Field.LATITUDE)) {
- fields[0] = Field.LATITUDE;
- }
- else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
- fields[1] = Field.LONGITUDE;
- }
- // Longitude _could_ have overwritten latitude in position 1
- if (!checkArrayHasField(fields, Field.LATITUDE)) {
- fields[0] = Field.LATITUDE;
- }
- return fields;
- }
-
-
- /**
- * Check whether the given field array has the specified field
- * @param inFields
- * @param inCheckField
- * @return true if Field is contained within the array
- */
- private static boolean checkArrayHasField(Field[] inFields, Field inCheckField)
- {
- for (int f=0; f<inFields.length; f++)
- {
- if (inFields[f] != null && inFields[f].equals(inCheckField)) {
- return true;
- }
- }
- // not found
- return false;
- }
-
-
- /**
- * Check whether the given String looks like a Latitude value
- * @param inValue value from file
- * @param inIsHeader true if this is a header line, false for data
- * @return true if it could be latitude
- */
- private static boolean fieldLooksLikeLatitude(String inValue, boolean inIsHeader)
- {
- if (inValue == null || inValue.equals("")) {return false;}
- if (inIsHeader)
- {
- // This is a header line so look for english or local text
- String upperValue = inValue.toUpperCase();
- return (upperValue.equals("LATITUDE")
- || upperValue.equals(I18nManager.getText("fieldname.latitude").toUpperCase()));
- }
- else
- {
- // Note this will also catch longitudes too
- Latitude lat = new Latitude(inValue);
- return lat.isValid();
- }
- }
-
- /**
- * Check whether the given String looks like a Longitude value
- * @param inValue value from file
- * @param inIsHeader true if this is a header line, false for data
- * @return true if it could be longitude
- */
- private static boolean fieldLooksLikeLongitude(String inValue, boolean inIsHeader)
- {
- if (inValue == null || inValue.equals("")) {return false;}
- if (inIsHeader)
- {
- // This is a header line so look for english or local text
- String upperValue = inValue.toUpperCase();
- return (upperValue.equals("LONGITUDE")
- || upperValue.equals(I18nManager.getText("fieldname.longitude").toUpperCase()));
- }
- else
- {
- // Note this will also catch latitudes too
- Longitude lon = new Longitude(inValue);
- return lon.isValid();
- }
- }
-
- /**
- * Check whether the given String looks like an Altitude value
- * @param inValue value from file
- * @param inIsHeader true if this is a header line, false for data
- * @return true if it could be altitude
- */
- private static boolean fieldLooksLikeAltitude(String inValue, boolean inIsHeader)
- {
- if (inValue == null || inValue.equals("")) {return false;}
- if (inIsHeader)
- {
- // This is a header line so look for english or local text
- String upperValue = inValue.toUpperCase();
- return (upperValue.equals("ALTITUDE")
- || upperValue.equals("ALT")
- || upperValue.equals("HMSL") // height above mean sea level
- || upperValue.equals(I18nManager.getText("fieldname.altitude").toUpperCase()));
- }
- else
- {
- // Look for a number less than 100000
- try
- {
- int intValue = Integer.parseInt(inValue);
- return (intValue > 0 && intValue < 100000);
- }
- catch (NumberFormatException nfe) {}
- return false;
- }
- }
-
-
- /**
- * Check whether the given String looks like a waypoint name
- * @param inValue value from file
- * @param inIsHeader true if this is a header line, false for data
- * @return true if it could be a name
- */
- private static boolean fieldLooksLikeName(String inValue, boolean inIsHeader)
- {
- if (inValue == null || inValue.equals("")) {return false;}
- if (inIsHeader)
- {
- // This is a header line so look for english or local text
- String upperValue = inValue.toUpperCase();
- return (upperValue.equals("NAME")
- || upperValue.equals("LABEL")
- || upperValue.equals(I18nManager.getText("fieldname.waypointname").toUpperCase()));
- }
- else
- {
- // Look for at least two letters in it
- int numLetters = 0;
- for (int i=0; i<inValue.length(); i++)
- {
- char currChar = inValue.charAt(i);
- if (Character.isLetter(currChar)) {
- numLetters++;
- }
- // Not interested if it contains ":" or "."
- if (currChar == ':' || currChar == '.') {return false;}
- }
- return numLetters >= 2;
- }
- }
-
- /**
- * Check whether the given String looks like a timestamp
- * @param inValue value from file
- * @param inIsHeader true if this is a header line, false for data
- * @return true if it could be a timestamp
- */
- private static boolean fieldLooksLikeTimestamp(String inValue, boolean inIsHeader)
- {
- if (inValue == null || inValue.equals("")) {return false;}
- if (inIsHeader)
- {
- String upperValue = inValue.toUpperCase();
- // This is a header line so look for english or local text
- return (upperValue.equals("TIMESTAMP")
- || upperValue.equals("TIME")
- || upperValue.equals(I18nManager.getText("fieldname.timestamp").toUpperCase()));
- }
- else
- {
- // must be at least 7 characters long
- if (inValue.length() < 7) {return false;}
- TimestampUtc stamp = new TimestampUtc(inValue);
- return stamp.isValid();
- }
- }
-
- /**
- * Check whether the given String looks like a track segment field
- * @param inValue value from file
- * @param inIsHeader true if this is a header line, false for data
- * @return true if it could be a track segment
- */
- private static boolean fieldLooksLikeSegment(String inValue, boolean inIsHeader)
- {
- if (inValue == null || inValue.equals("")) {return false;}
- if (inIsHeader)
- {
- String upperValue = inValue.toUpperCase();
- // This is a header line so look for english or local text
- return upperValue.equals("SEGMENT")
- || upperValue.equals(I18nManager.getText("fieldname.newsegment").toUpperCase());
- }
- else
- {
- // can't reliably identify it just using the value
- return false;
- }
- }
-
- /**
- * Check whether the given String looks like a waypoint type
- * @param inValue value from file
- * @param inIsHeader true if this is a header line, false for data
- * @return true if it could be a waypoint type
- */
- private static boolean fieldLooksLikeWaypointType(String inValue, boolean inIsHeader)
- {
- if (inValue == null || inValue.equals("")) {return false;}
- if (inIsHeader)
- {
- String upperValue = inValue.toUpperCase();
- // This is a header line so look for english or local text
- return (upperValue.equals("TYPE")
- || upperValue.equals(I18nManager.getText("fieldname.waypointtype").toUpperCase()));
- }
- else
- {
- // can't reliably identify it just using the value
- return false;
- }
- }
-}