mirror of
https://gitlab.com/oeffi/public-transport-enabler.git
synced 2025-07-14 16:40:30 +00:00
fix Bavaria by implementing mobile efa protocol
This commit is contained in:
parent
01bc6b6219
commit
d1a23d7fdb
6 changed files with 978 additions and 29 deletions
|
@ -22,12 +22,14 @@ import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
@ -333,7 +335,7 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
throw new JSONException("unknown type: " + type);
|
throw new JSONException("unknown type: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<Location> xmlStopfinderRequest(final Location constraint) throws IOException
|
private StringBuilder stopfinderRequestParameters(final Location constraint)
|
||||||
{
|
{
|
||||||
final StringBuilder parameters = new StringBuilder();
|
final StringBuilder parameters = new StringBuilder();
|
||||||
appendCommonRequestParams(parameters, "XML");
|
appendCommonRequestParams(parameters, "XML");
|
||||||
|
@ -351,6 +353,13 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
parameters.append("&useHouseNumberList=true");
|
parameters.append("&useHouseNumberList=true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Location> xmlStopfinderRequest(final Location constraint) throws IOException
|
||||||
|
{
|
||||||
|
final StringBuilder parameters = stopfinderRequestParameters(constraint);
|
||||||
|
|
||||||
final StringBuilder uri = new StringBuilder(stopFinderEndpoint);
|
final StringBuilder uri = new StringBuilder(stopFinderEndpoint);
|
||||||
if (!httpPost)
|
if (!httpPost)
|
||||||
uri.append(parameters);
|
uri.append(parameters);
|
||||||
|
@ -419,7 +428,147 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected NearbyStationsResult xmlCoordRequest(final int lat, final int lon, final int maxDistance, final int maxStations) throws IOException
|
private class LocationAndQuality implements Serializable, Comparable<LocationAndQuality>
|
||||||
|
{
|
||||||
|
public final Location location;
|
||||||
|
public final int quality;
|
||||||
|
|
||||||
|
public LocationAndQuality(final Location location, final int quality)
|
||||||
|
{
|
||||||
|
this.location = location;
|
||||||
|
this.quality = quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(final LocationAndQuality other)
|
||||||
|
{
|
||||||
|
// prefer quality
|
||||||
|
if (this.quality > other.quality)
|
||||||
|
return -1;
|
||||||
|
else if (this.quality < other.quality)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// prefer stations
|
||||||
|
final int compareLocationType = this.location.type.compareTo(other.location.type);
|
||||||
|
if (compareLocationType != 0)
|
||||||
|
return compareLocationType;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o)
|
||||||
|
{
|
||||||
|
if (o == this)
|
||||||
|
return true;
|
||||||
|
if (!(o instanceof LocationAndQuality))
|
||||||
|
return false;
|
||||||
|
final LocationAndQuality other = (LocationAndQuality) o;
|
||||||
|
return location.equals(other.location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return location.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return quality + ":" + location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Location> mobileStopfinderRequest(final Location constraint) throws IOException
|
||||||
|
{
|
||||||
|
final StringBuilder parameters = stopfinderRequestParameters(constraint);
|
||||||
|
|
||||||
|
final StringBuilder uri = new StringBuilder(stopFinderEndpoint);
|
||||||
|
if (!httpPost)
|
||||||
|
uri.append(parameters);
|
||||||
|
|
||||||
|
// System.out.println(uri);
|
||||||
|
// System.out.println(parameters);
|
||||||
|
|
||||||
|
InputStream is = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
is = ParserUtils.scrapeInputStream(uri.toString(), httpPost ? parameters.substring(1) : null, null, httpReferer, null, 3);
|
||||||
|
|
||||||
|
final XmlPullParser pp = parserFactory.newPullParser();
|
||||||
|
pp.setInput(is, null);
|
||||||
|
enterEfa(pp);
|
||||||
|
|
||||||
|
final List<LocationAndQuality> locations = new ArrayList<LocationAndQuality>();
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "sf");
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "p"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "p");
|
||||||
|
|
||||||
|
final String name = normalizeLocationName(requireValueTag(pp, "n"));
|
||||||
|
final String u = requireValueTag(pp, "u");
|
||||||
|
if (!"sf".equals(u))
|
||||||
|
throw new RuntimeException("unknown usage: " + u);
|
||||||
|
final String ty = requireValueTag(pp, "ty");
|
||||||
|
final LocationType type;
|
||||||
|
if ("stop".equals(ty))
|
||||||
|
type = LocationType.STATION;
|
||||||
|
else if ("poi".equals(ty))
|
||||||
|
type = LocationType.POI;
|
||||||
|
else if ("loc".equals(ty))
|
||||||
|
type = LocationType.ADDRESS;
|
||||||
|
else if ("street".equals(ty))
|
||||||
|
type = LocationType.ADDRESS;
|
||||||
|
else if ("singlehouse".equals(ty))
|
||||||
|
type = LocationType.ADDRESS;
|
||||||
|
else
|
||||||
|
throw new RuntimeException("unknown type: " + ty);
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "r");
|
||||||
|
|
||||||
|
final int id = Integer.parseInt(requireValueTag(pp, "id"));
|
||||||
|
requireValueTag(pp, "omc");
|
||||||
|
final String place = normalizeLocationName(optValueTag(pp, "pc"));
|
||||||
|
requireValueTag(pp, "pid");
|
||||||
|
final Point coord = coordStrToPoint(optValueTag(pp, "c"));
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "r");
|
||||||
|
|
||||||
|
final String qal = optValueTag(pp, "qal");
|
||||||
|
final int quality = qal != null ? Integer.parseInt(qal) : 0;
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "p");
|
||||||
|
|
||||||
|
final Location location = new Location(type, type == LocationType.STATION ? id : 0, coord != null ? coord.lat : 0,
|
||||||
|
coord != null ? coord.lon : 0, place, name);
|
||||||
|
final LocationAndQuality locationAndQuality = new LocationAndQuality(location, quality);
|
||||||
|
locations.add(locationAndQuality);
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "sf");
|
||||||
|
|
||||||
|
Collections.sort(locations);
|
||||||
|
|
||||||
|
final List<Location> results = new ArrayList<Location>(locations.size());
|
||||||
|
for (final LocationAndQuality location : locations)
|
||||||
|
results.add(location.location);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
catch (final XmlPullParserException x)
|
||||||
|
{
|
||||||
|
throw new ParserException(x);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (is != null)
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuilder xmlCoordRequestParameters(final int lat, final int lon, final int maxDistance, final int maxStations)
|
||||||
{
|
{
|
||||||
final StringBuilder parameters = new StringBuilder();
|
final StringBuilder parameters = new StringBuilder();
|
||||||
appendCommonRequestParams(parameters, "XML");
|
appendCommonRequestParams(parameters, "XML");
|
||||||
|
@ -429,6 +578,13 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
parameters.append("&inclFilter=1&radius_1=").append(maxDistance != 0 ? maxDistance : 1320);
|
parameters.append("&inclFilter=1&radius_1=").append(maxDistance != 0 ? maxDistance : 1320);
|
||||||
parameters.append("&type_1=STOP"); // ENTRANCE, BUS_POINT, POI_POINT
|
parameters.append("&type_1=STOP"); // ENTRANCE, BUS_POINT, POI_POINT
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NearbyStationsResult xmlCoordRequest(final int lat, final int lon, final int maxDistance, final int maxStations) throws IOException
|
||||||
|
{
|
||||||
|
final StringBuilder parameters = xmlCoordRequestParameters(lat, lon, maxDistance, maxStations);
|
||||||
|
|
||||||
final StringBuilder uri = new StringBuilder(coordEndpoint);
|
final StringBuilder uri = new StringBuilder(coordEndpoint);
|
||||||
if (!httpPost)
|
if (!httpPost)
|
||||||
uri.append(parameters);
|
uri.append(parameters);
|
||||||
|
@ -493,6 +649,78 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected NearbyStationsResult mobileCoordRequest(final int lat, final int lon, final int maxDistance, final int maxStations) throws IOException
|
||||||
|
{
|
||||||
|
final StringBuilder parameters = xmlCoordRequestParameters(lat, lon, maxDistance, maxStations);
|
||||||
|
|
||||||
|
final StringBuilder uri = new StringBuilder(coordEndpoint);
|
||||||
|
if (!httpPost)
|
||||||
|
uri.append(parameters);
|
||||||
|
|
||||||
|
// System.out.println(uri);
|
||||||
|
// System.out.println(parameters);
|
||||||
|
|
||||||
|
InputStream is = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
is = ParserUtils.scrapeInputStream(uri.toString(), httpPost ? parameters.substring(1) : null, null, httpReferer, null, 3);
|
||||||
|
|
||||||
|
final XmlPullParser pp = parserFactory.newPullParser();
|
||||||
|
pp.setInput(is, null);
|
||||||
|
final ResultHeader header = enterEfa(pp);
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "ci");
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "request");
|
||||||
|
XmlPullUtil.exit(pp, "request");
|
||||||
|
|
||||||
|
final List<Location> stations = new ArrayList<Location>();
|
||||||
|
|
||||||
|
if (XmlPullUtil.test(pp, "pis"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "pis");
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "pi"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "pi");
|
||||||
|
|
||||||
|
final String name = normalizeLocationName(requireValueTag(pp, "de"));
|
||||||
|
final String type = requireValueTag(pp, "ty");
|
||||||
|
if (!"STOP".equals(type))
|
||||||
|
throw new RuntimeException("unknown type");
|
||||||
|
|
||||||
|
final int id = Integer.parseInt(requireValueTag(pp, "id"));
|
||||||
|
requireValueTag(pp, "omc");
|
||||||
|
requireValueTag(pp, "pid");
|
||||||
|
final String place = normalizeLocationName(requireValueTag(pp, "locality"));
|
||||||
|
requireValueTag(pp, "layer");
|
||||||
|
requireValueTag(pp, "gisID");
|
||||||
|
requireValueTag(pp, "ds");
|
||||||
|
final Point coord = coordStrToPoint(requireValueTag(pp, "c"));
|
||||||
|
|
||||||
|
stations.add(new Location(LocationType.STATION, id, coord.lat, coord.lon, place, name));
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "pi");
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "pis");
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "ci");
|
||||||
|
|
||||||
|
return new NearbyStationsResult(header, stations);
|
||||||
|
}
|
||||||
|
catch (final XmlPullParserException x)
|
||||||
|
{
|
||||||
|
throw new ParserException(x);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (is != null)
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<Location> autocompleteStations(final CharSequence constraint) throws IOException
|
public List<Location> autocompleteStations(final CharSequence constraint) throws IOException
|
||||||
{
|
{
|
||||||
return jsonStopfinderRequest(new Location(LocationType.ANY, 0, null, constraint.toString()));
|
return jsonStopfinderRequest(new Location(LocationType.ANY, 0, null, constraint.toString()));
|
||||||
|
@ -1127,7 +1355,7 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
+ "' trainType='" + trainType + "' trainNum='" + trainNum + "' trainName='" + trainName + "'");
|
+ "' trainType='" + trainType + "' trainNum='" + trainNum + "' trainName='" + trainName + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryDeparturesResult queryDepartures(final int stationId, final int maxDepartures, final boolean equivs) throws IOException
|
protected StringBuilder queryDeparturesParameters(final int stationId, final int maxDepartures, final boolean equivs)
|
||||||
{
|
{
|
||||||
final StringBuilder parameters = new StringBuilder();
|
final StringBuilder parameters = new StringBuilder();
|
||||||
appendCommonRequestParams(parameters, "XML");
|
appendCommonRequestParams(parameters, "XML");
|
||||||
|
@ -1141,6 +1369,13 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
if (maxDepartures > 0)
|
if (maxDepartures > 0)
|
||||||
parameters.append("&limit=").append(maxDepartures);
|
parameters.append("&limit=").append(maxDepartures);
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryDeparturesResult queryDepartures(final int stationId, final int maxDepartures, final boolean equivs) throws IOException
|
||||||
|
{
|
||||||
|
final StringBuilder parameters = queryDeparturesParameters(stationId, maxDepartures, equivs);
|
||||||
|
|
||||||
final StringBuilder uri = new StringBuilder(departureMonitorEndpoint);
|
final StringBuilder uri = new StringBuilder(departureMonitorEndpoint);
|
||||||
if (!httpPost)
|
if (!httpPost)
|
||||||
uri.append(parameters);
|
uri.append(parameters);
|
||||||
|
@ -1352,6 +1587,191 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected QueryDeparturesResult queryDeparturesMobile(final int stationId, final int maxDepartures, final boolean equivs) throws IOException
|
||||||
|
{
|
||||||
|
final StringBuilder parameters = queryDeparturesParameters(stationId, maxDepartures, equivs);
|
||||||
|
|
||||||
|
final StringBuilder uri = new StringBuilder(departureMonitorEndpoint);
|
||||||
|
if (!httpPost)
|
||||||
|
uri.append(parameters);
|
||||||
|
|
||||||
|
// System.out.println(uri);
|
||||||
|
// System.out.println(parameters);
|
||||||
|
|
||||||
|
InputStream is = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
is = ParserUtils.scrapeInputStream(uri.toString(), httpPost ? parameters.substring(1) : null, null, httpReferer, null, 3);
|
||||||
|
|
||||||
|
final XmlPullParser pp = parserFactory.newPullParser();
|
||||||
|
pp.setInput(is, null);
|
||||||
|
final ResultHeader header = enterEfa(pp);
|
||||||
|
final QueryDeparturesResult result = new QueryDeparturesResult(header);
|
||||||
|
|
||||||
|
XmlPullUtil.require(pp, "dps");
|
||||||
|
if (!pp.isEmptyElementTag())
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "dps");
|
||||||
|
|
||||||
|
final Calendar plannedDepartureTime = new GregorianCalendar(timeZone());
|
||||||
|
final Calendar predictedDepartureTime = new GregorianCalendar(timeZone());
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "dp"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "dp");
|
||||||
|
|
||||||
|
// misc
|
||||||
|
/* final String stationName = */normalizeLocationName(requireValueTag(pp, "n"));
|
||||||
|
final boolean isRealtime = requireValueTag(pp, "realtime").equals("1");
|
||||||
|
|
||||||
|
XmlPullUtil.optSkip(pp, "dt");
|
||||||
|
|
||||||
|
// time
|
||||||
|
parseMobileSt(pp, plannedDepartureTime, predictedDepartureTime);
|
||||||
|
|
||||||
|
final LineDestination lineDestination = parseMobileM(pp, true);
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "r");
|
||||||
|
final int assignedId = Integer.parseInt(requireValueTag(pp, "id"));
|
||||||
|
requireValueTag(pp, "a");
|
||||||
|
final String position = optValueTag(pp, "pl");
|
||||||
|
XmlPullUtil.exit(pp, "r");
|
||||||
|
|
||||||
|
/* final Point positionCoordinate = */coordStrToPoint(optValueTag(pp, "c"));
|
||||||
|
|
||||||
|
// TODO messages
|
||||||
|
|
||||||
|
StationDepartures stationDepartures = findStationDepartures(result.stationDepartures, assignedId);
|
||||||
|
if (stationDepartures == null)
|
||||||
|
{
|
||||||
|
stationDepartures = new StationDepartures(new Location(LocationType.STATION, assignedId), new ArrayList<Departure>(
|
||||||
|
maxDepartures), null);
|
||||||
|
result.stationDepartures.add(stationDepartures);
|
||||||
|
}
|
||||||
|
|
||||||
|
stationDepartures.departures.add(new Departure(plannedDepartureTime.getTime(),
|
||||||
|
predictedDepartureTime.isSet(Calendar.HOUR_OF_DAY) ? predictedDepartureTime.getTime() : null, lineDestination.line,
|
||||||
|
position, lineDestination.destination, null, null));
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "dp");
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "dps");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new QueryDeparturesResult(header, QueryDeparturesResult.Status.INVALID_STATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final XmlPullParserException x)
|
||||||
|
{
|
||||||
|
throw new ParserException(x);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (is != null)
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Pattern P_MOBILE_M_SYMBOL = Pattern.compile("([^\\s]*)\\s+([^\\s]*)");
|
||||||
|
|
||||||
|
private LineDestination parseMobileM(final XmlPullParser pp, final boolean tyOrCo) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "m");
|
||||||
|
|
||||||
|
final String n = optValueTag(pp, "n");
|
||||||
|
final String productNu = requireValueTag(pp, "nu");
|
||||||
|
final String ty = requireValueTag(pp, "ty");
|
||||||
|
|
||||||
|
final Line line;
|
||||||
|
final Location destination;
|
||||||
|
if ("100".equals(ty) || "99".equals(ty))
|
||||||
|
{
|
||||||
|
destination = null;
|
||||||
|
line = Line.FOOTWAY;
|
||||||
|
}
|
||||||
|
else if ("98".equals(ty))
|
||||||
|
{
|
||||||
|
destination = null;
|
||||||
|
line = Line.SECURE_CONNECTION;
|
||||||
|
}
|
||||||
|
else if ("97".equals(ty))
|
||||||
|
{
|
||||||
|
destination = null;
|
||||||
|
line = Line.DO_NOT_CHANGE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
final String co = requireValueTag(pp, "co");
|
||||||
|
final String productType = tyOrCo ? ty : co;
|
||||||
|
final String destinationName = normalizeLocationName(requireValueTag(pp, "des"));
|
||||||
|
destination = new Location(LocationType.ANY, 0, null, destinationName);
|
||||||
|
optValueTag(pp, "dy");
|
||||||
|
final String de = optValueTag(pp, "de");
|
||||||
|
final String productName = n != null ? n : de;
|
||||||
|
final String lineId = parseMobileDv(pp);
|
||||||
|
|
||||||
|
final String symbol = productNu.endsWith(" " + productName) ? productNu.substring(0, productNu.length() - productName.length() - 1)
|
||||||
|
: productNu;
|
||||||
|
final String trainType;
|
||||||
|
final String trainNum;
|
||||||
|
final Matcher mSymbol = P_MOBILE_M_SYMBOL.matcher(symbol);
|
||||||
|
if (mSymbol.matches())
|
||||||
|
{
|
||||||
|
trainType = mSymbol.group(1);
|
||||||
|
trainNum = mSymbol.group(2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trainType = null;
|
||||||
|
trainNum = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String lineLabel = parseLine(productType, symbol, symbol, null, trainType, trainNum, productName);
|
||||||
|
line = new Line(lineId, lineLabel, lineStyle(lineLabel));
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "m");
|
||||||
|
|
||||||
|
return new LineDestination(line, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseMobileDv(final XmlPullParser pp) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "dv");
|
||||||
|
optValueTag(pp, "branch");
|
||||||
|
final String lineIdLi = requireValueTag(pp, "li");
|
||||||
|
final String lineIdSu = requireValueTag(pp, "su");
|
||||||
|
final String lineIdPr = requireValueTag(pp, "pr");
|
||||||
|
final String lineIdDct = requireValueTag(pp, "dct");
|
||||||
|
final String lineIdNe = requireValueTag(pp, "ne");
|
||||||
|
XmlPullUtil.exit(pp, "dv");
|
||||||
|
|
||||||
|
return lineIdNe + ":" + lineIdLi + ":" + lineIdSu + ":" + lineIdDct + ":" + lineIdPr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseMobileSt(final XmlPullParser pp, final Calendar plannedDepartureTime, final Calendar predictedDepartureTime)
|
||||||
|
throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "st");
|
||||||
|
|
||||||
|
plannedDepartureTime.clear();
|
||||||
|
ParserUtils.parseIsoDate(plannedDepartureTime, requireValueTag(pp, "da"));
|
||||||
|
ParserUtils.parseIsoTime(plannedDepartureTime, requireValueTag(pp, "t"));
|
||||||
|
|
||||||
|
predictedDepartureTime.clear();
|
||||||
|
if (XmlPullUtil.test(pp, "rda"))
|
||||||
|
{
|
||||||
|
ParserUtils.parseIsoDate(predictedDepartureTime, requireValueTag(pp, "rda"));
|
||||||
|
ParserUtils.parseIsoTime(predictedDepartureTime, requireValueTag(pp, "rt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "st");
|
||||||
|
}
|
||||||
|
|
||||||
private StationDepartures findStationDepartures(final List<StationDepartures> stationDepartures, final int id)
|
private StationDepartures findStationDepartures(final List<StationDepartures> stationDepartures, final int id)
|
||||||
{
|
{
|
||||||
for (final StationDepartures stationDeparture : stationDepartures)
|
for (final StationDepartures stationDeparture : stationDepartures)
|
||||||
|
@ -1653,6 +2073,41 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected QueryTripsResult queryTripsMobile(final Location from, final Location via, final Location to, final Date date, final boolean dep,
|
||||||
|
final int numTrips, final Collection<Product> products, final WalkSpeed walkSpeed, final Accessibility accessibility,
|
||||||
|
final Set<Option> options) throws IOException
|
||||||
|
{
|
||||||
|
|
||||||
|
final String parameters = xsltTripRequestParameters(from, via, to, date, dep, numTrips, products, walkSpeed, accessibility, options);
|
||||||
|
|
||||||
|
final StringBuilder uri = new StringBuilder(tripEndpoint);
|
||||||
|
if (!httpPost)
|
||||||
|
uri.append(parameters);
|
||||||
|
|
||||||
|
// System.out.println(uri);
|
||||||
|
// System.out.println(parameters);
|
||||||
|
|
||||||
|
InputStream is = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
is = ParserUtils.scrapeInputStream(uri.toString(), httpPost ? parameters.substring(1) : null, null, httpRefererTrip, "NSC_", 3);
|
||||||
|
return queryTripsMobile(uri.toString(), from, via, to, is);
|
||||||
|
}
|
||||||
|
catch (final XmlPullParserException x)
|
||||||
|
{
|
||||||
|
throw new ParserException(x);
|
||||||
|
}
|
||||||
|
catch (final RuntimeException x)
|
||||||
|
{
|
||||||
|
throw new RuntimeException("uncategorized problem while processing " + uri, x);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (is != null)
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final Pattern P_SESSION_EXPIRED = Pattern.compile("Your session has expired");
|
private static final Pattern P_SESSION_EXPIRED = Pattern.compile("Your session has expired");
|
||||||
|
|
||||||
public QueryTripsResult queryMoreTrips(final QueryTripsContext contextObj, final boolean later, final int numTrips) throws IOException
|
public QueryTripsResult queryMoreTrips(final QueryTripsContext contextObj, final boolean later, final int numTrips) throws IOException
|
||||||
|
@ -1701,6 +2156,49 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected QueryTripsResult queryMoreTripsMobile(final QueryTripsContext contextObj, final boolean later, final int numConnections)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
final Context context = (Context) contextObj;
|
||||||
|
final String commandUri = context.context;
|
||||||
|
final StringBuilder uri = new StringBuilder(commandUri);
|
||||||
|
uri.append("&command=").append(later ? "tripNext" : "tripPrev");
|
||||||
|
|
||||||
|
InputStream is = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
is = new BufferedInputStream(ParserUtils.scrapeInputStream(uri.toString(), null, null, httpRefererTrip, "NSC_", 3));
|
||||||
|
is.mark(512);
|
||||||
|
|
||||||
|
return queryTripsMobile(uri.toString(), null, null, null, is);
|
||||||
|
}
|
||||||
|
catch (final XmlPullParserException x)
|
||||||
|
{
|
||||||
|
throw new ParserException(x);
|
||||||
|
}
|
||||||
|
catch (final ProtocolException x) // must be html content
|
||||||
|
{
|
||||||
|
is.reset();
|
||||||
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null)
|
||||||
|
if (P_SESSION_EXPIRED.matcher(line).find())
|
||||||
|
throw new SessionExpiredException();
|
||||||
|
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
catch (final RuntimeException x)
|
||||||
|
{
|
||||||
|
throw new RuntimeException("uncategorized problem while processing " + uri, x);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (is != null)
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private QueryTripsResult queryTrips(final String uri, final InputStream is) throws XmlPullParserException, IOException
|
private QueryTripsResult queryTrips(final String uri, final InputStream is) throws XmlPullParserException, IOException
|
||||||
{
|
{
|
||||||
// System.out.println(uri);
|
// System.out.println(uri);
|
||||||
|
@ -2252,10 +2750,257 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private QueryTripsResult queryTripsMobile(final String uri, final Location from, final Location via, final Location to, final InputStream is)
|
||||||
|
throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
// System.out.println(uri);
|
||||||
|
|
||||||
|
final XmlPullParser pp = parserFactory.newPullParser();
|
||||||
|
pp.setInput(is, null);
|
||||||
|
final ResultHeader header = enterEfa(pp);
|
||||||
|
|
||||||
|
final Calendar plannedTime = new GregorianCalendar(timeZone());
|
||||||
|
final Calendar predictedTime = new GregorianCalendar(timeZone());
|
||||||
|
|
||||||
|
final List<Trip> trips = new ArrayList<Trip>();
|
||||||
|
|
||||||
|
if (XmlPullUtil.test(pp, "ts"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "ts");
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "tp"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "tp");
|
||||||
|
|
||||||
|
XmlPullUtil.optSkip(pp, "attrs");
|
||||||
|
|
||||||
|
requireValueTag(pp, "d"); // duration
|
||||||
|
final int numChanges = Integer.parseInt(requireValueTag(pp, "ic"));
|
||||||
|
final String tripId = requireValueTag(pp, "de");
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "ls");
|
||||||
|
|
||||||
|
final List<Trip.Leg> legs = new LinkedList<Trip.Leg>();
|
||||||
|
Location firstDepartureLocation = null;
|
||||||
|
Location lastArrivalLocation = null;
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "l"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "l");
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "ps");
|
||||||
|
|
||||||
|
Stop departure = null;
|
||||||
|
Stop arrival = null;
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "p"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "p");
|
||||||
|
|
||||||
|
final String name = requireValueTag(pp, "n");
|
||||||
|
final String usage = requireValueTag(pp, "u");
|
||||||
|
optValueTag(pp, "de");
|
||||||
|
|
||||||
|
XmlPullUtil.requireSkip(pp, "dt");
|
||||||
|
|
||||||
|
parseMobileSt(pp, plannedTime, predictedTime);
|
||||||
|
|
||||||
|
XmlPullUtil.requireSkip(pp, "lis");
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "r");
|
||||||
|
final int id = Integer.parseInt(requireValueTag(pp, "id"));
|
||||||
|
optValueTag(pp, "a");
|
||||||
|
final String position = optValueTag(pp, "pl");
|
||||||
|
final String place = normalizeLocationName(optValueTag(pp, "pc"));
|
||||||
|
final Point coord = coordStrToPoint(requireValueTag(pp, "c"));
|
||||||
|
XmlPullUtil.exit(pp, "r");
|
||||||
|
|
||||||
|
final Location location;
|
||||||
|
if (id == 99999997 || id == 99999998)
|
||||||
|
location = new Location(LocationType.ADDRESS, 0, coord.lat, coord.lon, place, name);
|
||||||
|
else
|
||||||
|
location = new Location(LocationType.STATION, id, coord.lat, coord.lon, place, name);
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "p");
|
||||||
|
|
||||||
|
if ("departure".equals(usage))
|
||||||
|
{
|
||||||
|
departure = new Stop(location, true, plannedTime.isSet(Calendar.HOUR_OF_DAY) ? plannedTime.getTime()
|
||||||
|
: predictedTime.getTime(), predictedTime.isSet(Calendar.HOUR_OF_DAY) ? predictedTime.getTime() : null, position,
|
||||||
|
null);
|
||||||
|
if (firstDepartureLocation == null)
|
||||||
|
firstDepartureLocation = location;
|
||||||
|
}
|
||||||
|
else if ("arrival".equals(usage))
|
||||||
|
{
|
||||||
|
arrival = new Stop(location, false, plannedTime.isSet(Calendar.HOUR_OF_DAY) ? plannedTime.getTime()
|
||||||
|
: predictedTime.getTime(), predictedTime.isSet(Calendar.HOUR_OF_DAY) ? predictedTime.getTime() : null, position,
|
||||||
|
null);
|
||||||
|
lastArrivalLocation = location;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("unknown usage: " + usage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "ps");
|
||||||
|
|
||||||
|
final boolean isRealtime = requireValueTag(pp, "realtime").equals("1");
|
||||||
|
|
||||||
|
final LineDestination lineDestination = parseMobileM(pp, false);
|
||||||
|
|
||||||
|
final List<Point> path;
|
||||||
|
if (XmlPullUtil.test(pp, "pt"))
|
||||||
|
path = processCoordinateStrings(pp, "pt");
|
||||||
|
else
|
||||||
|
path = null;
|
||||||
|
|
||||||
|
XmlPullUtil.require(pp, "pss");
|
||||||
|
|
||||||
|
final List<Stop> intermediateStops;
|
||||||
|
|
||||||
|
if (!pp.isEmptyElementTag())
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "pss");
|
||||||
|
|
||||||
|
intermediateStops = new LinkedList<Stop>();
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "s"))
|
||||||
|
{
|
||||||
|
plannedTime.clear();
|
||||||
|
predictedTime.clear();
|
||||||
|
|
||||||
|
final String s = requireValueTag(pp, "s");
|
||||||
|
final String[] intermediateParts = s.split(";");
|
||||||
|
final int id = Integer.parseInt(intermediateParts[0]);
|
||||||
|
if (id != departure.location.id && id != arrival.location.id)
|
||||||
|
{
|
||||||
|
final String name = normalizeLocationName(intermediateParts[1]);
|
||||||
|
|
||||||
|
if (!("0000-1".equals(intermediateParts[2]) && "000-1".equals(intermediateParts[3])))
|
||||||
|
{
|
||||||
|
ParserUtils.parseIsoDate(plannedTime, intermediateParts[2]);
|
||||||
|
ParserUtils.parseIsoTime(plannedTime, intermediateParts[3]);
|
||||||
|
|
||||||
|
if (isRealtime)
|
||||||
|
{
|
||||||
|
ParserUtils.parseIsoDate(predictedTime, intermediateParts[2]);
|
||||||
|
ParserUtils.parseIsoTime(predictedTime, intermediateParts[3]);
|
||||||
|
|
||||||
|
if (intermediateParts.length > 5)
|
||||||
|
{
|
||||||
|
final int delay = Integer.parseInt(intermediateParts[5]);
|
||||||
|
predictedTime.add(Calendar.MINUTE, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String coord = intermediateParts[4];
|
||||||
|
|
||||||
|
final Location location;
|
||||||
|
if (!"::".equals(coord))
|
||||||
|
{
|
||||||
|
final String[] coordParts = coord.split(":");
|
||||||
|
if (!"WGS84".equals(coordParts[2]))
|
||||||
|
throw new IllegalStateException("unknown map name: " + coordParts[2]);
|
||||||
|
final int lat = Math.round(Float.parseFloat(coordParts[1]));
|
||||||
|
final int lon = Math.round(Float.parseFloat(coordParts[0]));
|
||||||
|
location = new Location(LocationType.STATION, id, lat, lon, null, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
location = new Location(LocationType.STATION, id, null, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Stop stop = new Stop(location, false, plannedTime.isSet(Calendar.HOUR_OF_DAY) ? plannedTime.getTime()
|
||||||
|
: predictedTime.getTime(), predictedTime.isSet(Calendar.HOUR_OF_DAY) ? predictedTime.getTime() : null, null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
intermediateStops.add(stop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "pss");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
intermediateStops = null;
|
||||||
|
|
||||||
|
XmlPullUtil.next(pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.optSkip(pp, "interchange");
|
||||||
|
|
||||||
|
XmlPullUtil.requireSkip(pp, "ns");
|
||||||
|
// TODO messages
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "l");
|
||||||
|
|
||||||
|
if (lineDestination.line == Line.FOOTWAY)
|
||||||
|
{
|
||||||
|
legs.add(new Trip.Individual(Trip.Individual.Type.WALK, departure.location, departure.getDepartureTime(), arrival.location,
|
||||||
|
arrival.getArrivalTime(), path, 0));
|
||||||
|
}
|
||||||
|
else if (lineDestination.line == Line.SECURE_CONNECTION || lineDestination.line == Line.DO_NOT_CHANGE)
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
legs.add(new Trip.Public(lineDestination.line, lineDestination.destination, departure, arrival, intermediateStops, path, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "ls");
|
||||||
|
|
||||||
|
XmlPullUtil.require(pp, "tcs");
|
||||||
|
|
||||||
|
final List<Fare> fares;
|
||||||
|
|
||||||
|
if (!pp.isEmptyElementTag())
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "tcs");
|
||||||
|
|
||||||
|
fares = new ArrayList<Fare>(2);
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "tc"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "tc");
|
||||||
|
// TODO fares
|
||||||
|
XmlPullUtil.exit(pp, "tc");
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "tcs");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fares = null;
|
||||||
|
|
||||||
|
XmlPullUtil.next(pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Trip trip = new Trip(tripId, firstDepartureLocation, lastArrivalLocation, legs, fares, null, numChanges);
|
||||||
|
trips.add(trip);
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "tp");
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "ts");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trips.size() > 0)
|
||||||
|
{
|
||||||
|
final String[] context = (String[]) header.context;
|
||||||
|
return new QueryTripsResult(header, uri, from, via, to, new Context(commandLink(context[0], context[1])), trips);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new QueryTripsResult(header, QueryTripsResult.Status.NO_TRIPS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<Point> processItdPathCoordinates(final XmlPullParser pp) throws XmlPullParserException, IOException
|
private List<Point> processItdPathCoordinates(final XmlPullParser pp) throws XmlPullParserException, IOException
|
||||||
{
|
{
|
||||||
final List<Point> path = new LinkedList<Point>();
|
|
||||||
|
|
||||||
XmlPullUtil.enter(pp, "itdPathCoordinates");
|
XmlPullUtil.enter(pp, "itdPathCoordinates");
|
||||||
|
|
||||||
XmlPullUtil.enter(pp, "coordEllipsoid");
|
XmlPullUtil.enter(pp, "coordEllipsoid");
|
||||||
|
@ -2272,19 +3017,33 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
if (!"GEO_DECIMAL".equals(type))
|
if (!"GEO_DECIMAL".equals(type))
|
||||||
throw new IllegalStateException("unknown type: " + type);
|
throw new IllegalStateException("unknown type: " + type);
|
||||||
|
|
||||||
XmlPullUtil.enter(pp, "itdCoordinateString");
|
final List<Point> path = processCoordinateStrings(pp, "itdCoordinateString");
|
||||||
for (final String coordStr : pp.getText().split(" +"))
|
|
||||||
{
|
|
||||||
final String[] coordsStr = coordStr.split(",");
|
|
||||||
path.add(new Point(Math.round(Float.parseFloat(coordsStr[1])), Math.round(Float.parseFloat(coordsStr[0]))));
|
|
||||||
}
|
|
||||||
XmlPullUtil.exit(pp, "itdCoordinateString");
|
|
||||||
|
|
||||||
XmlPullUtil.exit(pp, "itdPathCoordinates");
|
XmlPullUtil.exit(pp, "itdPathCoordinates");
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Point> processCoordinateStrings(final XmlPullParser pp, final String tag) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
final List<Point> path = new LinkedList<Point>();
|
||||||
|
|
||||||
|
final String value = requireValueTag(pp, tag);
|
||||||
|
for (final String coordStr : value.split(" +"))
|
||||||
|
path.add(coordStrToPoint(coordStr));
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Point coordStrToPoint(final String coordStr)
|
||||||
|
{
|
||||||
|
if (coordStr == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
final String[] parts = coordStr.split(",");
|
||||||
|
return new Point(Math.round(Float.parseFloat(parts[1])), Math.round(Float.parseFloat(parts[0])));
|
||||||
|
}
|
||||||
|
|
||||||
private Fare processItdGenericTicketGroup(final XmlPullParser pp, final String net, final Currency currency) throws XmlPullParserException,
|
private Fare processItdGenericTicketGroup(final XmlPullParser pp, final String net, final Currency currency) throws XmlPullParserException,
|
||||||
IOException
|
IOException
|
||||||
{
|
{
|
||||||
|
@ -2491,4 +3250,78 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider
|
||||||
|
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResultHeader enterEfa(final XmlPullParser pp) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
if (pp.getEventType() != XmlPullParser.START_DOCUMENT)
|
||||||
|
throw new IllegalStateException("start of document expected");
|
||||||
|
|
||||||
|
pp.next();
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "efa");
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "now");
|
||||||
|
final String now = pp.getText();
|
||||||
|
final Calendar serverTime = new GregorianCalendar(timeZone());
|
||||||
|
ParserUtils.parseIsoDate(serverTime, now.substring(0, 10));
|
||||||
|
ParserUtils.parseEuropeanTime(serverTime, now.substring(11));
|
||||||
|
XmlPullUtil.exit(pp, "now");
|
||||||
|
|
||||||
|
final Map<String, String> params = processPas(pp);
|
||||||
|
final String sessionId = params.get("sessionID");
|
||||||
|
final String requestId = params.get("requestID");
|
||||||
|
|
||||||
|
final ResultHeader header = new ResultHeader(SERVER_PRODUCT, null, serverTime.getTimeInMillis(), new String[] { sessionId, requestId });
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> processPas(final XmlPullParser pp) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
final Map<String, String> params = new HashMap<String, String>();
|
||||||
|
|
||||||
|
XmlPullUtil.enter(pp, "pas");
|
||||||
|
|
||||||
|
while (XmlPullUtil.test(pp, "pa"))
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, "pa");
|
||||||
|
final String name = requireValueTag(pp, "n");
|
||||||
|
final String value = requireValueTag(pp, "v");
|
||||||
|
params.put(name, value);
|
||||||
|
XmlPullUtil.exit(pp, "pa");
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlPullUtil.exit(pp, "pas");
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String optValueTag(final XmlPullParser pp, final String name) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
if (XmlPullUtil.test(pp, name))
|
||||||
|
{
|
||||||
|
if (!pp.isEmptyElementTag())
|
||||||
|
{
|
||||||
|
return requireValueTag(pp, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pp.next();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String requireValueTag(final XmlPullParser pp, final String name) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
XmlPullUtil.enter(pp, name);
|
||||||
|
final String value = pp.getText();
|
||||||
|
XmlPullUtil.exit(pp, name);
|
||||||
|
|
||||||
|
return value != null ? value.trim() : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,37 @@
|
||||||
|
|
||||||
package de.schildbach.pte;
|
package de.schildbach.pte;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.schildbach.pte.dto.Location;
|
||||||
|
import de.schildbach.pte.dto.LocationType;
|
||||||
|
import de.schildbach.pte.dto.NearbyStationsResult;
|
||||||
|
import de.schildbach.pte.dto.Product;
|
||||||
|
import de.schildbach.pte.dto.QueryDeparturesResult;
|
||||||
|
import de.schildbach.pte.dto.QueryTripsContext;
|
||||||
|
import de.schildbach.pte.dto.QueryTripsResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andreas Schildbach
|
* @author Andreas Schildbach
|
||||||
*/
|
*/
|
||||||
public class BayernProvider extends AbstractEfaProvider
|
public class BayernProvider extends AbstractEfaProvider
|
||||||
{
|
{
|
||||||
public static final NetworkId NETWORK_ID = NetworkId.BAYERN;
|
public static final NetworkId NETWORK_ID = NetworkId.BAYERN;
|
||||||
private final static String API_BASE = "http://vm-bayern-fahrplan03.defas-fgi.de:81/standard/";
|
private final static String API_BASE = "http://mobile.defas-fgi.de/beg/";
|
||||||
|
private static final String DEPARTURE_MONITOR_ENDPOINT = "XML_DM_REQUEST";
|
||||||
|
private static final String TRIP_ENDPOINT = "XML_TRIP_REQUEST2";
|
||||||
|
private static final String STOP_FINDER_ENDPOINT = "XML_STOPFINDER_REQUEST";
|
||||||
|
|
||||||
public BayernProvider()
|
public BayernProvider()
|
||||||
{
|
{
|
||||||
super(API_BASE);
|
super(API_BASE, DEPARTURE_MONITOR_ENDPOINT, TRIP_ENDPOINT, STOP_FINDER_ENDPOINT, null);
|
||||||
|
|
||||||
|
setRequestUrlEncoding(UTF_8);
|
||||||
|
setIncludeRegionId(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkId id()
|
public NetworkId id()
|
||||||
|
@ -43,4 +63,42 @@ public class BayernProvider extends AbstractEfaProvider
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NearbyStationsResult queryNearbyStations(final Location location, final int maxDistance, final int maxStations) throws IOException
|
||||||
|
{
|
||||||
|
if (location.hasLocation())
|
||||||
|
return mobileCoordRequest(location.lat, location.lon, maxDistance, maxStations);
|
||||||
|
|
||||||
|
if (location.type != LocationType.STATION)
|
||||||
|
throw new IllegalArgumentException("cannot handle: " + location.type);
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("station"); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryDeparturesResult queryDepartures(final int stationId, final int maxDepartures, final boolean equivs) throws IOException
|
||||||
|
{
|
||||||
|
return queryDeparturesMobile(stationId, maxDepartures, equivs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Location> autocompleteStations(final CharSequence constraint) throws IOException
|
||||||
|
{
|
||||||
|
return mobileStopfinderRequest(new Location(LocationType.ANY, 0, null, constraint.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryTripsResult queryTrips(final Location from, final Location via, final Location to, final Date date, final boolean dep,
|
||||||
|
final int numTrips, final Collection<Product> products, final WalkSpeed walkSpeed, final Accessibility accessibility,
|
||||||
|
final Set<Option> options) throws IOException
|
||||||
|
{
|
||||||
|
return queryTripsMobile(from, via, to, date, dep, numTrips, products, walkSpeed, accessibility, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryTripsResult queryMoreTrips(final QueryTripsContext contextObj, final boolean later, final int numTrips) throws IOException
|
||||||
|
{
|
||||||
|
return queryMoreTripsMobile(contextObj, later, numTrips);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,10 @@ public final class Line implements Serializable, Comparable<Line>
|
||||||
|
|
||||||
private static final String PRODUCT_ORDER = "IRSUTBPFC?";
|
private static final String PRODUCT_ORDER = "IRSUTBPFC?";
|
||||||
|
|
||||||
|
public static final Line FOOTWAY = new Line(null, null, null);
|
||||||
|
public static final Line SECURE_CONNECTION = new Line(null, null, null);
|
||||||
|
public static final Line DO_NOT_CHANGE = new Line(null, null, null);
|
||||||
|
|
||||||
public Line(final String id, final String label, final Style style)
|
public Line(final String id, final String label, final Style style)
|
||||||
{
|
{
|
||||||
this(id, label, style, null, null);
|
this(id, label, style, null, null);
|
||||||
|
|
|
@ -413,7 +413,7 @@ public final class ParserUtils
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern P_ISO_DATE = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
|
private static final Pattern P_ISO_DATE = Pattern.compile("(\\d{4})-?(\\d{2})-?(\\d{2})");
|
||||||
private static final Pattern P_ISO_DATE_REVERSE = Pattern.compile("(\\d{2})-(\\d{2})-(\\d{4})");
|
private static final Pattern P_ISO_DATE_REVERSE = Pattern.compile("(\\d{2})-(\\d{2})-(\\d{4})");
|
||||||
|
|
||||||
public static final void parseIsoDate(final Calendar calendar, final CharSequence str)
|
public static final void parseIsoDate(final Calendar calendar, final CharSequence str)
|
||||||
|
@ -437,7 +437,21 @@ public final class ParserUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new RuntimeException("cannot parse: '" + str + "'");
|
throw new RuntimeException("cannot parse: '" + str + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Pattern P_ISO_TIME = Pattern.compile("(\\d{2})-?(\\d{2})");
|
||||||
|
|
||||||
|
public static final void parseIsoTime(final Calendar calendar, final CharSequence str)
|
||||||
|
{
|
||||||
|
final Matcher mIso = P_ISO_TIME.matcher(str);
|
||||||
|
if (mIso.matches())
|
||||||
|
{
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(mIso.group(1)));
|
||||||
|
calendar.set(Calendar.MINUTE, Integer.parseInt(mIso.group(2)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("cannot parse: '" + str + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern P_GERMAN_DATE = Pattern.compile("(\\d{2})[\\./-](\\d{2})[\\./-](\\d{2,4})");
|
private static final Pattern P_GERMAN_DATE = Pattern.compile("(\\d{2})[\\./-](\\d{2})[\\./-](\\d{2,4})");
|
||||||
|
|
|
@ -21,15 +21,6 @@ public final class XmlPullUtil
|
||||||
{
|
{
|
||||||
public static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
|
public static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
|
||||||
|
|
||||||
/**
|
|
||||||
* directly jumps forward to start tag, ignoring any structure
|
|
||||||
*/
|
|
||||||
public static void jump(final XmlPullParser pp, final String tagName) throws XmlPullParserException, IOException
|
|
||||||
{
|
|
||||||
if (!jumpToStartTag(pp, null, tagName))
|
|
||||||
throw new IllegalStateException("cannot find <" + tagName + " />");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void require(final XmlPullParser pp, final String tagName) throws XmlPullParserException, IOException
|
public static void require(final XmlPullParser pp, final String tagName) throws XmlPullParserException, IOException
|
||||||
{
|
{
|
||||||
pp.require(XmlPullParser.START_TAG, null, tagName);
|
pp.require(XmlPullParser.START_TAG, null, tagName);
|
||||||
|
@ -91,6 +82,27 @@ public final class XmlPullUtil
|
||||||
return pp.getEventType() == XmlPullParser.START_TAG && pp.getName().equals(tagName);
|
return pp.getEventType() == XmlPullParser.START_TAG && pp.getName().equals(tagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void requireSkip(final XmlPullParser pp, final String tagName) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
require(pp, tagName);
|
||||||
|
|
||||||
|
if (!pp.isEmptyElementTag())
|
||||||
|
{
|
||||||
|
enter(pp);
|
||||||
|
exit(pp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next(pp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void optSkip(final XmlPullParser pp, final String tagName) throws XmlPullParserException, IOException
|
||||||
|
{
|
||||||
|
if (test(pp, tagName))
|
||||||
|
requireSkip(pp, tagName);
|
||||||
|
}
|
||||||
|
|
||||||
public static void next(final XmlPullParser pp) throws XmlPullParserException, IOException
|
public static void next(final XmlPullParser pp) throws XmlPullParserException, IOException
|
||||||
{
|
{
|
||||||
skipSubTree(pp);
|
skipSubTree(pp);
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package de.schildbach.pte.live;
|
package de.schildbach.pte.live;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -61,9 +63,14 @@ public class BayernProviderLiveTest extends AbstractProviderLiveTest
|
||||||
@Test
|
@Test
|
||||||
public void queryDepartures() throws Exception
|
public void queryDepartures() throws Exception
|
||||||
{
|
{
|
||||||
final QueryDeparturesResult result = provider.queryDepartures(80001083, 0, false);
|
final QueryDeparturesResult munichOstbahnhof = provider.queryDepartures(80000793, 0, false);
|
||||||
|
print(munichOstbahnhof);
|
||||||
|
|
||||||
print(result);
|
final QueryDeparturesResult munichHauptbahnhof = provider.queryDepartures(80000689, 0, false);
|
||||||
|
print(munichHauptbahnhof);
|
||||||
|
|
||||||
|
final QueryDeparturesResult nurembergHauptbahnhof = provider.queryDepartures(80001020, 0, false);
|
||||||
|
print(nurembergHauptbahnhof);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -82,6 +89,27 @@ public class BayernProviderLiveTest extends AbstractProviderLiveTest
|
||||||
print(autocompletes);
|
print(autocompletes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void autocompleteAddress() throws Exception
|
||||||
|
{
|
||||||
|
final List<Location> autocompletes = provider.autocompleteStations("München, Friedenstraße 2");
|
||||||
|
|
||||||
|
print(autocompletes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void autocompleteLocal() throws Exception
|
||||||
|
{
|
||||||
|
final List<Location> autocompleteRegensburg = provider.autocompleteStations("Regensburg");
|
||||||
|
assertEquals(80001083, autocompleteRegensburg.iterator().next().id);
|
||||||
|
|
||||||
|
final List<Location> autocompleteMunich = provider.autocompleteStations("München");
|
||||||
|
assertEquals(80000689, autocompleteMunich.iterator().next().id);
|
||||||
|
|
||||||
|
final List<Location> autocompleteNuremberg = provider.autocompleteStations("Nürnberg");
|
||||||
|
assertEquals(80001020, autocompleteNuremberg.iterator().next().id);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shortTrip() throws Exception
|
public void shortTrip() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -114,8 +142,8 @@ public class BayernProviderLiveTest extends AbstractProviderLiveTest
|
||||||
@Test
|
@Test
|
||||||
public void tripBetweenCoordinateAndStation() throws Exception
|
public void tripBetweenCoordinateAndStation() throws Exception
|
||||||
{
|
{
|
||||||
final QueryTripsResult result = queryTrips(new Location(LocationType.ADDRESS, 0, 48238341, 11478230), null, new Location(LocationType.ANY, 0,
|
final QueryTripsResult result = queryTrips(new Location(LocationType.ADDRESS, 0, 48238341, 11478230), null, new Location(
|
||||||
null, "Ostbahnhof"), new Date(), true, Product.ALL, WalkSpeed.NORMAL, Accessibility.NEUTRAL);
|
LocationType.STATION, 80000793, "München", "Ostbahnhof"), new Date(), true, Product.ALL, WalkSpeed.NORMAL, Accessibility.NEUTRAL);
|
||||||
System.out.println(result);
|
System.out.println(result);
|
||||||
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
|
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
|
||||||
System.out.println(laterResult);
|
System.out.println(laterResult);
|
||||||
|
@ -134,7 +162,7 @@ public class BayernProviderLiveTest extends AbstractProviderLiveTest
|
||||||
@Test
|
@Test
|
||||||
public void tripBetweenStationAndAddress() throws Exception
|
public void tripBetweenStationAndAddress() throws Exception
|
||||||
{
|
{
|
||||||
final QueryTripsResult result = queryTrips(new Location(LocationType.STATION, 1220, null, "Josephsburg"), null, new Location(
|
final QueryTripsResult result = queryTrips(new Location(LocationType.STATION, 1001220, null, "Josephsburg"), null, new Location(
|
||||||
LocationType.ADDRESS, 0, 48188018, 11574239, null, "München Frankfurter Ring 35"), new Date(), true, Product.ALL, WalkSpeed.NORMAL,
|
LocationType.ADDRESS, 0, 48188018, 11574239, null, "München Frankfurter Ring 35"), new Date(), true, Product.ALL, WalkSpeed.NORMAL,
|
||||||
Accessibility.NEUTRAL);
|
Accessibility.NEUTRAL);
|
||||||
System.out.println(result);
|
System.out.println(result);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue