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 &quot;read&quot; 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