blob: 6a010986f97e05272932b6bb5e07ddde85f4bcd3 [file] [log] [blame]
Katie McCormick2f8cc172012-03-16 17:47:55 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package com.example.android.networkusage;
16
17import android.util.Xml;
18
19import org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21
22import java.io.IOException;
23import java.io.InputStream;
24import java.util.ArrayList;
25import java.util.List;
26
27/**
28 * This class parses XML feeds from stackoverflow.com.
29 * Given an InputStream representation of a feed, it returns a List of entries,
30 * where each list element represents a single entry (post) in the XML feed.
31 */
32public class StackOverflowXmlParser {
33 private static final String ns = null;
34
35 // We don't use namespaces
36
37 public List<Entry> parse(InputStream in) throws XmlPullParserException, IOException {
38 try {
39 XmlPullParser parser = Xml.newPullParser();
40 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
41 parser.setInput(in, null);
42 parser.nextTag();
43 return readFeed(parser);
44 } finally {
45 in.close();
46 }
47 }
48
49 private List<Entry> readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
50 List<Entry> entries = new ArrayList<Entry>();
51
52 parser.require(XmlPullParser.START_TAG, ns, "feed");
53 while (parser.next() != XmlPullParser.END_TAG) {
54 if (parser.getEventType() != XmlPullParser.START_TAG) {
55 continue;
56 }
57 String name = parser.getName();
58 // Starts by looking for the entry tag
59 if (name.equals("entry")) {
60 entries.add(readEntry(parser));
61 } else {
62 skip(parser);
63 }
64 }
65 return entries;
66 }
67
68 // This class represents a single entry (post) in the XML feed.
69 // It includes the data members "title," "link," and "summary."
70 public static class Entry {
71 public final String title;
72 public final String link;
73 public final String summary;
74
75 private Entry(String title, String summary, String link) {
76 this.title = title;
77 this.summary = summary;
78 this.link = link;
79 }
80 }
81
82 // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them
83 // off
84 // to their respective &quot;read&quot; methods for processing. Otherwise, skips the tag.
85 private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
86 parser.require(XmlPullParser.START_TAG, ns, "entry");
87 String title = null;
88 String summary = null;
89 String link = null;
90 while (parser.next() != XmlPullParser.END_TAG) {
91 if (parser.getEventType() != XmlPullParser.START_TAG) {
92 continue;
93 }
94 String name = parser.getName();
95 if (name.equals("title")) {
96 title = readTitle(parser);
97 } else if (name.equals("summary")) {
98 summary = readSummary(parser);
99 } else if (name.equals("link")) {
100 link = readLink(parser);
101 } else {
102 skip(parser);
103 }
104 }
105 return new Entry(title, summary, link);
106 }
107
108 // Processes title tags in the feed.
109 private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
110 parser.require(XmlPullParser.START_TAG, ns, "title");
111 String title = readText(parser);
112 parser.require(XmlPullParser.END_TAG, ns, "title");
113 return title;
114 }
115
116 // Processes link tags in the feed.
117 private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
118 String link = "";
119 parser.require(XmlPullParser.START_TAG, ns, "link");
120 String tag = parser.getName();
121 String relType = parser.getAttributeValue(null, "rel");
122 if (tag.equals("link")) {
123 if (relType.equals("alternate")) {
124 link = parser.getAttributeValue(null, "href");
125 parser.nextTag();
126 }
127 }
128 parser.require(XmlPullParser.END_TAG, ns, "link");
129 return link;
130 }
131
132 // Processes summary tags in the feed.
133 private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
134 parser.require(XmlPullParser.START_TAG, ns, "summary");
135 String summary = readText(parser);
136 parser.require(XmlPullParser.END_TAG, ns, "summary");
137 return summary;
138 }
139
140 // For the tags title and summary, extracts their text values.
141 private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
142 String result = "";
143 if (parser.next() == XmlPullParser.TEXT) {
144 result = parser.getText();
145 parser.nextTag();
146 }
147 return result;
148 }
149
150 // Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e.,
151 // if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it
152 // finds the matching END_TAG (as indicated by the value of "depth" being 0).
153 private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
154 if (parser.getEventType() != XmlPullParser.START_TAG) {
155 throw new IllegalStateException();
156 }
157 int depth = 1;
158 while (depth != 0) {
159 switch (parser.next()) {
160 case XmlPullParser.END_TAG:
161 depth--;
162 break;
163 case XmlPullParser.START_TAG:
164 depth++;
165 break;
166 }
167 }
168 }
169}