12f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick/* 22f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * Copyright (C) 2012 The Android Open Source Project 32f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * 42f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 52f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * in compliance with the License. You may obtain a copy of the License at 62f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * 72f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * http://www.apache.org/licenses/LICENSE-2.0 82f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * 92f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * Unless required by applicable law or agreed to in writing, software distributed under the License 102f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 112f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * or implied. See the License for the specific language governing permissions and limitations under 122f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * the License. 132f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick */ 142f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 152f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickpackage com.example.android.networkusage; 162f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 172f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickimport android.util.Xml; 182f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 192f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickimport org.xmlpull.v1.XmlPullParser; 202f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickimport org.xmlpull.v1.XmlPullParserException; 212f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 222f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickimport java.io.IOException; 232f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickimport java.io.InputStream; 242f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickimport java.util.ArrayList; 252f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickimport java.util.List; 262f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 272f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick/** 282f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * This class parses XML feeds from stackoverflow.com. 292f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * Given an InputStream representation of a feed, it returns a List of entries, 302f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick * where each list element represents a single entry (post) in the XML feed. 312f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick */ 322f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormickpublic class StackOverflowXmlParser { 332f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private static final String ns = null; 342f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 352f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // We don't use namespaces 362f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 372f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick public List<Entry> parse(InputStream in) throws XmlPullParserException, IOException { 382f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick try { 392f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick XmlPullParser parser = Xml.newPullParser(); 402f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); 412f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.setInput(in, null); 422f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.nextTag(); 432f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick return readFeed(parser); 442f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } finally { 452f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick in.close(); 462f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 472f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 482f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 492f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private List<Entry> readFeed(XmlPullParser parser) throws XmlPullParserException, IOException { 502f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick List<Entry> entries = new ArrayList<Entry>(); 512f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 522f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.START_TAG, ns, "feed"); 532f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick while (parser.next() != XmlPullParser.END_TAG) { 542f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (parser.getEventType() != XmlPullParser.START_TAG) { 552f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick continue; 562f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 572f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String name = parser.getName(); 582f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // Starts by looking for the entry tag 592f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (name.equals("entry")) { 602f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick entries.add(readEntry(parser)); 612f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } else { 622f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick skip(parser); 632f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 642f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 652f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick return entries; 662f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 672f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 682f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // This class represents a single entry (post) in the XML feed. 692f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // It includes the data members "title," "link," and "summary." 702f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick public static class Entry { 712f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick public final String title; 722f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick public final String link; 732f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick public final String summary; 742f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 752f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private Entry(String title, String summary, String link) { 762f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick this.title = title; 772f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick this.summary = summary; 782f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick this.link = link; 792f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 802f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 812f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 822f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them 832f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // off 842f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // to their respective "read" methods for processing. Otherwise, skips the tag. 852f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException { 862f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.START_TAG, ns, "entry"); 872f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String title = null; 882f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String summary = null; 892f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String link = null; 902f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick while (parser.next() != XmlPullParser.END_TAG) { 912f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (parser.getEventType() != XmlPullParser.START_TAG) { 922f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick continue; 932f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 942f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String name = parser.getName(); 952f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (name.equals("title")) { 962f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick title = readTitle(parser); 972f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } else if (name.equals("summary")) { 982f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick summary = readSummary(parser); 992f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } else if (name.equals("link")) { 1002f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick link = readLink(parser); 1012f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } else { 1022f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick skip(parser); 1032f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1042f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1052f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick return new Entry(title, summary, link); 1062f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1072f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 1082f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // Processes title tags in the feed. 1092f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException { 1102f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.START_TAG, ns, "title"); 1112f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String title = readText(parser); 1122f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.END_TAG, ns, "title"); 1132f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick return title; 1142f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1152f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 1162f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // Processes link tags in the feed. 1172f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException { 1182f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String link = ""; 1192f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.START_TAG, ns, "link"); 1202f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String tag = parser.getName(); 1212f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String relType = parser.getAttributeValue(null, "rel"); 1222f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (tag.equals("link")) { 1232f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (relType.equals("alternate")) { 1242f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick link = parser.getAttributeValue(null, "href"); 1252f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.nextTag(); 1262f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1272f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1282f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.END_TAG, ns, "link"); 1292f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick return link; 1302f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1312f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 1322f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // Processes summary tags in the feed. 1332f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException { 1342f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.START_TAG, ns, "summary"); 1352f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String summary = readText(parser); 1362f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.require(XmlPullParser.END_TAG, ns, "summary"); 1372f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick return summary; 1382f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1392f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 1402f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // For the tags title and summary, extracts their text values. 1412f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { 1422f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick String result = ""; 1432f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (parser.next() == XmlPullParser.TEXT) { 1442f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick result = parser.getText(); 1452f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick parser.nextTag(); 1462f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1472f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick return result; 1482f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1492f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick 1502f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e., 1512f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it 1522f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick // finds the matching END_TAG (as indicated by the value of "depth" being 0). 1532f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { 1542f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick if (parser.getEventType() != XmlPullParser.START_TAG) { 1552f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick throw new IllegalStateException(); 1562f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1572f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick int depth = 1; 1582f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick while (depth != 0) { 1592f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick switch (parser.next()) { 1602f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick case XmlPullParser.END_TAG: 1612f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick depth--; 1622f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick break; 1632f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick case XmlPullParser.START_TAG: 1642f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick depth++; 1652f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick break; 1662f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1672f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1682f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick } 1692f8cc17f5fbc2e05ac0889fbbddf4e530750087bKatie McCormick} 170