[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
+ * 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