/** * 2011 Foxykeep (http://datadroid.foxykeep.com) * <p> * Licensed under the Beerware License : <br /> * As long as you retain this notice you can do whatever you want with this stuff. If we meet some * day, and you think this stuff is worth it, you can buy me a beer in return */ package com.foxykeep.datadroidpoc.data.factory; import com.foxykeep.datadroid.exception.DataException; import com.foxykeep.datadroidpoc.data.model.RssFeed; import com.foxykeep.datadroidpoc.data.model.RssItem; import android.text.TextUtils; import android.util.Log; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import java.io.IOException; import java.io.StringReader; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Locale; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; /** * Factory used to parse the RSS feed. * * @author Foxykeep */ public final class RssFactory { private static final String TAG = RssFactory.class.getSimpleName(); private RssFactory() { // No public constructor } public static RssFeed parseResult(String wsResponse) throws DataException { RssHandler parser = new RssHandler(); try { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); xr.setContentHandler(parser); StringReader sr = new StringReader(wsResponse); InputSource is = new InputSource(sr); xr.parse(is); } catch (ParserConfigurationException e) { Log.e(TAG, "ParserConfigurationException", e); throw new DataException(e); } catch (SAXException e) { Log.e(TAG, "SAXException", e); throw new DataException(e); } catch (IOException e) { Log.e(TAG, "IOException", e); throw new DataException(e); } return parser.mRssFeed; } private static class RssHandler extends DefaultHandler { private static final String DATE_FORMAT_1 = "ccc',' dd MMM yyyy HH:mm:ss Z"; private static final String DATE_FORMAT_2 = "ccc',' dd MMM yyyy"; private static final SimpleDateFormat SIMPLE_DATE_FORMAT_1 = new SimpleDateFormat(DATE_FORMAT_1, Locale.US); private static final SimpleDateFormat SIMPLE_DATE_FORMAT_2 = new SimpleDateFormat(DATE_FORMAT_2, Locale.US); private StringBuilder mSb = new StringBuilder(); public RssFeed mRssFeed = new RssFeed(); private RssItem mCurrentRssItem = null; private boolean mIsInItem = false; private boolean mIsInImage = false; private ArrayList<String> mSkipDayList = new ArrayList<String>(); private ArrayList<String> mSkipHourList = new ArrayList<String>(); @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { mSb.setLength(0); if (TextUtils.isEmpty(namespaceURI)) { if (localName.equals("item")) { mIsInItem = true; mCurrentRssItem = new RssItem(); } else if (localName.equals("image")) { mIsInImage = true; mRssFeed.imageWidth = 31; // Default value from W3Schools mRssFeed.imageHeight = 88; // Default value from W3Schools } else if (localName.equals("guid")) { String isPermaLink = atts.getValue("isPermaLink"); mCurrentRssItem.isGuidPermaLink = isPermaLink != null && isPermaLink.equals("true"); } else if (localName.equals("source")) { mCurrentRssItem.sourceLink = atts.getValue("url"); } else if (localName.equals("enclosure")) { mCurrentRssItem.enclosureLink = atts.getValue("url"); mCurrentRssItem.enclosureSize = Integer.parseInt(atts.getValue("length")); mCurrentRssItem.enclosureMimeType = atts.getValue("type"); } } } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (TextUtils.isEmpty(namespaceURI)) { if (localName.equals("item")) { mRssFeed.rssItemList.add(mCurrentRssItem); mIsInItem = false; } else if (localName.equals("image")) { mIsInImage = false; } else if (localName.equals("title")) { if (mIsInImage) { mRssFeed.imageTitle = mSb.toString(); } else if (mIsInItem) { mCurrentRssItem.title = mSb.toString(); } else { mRssFeed.title = mSb.toString(); } } else if (localName.equals("link")) { if (mIsInImage) { mRssFeed.imageWebsiteLink = mSb.toString(); } else if (mIsInItem) { mCurrentRssItem.link = mSb.toString(); } else { mRssFeed.link = mSb.toString(); } } else if (localName.equals("description")) { if (mIsInImage) { mRssFeed.imageDescription = mSb.toString(); } else if (mIsInItem) { mCurrentRssItem.description = mSb.toString(); } else { mRssFeed.description = mSb.toString(); } } else if (localName.equals("category")) { if (mIsInItem) { mCurrentRssItem.categoryList.add(mSb.toString()); } else { mRssFeed.categoryList.add(mSb.toString()); } } else if (localName.equals("pubDate")) { if (mIsInItem) { mCurrentRssItem.pubDate = getMillisFromDate(mSb.toString()); } else { mRssFeed.pubDate = getMillisFromDate(mSb.toString()); } } else if (localName.equals("lastBuildDate")) { getMillisFromDate(mSb.toString()); } else if (localName.equals("language")) { mRssFeed.language = mSb.toString(); } else if (localName.equals("copyright")) { mRssFeed.copyright = mSb.toString(); } else if (localName.equals("generator")) { mRssFeed.generator = mSb.toString(); } else if (localName.equals("url")) { mRssFeed.imageUrl = mSb.toString(); } else if (localName.equals("width")) { mRssFeed.imageWidth = Integer.parseInt(mSb.toString()); } else if (localName.equals("height")) { mRssFeed.imageHeight = Integer.parseInt(mSb.toString()); } else if (localName.equals("managingEditor")) { mRssFeed.managingEditor = mSb.toString(); } else if (localName.equals("webMaster")) { mRssFeed.webmaster = mSb.toString(); } else if (localName.equals("author")) { mCurrentRssItem.author = mSb.toString(); } else if (localName.equals("guid")) { mCurrentRssItem.guid = mSb.toString(); } else if (localName.equals("encodedText")) { mCurrentRssItem.encodedContext = mSb.toString(); } else if (localName.equals("source")) { mCurrentRssItem.sourceText = mSb.toString(); } else if (localName.equals("day")) { mSkipDayList.add(mSb.toString().toLowerCase(Locale.US)); } else if (localName.equals("skipDays")) { int skipDayListSize = mSkipDayList.size(); mRssFeed.skipDayArray = new int[skipDayListSize]; for (int i = 0; i < skipDayListSize; i++) { final String day = mSkipDayList.get(i); if (day.equals("monday")) { mRssFeed.skipDayArray[i] = 0; } else if (day.equals("tuesday")) { mRssFeed.skipDayArray[i] = 1; } else if (day.equals("wednesday")) { mRssFeed.skipDayArray[i] = 2; } else if (day.equals("thrusday")) { mRssFeed.skipDayArray[i] = 3; } else if (day.equals("friday")) { mRssFeed.skipDayArray[i] = 4; } else if (day.equals("saturday")) { mRssFeed.skipDayArray[i] = 5; } else if (day.equals("sunday")) { mRssFeed.skipDayArray[i] = 6; } } } else if (localName.equals("hour")) { mSkipHourList.add(mSb.toString().toLowerCase(Locale.US)); } else if (localName.equals("skipHours")) { int skipHourListSize = mSkipHourList.size(); mRssFeed.skipHourArray = new int[skipHourListSize]; for (int i = 0; i < skipHourListSize; i++) { mRssFeed.skipHourArray[i] = Integer.parseInt(mSkipHourList.get(i)); } } else if (localName.equals("ttl")) { mRssFeed.ttl = Integer.parseInt(mSb.toString()); } } } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); mSb.append(ch, start, length); } /** * Method used to get the milliseconds corresponding to the 2 common formats for the date. * * @param date The given date. * @return The timestamp corresponding to the given date. * @throws SAXException Exception thrown if the given date doesn't follow one of the 2 * common formats. */ private long getMillisFromDate(String date) throws SAXException { long millis = -1; try { millis = SIMPLE_DATE_FORMAT_1.parse(date).getTime(); } catch (ParseException e) { // fall through } if (millis != -1) { return millis; } try { return SIMPLE_DATE_FORMAT_2.parse(date).getTime(); } catch (ParseException e) { throw new SAXException(e); } } } }