rudimentary connnections using API for Belgium and Switzerland

git-svn-id: https://public-transport-enabler.googlecode.com/svn/trunk@302 0924bc21-9374-b0fa-ee44-9ff1593b38f0
This commit is contained in:
andreas.schildbach 2010-10-17 14:17:11 +00:00
parent b990d82f8e
commit b9f572b658
10 changed files with 515 additions and 69 deletions

View file

@ -20,7 +20,11 @@ package de.schildbach.pte;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -31,9 +35,12 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserFactory;
import de.schildbach.pte.dto.Connection;
import de.schildbach.pte.dto.GetConnectionDetailsResult;
import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType; import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyStationsResult; import de.schildbach.pte.dto.NearbyStationsResult;
import de.schildbach.pte.dto.QueryConnectionsResult;
import de.schildbach.pte.dto.Station; import de.schildbach.pte.dto.Station;
import de.schildbach.pte.util.Color; import de.schildbach.pte.util.Color;
import de.schildbach.pte.util.ParserUtils; import de.schildbach.pte.util.ParserUtils;
@ -46,29 +53,62 @@ public abstract class AbstractHafasProvider implements NetworkProvider
{ {
private static final String DEFAULT_ENCODING = "ISO-8859-1"; private static final String DEFAULT_ENCODING = "ISO-8859-1";
private final String autocompleteUri; private final String apiUri;
private final String prod; private static final String prod = "hafas";
private final String accessId; private final String accessId;
public AbstractHafasProvider(final String autocompleteUri, final String prod, final String accessId) public AbstractHafasProvider(final String apiUri, final String accessId)
{ {
this.autocompleteUri = autocompleteUri; this.apiUri = apiUri;
this.prod = prod;
this.accessId = accessId; this.accessId = accessId;
} }
private final String wrap(final String request)
{
return "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>" //
+ "<ReqC ver=\"1.1\" prod=\"" + prod + "\" lang=\"DE\"" + (accessId != null ? " accessId=\"" + accessId + "\"" : "") + ">" //
+ request //
+ "</ReqC>";
}
private static final Location parseLocation(final XmlPullParser pp)
{
final String type = pp.getName();
if ("Station".equals(type))
{
final String name = pp.getAttributeValue(null, "name").trim();
final int id = Integer.parseInt(pp.getAttributeValue(null, "externalStationNr"));
final int x = Integer.parseInt(pp.getAttributeValue(null, "x"));
final int y = Integer.parseInt(pp.getAttributeValue(null, "y"));
return new Location(LocationType.STATION, id, y, x, name);
}
throw new IllegalStateException("cannot handle: " + type);
}
private static final Location parsePoi(final XmlPullParser pp)
{
final String type = pp.getName();
if ("Poi".equals(type))
{
String name = pp.getAttributeValue(null, "name").trim();
if (name.equals("unknown"))
name = null;
final int x = Integer.parseInt(pp.getAttributeValue(null, "x"));
final int y = Integer.parseInt(pp.getAttributeValue(null, "y"));
return new Location(LocationType.POI, 0, y, x, name);
}
throw new IllegalStateException("cannot handle: " + type);
}
public List<Location> autocompleteStations(final CharSequence constraint) throws IOException public List<Location> autocompleteStations(final CharSequence constraint) throws IOException
{ {
final String request = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>" // final String request = "<LocValReq id=\"station\" maxNr=\"10\"><ReqLoc match=\"" + constraint + "\" type=\"ST\"/></LocValReq>" //
+ "<ReqC ver=\"1.1\" prod=\"" + prod + "\" lang=\"DE\"" + (accessId != null ? " accessId=\"" + accessId + "\"" : "") + ">" // + "<LocValReq id=\"poi\" maxNr=\"10\"><ReqLoc match=\"" + constraint + "\" type=\"POI\"/></LocValReq>";
+ "<LocValReq id=\"station\" maxNr=\"10\"><ReqLoc match=\"" + constraint + "\" type=\"ST\"/></LocValReq>" //
+ "<LocValReq id=\"poi\" maxNr=\"10\"><ReqLoc match=\"" + constraint + "\" type=\"POI\"/></LocValReq>" //
+ "</ReqC>";
InputStream is = null; InputStream is = null;
try try
{ {
is = ParserUtils.scrapeInputStream(autocompleteUri, request); is = ParserUtils.scrapeInputStream(apiUri, wrap(request));
final List<Location> results = new ArrayList<Location>(); final List<Location> results = new ArrayList<Location>();
@ -85,32 +125,25 @@ public abstract class AbstractHafasProvider implements NetworkProvider
while (XmlPullUtil.test(pp, "Station")) while (XmlPullUtil.test(pp, "Station"))
{ {
final String name = pp.getAttributeValue(null, "name"); results.add(parseLocation(pp));
final int id = Integer.parseInt(pp.getAttributeValue(null, "externalStationNr"));
final int x = Integer.parseInt(pp.getAttributeValue(null, "x"));
final int y = Integer.parseInt(pp.getAttributeValue(null, "y"));
results.add(new Location(LocationType.STATION, id, y, x, name));
XmlPullUtil.skipTree(pp); XmlPullUtil.next(pp);
} }
XmlPullUtil.exit(pp); XmlPullUtil.exit(pp);
XmlPullUtil.require(pp, "LocValRes"); // XmlPullUtil.require(pp, "LocValRes");
XmlPullUtil.requireAttr(pp, "id", "poi"); // XmlPullUtil.requireAttr(pp, "id", "poi");
XmlPullUtil.enter(pp); // XmlPullUtil.enter(pp);
//
while (XmlPullUtil.test(pp, "Poi")) // while (XmlPullUtil.test(pp, "Poi"))
{ // {
final String name = pp.getAttributeValue(null, "name"); // results.add(parsePoi(pp));
final int x = Integer.parseInt(pp.getAttributeValue(null, "x")); //
final int y = Integer.parseInt(pp.getAttributeValue(null, "y")); // XmlPullUtil.next(pp);
results.add(new Location(LocationType.POI, 0, y, x, name)); // }
//
XmlPullUtil.skipTree(pp); // XmlPullUtil.exit(pp);
}
XmlPullUtil.exit(pp);
return results; return results;
} }
@ -129,6 +162,371 @@ public abstract class AbstractHafasProvider implements NetworkProvider
} }
} }
public QueryConnectionsResult queryConnections(Location from, Location via, Location to, final Date date, final boolean dep,
final String products, final WalkSpeed walkSpeed) throws IOException
{
if (from.type == LocationType.ANY)
{
final List<Location> autocompletes = autocompleteStations(from.name);
if (autocompletes.isEmpty())
return new QueryConnectionsResult(QueryConnectionsResult.Status.NO_CONNECTIONS); // TODO
if (autocompletes.size() > 1)
return new QueryConnectionsResult(autocompletes, null, null);
from = autocompletes.get(0);
}
if (via != null && via.type == LocationType.ANY)
{
final List<Location> autocompletes = autocompleteStations(via.name);
if (autocompletes.isEmpty())
return new QueryConnectionsResult(QueryConnectionsResult.Status.NO_CONNECTIONS); // TODO
if (autocompletes.size() > 1)
return new QueryConnectionsResult(null, autocompletes, null);
via = autocompletes.get(0);
}
if (to.type == LocationType.ANY)
{
final List<Location> autocompletes = autocompleteStations(to.name);
if (autocompletes.isEmpty())
return new QueryConnectionsResult(QueryConnectionsResult.Status.NO_CONNECTIONS); // TODO
if (autocompletes.size() > 1)
return new QueryConnectionsResult(null, null, autocompletes);
to = autocompletes.get(0);
}
final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
final DateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm");
final String request = "<ConReq>" //
+ "<Start>" + location(from) + "<Prod bike=\"0\" couchette=\"0\" direct=\"0\" sleeper=\"0\"/></Start>" //
+ (via != null ? "<Via>" + location(via) + "</Via>" : "") //
+ "<Dest>" + location(to) + "</Dest>" //
+ "<ReqT a=\"" + (dep ? 0 : 1) + "\" date=\"" + DATE_FORMAT.format(date) + "\" time=\"" + TIME_FORMAT.format(date) + "\"/>" //
+ "<RFlags b=\"0\" chExtension=\"0\" f=\"4\" sMode=\"N\"/>" //
+ "</ConReq>";
// System.out.println(request);
// System.out.println(ParserUtils.scrape(apiUri, true, wrap(request), null, false));
InputStream is = null;
try
{
is = ParserUtils.scrapeInputStream(apiUri, wrap(request));
final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null);
final XmlPullParser pp = factory.newPullParser();
pp.setInput(is, DEFAULT_ENCODING);
XmlPullUtil.jump(pp, "ConRes");
XmlPullUtil.enter(pp);
if (pp.getName().equals("Err"))
{
final String code = XmlPullUtil.attr(pp, "code");
if (code.equals("K9380"))
return QueryConnectionsResult.TOO_CLOSE;
if (code.equals("K9220")) // Nearby to the given address stations could not be found
return QueryConnectionsResult.NO_CONNECTIONS;
throw new IllegalStateException("error " + code + " " + XmlPullUtil.attr(pp, "text"));
}
XmlPullUtil.require(pp, "ConResCtxt");
final String sessionId = XmlPullUtil.text(pp);
XmlPullUtil.require(pp, "ConnectionList");
XmlPullUtil.enter(pp);
final List<Connection> connections = new ArrayList<Connection>();
while (XmlPullUtil.test(pp, "Connection"))
{
final String id = XmlPullUtil.attr(pp, "id");
XmlPullUtil.enter(pp);
while (pp.getName().equals("RtStateList"))
XmlPullUtil.next(pp);
XmlPullUtil.require(pp, "Overview");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Date");
final Date currentDate = DATE_FORMAT.parse(XmlPullUtil.text(pp));
XmlPullUtil.exit(pp);
XmlPullUtil.require(pp, "ConSectionList");
XmlPullUtil.enter(pp);
final List<Connection.Part> parts = new ArrayList<Connection.Part>(4);
Date firstDepartureTime = null;
Date lastArrivalTime = null;
while (XmlPullUtil.test(pp, "ConSection"))
{
XmlPullUtil.enter(pp);
// departure
XmlPullUtil.require(pp, "Departure");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "BasicStop");
XmlPullUtil.enter(pp);
while (pp.getName().equals("StAttrList"))
XmlPullUtil.next(pp);
Location departure;
if (pp.getName().equals("Station"))
departure = parseLocation(pp);
else if (pp.getName().equals("Poi"))
departure = parsePoi(pp);
else
throw new IllegalStateException("cannot parse: " + pp.getName());
XmlPullUtil.next(pp);
XmlPullUtil.require(pp, "Dep");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Time");
final Date departureTime = ParserUtils.joinDateTime(currentDate, TIME_FORMAT.parse(XmlPullUtil.text(pp).substring(3, 8)));
XmlPullUtil.require(pp, "Platform");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Text");
String departurePos = XmlPullUtil.text(pp).trim();
if (departurePos.length() == 0)
departurePos = null;
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
// journey
String line = null;
String direction = null;
int min = 0;
final String tag = pp.getName();
if (tag.equals("Journey"))
{
XmlPullUtil.enter(pp);
while (pp.getName().equals("JHandle"))
XmlPullUtil.next(pp);
XmlPullUtil.require(pp, "JourneyAttributeList");
XmlPullUtil.enter(pp);
String name = null;
String category = null;
while (XmlPullUtil.test(pp, "JourneyAttribute"))
{
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Attribute");
final String attrName = XmlPullUtil.attr(pp, "type");
XmlPullUtil.enter(pp);
final String attrValue = parseAttributeVariant(pp, "NORMAL");
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
if ("NAME".equals(attrName))
name = attrValue;
else if ("CATEGORY".equals(attrName))
category = attrValue;
else if ("DIRECTION".equals(attrName))
direction = attrValue;
}
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
line = _normalizeLine(category, name);
}
else if (tag.equals("Walk") || tag.equals("Transfer"))
{
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Duration");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Time");
min = parseDuration(XmlPullUtil.text(pp).substring(3, 8));
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
}
else
{
throw new IllegalStateException("cannot handle: " + pp.getName());
}
// arrival
XmlPullUtil.require(pp, "Arrival");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "BasicStop");
XmlPullUtil.enter(pp);
while (pp.getName().equals("StAttrList"))
XmlPullUtil.next(pp);
Location arrival;
if (pp.getName().equals("Station"))
arrival = parseLocation(pp);
else if (pp.getName().equals("Poi"))
arrival = parsePoi(pp);
else
throw new IllegalStateException("cannot parse: " + pp.getName());
XmlPullUtil.next(pp);
XmlPullUtil.require(pp, "Arr");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Time");
final Date arrivalTime = ParserUtils.joinDateTime(currentDate, TIME_FORMAT.parse(XmlPullUtil.text(pp).substring(3, 8)));
XmlPullUtil.require(pp, "Platform");
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Text");
String arrivalPos = XmlPullUtil.text(pp).trim();
if (arrivalPos.length() == 0)
arrivalPos = null;
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
if (min == 0 || line != null)
{
parts.add(new Connection.Trip(line, lineColors(line), 0, direction, departureTime, departurePos, departure.id,
departure.name, arrivalTime, arrivalPos, arrival.id, arrival.name));
}
else
{
if (parts.size() > 0 && parts.get(parts.size() - 1) instanceof Connection.Footway)
{
final Connection.Footway lastFootway = (Connection.Footway) parts.remove(parts.size() - 1);
parts.add(new Connection.Footway(lastFootway.min + min, lastFootway.departureId, lastFootway.departure, arrival.id,
arrival.name));
}
else
{
parts.add(new Connection.Footway(min, departure.id, departure.name, arrival.id, arrival.name));
}
}
if (firstDepartureTime == null)
firstDepartureTime = departureTime;
lastArrivalTime = arrivalTime;
}
XmlPullUtil.exit(pp);
XmlPullUtil.exit(pp);
connections.add(new Connection(id, null, firstDepartureTime, lastArrivalTime, null, null, 0, null, 0, null, parts));
}
XmlPullUtil.exit(pp);
return new QueryConnectionsResult(null, from, via, to, null, null, connections);
}
catch (final XmlPullParserException x)
{
throw new RuntimeException(x);
}
catch (final SocketTimeoutException x)
{
throw new RuntimeException(x);
}
catch (final ParseException x)
{
throw new RuntimeException(x);
}
finally
{
if (is != null)
is.close();
}
}
private final String parseAttributeVariant(final XmlPullParser pp, final String type) throws XmlPullParserException, IOException
{
String value = null;
while (XmlPullUtil.test(pp, "AttributeVariant"))
{
if (type.equals(XmlPullUtil.attr(pp, "type")))
{
XmlPullUtil.enter(pp);
XmlPullUtil.require(pp, "Text");
value = XmlPullUtil.text(pp).trim();
XmlPullUtil.exit(pp);
}
else
{
XmlPullUtil.next(pp);
}
}
return value;
}
private static final Pattern P_DURATION = Pattern.compile("(\\d+):(\\d{2})");
private final int parseDuration(final String str)
{
final Matcher m = P_DURATION.matcher(str);
if (m.matches())
return Integer.parseInt(m.group(1)) * 60 + Integer.parseInt(m.group(2));
else
throw new IllegalArgumentException("cannot parse duration: " + str);
}
private final String location(final Location location)
{
if (location.type == LocationType.STATION && location.id != 0)
return "<Station externalId=\"" + location.id + "\" />";
if (location.type == LocationType.POI && (location.lat != 0 || location.lon != 0))
return "<Poi type=\"WGS84\" x=\"" + location.lon + "\" y=\"" + location.lat + "\" />";
throw new IllegalArgumentException("cannot handle: " + location.toDebugString());
}
private final String _normalizeLine(final String type, final String name)
{
final String normalizedType = type.split(" ", 2)[0];
final String normalizedName = normalizeWhitespace(name);
if ("THALYS".equals(normalizedType))
return "I" + normalizedName;
if ("IC".equals(normalizedType))
return "I" + normalizedName;
if ("EN".equals(normalizedType))
return "I" + normalizedName;
if ("OEC".equals(normalizedType))
return "I" + normalizedName;
if ("R".equals(normalizedType))
return "R" + normalizedName;
if ("RE".equals(normalizedType))
return "R" + normalizedName;
if ("IR".equals(normalizedType))
return "R" + normalizedName;
if ("Tramway".equals(normalizedType))
return "T" + normalizedName;
if ("BUS".equals(normalizedType))
return "B" + normalizedName;
if ("L".equals(normalizedType))
return "?" + normalizedName;
if ("NZ".equals(normalizedType))
return "?" + normalizedName;
throw new IllegalStateException("cannot normalize type '" + normalizedType + "' (" + type + ") name '" + normalizedName + "'");
}
private final static Pattern P_WHITESPACE = Pattern.compile("\\s+");
private final String normalizeWhitespace(final String str)
{
return P_WHITESPACE.matcher(str).replaceAll("");
}
public QueryConnectionsResult queryMoreConnections(String uri) throws IOException
{
throw new UnsupportedOperationException();
}
public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException
{
throw new UnsupportedOperationException();
}
private final static Pattern P_NEARBY_COARSE = Pattern.compile("<tr class=\"(zebra[^\"]*)\">(.*?)</tr>", Pattern.DOTALL); private final static Pattern P_NEARBY_COARSE = Pattern.compile("<tr class=\"(zebra[^\"]*)\">(.*?)</tr>", Pattern.DOTALL);
private final static Pattern P_NEARBY_FINE_COORDS = Pattern private final static Pattern P_NEARBY_FINE_COORDS = Pattern
.compile("&REQMapRoute0\\.Location0\\.X=(-?\\d+)&REQMapRoute0\\.Location0\\.Y=(-?\\d+)&"); .compile("&REQMapRoute0\\.Location0\\.X=(-?\\d+)&REQMapRoute0\\.Location0\\.Y=(-?\\d+)&");
@ -200,7 +598,7 @@ public abstract class AbstractHafasProvider implements NetworkProvider
if (normalizedType != 0) if (normalizedType != 0)
return normalizedType + strippedLine; return normalizedType + strippedLine;
throw new IllegalStateException("cannot normalize type " + type + " line " + line); throw new IllegalStateException("cannot normalize type '" + type + "' line '" + line + "'");
} }
protected abstract char normalizeType(String type); protected abstract char normalizeType(String type);

