rpicms-android

Android app for the RPICMS.
git clone git://archive.git.mtrnord.blog/RpicmsTeam/rpicms-android.git
Log | Files | Refs

FeedParser.java (10690B)


      1 package de.nordgedanken.rpicms.basicsyncadapter.net;
      2 
      3 /**
      4  * Created by frank on 13.10.14.
      5  */
      6 import android.text.format.Time;
      7 import android.util.Xml;
      8 
      9 import org.xmlpull.v1.XmlPullParser;
     10 import org.xmlpull.v1.XmlPullParserException;
     11 
     12 import java.io.IOException;
     13 import java.io.InputStream;
     14 import java.text.ParseException;
     15 import java.util.ArrayList;
     16 import java.util.List;
     17 public class FeedParser {
     18 
     19 
     20     /**
     21      * This class parses generic Atom feeds.
     22      *
     23      * <p>Given an InputStream representation of a feed, it returns a List of entries,
     24      * where each list element represents a single entry (post) in the XML feed.
     25      *
     26      * <p>An example of an Atom feed can be found at:
     27      * http://en.wikipedia.org/w/index.php?title=Atom_(standard)&oldid=560239173#Example_of_an_Atom_1.0_feed
     28      */
     29 
     30         // Constants indicting XML element names that we're interested in
     31         private static final int TAG_ID = 1;
     32         private static final int TAG_TITLE = 2;
     33         private static final int TAG_PUBLISHED = 3;
     34         private static final int TAG_LINK = 4;
     35 
     36         // We don't use XML namespaces
     37         private static final String ns = null;
     38 
     39         /** Parse an Atom feed, returning a collection of Entry objects.
     40          *
     41          * @param in Atom feed, as a stream.
     42          * @return List of {@link de.nordgedanken.rpicms.basicsyncadapter.net.FeedParser.Entry} objects.
     43          * @throws org.xmlpull.v1.XmlPullParserException on error parsing feed.
     44          * @throws java.io.IOException on I/O error.
     45          */
     46         public List<Entry> parse(InputStream in)
     47                 throws XmlPullParserException, IOException, ParseException {
     48             try {
     49                 XmlPullParser parser = Xml.newPullParser();
     50                 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
     51                 parser.setInput(in, null);
     52                 parser.nextTag();
     53                 return readFeed(parser);
     54             } finally {
     55                 in.close();
     56             }
     57         }
     58 
     59         /**
     60          * Decode a feed attached to an XmlPullParser.
     61          *
     62          * @param parser Incoming XMl
     63          * @return List of {@link de.nordgedanken.rpicms.basicsyncadapter.net.FeedParser.Entry} objects.
     64          * @throws org.xmlpull.v1.XmlPullParserException on error parsing feed.
     65          * @throws java.io.IOException on I/O error.
     66          */
     67         private List<Entry> readFeed(XmlPullParser parser)
     68                 throws XmlPullParserException, IOException, ParseException {
     69             List<Entry> entries = new ArrayList<Entry>();
     70 
     71             // Search for <feed> tags. These wrap the beginning/end of an Atom document.
     72             //
     73             // Example:
     74             // <?xml version="1.0" encoding="utf-8"?>
     75             // <feed xmlns="http://www.w3.org/2005/Atom">
     76             // ...
     77             // </feed>
     78             parser.require(XmlPullParser.START_TAG, ns, "feed");
     79             while (parser.next() != XmlPullParser.END_TAG) {
     80                 if (parser.getEventType() != XmlPullParser.START_TAG) {
     81                     continue;
     82                 }
     83                 String name = parser.getName();
     84                 // Starts by looking for the <entry> tag. This tag repeates inside of <feed> for each
     85                 // article in the feed.
     86                 //
     87                 // Example:
     88                 // <entry>
     89                 //   <title>Article title</title>
     90                 //   <link rel="alternate" type="text/html" href="http://example.com/article/1234"/>
     91                 //   <link rel="edit" href="http://example.com/admin/article/1234"/>
     92                 //   <id>urn:uuid:218AC159-7F68-4CC6-873F-22AE6017390D</id>
     93                 //   <published>2003-06-27T12:00:00Z</published>
     94                 //   <updated>2003-06-28T12:00:00Z</updated>
     95                 //   <summary>Article summary goes here.</summary>
     96                 //   <author>
     97                 //     <name>Rick Deckard</name>
     98                 //     <email>deckard@example.com</email>
     99                 //   </author>
    100                 // </entry>
    101                 if (name.equals("entry")) {
    102                     entries.add(readEntry(parser));
    103                 } else {
    104                     skip(parser);
    105                 }
    106             }
    107             return entries;
    108         }
    109 
    110         /**
    111          * Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them
    112          * off to their respective "read" methods for processing. Otherwise, skips the tag.
    113          */
    114         private Entry readEntry(XmlPullParser parser)
    115                 throws XmlPullParserException, IOException, ParseException {
    116             parser.require(XmlPullParser.START_TAG, ns, "entry");
    117             String id = null;
    118             String title = null;
    119             String link = null;
    120             long publishedOn = 0;
    121 
    122             while (parser.next() != XmlPullParser.END_TAG) {
    123                 if (parser.getEventType() != XmlPullParser.START_TAG) {
    124                     continue;
    125                 }
    126                 String name = parser.getName();
    127                 if (name.equals("id")){
    128                     // Example: <id>urn:uuid:218AC159-7F68-4CC6-873F-22AE6017390D</id>
    129                     id = readTag(parser, TAG_ID);
    130                 } else if (name.equals("title")) {
    131                     // Example: <title>Article title</title>
    132                     title = readTag(parser, TAG_TITLE);
    133                 } else if (name.equals("link")) {
    134                     // Example: <link rel="alternate" type="text/html" href="http://example.com/article/1234"/>
    135                     //
    136                     // Multiple link types can be included. readAlternateLink() will only return
    137                     // non-null when reading an "alternate"-type link. Ignore other responses.
    138                     String tempLink = readTag(parser, TAG_LINK);
    139                     if (tempLink != null) {
    140                         link = tempLink;
    141                     }
    142                 } else if (name.equals("published")) {
    143                     // Example: <published>2003-06-27T12:00:00Z</published>
    144                     Time t = new Time();
    145                     t.parse3339(readTag(parser, TAG_PUBLISHED));
    146                     publishedOn = t.toMillis(false);
    147                 } else {
    148                     skip(parser);
    149                 }
    150             }
    151             return new Entry(id, title, link, publishedOn);
    152         }
    153 
    154         /**
    155          * Process an incoming tag and read the selected value from it.
    156          */
    157         private String readTag(XmlPullParser parser, int tagType)
    158                 throws IOException, XmlPullParserException {
    159             String tag = null;
    160             String endTag = null;
    161 
    162             switch (tagType) {
    163                 case TAG_ID:
    164                     return readBasicTag(parser, "id");
    165                 case TAG_TITLE:
    166                     return readBasicTag(parser, "title");
    167                 case TAG_PUBLISHED:
    168                     return readBasicTag(parser, "published");
    169                 case TAG_LINK:
    170                     return readAlternateLink(parser);
    171                 default:
    172                     throw new IllegalArgumentException("Unknown tag type: " + tagType);
    173             }
    174         }
    175 
    176         /**
    177          * Reads the body of a basic XML tag, which is guaranteed not to contain any nested elements.
    178          *
    179          * <p>You probably want to call readTag().
    180          *
    181          * @param parser Current parser object
    182          * @param tag XML element tag name to parse
    183          * @return Body of the specified tag
    184          * @throws java.io.IOException
    185          * @throws org.xmlpull.v1.XmlPullParserException
    186          */
    187         private String readBasicTag(XmlPullParser parser, String tag)
    188                 throws IOException, XmlPullParserException {
    189             parser.require(XmlPullParser.START_TAG, ns, tag);
    190             String result = readText(parser);
    191             parser.require(XmlPullParser.END_TAG, ns, tag);
    192             return result;
    193         }
    194 
    195         /**
    196          * Processes link tags in the feed.
    197          */
    198         private String readAlternateLink(XmlPullParser parser)
    199                 throws IOException, XmlPullParserException {
    200             String link = null;
    201             parser.require(XmlPullParser.START_TAG, ns, "link");
    202             String tag = parser.getName();
    203             String relType = parser.getAttributeValue(null, "rel");
    204             if (relType.equals("alternate")) {
    205                 link = parser.getAttributeValue(null, "href");
    206             }
    207             while (true) {
    208                 if (parser.nextTag() == XmlPullParser.END_TAG) break;
    209                 // Intentionally break; consumes any remaining sub-tags.
    210             }
    211             return link;
    212         }
    213 
    214         /**
    215          * For the tags title and summary, extracts their text values.
    216          */
    217         private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
    218             String result = null;
    219             if (parser.next() == XmlPullParser.TEXT) {
    220                 result = parser.getText();
    221                 parser.nextTag();
    222             }
    223             return result;
    224         }
    225 
    226         /**
    227          * Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e.,
    228          * if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it
    229          * finds the matching END_TAG (as indicated by the value of "depth" being 0).
    230          */
    231         private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
    232             if (parser.getEventType() != XmlPullParser.START_TAG) {
    233                 throw new IllegalStateException();
    234             }
    235             int depth = 1;
    236             while (depth != 0) {
    237                 switch (parser.next()) {
    238                     case XmlPullParser.END_TAG:
    239                         depth--;
    240                         break;
    241                     case XmlPullParser.START_TAG:
    242                         depth++;
    243                         break;
    244                 }
    245             }
    246         }
    247 
    248         /**
    249          * This class represents a single entry (post) in the XML feed.
    250          *
    251          * <p>It includes the data members "title," "link," and "summary."
    252          */
    253         public static class Entry {
    254             public final String id;
    255             public final String title;
    256             public final String link;
    257             public final long published;
    258 
    259             Entry(String id, String title, String link, long published) {
    260                 this.id = id;
    261                 this.title = title;
    262                 this.link = link;
    263                 this.published = published;
    264             }
    265         }
    266 
    267 }