ConverterForOPML10.java
/*
* ConverterForOPML10.java
*
* Created on April 25, 2006, 1:26 AM
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-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 com.rometools.opml.feed.synd.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.rometools.opml.feed.opml.Attribute;
import com.rometools.opml.feed.opml.Opml;
import com.rometools.opml.feed.opml.Outline;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.feed.synd.Converter;
import com.rometools.rome.feed.synd.SyndCategory;
import com.rometools.rome.feed.synd.SyndCategoryImpl;
import com.rometools.rome.feed.synd.SyndContent;
import com.rometools.rome.feed.synd.SyndContentImpl;
import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndEntryImpl;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndLink;
import com.rometools.rome.feed.synd.SyndLinkImpl;
import com.rometools.rome.feed.synd.SyndPerson;
import com.rometools.rome.feed.synd.SyndPersonImpl;
public class ConverterForOPML10 implements Converter {
private static final Logger LOG = LoggerFactory.getLogger(ConverterForOPML10.class);
public static final String URI_TREE = "urn:rome.tree";
public static final String URI_ATTRIBUTE = "urn:rome.attribute#";
protected void addOwner(final Opml opml, final SyndFeed syndFeed) {
if (opml.getOwnerEmail() != null || opml.getOwnerName() != null) {
final List<SyndPerson> authors = new ArrayList<SyndPerson>();
final SyndPerson person = new SyndPersonImpl();
person.setEmail(opml.getOwnerEmail());
person.setName(opml.getOwnerName());
authors.add(person);
syndFeed.setAuthors(authors);
}
}
/**
* Makes a deep copy/conversion of the values of a real feed into a SyndFeedImpl.
* <p>
* It assumes the given SyndFeedImpl has no properties set.
* <p>
*
* @param feed real feed to copy/convert.
* @param syndFeed the SyndFeedImpl that will contain the copied/converted values of the real feed.
*/
@Override
public void copyInto(final WireFeed feed, final SyndFeed syndFeed) {
final Opml opml = (Opml) feed;
syndFeed.setTitle(opml.getTitle());
addOwner(opml, syndFeed);
syndFeed.setPublishedDate(opml.getModified() != null ? opml.getModified() : opml.getCreated());
syndFeed.setFeedType(opml.getFeedType());
syndFeed.setModules(opml.getModules());
syndFeed.setFeedType(getType());
createEntries(new Stack<Integer>(), syndFeed.getEntries(), opml.getOutlines());
}
protected void createEntries(final Stack<Integer> context, final List<SyndEntry> allEntries, final List<Outline> outlines) {
final List<Outline> so = Collections.synchronizedList(outlines);
for (int i = 0; i < so.size(); i++) {
createEntry(context, allEntries, so.get(i));
}
}
protected SyndEntry createEntry(final Stack<Integer> context, final List<SyndEntry> allEntries, final Outline outline) {
final SyndEntry entry = new SyndEntryImpl();
if (outline.getType() != null && outline.getType().equals("rss")) {
entry.setLink(outline.getHtmlUrl() != null ? outline.getHtmlUrl() : outline.getXmlUrl());
} else if (outline.getType() != null && outline.getType().equals("link")) {
entry.setLink(outline.getUrl());
}
if (outline.getHtmlUrl() != null) {
final SyndLink link = new SyndLinkImpl();
link.setRel("alternate");
link.setType("text/html");
link.setHref(outline.getHtmlUrl());
entry.getLinks().add(link);
entry.setLink(outline.getHtmlUrl());
}
if (outline.getXmlUrl() != null && outline.getType() != null && outline.getType().equalsIgnoreCase("rss")) {
final SyndLink link = new SyndLinkImpl();
link.setRel("alternate");
link.setType("application/rss+xml");
link.setHref(outline.getXmlUrl());
entry.getLinks().add(link);
if (entry.getLink() == null) {
entry.setLink(outline.getXmlUrl());
}
}
if (outline.getXmlUrl() != null && outline.getType() != null && outline.getType().equalsIgnoreCase("atom")) {
final SyndLink link = new SyndLinkImpl();
link.setRel("alternate");
link.setType("application/atom+xml");
link.setHref(outline.getXmlUrl());
entry.getLinks().add(link);
if (entry.getLink() == null) {
entry.setLink(outline.getXmlUrl());
}
}
if (outline.getType() != null && outline.getType().equals("rss")) {
entry.setTitle(outline.getTitle());
} else {
entry.setTitle(outline.getText());
}
if (outline.getText() == null && entry.getTitle() != null) {
final SyndContent c = new SyndContentImpl();
c.setValue(outline.getText());
entry.setDescription(c);
}
entry.setPublishedDate(outline.getCreated());
final String nodeName = "node." + outline.hashCode();
final SyndCategory cat = new TreeCategoryImpl();
cat.setTaxonomyUri(URI_TREE);
cat.setName(nodeName);
entry.getCategories().add(cat);
if (!context.isEmpty()) {
final Integer parent = context.peek();
final SyndCategory pcat = new TreeCategoryImpl();
pcat.setTaxonomyUri(URI_TREE);
pcat.setName("parent." + parent);
entry.getCategories().add(pcat);
}
final List<Attribute> attributes = Collections.synchronizedList(outline.getAttributes());
for (int i = 0; i < attributes.size(); i++) {
final Attribute a = attributes.get(i);
final SyndCategory acat = new SyndCategoryImpl();
acat.setName(a.getValue());
acat.setTaxonomyUri(URI_ATTRIBUTE + a.getName());
entry.getCategories().add(acat);
}
entry.setModules(outline.getModules());
allEntries.add(entry);
context.push(Integer.valueOf(outline.hashCode()));
createEntries(context, allEntries, outline.getChildren());
context.pop();
return entry;
}
/**
* Creates real feed with a deep copy/conversion of the values of a SyndFeedImpl.
* <p>
*
* @param syndFeed SyndFeedImpl to copy/convert value from.
* @return a real feed with copied/converted values of the SyndFeedImpl.
*/
@Override
public WireFeed createRealFeed(final SyndFeed syndFeed) {
final List<SyndEntry> entries = Collections.synchronizedList(syndFeed.getEntries());
final HashMap<String, Outline> entriesByNode = new HashMap<String, Outline>();
// this will hold entries that we can't parent the first time.
final ArrayList<OutlineHolder> doAfterPass = new ArrayList<OutlineHolder>();
// this holds root level outlines;
final ArrayList<Outline> root = new ArrayList<Outline>();
for (int i = 0; i < entries.size(); i++) {
final SyndEntry entry = entries.get(i);
final Outline o = new Outline();
final List<SyndCategory> cats = Collections.synchronizedList(entry.getCategories());
boolean parentFound = false;
final StringBuffer category = new StringBuffer();
for (int j = 0; j < cats.size(); j++) {
final SyndCategory cat = cats.get(j);
if (cat.getTaxonomyUri() != null && cat.getTaxonomyUri().equals(URI_TREE)) {
final String nodeVal = cat.getName().substring(cat.getName().lastIndexOf("."), cat.getName().length());
if (cat.getName().startsWith("node.")) {
entriesByNode.put(nodeVal, o);
} else if (cat.getName().startsWith("parent.")) {
parentFound = true;
final Outline parent = entriesByNode.get(nodeVal);
if (parent != null) {
parent.getChildren().add(o);
} else {
doAfterPass.add(new OutlineHolder(o, nodeVal));
}
}
} else if (cat.getTaxonomyUri() != null && cat.getTaxonomyUri().startsWith(URI_ATTRIBUTE)) {
final String name = cat.getTaxonomyUri().substring(cat.getTaxonomyUri().indexOf("#") + 1, cat.getTaxonomyUri().length());
o.getAttributes().add(new Attribute(name, cat.getName()));
} else {
if (category.length() > 0) {
category.append(", ");
}
category.append(cat.getName());
}
}
if (!parentFound) {
root.add(o);
}
if (category.length() > 0) {
o.getAttributes().add(new Attribute("category", category.toString()));
}
final List<SyndLink> links = Collections.synchronizedList(entry.getLinks());
// final String entryLink = entry.getLink();
for (int j = 0; j < links.size(); j++) {
final SyndLink link = links.get(j);
// if(link.getHref().equals(entryLink)) {
if (link.getType() != null && link.getRel() != null && link.getRel().equals("alternate")
&& (link.getType().equals("application/rss+xml") || link.getType().equals("application/atom+xml"))) {
o.setType("rss");
if (o.getXmlUrl() == null) {
o.getAttributes().add(new Attribute("xmlUrl", link.getHref()));
}
} else if (link.getType() != null && link.getType().equals("text/html")) {
if (o.getHtmlUrl() == null) {
o.getAttributes().add(new Attribute("htmlUrl", link.getHref()));
}
} else {
o.setType(link.getType());
}
// }
}
if (o.getType() == null || o.getType().equals("link")) {
o.setText(entry.getTitle());
} else {
o.setTitle(entry.getTitle());
}
if (o.getText() == null && entry.getDescription() != null) {
o.setText(entry.getDescription().getValue());
}
}
// Do back and parenting for things we missed.
for (int i = 0; i < doAfterPass.size(); i++) {
final OutlineHolder o = doAfterPass.get(i);
final Outline parent = entriesByNode.get(o.parent);
if (parent == null) {
root.add(o.outline);
LOG.warn("Unable to find parent node: {}", o.parent);
} else {
parent.getChildren().add(o.outline);
}
}
final Opml opml = new Opml();
opml.setFeedType(getType());
opml.setCreated(syndFeed.getPublishedDate());
opml.setTitle(syndFeed.getTitle());
final List<SyndPerson> authors = Collections.synchronizedList(syndFeed.getAuthors());
for (int i = 0; i < authors.size(); i++) {
final SyndPerson p = authors.get(i);
if (syndFeed.getAuthor() == null || syndFeed.getAuthor().equals(p.getName())) {
opml.setOwnerName(p.getName());
opml.setOwnerEmail(p.getEmail());
opml.setOwnerId(p.getUri());
}
}
opml.setOutlines(root);
return opml;
}
/**
* Returns the type (version) of the real feed this converter handles.
*
* @return the real feed type.
* @see WireFeed for details on the format of this string.
*/
@Override
public String getType() {
return "opml_1.0";
}
private static class OutlineHolder {
private final Outline outline;
private final String parent;
public OutlineHolder(final Outline outline, final String parent) {
this.outline = outline;
this.parent = parent;
}
}
}