View file

@ -48,7 +48,7 @@ public class NasaProvider extends AbstractHafasProvider
public NasaProvider() public NasaProvider()
{ {
super(null, null, null); super(null, null);
} }
public boolean hasCapabilities(final Capability... capabilities) public boolean hasCapabilities(final Capability... capabilities)
@ -250,17 +250,20 @@ public class NasaProvider extends AbstractHafasProvider
return 0; return 0;
} }
@Override
public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep, public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep,
final String products, final WalkSpeed walkSpeed) throws IOException final String products, final WalkSpeed walkSpeed) throws IOException
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public QueryConnectionsResult queryMoreConnections(String uri) throws IOException public QueryConnectionsResult queryMoreConnections(String uri) throws IOException
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View file

@ -50,7 +50,7 @@ public class OebbProvider extends AbstractHafasProvider
public OebbProvider() public OebbProvider()
{ {
super(null, null, null); super(null, null);
} }
public boolean hasCapabilities(final Capability... capabilities) public boolean hasCapabilities(final Capability... capabilities)
@ -233,6 +233,7 @@ public class OebbProvider extends AbstractHafasProvider
"<select.*? name=\"(REQ0JourneyStopsS0K|REQ0JourneyStopsZ0K|REQ0JourneyStops1\\.0K)\"[^>]*>\n(.*?)</select>", Pattern.DOTALL); "<select.*? name=\"(REQ0JourneyStopsS0K|REQ0JourneyStopsZ0K|REQ0JourneyStops1\\.0K)\"[^>]*>\n(.*?)</select>", Pattern.DOTALL);
private static final Pattern P_ADDRESSES = Pattern.compile("<option[^>]*>\\s*([^<\\[]*)(?:\\[[^\\[]*\\])?\\s*</option>", Pattern.DOTALL); private static final Pattern P_ADDRESSES = Pattern.compile("<option[^>]*>\\s*([^<\\[]*)(?:\\[[^\\[]*\\])?\\s*</option>", Pattern.DOTALL);
@Override
public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep, public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep,
final String products, final WalkSpeed walkSpeed) throws IOException final String products, final WalkSpeed walkSpeed) throws IOException
{ {
@ -293,6 +294,7 @@ public class OebbProvider extends AbstractHafasProvider
return queryConnections(baseUri, page); return queryConnections(baseUri, page);
} }
@Override
public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException
{ {
final CharSequence page = ParserUtils.scrape(uri, false, null, null, true); final CharSequence page = ParserUtils.scrape(uri, false, null, null, true);
@ -483,6 +485,7 @@ public class OebbProvider extends AbstractHafasProvider
} }
} }
@Override
public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View file

