/* * RssItunesFeed.java * * Copyright (C) 2007-2008 Tommi Laukkanen * Copyright (C) 2007-2008 Irving Bunton * http://www.substanceofcode.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ // Expand to define itunes define //#define DNOITUNES // Expand to define logging define //#define DNOLOGGING package com.substanceofcode.rssreader.businessentities; import com.substanceofcode.utils.Base64; import com.substanceofcode.utils.StringUtil; import java.io.UnsupportedEncodingException; import java.util.*; import com.substanceofcode.rssreader.presentation.RssReaderMIDlet; import com.substanceofcode.utils.CauseException; import com.substanceofcode.utils.CauseMemoryException; //#ifdef DLOGGING import net.sf.jlogmicro.util.logging.Logger; import net.sf.jlogmicro.util.logging.Level; //#endif /** * RssItunesFeed class contains one RSS Itunes feed's properties. * Properties include name and subtitle and summary. * * @author Irving Bunton */ public class RssItunesFeed extends RssFeed{ // Make max summary same as max description (actual max is 50K) public static int MAX_SUMMARY = 500; // Beginning of data that has 0 itunes info. // Number of Itunes info private static int NBR_ITUNES_FEED_INFO = 8; public static int INAME_OFFSET= NBR_ITUNES_FEED_INFO + NAME_OFFSET; public static int IDATE_OFFSET = NBR_ITUNES_FEED_INFO + DATE_OFFSET; private static String EMPTY_ITUNES_FEED_INFO = "|||||||"; //#ifdef DLOGGING private Logger logger = Logger.getLogger("RssItunesFeed"); //#endif //#ifdef DLOGGING private boolean fineLoggable = logger.isLoggable(Level.FINE); private boolean finestLoggable = logger.isLoggable(Level.FINEST); //#endif // Value that shows that the first item (and those following may // contain ITunes items (or all may not contain any, but they // can later be modified to contain them). private static int INT_ITUNES_INDICATOR = NBR_ITUNES_FEED_INFO; protected boolean m_itunes = false; protected String m_title = ""; protected String m_description = ""; protected String m_language = ""; // The RSS feed language protected String m_author = ""; // The RSS feed author protected String m_subtitle = ""; // The RSS feed subtitle protected String m_summary = ""; // The RSS feed summary protected byte m_explicit = RssItunesItem.BNO_EXPLICIT; // The RSS feed explicit /** Creates a new instance of RSSBookmark */ public RssItunesFeed(){ super(); } /** Creates a new instance of RSSBookmark */ public RssItunesFeed(String name, String url, String username, String password){ super(name, url, username, password); } /** Creates a new instance of RSSBookmark */ public RssItunesFeed(String name, String url, String username, String password, Date upddate, String link, Date date, int category, boolean itunes, String title, String description, String language, String author, String subtitle, String summary, byte explicit) { super(name, url, username, password, upddate, link, date, category); if (itunes) { modifyItunes(itunes, title, description, language, author, subtitle, summary, explicit); } } /** Modify fields for Itunes. */ public void modifyItunes(boolean itunes, String title, String description, String language, String author, String subtitle, String summary, byte explicit) { //#ifdef DITUNES this.m_itunes = itunes; this.m_title = title; this.m_description = description; this.m_language = language; this.m_author = author; this.m_subtitle = subtitle; this.m_summary = summary; this.m_explicit = explicit; //#endif } /** Create feed from an existing feed. **/ public RssItunesFeed(RssFeed feed) { super(feed); try { if (feed instanceof RssItunesFeed) { RssItunesFeed itfeed = (RssItunesFeed)feed; this.m_itunes = itfeed.m_itunes; if (this.m_itunes) { this.m_language = itfeed.m_language; this.m_author = itfeed.m_author; this.m_subtitle = itfeed.m_subtitle; this.m_summary = itfeed.m_summary; this.m_explicit = itfeed.m_explicit; } } else { final Vector cvitems = feed.getItems(); if (cvitems.size() > 0) { RssItem[] citems = new RssItem[cvitems.size()]; cvitems.copyInto(citems); Vector nvitems = new Vector(); for (int ic = 0; ic < citems.length; ic++) { final RssItem item = citems[ic]; if (item instanceof RssItunesItem) { nvitems.addElement((RssItunesItem)item); } else { nvitems.addElement(new RssItunesItem(item)); } } m_items = nvitems; } } } catch(Throwable e) { System.err.println("RssItunesFeed contructor : " + e.toString()); e.printStackTrace(); } } /** Deserialize the object Creates a new instance of RssItunesFeed from store string **/ public static RssItunesFeed deserialize(boolean encoded, String storeString) throws CauseMemoryException, CauseException { try { boolean hasPipe = (storeString.indexOf(CONE) >= 0); String[] nodes = StringUtil.split( storeString, '|' ); RssItunesFeed feed = new RssItunesFeed(); feed.init(hasPipe, encoded, nodes); return feed; } catch (CauseMemoryException e) { throw e; } catch (CauseException e) { throw e; } catch(Exception e) { CauseException ce = new CauseException( "Internal error during deserialize", e); //#ifdef DLOGGING Logger logger = Logger.getLogger("RssItunesFeed"); logger.severe(ce.getMessage(), e); //#endif System.err.println(ce.getMessage() + " " + e.toString()); e.printStackTrace(); throw ce; } catch(OutOfMemoryError e) { CauseMemoryException ce = new CauseMemoryException( "Out of memory error during deserialize", e); //#ifdef DLOGGING Logger logger = Logger.getLogger("RssItunesFeed"); logger.severe(ce.getMessage(), e); //#endif System.err.println(ce.getMessage() + " " + e.toString()); e.printStackTrace(); throw ce; } catch(Throwable e) { CauseException ce = new CauseException( "Internal error during deserialize", e); //#ifdef DLOGGING Logger logger = Logger.getLogger("RssItunesFeed"); logger.severe(ce.getMessage(), e); //#endif System.err.println(ce.getMessage() + " " + e.toString()); e.printStackTrace(); throw ce; } } public void init(boolean hasPipe, boolean encoded, String [] nodes) throws CauseMemoryException, CauseException { try { /* Node count should be 6 * m_itunes | m_title | m_description | m_language | m_author | m_subtitle | m_summary | m_explicit | rss feed fields */ //#ifdef DLOGGING if (finestLoggable) {logger.finest("nodes.length=" + nodes.length);} //#endif //#ifdef DITUNES int ITUNES = 0; m_itunes = nodes[ITUNES].equals("1"); if (m_itunes) { int TITLE = 1; m_title = nodes[TITLE]; if (hasPipe) { m_title = m_title.replace(CONE, '|'); } int DESCRIPTION = 2; m_description = nodes[DESCRIPTION]; if (hasPipe) { m_description = m_description.replace(CONE, '|'); } int LANGUAGE = 3; m_language = nodes[LANGUAGE]; int AUTHOR = 4; m_author = nodes[AUTHOR]; if (hasPipe) { m_author = m_author.replace(CONE, '|'); } int SUBTITLE = 5; m_subtitle = nodes[SUBTITLE]; if (hasPipe) { m_subtitle = m_subtitle.replace(CONE, '|'); } int SUMMARY = 6; m_summary = nodes[SUMMARY]; if (hasPipe) { m_summary = m_summary.replace(CONE, '|'); } int EXPLICIT = 7; final String explicit = nodes[EXPLICIT]; if (explicit.length() > 0) { m_explicit = (byte)Integer.parseInt(explicit); } else { m_explicit = RssItunesItem.BNO_EXPLICIT; } } //#endif super.init(false, NBR_ITUNES_FEED_INFO, true, hasPipe, encoded, nodes); } catch (CauseMemoryException e) { throw e; } catch (CauseException e) { throw e; } catch(Exception e) { CauseException ce = new CauseException( "Internal error during initialize of RssItunesFeed", e); //#ifdef DLOGGING logger.severe(ce.getMessage(), e); //#endif System.err.println(ce.getMessage() + " " + e.toString()); e.printStackTrace(); throw ce; } catch(OutOfMemoryError e) { CauseMemoryException ce = new CauseMemoryException( "Out of memory error during initialize of RssItunesFeed", e); //#ifdef DLOGGING logger.severe(ce.getMessage(), e); //#endif System.err.println(ce.getMessage() + " " + e.toString()); e.printStackTrace(); throw ce; } catch(Throwable e) { CauseException ce = new CauseException( "Internal error during initialize of RssItunesFeed", e); //#ifdef DLOGGING logger.severe(ce.getMessage(), e); //#endif System.err.println(ce.getMessage() + " " + e.toString()); e.printStackTrace(); throw ce; } } /** Return record store string */ public String getStoreString(final boolean saveHdr, final boolean serializeItems, final boolean encoded){ //#ifdef DLOGGING if (finestLoggable) {logger.finest("saveHdr,serializeItems,encoded=" + saveHdr + "," + serializeItems + "," + encoded);} //#endif String itunesInfo; // //#ifdef DITUNES // if (saveHdr && m_itunes) { // String title = ""; // String description = ""; // String author = ""; // String subtitle = ""; // String summary = ""; // if (m_itunes) { // title = m_title.replace('|', CONE); // description = m_description.replace('|', CONE); // author = m_author.replace('|', CONE); // subtitle = m_subtitle.replace('|', CONE); // summary = m_summary.replace('|', CONE); // } // itunesInfo = (m_itunes ? "1" : "") + "|" + title + "|" + // description + "|" + m_language + "|" + // author + "|" + subtitle + "|" + summary + "|" + // ((m_explicit == RssItunesItem.BNO_EXPLICIT) ? "" : // Integer.toString((int)m_explicit)); // } else { // //#endif // itunesInfo = EMPTY_ITUNES_FEED_INFO; // //#ifdef DITUNES // } // //#endif if (!(saveHdr && m_itunes)) itunesInfo = EMPTY_ITUNES_FEED_INFO; else { String title = ""; String description = ""; String author = ""; String subtitle = ""; String summary = ""; if (m_itunes) { title = m_title.replace('|', CONE); description = m_description.replace('|', CONE); author = m_author.replace('|', CONE); subtitle = m_subtitle.replace('|', CONE); summary = m_summary.replace('|', CONE); } itunesInfo = (m_itunes ? "1" : "") + "|" + title + "|" + description + "|" + m_language + "|" + author + "|" + subtitle + "|" + summary + "|" + ((m_explicit == RssItunesItem.BNO_EXPLICIT) ? "" : Integer.toString((int)m_explicit)); } String storeString = itunesInfo + "|" + super.getStoreString(saveHdr, serializeItems, encoded); return storeString; } /** Return record store string info */ final public static RssStoreInfo getStoreStringInfo( final boolean serializeItems, final boolean encoded, final String storeString, boolean sencoded){ // TODO handle serialize items //#ifdef DLOGGING Logger logger = Logger.getLogger("RssItunesFeed"); logger.finest("serializeItems,encoded,sencoded=" + serializeItems + "," + encoded + "," + sencoded); //#endif //#ifdef DTEST long encodeTime = 0L; long splitTime = 0L; long joinTime = 0L; long lngStart = System.currentTimeMillis(); //#endif String[] nodes = StringUtil.split( storeString, '|' ); //#ifdef DTEST splitTime += System.currentTimeMillis() - lngStart; //#endif final String name = nodes[NBR_ITUNES_FEED_INFO]; if (encoded == sencoded) { //#ifdef DTEST if (true) return new RssStoreInfo(name, storeString, encodeTime, splitTime, joinTime); //#else return new RssStoreInfo(name, storeString); //#endif } final int itemsOff = NBR_ITUNES_FEED_INFO + RssFeed.ITUNES_ITEMS; if (itemsOff >= nodes.length) { //#ifdef DTEST if (true) return new RssStoreInfo(name, storeString, encodeTime, splitTime, joinTime); //#else return new RssStoreInfo(name, storeString); //#endif } if (!sencoded) { nodes[itemsOff] = nodes[itemsOff].replace(CTHREE, '|'); } //#ifdef DTEST lngStart = System.currentTimeMillis(); //#endif final String[] itemArrayData = StringUtil.split(nodes[itemsOff], (sencoded ? '.' : RssFeed.CTWO)); //#ifdef DTEST splitTime += System.currentTimeMillis() - lngStart; //#endif final String[] nitemArrayData = new String[itemArrayData.length]; if (sencoded) { for (int ic = 0; ic < itemArrayData.length; ic++) { // Base64 decode Base64 b64 = new Base64(); byte[] decodedData = b64.decode(itemArrayData[ic]); try { nitemArrayData[ic] = new String( decodedData, "UTF-8" ); } catch (UnsupportedEncodingException e) { nitemArrayData[ic] = new String( decodedData ); } } nodes[itemsOff] = StringUtil.join(nitemArrayData, RssFeed.CTWO, 0); nodes[itemsOff] = nodes[itemsOff].replace('|', RssFeed.CTHREE); } else { //#ifdef DTEST lngStart = System.currentTimeMillis(); //#endif for (int ic = 0; ic < itemArrayData.length; ic++) { // Base64 decode Base64 b64 = new Base64(); String data; try { nitemArrayData[ic] = b64.encode( itemArrayData[ic].getBytes("UTF-8") ); } catch (UnsupportedEncodingException e) { nitemArrayData[ic] = b64.encode( itemArrayData[ic].getBytes() ); } } //#ifdef DTEST encodeTime += System.currentTimeMillis() - lngStart; lngStart = System.currentTimeMillis(); //#endif nodes[itemsOff] = StringUtil.join(nitemArrayData, '.', 0); //#ifdef DTEST joinTime += System.currentTimeMillis() - lngStart; //#endif } //#ifdef DTEST RssStoreInfo rsi; rsi = new RssStoreInfo(name, StringUtil.join( nodes, '|', 0), encodeTime, splitTime, joinTime); //#else rsi = new RssStoreInfo(name, StringUtil.join( nodes, '|', 0)); //#endif return rsi; } /** Return record store string info */ final public static RssShortItem[] getShortItems( final RssReaderMIDlet midlet, final String storeString) { //#ifdef DLOGGING Logger logger = Logger.getLogger("RssItunesFeed"); logger.finest("storeString=" + storeString); //#endif String[] nodes = StringUtil.split( storeString, '|' ); final int itemsOff = NBR_ITUNES_FEED_INFO + RssFeed.ITUNES_ITEMS; if ((itemsOff >= nodes.length) || (nodes[itemsOff].length() == 0)) { return new RssShortItem[0]; } final String[] itemArrayData = StringUtil.split(nodes[itemsOff], RssFeed.CTWO); final RssShortItem[] sitems = new RssShortItem[itemArrayData.length - 1]; //#ifdef DITUNES String sfdate = null; Date fdate = null; //#endif for (int ic = 0; ic < sitems.length; ic++) { String[] sparts = StringUtil.split(itemArrayData[ic], CTHREE); String title = sparts[RssItunesItem.ITITLE_OFFSET].replace( RssItem.CONE, '|'); if (title.length() == 0) { title = midlet.getItemDescription( sparts[RssItunesItem.IDESC_OFFSET].replace( RssItem.CONE, '|')); } final String sunreadItem = sparts[RssItunesItem.IUNREAD_ITEM]; boolean unreadItem = false; if (sunreadItem.equals("1")) { unreadItem = true; } String sdate = sparts[RssItunesItem.IDATE_OFFSET]; Date date = null; if (sdate.length() > 0) { date = new Date(Long.parseLong(sdate, 16)); //#ifdef DITUNES } else { if (sfdate == null) { sfdate = nodes[IDATE_OFFSET]; if (sfdate.length() > 0) { fdate = new Date(Long.parseLong(sfdate, 16)); } } if (fdate != null) { date = fdate; } //#endif } sitems[ic] = new RssShortItem(title, date, unreadItem, ic); } return sitems; } /** Copy feed to an existing feed. **/ public void copyTo(RssItunesFeed toFeed) { super.copyTo(toFeed); //#ifdef DITUNES toFeed.m_title = this.m_title; toFeed.m_description = this.m_description; toFeed.m_language = this.m_language; toFeed.m_author = this.m_author; toFeed.m_subtitle = this.m_subtitle; toFeed.m_summary = this.m_summary; toFeed.m_explicit = this.m_explicit; //#endif } /** Compare feed to an existing feed. **/ public boolean equals(RssItunesFeed feed) { if (!super.equals(feed)) { return false; } if (feed.m_itunes != m_itunes) { //#ifdef DLOGGING if (finestLoggable) {logger.finest("unequal feed.m_itunes,this=" + feed.m_itunes + "," + m_itunes);} //#endif return false; } if (!feed.m_language.equals(this.m_language)) { //#ifdef DLOGGING if (finestLoggable) {logger.finest("unequal feed.m_language,this=" + feed.m_language + "," + m_language);} //#endif return false; } if (!feed.m_author.equals(this.m_author)) { //#ifdef DLOGGING if (finestLoggable) {logger.finest("unequal feed.m_author,this=" + feed.m_author + "," + m_author);} //#endif return false; } if (!feed.m_summary.equals(this.m_summary)) { //#ifdef DLOGGING if (finestLoggable) {logger.finest("unequal feed.m_summary,this=" + feed.m_summary + "," + m_summary);} //#endif return false; } if (feed.m_explicit != m_explicit) { //#ifdef DLOGGING if (finestLoggable) {logger.finest("unequal feed.m_explicit,this=" + feed.m_explicit + "," + m_explicit);} //#endif return false; } return true; } public void setCategory(int category) { this.m_category = category; } public int getCategory() { return (m_category); } /** Write record as a string */ public String toString(){ String storeString = m_itunes + "|" + m_title + "|" + m_description + "|" + m_language + "|" + m_author + "|" + m_subtitle + "|" + m_summary + "|" + ((m_explicit == RssItunesItem.BNO_EXPLICIT) ? "" : Integer.toString((int)m_explicit)) + "|" + super.toString(); return storeString; } public void setDescription(String description) { this.m_description = description; } public String getDescription() { return (m_description); } public void setLanguage(String language) { this.m_language = language; } public String getLanguage() { return (m_language); } public void setAuthor(String author) { this.m_author = author; } public String getAuthor() { return (m_author); } public void setSubtitle(String subtitle) { this.m_subtitle = subtitle; } public String getSubtitle() { return (m_subtitle); } public void setSummary(String summary) { this.m_summary = summary; } public String getSummary() { return (m_summary); } public void setExplicit(int explicit) { this.m_explicit = (byte)explicit; } public String getExplicit() { switch (m_explicit) { case (byte)0: return "no"; case (byte)1: return "clean"; case (byte)2: return "yes"; default: return RssItunesItem.UNSPECIFIED; } } public void setItunes(boolean itunes) { this.m_itunes = itunes; } public boolean isItunes() { boolean result=false; // If itunes, allow it. If not itunes, make it seem that it is not. //#ifdef DITUNES result=m_itunes; //#else //@ return (false); //#endif return result; } public void setTitle(String title) { this.m_title = title; } public String getTitle() { return (m_title); } }