package com.nutiteq.services;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Vector;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import com.java4ever.apime.io.GZIP;
import com.nutiteq.components.Distance;
import com.nutiteq.components.DurationTime;
import com.nutiteq.components.Line;
import com.nutiteq.components.Route;
import com.nutiteq.components.RouteInstruction;
import com.nutiteq.components.RouteSummary;
import com.nutiteq.components.WgsBoundingBox;
import com.nutiteq.components.WgsPoint;
import com.nutiteq.log.Log;
import com.nutiteq.utils.Utils;
public class OpenLSReader {
private static final String ROUTE_SUMMARY_TAG = "xls:RouteSummary";
private static final String TOTAL_TIME_TAG = "xls:TotalTime";
private static final String TOTAL_DISTANCE_TAG = "xls:TotalDistance";
private static final String BOUNDING_BOX_TAG = "xls:BoundingBox";
private static final String POSITION_TAG = "gml:pos";
private static final String LINE_STRING_TAG = "gml:LineString";
private static final String ROUTE_INSTRUCTIONS_LIST_TAG = "xls:RouteInstructionsList";
private static final String ROUTE_INSTRUCTION_TAG = "xls:RouteInstruction";
private static final String INSTRUCTION_TAG = "xls:Instruction";
private static final String DISTANCE_TAG = "xls:distance";
private static final String POINT_TAG = "xls:Point";
private static final String ERROR_TAG = "xls:Error";
private static final String ERROR_LIST_TAG = "xls:ErrorList";
private final byte[] data;
private final DirectionsWaiter waiter;
//TODO jaanus : directions waiter should only be in one place. all here or remove from here
public OpenLSReader(final byte[] data, final DirectionsWaiter waiter) {
this.data = data;
this.waiter = waiter;
}
public Route read() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(GZIP.inflate(data));
InputStreamReader reader;
try {
reader = new InputStreamReader(bais, "utf-8");
} catch (final Exception e) {
reader = new InputStreamReader(bais);
}
return read(reader);
}
protected Route read(final InputStreamReader isr) throws IOException {
final KXmlParser parser = new KXmlParser();
RouteSummary summary = null;
Line routeLine = null;
RouteInstruction[] instructions = null;
try {
parser.setInput(isr);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
final String tagName = parser.getName();
if (ROUTE_SUMMARY_TAG.equals(tagName)) {
summary = readRouteSummary(parser);
} else if (LINE_STRING_TAG.equals(tagName)) {
routeLine = readRouteLine(parser);
} else if (ROUTE_INSTRUCTIONS_LIST_TAG.equals(tagName)) {
instructions = readRouteInstrictions(parser);
} else if (ERROR_LIST_TAG.equals(tagName)) {
final int errorCodes = readErrorCodes(parser);
waiter.routingErrors(errorCodes);
return null;
}
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.printStackTrace(e);
}
return new Route(summary, routeLine, instructions);
}
private int readErrorCodes(final KXmlParser parser) throws IOException {
final Vector errors = new Vector();
try {
int eventType = parser.next();
while (!ERROR_LIST_TAG.equals(parser.getName())) {
if (eventType == XmlPullParser.START_TAG && ERROR_TAG.equals(parser.getName())) {
errors.addElement(parser.getAttributeValue(null, "errorCode"));
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.printStackTrace(e);
}
if (errors.size() == 0) {
return 0;
}
int result = Integer.parseInt((String) errors.elementAt(0));
for (int i = 1; i < errors.size(); i++) {
result = result | Integer.parseInt((String) errors.elementAt(i));
}
return result;
}
private RouteInstruction[] readRouteInstrictions(final KXmlParser parser) throws IOException {
final Vector instructions = new Vector();
try {
int eventType = parser.next();
while (!ROUTE_INSTRUCTIONS_LIST_TAG.equals(parser.getName())) {
if (eventType == XmlPullParser.START_TAG && ROUTE_INSTRUCTION_TAG.equals(parser.getName())) {
instructions.addElement(readInstructionPoint(parser));
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.printStackTrace(e);
}
final RouteInstruction[] result = new RouteInstruction[instructions.size()];
instructions.copyInto(result);
return result;
}
private RouteInstruction readInstructionPoint(final KXmlParser parser) throws IOException {
final String pointDescription = parser.getAttributeValue(null, "description");
final DurationTime duration = parseDuration(parser.getAttributeValue(null, "duration"));
final int pointNumber = Integer.parseInt(parser.getAttributeValue(null, "tour"));
final int icon;
String instruction = null;
Distance distance = null;
WgsPoint point = null;
if ("".equals(pointDescription)) {
icon = pointNumber == 1 ? DirectionsService.IMAGE_ROUTE_START
: DirectionsService.IMAGE_ROUTE_END;
} else {
icon = Integer.parseInt(pointDescription);
}
try {
int eventType = parser.next();
while (!ROUTE_INSTRUCTION_TAG.equals(parser.getName())) {
if (eventType == XmlPullParser.START_TAG) {
final String tagName = parser.getName();
if (INSTRUCTION_TAG.equals(tagName)) {
instruction = parser.nextText();
} else if (DISTANCE_TAG.equals(tagName)) {
distance = readDistance(parser);
} else if (POINT_TAG.equals(tagName)) {
point = parseCoordinates(parser.nextText());
}
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.printStackTrace(e);
}
return new RouteInstruction(pointNumber, icon, duration, instruction, distance, point);
}
private Line readRouteLine(final KXmlParser parser) throws IOException {
final Vector points = new Vector();
try {
int eventType = parser.next();
while (!LINE_STRING_TAG.equals(parser.getName())) {
if (eventType == XmlPullParser.START_TAG && POSITION_TAG.equals(parser.getName())) {
points.addElement(parseCoordinates(parser.nextText()));
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.printStackTrace(e);
}
final WgsPoint[] routePoints = new WgsPoint[points.size()];
points.copyInto(routePoints);
return new Line(routePoints);
}
private RouteSummary readRouteSummary(final KXmlParser parser) throws IOException {
DurationTime totalTime = null;
Distance distance = null;
WgsBoundingBox boundingBox = null;
try {
int eventType = parser.next();
while (!ROUTE_SUMMARY_TAG.equals(parser.getName())) {
if (XmlPullParser.START_TAG == eventType) {
final String tagName = parser.getName();
if (TOTAL_TIME_TAG.equals(tagName)) {
totalTime = parseDuration(parser.nextText());
} else if (TOTAL_DISTANCE_TAG.equals(tagName)) {
distance = readDistance(parser);
} else if (BOUNDING_BOX_TAG.equals(tagName)) {
boundingBox = readBoundingBox(parser);
}
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.printStackTrace(e);
}
return new RouteSummary(totalTime, distance, boundingBox);
}
private Distance readDistance(final KXmlParser parser) {
final String distanceString = parser.getAttributeValue(null, "value");
final String uom = parser.getAttributeValue(null, "uom");
try {
return new Distance(Float.parseFloat(distanceString), uom);
} catch (final NumberFormatException e) {
Log.printStackTrace(e);
return new Distance(0, "");
}
}
private WgsBoundingBox readBoundingBox(final KXmlParser parser) throws IOException {
final Vector points = new Vector();
try {
int eventType = parser.next();
while (!BOUNDING_BOX_TAG.equals(parser.getName())) {
if (XmlPullParser.START_TAG == eventType) {
final String tagName = parser.getName();
if (POSITION_TAG.equals(tagName)) {
points.addElement(parseCoordinates(parser.nextText()));
}
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.printStackTrace(e);
}
if (points.size() != 2) {
return null;
}
final WgsPoint[] coordinates = new WgsPoint[2];
points.copyInto(coordinates);
final WgsPoint min = new WgsPoint(Math.min(coordinates[0].getLon(), coordinates[1].getLon()),
Math.min(coordinates[0].getLat(), coordinates[1].getLat()));
final WgsPoint max = new WgsPoint(Math.max(coordinates[0].getLon(), coordinates[1].getLon()),
Math.max(coordinates[0].getLat(), coordinates[1].getLat()));
return new WgsBoundingBox(min, max);
}
private WgsPoint parseCoordinates(final String coordinates) {
final String[] split = Utils.split(coordinates, " ");
if (split.length != 2) {
return null;
}
try {
return new WgsPoint(Double.parseDouble(split[1].trim()), Double.parseDouble(split[0].trim()));
} catch (final NumberFormatException e) {
Log.printStackTrace(e);
return null;
}
}
protected DurationTime parseDuration(final String time) {
if (time == null || "".equals(time.trim())) {
return new DurationTime(0, 0, 0, 0);
}
try {
final int daysInt = time.indexOf("D") > 0 ? Integer.parseInt(time.substring(1, time
.indexOf("D"))) : 0;
final int hoursInt = Integer.parseInt(time
.substring(time.indexOf("T") + 1, time.indexOf("H")));
final int minutesInt = Integer.parseInt(time.substring(time.indexOf("H") + 1, time
.indexOf("M")));
final int secondsInt = Integer.parseInt(time.substring(time.indexOf("M") + 1, time
.indexOf("S")));
return new DurationTime(daysInt, hoursInt, minutesInt, secondsInt);
} catch (final NumberFormatException e) {
Log.error("Error parsing duration from " + time);
Log.printStackTrace(e);
return null;
}
}
}