@ -53,7 +53,7 @@ public class RmvProvider extends AbstractHafasProvider
public RmvProvider() public RmvProvider()
{ {
super(null, null, null); super(null, null);
} }
public boolean hasCapabilities(final Capability... capabilities) public boolean hasCapabilities(final Capability... capabilities)
@ -184,6 +184,7 @@ public class RmvProvider extends AbstractHafasProvider
private static final Pattern P_CHECK_CONNECTIONS_ERROR = Pattern.compile( private static final Pattern P_CHECK_CONNECTIONS_ERROR = Pattern.compile(
"(mehrfach vorhanden oder identisch)|(keine Verbindung gefunden werden)|(derzeit nur Ausk&#252;nfte vom)", Pattern.CASE_INSENSITIVE); "(mehrfach vorhanden oder identisch)|(keine Verbindung gefunden werden)|(derzeit nur Ausk&#252;nfte vom)", Pattern.CASE_INSENSITIVE);
@Override
public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep, public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep,
final String products, final WalkSpeed walkSpeed) throws IOException final String products, final WalkSpeed walkSpeed) throws IOException
{ {
@ -249,6 +250,7 @@ public class RmvProvider extends AbstractHafasProvider
+ "(?:&nbsp;(.+?))?" // + "(?:&nbsp;(.+?))?" //
, Pattern.DOTALL); , Pattern.DOTALL);
@Override
public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException
{ {
final CharSequence page = ParserUtils.scrape(uri); final CharSequence page = ParserUtils.scrape(uri);
@ -333,6 +335,7 @@ public class RmvProvider extends AbstractHafasProvider
+ "- <b>(.*?)" // arrival + "- <b>(.*?)" // arrival
, Pattern.DOTALL); , Pattern.DOTALL);
@Override
public GetConnectionDetailsResult getConnectionDetails(final String uri) throws IOException public GetConnectionDetailsResult getConnectionDetails(final String uri) throws IOException
{ {
final CharSequence page = ParserUtils.scrape(uri); final CharSequence page = ParserUtils.scrape(uri);

View file

@ -45,12 +45,13 @@ public class SbbProvider extends AbstractHafasProvider
{ {
public static final String NETWORK_ID = "fahrplan.sbb.ch"; public static final String NETWORK_ID = "fahrplan.sbb.ch";
private static final String API_BASE = "http://fahrplan.sbb.ch/bin/"; private static final String API_BASE = "http://fahrplan.sbb.ch/bin/";
private static final String API_URI = "http://fahrplan.sbb.ch/bin/extxml.exe";
private static final long PARSER_DAY_ROLLOVER_THRESHOLD_MS = 12 * 60 * 60 * 1000; private static final long PARSER_DAY_ROLLOVER_THRESHOLD_MS = 12 * 60 * 60 * 1000;
public SbbProvider() public SbbProvider(final String accessId)
{ {
super("http://fahrplan.sbb.ch/bin/extxml.exe", "iPhone3.1", "MJXZ841ZfsmqqmSymWhBPy5dMNoqoGsHInHbWJQ5PTUZOJ1rLTkn8vVZOZDFfSe"); super(API_URI, accessId);
} }
public boolean hasCapabilities(final Capability... capabilities) public boolean hasCapabilities(final Capability... capabilities)
@ -131,6 +132,7 @@ public class SbbProvider extends AbstractHafasProvider
private static final Pattern P_CHECK_CONNECTIONS_ERROR = Pattern private static final Pattern P_CHECK_CONNECTIONS_ERROR = Pattern
.compile("(mehrfach vorhanden oder identisch)|(keine Verbindung gefunden werden)|(liegt nach dem Ende der Fahrplanperiode|liegt vor Beginn der Fahrplanperiode)"); .compile("(mehrfach vorhanden oder identisch)|(keine Verbindung gefunden werden)|(liegt nach dem Ende der Fahrplanperiode|liegt vor Beginn der Fahrplanperiode)");
@Override
public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep, public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep,
final String products, final WalkSpeed walkSpeed) throws IOException final String products, final WalkSpeed walkSpeed) throws IOException
{ {
@ -185,6 +187,7 @@ public class SbbProvider extends AbstractHafasProvider
return queryConnections(uri, page); return queryConnections(uri, page);
} }
@Override
public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException
{ {
final CharSequence page = ParserUtils.scrape(uri); final CharSequence page = ParserUtils.scrape(uri);
@ -293,6 +296,7 @@ public class SbbProvider extends AbstractHafasProvider
+ "- ([^<]*) -\n" // destination + "- ([^<]*) -\n" // destination
, Pattern.DOTALL); , Pattern.DOTALL);
@Override
public GetConnectionDetailsResult getConnectionDetails(final String uri) throws IOException public GetConnectionDetailsResult getConnectionDetails(final String uri) throws IOException
{ {
final CharSequence page = ParserUtils.scrape(uri); final CharSequence page = ParserUtils.scrape(uri);

View file

@ -27,9 +27,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import de.schildbach.pte.dto.Departure; import de.schildbach.pte.dto.Departure;
import de.schildbach.pte.dto.GetConnectionDetailsResult;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.QueryConnectionsResult;
import de.schildbach.pte.dto.QueryDeparturesResult; import de.schildbach.pte.dto.QueryDeparturesResult;
import de.schildbach.pte.dto.QueryDeparturesResult.Status; import de.schildbach.pte.dto.QueryDeparturesResult.Status;
import de.schildbach.pte.util.ParserUtils; import de.schildbach.pte.util.ParserUtils;
@ -43,17 +40,17 @@ public class SncbProvider extends AbstractHafasProvider
private static final long PARSER_DAY_ROLLOVER_THRESHOLD_MS = 12 * 60 * 60 * 1000; private static final long PARSER_DAY_ROLLOVER_THRESHOLD_MS = 12 * 60 * 60 * 1000;
private static final String AUTOCOMPLETE_URI = "http://hari.b-rail.be/Hafas/bin/extxml.exe"; private static final String API_URI = "http://hari.b-rail.be/Hafas/bin/extxml.exe";
public SncbProvider() public SncbProvider()
{ {
super(AUTOCOMPLETE_URI, "irail", null); super(API_URI, null);
} }
public boolean hasCapabilities(final Capability... capabilities) public boolean hasCapabilities(final Capability... capabilities)
{ {
for (final Capability capability : capabilities) for (final Capability capability : capabilities)
if (capability == Capability.DEPARTURES) if (capability == Capability.DEPARTURES || capability == Capability.CONNECTIONS)
return true; return true;
return false; return false;
@ -67,22 +64,6 @@ public class SncbProvider extends AbstractHafasProvider
return String.format(NEARBY_URI, stationId); return String.format(NEARBY_URI, stationId);
} }
public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep,
final String products, final WalkSpeed walkSpeed) throws IOException
{
throw new UnsupportedOperationException();
}
public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException
{
throw new UnsupportedOperationException();
}
public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException
{
throw new UnsupportedOperationException();
}
public String departuresQueryUri(final String stationId, final int maxDepartures) public String departuresQueryUri(final String stationId, final int maxDepartures)
{ {
final StringBuilder uri = new StringBuilder(); final StringBuilder uri = new StringBuilder();

View file

@ -48,7 +48,7 @@ public class VgsProvider extends AbstractHafasProvider
public VgsProvider() public VgsProvider()
{ {
super(null, null, null); super(null, null);
} }
public boolean hasCapabilities(final Capability... capabilities) public boolean hasCapabilities(final Capability... capabilities)
@ -247,17 +247,20 @@ public class VgsProvider extends AbstractHafasProvider
return 0; return 0;
} }
@Override
public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep, public QueryConnectionsResult queryConnections(final Location from, final Location via, final Location to, final Date date, final boolean dep,
final String products, final WalkSpeed walkSpeed) throws IOException final String products, final WalkSpeed walkSpeed) throws IOException
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public QueryConnectionsResult queryMoreConnections(String uri) throws IOException public QueryConnectionsResult queryMoreConnections(String uri) throws IOException
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View file

@ -32,6 +32,7 @@ public final class XmlPullUtil
/** /**
* enters current tag * enters current tag
*
* @throws IOException * @throws IOException
*/ */
public static void enter(final XmlPullParser pp) throws XmlPullParserException, IOException public static void enter(final XmlPullParser pp) throws XmlPullParserException, IOException
@ -40,13 +41,25 @@ public final class XmlPullUtil
throw new IllegalStateException("expecting start tag to enter"); throw new IllegalStateException("expecting start tag to enter");
if (pp.isEmptyElementTag()) if (pp.isEmptyElementTag())
throw new IllegalStateException("cannot enter empty tag"); throw new IllegalStateException("cannot enter empty tag");
pp.next(); pp.next();
} }
public static void exit(final XmlPullParser pp) throws XmlPullParserException, IOException public static void exit(final XmlPullParser pp) throws XmlPullParserException, IOException
{ {
while (pp.getEventType() != XmlPullParser.END_TAG)
{
if (pp.getEventType() == XmlPullParser.START_TAG)
next(pp);
else if (pp.getEventType() == XmlPullParser.TEXT)
pp.next();
else
throw new IllegalStateException();
}
if (pp.getEventType() != XmlPullParser.END_TAG) if (pp.getEventType() != XmlPullParser.END_TAG)
throw new IllegalStateException("expecting end tag to exit"); throw new IllegalStateException("expecting end tag to exit");
pp.next(); pp.next();
} }
@ -55,7 +68,7 @@ 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 skipTree(final XmlPullParser pp) throws XmlPullParserException, IOException public static void next(final XmlPullParser pp) throws XmlPullParserException, IOException
{ {
skipSubTree(pp); skipSubTree(pp);
pp.next(); pp.next();
@ -82,6 +95,22 @@ public final class XmlPullUtil
throw new IllegalStateException("cannot find " + attrName + "=\"" + requiredValue + "\" />"); throw new IllegalStateException("cannot find " + attrName + "=\"" + requiredValue + "\" />");
} }
public static String text(final XmlPullParser pp) throws XmlPullParserException, IOException
{
if (pp.getEventType() != XmlPullParser.START_TAG || pp.isEmptyElementTag())
throw new IllegalStateException("expecting start tag to get text from");
enter(pp);
String text = "";
if (pp.getEventType() == XmlPullParser.TEXT)
text = pp.getText();
exit(pp);
return text;
}
/** /**
* Return value of attribute with given name and no namespace. * Return value of attribute with given name and no namespace.
*/ */

