/*
* RssFeed.java
*
* Copyright (C) 2005-2006 Tommi Laukkanen
* 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 logging define
//#define DNOLOGGING
// Expand to define itunes define
//#define DNOITUNES
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.utils.CauseException;
import com.substanceofcode.utils.CauseMemoryException;
//#ifdef DLOGGING
import net.sf.jlogmicro.util.logging.Logger;
import net.sf.jlogmicro.util.logging.Level;
//#endif
/**
* RssFeed class contains one RSS feed's properties.
* Properties include name and URL to RSS feed.
*
* @author Tommi Laukkanen
*/
public class RssFeed{
public static int NAME_OFFSET = 0;
public static final int DATE_OFFSET = 6;
protected static final char CONE = (char)1;
protected static final char [] CBONE = {CONE};
public static String STR_ONE = new String(CBONE);
protected static final char CTWO = (char)2;
protected static final char [] CBTWO = {CTWO};
protected static final char CTHREE = (char)3;
protected static final char [] CBTHREE = {CTHREE};
public static String STR_TWO = new String(CBTWO);
public static int ITUNES_ITEMS = 8;
//#ifdef DLOGGING
private Logger logger = Logger.getLogger("RssFeed");
//#endif
//#ifdef DLOGGING
private boolean fineLoggable = logger.isLoggable(Level.FINE);
private boolean finestLoggable = logger.isLoggable(Level.FINEST);
//#endif
protected String m_url = "";
protected String m_name = "";
protected String m_username = "";
protected String m_password = "";
protected Date m_upddate = null;
protected Date m_date = null;
protected String m_link = ""; // The RSS feed link
protected int m_category = -1; // The RSS feed category
protected Vector m_items = new Vector(); // The RSS item vector
/** Creates a new instance of RSSBookmark */
public RssFeed(){
}
/** Creates a new instance of RSSBookmark */
public RssFeed(String name, String url, String username, String password){
m_name = name;
m_url = url;
m_username = username;
m_password = password;
}
/** Creates a new instance of RSSBookmark */
public RssFeed(String name, String url, String username, String password,
Date upddate,
String link,
Date date,
int category) {
m_name = name;
m_url = url;
m_username = username;
m_password = password;
m_upddate = upddate;
//#ifdef DITUNES
m_link = link;
m_date = date;
//#endif
m_category = category;
}
/** Create feed from an existing feed. **/
public RssFeed(RssFeed feed) {
this.m_url = feed.m_url;
this.m_name = feed.m_name;
this.m_username = feed.m_username;
this.m_password = feed.m_password;
this.m_upddate = feed.m_upddate;
//#ifdef DITUNES
this.m_link = feed.m_link;
this.m_date = feed.m_date;
//#endif
this.m_category = feed.m_category;
this.m_items = new Vector();
int ilen = feed.m_items.size();
RssItem [] rItems = new RssItem[ilen];
feed.m_items.copyInto(rItems);
for (int ic = 0; ic < ilen; ic++) {
m_items.addElement(rItems[ic]);
}
}
/** Creates a new instance of RSSBookmark with record store string
firstSettings - True if the data was from the settings which were
compatible with the first version and several after.
**/
public RssFeed(boolean firstSettings, boolean encoded, String storeString){
try {
String[] nodes = StringUtil.split( storeString, '|' );
init(firstSettings, 0, false, false, encoded, nodes);
} catch(Exception e) {
System.err.println("Error while rssfeed initialization : " + e.toString());
e.printStackTrace();
}
}
/**
Initialize fields in the class from data.
startIndex - Starting index in nodes of RssItem
iTunesCapable - True if the data can support Itunes (but may not
actually have Itunes data) or may not be turned
on by the user. So, the serializaion/deserialization
will account for iTunes fields except if not
enabled, the will have empty values.
hasPipe - True if the data has a pipe in at least one item
nodes - (elements in an array).
**/
protected void init(boolean firstSettings,
int startIndex, boolean iTunesCapable,
boolean hasPipe, boolean encoded,
String [] nodes)
throws CauseMemoryException, CauseException {
try {
/* Node count should be 8
* name | url | username | password | upddate | link | date |
* category | items
*/
int NAME = NAME_OFFSET;
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("startIndex,nodes.length,first nodes=" + startIndex + "," + nodes.length + "|" + nodes[ startIndex + NAME ]);}
//#endif
m_name = nodes[ startIndex + NAME ];
int URL = 1;
m_url = nodes[ startIndex + URL ];
int USERNAME = 2;
m_username = nodes[ startIndex + USERNAME ];
if (iTunesCapable && hasPipe) {
m_username = m_username.replace(CONE, '|');
}
int PASSWORD = 3;
m_password = nodes[ startIndex + PASSWORD ];
if (iTunesCapable) {
// Dencode so that password is not in regular lettters.
Base64 b64 = new Base64();
byte[] decodedPassword = b64.decode(m_password);
try {
m_password = new String( decodedPassword , "UTF-8" );
} catch (UnsupportedEncodingException e) {
m_password = new String( decodedPassword );
}
if (hasPipe) {
m_password = m_password.replace(CONE, '|');
}
}
m_items = new Vector();
if (firstSettings) {
// Given the bugs with the first settings, we do not
// retrieve the items so that we can restore them
// without the bugs.
return;
}
int ITEMS = (iTunesCapable ? ITUNES_ITEMS : 5);
int UPDDATE = 4;
String dateString = nodes[startIndex + UPDDATE];
if(dateString.length()>0) {
if (iTunesCapable) {
m_upddate = new Date(Long.parseLong(dateString, 16));
} else {
m_upddate = new Date(Long.parseLong(dateString));
}
}
if (iTunesCapable && hasPipe) {
m_name = m_name.replace(CONE, '|');
} else {
if (!iTunesCapable) {
// Dencode for better UTF-8 and to allow '|' in the name.
// For iTunesCapable, replace | with (char)1
Base64 b64 = new Base64();
byte[] decodedName = b64.decode(m_name);
try {
m_name = new String( decodedName , "UTF-8" );
} catch (UnsupportedEncodingException e) {
m_name = new String( decodedName );
}
}
}
if (iTunesCapable) {
//#ifdef DITUNES
int LINK = 5;
if (nodes[startIndex + LINK].length() > 0) {
m_link = nodes[startIndex + LINK];
}
int DATE = DATE_OFFSET;
String fdateString = nodes[startIndex + DATE];
if (fdateString.length() > 0) {
m_date = new Date(Long.parseLong(fdateString, 16));
}
//#endif
int CATEGORY = 7;
if (nodes[startIndex + CATEGORY].length() > 0) {
m_category = Integer.parseInt(nodes[startIndex + CATEGORY]);
}
}
if (firstSettings) {
// Given the bugs with the first settings, we do not
// retrieve the items so that we can restore them
// without the bugs.
return;
}
String itemArrayData = nodes[ startIndex + ITEMS ];
// Deserialize itemss
if (!encoded) {
itemArrayData = itemArrayData.replace(CTHREE, '|');
}
String[] serializedItems = StringUtil.split(itemArrayData,
(encoded ? '.' : CTWO));
for(int itemIndex=0; itemIndex<serializedItems.length; itemIndex++) {
String serializedItem = serializedItems[ itemIndex ];
if(serializedItem.length()>0) {
RssItem rssItem;
if (iTunesCapable) {
if (encoded) {
rssItem = RssItunesItem.deserialize(
serializedItem );
} else {
rssItem = RssItunesItem.unencodedDeserialize(
serializedItem );
}
} else {
rssItem = RssItem.deserialize( serializedItem );
}
if (rssItem != null) {
m_items.addElement( rssItem );
}
}
}
} catch (CauseMemoryException e) {
throw e;
} catch (CauseException e) {
throw e;
} catch(Exception e) {
CauseException ce = new CauseException(
"Internal error during initialize of RssFeed", 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 RssFeed", 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 RssFeed", e);
//#ifdef DLOGGING
logger.severe(ce.getMessage(), e);
//#endif
System.err.println(ce.getMessage() + " " + e.toString());
e.printStackTrace();
throw ce;
}
}
/** Return bookmark's name */
public String getName(){
return m_name;
}
public void setName(String m_name) {
this.m_name = m_name;
}
/** Return bookmark's URL */
public String getUrl(){
return m_url;
}
public void setUrl(String url) {
this.m_url = url;
}
/** Return bookmark's username for basic authentication */
public String getUsername(){
return m_username;
}
/** Return bookmark's password for basic authentication */
public String getPassword(){
return m_password;
}
/** Return record store string for feed only. This excludes items which
are put into store string by RssItunesFeed. */
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
StringBuffer serializedItems = new StringBuffer();
if( serializeItems ) {
int ilen = m_items.size();
RssItunesItem [] ritems = new RssItunesItem[ilen];
m_items.copyInto(ritems);
for(int itemIndex=0; itemIndex<ilen;itemIndex++) {
RssItunesItem rssItem = (RssItunesItem)ritems[itemIndex];
if (encoded) {
serializedItems.append(rssItem.serialize());
serializedItems.append(".");
} else {
serializedItems.append(
rssItem.unencodedSerialize().replace('|', CTHREE));
serializedItems.append(CTWO);
}
}
}
String name = m_name.replace('|', CONE);
String username = m_username.replace('|' , CONE);
String password = m_password.replace('|' , CONE);
String encodedPassword;
// Encode password to make reading password difficult
Base64 b64 = new Base64();
try {
encodedPassword = b64.encode( m_password.getBytes("UTF-8") );
} catch (UnsupportedEncodingException e) {
encodedPassword = b64.encode( m_password.getBytes() );
}
String exInfoString;
String updString;
if (saveHdr) {
String dateString;
if(m_date==null){
dateString = "";
} else {
// We use base 16 (hex) for the date so that we can save some
// space for toString.
dateString = Long.toString( m_date.getTime(), 16 );
}
if(m_upddate==null){
updString = "";
} else {
// We use base 16 (hex) for the update date so that we can save some
// space for toString.
updString = Long.toString( m_upddate.getTime(), 16 );
}
exInfoString = dateString + "|" +
((m_category == -1) ? "" : Integer.toString(m_category));
} else {
updString = "";
exInfoString = "|";
}
String storeString = name + "|" +
m_url + "|" + username + "|" +
encodedPassword + "|" + updString + "|" +
m_link + "|" + exInfoString + "|" + serializedItems;
return storeString;
}
/** Copy feed to an existing feed. **/
public void copyTo(RssFeed toFeed) {
toFeed.m_url = this.m_url;
toFeed.m_name = this.m_name;
toFeed.m_username = this.m_username;
toFeed.m_password = this.m_password;
toFeed.m_upddate = this.m_upddate;
//#ifdef DITUNES
toFeed.m_link = this.m_link;
toFeed.m_date = this.m_date;
//#endif
toFeed.m_category = this.m_category;
toFeed.m_items = new Vector();
int ilen = m_items.size();
RssItem [] ritems = new RssItem[ilen];
m_items.copyInto(ritems);
for (int ic = 0; ic < ilen; ic++) {
toFeed.m_items.addElement(ritems[ic]);
}
}
/** Compare feed to an existing feed. **/
public boolean equals(RssFeed feed) {
if (!feed.m_url.equals(this.m_url)) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal feed.m_url,this=" + feed.m_url + "," + m_url);}
//#endif
return false;
}
if (!feed.m_name.equals(this.m_name)) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal feed.m_name,this=" + feed.m_name + "," + m_name);}
//#endif
return false;
}
if (!feed.m_username.equals(this.m_username)) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal feed.m_password,this=" + feed.m_password + "," + m_password);}
//#endif
return false;
}
if (!feed.m_password.equals(this.m_password)) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal feed.m_password,this=" + feed.m_password + "," + m_password);}
//#endif
return false;
}
if ((feed.m_date == null) && (this.m_date == null)) {
} else if ((feed.m_date != null) && (this.m_date != null)) {
if (feed.m_date.equals(this.m_date)) {
} else {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal dates=" + feed.m_date + "," + m_date);}
//#endif
return false;
}
} else {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal dates=" + feed.m_date + "," + m_date);}
//#endif
return false;
}
if (!feed.m_link.equals(m_link)) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal feed.m_link,this=" + feed.m_link + "," + m_link);}
//#endif
return false;
}
if ((feed.m_date == null) && (this.m_date == null)) {
} else if ((feed.m_date != null) && (this.m_date != null)) {
if (feed.m_date.equals(this.m_date)) {
} else {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal dates=" + feed.m_date + "," + m_date);}
//#endif
return false;
}
} else {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal dates=" + feed.m_date + "," + m_date);}
//#endif
return false;
}
if (feed.m_category != this.m_category) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal feed.m_category,this=" + feed.m_category + "," + m_category);}
//#endif
return false;
}
int flen = feed.m_items.size();
int ilen = m_items.size();
if (flen != ilen) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal size feed,this=" + flen + "," + ilen);}
//#endif
return false;
}
RssItem [] ritems = new RssItem[ilen];
m_items.copyInto(ritems);
RssItem [] fitems = new RssItem[flen];
feed.m_items.copyInto(fitems);
for (int ic = 0; ic < ilen; ic++) {
if (!fitems[ic].equals(ritems[ic])) {
//#ifdef DLOGGING
if (finestLoggable) {logger.finest("unequal ic,fitems[ic],ritems[ic]" + ic + "," + fitems[ic] + "," + ritems[ic]);}
//#endif
return false;
}
}
return true;
}
/** Return RSS feed items */
public Vector getItems() {
return m_items;
}
/** Set items */
public void setItems(Vector items) {
m_items = items;
}
public void setUpddate(Date upddate) {
this.m_upddate = upddate;
}
public Date getUpddate() {
return (m_upddate);
}
public void setCategory(int category) {
this.m_category = category;
}
public int getCategory() {
return (m_category);
}
/** Write record as a string */
public String toString(){
StringBuffer serializedItems = new StringBuffer();
int ilen = m_items.size();
RssItunesItem [] ritems = new RssItunesItem[ilen];
m_items.copyInto(ritems);
for(int itemIndex=0; itemIndex<ilen;itemIndex++) {
RssItunesItem rssItem = (RssItunesItem)ritems[itemIndex];
serializedItems.append(rssItem.toString());
serializedItems.append(".");
}
String dateString;
if(m_date==null){
dateString = "";
} else {
// We use base 16 (hex) for the date so that we can save some
// space for toString.
dateString = Long.toString( m_date.getTime(), 16 );
}
String updString;
if(m_upddate==null){
updString = "";
} else {
// We use base 16 (hex) for the update date so that we can save some
// space for toString.
updString = Long.toString( m_upddate.getTime(), 16 );
}
String storeString = m_name + "|" + m_url + "|" + m_username + "|" +
m_password + "|" +
updString + "|" + m_link + "|" + m_category + "|" +
dateString + "|" + serializedItems.toString();
return storeString;
}
public void setLink(String link) {
//#ifdef DITUNES
if (!link.equals(m_url)) {
this.m_link = link;
}
//#endif
}
public String getLink() {
return (m_link);
}
public void setDate(Date date) {
//#ifdef DITUNES
this.m_date = date;
//#endif
}
public Date getDate() {
return (m_date);
}
}