diff --git a/pom.xml b/pom.xml index 12179e1c..9fd067e5 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,13 @@ provided + + net.sf.kxml + kxml2 + 2.3.0 + provided + + junit junit diff --git a/src/de/schildbach/pte/AbstractEfaProvider.java b/src/de/schildbach/pte/AbstractEfaProvider.java index a901282a..ae584875 100644 --- a/src/de/schildbach/pte/AbstractEfaProvider.java +++ b/src/de/schildbach/pte/AbstractEfaProvider.java @@ -18,11 +18,20 @@ package de.schildbach.pte; import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import de.schildbach.pte.util.XmlPullUtil; + /** * @author Andreas Schildbach */ @@ -112,4 +121,78 @@ public abstract class AbstractEfaProvider implements NetworkProvider { return (double) value / 1000000; } + + protected abstract String parseLine(String number, String symbol, String mot); + + public QueryDeparturesResult queryDepartures(final String uri) throws IOException + { + try + { + final CharSequence page = ParserUtils.scrape(uri); + // System.out.println(page); + + final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); + final XmlPullParser pp = factory.newPullParser(); + pp.setInput(new StringReader(page.toString())); + + XmlPullUtil.jumpToStartTag(pp, null, "odvNameElem"); + final int locationId = Integer.parseInt(pp.getAttributeValue(null, "stopID")); + + final String location = pp.nextText(); + + final Calendar departureTime = new GregorianCalendar(); + final List departures = new ArrayList(8); + + XmlPullUtil.jumpToStartTag(pp, null, "itdDepartureList"); + while (XmlPullUtil.jumpToStartTag(pp, null, "itdDeparture")) + { + if (Integer.parseInt(pp.getAttributeValue(null, "stopID")) == locationId) + { + String position = pp.getAttributeValue(null, "platform"); + if (position != null) + position = "Gl. " + position; + + departureTime.clear(); + XmlPullUtil.jumpToStartTag(pp, null, "itdDate"); + processItdDate(pp, departureTime); + XmlPullUtil.jumpToStartTag(pp, null, "itdTime"); + processItdTime(pp, departureTime); + XmlPullUtil.jumpToStartTag(pp, null, "itdServingLine"); + + final String line = parseLine(pp.getAttributeValue(null, "number"), pp.getAttributeValue(null, "symbol"), pp.getAttributeValue( + null, "motType")); + + final boolean isRealtime = pp.getAttributeValue(null, "realtime").equals("1"); + + final String destination = pp.getAttributeValue(null, "direction"); + + final int destinationId = Integer.parseInt(pp.getAttributeValue(null, "destID")); + + departures.add(new Departure(!isRealtime ? departureTime.getTime() : null, isRealtime ? departureTime.getTime() : null, line, + lineColors(line), null, position, destinationId, destination, null)); + } + } + + return new QueryDeparturesResult(uri, locationId, location, departures); + } + catch (final XmlPullParserException x) + { + throw new RuntimeException(x); + } + } + + private void processItdDate(final XmlPullParser pp, final Calendar calendar) throws XmlPullParserException, IOException + { + pp.require(XmlPullParser.START_TAG, null, "itdDate"); + calendar.set(Calendar.YEAR, Integer.parseInt(pp.getAttributeValue(null, "year"))); + calendar.set(Calendar.MONTH, Integer.parseInt(pp.getAttributeValue(null, "month")) - 1); + calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(pp.getAttributeValue(null, "day"))); + } + + private void processItdTime(final XmlPullParser pp, final Calendar calendar) throws XmlPullParserException, IOException + { + pp.require(XmlPullParser.START_TAG, null, "itdTime"); + calendar.set(Calendar.HOUR, Integer.parseInt(pp.getAttributeValue(null, "hour"))); + calendar.set(Calendar.MINUTE, Integer.parseInt(pp.getAttributeValue(null, "minute"))); + } } diff --git a/src/de/schildbach/pte/GvhProvider.java b/src/de/schildbach/pte/GvhProvider.java index 5c41d1b0..93ef9c01 100644 --- a/src/de/schildbach/pte/GvhProvider.java +++ b/src/de/schildbach/pte/GvhProvider.java @@ -19,14 +19,27 @@ package de.schildbach.pte; import java.io.IOException; import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; /** * @author Andreas Schildbach */ public class GvhProvider extends AbstractEfaProvider { + public static final String NETWORK_ID = "mobil.gvh.de"; private static final String API_BASE = "http://mobil.gvh.de/mobile2/"; + public boolean hasCapabilities(final Capability... capabilities) + { + for (final Capability capability : capabilities) + if (capability == Capability.DEPARTURES) + return true; + + return false; + } + private static final String AUTOCOMPLETE_URI = API_BASE + "XML_STOPFINDER_REQUEST?outputFormat=XML&coordOutputFormat=WGS84&name_sf=%s&type_sf=any"; private static final String ENCODING = "ISO-8859-1"; @@ -52,24 +65,94 @@ public class GvhProvider extends AbstractEfaProvider return null; } + public StationLocationResult stationLocation(String stationId) throws IOException + { + throw new UnsupportedOperationException(); + } + public String departuresQueryUri(String stationId, int maxDepartures) { - throw new UnsupportedOperationException(); + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE).append("XSLT_DM_REQUEST"); + uri.append("?outputFormat=XML"); + uri.append("&coordOutputFormat=WGS84"); + uri.append("&type_dm=stop"); + uri.append("&name_dm=").append(stationId); + uri.append("&mode=direct"); + return uri.toString(); } - public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException - { - throw new UnsupportedOperationException(); - } + private static final Pattern P_LINE_RE = Pattern.compile("RE\\d+"); + private static final Pattern P_LINE_RB = Pattern.compile("RB\\d+"); - public boolean hasCapabilities(Capability... capabilities) + @Override + protected String parseLine(final String number, final String symbol, final String mot) { - throw new UnsupportedOperationException(); - } + if (!number.equals(symbol)) + throw new IllegalStateException("number " + number + ", symbol " + symbol); - public int[] lineColors(String line) - { - throw new UnsupportedOperationException(); + int t = Integer.parseInt(mot); + + if (t == 0) + { + final String[] parts = number.split(" ", 3); + final String type = parts[0]; + final String num = parts.length >= 2 ? parts[1] : null; + final String str = type + (num != null ? num : ""); + if (type.equals("EC")) // Eurocity + return 'I' + str; + if (type.equals("IC")) // Intercity + return 'I' + str; + if (type.equals("ICE")) // Intercity Express + return 'I' + str; + if (type.equals("THA")) // Thalys + return 'I' + str; + + if (type.equals("IR")) // Interregio + return 'R' + str; + if (type.equals("RE")) // Regional-Express + return 'R' + str; + if (P_LINE_RE.matcher(type).matches()) + return 'R' + str; + if (type.equals("RB")) // Regionalbahn + return 'R' + str; + if (P_LINE_RB.matcher(type).matches()) + return 'R' + str; + if (type.equals("R")) // Regionalzug + return 'R' + str; + if (type.equals("WFB")) // Westfalenbahn + return 'R' + str; + if (type.equals("NWB")) // NordWestBahn + return 'R' + str; + if (type.equals("ME")) // Metronom + return 'R' + str; + if (type.equals("ERB")) // eurobahn + return 'R' + str; + if (type.equals("CAN")) // cantus + return 'R' + str; + if (type.equals("HEX")) // Veolia Verkehr Sachsen-Anhalt + return 'R' + str; + if (type.equals("EB")) // Erfurter Bahn + return 'R' + str; + if (type.equals("MRB")) // Mittelrheinbahn + return 'R' + str; + if (type.equals("ABR")) // ABELLIO Rail NRW + return 'R' + str; + + throw new IllegalArgumentException("cannot normalize: " + number); + } + if (t == 1) + return 'S' + number; + if (t == 3 || t == 4) + return 'T' + number; + if (t == 5 || t == 6 || t == 7 || t == 10) + return 'B' + number; + if (t == 9) + return 'F' + number; + if (t == 11) + return '?' + number; + + throw new IllegalStateException("cannot normalize mot '" + mot + "' number '" + number + "'"); } public QueryConnectionsResult queryConnections(LocationType fromType, String from, LocationType viaType, String via, LocationType toType, @@ -78,18 +161,32 @@ public class GvhProvider extends AbstractEfaProvider throw new UnsupportedOperationException(); } - public QueryDeparturesResult queryDepartures(String queryUri) throws IOException - { - throw new UnsupportedOperationException(); - } - public QueryConnectionsResult queryMoreConnections(String uri) throws IOException { throw new UnsupportedOperationException(); } - public StationLocationResult stationLocation(String stationId) throws IOException + public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException { throw new UnsupportedOperationException(); } + + private static final Map LINES = new HashMap(); + + static + { + LINES.put('I', new int[] { Color.WHITE, Color.RED, Color.RED }); + LINES.put('R', new int[] { Color.GRAY, Color.WHITE }); + LINES.put('S', new int[] { Color.parseColor("#006e34"), Color.WHITE }); + LINES.put('U', new int[] { Color.parseColor("#003090"), Color.WHITE }); + LINES.put('T', new int[] { Color.parseColor("#cc0000"), Color.WHITE }); + LINES.put('B', new int[] { Color.parseColor("#993399"), Color.WHITE }); + LINES.put('F', new int[] { Color.BLUE, Color.WHITE }); + LINES.put('?', new int[] { Color.DKGRAY, Color.WHITE }); + } + + public int[] lineColors(final String line) + { + return LINES.get(line.charAt(0)); + } } diff --git a/src/de/schildbach/pte/LinzProvider.java b/src/de/schildbach/pte/LinzProvider.java index e24fb795..3b5b5e2f 100644 --- a/src/de/schildbach/pte/LinzProvider.java +++ b/src/de/schildbach/pte/LinzProvider.java @@ -18,15 +18,9 @@ package de.schildbach.pte; import java.io.IOException; -import java.util.ArrayList; -import java.util.Calendar; import java.util.Date; -import java.util.GregorianCalendar; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * @author Andreas Schildbach @@ -109,113 +103,8 @@ public class LinzProvider extends AbstractEfaProvider return uri.toString(); } - private static final Pattern P_DEPARTURES_HEAD_COARSE = Pattern.compile(".*?" // - + "(?:" // - + "(.*?).*?" // head - + "(?:(.*?).*?)?" // departures - + "|" // - + "(Server-Wartung).*?" // messages - + ")" // - , Pattern.DOTALL); - private static final Pattern P_DEPARTURES_HEAD_FINE = Pattern.compile(".*?" // - + "]*>" // locationId - + "([^<]*).*?" // location - , Pattern.DOTALL); - private static final Pattern P_DEPARTURES_COARSE = Pattern.compile("", Pattern.DOTALL); - private static final Pattern P_DEPARTURES_FINE = Pattern.compile("" // - + "stopID=\"(\\d+)\" [^>]* area=\"(\\d+)\" platform=\"(\\d+)?\" platformName=\"\".*?" // locationId - + "" // date - + "" // time - + ".*?" // - + "]* number=\"([^<]*)\" symbol=\"([^<]*)\" motType=\"(\\d+)\" " // line, symbol, type - + "realtime=\"(\\d+)\" " // realtime - + "direction=\"([^\"]*)\" destID=\"(\\d+)\"" // destination, destinationId - + ".*?" // - , Pattern.DOTALL); - - public QueryDeparturesResult queryDepartures(final String uri) throws IOException - { - final CharSequence page = ParserUtils.scrape(uri); - - // parse page - final Matcher mHeadCoarse = P_DEPARTURES_HEAD_COARSE.matcher(page); - if (mHeadCoarse.matches()) - { - if (mHeadCoarse.group(3) != null) - return new QueryDeparturesResult(uri, QueryDeparturesResult.Status.SERVICE_DOWN); - - final String headerText = mHeadCoarse.group(1); - final String departuresText = mHeadCoarse.group(2); - - final Matcher mHeadFine = P_DEPARTURES_HEAD_FINE.matcher(headerText); - if (mHeadFine.matches()) - { - final int locationId = Integer.parseInt(mHeadFine.group(1)); - final String location = ParserUtils.resolveEntities(mHeadFine.group(2)); - final List departures = new ArrayList(8); - - if (departuresText != null) - { - final Matcher mDepCoarse = P_DEPARTURES_COARSE.matcher(departuresText); - while (mDepCoarse.find()) - { - final Matcher mDepFine = P_DEPARTURES_FINE.matcher(mDepCoarse.group(1)); - if (mDepFine.matches()) - { - if (Integer.parseInt(mDepFine.group(1)) == locationId) - { - final String area = mDepFine.group(2); // FIXME not clear what this is - - final String position = mDepFine.group(3) != null ? "Gl. " + mDepFine.group(3) : null; - - final Date departureDate = parseDate(mDepFine.group(4), mDepFine.group(5), mDepFine.group(6), mDepFine.group(7), - mDepFine.group(8)); - - final String line = parseLine(mDepFine.group(9), mDepFine.group(10), mDepFine.group(11)); - - final boolean isRealtime = mDepFine.group(12).equals("1"); - - final String destination = mDepFine.group(13); - - final int destinationId = Integer.parseInt(mDepFine.group(14)); - - departures.add(new Departure(!isRealtime ? departureDate : null, isRealtime ? departureDate : null, line, LINES - .get(line.charAt(0)), null, position, destinationId, destination, null)); - } - } - else - { - throw new IllegalArgumentException("cannot parse '" + mDepCoarse.group(1) + "' on " + uri); - } - } - } - - return new QueryDeparturesResult(uri, locationId, location, departures); - } - else - { - throw new IllegalArgumentException("cannot parse '" + headerText + "' on " + uri); - } - } - else - { - throw new IllegalArgumentException("cannot parse '" + page + "' on " + uri); - } - } - - private static Date parseDate(final String year, final String month, final String day, final String hour, final String minute) - { - final Calendar calendar = new GregorianCalendar(); - calendar.clear(); - calendar.set(Calendar.YEAR, Integer.parseInt(year)); - calendar.set(Calendar.MONTH, Integer.parseInt(month) - 1); - calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day)); - calendar.set(Calendar.HOUR, Integer.parseInt(hour)); - calendar.set(Calendar.MINUTE, Integer.parseInt(minute)); - return calendar.getTime(); - } - - private String parseLine(final String number, final String symbol, final String mot) + @Override + protected String parseLine(final String number, final String symbol, final String mot) { if (!number.equals(symbol)) throw new IllegalStateException("number " + number + ", symbol " + symbol); @@ -229,7 +118,7 @@ public class LinzProvider extends AbstractEfaProvider if (t == 8) return 'C' + number; - throw new IllegalStateException("cannot normalize type '" + mot + "' line '" + number + "'"); + throw new IllegalStateException("cannot normalize mot '" + mot + "' number '" + number + "'"); } private static final Map LINES = new HashMap(); diff --git a/src/de/schildbach/pte/util/XmlPullUtil.java b/src/de/schildbach/pte/util/XmlPullUtil.java new file mode 100644 index 00000000..07bbfdf1 --- /dev/null +++ b/src/de/schildbach/pte/util/XmlPullUtil.java @@ -0,0 +1,367 @@ +/* + * For license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/). + * According to www.xmlpull.org, this code is in the public domain. + */ + +package de.schildbach.pte.util; + +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +/** + * Handy functions that combines XmlPull API into higher level functionality. + * + * @author Aleksander Slominski + * @author Naresh Bhatia + */ +public class XmlPullUtil +{ + public static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"; + + private XmlPullUtil() + { + } + + /** + * Return value of attribute with given name and no namespace. + */ + public static String getAttributeValue(XmlPullParser pp, String name) + { + return pp.getAttributeValue(XmlPullParser.NO_NAMESPACE, name); + } + + /** + * Return PITarget from Processing Instruction (PI) as defined in XML 1.0 Section 2.6 Processing Instructions [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' + */ + public static String getPITarget(XmlPullParser pp) throws IllegalStateException + { + int eventType; + + try + { + eventType = pp.getEventType(); + } + catch (XmlPullParserException ex) + { + // should never happen ... + throw new IllegalStateException("could not determine parser state: " + ex + pp.getPositionDescription()); + } + + if (eventType != XmlPullParser.PROCESSING_INSTRUCTION) + throw new IllegalStateException("parser must be on processing instruction and not " + XmlPullParser.TYPES[eventType] + + pp.getPositionDescription()); + + final String PI = pp.getText(); + for (int i = 0; i < PI.length(); i++) + { + if (isS(PI.charAt(i))) + { + // assert i > 0 + return PI.substring(0, i); + } + } + return PI; + } + + /** + * Return everything past PITarget and S from Processing Instruction (PI) as defined in XML 1.0 Section 2.6 + * Processing Instructions [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' + * + *

+ * NOTE: if there is no PI data it returns empty string. + */ + public static String getPIData(XmlPullParser pp) throws IllegalStateException + { + int eventType; + try + { + eventType = pp.getEventType(); + } + catch (XmlPullParserException ex) + { + // should never happen ... + throw new IllegalStateException("could not determine parser state: " + ex + pp.getPositionDescription()); + } + + if (eventType != XmlPullParser.PROCESSING_INSTRUCTION) + throw new IllegalStateException("parser must be on processing instruction and not " + XmlPullParser.TYPES[eventType] + + pp.getPositionDescription()); + + final String PI = pp.getText(); + int pos = -1; + for (int i = 0; i < PI.length(); i++) + { + if (isS(PI.charAt(i))) + { + pos = i; + } + else if (pos > 0) + { + return PI.substring(i); + } + } + return ""; + } + + /** + * Return true if chacters is S as defined in XML 1.0 S ::= (#x20 | #x9 | #xD | #xA)+ + */ + private static boolean isS(char ch) + { + return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'); + } + + /** + * Skip sub tree that is currently porser positioned on.
+ * NOTE: parser must be on START_TAG and when funtion returns parser will be positioned on corresponding END_TAG + */ + public static void skipSubTree(XmlPullParser pp) throws XmlPullParserException, IOException + { + pp.require(XmlPullParser.START_TAG, null, null); + + int level = 1; + while (level > 0) + { + int eventType = pp.next(); + if (eventType == XmlPullParser.END_TAG) + --level; + else if (eventType == XmlPullParser.START_TAG) + ++level; + } + } + + /** + * call parser nextTag() and check that it is START_TAG, throw exception if not. + */ + public static void nextStartTag(XmlPullParser pp) throws XmlPullParserException, IOException + { + if (pp.nextTag() != XmlPullParser.START_TAG) + throw new XmlPullParserException("expected START_TAG and not " + pp.getPositionDescription()); + } + + /** + * combine nextTag(); pp.require(XmlPullParser.START_TAG, null, name); + */ + public static void nextStartTag(XmlPullParser pp, String name) throws XmlPullParserException, IOException + { + pp.nextTag(); + pp.require(XmlPullParser.START_TAG, null, name); + } + + /** + * combine nextTag(); pp.require(XmlPullParser.START_TAG, namespace, name); + */ + public static void nextStartTag(XmlPullParser pp, String namespace, String name) throws XmlPullParserException, IOException + { + pp.nextTag(); + pp.require(XmlPullParser.START_TAG, namespace, name); + } + + /** + * combine nextTag(); pp.require(XmlPullParser.END_TAG, namespace, name); + */ + public static void nextEndTag(XmlPullParser pp, String namespace, String name) throws XmlPullParserException, IOException + { + pp.nextTag(); + pp.require(XmlPullParser.END_TAG, namespace, name); + } + + /** + * Read text content of element ith given namespace and name (use null namespace do indicate that nemspace should + * not be checked) + */ + + public static String nextText(XmlPullParser pp, String namespace, String name) throws IOException, XmlPullParserException + { + if (name == null) + throw new XmlPullParserException("name for element can not be null"); + + pp.require(XmlPullParser.START_TAG, namespace, name); + return pp.nextText(); + } + + /** + * Read attribute value and return it or throw exception if current element does not have such attribute. + */ + + public static String getRequiredAttributeValue(XmlPullParser pp, String namespace, String name) throws IOException, XmlPullParserException + { + final String value = pp.getAttributeValue(namespace, name); + if (value == null) + throw new XmlPullParserException("required attribute " + name + " is not present"); + else + return value; + } + + /** + * Call parser nextTag() and check that it is END_TAG, throw exception if not. + */ + public static void nextEndTag(XmlPullParser pp) throws XmlPullParserException, IOException + { + if (pp.nextTag() != XmlPullParser.END_TAG) + throw new XmlPullParserException("expected END_TAG and not" + pp.getPositionDescription()); + } + + /** + * Tests if the current event is of the given type and if the namespace and name match. null will match any + * namespace and any name. If the test passes a true is returned otherwise a false is returned. + */ + public static boolean matches(XmlPullParser pp, int type, String namespace, String name) throws XmlPullParserException + { + boolean matches = type == pp.getEventType() && (namespace == null || namespace.equals(pp.getNamespace())) + && (name == null || name.equals(pp.getName())); + + return matches; + } + + /** + * Writes a simple element such as johndoe. The namespace and elementText are allowed to be + * null. If elementText is null, an xsi:nil="true" will be added as an attribute. + */ + public static void writeSimpleElement(XmlSerializer serializer, String namespace, String elementName, String elementText) throws IOException, + XmlPullParserException + { + if (elementName == null) + throw new XmlPullParserException("name for element can not be null"); + + serializer.startTag(namespace, elementName); + if (elementText == null) + { + serializer.attribute(XSI_NS, "nil", "true"); + } + else + { + serializer.text(elementText); + } + serializer.endTag(namespace, elementName); + } + + /** + * This method bypasses all child subtrees until it reached END_TAG for current tree. Parser must be on START_TAG of + * one of child subtrees. + */ + public static void jumpToEndOfTree(final XmlPullParser pp) throws XmlPullParserException, IOException + { + pp.require(XmlPullParser.START_TAG, null, null); + + while (true) + { + int eventType = pp.next(); + if (eventType == XmlPullParser.START_TAG) + { + skipSubTree(pp); + pp.require(XmlPullParser.END_TAG, null, null); + pp.next(); // skip end tag + } + else if (eventType == XmlPullParser.END_TAG) + { + break; + } + } + } + + /** + * This method bypasses all child subtrees until it finds a child subtree with start tag that matches the tag name + * (if not null) and namespsce (if not null) passed in. Parser must be positioned on START_TAG. + *

+ * If succesfulpositions parser on such START_TAG and return true otherwise this method returns false and parser is + * positioned on END_TAG signaling last element in curren subtree. + */ + public static boolean jumpToSubTree(final XmlPullParser pp, final String tagNamespace, final String tagName) throws XmlPullParserException, + IOException + { + if (tagNamespace == null && tagName == null) + throw new IllegalArgumentException("namespace and name argument can not be both null:" + pp.getPositionDescription()); + + pp.require(XmlPullParser.START_TAG, null, null); + while (true) + { + int eventType = pp.next(); + + if (eventType == XmlPullParser.START_TAG) + { + String name = pp.getName(); + String namespace = pp.getNamespace(); + boolean matches = (tagNamespace != null && tagNamespace.equals(namespace)) || (tagName != null && tagName.equals(name)); + if (matches) + { + return true; + } + skipSubTree(pp); + pp.require(XmlPullParser.END_TAG, name, namespace); + pp.next(); // skip end tag + } + else if (eventType == XmlPullParser.END_TAG) + { + return false; + } + } + } + + /** + * This method bypasses all events until it finds a start tag that has passed in namespace (if not null) and + * namespace (if not null). + * + * @return true if such START_TAG was found or false otherwise (and parser is on END_DOCUMENT). + */ + public static boolean jumpToStartTag(final XmlPullParser pp, final String tagNamespace, final String tagName) throws XmlPullParserException, + IOException + { + if (tagNamespace == null && tagName == null) + throw new IllegalArgumentException("namespace and name argument can not be both null:" + pp.getPositionDescription()); + + while (true) + { + int eventType = pp.next(); + if (eventType == XmlPullParser.START_TAG) + { + String name = pp.getName(); + String namespace = pp.getNamespace(); + boolean matches = (tagNamespace != null && tagNamespace.equals(namespace)) || (tagName != null && tagName.equals(name)); + if (matches) + { + return true; + } + } + else if (eventType == XmlPullParser.END_DOCUMENT) + { + return false; + } + } + } + + /** + * This method bypasses all events until it finds an end tag that has passed in namespace (if not null) and + * namespace (if not null). + * + * @return true if such END_TAG was found or false otherwise (and parser is on END_DOCUMENT). + */ + public static boolean jumpToEndTag(final XmlPullParser pp, final String tagNamespace, final String tagName) throws XmlPullParserException, + IOException + { + if (tagNamespace == null && tagName == null) + throw new IllegalArgumentException("namespace and name argument can not be both null:" + pp.getPositionDescription()); + + while (true) + { + int eventType = pp.next(); + if (eventType == XmlPullParser.END_TAG) + { + String name = pp.getName(); + String namespace = pp.getNamespace(); + boolean matches = (tagNamespace != null && tagNamespace.equals(namespace)) || (tagName != null && tagName.equals(name)); + if (matches) + { + return true; + } + } + else if (eventType == XmlPullParser.END_DOCUMENT) + { + return false; + } + } + } +} diff --git a/test/de/schildbach/pte/live/GvhProviderLiveTest.java b/test/de/schildbach/pte/live/GvhProviderLiveTest.java new file mode 100644 index 00000000..4d77eb21 --- /dev/null +++ b/test/de/schildbach/pte/live/GvhProviderLiveTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2010 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.schildbach.pte.live; + +import java.util.List; + +import org.junit.Test; + +import de.schildbach.pte.Autocomplete; +import de.schildbach.pte.GvhProvider; +import de.schildbach.pte.Station; + +/** + * @author Andreas Schildbach + */ +public class GvhProviderLiveTest +{ + private final GvhProvider provider = new GvhProvider(); + + @Test + public void autocomplete() throws Exception + { + final List results = provider.autocompleteStations("Hannover"); + + System.out.println(results.size() + " " + results); + } + + @Test + public void nearby() throws Exception + { + final List results = provider.nearbyStations("25000031", 0, 0, 0, 0); + + System.out.println(results.size() + " " + results); + } +} diff --git a/test/de/schildbach/pte/live/LinzProviderLiveTest.java b/test/de/schildbach/pte/live/LinzProviderLiveTest.java new file mode 100644 index 00000000..66d605e2 --- /dev/null +++ b/test/de/schildbach/pte/live/LinzProviderLiveTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2010 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.schildbach.pte.live; + +import java.util.Date; + +import org.junit.Test; + +import de.schildbach.pte.LinzProvider; +import de.schildbach.pte.QueryConnectionsResult; +import de.schildbach.pte.QueryDeparturesResult; +import de.schildbach.pte.NetworkProvider.LocationType; +import de.schildbach.pte.NetworkProvider.WalkSpeed; + +/** + * @author Andreas Schildbach + */ +public class LinzProviderLiveTest +{ + private LinzProvider provider = new LinzProvider(); + + @Test + public void queryDepartures() throws Exception + { + final QueryDeparturesResult result = provider.queryDepartures(provider.departuresQueryUri("60501720", 0)); + System.out.println(result); + } + + @Test + public void shortConnection() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections(LocationType.STATION, "Linz Hauptbahnhof", null, null, LocationType.STATION, + "Linz Auwiesen", new Date(), true, WalkSpeed.FAST); + System.out.println(result); +// final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); +// System.out.println(moreResult); + } + + @Test + public void longConnection() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections(LocationType.STATION, "Linz Auwiesen", null, null, LocationType.STATION, + "Linz Hafen", new Date(), true, WalkSpeed.SLOW); + System.out.println(result); +// final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); +// System.out.println(moreResult); + } + + @Test + public void connectionBetweenCoordinates() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections(LocationType.WGS84, "48.165238,11.577473", null, null, LocationType.WGS84, + "47.987199,11.326532", new Date(), true, WalkSpeed.NORMAL); + System.out.println(result); +// final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); +// System.out.println(moreResult); + } + + @Test + public void connectionBetweenCoordinateAndStation() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections(LocationType.WGS84, "48.238341,11.478230", null, null, LocationType.ANY, + "Ostbahnhof", new Date(), true, WalkSpeed.NORMAL); + System.out.println(result); +// final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); +// System.out.println(moreResult); + } + + @Test + public void connectionBetweenAddresses() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections(LocationType.ADDRESS, "München, Maximilianstr. 1", null, null, + LocationType.ADDRESS, "Starnberg, Jahnstraße 50", new Date(), true, WalkSpeed.NORMAL); + System.out.println(result); +// final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); +// System.out.println(moreResult); + } +}