/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package edu.tufts.vue.rss;
import tufts.vue.*;
import tufts.Util;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
import com.sun.syndication.io.*;
import com.sun.syndication.feed.*;
import com.sun.syndication.feed.synd.*;
/**
* @version $Revision: 1.21 $ / $Date: 2010-02-03 19:25:37 $ / $Author: mike $
*
* @author akumar03
* @author Daniel J. Heller
* @author Scott Fraize
*/
public class RSSDataSource extends BrowseDataSource
{
private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(RSSDataSource.class);
private List<SyndEntry> mItems;
public RSSDataSource() {
//Log.debug("created empty RSS feed");
}
public RSSDataSource(String displayName, String address) throws DataSourceException {
this.setDisplayName(displayName);
this.setAddress(address);
}
@Override
public String getTypeName() {
return "RSS Feed";
}
@Override
public int getCount() {
return mItems == null ? -1 : mItems.size();
}
/** apparently, some RSS Feeds (e.g., craigslist) will fail with "TOO MANY REDIRECTS" if there isn't SOME cookie sent,
* so this always at least returns "none" */
@Override public String getAuthenticationCookie() {
final String cookie = super.getAuthenticationCookie();
return cookie == null ? "none" : cookie;
}
@Override
protected JComponent buildResourceViewer() {
return loadViewer();
}
private JComponent loadViewer() {
Log.debug("loadContentAndBuildViewer...");
tufts.vue.VUE.diagPush("RSSLoad");
JComponent viewer = null;
try {
viewer = loadContentAndBuildViewer();
} finally {
tufts.vue.VUE.diagPop();
}
return viewer;
}
private JComponent loadContentAndBuildViewer()
{
Log.debug("loadContentAndBuildViewer...");
//final WireFeedInput feedBuilder = new WireFeedInput();
final URLConnection conn = openAddress();
final Map<String,List<String>> headers = conn.getHeaderFields();
final SyndFeedInput feedBuilder = new SyndFeedInput();
final SyndFeed _feed;
try {
//feed = feedBuilder.build(new InputStreamReader(conn.getInputStream()));
// XmlReader does magic to try and best handle the input charset-encoding:
XmlReader charsetEncodingReader = new XmlReader(conn);
_feed = feedBuilder.build(charsetEncodingReader);
} catch (java.io.IOException io) {
throw new DataSourceException(null, io);
} catch (FeedException fe) {
throw new DataSourceException(null, fe);
}
final SyndFeed feed = _feed;
if (TEST_DEBUG) {
dumpFeed(feed);
return null;
}
feed.setFeedType("atom_1.0");
final WireFeed wireFeed = _feed.createWireFeed();
this.mItems = feed.getEntries();
Log.debug("WIRE FEED: " + tufts.Util.tag(wireFeed));
final List<Resource> resources = new ArrayList<Resource>();
Log.debug("item count: " + mItems.size());
if (mItems.size() == 0)
throw new DataSourceException("[Empty RSS feed]");
final Resource fr = Resource.instance(feed.getLink());
fr.reset();
fr.setClientType(Resource.DIRECTORY);
//fr.setDataType("rss");
fr.setTitle(feed.getTitle());
if (DEBUG.Enabled) {
if (DEBUG.WORK) {
fr.setProperty("~WIREMARK", wireFeed.getForeignMarkup());
fr.setProperty("~WIREMOD", wireFeed.getModules());
fr.setProperty("zFEED", feed);
fr.addPropertyIfContent("feed-supported-types", feed.getSupportedFeedTypes());
}
fr.addPropertyIfContent("feed-uri", feed.getUri());
fr.addPropertyIfContent("feed-image", feed.getImage());
fr.addPropertyIfContent("feed-type", feed.getEncoding());
fr.addPropertyIfContent("feed-language", feed.getLanguage());
fr.addPropertyIfContent("feed-encoding", feed.getEncoding());
for (Map.Entry<String,List<String>> e : headers.entrySet()) {
Object value = e.getValue();
if (value instanceof Collection && ((Collection)value).size() == 1)
value = ((Collection)value).toArray()[0];
if (e.getKey() == null)
fr.setProperty("HTTP-response", value);
else
fr.setProperty("HTTP:" + e.getKey(), value);
}
}
fr.addPropertyIfContent("Title", feed.getTitle());
fr.addPropertyIfContent("Copyright", feed.getCopyright());
fr.addPropertyIfContent("Author", feed.getAuthor());
final String desc = feed.getDescription().trim();
if ("This file is an XML representation of some issues".equalsIgnoreCase(desc))
// ignore JIRA standard meaningless comment
((URLResource)fr).setSpec(getAddress());
else
fr.addPropertyIfContent("Description", desc);
//r.addPropertyIfContent("Copyright", feed.getTitle());
resources.add(fr);
for (SyndEntry item : mItems) {
Resource r = null;
try {
r = createRSSResource(item);
} catch (Throwable t) {
Log.error("creating resource from: " + item + " in feed " + feed, t);
}
if (r == null) {
Log.warn("failed to create resource from rss feed item, skipping; link=" + item.getLink());
continue;
}
resources.add(r);
}
VueDragTree fileTree = new VueDragTree(resources, this.getDisplayName());
fileTree.setRootVisible(true);
fileTree.setShowsRootHandles(true);
fileTree.expandRow(0);
fileTree.setRootVisible(false);
fileTree.setName(getClass().getSimpleName() + ": " + getAddress());
return fileTree;
}
private Resource createRSSResource(final SyndEntry item)
{
// DH: getUri did not work for Reuters (and Atom feeds in general?),
// switched to getLink() instead (see below)
final Resource r = Resource.instance(item.getLink());
r.setTitle(item.getTitle());
r.addPropertyIfContent("Title", item.getTitle());
r.addPropertyIfContent("Author", item.getAuthor());
r.addPropertyIfContent("Published", item.getPublishedDate());
r.addPropertyIfContent("Updated", item.getUpdatedDate());
r.addPropertyIfContent("URI", item.getUri());
final SyndContent content = item.getDescription();
r.addPropertyIfContent("Description", content.getValue());
if (!DEBUG.Enabled)
return r;
r.addPropertyIfContent("~0rss-content-type", content.getType());
r.addPropertyIfContent("~0rss-content-mode", content.getMode());
r.addPropertyIfContent("~Authors", item.getAuthors());
r.addPropertyIfContent("~Contributors", item.getContributors());
Object fm = item.getForeignMarkup();
if (fm instanceof Collection) {
String fmt = tufts.Util.tag(fm);
for (Object o : ((Collection)fm)) {
fmt += '\n';
fmt += tufts.Util.tags(o);
}
fm = fmt;
}
r.addPropertyIfContent("~ForeignMarkup", fm);
r.addPropertyIfContent("~Categories", item.getCategories());
r.addPropertyIfContent("~Contents", item.getContents());
r.addPropertyIfContent("~Enclosures", item.getEnclosures());
r.addPropertyIfContent("~Modules", item.getModules());
r.addPropertyIfContent("~Description", content);
return r;
}
private static void dumpFeed(SyndFeed _feed)
{
Log.info("supported-types: " + _feed.getSupportedFeedTypes());
if (true) {
WireFeed feed = _feed.createWireFeed();
//feed.setFeedType("atom_1.0");
Log.info("WIRE-OUT types: " + WireFeedOutput.getSupportedFeedTypes());
Log.info("FEED: " + Util.tags(feed));
WireFeedOutput feedOut = new WireFeedOutput();
try {
feedOut.output(feed, new PrintWriter(System.out));
//Log.info("AS-STRING:" + feedOut.outputString(feed));
} catch (Throwable t) {
Log.error(t, t);
}
} else {
SyndFeed feed = _feed;
feed.setFeedType("atom_1.0");
//feed.setFeedType("rss_2.0");
//feed.setFeedType("rss_1.0");
SyndFeedOutput feedOut = new SyndFeedOutput();
try {
feedOut.output(feed, new PrintWriter(System.out));
} catch (Throwable t) {
Log.error(t, t);
}
}
}
private static boolean TEST_DEBUG = false;
public static void main(String[] args)
{
TEST_DEBUG = true;
DEBUG.Enabled = true;
DEBUG.DR = true;
DEBUG.RESOURCE = true;
DEBUG.DATA = true;
tufts.vue.VUE.init(args);
RSSDataSource ds = new RSSDataSource("test", args[0]);
ds.loadContentAndBuildViewer();
}
}
// Resource res = null;
// try {
// //res = new URLResource(new URL(entry.getUri()));
// //System.out.println("trying to create rss item resource entry is: " + entry);
// String link = entry.getLink();
// //System.out.println("trying to create rss resource - link:" + link);
// URL url = new URL(link);
// //System.out.println("trying to create rss resource - url:" + url);
// /*link = *///java.net.URLDecoder.decode(link,"UTF-8");
// //res = new URLResource(url);
// res = Resource.getFactory().get(url);
// }
// catch(MalformedURLException mue) {
// System.out.println("Malformed URL Exception while creating RSS feed resource: " + mue);
// }