package au.gov.amsa.util.nmea;
import java.util.Arrays;
import java.util.LinkedHashMap;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
/**
* Parses NMEA messages.
*
*/
public class NmeaMessageParser {
private static final String CHECKSUM_DELIMITER = "*";
private static final String PARAMETER_DELIMITER = ",";
private static final String CODE_DELIMITER = ":";
/**
* Return an {@link NmeaMessage} from the given NMEA line.
*
* @param line
* @return
*/
public NmeaMessage parse(String line) {
LinkedHashMap<String, String> tags = Maps.newLinkedHashMap();
String remaining;
if (line.startsWith("\\")) {
int tagFinish = line.lastIndexOf('\\', line.length() - 1);
if (tagFinish == -1)
throw new NmeaMessageParseException(
"no matching \\ symbol to finish tag block: " + line);
if (tagFinish == 0)
throw new NmeaMessageParseException("tag block is empty or not terminated");
tags = extractTags(line.substring(1, tagFinish));
remaining = line.substring(tagFinish + 1);
} else
remaining = line;
String[] items;
String checksum;
if (remaining.length() > 0) {
if (!remaining.contains("*"))
throw new NmeaMessageParseException("checksum delimiter * not found");
items = getNmeaItems(remaining);
// TODO validate message using checksum
checksum = line.substring(line.indexOf('*') + 1);
} else {
items = new String[] {};
// TODO decide what value to put here
checksum = "";
}
return new NmeaMessage(tags, Arrays.asList(items), checksum);
}
/**
* Returns the items from the comma delimited NMEA line. The last item
* always contains a * followed by a checksum. If * is not present
* {@link IndexOutOfBoundsException} will be thrown.
*
* @param line
* @return
*/
private static String[] getNmeaItems(String line) {
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(line,
PARAMETER_DELIMITER);
// remove the checksum from the end
String last = items[items.length - 1];
if (last.contains("*"))
items[items.length - 1] = last.substring(0, last.lastIndexOf('*'));
return items;
}
/**
* Returns the tags from the tag block section of the message (NMEA v4.0).
* If there is no tag block then returns an empty map.
*
* @param s
* @return
*/
public static LinkedHashMap<String, String> extractTags(String s) {
LinkedHashMap<String, String> map = Maps.newLinkedHashMap();
int c = s.lastIndexOf(CHECKSUM_DELIMITER);
if (c == -1) {
return map;
}
s = s.substring(0, c);
String[] items = s.split(PARAMETER_DELIMITER);
for (String item : items) {
int i = item.indexOf(CODE_DELIMITER);
if (i == -1)
throw new NmeaMessageParseException(
"TAG BLOCK parameter is not is format 'a:b' :" + s);
map.put(item.substring(0, i), item.substring(i + 1));
}
return map;
}
}