Use HttpUrl and HttpUrl.Builder where possible.

This commit is contained in:
Andreas Schildbach 2017-01-02 19:15:29 +01:00
parent e50098f092
commit 4f7bd832e6
79 changed files with 977 additions and 916 deletions

View file

@ -22,7 +22,6 @@ import static com.google.common.base.Preconditions.checkState;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
@ -52,7 +51,6 @@ 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 com.google.common.base.Charsets;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Strings; import com.google.common.base.Strings;
@ -97,17 +95,15 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
protected static final String SERVER_PRODUCT = "efa"; protected static final String SERVER_PRODUCT = "efa";
private final String departureMonitorEndpoint; private final HttpUrl departureMonitorEndpoint;
private final String tripEndpoint; private final HttpUrl tripEndpoint;
private final String stopFinderEndpoint; private final HttpUrl stopFinderEndpoint;
private final String coordEndpoint; private final HttpUrl coordEndpoint;
private String language = "de"; private String language = "de";
private @Nullable String additionalQueryParameter = null;
private boolean needsSpEncId = false; private boolean needsSpEncId = false;
private boolean includeRegionId = true; private boolean includeRegionId = true;
private boolean useProxFootSearch = true; private boolean useProxFootSearch = true;
private Charset requestUrlEncoding = Charsets.ISO_8859_1;
private @Nullable String httpReferer = null; private @Nullable String httpReferer = null;
private @Nullable String httpRefererTrip = null; private @Nullable String httpRefererTrip = null;
private boolean httpPost = false; private boolean httpPost = false;
@ -144,22 +140,28 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
} }
} }
public AbstractEfaProvider(final NetworkId network, final String apiBase) { public AbstractEfaProvider(final NetworkId network, final HttpUrl apiBase) {
this(network, apiBase, null, null, null, null); this(network, apiBase, null, null, null, null);
} }
public AbstractEfaProvider(final NetworkId network, final String apiBase, final String departureMonitorEndpoint, public AbstractEfaProvider(final NetworkId network, final HttpUrl apiBase, final String departureMonitorEndpoint,
final String tripEndpoint, final String stopFinderEndpoint, final String coordEndpoint) { final String tripEndpoint, final String stopFinderEndpoint, final String coordEndpoint) {
this(network, this(network,
apiBase + (departureMonitorEndpoint != null ? departureMonitorEndpoint apiBase.newBuilder()
: DEFAULT_DEPARTURE_MONITOR_ENDPOINT), // .addPathSegment(departureMonitorEndpoint != null ? departureMonitorEndpoint
apiBase + (tripEndpoint != null ? tripEndpoint : DEFAULT_TRIP_ENDPOINT), // : DEFAULT_DEPARTURE_MONITOR_ENDPOINT)
apiBase + (stopFinderEndpoint != null ? stopFinderEndpoint : DEFAULT_STOPFINDER_ENDPOINT), // .build(),
apiBase + (coordEndpoint != null ? coordEndpoint : DEFAULT_COORD_ENDPOINT)); apiBase.newBuilder().addPathSegment(tripEndpoint != null ? tripEndpoint : DEFAULT_TRIP_ENDPOINT)
.build(),
apiBase.newBuilder()
.addPathSegment(stopFinderEndpoint != null ? stopFinderEndpoint : DEFAULT_STOPFINDER_ENDPOINT)
.build(),
apiBase.newBuilder().addPathSegment(coordEndpoint != null ? coordEndpoint : DEFAULT_COORD_ENDPOINT)
.build());
} }
public AbstractEfaProvider(final NetworkId network, final String departureMonitorEndpoint, public AbstractEfaProvider(final NetworkId network, final HttpUrl departureMonitorEndpoint,
final String tripEndpoint, final String stopFinderEndpoint, final String coordEndpoint) { final HttpUrl tripEndpoint, final HttpUrl stopFinderEndpoint, final HttpUrl coordEndpoint) {
super(network); super(network);
try { try {
@ -180,16 +182,6 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return this; return this;
} }
protected AbstractEfaProvider setAdditionalQueryParameter(final String additionalQueryParameter) {
this.additionalQueryParameter = additionalQueryParameter;
return this;
}
protected AbstractEfaProvider setRequestUrlEncoding(final Charset requestUrlEncoding) {
this.requestUrlEncoding = requestUrlEncoding;
return this;
}
protected AbstractEfaProvider setHttpReferer(final String httpReferer) { protected AbstractEfaProvider setHttpReferer(final String httpReferer) {
this.httpReferer = httpReferer; this.httpReferer = httpReferer;
this.httpRefererTrip = httpReferer; this.httpRefererTrip = httpReferer;
@ -246,24 +238,21 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return true; return true;
} }
private final void appendCommonRequestParams(final StringBuilder uri, final String outputFormat) { private final void appendCommonRequestParams(final HttpUrl.Builder url, final String outputFormat) {
uri.append("?outputFormat=").append(outputFormat); url.addEncodedQueryParameter("outputFormat", outputFormat);
uri.append("&language=").append(language); url.addEncodedQueryParameter("language", language);
uri.append("&stateless=1"); url.addEncodedQueryParameter("stateless", "1");
uri.append("&coordOutputFormat=WGS84"); url.addEncodedQueryParameter("coordOutputFormat", "WGS84");
if (additionalQueryParameter != null)
uri.append('&').append(additionalQueryParameter);
} }
protected SuggestLocationsResult jsonStopfinderRequest(final Location constraint) throws IOException { protected SuggestLocationsResult jsonStopfinderRequest(final Location constraint) throws IOException {
final StringBuilder uri = new StringBuilder(stopFinderEndpoint); final HttpUrl.Builder url = stopFinderEndpoint.newBuilder();
final StringBuilder parameters = stopfinderRequestParameters(constraint, "JSON"); appendStopfinderRequestParameters(url, constraint, "JSON");
final CharSequence page; final CharSequence page;
if (httpPost) if (httpPost)
page = httpClient.get(HttpUrl.parse(uri.toString()), parameters.substring(1), page = httpClient.get(url.build(), url.build().encodedQuery(), "application/x-www-form-urlencoded");
"application/x-www-form-urlencoded");
else else
page = httpClient.get(HttpUrl.parse(uri.append(parameters).toString())); page = httpClient.get(url.build());
final ResultHeader header = new ResultHeader(network, SERVER_PRODUCT); final ResultHeader header = new ResultHeader(network, SERVER_PRODUCT);
try { try {
@ -310,7 +299,7 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return new SuggestLocationsResult(header, locations); return new SuggestLocationsResult(header, locations);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + page + "' on " + url, x);
} }
} }
@ -347,29 +336,28 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return new SuggestedLocation(location, quality); return new SuggestedLocation(location, quality);
} }
private StringBuilder stopfinderRequestParameters(final Location constraint, final String outputFormat) { private void appendStopfinderRequestParameters(final HttpUrl.Builder url, final Location constraint,
final StringBuilder parameters = new StringBuilder(); final String outputFormat) {
appendCommonRequestParams(parameters, outputFormat); appendCommonRequestParams(url, outputFormat);
parameters.append("&locationServerActive=1"); url.addEncodedQueryParameter("locationServerActive", "1");
if (includeRegionId) if (includeRegionId)
parameters.append("&regionID_sf=1"); // prefer own region url.addEncodedQueryParameter("regionID_sf", "1"); // prefer own region
appendLocation(parameters, constraint, "sf"); appendLocationParams(url, constraint, "sf");
if (constraint.type == LocationType.ANY) { if (constraint.type == LocationType.ANY) {
if (needsSpEncId) if (needsSpEncId)
parameters.append("&SpEncId=0"); url.addEncodedQueryParameter("SpEncId", "0");
// 1=place 2=stop 4=street 8=address 16=crossing 32=poi 64=postcode // 1=place 2=stop 4=street 8=address 16=crossing 32=poi 64=postcode
parameters.append("&anyObjFilter_sf=").append(2 + 4 + 8 + 16 + 32 + 64); url.addEncodedQueryParameter("anyObjFilter_sf", Integer.toString(2 + 4 + 8 + 16 + 32 + 64));
parameters.append("&reducedAnyPostcodeObjFilter_sf=64&reducedAnyTooManyObjFilter_sf=2"); url.addEncodedQueryParameter("reducedAnyPostcodeObjFilter_sf", "64");
parameters.append("&useHouseNumberList=true"); url.addEncodedQueryParameter("reducedAnyTooManyObjFilter_sf", "2");
parameters.append("&anyMaxSizeHitList=500"); url.addEncodedQueryParameter("useHouseNumberList", "true");
url.addEncodedQueryParameter("anyMaxSizeHitList", "500");
} }
return parameters;
} }
protected SuggestLocationsResult xmlStopfinderRequest(final Location constraint) throws IOException { protected SuggestLocationsResult xmlStopfinderRequest(final Location constraint) throws IOException {
final StringBuilder uri = new StringBuilder(stopFinderEndpoint); final HttpUrl.Builder url = stopFinderEndpoint.newBuilder();
final StringBuilder parameters = stopfinderRequestParameters(constraint, "XML"); appendStopfinderRequestParameters(url, constraint, "XML");
final AtomicReference<SuggestLocationsResult> result = new AtomicReference<SuggestLocationsResult>(); final AtomicReference<SuggestLocationsResult> result = new AtomicReference<SuggestLocationsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -401,17 +389,17 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpReferer); "application/x-www-form-urlencoded", httpReferer);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpReferer); httpClient.getInputStream(callback, url.build(), httpReferer);
return result.get(); return result.get();
} }
protected SuggestLocationsResult mobileStopfinderRequest(final Location constraint) throws IOException { protected SuggestLocationsResult mobileStopfinderRequest(final Location constraint) throws IOException {
final StringBuilder uri = new StringBuilder(stopFinderEndpoint); final HttpUrl.Builder url = stopFinderEndpoint.newBuilder();
final StringBuilder parameters = stopfinderRequestParameters(constraint, "XML"); appendStopfinderRequestParameters(url, constraint, "XML");
final AtomicReference<SuggestLocationsResult> result = new AtomicReference<SuggestLocationsResult>(); final AtomicReference<SuggestLocationsResult> result = new AtomicReference<SuggestLocationsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -486,45 +474,42 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpReferer); "application/x-www-form-urlencoded", httpReferer);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpReferer); httpClient.getInputStream(callback, url.build(), httpReferer);
return result.get(); return result.get();
} }
private StringBuilder xmlCoordRequestParameters(final EnumSet<LocationType> types, final int lat, final int lon, private void appendXmlCoordRequestParameters(final HttpUrl.Builder url, final EnumSet<LocationType> types,
final int maxDistance, final int maxLocations) { final int lat, final int lon, final int maxDistance, final int maxLocations) {
final StringBuilder parameters = new StringBuilder(); appendCommonRequestParams(url, "XML");
appendCommonRequestParams(parameters, "XML"); url.addEncodedQueryParameter("coord",
parameters.append("&coord=") ParserUtils.urlEncode(
.append(String.format(Locale.ENGLISH, "%2.6f:%2.6f:WGS84", latLonToDouble(lon), latLonToDouble(lat))); String.format(Locale.ENGLISH, "%2.6f:%2.6f:WGS84", latLonToDouble(lon), latLonToDouble(lat)),
requestUrlEncoding));
if (useStringCoordListOutputFormat) if (useStringCoordListOutputFormat)
parameters.append("&coordListOutputFormat=STRING"); url.addEncodedQueryParameter("coordListOutputFormat", "STRING");
parameters.append("&max=").append(maxLocations != 0 ? maxLocations : 50); url.addEncodedQueryParameter("max", Integer.toString(maxLocations != 0 ? maxLocations : 50));
parameters.append("&inclFilter=1"); url.addEncodedQueryParameter("inclFilter", "1");
int i = 1; int i = 1;
for (final LocationType type : types) { for (final LocationType type : types) {
parameters.append("&radius_").append(i).append('=').append(maxDistance != 0 ? maxDistance : 1320); url.addEncodedQueryParameter("radius_" + i, Integer.toString(maxDistance != 0 ? maxDistance : 1320));
parameters.append("&type_").append(i).append('=');
if (type == LocationType.STATION) if (type == LocationType.STATION)
parameters.append("STOP"); url.addEncodedQueryParameter("type_" + i, "STOP");
else if (type == LocationType.POI) else if (type == LocationType.POI)
parameters.append("POI_POINT"); url.addEncodedQueryParameter("type_" + i, "POI_POINT");
else else
throw new IllegalArgumentException("cannot handle location type: " + type); // ENTRANCE, throw new IllegalArgumentException("cannot handle location type: " + type);
// BUS_POINT
i++; i++;
} }
return parameters;
} }
protected NearbyLocationsResult xmlCoordRequest(final EnumSet<LocationType> types, final int lat, final int lon, protected NearbyLocationsResult xmlCoordRequest(final EnumSet<LocationType> types, final int lat, final int lon,
final int maxDistance, final int maxStations) throws IOException { final int maxDistance, final int maxStations) throws IOException {
final StringBuilder uri = new StringBuilder(coordEndpoint); final HttpUrl.Builder url = coordEndpoint.newBuilder();
final StringBuilder parameters = xmlCoordRequestParameters(types, lat, lon, maxDistance, maxStations); appendXmlCoordRequestParameters(url, types, lat, lon, maxDistance, maxStations);
final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>(); final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -587,18 +572,18 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpReferer); "application/x-www-form-urlencoded", httpReferer);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpReferer); httpClient.getInputStream(callback, url.build(), httpReferer);
return result.get(); return result.get();
} }
protected NearbyLocationsResult mobileCoordRequest(final EnumSet<LocationType> types, final int lat, final int lon, protected NearbyLocationsResult mobileCoordRequest(final EnumSet<LocationType> types, final int lat, final int lon,
final int maxDistance, final int maxStations) throws IOException { final int maxDistance, final int maxStations) throws IOException {
final StringBuilder uri = new StringBuilder(coordEndpoint); final HttpUrl.Builder url = coordEndpoint.newBuilder();
final StringBuilder parameters = xmlCoordRequestParameters(types, lat, lon, maxDistance, maxStations); appendXmlCoordRequestParameters(url, types, lat, lon, maxDistance, maxStations);
final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>(); final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -665,10 +650,10 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpReferer); "application/x-www-form-urlencoded", httpReferer);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpReferer); httpClient.getInputStream(callback, url.build(), httpReferer);
return result.get(); return result.get();
} }
@ -864,17 +849,18 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
private NearbyLocationsResult nearbyStationsRequest(final String stationId, final int maxLocations) private NearbyLocationsResult nearbyStationsRequest(final String stationId, final int maxLocations)
throws IOException { throws IOException {
final StringBuilder uri = new StringBuilder(departureMonitorEndpoint); final HttpUrl.Builder url = departureMonitorEndpoint.newBuilder();
final StringBuilder parameters = new StringBuilder(); appendCommonRequestParams(url, "XML");
appendCommonRequestParams(parameters, "XML"); url.addEncodedQueryParameter("type_dm", "stop");
parameters.append("&type_dm=stop&name_dm=").append(normalizeStationId(stationId)); url.addEncodedQueryParameter("name_dm",
parameters.append("&itOptionsActive=1"); ParserUtils.urlEncode(normalizeStationId(stationId), requestUrlEncoding));
parameters.append("&ptOptionsActive=1"); url.addEncodedQueryParameter("itOptionsActive", "1");
url.addEncodedQueryParameter("ptOptionsActive", "1");
if (useProxFootSearch) if (useProxFootSearch)
parameters.append("&useProxFootSearch=1"); url.addEncodedQueryParameter("useProxFootSearch", "1");
parameters.append("&mergeDep=1"); url.addEncodedQueryParameter("mergeDep", "1");
parameters.append("&useAllStops=1"); url.addEncodedQueryParameter("useAllStops", "1");
parameters.append("&mode=direct"); url.addEncodedQueryParameter("mode", "direct");
final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>(); final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -923,10 +909,10 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpReferer); "application/x-www-form-urlencoded", httpReferer);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpReferer); httpClient.getInputStream(callback, url.build(), httpReferer);
return result.get(); return result.get();
} }
@ -1433,28 +1419,26 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return xsltDepartureMonitorRequest(stationId, time, maxDepartures, equivs); return xsltDepartureMonitorRequest(stationId, time, maxDepartures, equivs);
} }
protected StringBuilder xsltDepartureMonitorRequestParameters(final String stationId, final @Nullable Date time, protected void appendXsltDepartureMonitorRequestParameters(final HttpUrl.Builder url, final String stationId,
final int maxDepartures, final boolean equivs) { final @Nullable Date time, final int maxDepartures, final boolean equivs) {
final StringBuilder parameters = new StringBuilder(); appendCommonRequestParams(url, "XML");
appendCommonRequestParams(parameters, "XML"); url.addEncodedQueryParameter("type_dm", "stop");
parameters.append("&type_dm=stop"); url.addEncodedQueryParameter("name_dm",
parameters.append("&name_dm=").append(normalizeStationId(stationId)); ParserUtils.urlEncode(normalizeStationId(stationId), requestUrlEncoding));
if (time != null) if (time != null)
appendItdDateTimeParameters(parameters, time); appendItdDateTimeParameters(url, time);
parameters.append("&useRealtime=1"); url.addEncodedQueryParameter("useRealtime", "1");
parameters.append("&mode=direct"); url.addEncodedQueryParameter("mode", "direct");
parameters.append("&ptOptionsActive=1"); url.addEncodedQueryParameter("ptOptionsActive", "1");
parameters.append("&deleteAssignedStops_dm=").append(equivs ? '0' : '1'); url.addEncodedQueryParameter("deleteAssignedStops_dm", equivs ? "0" : "1");
if (useProxFootSearch) if (useProxFootSearch)
parameters.append("&useProxFootSearch=").append(equivs ? '1' : '0'); url.addEncodedQueryParameter("useProxFootSearch", equivs ? "1" : "0");
parameters.append("&mergeDep=1"); // merge departures url.addEncodedQueryParameter("mergeDep", "1"); // merge departures
if (maxDepartures > 0) if (maxDepartures > 0)
parameters.append("&limit=").append(maxDepartures); url.addEncodedQueryParameter("limit", Integer.toString(maxDepartures));
return parameters;
} }
private final void appendItdDateTimeParameters(final StringBuilder uri, final Date time) { private final void appendItdDateTimeParameters(final HttpUrl.Builder url, final Date time) {
final Calendar c = new GregorianCalendar(timeZone); final Calendar c = new GregorianCalendar(timeZone);
c.setTime(time); c.setTime(time);
final int year = c.get(Calendar.YEAR); final int year = c.get(Calendar.YEAR);
@ -1462,14 +1446,14 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
final int day = c.get(Calendar.DAY_OF_MONTH); final int day = c.get(Calendar.DAY_OF_MONTH);
final int hour = c.get(Calendar.HOUR_OF_DAY); final int hour = c.get(Calendar.HOUR_OF_DAY);
final int minute = c.get(Calendar.MINUTE); final int minute = c.get(Calendar.MINUTE);
uri.append("&itdDate=").append(String.format(Locale.ENGLISH, "%04d%02d%02d", year, month, day)); url.addEncodedQueryParameter("itdDate", String.format(Locale.ENGLISH, "%04d%02d%02d", year, month, day));
uri.append("&itdTime=").append(String.format(Locale.ENGLISH, "%02d%02d", hour, minute)); url.addEncodedQueryParameter("itdTime", String.format(Locale.ENGLISH, "%02d%02d", hour, minute));
} }
private QueryDeparturesResult xsltDepartureMonitorRequest(final String stationId, final @Nullable Date time, private QueryDeparturesResult xsltDepartureMonitorRequest(final String stationId, final @Nullable Date time,
final int maxDepartures, final boolean equivs) throws IOException { final int maxDepartures, final boolean equivs) throws IOException {
final StringBuilder uri = new StringBuilder(departureMonitorEndpoint); final HttpUrl.Builder url = departureMonitorEndpoint.newBuilder();
final StringBuilder parameters = xsltDepartureMonitorRequestParameters(stationId, time, maxDepartures, equivs); appendXsltDepartureMonitorRequestParameters(url, stationId, time, maxDepartures, equivs);
final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>(); final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -1632,18 +1616,18 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpReferer); "application/x-www-form-urlencoded", httpReferer);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpReferer); httpClient.getInputStream(callback, url.build(), httpReferer);
return result.get(); return result.get();
} }
protected QueryDeparturesResult queryDeparturesMobile(final String stationId, final @Nullable Date time, protected QueryDeparturesResult queryDeparturesMobile(final String stationId, final @Nullable Date time,
final int maxDepartures, final boolean equivs) throws IOException { final int maxDepartures, final boolean equivs) throws IOException {
final StringBuilder uri = new StringBuilder(departureMonitorEndpoint); final HttpUrl.Builder url = departureMonitorEndpoint.newBuilder();
final StringBuilder parameters = xsltDepartureMonitorRequestParameters(stationId, time, maxDepartures, equivs); appendXsltDepartureMonitorRequestParameters(url, stationId, time, maxDepartures, equivs);
final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>(); final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -1716,10 +1700,10 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpReferer); "application/x-www-form-urlencoded", httpReferer);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpReferer); httpClient.getInputStream(callback, url.build(), httpReferer);
return result.get(); return result.get();
} }
@ -1956,115 +1940,114 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return (double) value / 1000000; return (double) value / 1000000;
} }
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(); final @Nullable Set<Option> options) {
appendCommonRequestParams(uri, "XML"); appendCommonRequestParams(url, "XML");
uri.append("&sessionID=0"); url.addEncodedQueryParameter("sessionID", "0");
uri.append("&requestID=0"); url.addEncodedQueryParameter("requestID", "0");
appendCommonXsltTripRequest2Params(uri); appendCommonXsltTripRequest2Params(url);
appendLocation(uri, from, "origin"); appendLocationParams(url, from, "origin");
appendLocation(uri, to, "destination"); appendLocationParams(url, to, "destination");
if (via != null) if (via != null)
appendLocation(uri, via, "via"); appendLocationParams(url, via, "via");
appendItdDateTimeParameters(uri, time); appendItdDateTimeParameters(url, time);
uri.append("&itdTripDateTimeDepArr=").append(dep ? "dep" : "arr"); url.addEncodedQueryParameter("itdTripDateTimeDepArr", dep ? "dep" : "arr");
uri.append("&calcNumberOfTrips=").append(numTripsRequested); url.addEncodedQueryParameter("calcNumberOfTrips", Integer.toString(numTripsRequested));
uri.append("&ptOptionsActive=1"); // enable public transport options url.addEncodedQueryParameter("ptOptionsActive", "1"); // enable public transport options
uri.append("&itOptionsActive=1"); // enable individual transport options url.addEncodedQueryParameter("itOptionsActive", "1"); // enable individual transport options
if (optimize == Optimize.LEAST_DURATION) if (optimize == Optimize.LEAST_DURATION)
uri.append("&routeType=LEASTTIME"); url.addEncodedQueryParameter("routeType", "LEASTTIME");
else if (optimize == Optimize.LEAST_CHANGES) else if (optimize == Optimize.LEAST_CHANGES)
uri.append("&routeType=LEASTINTERCHANGE"); url.addEncodedQueryParameter("routeType", "LEASTINTERCHANGE");
else if (optimize == Optimize.LEAST_WALKING) else if (optimize == Optimize.LEAST_WALKING)
uri.append("&routeType=LEASTWALKING"); url.addEncodedQueryParameter("routeType", "LEASTWALKING");
else if (optimize != null) else if (optimize != null)
log.info("Cannot handle " + optimize + ", ignoring."); log.info("Cannot handle " + optimize + ", ignoring.");
uri.append("&changeSpeed=").append(WALKSPEED_MAP.get(walkSpeed)); url.addEncodedQueryParameter("changeSpeed", WALKSPEED_MAP.get(walkSpeed));
if (accessibility == Accessibility.BARRIER_FREE) if (accessibility == Accessibility.BARRIER_FREE)
uri.append("&imparedOptionsActive=1").append("&wheelchair=on").append("&noSolidStairs=on"); url.addEncodedQueryParameter("imparedOptionsActive", "1").addEncodedQueryParameter("wheelchair", "on")
.addEncodedQueryParameter("noSolidStairs", "on");
else if (accessibility == Accessibility.LIMITED) else if (accessibility == Accessibility.LIMITED)
uri.append("&imparedOptionsActive=1").append("&wheelchair=on").append("&lowPlatformVhcl=on") url.addEncodedQueryParameter("imparedOptionsActive", "1").addEncodedQueryParameter("wheelchair", "on")
.append("&noSolidStairs=on"); .addEncodedQueryParameter("lowPlatformVhcl", "on").addEncodedQueryParameter("noSolidStairs", "on");
if (products != null) { if (products != null) {
uri.append("&includedMeans=checkbox"); url.addEncodedQueryParameter("includedMeans", "checkbox");
boolean hasI = false; boolean hasI = false;
for (final Product p : products) { for (final Product p : products) {
if (p == Product.HIGH_SPEED_TRAIN || p == Product.REGIONAL_TRAIN) { if (p == Product.HIGH_SPEED_TRAIN || p == Product.REGIONAL_TRAIN) {
uri.append("&inclMOT_0=on"); url.addEncodedQueryParameter("inclMOT_0", "on");
if (p == Product.HIGH_SPEED_TRAIN) if (p == Product.HIGH_SPEED_TRAIN)
hasI = true; hasI = true;
} }
if (p == Product.SUBURBAN_TRAIN) if (p == Product.SUBURBAN_TRAIN)
uri.append("&inclMOT_1=on"); url.addEncodedQueryParameter("inclMOT_1", "on");
if (p == Product.SUBWAY) if (p == Product.SUBWAY)
uri.append("&inclMOT_2=on"); url.addEncodedQueryParameter("inclMOT_2", "on");
if (p == Product.TRAM) if (p == Product.TRAM)
uri.append("&inclMOT_3=on&inclMOT_4=on"); url.addEncodedQueryParameter("inclMOT_3", "on").addEncodedQueryParameter("inclMOT_4", "on");
if (p == Product.BUS) if (p == Product.BUS)
uri.append("&inclMOT_5=on&inclMOT_6=on&inclMOT_7=on"); url.addEncodedQueryParameter("inclMOT_5", "on").addEncodedQueryParameter("inclMOT_6", "on")
.addEncodedQueryParameter("inclMOT_7", "on");
if (p == Product.ON_DEMAND) if (p == Product.ON_DEMAND)
uri.append("&inclMOT_10=on"); url.addEncodedQueryParameter("inclMOT_10", "on");
if (p == Product.FERRY) if (p == Product.FERRY)
uri.append("&inclMOT_9=on"); url.addEncodedQueryParameter("inclMOT_9", "on");
if (p == Product.CABLECAR) if (p == Product.CABLECAR)
uri.append("&inclMOT_8=on"); url.addEncodedQueryParameter("inclMOT_8", "on");
} }
// workaround for highspeed trains: fails when you want highspeed, but not regional // workaround for highspeed trains: fails when you want highspeed, but not regional
if (useLineRestriction && !hasI) if (useLineRestriction && !hasI)
uri.append("&lineRestriction=403"); // means: all but ice url.addEncodedQueryParameter("lineRestriction", "403"); // means: all but ice
} }
if (useProxFootSearch) if (useProxFootSearch)
uri.append("&useProxFootSearch=1"); // walk if it makes journeys quicker url.addEncodedQueryParameter("useProxFootSearch", "1"); // walk if it makes journeys quicker
uri.append("&trITMOTvalue100=10"); // maximum time to walk to first or from last stop url.addEncodedQueryParameter("trITMOTvalue100", "10"); // maximum time to walk to first or from last
// stop
if (options != null && options.contains(Option.BIKE)) if (options != null && options.contains(Option.BIKE))
uri.append("&bikeTakeAlong=1"); url.addEncodedQueryParameter("bikeTakeAlong", "1");
uri.append("&locationServerActive=1"); url.addEncodedQueryParameter("locationServerActive", "1");
uri.append("&useRealtime=1"); url.addEncodedQueryParameter("useRealtime", "1");
uri.append("&nextDepsPerLeg=1"); // next departure in case previous was missed url.addEncodedQueryParameter("nextDepsPerLeg", "1"); // next departure in case previous was missed
return uri.toString();
} }
private String commandLink(final String sessionId, final String requestId) { private HttpUrl commandLink(final String sessionId, final String requestId) {
final StringBuilder uri = new StringBuilder(tripEndpoint); final HttpUrl.Builder url = tripEndpoint.newBuilder();
url.addEncodedQueryParameter("sessionID", sessionId);
uri.append("?sessionID=").append(sessionId); url.addEncodedQueryParameter("requestID", requestId);
uri.append("&requestID=").append(requestId); url.addEncodedQueryParameter("calcNumberOfTrips", Integer.toString(numTripsRequested));
uri.append("&calcNumberOfTrips=").append(numTripsRequested); appendCommonXsltTripRequest2Params(url);
appendCommonXsltTripRequest2Params(uri); return url.build();
return uri.toString();
} }
private final void appendCommonXsltTripRequest2Params(final StringBuilder uri) { private final void appendCommonXsltTripRequest2Params(final HttpUrl.Builder url) {
if (useStringCoordListOutputFormat) if (useStringCoordListOutputFormat)
uri.append("&coordListOutputFormat=STRING"); url.addEncodedQueryParameter("coordListOutputFormat", "STRING");
} }
@Override @Override
@ -2072,29 +2055,29 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
final Date date, final boolean dep, final @Nullable Set<Product> products, final Date date, final boolean dep, final @Nullable Set<Product> products,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) throws IOException { final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) throws IOException {
final StringBuilder uri = new StringBuilder(tripEndpoint); final HttpUrl.Builder url = tripEndpoint.newBuilder();
final String parameters = xsltTripRequestParameters(from, via, to, date, dep, products, optimize, walkSpeed, appendXsltTripRequestParameters(url, from, via, to, date, dep, products, optimize, walkSpeed, accessibility,
accessibility, options); options);
final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>(); final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@Override @Override
public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException { public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException {
try { try {
result.set(queryTrips(uri.toString(), body.byteStream())); result.set(queryTrips(url.build(), body.byteStream()));
} catch (final XmlPullParserException x) { } catch (final XmlPullParserException x) {
throw new ParserException("cannot parse xml: " + bodyPeek, x); throw new ParserException("cannot parse xml: " + bodyPeek, x);
} catch (final RuntimeException x) { } catch (final RuntimeException x) {
throw new RuntimeException("uncategorized problem while processing " + uri, x); throw new RuntimeException("uncategorized problem while processing " + url, x);
} }
} }
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpRefererTrip); "application/x-www-form-urlencoded", httpRefererTrip);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpRefererTrip); httpClient.getInputStream(callback, url.build(), httpRefererTrip);
return result.get(); return result.get();
} }
@ -2103,29 +2086,29 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
final Date date, final boolean dep, final @Nullable Collection<Product> products, final Date date, final boolean dep, final @Nullable Collection<Product> products,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) throws IOException { final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) throws IOException {
final StringBuilder uri = new StringBuilder(tripEndpoint); final HttpUrl.Builder url = tripEndpoint.newBuilder();
final String parameters = xsltTripRequestParameters(from, via, to, date, dep, products, optimize, walkSpeed, appendXsltTripRequestParameters(url, from, via, to, date, dep, products, optimize, walkSpeed, accessibility,
accessibility, options); options);
final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>(); final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@Override @Override
public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException { public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException {
try { try {
result.set(queryTripsMobile(uri.toString(), from, via, to, body.byteStream())); result.set(queryTripsMobile(url.build(), from, via, to, body.byteStream()));
} catch (final XmlPullParserException x) { } catch (final XmlPullParserException x) {
throw new ParserException("cannot parse xml: " + bodyPeek, x); throw new ParserException("cannot parse xml: " + bodyPeek, x);
} catch (final RuntimeException x) { } catch (final RuntimeException x) {
throw new RuntimeException("uncategorized problem while processing " + uri, x); throw new RuntimeException("uncategorized problem while processing " + url, x);
} }
} }
}; };
if (httpPost) if (httpPost)
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), parameters.substring(1), httpClient.getInputStream(callback, url.build(), url.build().encodedQuery(),
"application/x-www-form-urlencoded", httpRefererTrip); "application/x-www-form-urlencoded", httpRefererTrip);
else else
httpClient.getInputStream(callback, HttpUrl.parse(uri.append(parameters).toString()), httpRefererTrip); httpClient.getInputStream(callback, url.build(), httpRefererTrip);
return result.get(); return result.get();
} }
@ -2133,25 +2116,25 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
@Override @Override
public QueryTripsResult queryMoreTrips(final QueryTripsContext contextObj, final boolean later) throws IOException { public QueryTripsResult queryMoreTrips(final QueryTripsContext contextObj, final boolean later) throws IOException {
final Context context = (Context) contextObj; final Context context = (Context) contextObj;
final String commandUri = context.context; final HttpUrl commandUrl = HttpUrl.parse(context.context);
final StringBuilder uri = new StringBuilder(commandUri); final HttpUrl.Builder url = commandUrl.newBuilder();
uri.append("&command=").append(later ? "tripNext" : "tripPrev"); url.addEncodedQueryParameter("command", later ? "tripNext" : "tripPrev");
final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>(); final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@Override @Override
public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException { public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException {
try { try {
result.set(queryTrips(uri.toString(), body.byteStream())); result.set(queryTrips(url.build(), body.byteStream()));
} catch (final XmlPullParserException x) { } catch (final XmlPullParserException x) {
throw new ParserException("cannot parse xml: " + bodyPeek, x); throw new ParserException("cannot parse xml: " + bodyPeek, x);
} catch (final RuntimeException x) { } catch (final RuntimeException x) {
throw new RuntimeException("uncategorized problem while processing " + uri, x); throw new RuntimeException("uncategorized problem while processing " + url, x);
} }
} }
}; };
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), httpRefererTrip); httpClient.getInputStream(callback, url.build(), httpRefererTrip);
return result.get(); return result.get();
} }
@ -2159,30 +2142,30 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
protected QueryTripsResult queryMoreTripsMobile(final QueryTripsContext contextObj, final boolean later) protected QueryTripsResult queryMoreTripsMobile(final QueryTripsContext contextObj, final boolean later)
throws IOException { throws IOException {
final Context context = (Context) contextObj; final Context context = (Context) contextObj;
final String commandUri = context.context; final HttpUrl commandUrl = HttpUrl.parse(context.context);
final StringBuilder uri = new StringBuilder(commandUri); final HttpUrl.Builder url = commandUrl.newBuilder();
uri.append("&command=").append(later ? "tripNext" : "tripPrev"); url.addEncodedQueryParameter("command", later ? "tripNext" : "tripPrev");
final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>(); final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@Override @Override
public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException { public void onSuccessful(final CharSequence bodyPeek, final ResponseBody body) throws IOException {
try { try {
result.set(queryTripsMobile(uri.toString(), null, null, null, body.byteStream())); result.set(queryTripsMobile(url.build(), null, null, null, body.byteStream()));
} catch (final XmlPullParserException x) { } catch (final XmlPullParserException x) {
throw new ParserException("cannot parse xml: " + bodyPeek, x); throw new ParserException("cannot parse xml: " + bodyPeek, x);
} catch (final RuntimeException x) { } catch (final RuntimeException x) {
throw new RuntimeException("uncategorized problem while processing " + uri, x); throw new RuntimeException("uncategorized problem while processing " + url, x);
} }
} }
}; };
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString()), httpRefererTrip); httpClient.getInputStream(callback, url.build(), httpRefererTrip);
return result.get(); return result.get();
} }
private QueryTripsResult queryTrips(final String uri, final InputStream is) private QueryTripsResult queryTrips(final HttpUrl url, final InputStream is)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
final XmlPullParser pp = parserFactory.newPullParser(); final XmlPullParser pp = parserFactory.newPullParser();
pp.setInput(is, null); // Read encoding from XML declaration pp.setInput(is, null); // Read encoding from XML declaration
@ -2461,8 +2444,8 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
XmlPullUtil.next(pp); XmlPullUtil.next(pp);
} }
return new QueryTripsResult(header, uri, from, via, to, new Context(commandLink((String) context, requestId)), return new QueryTripsResult(header, url.toString(), from, via, to,
trips); new Context(commandLink((String) context, requestId).toString()), trips);
} }
private void processIndividualLeg(final XmlPullParser pp, final List<Leg> legs, private void processIndividualLeg(final XmlPullParser pp, final List<Leg> legs,
@ -2692,7 +2675,7 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return cancelled; return cancelled;
} }
private QueryTripsResult queryTripsMobile(final String uri, final Location from, final @Nullable Location via, private QueryTripsResult queryTripsMobile(final HttpUrl url, final Location from, final @Nullable Location via,
final Location to, final InputStream is) throws XmlPullParserException, IOException { final Location to, final InputStream is) throws XmlPullParserException, IOException {
final XmlPullParser pp = parserFactory.newPullParser(); final XmlPullParser pp = parserFactory.newPullParser();
pp.setInput(is, null); // Read encoding from XML declaration pp.setInput(is, null); // Read encoding from XML declaration
@ -2921,8 +2904,8 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
if (trips.size() > 0) { if (trips.size() > 0) {
final String[] context = (String[]) header.context; final String[] context = (String[]) header.context;
return new QueryTripsResult(header, uri, from, via, to, new Context(commandLink(context[0], context[1])), return new QueryTripsResult(header, url.toString(), from, via, to,
trips); new Context(commandLink(context[0], context[1]).toString()), trips);
} else { } else {
return new QueryTripsResult(header, QueryTripsResult.Status.NO_TRIPS); return new QueryTripsResult(header, QueryTripsResult.Status.NO_TRIPS);
} }
@ -3068,18 +3051,16 @@ public abstract class AbstractEfaProvider extends AbstractNetworkProvider {
return super.parsePosition(position); return super.parsePosition(position);
} }
private void appendLocation(final StringBuilder uri, final Location location, final String paramSuffix) { private void appendLocationParams(final HttpUrl.Builder url, final Location location, final String paramSuffix) {
final String name = locationValue(location); final String name = locationValue(location);
if ((location.type == LocationType.ADDRESS || location.type == LocationType.COORD) && location.hasLocation()) { if ((location.type == LocationType.ADDRESS || location.type == LocationType.COORD) && location.hasLocation()) {
uri.append("&type_").append(paramSuffix).append("=coord"); url.addEncodedQueryParameter("type_" + paramSuffix, "coord");
uri.append("&name_").append(paramSuffix).append("=") url.addEncodedQueryParameter("name_" + paramSuffix, ParserUtils.urlEncode(
.append(String.format(Locale.ENGLISH, "%.6f:%.6f", location.lon / 1E6, location.lat / 1E6)) String.format(Locale.ENGLISH, "%.6f:%.6f", location.lon / 1E6, location.lat / 1E6) + ":WGS84",
.append(":WGS84"); requestUrlEncoding));
} else if (name != null) { } else if (name != null) {
uri.append("&type_").append(paramSuffix).append("=").append(locationTypeValue(location)); url.addEncodedQueryParameter("type_" + paramSuffix, locationTypeValue(location));
uri.append("&name_").append(paramSuffix).append("=") url.addEncodedQueryParameter("name_" + paramSuffix, ParserUtils.urlEncode(name, requestUrlEncoding));
.append(ParserUtils.urlEncode(name, requestUrlEncoding));
} else { } else {
throw new IllegalArgumentException("cannot append location: " + location); throw new IllegalArgumentException("cannot append location: " + location);
} }

View file

@ -102,18 +102,18 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
protected static final int DEFAULT_MAX_DEPARTURES = 100; protected static final int DEFAULT_MAX_DEPARTURES = 100;
protected static final int DEFAULT_MAX_LOCATIONS = 50; protected static final int DEFAULT_MAX_LOCATIONS = 50;
protected String stationBoardEndpoint; protected HttpUrl stationBoardEndpoint;
protected String getStopEndpoint; protected HttpUrl getStopEndpoint;
protected String queryEndpoint; protected HttpUrl queryEndpoint;
protected final String mgateEndpoint; protected final HttpUrl mgateEndpoint;
private @Nullable String extXmlEndpoint = null; private @Nullable HttpUrl extXmlEndpoint = null;
protected final String apiLanguage;
private Product[] productsMap; private Product[] productsMap;
private @Nullable String accessId = null; private @Nullable String accessId = null;
private @Nullable String clientType = "ANDROID"; private @Nullable String clientType = "ANDROID";
private @Nullable String jsonApiVersion; private @Nullable String jsonApiVersion;
private @Nullable String jsonApiAuthorization; private @Nullable String jsonApiAuthorization;
private @Nullable String jsonApiClient; private @Nullable String jsonApiClient;
private Charset jsonGetStopsEncoding = Charsets.ISO_8859_1;
private boolean jsonGetStopsUseWeight = true; private boolean jsonGetStopsUseWeight = true;
private Charset jsonNearbyLocationsEncoding = Charsets.ISO_8859_1; private Charset jsonNearbyLocationsEncoding = Charsets.ISO_8859_1;
private boolean dominantPlanStopTime = false; private boolean dominantPlanStopTime = false;
@ -207,33 +207,33 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
} }
} }
public AbstractHafasProvider(final NetworkId network, final String apiBase, final String apiLanguage, public AbstractHafasProvider(final NetworkId network, final HttpUrl apiBase, final String apiLanguage,
final Product[] productsMap) { final Product[] productsMap) {
super(network); super(network);
this.stationBoardEndpoint = apiBase.newBuilder().addPathSegment("stboard.exe").build();
this.stationBoardEndpoint = apiBase + "stboard.exe/" + apiLanguage; this.getStopEndpoint = apiBase.newBuilder().addPathSegment("ajax-getstop.exe").build();
this.getStopEndpoint = apiBase + "ajax-getstop.exe/" + apiLanguage; this.queryEndpoint = apiBase.newBuilder().addPathSegment("query.exe").build();
this.queryEndpoint = apiBase + "query.exe/" + apiLanguage; this.mgateEndpoint = apiBase.newBuilder().addPathSegment("mgate.exe").build();
this.mgateEndpoint = apiBase + "mgate.exe"; this.apiLanguage = apiLanguage;
this.productsMap = productsMap; this.productsMap = productsMap;
} }
protected AbstractHafasProvider setStationBoardEndpoint(final String stationBoardEndpoint) { protected AbstractHafasProvider setStationBoardEndpoint(final HttpUrl stationBoardEndpoint) {
this.stationBoardEndpoint = stationBoardEndpoint; this.stationBoardEndpoint = stationBoardEndpoint;
return this; return this;
} }
protected AbstractHafasProvider setGetStopEndpoint(final String getStopEndpoint) { protected AbstractHafasProvider setGetStopEndpoint(final HttpUrl getStopEndpoint) {
this.getStopEndpoint = getStopEndpoint; this.getStopEndpoint = getStopEndpoint;
return this; return this;
} }
protected AbstractHafasProvider setQueryEndpoint(final String queryEndpoint) { protected AbstractHafasProvider setQueryEndpoint(final HttpUrl queryEndpoint) {
this.queryEndpoint = queryEndpoint; this.queryEndpoint = queryEndpoint;
return this; return this;
} }
protected AbstractHafasProvider setExtXmlEndpoint(final String extXmlEndpoint) { protected AbstractHafasProvider setExtXmlEndpoint(final HttpUrl extXmlEndpoint) {
this.extXmlEndpoint = extXmlEndpoint; this.extXmlEndpoint = extXmlEndpoint;
return this; return this;
} }
@ -268,11 +268,6 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
return this; return this;
} }
protected AbstractHafasProvider setJsonGetStopsEncoding(final Charset jsonGetStopsEncoding) {
this.jsonGetStopsEncoding = jsonGetStopsEncoding;
return this;
}
protected AbstractHafasProvider setJsonGetStopsUseWeight(final boolean jsonGetStopsUseWeight) { protected AbstractHafasProvider setJsonGetStopsUseWeight(final boolean jsonGetStopsUseWeight) {
this.jsonGetStopsUseWeight = jsonGetStopsUseWeight; this.jsonGetStopsUseWeight = jsonGetStopsUseWeight;
return this; return this;
@ -449,29 +444,28 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
@Override @Override
public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException { public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException {
final StringBuilder uri = new StringBuilder(getStopEndpoint); final HttpUrl.Builder url = getStopEndpoint.newBuilder().addPathSegment(apiLanguage);
appendJsonGetStopsParameters(uri, checkNotNull(constraint), 0); appendJsonGetStopsParameters(url, checkNotNull(constraint), 0);
return jsonGetStops(url.build());
return jsonGetStops(uri.toString());
} }
protected void appendJsonGetStopsParameters(final StringBuilder uri, final CharSequence constraint, protected void appendJsonGetStopsParameters(final HttpUrl.Builder url, final CharSequence constraint,
final int maxStops) { final int maxStops) {
uri.append("?getstop=1"); url.addQueryParameter("getstop", "1");
uri.append("&REQ0JourneyStopsS0A=255"); url.addQueryParameter("REQ0JourneyStopsS0A", "255");
uri.append("&REQ0JourneyStopsS0G=").append(ParserUtils.urlEncode(constraint.toString(), jsonGetStopsEncoding)) url.addEncodedQueryParameter("REQ0JourneyStopsS0G",
.append("?"); ParserUtils.urlEncode(constraint.toString() + "?", requestUrlEncoding));
if (maxStops > 0) if (maxStops > 0)
uri.append("&REQ0JourneyStopsB=").append(maxStops); url.addQueryParameter("REQ0JourneyStopsB", Integer.toString(maxStops));
uri.append("&js=true"); url.addQueryParameter("js", "true");
} }
private static final Pattern P_AJAX_GET_STOPS_JSON = Pattern private static final Pattern P_AJAX_GET_STOPS_JSON = Pattern
.compile("SLs\\.sls\\s*=\\s*(.*?);\\s*SLs\\.showSuggestion\\(\\);", Pattern.DOTALL); .compile("SLs\\.sls\\s*=\\s*(.*?);\\s*SLs\\.showSuggestion\\(\\);", Pattern.DOTALL);
private static final Pattern P_AJAX_GET_STOPS_ID = Pattern.compile(".*?@L=0*(\\d+)@.*?"); private static final Pattern P_AJAX_GET_STOPS_ID = Pattern.compile(".*?@L=0*(\\d+)@.*?");
protected final SuggestLocationsResult jsonGetStops(final String uri) throws IOException { protected final SuggestLocationsResult jsonGetStops(final HttpUrl url) throws IOException {
final CharSequence page = httpClient.get(HttpUrl.parse(uri)); final CharSequence page = httpClient.get(url);
final Matcher mJson = P_AJAX_GET_STOPS_JSON.matcher(page); final Matcher mJson = P_AJAX_GET_STOPS_JSON.matcher(page);
if (mJson.matches()) { if (mJson.matches()) {
@ -521,7 +515,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
location = null; location = null;
// don't know what to do // don't know what to do
} else { } else {
throw new IllegalStateException("unknown type " + type + " on " + uri); throw new IllegalStateException("unknown type " + type + " on " + url);
} }
if (location != null) { if (location != null) {
@ -533,10 +527,10 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
return new SuggestLocationsResult(new ResultHeader(network, SERVER_PRODUCT), locations); return new SuggestLocationsResult(new ResultHeader(network, SERVER_PRODUCT), locations);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new RuntimeException("cannot parse: '" + json + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + json + "' on " + url, x);
} }
} else { } else {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri); throw new RuntimeException("cannot parse: '" + page + "' on " + url);
} }
} }
@ -545,30 +539,30 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
final int maxDepartures, final boolean equivs) throws IOException { final int maxDepartures, final boolean equivs) throws IOException {
checkNotNull(Strings.emptyToNull(stationId)); checkNotNull(Strings.emptyToNull(stationId));
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
appendXmlStationBoardParameters(uri, time, stationId, maxDepartures, equivs, "vs_java3"); appendXmlStationBoardParameters(url, time, stationId, maxDepartures, equivs, "vs_java3");
return xmlStationBoard(url.build(), stationId);
return xmlStationBoard(uri.toString(), stationId);
} }
protected void appendXmlStationBoardParameters(final StringBuilder uri, final @Nullable Date time, protected void appendXmlStationBoardParameters(final HttpUrl.Builder url, final @Nullable Date time,
final String stationId, final int maxDepartures, final boolean equivs, final @Nullable String styleSheet) { final String stationId, final int maxDepartures, final boolean equivs, final @Nullable String styleSheet) {
uri.append("?productsFilter=").append(allProductsString()); url.addQueryParameter("productsFilter", allProductsString().toString());
uri.append("&boardType=dep"); url.addQueryParameter("boardType", "dep");
if (stationBoardCanDoEquivs) if (stationBoardCanDoEquivs)
uri.append("&disableEquivs=").append(equivs ? "0" : "1"); url.addQueryParameter("disableEquivs", equivs ? "0" : "1");
uri.append("&maxJourneys=").append(maxDepartures > 0 ? maxDepartures : DEFAULT_MAX_DEPARTURES); url.addQueryParameter("maxJourneys",
uri.append("&input=").append(normalizeStationId(stationId)); Integer.toString(maxDepartures > 0 ? maxDepartures : DEFAULT_MAX_DEPARTURES));
appendDateTimeParameters(uri, time, "date", "time"); url.addEncodedQueryParameter("input", ParserUtils.urlEncode(normalizeStationId(stationId), requestUrlEncoding));
appendDateTimeParameters(url, time, "date", "time");
if (clientType != null) if (clientType != null)
uri.append("&clientType=").append(ParserUtils.urlEncode(clientType)); url.addEncodedQueryParameter("clientType", ParserUtils.urlEncode(clientType, requestUrlEncoding));
if (styleSheet != null) if (styleSheet != null)
uri.append("&L=").append(styleSheet); url.addQueryParameter("L", styleSheet);
uri.append("&hcount=0"); // prevents showing old departures url.addQueryParameter("hcount", "0"); // prevents showing old departures
uri.append("&start=yes"); url.addQueryParameter("start", "yes");
} }
protected void appendDateTimeParameters(final StringBuilder uri, final Date time, final String dateParamName, protected void appendDateTimeParameters(final HttpUrl.Builder url, final Date time, final String dateParamName,
final String timeParamName) { final String timeParamName) {
final Calendar c = new GregorianCalendar(timeZone); final Calendar c = new GregorianCalendar(timeZone);
c.setTime(time); c.setTime(time);
@ -577,16 +571,19 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
final int day = c.get(Calendar.DAY_OF_MONTH); final int day = c.get(Calendar.DAY_OF_MONTH);
final int hour = c.get(Calendar.HOUR_OF_DAY); final int hour = c.get(Calendar.HOUR_OF_DAY);
final int minute = c.get(Calendar.MINUTE); final int minute = c.get(Calendar.MINUTE);
uri.append('&').append(dateParamName).append('='); url.addEncodedQueryParameter(dateParamName,
uri.append(ParserUtils.urlEncode(useIso8601 ? String.format(Locale.ENGLISH, "%04d-%02d-%02d", year, month, day) ParserUtils.urlEncode(
: String.format(Locale.ENGLISH, "%02d.%02d.%02d", day, month, year - 2000))); useIso8601 ? String.format(Locale.ENGLISH, "%04d-%02d-%02d", year, month, day)
uri.append('&').append(timeParamName).append('='); : String.format(Locale.ENGLISH, "%02d.%02d.%02d", day, month, year - 2000),
uri.append(ParserUtils.urlEncode(String.format(Locale.ENGLISH, "%02d:%02d", hour, minute))); requestUrlEncoding));
url.addEncodedQueryParameter(timeParamName,
ParserUtils.urlEncode(String.format(Locale.ENGLISH, "%02d:%02d", hour, minute), requestUrlEncoding));
} }
private static final Pattern P_XML_STATION_BOARD_DELAY = Pattern.compile("(?:-|k\\.A\\.?|cancel|([+-]?\\s*\\d+))"); private static final Pattern P_XML_STATION_BOARD_DELAY = Pattern.compile("(?:-|k\\.A\\.?|cancel|([+-]?\\s*\\d+))");
protected final QueryDeparturesResult xmlStationBoard(final String uri, final String stationId) throws IOException { protected final QueryDeparturesResult xmlStationBoard(final HttpUrl url, final String stationId)
throws IOException {
final String normalizedStationId = normalizeStationId(stationId); final String normalizedStationId = normalizeStationId(stationId);
final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>(); final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>();
@ -819,7 +816,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
throw new ParserException("cannot parse xml: " + firstChars, x); throw new ParserException("cannot parse xml: " + firstChars, x);
} }
} }
}, HttpUrl.parse(uri)); }, url);
return result.get(); return result.get();
} }
@ -845,8 +842,8 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
+ "\"getPOIs\":" + getPOIs + "}", // + "\"getPOIs\":" + getPOIs + "}", //
false); false);
final String uri = checkNotNull(mgateEndpoint); final HttpUrl url = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(HttpUrl.parse(uri), request, "application/json"); final CharSequence page = httpClient.get(url, request, "application/json");
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -886,7 +883,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
return new NearbyLocationsResult(header, locations); return new NearbyLocationsResult(header, locations);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x); throw new ParserException("cannot parse json: '" + page + "' on " + url, x);
} }
} }
@ -910,8 +907,8 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
+ getPasslist + "}", + getPasslist + "}",
false); false);
final String uri = checkNotNull(mgateEndpoint); final HttpUrl url = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(HttpUrl.parse(uri), request, "application/json"); final CharSequence page = httpClient.get(url, request, "application/json");
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -1001,7 +998,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
return result; return result;
} catch (final JSONException x) { } catch (final JSONException x) {
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x); throw new ParserException("cannot parse json: '" + page + "' on " + url, x);
} }
} }
@ -1011,8 +1008,8 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
+ ",\"meta\":false},\"maxLoc\":" + DEFAULT_MAX_LOCATIONS + "}}", + ",\"meta\":false},\"maxLoc\":" + DEFAULT_MAX_LOCATIONS + "}}",
true); true);
final String uri = checkNotNull(mgateEndpoint); final HttpUrl url = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(HttpUrl.parse(uri), request, "application/json"); final CharSequence page = httpClient.get(url, request, "application/json");
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -1044,7 +1041,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
return new SuggestLocationsResult(header, suggestedLocations); return new SuggestLocationsResult(header, suggestedLocations);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x); throw new ParserException("cannot parse json: '" + page + "' on " + url, x);
} }
} }
@ -1103,8 +1100,8 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
+ "\"getPolyline\":false,\"getPasslist\":true,\"getIST\":false,\"getEco\":false,\"extChgTime\":-1}", // + "\"getPolyline\":false,\"getPasslist\":true,\"getIST\":false,\"getEco\":false,\"extChgTime\":-1}", //
false); false);
final String uri = checkNotNull(mgateEndpoint); final HttpUrl url = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(HttpUrl.parse(uri), request, "application/json"); final CharSequence page = httpClient.get(url, request, "application/json");
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -1233,7 +1230,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
res.optString("outCtxScrB")); res.optString("outCtxScrB"));
return new QueryTripsResult(header, null, from, null, to, context, trips); return new QueryTripsResult(header, null, from, null, to, context, trips);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x); throw new ParserException("cannot parse json: '" + page + "' on " + url, x);
} }
} }
@ -1524,7 +1521,8 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
final CharSequence conReq, final Location from, final @Nullable Location via, final Location to) final CharSequence conReq, final Location from, final @Nullable Location via, final Location to)
throws IOException { throws IOException {
final String request = wrapReqC(conReq, null); final String request = wrapReqC(conReq, null);
final String endpoint = extXmlEndpoint != null ? extXmlEndpoint : queryEndpoint; final HttpUrl endpoint = extXmlEndpoint != null ? extXmlEndpoint
: queryEndpoint.newBuilder().addPathSegment(apiLanguage).build();
final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>(); final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>();
httpClient.getInputStream(new HttpClient.Callback() { httpClient.getInputStream(new HttpClient.Callback() {
@Override @Override
@ -1917,7 +1915,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
throw new ParserException("cannot parse xml: " + bodyPeek, x); throw new ParserException("cannot parse xml: " + bodyPeek, x);
} }
} }
}, HttpUrl.parse(endpoint), request, "application/xml", null); }, endpoint, request, "application/xml", null);
return result.get(); return result.get();
} }
@ -2028,65 +2026,61 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
throw new IllegalArgumentException(location.type.toString()); throw new IllegalArgumentException(location.type.toString());
} }
protected void appendQueryTripsBinaryParameters(final StringBuilder uri, final Location from, protected void appendQueryTripsBinaryParameters(final HttpUrl.Builder url, final Location from,
final @Nullable Location via, final Location to, final Date date, final boolean dep, final @Nullable Location via, final Location to, final Date date, final boolean dep,
final @Nullable Set<Product> products, final @Nullable Accessibility accessibility, final @Nullable Set<Product> products, final @Nullable Accessibility accessibility,
final @Nullable Set<Option> options) { final @Nullable Set<Option> options) {
uri.append("?start=Suchen"); url.addQueryParameter("start", "Suchen");
url.addEncodedQueryParameter("REQ0JourneyStopsS0ID",
uri.append("&REQ0JourneyStopsS0ID=").append(ParserUtils.urlEncode(locationId(from), Charsets.ISO_8859_1)); ParserUtils.urlEncode(locationId(from), requestUrlEncoding));
uri.append("&REQ0JourneyStopsZ0ID=").append(ParserUtils.urlEncode(locationId(to), Charsets.ISO_8859_1)); url.addEncodedQueryParameter("REQ0JourneyStopsZ0ID", ParserUtils.urlEncode(locationId(to), requestUrlEncoding));
if (via != null) { if (via != null) {
// workaround, for there does not seem to be a REQ0JourneyStops1.0ID parameter // workaround, for there does not seem to be a REQ0JourneyStops1.0ID parameter
url.addQueryParameter("REQ0JourneyStops1.0A", Integer.toString(locationType(via)));
uri.append("&REQ0JourneyStops1.0A=").append(locationType(via));
if (via.type == LocationType.STATION && via.hasId()) { if (via.type == LocationType.STATION && via.hasId()) {
uri.append("&REQ0JourneyStops1.0L=").append(via.id); url.addQueryParameter("REQ0JourneyStops1.0L", via.id);
} else if (via.hasLocation()) { } else if (via.hasLocation()) {
uri.append("&REQ0JourneyStops1.0X=").append(via.lon); url.addQueryParameter("REQ0JourneyStops1.0X", Integer.toString(via.lon));
uri.append("&REQ0JourneyStops1.0Y=").append(via.lat); url.addQueryParameter("REQ0JourneyStops1.0Y", Integer.toString(via.lat));
if (via.name == null) if (via.name == null)
uri.append("&REQ0JourneyStops1.0O=") url.addQueryParameter("REQ0JourneyStops1.0O",
.append(ParserUtils.urlEncode( String.format(Locale.ENGLISH, "%.6f, %.6f", via.lat / 1E6, via.lon / 1E6));
String.format(Locale.ENGLISH, "%.6f, %.6f", via.lat / 1E6, via.lon / 1E6),
Charsets.ISO_8859_1));
} else if (via.name != null) { } else if (via.name != null) {
uri.append("&REQ0JourneyStops1.0G=").append(ParserUtils.urlEncode(via.name, Charsets.ISO_8859_1)); url.addEncodedQueryParameter("REQ0JourneyStops1.0G", ParserUtils
if (via.type != LocationType.ANY) .urlEncode(via.name + (via.type != LocationType.ANY ? "!" : ""), requestUrlEncoding));
uri.append('!');
} }
} }
uri.append("&REQ0HafasSearchForw=").append(dep ? "1" : "0"); url.addQueryParameter("REQ0HafasSearchForw", dep ? "1" : "0");
appendDateTimeParameters(uri, date, "REQ0JourneyDate", "REQ0JourneyTime"); appendDateTimeParameters(url, date, "REQ0JourneyDate", "REQ0JourneyTime");
final CharSequence productsStr; final CharSequence productsStr;
if (products != null) if (products != null)
productsStr = productsString(products); productsStr = productsString(products);
else else
productsStr = allProductsString(); productsStr = allProductsString();
uri.append("&REQ0JourneyProduct_prod_list_1=").append(productsStr); url.addQueryParameter("REQ0JourneyProduct_prod_list_1", productsStr.toString());
if (accessibility != null && accessibility != Accessibility.NEUTRAL) { if (accessibility != null && accessibility != Accessibility.NEUTRAL) {
if (accessibility == Accessibility.LIMITED) if (accessibility == Accessibility.LIMITED)
uri.append("&REQ0AddParamBaimprofile=1"); url.addQueryParameter("REQ0AddParamBaimprofile", "1");
else if (accessibility == Accessibility.BARRIER_FREE) else if (accessibility == Accessibility.BARRIER_FREE)
uri.append("&REQ0AddParamBaimprofile=0"); url.addQueryParameter("REQ0AddParamBaimprofile", "0");
} }
if (options != null && options.contains(Option.BIKE)) if (options != null && options.contains(Option.BIKE))
uri.append("&REQ0JourneyProduct_opt3=1"); url.addQueryParameter("REQ0JourneyProduct_opt3", "1");
appendCommonQueryTripsBinaryParameters(uri); appendCommonQueryTripsBinaryParameters(url);
} }
protected void appendCommonQueryTripsBinaryParameters(final StringBuilder uri) { protected void appendCommonQueryTripsBinaryParameters(final HttpUrl.Builder url) {
uri.append("&h2g-direct=11"); url.addQueryParameter("h2g-direct", "11");
if (clientType != null) if (clientType != null)
uri.append("&clientType=").append(ParserUtils.urlEncode(clientType)); url.addEncodedQueryParameter("clientType", ParserUtils.urlEncode(clientType, requestUrlEncoding));
} }
private final static int QUERY_TRIPS_BINARY_BUFFER_SIZE = 384 * 1024; private final static int QUERY_TRIPS_BINARY_BUFFER_SIZE = 384 * 1024;
@ -2124,32 +2118,28 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
to = locations.get(0); to = locations.get(0);
} }
final StringBuilder uri = new StringBuilder(queryEndpoint); final HttpUrl.Builder url = queryEndpoint.newBuilder().addPathSegment(apiLanguage);
appendQueryTripsBinaryParameters(uri, from, via, to, date, dep, products, accessibility, options); appendQueryTripsBinaryParameters(url, from, via, to, date, dep, products, accessibility, options);
return queryTripsBinary(url.build(), from, via, to, QUERY_TRIPS_BINARY_BUFFER_SIZE);
return queryTripsBinary(uri.toString(), from, via, to, QUERY_TRIPS_BINARY_BUFFER_SIZE);
} }
protected void appendQueryMoreTripsBinaryParameters(final StringBuilder uri, final QueryTripsBinaryContext context, protected void appendQueryMoreTripsBinaryParameters(final HttpUrl.Builder url,
final boolean later) { final QueryTripsBinaryContext context, final boolean later) {
uri.append("?seqnr=").append(context.seqNr); url.addQueryParameter("seqnr", Integer.toString(context.seqNr));
uri.append("&ident=").append(context.ident); url.addQueryParameter("ident", context.ident);
if (context.ld != null) if (context.ld != null)
uri.append("&ld=").append(context.ld); url.addQueryParameter("ld", context.ld);
uri.append("&REQ0HafasScrollDir=").append(later ? 1 : 2); url.addQueryParameter("REQ0HafasScrollDir", later ? "1" : "2");
appendCommonQueryTripsBinaryParameters(url);
appendCommonQueryTripsBinaryParameters(uri);
} }
protected QueryTripsResult queryMoreTripsBinary(final QueryTripsContext contextObj, final boolean later) protected QueryTripsResult queryMoreTripsBinary(final QueryTripsContext contextObj, final boolean later)
throws IOException { throws IOException {
final QueryTripsBinaryContext context = (QueryTripsBinaryContext) contextObj; final QueryTripsBinaryContext context = (QueryTripsBinaryContext) contextObj;
final StringBuilder uri = new StringBuilder(queryEndpoint); final HttpUrl.Builder url = queryEndpoint.newBuilder().addPathSegment(apiLanguage);
appendQueryMoreTripsBinaryParameters(uri, context, later); appendQueryMoreTripsBinaryParameters(url, context, later);
return queryTripsBinary(url.build(), null, null, null, QUERY_TRIPS_BINARY_BUFFER_SIZE + context.usedBufferSize);
return queryTripsBinary(uri.toString(), null, null, null,
QUERY_TRIPS_BINARY_BUFFER_SIZE + context.usedBufferSize);
} }
private class CustomBufferedInputStream extends BufferedInputStream { private class CustomBufferedInputStream extends BufferedInputStream {
@ -2162,7 +2152,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
} }
} }
private QueryTripsResult queryTripsBinary(final String uri, final Location from, final @Nullable Location via, private QueryTripsResult queryTripsBinary(final HttpUrl url, final Location from, final @Nullable Location via,
final Location to, final int expectedBufferSize) throws IOException { final Location to, final int expectedBufferSize) throws IOException {
/* /*
* Many thanks to Malte Starostik and Robert, who helped a lot with analyzing this API! * Many thanks to Malte Starostik and Robert, who helped a lot with analyzing this API!
@ -2228,7 +2218,8 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
final int numTrips = is.readShortReverse(); final int numTrips = is.readShortReverse();
if (numTrips == 0) { if (numTrips == 0) {
result.set(new QueryTripsResult(header, uri, from, via, to, null, new LinkedList<Trip>())); result.set(new QueryTripsResult(header, url.toString(), from, via, to, null,
new LinkedList<Trip>()));
return; return;
} }
@ -2655,7 +2646,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
final boolean canQueryMore = trips.size() != 1 || trips.get(0).legs.size() != 1 final boolean canQueryMore = trips.size() != 1 || trips.get(0).legs.size() != 1
|| !(trips.get(0).legs.get(0) instanceof Trip.Individual); || !(trips.get(0).legs.get(0) instanceof Trip.Individual);
result.set(new QueryTripsResult(header, uri, from, via, to, result.set(new QueryTripsResult(header, url.toString(), from, via, to,
new QueryTripsBinaryContext(requestId, seqNr, ld, bis.getCount(), canQueryMore), trips)); new QueryTripsBinaryContext(requestId, seqNr, ld, bis.getCount(), canQueryMore), trips));
} else { } else {
log.debug("Hafas error: {}", errorCode); log.debug("Hafas error: {}", errorCode);
@ -2749,11 +2740,11 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
result.set(new QueryTripsResult(header, QueryTripsResult.Status.SERVICE_DOWN)); result.set(new QueryTripsResult(header, QueryTripsResult.Status.SERVICE_DOWN));
return; return;
} else { } else {
throw new IllegalStateException("error " + errorCode + " on " + uri); throw new IllegalStateException("error " + errorCode + " on " + url);
} }
} }
} }
}, HttpUrl.parse(uri)); }, url);
return result.get(); return result.get();
} }
@ -2958,34 +2949,34 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
protected final NearbyLocationsResult nearbyLocationsByCoordinate(final EnumSet<LocationType> types, final int lat, protected final NearbyLocationsResult nearbyLocationsByCoordinate(final EnumSet<LocationType> types, final int lat,
final int lon, final int maxDistance, final int maxLocations) throws IOException { final int lon, final int maxDistance, final int maxLocations) throws IOException {
if (types.contains(LocationType.STATION)) { if (types.contains(LocationType.STATION)) {
final StringBuilder uri = new StringBuilder(queryEndpoint); final HttpUrl.Builder url = queryEndpoint.newBuilder().addPathSegment(apiLanguage + "y");
appendJsonNearbyStationsParameters(uri, lat, lon, maxDistance, maxLocations); appendJsonNearbyStationsParameters(url, lat, lon, maxDistance, maxLocations);
return jsonNearbyLocations(url.build());
return jsonNearbyLocations(uri.toString());
} else if (types.contains(LocationType.POI)) { } else if (types.contains(LocationType.POI)) {
final StringBuilder uri = new StringBuilder(queryEndpoint); final HttpUrl.Builder url = queryEndpoint.newBuilder().addPathSegment(apiLanguage + "y");
appendJsonNearbyPOIsParameters(uri, lat, lon, maxDistance, maxLocations); appendJsonNearbyPOIsParameters(url, lat, lon, maxDistance, maxLocations);
return jsonNearbyLocations(url.build());
return jsonNearbyLocations(uri.toString());
} else { } else {
return new NearbyLocationsResult(null, Collections.<Location> emptyList()); return new NearbyLocationsResult(null, Collections.<Location> emptyList());
} }
} }
protected NearbyLocationsResult nearbyStationsById(final String id, final int maxDistance) throws IOException { protected NearbyLocationsResult nearbyStationsById(final String id, final int maxDistance) throws IOException {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
appendXmlNearbyStationsParameters(uri, id); appendXmlNearbyStationsParameters(url, id);
return xmlNearbyStations(url.build());
return xmlNearbyStations(uri.toString());
} }
protected final void appendXmlNearbyStationsParameters(final StringBuilder uri, final String stationId) { protected final void appendXmlNearbyStationsParameters(final HttpUrl.Builder url, final String stationId) {
uri.append("?productsFilter=").append(allProductsString()); url.addQueryParameter("productsFilter", allProductsString().toString());
uri.append("&boardType=dep"); url.addQueryParameter("boardType", "dep");
uri.append("&input=").append(normalizeStationId(stationId)); url.addEncodedQueryParameter("input", ParserUtils.urlEncode(normalizeStationId(stationId), requestUrlEncoding));
uri.append("&sTI=1&start=yes&hcount=0&L=vs_java3"); url.addQueryParameter("sTI", "1");
url.addQueryParameter("start", "yes");
url.addQueryParameter("hcount", "0");
url.addQueryParameter("L", "vs_java3");
if (clientType != null) if (clientType != null)
uri.append("&clientType=").append(ParserUtils.urlEncode(clientType)); url.addEncodedQueryParameter("clientType", ParserUtils.urlEncode(clientType, requestUrlEncoding));
} }
private static final Pattern P_XML_NEARBY_STATIONS_COARSE = Pattern.compile("\\G<\\s*St\\s*(.*?)/?>(?:\n|\\z)", private static final Pattern P_XML_NEARBY_STATIONS_COARSE = Pattern.compile("\\G<\\s*St\\s*(.*?)/?>(?:\n|\\z)",
@ -3000,9 +2991,9 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
private static final Pattern P_XML_NEARBY_STATIONS_MESSAGES = Pattern private static final Pattern P_XML_NEARBY_STATIONS_MESSAGES = Pattern
.compile("<Err code=\"([^\"]*)\" text=\"([^\"]*)\""); .compile("<Err code=\"([^\"]*)\" text=\"([^\"]*)\"");
protected final NearbyLocationsResult xmlNearbyStations(final String uri) throws IOException { protected final NearbyLocationsResult xmlNearbyStations(final HttpUrl url) throws IOException {
// scrape page // scrape page
final CharSequence page = httpClient.get(HttpUrl.parse(uri)); final CharSequence page = httpClient.get(url);
final List<Location> stations = new ArrayList<Location>(); final List<Location> stations = new ArrayList<Location>();
@ -3041,44 +3032,44 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
stations.add(new Location(LocationType.STATION, parsedId, parsedLat, parsedLon, placeAndName[0], stations.add(new Location(LocationType.STATION, parsedId, parsedLat, parsedLon, placeAndName[0],
placeAndName[1])); placeAndName[1]));
} else { } else {
throw new IllegalArgumentException("cannot parse '" + mCoarse.group(1) + "' on " + uri); throw new IllegalArgumentException("cannot parse '" + mCoarse.group(1) + "' on " + url);
} }
} }
return new NearbyLocationsResult(null, stations); return new NearbyLocationsResult(null, stations);
} }
protected void appendJsonNearbyStationsParameters(final StringBuilder uri, final int lat, final int lon, protected void appendJsonNearbyStationsParameters(final HttpUrl.Builder url, final int lat, final int lon,
final int maxDistance, final int maxStations) { final int maxDistance, final int maxStations) {
uri.append('y'); url.addQueryParameter("performLocating", "2");
uri.append("?performLocating=2&tpl=stop2json"); url.addQueryParameter("tpl", "stop2json");
uri.append("&look_stopclass=").append(allProductsInt()); url.addQueryParameter("look_stopclass", Integer.toString(allProductsInt()));
uri.append("&look_nv=get_stopweight|yes"); url.addQueryParameter("look_nv", "get_stopweight|yes");
// get_shortjson|yes // get_shortjson|yes
// get_lines|yes // get_lines|yes
// combinemode|2 // combinemode|2
// density|80 // density|80
// get_stopweight|yes // get_stopweight|yes
// get_infotext|yes // get_infotext|yes
uri.append("&look_x=").append(lon); url.addQueryParameter("look_x", Integer.toString(lon));
uri.append("&look_y=").append(lat); url.addQueryParameter("look_y", Integer.toString(lat));
uri.append("&look_maxno=").append(maxStations != 0 ? maxStations : 200); url.addQueryParameter("look_maxno", Integer.toString(maxStations != 0 ? maxStations : 200));
uri.append("&look_maxdist=").append(maxDistance != 0 ? maxDistance : 5000); url.addQueryParameter("look_maxdist", Integer.toString(maxDistance != 0 ? maxDistance : 5000));
} }
protected void appendJsonNearbyPOIsParameters(final StringBuilder uri, final int lat, final int lon, protected void appendJsonNearbyPOIsParameters(final HttpUrl.Builder url, final int lat, final int lon,
final int maxDistance, final int maxStations) { final int maxDistance, final int maxStations) {
uri.append('y'); url.addQueryParameter("performLocating", "4");
uri.append("?performLocating=4&tpl=poi2json"); url.addQueryParameter("tpl", "poi2json");
uri.append("&look_pois="); // all categories url.addQueryParameter("look_pois", ""); // all categories
uri.append("&look_x=").append(lon); url.addQueryParameter("look_x", Integer.toString(lon));
uri.append("&look_y=").append(lat); url.addQueryParameter("look_y", Integer.toString(lat));
uri.append("&look_maxno=").append(maxStations != 0 ? maxStations : 200); url.addQueryParameter("look_maxno", Integer.toString(maxStations != 0 ? maxStations : 200));
uri.append("&look_maxdist=").append(maxDistance != 0 ? maxDistance : 5000); url.addQueryParameter("look_maxdist", Integer.toString(maxDistance != 0 ? maxDistance : 5000));
} }
protected final NearbyLocationsResult jsonNearbyLocations(final String uri) throws IOException { protected final NearbyLocationsResult jsonNearbyLocations(final HttpUrl url) throws IOException {
final CharSequence page = httpClient.get(HttpUrl.parse(uri)); final CharSequence page = httpClient.get(url);
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -3138,7 +3129,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
} }
} catch (final JSONException x) { } catch (final JSONException x) {
x.printStackTrace(); x.printStackTrace();
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + page + "' on " + url, x);
} }
} }
@ -3157,10 +3148,10 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
private static final Pattern P_HTML_NEARBY_STATIONS_MESSAGES = Pattern private static final Pattern P_HTML_NEARBY_STATIONS_MESSAGES = Pattern
.compile("(Ihre Eingabe kann nicht interpretiert werden)"); .compile("(Ihre Eingabe kann nicht interpretiert werden)");
protected final NearbyLocationsResult htmlNearbyStations(final String uri) throws IOException { protected final NearbyLocationsResult htmlNearbyStations(final HttpUrl url) throws IOException {
final List<Location> stations = new ArrayList<Location>(); final List<Location> stations = new ArrayList<Location>();
final CharSequence page = httpClient.get(HttpUrl.parse(uri)); final CharSequence page = httpClient.get(url);
String oldZebra = null; String oldZebra = null;
final Matcher mCoarse = htmlNearbyStationsPattern.matcher(page); final Matcher mCoarse = htmlNearbyStationsPattern.matcher(page);
@ -3197,7 +3188,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider {
stations.add(new Location(LocationType.STATION, parsedId, parsedLat, parsedLon, placeAndName[0], stations.add(new Location(LocationType.STATION, parsedId, parsedLat, parsedLon, placeAndName[0],
placeAndName[1])); placeAndName[1]));
} else { } else {
throw new IllegalArgumentException("cannot parse '" + mCoarse.group(2) + "' on " + uri); throw new IllegalArgumentException("cannot parse '" + mCoarse.group(2) + "' on " + url);
} }
} }

View file

@ -64,7 +64,6 @@ import de.schildbach.pte.dto.Trip.Leg;
import de.schildbach.pte.dto.Trip.Public; import de.schildbach.pte.dto.Trip.Public;
import de.schildbach.pte.exception.NotFoundException; import de.schildbach.pte.exception.NotFoundException;
import de.schildbach.pte.exception.ParserException; import de.schildbach.pte.exception.ParserException;
import de.schildbach.pte.util.ParserUtils;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
@ -77,7 +76,8 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
protected final static String SERVER_PRODUCT = "navitia"; protected final static String SERVER_PRODUCT = "navitia";
protected final static String SERVER_VERSION = "v1"; protected final static String SERVER_VERSION = "v1";
protected String apiBase = "https://api.navitia.io/" + SERVER_VERSION + "/"; protected HttpUrl apiBase = HttpUrl.parse("https://api.navitia.io/").newBuilder().addPathSegment(SERVER_VERSION)
.build();
private enum PlaceType { private enum PlaceType {
ADDRESS, ADMINISTRATIVE_REGION, POI, STOP_POINT, STOP_AREA ADDRESS, ADMINISTRATIVE_REGION, POI, STOP_POINT, STOP_AREA
@ -125,7 +125,7 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
} }
} }
public AbstractNavitiaProvider(final NetworkId network, final String apiBase, final String authorization) { public AbstractNavitiaProvider(final NetworkId network, final HttpUrl apiBase, final String authorization) {
this(network, authorization); this(network, authorization);
this.apiBase = apiBase; this.apiBase = apiBase;
@ -163,12 +163,8 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
} }
} }
private String uri() { private HttpUrl.Builder url() {
return apiBase + "coverage/" + region() + "/"; return apiBase.newBuilder().addPathSegment("coverage").addPathSegment(region());
}
private String tripUri() {
return apiBase;
} }
private Point parseCoord(final JSONObject coord) throws IOException { private Point parseCoord(final JSONObject coord) throws IOException {
@ -644,8 +640,9 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
} }
private String getStopAreaId(final String stopPointId) throws IOException { private String getStopAreaId(final String stopPointId) throws IOException {
final String uri = uri() + "stop_points/" + ParserUtils.urlEncode(stopPointId) + "?depth=1"; final HttpUrl.Builder url = url().addPathSegment("stop_points").addPathSegment(stopPointId);
final CharSequence page = httpClient.get(HttpUrl.parse(uri)); url.addQueryParameter("depth", "1");
final CharSequence page = httpClient.get(url.build());
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -672,37 +669,33 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
int maxDistance, final int maxLocations) throws IOException { int maxDistance, final int maxLocations) throws IOException {
final ResultHeader resultHeader = new ResultHeader(network, SERVER_PRODUCT, SERVER_VERSION, null, 0, null); final ResultHeader resultHeader = new ResultHeader(network, SERVER_PRODUCT, SERVER_VERSION, null, 0, null);
// Build query uri depending of location type. // Build url depending of location type.
final StringBuilder queryUri = new StringBuilder(uri()); final HttpUrl.Builder url = url();
if (location.type == LocationType.COORD || location.type == LocationType.ADDRESS if (location.type == LocationType.COORD || location.type == LocationType.ADDRESS
|| location.type == LocationType.ANY) { || location.type == LocationType.ANY) {
if (!location.hasLocation()) if (!location.hasLocation())
throw new IllegalArgumentException(); throw new IllegalArgumentException();
final double lon = location.lon / 1E6; final double lon = location.lon / 1E6;
final double lat = location.lat / 1E6; final double lat = location.lat / 1E6;
queryUri.append("coords/").append(lon).append(';').append(lat); url.addPathSegment("coords").addPathSegment(lon + ";" + lat);
} else if (location.type == LocationType.STATION) { } else if (location.type == LocationType.STATION) {
if (!location.isIdentified()) if (!location.isIdentified())
throw new IllegalArgumentException(); throw new IllegalArgumentException();
queryUri.append("stop_points/").append(location.id); url.addPathSegment("stop_points").addPathSegment(location.id);
} else if (location.type == LocationType.POI) { } else if (location.type == LocationType.POI) {
if (!location.isIdentified()) if (!location.isIdentified())
throw new IllegalArgumentException(); throw new IllegalArgumentException();
queryUri.append("pois/").append(location.id); url.addPathSegment("pois").addPathSegment(location.id);
} else { } else {
throw new IllegalArgumentException("Unhandled location type: " + location.type); throw new IllegalArgumentException("Unhandled location type: " + location.type);
} }
queryUri.append('/'); url.addPathSegment("places_nearby");
url.addQueryParameter("type[]", "stop_point");
if (maxDistance == 0) url.addQueryParameter("distance", Integer.toString(maxDistance == 0 ? 50000 : maxDistance));
maxDistance = 50000;
queryUri.append("places_nearby?type[]=stop_point");
queryUri.append("&distance=").append(maxDistance);
if (maxLocations > 0) if (maxLocations > 0)
queryUri.append("&count=").append(maxLocations); url.addQueryParameter("count", Integer.toString(maxLocations));
queryUri.append("&depth=3"); url.addQueryParameter("depth", "3");
final CharSequence page = httpClient.get(HttpUrl.parse(queryUri.toString())); final CharSequence page = httpClient.get(url.build());
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -747,37 +740,28 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
final QueryDeparturesResult result = new QueryDeparturesResult(resultHeader, final QueryDeparturesResult result = new QueryDeparturesResult(resultHeader,
QueryDeparturesResult.Status.OK); QueryDeparturesResult.Status.OK);
final String dateTime = printDate(time);
// If equivs is equal to true, get stop_area corresponding // If equivs is equal to true, get stop_area corresponding
// to stop_point and query departures. // to stop_point and query departures.
final StringBuilder queryUri = new StringBuilder(); final HttpUrl.Builder url = url();
queryUri.append(uri());
final String header = stationId.substring(0, stationId.indexOf(":")); final String header = stationId.substring(0, stationId.indexOf(":"));
if (equivs && header.equals("stop_point")) { if (equivs && header.equals("stop_point")) {
final String stopAreaId = getStopAreaId(stationId); final String stopAreaId = getStopAreaId(stationId);
queryUri.append("stop_areas/"); url.addPathSegment("stop_areas");
queryUri.append(stopAreaId); url.addPathSegment(stopAreaId);
queryUri.append("/"); } else if (header.equals("stop_area")) {
url.addPathSegment("stop_areas");
url.addPathSegment(stationId);
} else { } else {
if (header.equals("stop_area")) { url.addPathSegment("stop_points");
queryUri.append("stop_areas/"); url.addPathSegment(stationId);
queryUri.append(stationId);
queryUri.append("/");
} else {
queryUri.append("stop_points/");
queryUri.append(stationId);
queryUri.append("/");
}
} }
queryUri.append("departures?from_datetime="); url.addPathSegment("departures");
queryUri.append(dateTime); url.addQueryParameter("from_datetime", printDate(time));
queryUri.append("&count="); url.addQueryParameter("count", Integer.toString(maxDepartures));
queryUri.append(maxDepartures); url.addQueryParameter("duration", "86400");
queryUri.append("&duration=86400"); url.addQueryParameter("depth", "0");
queryUri.append("&depth=0");
final CharSequence page = httpClient.get(HttpUrl.parse(queryUri.toString())); final CharSequence page = httpClient.get(url.build());
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -843,9 +827,14 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException { public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException {
final String nameCstr = constraint.toString(); final String nameCstr = constraint.toString();
final String queryUri = uri() + "places?q=" + ParserUtils.urlEncode(nameCstr) final HttpUrl.Builder url = url().addPathSegment("places");
+ "&type[]=stop_area&type[]=address&type[]=poi&type[]=administrative_region" + "&depth=1"; url.addQueryParameter("q", nameCstr);
final CharSequence page = httpClient.get(HttpUrl.parse(queryUri)); url.addQueryParameter("type[]", "stop_area");
url.addQueryParameter("type[]", "address");
url.addQueryParameter("type[]", "poi");
url.addQueryParameter("type[]", "administrative_region");
url.addQueryParameter("depth", "1");
final CharSequence page = httpClient.get(url.build());
try { try {
final List<SuggestedLocation> locations = new ArrayList<SuggestedLocation>(); final List<SuggestedLocation> locations = new ArrayList<SuggestedLocation>();
@ -881,13 +870,13 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
try { try {
if (from != null && from.isIdentified() && to != null && to.isIdentified()) { if (from != null && from.isIdentified() && to != null && to.isIdentified()) {
final StringBuilder queryUri = new StringBuilder(tripUri()).append("journeys"); final HttpUrl.Builder url = apiBase.newBuilder().addPathSegment("journeys");
queryUri.append("?from=").append(ParserUtils.urlEncode(printLocation(from))); url.addQueryParameter("from", printLocation(from));
queryUri.append("&to=").append(ParserUtils.urlEncode(printLocation(to))); url.addQueryParameter("to", printLocation(to));
queryUri.append("&datetime=").append(printDate(date)); url.addQueryParameter("datetime", printDate(date));
queryUri.append("&datetime_represents=").append(dep ? "departure" : "arrival"); url.addQueryParameter("datetime_represents", dep ? "departure" : "arrival");
queryUri.append("&count=").append(this.numTripsRequested); url.addQueryParameter("count", Integer.toString(this.numTripsRequested));
queryUri.append("&depth=0"); url.addQueryParameter("depth", "0");
// Set walking speed. // Set walking speed.
if (walkSpeed != null) { if (walkSpeed != null) {
@ -905,51 +894,51 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
break; break;
} }
queryUri.append("&walking_speed=").append(walkingSpeed); url.addQueryParameter("walking_speed", Double.toString(walkingSpeed));
} }
if (options != null && options.contains(Option.BIKE)) { if (options != null && options.contains(Option.BIKE)) {
queryUri.append("&first_section_mode=bike"); url.addQueryParameter("first_section_mode", "bike");
queryUri.append("&last_section_mode=bike"); url.addQueryParameter("last_section_mode", "bike");
} }
// Set forbidden physical modes. // Set forbidden physical modes.
if (products != null && !products.equals(Product.ALL)) { if (products != null && !products.equals(Product.ALL)) {
queryUri.append("&forbidden_uris[]=physical_mode:Air"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Air");
queryUri.append("&forbidden_uris[]=physical_mode:Boat"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Boat");
if (!products.contains(Product.REGIONAL_TRAIN)) { if (!products.contains(Product.REGIONAL_TRAIN)) {
queryUri.append("&forbidden_uris[]=physical_mode:Localdistancetrain"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Localdistancetrain");
queryUri.append("&forbidden_uris[]=physical_mode:Train"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Train");
} }
if (!products.contains(Product.SUBURBAN_TRAIN)) { if (!products.contains(Product.SUBURBAN_TRAIN)) {
queryUri.append("&forbidden_uris[]=physical_mode:Localtrain"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Localtrain");
queryUri.append("&forbidden_uris[]=physical_mode:Train"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Train");
queryUri.append("&forbidden_uris[]=physical_mode:Rapidtransit"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Rapidtransit");
} }
if (!products.contains(Product.SUBWAY)) { if (!products.contains(Product.SUBWAY)) {
queryUri.append("&forbidden_uris[]=physical_mode:Metro"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Metro");
} }
if (!products.contains(Product.TRAM)) { if (!products.contains(Product.TRAM)) {
queryUri.append("&forbidden_uris[]=physical_mode:Tramway"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Tramway");
} }
if (!products.contains(Product.BUS)) { if (!products.contains(Product.BUS)) {
queryUri.append("&forbidden_uris[]=physical_mode:Bus"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Bus");
queryUri.append("&forbidden_uris[]=physical_mode:Busrapidtransit"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Busrapidtransit");
queryUri.append("&forbidden_uris[]=physical_mode:Coach"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Coach");
queryUri.append("&forbidden_uris[]=physical_mode:Shuttle"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Shuttle");
} }
if (!products.contains(Product.FERRY)) { if (!products.contains(Product.FERRY)) {
queryUri.append("&forbidden_uris[]=physical_mode:Ferry"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Ferry");
} }
if (!products.contains(Product.CABLECAR)) { if (!products.contains(Product.CABLECAR)) {
queryUri.append("&forbidden_uris[]=physical_mode:Funicular"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Funicular");
} }
if (!products.contains(Product.ON_DEMAND)) { if (!products.contains(Product.ON_DEMAND)) {
queryUri.append("&forbidden_uris[]=physical_mode:Taxi"); url.addQueryParameter("forbidden_uris[]", "physical_mode:Taxi");
} }
} }
final CharSequence page = httpClient.get(HttpUrl.parse(queryUri.toString())); final CharSequence page = httpClient.get(url.build());
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -964,21 +953,22 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
throw new IllegalArgumentException("Unhandled error id: " + id); throw new IllegalArgumentException("Unhandled error id: " + id);
} else { } else {
// Fill context. // Fill context.
String prevQueryUri = null; HttpUrl prevQueryUrl = null;
String nextQueryUri = null; HttpUrl nextQueryUrl = null;
final JSONArray links = head.getJSONArray("links"); final JSONArray links = head.getJSONArray("links");
for (int i = 0; i < links.length(); ++i) { for (int i = 0; i < links.length(); ++i) {
final JSONObject link = links.getJSONObject(i); final JSONObject link = links.getJSONObject(i);
final String type = link.getString("type"); final String type = link.getString("type");
if (type.equals("prev")) { if (type.equals("prev")) {
prevQueryUri = link.getString("href"); prevQueryUrl = HttpUrl.parse(link.getString("href"));
} else if (type.equals("next")) { } else if (type.equals("next")) {
nextQueryUri = link.getString("href"); nextQueryUrl = HttpUrl.parse(link.getString("href"));
} }
} }
final QueryTripsResult result = new QueryTripsResult(resultHeader, queryUri.toString(), from, final QueryTripsResult result = new QueryTripsResult(resultHeader, url.build().toString(), from,
null, to, new Context(from, to, prevQueryUri, nextQueryUri), new LinkedList<Trip>()); null, to, new Context(from, to, prevQueryUrl.toString(), nextQueryUrl.toString()),
new LinkedList<Trip>());
parseQueryTripsResult(head, from, to, result); parseQueryTripsResult(head, from, to, result);
@ -1051,8 +1041,8 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
final Context context = (Context) contextObj; final Context context = (Context) contextObj;
final Location from = context.from; final Location from = context.from;
final Location to = context.to; final Location to = context.to;
final String queryUri = later ? context.nextQueryUri : context.prevQueryUri; final HttpUrl queryUrl = HttpUrl.parse(later ? context.nextQueryUri : context.prevQueryUri);
final CharSequence page = httpClient.get(HttpUrl.parse(queryUri)); final CharSequence page = httpClient.get(queryUrl);
try { try {
if (from.isIdentified() && to.isIdentified()) { if (from.isIdentified() && to.isIdentified()) {
@ -1061,12 +1051,13 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
// Fill context. // Fill context.
final JSONArray links = head.getJSONArray("links"); final JSONArray links = head.getJSONArray("links");
final JSONObject prev = links.getJSONObject(0); final JSONObject prev = links.getJSONObject(0);
final String prevQueryUri = prev.getString("href"); final HttpUrl prevQueryUrl = HttpUrl.parse(prev.getString("href"));
final JSONObject next = links.getJSONObject(1); final JSONObject next = links.getJSONObject(1);
final String nextQueryUri = next.getString("href"); final HttpUrl nextQueryUrl = HttpUrl.parse(next.getString("href"));
final QueryTripsResult result = new QueryTripsResult(resultHeader, queryUri, from, null, to, final QueryTripsResult result = new QueryTripsResult(resultHeader, queryUrl.toString(), from, null, to,
new Context(from, to, prevQueryUri, nextQueryUri), new LinkedList<Trip>()); new Context(from, to, prevQueryUrl.toString(), nextQueryUrl.toString()),
new LinkedList<Trip>());
parseQueryTripsResult(head, from, to, result); parseQueryTripsResult(head, from, to, result);
@ -1081,8 +1072,8 @@ public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider {
@Override @Override
public Point[] getArea() throws IOException { public Point[] getArea() throws IOException {
final String queryUri = uri(); final HttpUrl.Builder url = url();
final CharSequence page = httpClient.get(HttpUrl.parse(queryUri)); final CharSequence page = httpClient.get(url.build());
try { try {
// Get shape string. // Get shape string.

View file

@ -19,6 +19,7 @@ package de.schildbach.pte;
import java.io.IOException; import java.io.IOException;
import java.net.Proxy; import java.net.Proxy;
import java.nio.charset.Charset;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -28,6 +29,7 @@ import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.google.common.base.Charsets;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import de.schildbach.pte.dto.Point; import de.schildbach.pte.dto.Point;
@ -43,6 +45,7 @@ public abstract class AbstractNetworkProvider implements NetworkProvider {
protected final NetworkId network; protected final NetworkId network;
protected final HttpClient httpClient = new HttpClient(); protected final HttpClient httpClient = new HttpClient();
protected Charset requestUrlEncoding = Charsets.ISO_8859_1;
protected TimeZone timeZone = TimeZone.getTimeZone("CET"); protected TimeZone timeZone = TimeZone.getTimeZone("CET");
protected int numTripsRequested = 6; protected int numTripsRequested = 6;
private @Nullable Map<String, Style> styles = null; private @Nullable Map<String, Style> styles = null;
@ -85,6 +88,11 @@ public abstract class AbstractNetworkProvider implements NetworkProvider {
return this; return this;
} }
protected AbstractNetworkProvider setRequestUrlEncoding(final Charset requestUrlEncoding) {
this.requestUrlEncoding = requestUrlEncoding;
return this;
}
protected AbstractNetworkProvider setTimeZone(final String timeZoneId) { protected AbstractNetworkProvider setTimeZone(final String timeZoneId) {
this.timeZone = TimeZone.getTimeZone(timeZoneId); this.timeZone = TimeZone.getTimeZone(timeZoneId);
return this; return this;

View file

@ -40,8 +40,6 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import com.google.common.base.Charsets;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType; import de.schildbach.pte.dto.LocationType;
@ -57,7 +55,6 @@ import de.schildbach.pte.dto.SuggestLocationsResult;
import de.schildbach.pte.dto.SuggestedLocation; import de.schildbach.pte.dto.SuggestedLocation;
import de.schildbach.pte.dto.Trip; import de.schildbach.pte.dto.Trip;
import de.schildbach.pte.exception.ParserException; import de.schildbach.pte.exception.ParserException;
import de.schildbach.pte.util.ParserUtils;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
@ -122,8 +119,8 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
} }
} }
private static final String DEFAULT_STOPFINDER_ENDPOINT = "/Transport/v2/"; private static final String DEFAULT_STOPFINDER_ENDPOINT = "Transport/v2";
private static final String DEFAULT_TRIP_ENDPOINT = "/journeyplanner/v2/"; private static final String DEFAULT_TRIP_ENDPOINT = "journeyplanner/v2";
private static final String SERVER_PRODUCT = "tsi"; private static final String SERVER_PRODUCT = "tsi";
private static Map<String, Product> TRANSPORT_MODES = new HashMap<String, Product>(); private static Map<String, Product> TRANSPORT_MODES = new HashMap<String, Product>();
@ -160,15 +157,15 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
} }
private final @Nullable String apiKey; private final @Nullable String apiKey;
private final String stopFinderEndpoint; private final HttpUrl stopFinderEndpoint;
private final String tripEndpoint; private final HttpUrl tripEndpoint;
public AbstractTsiProvider(final NetworkId network, final String apiKey, final String apiBase) { public AbstractTsiProvider(final NetworkId network, final String apiKey, final HttpUrl apiBase) {
this(network, apiKey, apiBase, null, null); this(network, apiKey, apiBase, null, null);
} }
public AbstractTsiProvider(final NetworkId network, final String apiKey, final String tripEndpoint, public AbstractTsiProvider(final NetworkId network, final String apiKey, final HttpUrl tripEndpoint,
final String stopFinderEndpoint) { final HttpUrl stopFinderEndpoint) {
super(network); super(network);
this.apiKey = apiKey; this.apiKey = apiKey;
@ -176,10 +173,14 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
this.stopFinderEndpoint = stopFinderEndpoint; this.stopFinderEndpoint = stopFinderEndpoint;
} }
public AbstractTsiProvider(final NetworkId network, final String apiKey, final String apiBase, public AbstractTsiProvider(final NetworkId network, final String apiKey, final HttpUrl apiBase,
final String tripEndpoint, final String stopFinderEndpoint) { final String tripEndpoint, final String stopFinderEndpoint) {
this(network, apiKey, apiBase + (tripEndpoint != null ? tripEndpoint : DEFAULT_TRIP_ENDPOINT), // this(network, apiKey,
apiBase + (stopFinderEndpoint != null ? stopFinderEndpoint : DEFAULT_STOPFINDER_ENDPOINT)); apiBase.newBuilder().addPathSegments(tripEndpoint != null ? tripEndpoint : DEFAULT_TRIP_ENDPOINT)
.build(),
apiBase.newBuilder()
.addPathSegments(stopFinderEndpoint != null ? stopFinderEndpoint : DEFAULT_STOPFINDER_ENDPOINT)
.build());
} }
@Override @Override
@ -192,14 +193,12 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
@Override @Override
public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException { public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException {
final StringBuilder parameters = buildCommonRequestParams("SearchTripPoint", "json"); final HttpUrl.Builder url = stopFinderEndpoint.newBuilder().addPathSegments("SearchTripPoint/json");
parameters.append("&MaxItems=").append(50); // XXX good value? appendCommonRequestParams(url);
parameters.append("&Keywords=").append(ParserUtils.urlEncode(constraint.toString(), Charsets.UTF_8)); url.addQueryParameter("MaxItems", "50"); // XXX good value?
url.addQueryParameter("Keywords", constraint.toString());
final StringBuilder uri = new StringBuilder(stopFinderEndpoint); final CharSequence page = httpClient.get(url.build());
uri.append(parameters);
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString()));
try { try {
final List<SuggestedLocation> locations = new ArrayList<SuggestedLocation>(); final List<SuggestedLocation> locations = new ArrayList<SuggestedLocation>();
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -227,11 +226,8 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
} }
} }
private final StringBuilder buildCommonRequestParams(final String method, final String outputFormat) { private final void appendCommonRequestParams(final HttpUrl.Builder url) {
final StringBuilder uri = new StringBuilder(method); url.addQueryParameter("Key", apiKey);
uri.append('/').append(outputFormat);
uri.append("?Key=").append(apiKey);
return uri;
} }
private Line createLine(final String id, final String mode, final String number, final String name, private Line createLine(final String id, final String mode, final String number, final String name,
@ -269,17 +265,15 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
private NearbyLocationsResult jsonCoordRequest(final int lat, final int lon, final int maxDistance, private NearbyLocationsResult jsonCoordRequest(final int lat, final int lon, final int maxDistance,
final int maxStations) throws IOException { final int maxStations) throws IOException {
final StringBuilder parameters = buildCommonRequestParams("SearchTripPoint", "json"); final HttpUrl.Builder url = stopFinderEndpoint.newBuilder().addPathSegments("SearchTripPoint/json");
parameters.append(String.format(Locale.FRENCH, "&Latitude=%2.6f&Longitude=%2.6f", latLonToDouble(lat), appendCommonRequestParams(url);
latLonToDouble(lon))); url.addQueryParameter("Latitude", String.format(Locale.FRENCH, "%2.6f", latLonToDouble(lat)));
parameters.append("&MaxItems=").append(maxStations != 0 ? maxStations : 50); url.addQueryParameter("Longitude", String.format(Locale.FRENCH, "%2.6f", latLonToDouble(lon)));
parameters.append("&Perimeter=").append(maxDistance != 0 ? maxDistance : 1320); url.addQueryParameter("MaxItems", Integer.toString(maxStations != 0 ? maxStations : 50));
parameters.append("&PointTypes=Stop_Place"); url.addQueryParameter("Perimeter", Integer.toString(maxDistance != 0 ? maxDistance : 1320));
url.addQueryParameter("PointTypes", "Stop_Place");
final StringBuilder uri = new StringBuilder(stopFinderEndpoint); final CharSequence page = httpClient.get(url.build());
uri.append(parameters);
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString()));
try { try {
final List<Location> stations = new ArrayList<Location>(); final List<Location> stations = new ArrayList<Location>();
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -310,14 +304,12 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
} }
private Location jsonStationRequestCoord(final String id) throws IOException { private Location jsonStationRequestCoord(final String id) throws IOException {
final StringBuilder parameters = buildCommonRequestParams("GetTripPoint", "json"); final HttpUrl.Builder url = stopFinderEndpoint.newBuilder().addPathSegments("GetTripPoint/json");
parameters.append("&TripPointId=").append(id); appendCommonRequestParams(url);
parameters.append("&PointType=Stop_Place"); url.addQueryParameter("TripPointId", id);
url.addQueryParameter("PointType", "Stop_Place");
final StringBuilder uri = new StringBuilder(stopFinderEndpoint); final CharSequence page = httpClient.get(url.build());
uri.append(parameters);
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString()));
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -621,62 +613,59 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
mode = null; mode = null;
} }
final String walkSpeedStr = translateWalkSpeed(context.walkSpeed); final HttpUrl.Builder url = tripEndpoint.newBuilder().addPathSegments("PlanTrip/json");
appendCommonRequestParams(url);
final StringBuilder parameters = buildCommonRequestParams("PlanTrip", "json"); url.addQueryParameter("Disruptions", "0"); // XXX what does this even mean?
parameters.append("&Disruptions=").append(0); // XXX what does this even mean? url.addQueryParameter("Algorithm", "FASTEST");
parameters.append("&Algorithm=FASTEST"); url.addQueryParameter("MaxWalkDist", "1000"); // XXX good value? (in meters)
parameters.append("&MaxWalkDist=1000"); // XXX good value? (in meters)
if (context.from.type == LocationType.STATION) { if (context.from.type == LocationType.STATION) {
parameters.append("&DepType=STOP_PLACE&DepId=").append(context.from.id); url.addQueryParameter("DepType", "STOP_PLACE");
parameters.append("%240"); // "$0" url.addQueryParameter("DepId", context.from.id + "$0");
} else if (context.from.type == LocationType.POI) { } else if (context.from.type == LocationType.POI) {
parameters.append("&DepType=POI&DepId=").append(context.from.id); url.addQueryParameter("DepType", "POI");
parameters.append("%240"); // "$0" url.addQueryParameter("DepId", context.from.id + "$0");
} else { } else {
parameters.append("&DepLat=").append(latLonToDouble(context.from.lat)); url.addQueryParameter("DepLat", Double.toString(latLonToDouble(context.from.lat)));
parameters.append("&DepLon=").append(latLonToDouble(context.from.lon)); url.addQueryParameter("DepLon", Double.toString(latLonToDouble(context.from.lon)));
} }
if (context.to.type == LocationType.STATION) { if (context.to.type == LocationType.STATION) {
parameters.append("&ArrType=STOP_PLACE&ArrId=").append(context.to.id); url.addQueryParameter("ArrType", "STOP_PLACE");
parameters.append("%240"); // "$0" url.addQueryParameter("ArrId", context.to.id + "$0");
} else if (context.to.type == LocationType.POI) { } else if (context.to.type == LocationType.POI) {
parameters.append("&ArrType=POI&ArrId=").append(context.to.id); url.addQueryParameter("ArrType", "POI");
parameters.append("%240"); // "$0" url.addQueryParameter("ArrId", context.to.id + "$0");
} else { } else {
parameters.append("&ArrLat=").append(latLonToDouble(context.to.lat)); url.addQueryParameter("ArrLat", Double.toString(latLonToDouble(context.to.lat)));
parameters.append("&ArrLon=").append(latLonToDouble(context.to.lon)); url.addQueryParameter("ArrLon", Double.toString(latLonToDouble(context.to.lon)));
} }
if (context.via != null) { if (context.via != null) {
if (context.via.type == LocationType.STATION) { if (context.via.type == LocationType.STATION) {
parameters.append("&ViaType=STOP_PLACE&ViaId=").append(context.via.id); url.addQueryParameter("ViaType", "STOP_PLACE");
parameters.append("%240"); // "$0" url.addQueryParameter("ViaId", context.via.id + "$0");
} else if (context.via.type == LocationType.POI) { } else if (context.via.type == LocationType.POI) {
parameters.append("&ViaType=POI&ViaId=").append(context.via.id); url.addQueryParameter("ViaType", "POI");
parameters.append("%240"); // "$0" url.addQueryParameter("ViaId", context.via.id + "$0");
} else { } else {
parameters.append("&ViaLat=").append(latLonToDouble(context.via.lat)); url.addQueryParameter("ViaLat", Double.toString(latLonToDouble(context.via.lat)));
parameters.append("&ViaLon=").append(latLonToDouble(context.via.lon)); url.addQueryParameter("ViaLon", Double.toString(latLonToDouble(context.via.lon)));
} }
} }
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US); final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
parameters.append("&date=").append(dateFormat.format(date)); url.addQueryParameter("date", dateFormat.format(date));
final DateFormat timeFormat = new SimpleDateFormat("HH-mm", Locale.US); final DateFormat timeFormat = new SimpleDateFormat("HH-mm", Locale.US);
parameters.append(dep ? "&DepartureTime=" : "&ArrivalTime=").append(timeFormat.format(date)); url.addQueryParameter(dep ? "DepartureTime" : "ArrivalTime", timeFormat.format(date));
parameters.append("&WalkSpeed=").append(walkSpeedStr); url.addQueryParameter("WalkSpeed", translateWalkSpeed(context.walkSpeed));
if (mode != null) if (mode != null)
parameters.append("&Modes=").append(ParserUtils.urlEncode(mode.toString(), Charsets.UTF_8)); url.addQueryParameter("Modes", mode);
final StringBuilder uri = new StringBuilder(tripEndpoint); final CharSequence page = httpClient.get(url.build());
uri.append(parameters);
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString()));
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -711,7 +700,8 @@ public abstract class AbstractTsiProvider extends AbstractNetworkProvider {
context.updateLatestDeparture(trips.get(trips.size() - 1).getFirstDepartureTime()); context.updateLatestDeparture(trips.get(trips.size() - 1).getFirstDepartureTime());
} }
return new QueryTripsResult(header, uri.toString(), context.from, context.via, context.to, context, trips); return new QueryTripsResult(header, url.build().toString(), context.from, context.via, context.to, context,
trips);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new ParserException(x); throw new ParserException(x);
} }

View file

@ -17,12 +17,13 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class AtcProvider extends AbstractEfaProvider { public class AtcProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://82.187.83.50/TravelPlanner/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://82.187.83.50/TravelPlanner/");
// http://cisium.webhop.net/TravelPlanner/ // http://cisium.webhop.net/TravelPlanner/
public AtcProvider() { public AtcProvider() {

View file

@ -30,11 +30,13 @@ import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class AvvProvider extends AbstractEfaProvider { public class AvvProvider extends AbstractEfaProvider {
private final static String API_BASE = "https://efa.avv-augsburg.de/avv2/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://efa.avv-augsburg.de/avv2/");
public AvvProvider() { public AvvProvider() {
super(NetworkId.AVV, API_BASE); super(NetworkId.AVV, API_BASE);
@ -44,21 +46,19 @@ public class AvvProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(super.xsltTripRequestParameters(from, via, to, time, dep, products, final @Nullable Set<Option> options) {
optimize, walkSpeed, accessibility, options)); super.appendXsltTripRequestParameters(url, from, via, to, time, dep, products, optimize, walkSpeed,
accessibility, options);
uri.append("&inclMOT_11=on"); // night bus url.addEncodedQueryParameter("inclMOT_11", "on"); // night bus
uri.append("&inclMOT_13=on"); url.addEncodedQueryParameter("inclMOT_13", "on");
uri.append("&inclMOT_14=on"); url.addEncodedQueryParameter("inclMOT_14", "on");
uri.append("&inclMOT_15=on"); url.addEncodedQueryParameter("inclMOT_15", "on");
uri.append("&inclMOT_16=on"); url.addEncodedQueryParameter("inclMOT_16", "on");
uri.append("&inclMOT_17=on"); url.addEncodedQueryParameter("inclMOT_17", "on");
return uri.toString();
} }
@Override @Override

View file

@ -23,11 +23,13 @@ import java.util.regex.Pattern;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public final class BahnProvider extends AbstractHafasProvider { public final class BahnProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://reiseauskunft.bahn.de/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://reiseauskunft.bahn.de/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY,
Product.SUBWAY, Product.TRAM, Product.ON_DEMAND, null, null, null, null }; Product.SUBWAY, Product.TRAM, Product.ON_DEMAND, null, null, null, null };
@ -35,7 +37,7 @@ public final class BahnProvider extends AbstractHafasProvider {
public BahnProvider() { public BahnProvider() {
super(NetworkId.DB, API_BASE, "dn", PRODUCTS_MAP); super(NetworkId.DB, API_BASE, "dn", PRODUCTS_MAP);
setStationBoardEndpoint("https://mobile.bahn.de/bin/mobil/bhftafel.exe/dn"); setStationBoardEndpoint(HttpUrl.parse("https://mobile.bahn.de/bin/mobil/bhftafel.exe"));
setStationBoardHasStationTable(false); setStationBoardHasStationTable(false);
setJsonGetStopsUseWeight(false); setJsonGetStopsUseWeight(false);
} }

View file

@ -40,12 +40,13 @@ import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class BayernProvider extends AbstractEfaProvider { public class BayernProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://mobile.defas-fgi.de/beg/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://mobile.defas-fgi.de/beg/");
// http://mobile.defas-fgi.de/xml/ // http://mobile.defas-fgi.de/xml/
private static final String DEPARTURE_MONITOR_ENDPOINT = "XML_DM_REQUEST"; private static final String DEPARTURE_MONITOR_ENDPOINT = "XML_DM_REQUEST";
@ -135,29 +136,24 @@ public class BayernProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(super.xsltTripRequestParameters(from, via, to, time, dep, products, final @Nullable Set<Option> options) {
optimize, walkSpeed, accessibility, options)); super.appendXsltTripRequestParameters(url, from, via, to, time, dep, products, optimize, walkSpeed,
accessibility, options);
if (products != null) { if (products != null) {
for (final Product p : products) { for (final Product p : products) {
if (p == Product.HIGH_SPEED_TRAIN) if (p == Product.HIGH_SPEED_TRAIN)
uri.append("&inclMOT_15=on&inclMOT_16=on"); url.addEncodedQueryParameter("inclMOT_15", "on").addEncodedQueryParameter("inclMOT_16", "on");
if (p == Product.REGIONAL_TRAIN) if (p == Product.REGIONAL_TRAIN)
uri.append("&inclMOT_13=on"); url.addEncodedQueryParameter("inclMOT_13", "on");
} }
} }
url.addEncodedQueryParameter("inclMOT_11", "on");
uri.append("&inclMOT_11=on"); url.addEncodedQueryParameter("inclMOT_14", "on");
uri.append("&inclMOT_14=on"); url.addEncodedQueryParameter("calcOneDirection", "1");
uri.append("&calcOneDirection=1");
return uri.toString();
} }
@Override @Override

View file

@ -31,11 +31,13 @@ import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class BsvagProvider extends AbstractEfaProvider { public class BsvagProvider extends AbstractEfaProvider {
private final static String API_BASE = "https://bsvg.efa.de/bsvagstd/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://bsvg.efa.de/bsvagstd/");
public BsvagProvider() { public BsvagProvider() {
super(NetworkId.BSVAG, API_BASE); super(NetworkId.BSVAG, API_BASE);
@ -47,16 +49,14 @@ public class BsvagProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(super.xsltTripRequestParameters(from, via, to, time, dep, products, final @Nullable Set<Option> options) {
optimize, walkSpeed, accessibility, options)); super.appendXsltTripRequestParameters(url, from, via, to, time, dep, products, optimize, walkSpeed,
accessibility, options);
uri.append("&inclMOT_11=on"); url.addEncodedQueryParameter("inclMOT_11", "on");
return uri.toString();
} }
private static final Map<String, Style> STYLES = new HashMap<String, Style>(); private static final Map<String, Style> STYLES = new HashMap<String, Style>();

View file

@ -23,11 +23,13 @@ import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType; import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class BvbProvider extends AbstractEfaProvider { public class BvbProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://www.efa-bvb.ch/bvb/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://www.efa-bvb.ch/bvb/");
public BvbProvider() { public BvbProvider() {
super(NetworkId.BVB, API_BASE); super(NetworkId.BVB, API_BASE);

View file

@ -31,19 +31,21 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public final class BvgProvider extends AbstractHafasProvider { public final class BvgProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://bvg-apps.hafas.de/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://bvg-apps.hafas.de/bin/");
private static final Product[] PRODUCTS_MAP = { Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM, Product.BUS, private static final Product[] PRODUCTS_MAP = { Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM, Product.BUS,
Product.FERRY, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.ON_DEMAND }; Product.FERRY, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.ON_DEMAND };
public BvgProvider() { public BvgProvider() {
super(NetworkId.BVG, API_BASE, "dn", PRODUCTS_MAP); super(NetworkId.BVG, API_BASE, "dn", PRODUCTS_MAP);
setRequestUrlEncoding(Charsets.UTF_8);
setJsonGetStopsUseWeight(false); setJsonGetStopsUseWeight(false);
setJsonGetStopsEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setStyles(STYLES); setStyles(STYLES);
} }

View file

@ -17,11 +17,13 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class DingProvider extends AbstractEfaProvider { public class DingProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://www.ding-ulm.de/ding2/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://www.ding-ulm.de/ding2/");
// http://www.ding.eu/swu // http://www.ding.eu/swu

View file

@ -22,11 +22,13 @@ import java.util.regex.Matcher;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class DsbProvider extends AbstractHafasProvider { public class DsbProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://mobil.rejseplanen.dk/mobil-bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://mobil.rejseplanen.dk/mobil-bin/");
// http://dk.hafas.de/bin/fat/ // http://dk.hafas.de/bin/fat/
// http://mobil.rejseplanen.dk/mobil-bin/ // http://mobil.rejseplanen.dk/mobil-bin/
// http://www.dsb.dk/Rejseplan/bin/ // http://www.dsb.dk/Rejseplan/bin/

View file

@ -17,11 +17,13 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class DubProvider extends AbstractEfaProvider { public class DubProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://wojhati.rta.ae/dub/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://wojhati.rta.ae/dub/");
public DubProvider() { public DubProvider() {
super(NetworkId.DUB, API_BASE); super(NetworkId.DUB, API_BASE);

View file

@ -31,13 +31,15 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryTripsContext; import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import okhttp3.HttpUrl;
/** /**
* Ireland, Dublin * Ireland, Dublin
* *
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class EireannProvider extends AbstractHafasProvider { public class EireannProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://journeyplanner.buseireann.ie/jp/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://journeyplanner.buseireann.ie/jp/bin/");
private static final Product[] PRODUCTS_MAP = { null, null, null, Product.BUS }; private static final Product[] PRODUCTS_MAP = { null, null, null, Product.BUS };
public EireannProvider() { public EireannProvider() {

View file

@ -21,6 +21,8 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Nicolas Derive * @author Nicolas Derive
* @author Stéphane Guillou * @author Stéphane Guillou
@ -29,7 +31,7 @@ public class FranceNorthEastProvider extends AbstractNavitiaProvider {
private static String API_REGION = "fr-ne"; private static String API_REGION = "fr-ne";
// dataset available at: https://navitia.opendatasoft.com/explore/dataset/fr-ne/ // dataset available at: https://navitia.opendatasoft.com/explore/dataset/fr-ne/
public FranceNorthEastProvider(final String apiBase, final String authorization) { public FranceNorthEastProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.FRANCENORTHEAST, apiBase, authorization); super(NetworkId.FRANCENORTHEAST, apiBase, authorization);
setTimeZone("Europe/Paris"); setTimeZone("Europe/Paris");

View file

@ -17,13 +17,15 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Patrick Kanzler * @author Patrick Kanzler
*/ */
public class FranceNorthWestProvider extends AbstractNavitiaProvider { public class FranceNorthWestProvider extends AbstractNavitiaProvider {
private static String API_REGION = "fr-nw"; private static String API_REGION = "fr-nw";
public FranceNorthWestProvider(final String apiBase, final String authorization) { public FranceNorthWestProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.FRANCENORTHWEST, apiBase, authorization); super(NetworkId.FRANCENORTHWEST, apiBase, authorization);
setTimeZone("Europe/Paris"); setTimeZone("Europe/Paris");

View file

@ -21,13 +21,15 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Anthony Chaput * @author Anthony Chaput
*/ */
public class FranceSouthEastProvider extends AbstractNavitiaProvider { public class FranceSouthEastProvider extends AbstractNavitiaProvider {
private static String API_REGION = "fr-se"; private static String API_REGION = "fr-se";
public FranceSouthEastProvider(final String apiBase, final String authorization) { public FranceSouthEastProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.FRANCESOUTHEAST, apiBase, authorization); super(NetworkId.FRANCESOUTHEAST, apiBase, authorization);
setTimeZone("Europe/Paris"); setTimeZone("Europe/Paris");

View file

@ -21,13 +21,15 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Nicolas Derive * @author Nicolas Derive
*/ */
public class FranceSouthWestProvider extends AbstractNavitiaProvider { public class FranceSouthWestProvider extends AbstractNavitiaProvider {
private static String API_REGION = "fr-sw"; private static String API_REGION = "fr-sw";
public FranceSouthWestProvider(final String apiBase, final String authorization) { public FranceSouthWestProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.FRANCESOUTHWEST, apiBase, authorization); super(NetworkId.FRANCESOUTHWEST, apiBase, authorization);
setTimeZone("Europe/Paris"); setTimeZone("Europe/Paris");

View file

@ -26,12 +26,13 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class GvhProvider extends AbstractEfaProvider { public class GvhProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://bhb.efa.de/bhb/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://bhb.efa.de/bhb/");
// http://www.efa.de/efaws2/cmsembedded_gvh/ // http://www.efa.de/efaws2/cmsembedded_gvh/
// http://bhb.efa.de/bhb/ // http://bhb.efa.de/bhb/
// http://mobil.efa.de/mobile3/ // http://mobil.efa.de/mobile3/
@ -40,7 +41,7 @@ public class GvhProvider extends AbstractEfaProvider {
this(API_BASE); this(API_BASE);
} }
public GvhProvider(final String apiBase) { public GvhProvider(final HttpUrl apiBase) {
super(NetworkId.GVH, apiBase); super(NetworkId.GVH, apiBase);
setIncludeRegionId(false); setIncludeRegionId(false);

View file

@ -18,7 +18,6 @@
package de.schildbach.pte; package de.schildbach.pte;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder;
import java.text.ParsePosition; import java.text.ParsePosition;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -70,7 +69,7 @@ import okhttp3.ResponseBody;
* @author Mats Sjöberg <mats@sjoberg.fi> * @author Mats Sjöberg <mats@sjoberg.fi>
*/ */
public class HslProvider extends AbstractNetworkProvider { public class HslProvider extends AbstractNetworkProvider {
private static final String API_BASE = "http://api.reittiopas.fi/hsl/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://api.reittiopas.fi/hsl/");
private static final String SERVER_PRODUCT = "hsl"; private static final String SERVER_PRODUCT = "hsl";
private static final String SERVER_VERSION = "1_2_0"; private static final String SERVER_VERSION = "1_2_0";
private static final int EARLIER_TRIPS_MINUTE_OFFSET = 5; private static final int EARLIER_TRIPS_MINUTE_OFFSET = 5;
@ -100,16 +99,16 @@ public class HslProvider extends AbstractNetworkProvider {
return true; return true;
} }
private StringBuilder apiUri(final String request) { private HttpUrl.Builder apiUrl(final String request) {
StringBuilder uri = new StringBuilder(API_BASE); final HttpUrl.Builder url = API_BASE.newBuilder();
uri.append(SERVER_VERSION + "/"); url.addPathSegment(SERVER_VERSION);
uri.append("?user=" + user); url.addQueryParameter("user", user);
uri.append("&pass=" + pass); url.addQueryParameter("pass", pass);
uri.append("&request=").append(request); url.addQueryParameter("request", request);
uri.append("&epsg_out=wgs84"); url.addQueryParameter("epsg_out", "wgs84");
uri.append("&epsg_in=wgs84"); url.addQueryParameter("epsg_in", "wgs84");
uri.append("&format=xml"); url.addQueryParameter("format", "xml");
return uri; return url;
} }
private Point coordStrToPoint(final String coordStr) { private Point coordStrToPoint(final String coordStr) {
@ -139,9 +138,9 @@ public class HslProvider extends AbstractNetworkProvider {
} }
private Location queryStop(final String stationId) throws IOException { private Location queryStop(final String stationId) throws IOException {
final StringBuilder uri = apiUri("stop"); final HttpUrl.Builder url = apiUrl("stop");
uri.append("&code=").append(stationId); url.addQueryParameter("code", stationId);
uri.append(String.format("&dep_limit=1")); url.addQueryParameter("dep_limit", "1");
final AtomicReference<Location> result = new AtomicReference<Location>(); final AtomicReference<Location> result = new AtomicReference<Location>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -164,7 +163,7 @@ public class HslProvider extends AbstractNetworkProvider {
} }
}; };
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString())); httpClient.getInputStream(callback, url.build());
return result.get(); return result.get();
} }
@ -174,7 +173,7 @@ public class HslProvider extends AbstractNetworkProvider {
@Override @Override
public NearbyLocationsResult queryNearbyLocations(EnumSet<LocationType> types, Location location, int maxDistance, public NearbyLocationsResult queryNearbyLocations(EnumSet<LocationType> types, Location location, int maxDistance,
int maxStations) throws IOException { int maxStations) throws IOException {
final StringBuilder uri = apiUri("stops_area"); final HttpUrl.Builder url = apiUrl("stops_area");
if (!location.hasLocation()) { if (!location.hasLocation()) {
if (location.type != LocationType.STATION) if (location.type != LocationType.STATION)
throw new IllegalArgumentException("cannot handle: " + location.type); throw new IllegalArgumentException("cannot handle: " + location.type);
@ -182,9 +181,9 @@ public class HslProvider extends AbstractNetworkProvider {
throw new IllegalArgumentException("at least one of stationId or lat/lon " + "must be given"); throw new IllegalArgumentException("at least one of stationId or lat/lon " + "must be given");
location = queryStop(location.id); location = queryStop(location.id);
} }
uri.append("&center_coordinate=").append(locationToCoords(location)); url.addQueryParameter("center_coordinate", locationToCoords(location));
uri.append(String.format("&limit=%d", maxStations)); url.addQueryParameter("limit", Integer.toString(maxStations));
uri.append(String.format("&diameter=%d", maxDistance * 2)); url.addQueryParameter("diameter", Integer.toString(maxDistance * 2));
final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>(); final AtomicReference<NearbyLocationsResult> result = new AtomicReference<NearbyLocationsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -219,7 +218,7 @@ public class HslProvider extends AbstractNetworkProvider {
} }
}; };
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString())); httpClient.getInputStream(callback, url.build());
return result.get(); return result.get();
} }
@ -256,13 +255,13 @@ public class HslProvider extends AbstractNetworkProvider {
@Override @Override
public QueryDeparturesResult queryDepartures(String stationId, @Nullable Date queryDate, final int maxDepartures, public QueryDeparturesResult queryDepartures(String stationId, @Nullable Date queryDate, final int maxDepartures,
boolean equivs) throws IOException { boolean equivs) throws IOException {
final StringBuilder uri = apiUri("stop"); final HttpUrl.Builder url = apiUrl("stop");
uri.append("&code=").append(stationId); url.addQueryParameter("code", stationId);
if (queryDate != null) { if (queryDate != null) {
uri.append("&date=").append(new SimpleDateFormat("yyyyMMdd").format(queryDate)); url.addQueryParameter("date", new SimpleDateFormat("yyyyMMdd").format(queryDate));
uri.append("&time=").append(new SimpleDateFormat("HHmm").format(queryDate)); url.addQueryParameter("time", new SimpleDateFormat("HHmm").format(queryDate));
} }
uri.append(String.format("&dep_limit=%d", maxDepartures)); url.addQueryParameter("dep_limit", Integer.toString(maxDepartures));
final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>(); final AtomicReference<QueryDeparturesResult> result = new AtomicReference<QueryDeparturesResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -321,7 +320,7 @@ public class HslProvider extends AbstractNetworkProvider {
} }
}; };
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString())); httpClient.getInputStream(callback, url.build());
return result.get(); return result.get();
} }
@ -335,13 +334,13 @@ public class HslProvider extends AbstractNetworkProvider {
*/ */
@Override @Override
public SuggestLocationsResult suggestLocations(CharSequence constraint) throws IOException { public SuggestLocationsResult suggestLocations(CharSequence constraint) throws IOException {
final StringBuilder uri = apiUri("geocode"); final HttpUrl.Builder url = apiUrl("geocode");
// Since HSL is picky about the input we clean out any // Since HSL is picky about the input we clean out any
// character that isn't alphabetic, numeral, -, ', / // character that isn't alphabetic, numeral, -, ', /
// or a space. Those should be all chars needed for a // or a space. Those should be all chars needed for a
// name. // name.
String constraintStr = constraint.toString().replaceAll("[^\\p{Ll}\\p{Lu}\\p{Lt}\\p{Lo}\\p{Nd}\\d-'/ ]", ""); String constraintStr = constraint.toString().replaceAll("[^\\p{Ll}\\p{Lu}\\p{Lt}\\p{Lo}\\p{Nd}\\d-'/ ]", "");
uri.append("&key=").append(URLEncoder.encode(constraintStr, "utf-8")); url.addQueryParameter("key", constraintStr);
final AtomicReference<SuggestLocationsResult> result = new AtomicReference<SuggestLocationsResult>(); final AtomicReference<SuggestLocationsResult> result = new AtomicReference<SuggestLocationsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -396,7 +395,7 @@ public class HslProvider extends AbstractNetworkProvider {
} }
}; };
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString())); httpClient.getInputStream(callback, url.build());
return result.get(); return result.get();
} }
@ -467,18 +466,18 @@ public class HslProvider extends AbstractNetworkProvider {
to = locations.get(0); to = locations.get(0);
} }
final StringBuilder uri = apiUri("route"); final HttpUrl.Builder url = apiUrl("route");
uri.append("&from=").append(locationToCoords(from)); url.addQueryParameter("from", locationToCoords(from));
if (via != null) if (via != null)
uri.append("&via=").append(locationToCoords(via)); url.addQueryParameter("via", locationToCoords(via));
uri.append("&to=").append(locationToCoords(to)); url.addQueryParameter("to", locationToCoords(to));
uri.append("&timetype=").append(dep ? "departure" : "arrival"); url.addQueryParameter("timetype", dep ? "departure" : "arrival");
if (walkSpeed != WalkSpeed.NORMAL) if (walkSpeed != WalkSpeed.NORMAL)
uri.append(String.format("&walk_speed=%d", walkSpeed == WalkSpeed.SLOW ? 30 : 100)); url.addQueryParameter("walk_speed", Integer.toString(walkSpeed == WalkSpeed.SLOW ? 30 : 100));
uri.append("&show=5"); url.addQueryParameter("show", "5");
if (products != null && products.size() > 0) { if (products != null && products.size() > 0) {
List<String> tt = new ArrayList<String>(); List<String> tt = new ArrayList<String>();
@ -495,10 +494,10 @@ public class HslProvider extends AbstractNetworkProvider {
tt.add("ferry"); tt.add("ferry");
if (tt.size() > 0) if (tt.size() > 0)
uri.append("&transport_types=").append(Joiner.on("|").join(tt)); url.addQueryParameter("transport_types", Joiner.on("|").join(tt));
} }
QueryTripsHslContext context = new QueryTripsHslContext(uri.toString(), from, via, to, date); QueryTripsHslContext context = new QueryTripsHslContext(url.build().toString(), from, via, to, date);
return queryHslTrips(from, via, to, context, date, true); return queryHslTrips(from, via, to, context, date, true);
} }
@ -548,9 +547,9 @@ public class HslProvider extends AbstractNetworkProvider {
private QueryTripsResult queryHslTrips(final Location from, final Location via, final Location to, private QueryTripsResult queryHslTrips(final Location from, final Location via, final Location to,
final QueryTripsHslContext context, Date date, final boolean later) throws IOException { final QueryTripsHslContext context, Date date, final boolean later) throws IOException {
final StringBuilder uri = new StringBuilder(context.uri); final HttpUrl.Builder url = HttpUrl.parse(context.uri).newBuilder();
uri.append("&date=").append(new SimpleDateFormat("yyyyMMdd").format(date)); url.addQueryParameter("date", new SimpleDateFormat("yyyyMMdd").format(date));
uri.append("&time=").append(new SimpleDateFormat("HHmm").format(date)); url.addQueryParameter("time", new SimpleDateFormat("HHmm").format(date));
final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>(); final AtomicReference<QueryTripsResult> result = new AtomicReference<QueryTripsResult>();
final HttpClient.Callback callback = new HttpClient.Callback() { final HttpClient.Callback callback = new HttpClient.Callback() {
@ -697,7 +696,7 @@ public class HslProvider extends AbstractNetworkProvider {
context.prevDate = firstDate; context.prevDate = firstDate;
context.trips = trips; context.trips = trips;
result.set(new QueryTripsResult(header, uri.toString(), from, via, to, context, trips)); result.set(new QueryTripsResult(header, url.build().toString(), from, via, to, context, trips));
} catch (final XmlPullParserException x) { } catch (final XmlPullParserException x) {
throw new ParserException("cannot parse xml: " + bodyPeek, x); throw new ParserException("cannot parse xml: " + bodyPeek, x);
} }
@ -706,7 +705,7 @@ public class HslProvider extends AbstractNetworkProvider {
context.date = date; context.date = date;
httpClient.getInputStream(callback, HttpUrl.parse(uri.toString())); httpClient.getInputStream(callback, url.build());
return result.get(); return result.get();
} }
} }

View file

@ -60,7 +60,7 @@ import okhttp3.HttpUrl;
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class InvgProvider extends AbstractHafasProvider { public class InvgProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://fpa.invg.de/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://fpa.invg.de/bin/");
// http://invg.hafas.de/bin/ // http://invg.hafas.de/bin/
private static final Product[] PRODUCTS_MAP = { null, null, null, null, null, null, null, null, null, null }; private static final Product[] PRODUCTS_MAP = { null, null, null, null, null, null, null, null, null, null };
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;
@ -68,11 +68,11 @@ public class InvgProvider extends AbstractHafasProvider {
public InvgProvider() { public InvgProvider() {
super(NetworkId.INVG, API_BASE, "dn", PRODUCTS_MAP); super(NetworkId.INVG, API_BASE, "dn", PRODUCTS_MAP);
setRequestUrlEncoding(Charsets.UTF_8);
setStationBoardCanDoEquivs(false); setStationBoardCanDoEquivs(false);
setJsonGetStopsEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setStyles(STYLES); setStyles(STYLES);
setExtXmlEndpoint(API_BASE + "extxml.exe"); setExtXmlEndpoint(API_BASE.newBuilder().addPathSegment("extxml.exe").build());
} }
@Override @Override
@ -120,12 +120,11 @@ public class InvgProvider extends AbstractHafasProvider {
public NearbyLocationsResult queryNearbyLocations(final EnumSet<LocationType> types, final Location location, public NearbyLocationsResult queryNearbyLocations(final EnumSet<LocationType> types, final Location location,
final int maxDistance, final int maxStations) throws IOException { final int maxDistance, final int maxStations) throws IOException {
if (location.type == LocationType.STATION && location.hasId()) { if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?near=Anzeigen"); url.addQueryParameter("near", "Anzeigen");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
return htmlNearbyStations(url.build());
return htmlNearbyStations(uri.toString());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }
@ -171,9 +170,9 @@ public class InvgProvider extends AbstractHafasProvider {
final QueryDeparturesResult result = new QueryDeparturesResult(header); final QueryDeparturesResult result = new QueryDeparturesResult(header);
// scrape page // scrape page
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
appendXmlStationBoardParameters(uri, time, stationId, maxDepartures, false, null); appendXmlStationBoardParameters(url, time, stationId, maxDepartures, false, null);
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString())); final CharSequence page = httpClient.get(url.build());
// parse page // parse page
final Matcher mHeadCoarse = P_DEPARTURES_HEAD_COARSE.matcher(page); final Matcher mHeadCoarse = P_DEPARTURES_HEAD_COARSE.matcher(page);

View file

@ -19,13 +19,15 @@ package de.schildbach.pte;
import de.schildbach.pte.util.WordUtils; import de.schildbach.pte.util.WordUtils;
import okhttp3.HttpUrl;
/** /**
* @author Antonio El Khoury * @author Antonio El Khoury
*/ */
public class ItalyProvider extends AbstractNavitiaProvider { public class ItalyProvider extends AbstractNavitiaProvider {
private static String API_REGION = "it"; private static String API_REGION = "it";
public ItalyProvider(final String apiBase, final String authorization) { public ItalyProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.IT, apiBase, authorization); super(NetworkId.IT, apiBase, authorization);
setTimeZone("Europe/Rome"); setTimeZone("Europe/Rome");

View file

@ -17,11 +17,13 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class IvbProvider extends AbstractEfaProvider { public class IvbProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://efa.ivb.at/ivb/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://efa.ivb.at/ivb/");
public IvbProvider() { public IvbProvider() {
super(NetworkId.IVB, API_BASE); super(NetworkId.IVB, API_BASE);

View file

@ -28,17 +28,19 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class KvvProvider extends AbstractEfaProvider { public class KvvProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://213.144.24.66/kvv2/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://213.144.24.66/kvv2/");
public KvvProvider() { public KvvProvider() {
this(API_BASE); this(API_BASE);
} }
public KvvProvider(final String apiBase) { public KvvProvider(final HttpUrl apiBase) {
super(NetworkId.KVV, apiBase); super(NetworkId.KVV, apiBase);
setStyles(STYLES); setStyles(STYLES);

View file

@ -22,12 +22,13 @@ import java.util.Map;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class LinzProvider extends AbstractEfaProvider { public class LinzProvider extends AbstractEfaProvider {
public static final String API_BASE = "https://www.linzag.at/linz2/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://www.linzag.at/linz2/");
// http://www.linzag.at/static/ // http://www.linzag.at/static/
public LinzProvider() { public LinzProvider() {

View file

@ -23,11 +23,13 @@ import com.google.common.base.Charsets;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class LuProvider extends AbstractHafasProvider { public class LuProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://mobiliteitszentral.hafas.de/hafas/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://mobiliteitszentral.hafas.de/hafas/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS,
Product.BUS, Product.BUS }; Product.BUS, Product.BUS };
@ -35,7 +37,7 @@ public class LuProvider extends AbstractHafasProvider {
public LuProvider() { public LuProvider() {
super(NetworkId.LU, API_BASE, "fn", PRODUCTS_MAP); super(NetworkId.LU, API_BASE, "fn", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
} }

View file

@ -29,11 +29,13 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Position; import de.schildbach.pte.dto.Position;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class MerseyProvider extends AbstractEfaProvider { public class MerseyProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://jp.merseytravel.gov.uk/nwm/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://jp.merseytravel.gov.uk/nwm/");
public MerseyProvider() { public MerseyProvider() {
super(NetworkId.MERSEY, API_BASE); super(NetworkId.MERSEY, API_BASE);

View file

@ -28,13 +28,15 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* Has been renamed to PTV (Public Transport Vicoria). * Has been renamed to PTV (Public Transport Vicoria).
* *
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class MetProvider extends AbstractEfaProvider { public class MetProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://jp.ptv.vic.gov.au/ptv/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://jp.ptv.vic.gov.au/ptv/");
public MetProvider() { public MetProvider() {
super(NetworkId.MET, API_BASE); super(NetworkId.MET, API_BASE);

View file

@ -19,11 +19,13 @@ package de.schildbach.pte;
import de.schildbach.pte.dto.Position; import de.schildbach.pte.dto.Position;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class MvgProvider extends AbstractEfaProvider { public class MvgProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://mobil.mvg-online.de/mvgMobil/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://mobil.mvg-online.de/mvgMobil/");
public MvgProvider() { public MvgProvider() {
super(NetworkId.MVG, API_BASE); super(NetworkId.MVG, API_BASE);

View file

@ -30,17 +30,19 @@ import de.schildbach.pte.dto.Position;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class MvvProvider extends AbstractEfaProvider { public class MvvProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://efa.mvv-muenchen.de/mobile/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://efa.mvv-muenchen.de/mobile/");
public MvvProvider() { public MvvProvider() {
this(API_BASE); this(API_BASE);
} }
public MvvProvider(final String apiBase) { public MvvProvider(final HttpUrl apiBase) {
super(NetworkId.MVV, apiBase); super(NetworkId.MVV, apiBase);
setIncludeRegionId(false); setIncludeRegionId(false);

View file

@ -29,11 +29,13 @@ import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.util.StringReplaceReader; import de.schildbach.pte.util.StringReplaceReader;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class NasaProvider extends AbstractHafasProvider { public class NasaProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://reiseauskunft.insa.de/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://reiseauskunft.insa.de/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.TRAM, Product.BUS, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.TRAM, Product.BUS,
Product.ON_DEMAND }; Product.ON_DEMAND };
@ -41,7 +43,7 @@ public class NasaProvider extends AbstractHafasProvider {
public NasaProvider() { public NasaProvider() {
super(NetworkId.NASA, API_BASE, "dn", PRODUCTS_MAP); super(NetworkId.NASA, API_BASE, "dn", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setStationBoardHasLocation(true); setStationBoardHasLocation(true);
} }
@ -79,12 +81,11 @@ public class NasaProvider extends AbstractHafasProvider {
if (location.hasLocation()) { if (location.hasLocation()) {
return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations); return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations);
} else if (location.type == LocationType.STATION && location.hasId()) { } else if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?near=Anzeigen"); url.addQueryParameter("near", "Anzeigen");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
return htmlNearbyStations(url.build());
return htmlNearbyStations(uri.toString());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }

View file

@ -30,18 +30,20 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryTripsContext; import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class NriProvider extends AbstractHafasProvider { public class NriProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://hafas.websrv05.reiseinfo.no/bin/dev/nri/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://hafas.websrv05.reiseinfo.no/bin/dev/nri/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.BUS, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.BUS,
Product.TRAM, Product.SUBWAY, Product.FERRY, Product.FERRY, Product.FERRY }; Product.TRAM, Product.SUBWAY, Product.FERRY, Product.FERRY, Product.FERRY };
public NriProvider() { public NriProvider() {
super(NetworkId.NRI, API_BASE, "on", PRODUCTS_MAP); super(NetworkId.NRI, API_BASE, "on", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
} }
private static final String[] PLACES = { "Oslo", "Bergen" }; private static final String[] PLACES = { "Oslo", "Bergen" };

View file

@ -27,11 +27,13 @@ import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult; import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class NsProvider extends AbstractHafasProvider { public class NsProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://hafas.bene-system.com/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://hafas.bene-system.com/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY,
Product.SUBWAY, Product.TRAM, Product.ON_DEMAND }; Product.SUBWAY, Product.TRAM, Product.ON_DEMAND };
@ -49,13 +51,12 @@ public class NsProvider extends AbstractHafasProvider {
public NearbyLocationsResult queryNearbyLocations(final EnumSet<LocationType> types, final Location location, public NearbyLocationsResult queryNearbyLocations(final EnumSet<LocationType> types, final Location location,
final int maxDistance, final int maxLocations) throws IOException { final int maxDistance, final int maxLocations) throws IOException {
if (location.type == LocationType.STATION && location.hasId()) { if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?near=Anzeigen"); url.addQueryParameter("near", "Anzeigen");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
uri.append("&L=profi"); url.addQueryParameter("L", "profi");
return htmlNearbyStations(url.build());
return htmlNearbyStations(uri.toString());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }

View file

@ -31,18 +31,22 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryTripsContext; import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class NvbwProvider extends AbstractEfaProvider { public class NvbwProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://www.efa-bw.de/nvbw/"; // no intermeditate stops // no intermeditate stops
private final static String API_BASE_MOBILE = "http://www.efa-bw.de/android/"; private final static HttpUrl API_BASE = HttpUrl.parse("http://www.efa-bw.de/nvbw/");
private final static HttpUrl API_BASE_MOBILE = HttpUrl.parse("http://www.efa-bw.de/android/");
// http://efa2.naldo.de/naldo/ // http://efa2.naldo.de/naldo/
public NvbwProvider() { public NvbwProvider() {
super(NetworkId.NVBW, API_BASE + DEFAULT_DEPARTURE_MONITOR_ENDPOINT, API_BASE_MOBILE + DEFAULT_TRIP_ENDPOINT, super(NetworkId.NVBW, API_BASE.newBuilder().addPathSegment(DEFAULT_DEPARTURE_MONITOR_ENDPOINT).build(),
API_BASE + DEFAULT_STOPFINDER_ENDPOINT, API_BASE + DEFAULT_COORD_ENDPOINT); API_BASE_MOBILE.newBuilder().addPathSegment(DEFAULT_TRIP_ENDPOINT).build(),
API_BASE.newBuilder().addPathSegment(DEFAULT_STOPFINDER_ENDPOINT).build(),
API_BASE.newBuilder().addPathSegment(DEFAULT_COORD_ENDPOINT).build());
setIncludeRegionId(false); setIncludeRegionId(false);
setUseRouteIndexAsTripId(false); setUseRouteIndexAsTripId(false);

View file

@ -29,11 +29,13 @@ import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.util.StringReplaceReader; import de.schildbach.pte.util.StringReplaceReader;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class NvvProvider extends AbstractHafasProvider { public class NvvProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://auskunft.nvv.de/auskunft/bin/jp/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://auskunft.nvv.de/auskunft/bin/jp/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM, Product.BUS, Product.BUS, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM, Product.BUS, Product.BUS,
Product.FERRY, Product.ON_DEMAND, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN }; Product.FERRY, Product.ON_DEMAND, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN };
@ -41,7 +43,7 @@ public class NvvProvider extends AbstractHafasProvider {
public NvvProvider() { public NvvProvider() {
super(NetworkId.NVV, API_BASE, "dn", PRODUCTS_MAP); super(NetworkId.NVV, API_BASE, "dn", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
httpClient.setSslAcceptAllHostnames(true); httpClient.setSslAcceptAllHostnames(true);
} }
@ -83,12 +85,12 @@ public class NvvProvider extends AbstractHafasProvider {
if (location.hasLocation()) { if (location.hasLocation()) {
return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations); return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations);
} else if (location.type == LocationType.STATION && location.hasId()) { } else if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?L=vs_rmv&near=Anzeigen"); url.addQueryParameter("near", "Anzeigen");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
url.addQueryParameter("L", "vs_rmv");
return htmlNearbyStations(uri.toString()); return htmlNearbyStations(url.build());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }

View file

@ -27,11 +27,13 @@ import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult; import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class OebbProvider extends AbstractHafasProvider { public class OebbProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://fahrplan.oebb.at/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://fahrplan.oebb.at/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN,
Product.BUS, Product.FERRY, Product.SUBWAY, Product.TRAM, Product.HIGH_SPEED_TRAIN, Product.ON_DEMAND, Product.BUS, Product.FERRY, Product.SUBWAY, Product.TRAM, Product.HIGH_SPEED_TRAIN, Product.ON_DEMAND,
@ -49,12 +51,11 @@ public class OebbProvider extends AbstractHafasProvider {
if (location.hasLocation()) { if (location.hasLocation()) {
return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations); return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations);
} else if (location.type == LocationType.STATION && location.hasId()) { } else if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?near=Suchen"); url.addQueryParameter("near", "Suchen");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
return htmlNearbyStations(url.build());
return htmlNearbyStations(uri.toString());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }

View file

@ -17,13 +17,15 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Stephane Berube * @author Stephane Berube
*/ */
public class OntarioProvider extends AbstractNavitiaProvider { public class OntarioProvider extends AbstractNavitiaProvider {
private static String API_REGION = "ca-on"; private static String API_REGION = "ca-on";
public OntarioProvider(final String apiBase, final String authorization) { public OntarioProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.ONTARIO, apiBase, authorization); super(NetworkId.ONTARIO, apiBase, authorization);
setTimeZone("America/Toronto"); setTimeZone("America/Toronto");

View file

@ -37,11 +37,13 @@ import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class OoevvProvider extends AbstractHafasProvider { public class OoevvProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://verkehrsauskunft.ooevv.at/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://verkehrsauskunft.ooevv.at/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY,
null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY, null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY,
Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null }; Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null };

View file

@ -19,12 +19,14 @@ package de.schildbach.pte;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Kjell Braden <afflux@pentabarf.de> * @author Kjell Braden <afflux@pentabarf.de>
*/ */
public class PacaProvider extends AbstractTsiProvider { public class PacaProvider extends AbstractTsiProvider {
public PacaProvider() { public PacaProvider() {
super(NetworkId.PACA, "PACA", "http://www.pacamobilite.fr/WebServices/TransinfoService/api"); super(NetworkId.PACA, "PACA", HttpUrl.parse("http://www.pacamobilite.fr/WebServices/TransinfoService/api"));
} }
@Override @Override

View file

@ -22,13 +22,15 @@ import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import de.schildbach.pte.util.WordUtils; import de.schildbach.pte.util.WordUtils;
import okhttp3.HttpUrl;
/** /**
* @author Antonio El Khoury * @author Antonio El Khoury
*/ */
public class ParisProvider extends AbstractNavitiaProvider { public class ParisProvider extends AbstractNavitiaProvider {
private static String API_REGION = "fr-idf"; private static String API_REGION = "fr-idf";
public ParisProvider(final String apiBase, final String authorization) { public ParisProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.PARIS, apiBase, authorization); super(NetworkId.PARIS, apiBase, authorization);
setTimeZone("Europe/Paris"); setTimeZone("Europe/Paris");

View file

@ -24,18 +24,20 @@ import com.google.common.base.Charsets;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.util.StringReplaceReader; import de.schildbach.pte.util.StringReplaceReader;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class PlProvider extends AbstractHafasProvider { public class PlProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://rozklad.bilkom.pl/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://rozklad.bilkom.pl/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.BUS, Product.FERRY }; Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.BUS, Product.FERRY };
public PlProvider() { public PlProvider() {
super(NetworkId.PL, API_BASE, "pn", PRODUCTS_MAP); super(NetworkId.PL, API_BASE, "pn", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
} }

View file

@ -17,13 +17,15 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Stephane Berube * @author Stephane Berube
*/ */
public class QuebecProvider extends AbstractNavitiaProvider { public class QuebecProvider extends AbstractNavitiaProvider {
private static String API_REGION = "ca-qc"; private static String API_REGION = "ca-qc";
public QuebecProvider(final String apiBase, final String authorization) { public QuebecProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.QUEBEC, apiBase, authorization); super(NetworkId.QUEBEC, apiBase, authorization);
setTimeZone("America/Montreal"); setTimeZone("America/Montreal");

View file

@ -22,11 +22,13 @@ import java.util.regex.Pattern;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class RtProvider extends AbstractHafasProvider { public class RtProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://railteam.hafas.de/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://railteam.hafas.de/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY,
Product.SUBWAY, Product.TRAM, Product.ON_DEMAND }; Product.SUBWAY, Product.TRAM, Product.ON_DEMAND };

View file

@ -22,12 +22,14 @@ import javax.annotation.Nullable;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class RtaChicagoProvider extends AbstractEfaProvider { public class RtaChicagoProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://tripplanner.rtachicago.com/ccg3/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://tripplanner.rtachicago.com/ccg3/");
// "http://elb-jpinstances-1463028547.us-east-1.elb.amazonaws.com/ccg3/"; // http://elb-jpinstances-1463028547.us-east-1.elb.amazonaws.com/ccg3/
public RtaChicagoProvider() { public RtaChicagoProvider() {
super(NetworkId.RTACHICAGO, API_BASE); super(NetworkId.RTACHICAGO, API_BASE);

View file

@ -22,11 +22,13 @@ import java.util.regex.Matcher;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class SbbProvider extends AbstractHafasProvider { public class SbbProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://fahrplan.sbb.ch/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://fahrplan.sbb.ch/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.FERRY, Product.SUBURBAN_TRAIN, Product.BUS, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.FERRY, Product.SUBURBAN_TRAIN, Product.BUS,
Product.CABLECAR, Product.REGIONAL_TRAIN, Product.TRAM }; Product.CABLECAR, Product.REGIONAL_TRAIN, Product.TRAM };

View file

@ -26,11 +26,13 @@ import com.google.common.base.Charsets;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class SeProvider extends AbstractHafasProvider { public class SeProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://reseplanerare.resrobot.se/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://reseplanerare.resrobot.se/bin/");
// http://samtrafiken.hafas.de/bin/ // http://samtrafiken.hafas.de/bin/
// http://reseplanerare.resrobot.se/bin/ // http://reseplanerare.resrobot.se/bin/
// http://api.vasttrafik.se/bin/ // http://api.vasttrafik.se/bin/
@ -41,7 +43,7 @@ public class SeProvider extends AbstractHafasProvider {
public SeProvider() { public SeProvider() {
super(NetworkId.SE, API_BASE, "sn", PRODUCTS_MAP); super(NetworkId.SE, API_BASE, "sn", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setUseIso8601(true); setUseIso8601(true);
setStationBoardHasStationTable(false); setStationBoardHasStationTable(false);

View file

@ -57,7 +57,7 @@ import okhttp3.HttpUrl;
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class SeptaProvider extends AbstractHafasProvider { public class SeptaProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://airs1.septa.org/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://airs1.septa.org/bin/");
private static final Product[] PRODUCTS_MAP = { Product.SUBWAY, Product.TRAM, Product.BUS, Product.SUBURBAN_TRAIN }; private static final Product[] PRODUCTS_MAP = { Product.SUBWAY, Product.TRAM, Product.BUS, Product.SUBURBAN_TRAIN };
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;
@ -72,19 +72,18 @@ public class SeptaProvider extends AbstractHafasProvider {
public NearbyLocationsResult queryNearbyLocations(final EnumSet<LocationType> types, final Location location, public NearbyLocationsResult queryNearbyLocations(final EnumSet<LocationType> types, final Location location,
final int maxDistance, final int maxLocations) throws IOException { final int maxDistance, final int maxLocations) throws IOException {
if (location.type == LocationType.STATION && location.hasId()) { if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?near=Anzeigen"); url.addQueryParameter("near", "Anzeigen");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
return htmlNearbyStations(url.build());
return htmlNearbyStations(uri.toString());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }
} }
@Override @Override
protected void appendDateTimeParameters(final StringBuilder uri, final Date time, final String dateParamName, protected void appendDateTimeParameters(final HttpUrl.Builder url, final Date time, final String dateParamName,
final String timeParamName) { final String timeParamName) {
final Calendar c = new GregorianCalendar(timeZone); final Calendar c = new GregorianCalendar(timeZone);
c.setTime(time); c.setTime(time);
@ -94,10 +93,8 @@ public class SeptaProvider extends AbstractHafasProvider {
final int hour = c.get(Calendar.HOUR); final int hour = c.get(Calendar.HOUR);
final int minute = c.get(Calendar.MINUTE); final int minute = c.get(Calendar.MINUTE);
final String amPm = c.get(Calendar.AM_PM) == Calendar.AM ? "am" : "pm"; final String amPm = c.get(Calendar.AM_PM) == Calendar.AM ? "am" : "pm";
uri.append('&').append(dateParamName).append('='); url.addQueryParameter(dateParamName, String.format(Locale.ENGLISH, "%02d%02d%04d", month, day, year));
uri.append(ParserUtils.urlEncode(String.format(Locale.ENGLISH, "%02d%02d%04d", month, day, year))); url.addQueryParameter(timeParamName, String.format(Locale.ENGLISH, "%02d:%02d %s", hour, minute, amPm));
uri.append('&').append(timeParamName).append('=');
uri.append(ParserUtils.urlEncode(String.format(Locale.ENGLISH, "%02d:%02d %s", hour, minute, amPm)));
} }
private static final Pattern P_DEPARTURES_PAGE_COARSE = Pattern.compile(".*?" // private static final Pattern P_DEPARTURES_PAGE_COARSE = Pattern.compile(".*?" //
@ -134,9 +131,9 @@ public class SeptaProvider extends AbstractHafasProvider {
final QueryDeparturesResult result = new QueryDeparturesResult(header); final QueryDeparturesResult result = new QueryDeparturesResult(header);
// scrape page // scrape page
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
appendXmlStationBoardParameters(uri, time, stationId, maxDepartures, false, null); appendXmlStationBoardParameters(url, time, stationId, maxDepartures, false, null);
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString())); final CharSequence page = httpClient.get(url.build());
// parse page // parse page
final Matcher mPageCoarse = P_DEPARTURES_PAGE_COARSE.matcher(page); final Matcher mPageCoarse = P_DEPARTURES_PAGE_COARSE.matcher(page);

View file

@ -42,11 +42,13 @@ import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class ShProvider extends AbstractHafasProvider { public class ShProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://nah.sh.hafas.de/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://nah.sh.hafas.de/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.BUS, Product.FERRY,
Product.SUBWAY, Product.TRAM, Product.ON_DEMAND }; Product.SUBWAY, Product.TRAM, Product.ON_DEMAND };
@ -57,7 +59,6 @@ public class ShProvider extends AbstractHafasProvider {
setJsonApiVersion("1.10"); setJsonApiVersion("1.10");
setJsonApiClient("{\"id\":\"NAHSH\"}"); setJsonApiClient("{\"id\":\"NAHSH\"}");
setJsonApiAuthorization(jsonApiAuthorization); setJsonApiAuthorization(jsonApiAuthorization);
setJsonGetStopsEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setStyles(STYLES); setStyles(STYLES);
} }

View file

@ -29,11 +29,13 @@ import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult; import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class SncbProvider extends AbstractHafasProvider { public class SncbProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://www.belgianrail.be/jp/sncb-nmbs-routeplanner/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://www.belgianrail.be/jp/sncb-nmbs-routeplanner/");
// http://hari.b-rail.be/hafas/bin/ // http://hari.b-rail.be/hafas/bin/
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, null, Product.HIGH_SPEED_TRAIN, null, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, null, Product.HIGH_SPEED_TRAIN, null,
null, Product.BUS, Product.REGIONAL_TRAIN, null, Product.SUBWAY, Product.BUS, Product.TRAM, null, null, null, Product.BUS, Product.REGIONAL_TRAIN, null, Product.SUBWAY, Product.BUS, Product.TRAM, null, null,
@ -42,7 +44,7 @@ public class SncbProvider extends AbstractHafasProvider {
public SncbProvider() { public SncbProvider() {
super(NetworkId.SNCB, API_BASE, "nn", PRODUCTS_MAP); super(NetworkId.SNCB, API_BASE, "nn", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setStationBoardHasLocation(true); setStationBoardHasLocation(true);
} }
@ -73,12 +75,11 @@ public class SncbProvider extends AbstractHafasProvider {
if (location.hasLocation()) { if (location.hasLocation()) {
return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations); return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations);
} else if (location.type == LocationType.STATION && location.hasId()) { } else if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?near=Zoek"); url.addQueryParameter("near", "Zoek");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
return htmlNearbyStations(url.build());
return htmlNearbyStations(uri.toString());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }

View file

@ -37,11 +37,13 @@ import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class StvProvider extends AbstractEfaProvider { public class StvProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://appefa10.verbundlinie.at/android/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://appefa10.verbundlinie.at/android/");
public StvProvider() { public StvProvider() {
super(NetworkId.STV, API_BASE); super(NetworkId.STV, API_BASE);

View file

@ -40,11 +40,13 @@ import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class SvvProvider extends AbstractHafasProvider { public class SvvProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://fahrplan.salzburg-verkehr.at/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://fahrplan.salzburg-verkehr.at/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY,
null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY, null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY,
Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null }; Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null };

View file

@ -30,11 +30,13 @@ import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class SydneyProvider extends AbstractEfaProvider { public class SydneyProvider extends AbstractEfaProvider {
private final static String API_BASE = "https://tp.transportnsw.info/nsw/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://tp.transportnsw.info/nsw/");
public SydneyProvider() { public SydneyProvider() {
super(NetworkId.SYDNEY, API_BASE); super(NetworkId.SYDNEY, API_BASE);
@ -47,27 +49,24 @@ public class SydneyProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(super.xsltTripRequestParameters(from, via, to, time, dep, products, final @Nullable Set<Option> options) {
optimize, walkSpeed, accessibility, options)); super.appendXsltTripRequestParameters(url, from, via, to, time, dep, products, optimize, walkSpeed,
accessibility, options);
if (products != null) { if (products != null) {
for (final Product p : products) { for (final Product p : products) {
if (p == Product.BUS) if (p == Product.BUS)
uri.append("&inclMOT_11=on"); // school bus url.addEncodedQueryParameter("inclMOT_11", "on"); // school bus
} }
} }
url.addEncodedQueryParameter("inclMOT_13", "on");
uri.append("&inclMOT_13=on"); url.addEncodedQueryParameter("inclMOT_14", "on");
uri.append("&inclMOT_14=on"); url.addEncodedQueryParameter("inclMOT_15", "on");
uri.append("&inclMOT_15=on"); url.addEncodedQueryParameter("inclMOT_16", "on");
uri.append("&inclMOT_16=on"); url.addEncodedQueryParameter("inclMOT_17", "on");
uri.append("&inclMOT_17=on");
return uri.toString();
} }
@Override @Override

View file

@ -24,11 +24,13 @@ import javax.annotation.Nullable;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class TfiProvider extends AbstractEfaProvider { public class TfiProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://www.journeyplanner.transportforireland.ie/nta/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://www.journeyplanner.transportforireland.ie/nta/");
// http://www.journeyplanner.transportforireland.ie/ultraLite/ // http://www.journeyplanner.transportforireland.ie/ultraLite/

View file

@ -29,12 +29,13 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class TlemProvider extends AbstractEfaProvider { public class TlemProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://www.travelineeastmidlands.co.uk/em/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://www.travelineeastmidlands.co.uk/em/");
// http://www.travelineeastmidlands.co.uk/em/ // http://www.travelineeastmidlands.co.uk/em/
// http://www.travelinesw.com/swe/ // http://www.travelinesw.com/swe/
// http://www.travelinesoutheast.org.uk/se/ // http://www.travelinesoutheast.org.uk/se/

View file

@ -27,11 +27,13 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VagfrProvider extends AbstractEfaProvider { public class VagfrProvider extends AbstractEfaProvider {
private final static String API_BASE = "https://efaserver.vag-freiburg.de/vagfr/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://efaserver.vag-freiburg.de/vagfr/");
public VagfrProvider() { public VagfrProvider() {
super(NetworkId.VAGFR, API_BASE); super(NetworkId.VAGFR, API_BASE);

View file

@ -40,11 +40,13 @@ import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VaoProvider extends AbstractHafasProvider { public class VaoProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://app.verkehrsauskunft.at/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://app.verkehrsauskunft.at/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY,
null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY, null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY,
Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null }; Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null };

View file

@ -27,11 +27,13 @@ import com.google.common.base.Charsets;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VbbProvider extends AbstractHafasProvider { public class VbbProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://fahrinfo.vbb.de/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://fahrinfo.vbb.de/bin/");
private static final Product[] PRODUCTS_MAP = { Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM, Product.BUS, private static final Product[] PRODUCTS_MAP = { Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM, Product.BUS,
Product.FERRY, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN }; Product.FERRY, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN };
private static final Set<Product> ALL_EXCEPT_HIGHSPEED_AND_ONDEMAND = EnumSet private static final Set<Product> ALL_EXCEPT_HIGHSPEED_AND_ONDEMAND = EnumSet
@ -40,8 +42,8 @@ public class VbbProvider extends AbstractHafasProvider {
public VbbProvider() { public VbbProvider() {
super(NetworkId.VBB, API_BASE, "dn", PRODUCTS_MAP); super(NetworkId.VBB, API_BASE, "dn", PRODUCTS_MAP);
setRequestUrlEncoding(Charsets.UTF_8);
setJsonGetStopsUseWeight(false); setJsonGetStopsUseWeight(false);
setJsonGetStopsEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setClientType(null); setClientType(null);
} }

View file

@ -22,11 +22,13 @@ import javax.annotation.Nullable;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VblProvider extends AbstractEfaProvider { public class VblProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://mobil.vbl.ch/vblmobil/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://mobil.vbl.ch/vblmobil/");
public VblProvider() { public VblProvider() {
super(NetworkId.VBL, API_BASE); super(NetworkId.VBL, API_BASE);

View file

@ -40,11 +40,13 @@ import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VbnProvider extends AbstractHafasProvider { public class VbnProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://fahrplaner.vbn.de/hafas/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://fahrplaner.vbn.de/hafas/");
// http://fahrplaner.vsninfo.de/hafas/ // http://fahrplaner.vsninfo.de/hafas/
// http://fahrplan.rsag-online.de/hafas/ // http://fahrplan.rsag-online.de/hafas/
// http://fahrplanauskunft.verkehrsverbund-warnow.de/bin/ // http://fahrplanauskunft.verkehrsverbund-warnow.de/bin/

View file

@ -30,6 +30,8 @@ import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
@ -37,7 +39,7 @@ public class VgnProvider extends AbstractEfaProvider {
private static final String DEPARTURE_MONITOR_ENDPOINT = "XML_DM_REQUEST"; private static final String DEPARTURE_MONITOR_ENDPOINT = "XML_DM_REQUEST";
private static final String TRIP_ENDPOINT = "XML_TRIP_REQUEST2"; private static final String TRIP_ENDPOINT = "XML_TRIP_REQUEST2";
public VgnProvider(final String apiBase) { public VgnProvider(final HttpUrl apiBase) {
super(NetworkId.VGN, apiBase, DEPARTURE_MONITOR_ENDPOINT, TRIP_ENDPOINT, null, null); super(NetworkId.VGN, apiBase, DEPARTURE_MONITOR_ENDPOINT, TRIP_ENDPOINT, null, null);
} }
@ -63,11 +65,13 @@ public class VgnProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date date, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date date, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
return super.xsltTripRequestParameters(from, via, to, date, dep, products, optimize, walkSpeed, accessibility, final @Nullable Set<Option> options) {
options) + "&itdLPxx_showTariffLevel=1"; super.appendXsltTripRequestParameters(url, from, via, to, date, dep, products, optimize, walkSpeed,
accessibility, options);
url.addEncodedQueryParameter("itdLPxx_showTariffLevel", "1");
} }
} }

View file

@ -26,11 +26,14 @@ import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult; import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VgsProvider extends AbstractHafasProvider { public class VgsProvider extends AbstractHafasProvider {
private static final String API_BASE = "http://www.saarfahrplan.de/cgi-bin/"; // http://www.vgs-online.de/cgi-bin/ private static final HttpUrl API_BASE = HttpUrl.parse("http://www.saarfahrplan.de/cgi-bin/");
// http://www.vgs-online.de/cgi-bin/
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM, Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, Product.TRAM,
Product.BUS, Product.CABLECAR, Product.ON_DEMAND, Product.BUS }; Product.BUS, Product.CABLECAR, Product.ON_DEMAND, Product.BUS };
@ -74,12 +77,11 @@ public class VgsProvider extends AbstractHafasProvider {
if (location.hasLocation()) { if (location.hasLocation()) {
return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations); return nearbyLocationsByCoordinate(types, location.lat, location.lon, maxDistance, maxLocations);
} else if (location.type == LocationType.STATION && location.hasId()) { } else if (location.type == LocationType.STATION && location.hasId()) {
final StringBuilder uri = new StringBuilder(stationBoardEndpoint); final HttpUrl.Builder url = stationBoardEndpoint.newBuilder().addPathSegment(apiLanguage);
uri.append("?near=Anzeigen"); url.addQueryParameter("near", "Anzeigen");
uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50); url.addQueryParameter("distance", Integer.toString(maxDistance != 0 ? maxDistance / 1000 : 50));
uri.append("&input=").append(normalizeStationId(location.id)); url.addQueryParameter("input", normalizeStationId(location.id));
return htmlNearbyStations(url.build());
return htmlNearbyStations(uri.toString());
} else { } else {
throw new IllegalArgumentException("cannot handle: " + location); throw new IllegalArgumentException("cannot handle: " + location);
} }

View file

@ -37,11 +37,13 @@ import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VmobilProvider extends AbstractHafasProvider { public class VmobilProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://fahrplan.vmobil.at/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://fahrplan.vmobil.at/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY,
null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY, null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY,
Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null }; Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null };

View file

@ -27,11 +27,13 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VmsProvider extends AbstractEfaProvider { public class VmsProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://www.vms.de/vms2/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://www.vms.de/vms2/");
public VmsProvider() { public VmsProvider() {
super(NetworkId.VMS, API_BASE); super(NetworkId.VMS, API_BASE);
@ -40,21 +42,19 @@ public class VmsProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(super.xsltTripRequestParameters(from, via, to, time, dep, products, final @Nullable Set<Option> options) {
optimize, walkSpeed, accessibility, options)); super.appendXsltTripRequestParameters(url, from, via, to, time, dep, products, optimize, walkSpeed,
accessibility, options);
uri.append("&inclMOT_11=on"); url.addEncodedQueryParameter("inclMOT_11", "on");
uri.append("&inclMOT_13=on"); url.addEncodedQueryParameter("inclMOT_13", "on");
uri.append("&inclMOT_14=on"); url.addEncodedQueryParameter("inclMOT_14", "on");
uri.append("&inclMOT_15=on"); url.addEncodedQueryParameter("inclMOT_15", "on");
uri.append("&inclMOT_16=on"); url.addEncodedQueryParameter("inclMOT_16", "on");
uri.append("&inclMOT_17=on"); url.addEncodedQueryParameter("inclMOT_17", "Fon");
return uri.toString();
} }
@Override @Override

View file

@ -17,11 +17,13 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VmvProvider extends AbstractEfaProvider { public class VmvProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://80.146.180.107/vmv2/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://80.146.180.107/vmv2/");
// http://80.146.180.107/vmv/ // http://80.146.180.107/vmv/
// http://80.146.180.107/delfi/ // http://80.146.180.107/delfi/

View file

@ -40,11 +40,13 @@ import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VorProvider extends AbstractHafasProvider { public class VorProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://anachb.vor.at/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://anachb.vor.at/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY,
null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY, null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY,
Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null }; Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null };

View file

@ -29,11 +29,13 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VrnProvider extends AbstractEfaProvider { public class VrnProvider extends AbstractEfaProvider {
private static final String API_BASE = "https://www.vrn.de/mngvrn/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://www.vrn.de/mngvrn/");
public VrnProvider() { public VrnProvider() {
super(NetworkId.VRN, API_BASE); super(NetworkId.VRN, API_BASE);

View file

@ -33,19 +33,20 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VrrProvider extends AbstractEfaProvider { public class VrrProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://efa.vrr.de/standard/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://efa.vrr.de/standard/");
// http://app.vrr.de/companion-vrr/ // http://app.vrr.de/companion-vrr/
public VrrProvider() { public VrrProvider() {
this(API_BASE); this(API_BASE);
} }
public VrrProvider(final String apiBase) { public VrrProvider(final HttpUrl apiBase) {
super(NetworkId.VRR, apiBase); super(NetworkId.VRR, apiBase);
setIncludeRegionId(false); setIncludeRegionId(false);
@ -58,21 +59,19 @@ public class VrrProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(super.xsltTripRequestParameters(from, via, to, time, dep, products, final @Nullable Set<Option> options) {
optimize, walkSpeed, accessibility, options)); super.appendXsltTripRequestParameters(url, from, via, to, time, dep, products, optimize, walkSpeed,
accessibility, options);
if (products != null) { if (products != null) {
for (final Product p : products) { for (final Product p : products) {
if (p == Product.CABLECAR) if (p == Product.CABLECAR)
uri.append("&inclMOT_11=on"); // Schwebebahn url.addEncodedQueryParameter("inclMOT_11", "on"); // Schwebebahn
} }
} }
return uri.toString();
} }
@Override @Override

View file

@ -72,7 +72,6 @@ import de.schildbach.pte.dto.SuggestLocationsResult;
import de.schildbach.pte.dto.SuggestedLocation; import de.schildbach.pte.dto.SuggestedLocation;
import de.schildbach.pte.dto.Trip; import de.schildbach.pte.dto.Trip;
import de.schildbach.pte.dto.Trip.Leg; import de.schildbach.pte.dto.Trip.Leg;
import de.schildbach.pte.util.ParserUtils;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
@ -150,7 +149,7 @@ public class VrsProvider extends AbstractNetworkProvider {
// encrypted with // encrypted with
// client certificate) // client certificate)
// performance comparison March 2015 showed www.vrsinfo.de to be fastest for trips // performance comparison March 2015 showed www.vrsinfo.de to be fastest for trips
protected static final String API_BASE = "http://android.vrsinfo.de/index.php"; protected static final HttpUrl API_BASE = HttpUrl.parse("http://android.vrsinfo.de/index.php");
protected static final String SERVER_PRODUCT = "vrs"; protected static final String SERVER_PRODUCT = "vrs";
@SuppressWarnings("serial") @SuppressWarnings("serial")
@ -361,24 +360,24 @@ public class VrsProvider extends AbstractNetworkProvider {
public NearbyLocationsResult queryNearbyLocations(EnumSet<LocationType> types /* only STATION supported */, public NearbyLocationsResult queryNearbyLocations(EnumSet<LocationType> types /* only STATION supported */,
Location location, int maxDistance, int maxLocations) throws IOException { Location location, int maxDistance, int maxLocations) throws IOException {
// g=p means group by product; not used here // g=p means group by product; not used here
final StringBuilder uri = new StringBuilder(API_BASE); final HttpUrl.Builder url = API_BASE.newBuilder();
uri.append("?eID=tx_vrsinfo_ass2_timetable"); url.addQueryParameter("eID", "tx_vrsinfo_ass2_timetable");
if (location.hasLocation()) { if (location.hasLocation()) {
uri.append("&r=") url.addQueryParameter("r",
.append(String.format(Locale.ENGLISH, "%.6f,%.6f", location.lat / 1E6, location.lon / 1E6)); String.format(Locale.ENGLISH, "%.6f,%.6f", location.lat / 1E6, location.lon / 1E6));
} else if (location.type == LocationType.STATION && location.hasId()) { } else if (location.type == LocationType.STATION && location.hasId()) {
uri.append("&i=").append(ParserUtils.urlEncode(location.id)); url.addQueryParameter("i", location.id);
} else { } else {
throw new IllegalArgumentException("at least one of stationId or lat/lon must be given"); throw new IllegalArgumentException("at least one of stationId or lat/lon must be given");
} }
// c=1 limits the departures at each stop to 1 - actually we don't need any at this point // c=1 limits the departures at each stop to 1 - actually we don't need any at this point
uri.append("&c=1"); url.addQueryParameter("c", "1");
if (maxLocations > 0) { if (maxLocations > 0) {
// s=number of stops // s=number of stops, artificially limited by server
uri.append("&s=").append(Math.min(16, maxLocations)); // artificial server limit url.addQueryParameter("s", Integer.toString(Math.min(16, maxLocations)));
} }
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString())); final CharSequence page = httpClient.get(url.build());
try { try {
final List<Location> locations = new ArrayList<Location>(); final List<Location> locations = new ArrayList<Location>();
@ -411,9 +410,9 @@ public class VrsProvider extends AbstractNetworkProvider {
final ResultHeader header = new ResultHeader(NetworkId.VRS, SERVER_PRODUCT, null, null, serverTime, null); final ResultHeader header = new ResultHeader(NetworkId.VRS, SERVER_PRODUCT, null, null, serverTime, null);
return new NearbyLocationsResult(header, locations); return new NearbyLocationsResult(header, locations);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + page + "' on " + url, x);
} catch (final ParseException e) { } catch (final ParseException e) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, e); throw new RuntimeException("cannot parse: '" + page + "' on " + url, e);
} }
} }
@ -427,14 +426,14 @@ public class VrsProvider extends AbstractNetworkProvider {
// g=p means group by product; not used here // g=p means group by product; not used here
// d=minutes overwrites c=count and returns departures for the next d minutes // d=minutes overwrites c=count and returns departures for the next d minutes
final StringBuilder uri = new StringBuilder(API_BASE); final HttpUrl.Builder url = API_BASE.newBuilder();
uri.append("?eID=tx_vrsinfo_ass2_timetable&i=").append(ParserUtils.urlEncode(stationId)); url.addQueryParameter("eID", "tx_vrsinfo_ass2_timetable");
uri.append("&c=").append(maxDepartures); url.addQueryParameter("i", stationId);
url.addQueryParameter("c", Integer.toString(maxDepartures));
if (time != null) { if (time != null) {
uri.append("&t="); url.addQueryParameter("t", formatDate(time));
appendDate(uri, time);
} }
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString())); final CharSequence page = httpClient.get(url.build());
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -511,9 +510,9 @@ public class VrsProvider extends AbstractNetworkProvider {
return result; return result;
} catch (final JSONException x) { } catch (final JSONException x) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + page + "' on " + url, x);
} catch (final ParseException e) { } catch (final ParseException e) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, e); throw new RuntimeException("cannot parse: '" + page + "' on " + url, e);
} }
} }
@ -522,10 +521,11 @@ public class VrsProvider extends AbstractNetworkProvider {
for (LineDestination lineDestionation : lineDestinations) { for (LineDestination lineDestionation : lineDestinations) {
lineNumbersAlreadyKnown.add(lineDestionation.line.label); lineNumbersAlreadyKnown.add(lineDestionation.line.label);
} }
final StringBuilder uri = new StringBuilder(API_BASE); final HttpUrl.Builder url = API_BASE.newBuilder();
uri.append("?eID=tx_vrsinfo_his_info&i=").append(ParserUtils.urlEncode(stationId)); url.addQueryParameter("eID", "tx_vrsinfo_his_info");
url.addQueryParameter("i", stationId);
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString())); final CharSequence page = httpClient.get(url.build());
try { try {
final JSONObject head = new JSONObject(page.toString()); final JSONObject head = new JSONObject(page.toString());
@ -560,7 +560,7 @@ public class VrsProvider extends AbstractNetworkProvider {
} }
} }
} catch (final JSONException x) { } catch (final JSONException x) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + page + "' on " + url, x);
} }
Collections.sort(lineDestinations, new LineDestinationComparator()); Collections.sort(lineDestinations, new LineDestinationComparator());
} }
@ -581,10 +581,15 @@ public class VrsProvider extends AbstractNetworkProvider {
// pc = points of interest count // pc = points of interest count
final int pc = 5; final int pc = 5;
// t = sap (stops and/or addresses and/or pois) // t = sap (stops and/or addresses and/or pois)
final String uri = API_BASE + "?eID=tx_vrsinfo_ass2_objects&sc=" + sc + "&ac=" + ac + "&pc=" + ac + "&t=sap&q=" final HttpUrl.Builder url = API_BASE.newBuilder();
+ ParserUtils.urlEncode(new Location(LocationType.ANY, null, null, constraint.toString()).name); url.addQueryParameter("eID", "tx_vrsinfo_ass2_objects");
url.addQueryParameter("sc", Integer.toString(sc));
url.addQueryParameter("ac", Integer.toString(ac));
url.addQueryParameter("pc", Integer.toString(pc));
url.addQueryParameter("t", "sap");
url.addQueryParameter("q", constraint.toString());
final CharSequence page = httpClient.get(HttpUrl.parse(uri)); final CharSequence page = httpClient.get(url.build());
try { try {
final List<SuggestedLocation> locations = new ArrayList<SuggestedLocation>(); final List<SuggestedLocation> locations = new ArrayList<SuggestedLocation>();
@ -628,7 +633,7 @@ public class VrsProvider extends AbstractNetworkProvider {
final ResultHeader header = new ResultHeader(NetworkId.VRS, SERVER_PRODUCT); final ResultHeader header = new ResultHeader(NetworkId.VRS, SERVER_PRODUCT);
return new SuggestLocationsResult(header, locations); return new SuggestLocationsResult(header, locations);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + page + "' on " + url, x);
} }
} }
@ -683,26 +688,22 @@ public class VrsProvider extends AbstractNetworkProvider {
QueryTripsResult.Status.UNKNOWN_TO); QueryTripsResult.Status.UNKNOWN_TO);
} }
final StringBuilder uri = new StringBuilder(API_BASE); final HttpUrl.Builder url = API_BASE.newBuilder();
uri.append("?eID=tx_vrsinfo_ass2_router&f=").append(fromString).append("&t=").append(toString); url.addQueryParameter("eID", "tx_vrsinfo_ass2_router");
url.addQueryParameter("f", fromString);
url.addQueryParameter("t", toString);
if (via != null) { if (via != null) {
uri.append("&v=").append(via.id); url.addQueryParameter("v", via.id);
} }
if (dep) { url.addQueryParameter(dep ? "d" : "a", formatDate(date));
uri.append("&d="); url.addQueryParameter("s", "t");
} else { url.addQueryParameter("p", generateProducts(products));
uri.append("&a="); url.addQueryParameter("o", "v");
}
appendDate(uri, date);
uri.append("&s=t");
uri.append("&p=");
uri.append(generateProducts(products));
uri.append("&o=v");
if (EXACT_POINTS) { if (EXACT_POINTS) {
uri.append("p"); url.addQueryParameter("p", "");
} }
final CharSequence page = httpClient.get(HttpUrl.parse(uri.toString())); final CharSequence page = httpClient.get(url.build());
try { try {
final List<Trip> trips = new ArrayList<Trip>(); final List<Trip> trips = new ArrayList<Trip>();
@ -891,11 +892,11 @@ public class VrsProvider extends AbstractNetworkProvider {
else else
context.disableEarlier(); context.disableEarlier();
} }
return new QueryTripsResult(header, uri.toString(), from, via, to, context, trips); return new QueryTripsResult(header, url.build().toString(), from, via, to, context, trips);
} catch (final JSONException x) { } catch (final JSONException x) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); throw new RuntimeException("cannot parse: '" + page + "' on " + url, x);
} catch (final ParseException e) { } catch (final ParseException e) {
throw new RuntimeException("cannot parse: '" + page + "' on " + uri, e); throw new RuntimeException("cannot parse: '" + page + "' on " + url, e);
} }
} }
@ -1126,7 +1127,7 @@ public class VrsProvider extends AbstractNetworkProvider {
} }
} }
private final static void appendDate(final StringBuilder uri, final Date time) { private final static String formatDate(final Date time) {
final Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC")); final Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
c.setTime(time); c.setTime(time);
final int year = c.get(Calendar.YEAR); final int year = c.get(Calendar.YEAR);
@ -1135,8 +1136,7 @@ public class VrsProvider extends AbstractNetworkProvider {
final int hour = c.get(Calendar.HOUR_OF_DAY); final int hour = c.get(Calendar.HOUR_OF_DAY);
final int minute = c.get(Calendar.MINUTE); final int minute = c.get(Calendar.MINUTE);
final int second = c.get(Calendar.SECOND); final int second = c.get(Calendar.SECOND);
uri.append(ParserUtils.urlEncode(String.format(Locale.ENGLISH, "%04d-%02d-%02dT%02d:%02d:%02dZ", year, month, return String.format(Locale.ENGLISH, "%04d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hour, minute, second);
day, hour, minute, second)));
} }
private final static Date parseDateTime(final String dateTimeStr) throws ParseException { private final static Date parseDateTime(final String dateTimeStr) throws ParseException {

View file

@ -17,11 +17,13 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VvmProvider extends AbstractEfaProvider { public class VvmProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://efa.mobilitaetsverbund.de/web/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://efa.mobilitaetsverbund.de/web/");
public VvmProvider() { public VvmProvider() {
super(NetworkId.VVM, API_BASE); super(NetworkId.VVM, API_BASE);

View file

@ -24,17 +24,19 @@ import com.google.common.base.Charsets;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VvoProvider extends AbstractEfaProvider { public class VvoProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://efa.vvo-online.de:8080/dvb/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://efa.vvo-online.de:8080/dvb/");
public VvoProvider() { public VvoProvider() {
this(API_BASE); this(API_BASE);
} }
public VvoProvider(final String apiBase) { public VvoProvider(final HttpUrl apiBase) {
super(NetworkId.VVO, apiBase); super(NetworkId.VVO, apiBase);
setRequestUrlEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);

View file

@ -23,17 +23,19 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Point; import de.schildbach.pte.dto.Point;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VvsProvider extends AbstractEfaProvider { public class VvsProvider extends AbstractEfaProvider {
private static final String API_BASE = "http://www2.vvs.de/vvs/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://www2.vvs.de/vvs/");
public VvsProvider() { public VvsProvider() {
this(API_BASE); this(API_BASE);
} }
public VvsProvider(final String apiBase) { public VvsProvider(final HttpUrl apiBase) {
super(NetworkId.VVS, apiBase); super(NetworkId.VVS, apiBase);
setIncludeRegionId(false); setIncludeRegionId(false);

View file

@ -37,11 +37,13 @@ import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VvtProvider extends AbstractHafasProvider { public class VvtProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://fahrplan.vvt.at/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://fahrplan.vvt.at/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY,
null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY, null, Product.TRAM, Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY,
Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null }; Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN, null, null, null };

View file

@ -17,13 +17,15 @@
package de.schildbach.pte; package de.schildbach.pte;
import okhttp3.HttpUrl;
/** /**
* Verkehrsverbund Vogtland * Verkehrsverbund Vogtland
* *
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VvvProvider extends AbstractEfaProvider { public class VvvProvider extends AbstractEfaProvider {
private final static String API_BASE = "http://195.30.98.162:8081/vvv2/"; private static final HttpUrl API_BASE = HttpUrl.parse("http://195.30.98.162:8081/vvv2/");
public VvvProvider() { public VvvProvider() {
super(NetworkId.VVV, API_BASE); super(NetworkId.VVV, API_BASE);

View file

@ -31,11 +31,13 @@ import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class WienProvider extends AbstractEfaProvider { public class WienProvider extends AbstractEfaProvider {
private final static String API_BASE = "https://www.wienerlinien.at/ogd_routing/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://www.wienerlinien.at/ogd_routing/");
public WienProvider() { public WienProvider() {
super(NetworkId.WIEN, API_BASE); super(NetworkId.WIEN, API_BASE);
@ -46,21 +48,19 @@ public class WienProvider extends AbstractEfaProvider {
} }
@Override @Override
protected String xsltTripRequestParameters(final Location from, final @Nullable Location via, final Location to, protected void appendXsltTripRequestParameters(final HttpUrl.Builder url, final Location from,
final Date time, final boolean dep, final @Nullable Collection<Product> products, final @Nullable Location via, final Location to, final Date time, final boolean dep,
final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed, final @Nullable Collection<Product> products, final @Nullable Optimize optimize,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) { final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final StringBuilder uri = new StringBuilder(super.xsltTripRequestParameters(from, via, to, time, dep, products, final @Nullable Set<Option> options) {
optimize, walkSpeed, accessibility, options)); super.appendXsltTripRequestParameters(url, from, via, to, time, dep, products, optimize, walkSpeed,
accessibility, options);
if (products != null) { if (products != null) {
for (final Product p : products) { for (final Product p : products) {
if (p == Product.BUS) if (p == Product.BUS)
uri.append("&inclMOT_11=on"); // night bus url.addEncodedQueryParameter("inclMOT_11", "on"); // night bus
} }
} }
return uri.toString();
} }
private static final Map<String, Style> STYLES = new HashMap<String, Style>(); private static final Map<String, Style> STYLES = new HashMap<String, Style>();

View file

@ -31,11 +31,13 @@ import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style; import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape; import de.schildbach.pte.dto.Style.Shape;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class ZvvProvider extends AbstractHafasProvider { public class ZvvProvider extends AbstractHafasProvider {
private static final String API_BASE = "https://online.fahrplan.zvv.ch/bin/"; private static final HttpUrl API_BASE = HttpUrl.parse("https://online.fahrplan.zvv.ch/bin/");
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN, private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.HIGH_SPEED_TRAIN,
Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.FERRY, Product.SUBURBAN_TRAIN, Product.BUS, Product.REGIONAL_TRAIN, Product.REGIONAL_TRAIN, Product.FERRY, Product.SUBURBAN_TRAIN, Product.BUS,
Product.CABLECAR, Product.SUBWAY, Product.TRAM }; Product.CABLECAR, Product.SUBWAY, Product.TRAM };
@ -43,7 +45,7 @@ public class ZvvProvider extends AbstractHafasProvider {
public ZvvProvider() { public ZvvProvider() {
super(NetworkId.ZVV, API_BASE, "dn", PRODUCTS_MAP); super(NetworkId.ZVV, API_BASE, "dn", PRODUCTS_MAP);
setJsonGetStopsEncoding(Charsets.UTF_8); setRequestUrlEncoding(Charsets.UTF_8);
setJsonNearbyLocationsEncoding(Charsets.UTF_8); setJsonNearbyLocationsEncoding(Charsets.UTF_8);
setStyles(STYLES); setStyles(STYLES);
} }

View file

@ -254,14 +254,6 @@ public final class ParserUtils {
} }
} }
public static String urlEncode(final String str) {
try {
return URLEncoder.encode(str, "utf-8");
} catch (final UnsupportedEncodingException x) {
throw new RuntimeException(x);
}
}
public static String urlEncode(final String str, final Charset encoding) { public static String urlEncode(final String str, final Charset encoding) {
try { try {
return URLEncoder.encode(str, encoding.name()); return URLEncoder.encode(str, encoding.name());

View file

@ -35,12 +35,14 @@ import de.schildbach.pte.dto.QueryDeparturesResult;
import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import okhttp3.HttpUrl;
/** /**
* @author Andreas Schildbach * @author Andreas Schildbach
*/ */
public class VgnProviderLiveTest extends AbstractProviderLiveTest { public class VgnProviderLiveTest extends AbstractProviderLiveTest {
public VgnProviderLiveTest() { public VgnProviderLiveTest() {
super(new VgnProvider(secretProperty("vgn.api_base"))); super(new VgnProvider(HttpUrl.parse(secretProperty("vgn.api_base"))));
} }
@Test @Test