View file

@ -36,7 +36,7 @@ import de.schildbach.pte.dto.QueryDeparturesResult;
*/ */
public class SbbProviderLiveTest public class SbbProviderLiveTest
{ {
private SbbProvider provider = new SbbProvider(); private SbbProvider provider = new SbbProvider(Secrets.SBB_ACCESS_ID);
private static final String ALL_PRODUCTS = "IRSUTBFC"; private static final String ALL_PRODUCTS = "IRSUTBFC";
@Test @Test
@ -74,8 +74,8 @@ public class SbbProviderLiveTest
@Test @Test
public void shortConnection() throws Exception public void shortConnection() throws Exception
{ {
final QueryConnectionsResult result = provider.queryConnections(new Location(LocationType.ANY, 0, 0, 0, "Zürich!"), null, new Location( final QueryConnectionsResult result = provider.queryConnections(new Location(LocationType.STATION, 8503000, 0, 0, "Zürich HB"), null,
LocationType.ANY, 0, 0, 0, "Bern"), new Date(), true, ALL_PRODUCTS, WalkSpeed.NORMAL); new Location(LocationType.STATION, 8507785, 0, 0, "Bern, Hauptbahnhof"), new Date(), true, ALL_PRODUCTS, WalkSpeed.NORMAL);
System.out.println(result); System.out.println(result);
final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater);
System.out.println(moreResult); System.out.println(moreResult);

View file

@ -16,13 +16,17 @@
*/ */
package de.schildbach.pte.live; package de.schildbach.pte.live;
import java.util.Date;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
import de.schildbach.pte.SncbProvider; import de.schildbach.pte.SncbProvider;
import de.schildbach.pte.NetworkProvider.WalkSpeed;
import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyStationsResult; import de.schildbach.pte.dto.NearbyStationsResult;
import de.schildbach.pte.dto.QueryConnectionsResult;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
@ -47,6 +51,24 @@ public class SncbProviderLiveTest
System.out.println(); System.out.println();
} }
@Test
public void shortConnection() throws Exception
{
final QueryConnectionsResult result = provider.queryConnections(new Location(LocationType.STATION, 100024, 0, 0, null), null, new Location(
LocationType.STATION, 100066, 0, 0, null), new Date(), true, null, WalkSpeed.FAST);
System.out.println(result.status + " " + result.connections);
}
@Test
public void longConnection() throws Exception
{
final QueryConnectionsResult result = provider.queryConnections(new Location(LocationType.STATION, 100024, 0, 0, null), null, new Location(
LocationType.STATION, 103624, 0, 0, null), new Date(), true, null, WalkSpeed.FAST);
System.out.println(result.status + " " + result.connections);
}
@Test @Test
public void nearbyStation() throws Exception public void nearbyStation() throws Exception
{ {