Java tutorial
// // Informa -- RSS Library for Java // Copyright (c) 2002 by Niko Schmuck // // Niko Schmuck // http://sourceforge.net/projects/informa // mailto:niko_schmuck@users.sourceforge.net // // This library is free software. // // You may redistribute it and/or modify it under the terms of the GNU // Lesser General Public License as published by the Free Software Foundation. // // Version 2.1 of the license should be included with this distribution in // the file LICENSE. If the license is not included with this distribution, // you may find a copy at the FSF web site at 'www.gnu.org' or 'www.fsf.org', // or you may write to the Free Software Foundation, 675 Mass Ave, Cambridge, // MA 02139 USA. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied waranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // $Id: Channel.java,v 1.37 2006/12/04 23:43:27 italobb Exp $ package de.nava.informa.impl.hibernate; import de.nava.informa.core.*; import de.nava.informa.utils.XmlPathUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdom.Element; import java.net.MalformedURLException; import java.net.URL; import java.util.*; /** * Hibernate implementation of the ChannelIF interface. * * @author Niko Schmuck (niko@nava.de) */ public class Channel implements ChannelIF { private static final Log LOG = LogFactory.getLog(Channel.class); private static final long serialVersionUID = 7579933431503905957L; private long id = -1; private String title; private String description; private URL location; private URL site; private String creator; private String publisher; private String language; private ChannelFormat format; private Set<ItemIF> items; // Items are ordered in RDF RSS 1.0. private Set<ChannelGroup> groups; private CloudIF cloud; private ImageIF image; private TextInputIF textInput; private String copyright; private Collection<CategoryIF> categories; private Date lastUpdated; private Date lastBuild; private Date pubDate; private String rating; private String generator; private String docs; private int ttl = -1; private Element channelElement; // RSS 1.0 Syndication Module values private ChannelUpdatePeriod updatePeriod = ChannelUpdatePeriod.UPDATE_DAILY; private int updateFrequency = 1; private Date updateBase; private transient Collection<ChannelObserverIF> observers; public Channel() { this(null, null, null); } public Channel(String title) { this(null, title, null); } public Channel(String title, String location) { this(null, title, location); } public Channel(String title, URL location) { this(null, title, location.toExternalForm()); } public Channel(Element channelElement) { this(channelElement, "Unnamed channel"); } public Channel(Element channelElement, String title) { this(channelElement, title, null); } public Channel(Element channelElement, String title, String location) { this.channelElement = channelElement; this.title = title; setLocationString(location); this.items = new HashSet<ItemIF>(); this.categories = new ArrayList<CategoryIF>(); this.observers = new ArrayList<ChannelObserverIF>(); this.groups = new HashSet<ChannelGroup>(); this.format = ChannelFormat.UNKNOWN_CHANNEL_FORMAT; this.lastUpdated = new Date(); } // -------------------------------------------------------------- // implementation of ChannelIF interface // -------------------------------------------------------------- /** * @return integer representation of identity. */ public long getId() { return id; } public void setId(long id) { this.id = id; } /** * @return title. */ public String getTitle() { return title; } public void setTitle(String aTitle) { this.title = aTitle; } /** * @return description. */ public String getDescription() { return description; } public void setDescription(String aDescription) { this.description = aDescription; } // We store the Location as a text string in the database, but as a URL in the memory based object. // As far as Hibernate is concerned this is a STRING property. However the getter and setter // convert to and from text for Informa. /** * @return location as a string. */ public String getLocationString() { return (location == null) ? null : location.toString(); } public void setLocationString(String loc) { if (loc == null || loc.trim().length() == 0) { location = null; } else { try { location = new URL(loc); } catch (MalformedURLException e) { LOG.warn("Tried to set location to invalid URL", e); location = null; } } } /** * @return the location */ public URL getLocation() { return location; } /** * @param location the location to set */ public void setLocation(URL location) { this.location = location; } /** * @return URL of the site. */ public String getSiteString() { return (site == null) ? null : site.toString(); } public void setSiteString(String siteUrl) { if (siteUrl == null || siteUrl.trim().length() == 0) { site = null; } else { try { site = new URL(siteUrl); } catch (MalformedURLException e) { LOG.warn("Tried to set site to invalid URL", e); site = null; } } } public URL getSite() { return site; } public void setSite(URL site) { this.site = site; } /** * @return name of creator. */ public String getCreator() { return creator; } public void setCreator(String aCreator) { this.creator = aCreator; } /** * @return publisher. */ public String getPublisher() { return publisher; } public void setPublisher(String aPublisher) { this.publisher = aPublisher; } /** * @return language of channel. */ public String getLanguage() { return language; } public void setLanguage(String aLanguage) { this.language = aLanguage; } /** * @return format string. */ public String getFormatString() { return format.toString(); } public void setFormatString(String strFormat) { // TODO: this could be improved by a format resolver if (strFormat.equals(ChannelFormat.RSS_0_90.toString())) { format = ChannelFormat.RSS_0_90; } else if (strFormat.equals(ChannelFormat.RSS_0_91.toString())) { format = ChannelFormat.RSS_0_91; } else if (strFormat.equals(ChannelFormat.RSS_0_92.toString())) { format = ChannelFormat.RSS_0_92; } else if (strFormat.equals(ChannelFormat.RSS_0_93.toString())) { format = ChannelFormat.RSS_0_93; } else if (strFormat.equals(ChannelFormat.RSS_0_94.toString())) { format = ChannelFormat.RSS_0_94; } else if (strFormat.equals(ChannelFormat.RSS_1_0.toString())) { format = ChannelFormat.RSS_1_0; } else if (strFormat.equals(ChannelFormat.RSS_2_0.toString())) { format = ChannelFormat.RSS_2_0; } } public ChannelFormat getFormat() { return format; } public void setFormat(ChannelFormat aFormat) { this.format = aFormat; } /** * @return set of groups. */ public Set<ChannelGroup> getGroups() { return groups; } public void setGroups(Set<ChannelGroup> aGroups) { this.groups = aGroups; } /** * @return items of channel. */ public Set<ItemIF> getItems() { return items; } public void setItems(Set<ItemIF> anItems) { this.items = anItems; } public void addItem(ItemIF item) { items.add(item); item.setChannel(this); notifyObserversItemAdded(item); } public void removeItem(ItemIF item) { items.remove(item); } public ItemIF getItem(long itemId) { // TODO: improve performance // hibernate query cannot be used (not possible: no session object) // may be use transient map: items.get(new Long(id)); ItemIF theItem = null; for (ItemIF item : items) { if (item.getId() == itemId) { theItem = item; break; } } return theItem; } /** * @return image. */ public ImageIF getImage() { return image; } public void setImage(ImageIF anImage) { this.image = anImage; } /** * @return text input. */ public TextInputIF getTextInput() { return textInput; } public void setTextInput(TextInputIF aTextInput) { this.textInput = aTextInput; } /** * @return copyright note. */ public String getCopyright() { return copyright; } public void setCopyright(String aCopyright) { this.copyright = aCopyright; } /** * @return rating. */ public String getRating() { return rating; } public void setRating(String aRating) { this.rating = aRating; } /** * @return cloud. */ public CloudIF getCloud() { return cloud; } public void setCloud(CloudIF aCloud) { this.cloud = aCloud; } /** * @return generator. */ public String getGenerator() { return generator; } public void setGenerator(String aGenerator) { this.generator = aGenerator; } /** * @return docs. */ public String getDocs() { return docs; } public void setDocs(String aDocs) { this.docs = aDocs; } /** * RSS 2.0: ttl stands for time to live. It's a number of minutes that * indicates how long a channel can be cached before refreshing from * the source * * @return TTL value. */ public int getTtl() { return ttl; } public void setTtl(int aTtl) { this.ttl = aTtl; } /** * @return categories. */ public Collection getCategories() { return categories; } public void setCategories(Collection<CategoryIF> aCategories) { this.categories = aCategories; } public void addCategory(CategoryIF category) { categories.add(category); } public void removeCategory(CategoryIF category) { categories.remove(category); } /** * @return date of last update. */ public Date getLastUpdated() { return lastUpdated; } public void setLastUpdated(Date date) { this.lastUpdated = date; notifyObserversChannelUpdated(); } /** * RSS 0.91: The date-time the last time the content of the channel changed. * RSS 2.0: The last time the content of the channel changed. * * @return date of last builing. */ public Date getLastBuildDate() { return lastBuild; } public void setLastBuildDate(Date date) { this.lastBuild = date; } /** * @return publication date. */ public Date getPubDate() { return pubDate; } public void setPubDate(Date date) { this.pubDate = date; } // RSS 1.0 Syndication Module methods /** * @see de.nava.informa.core.ChannelIF#getUpdatePeriod() */ public ChannelUpdatePeriod getUpdatePeriod() { return updatePeriod; } public void setUpdatePeriod(ChannelUpdatePeriod anUpdatePeriod) { this.updatePeriod = anUpdatePeriod; } /** * Accesses data provided by the Syndication module (will apply only * to RSS 1.0+). Returns the number of times during the * <code>updatePeriod</code> that a feed should be updated * * @return The number of times during <code>updatePeriod</code> to update the * feed (the update frequency). */ public int getUpdateFrequency() { return updateFrequency; } public void setUpdateFrequency(int anUpdateFrequency) { this.updateFrequency = anUpdateFrequency; } /** * @return update base. */ public Date getUpdateBase() { return updateBase; } public void setUpdateBase(Date date) { this.updateBase = date; } public String getElementValue(final String path) { return XmlPathUtils.getElementValue(channelElement, path); } public String[] getElementValues(final String path, final String[] elements) { return XmlPathUtils.getElementValues(channelElement, path, elements); } public String getAttributeValue(final String path, final String attribute) { return XmlPathUtils.getAttributeValue(channelElement, path, attribute); } public String[] getAttributeValues(final String path, final String[] attributes) { return XmlPathUtils.getAttributeValues(channelElement, path, attributes); } // -------------------------------------------------------------- // implementation of ChannelObservableIF interface // -------------------------------------------------------------- public void addObserver(ChannelObserverIF o) { observers.add(o); } public void removeObserver(ChannelObserverIF o) { observers.remove(o); } // -------------------------------------------------------------- // overwrite default method implementation from Object // -------------------------------------------------------------- /** * Returns a string representation of the object. * * @return a string representation of the object. */ public String toString() { return "[Hibernate Channel (" + id + "): " + title + "(" + getItems().size() + ")( " + location + " )]"; } /** * Compare two Channels for equality. Implementing this method and hashCode * correctly is CRITICAL for Hibernate to function correctly. The semantic is * that two Channels arensidered the 'same' RSS Channel. They may at one * point in time have different values for different properties, but are * they the SAME Channel? This is a very subtle Hibernate point, WHICH I * AM 90% Sure I got right. In this case, two Channels are equal specifically * if their RSS URL are the same. In other words, even if the title is * different or the description is different, it's still the same Channel. * * @param o the reference object with which to compare. * @return <code>true</code> if this object is the same as the obj * argument; <code>false</code> otherwise. */ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ChannelIF)) return false; final ChannelIF channel = (ChannelIF) o; final String channelTitle = channel.getTitle(); if (title != null ? !title.equals(channelTitle) : channelTitle != null) return false; // Comparison of links uses synchronized code of Java-NET. // This may hurt multi-threaded applications. So, please think twice // before using direct comparison of links. final URL channelLocation = channel.getLocation(); if (location != null ? channelLocation == null || !location.toString().equalsIgnoreCase(channelLocation.toString()) : channelLocation != null) { return false; } final String channelDescription = channel.getDescription(); return !(description != null ? !description.equals(channelDescription) : channelDescription != null); } /** * Hashcode, like equals, is touchy and critical for proper functioning of * Hibernate. * * @return a hash code value for this object. */ public int hashCode() { return location.toString().hashCode(); } /** * Loops through and notifies each observer if a new item was * detected. * * @param newItem item added. */ public void notifyObserversItemAdded(ItemIF newItem) { for (ChannelObserverIF observer : observers) { observer.itemAdded(newItem); } } /** * Loops through and notifies each observer if a new item was * detected. */ public void notifyObserversChannelUpdated() { for (ChannelObserverIF observer : observers) { observer.channelRetrieved(this); } } }