Merge rome-opml into rome

This commit is contained in:
mishako 2016-02-13 18:49:07 +01:00
commit ff58d2ac64
37 changed files with 3439 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/.classpath
/.project
/.settings
/target

6
README.md Normal file
View file

@ -0,0 +1,6 @@
rome
====
ROME is a set of RSS and Atom Utilities for Java. It makes it easy to work in Java with most syndication formats: RSS 0.90, RSS 0.91 Netscape, RSS 0.91 Userland, RSS 0.92, RSS 0.93, RSS 0.94, RSS 1.0, RSS 2.0, Atom 0.3, Atom 1.0
More Information: http://rometools.github.io/rome-opml/

91
pom.xml Normal file
View file

@ -0,0 +1,91 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.rometools</groupId>
<artifactId>rome-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
</parent>
<artifactId>rome-opml</artifactId>
<version>1.6.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>rome-opml</name>
<description>Support for OPML 1 and OPML 2 in ROME</description>
<url>http://rometools.github.io/rome-opml/</url>
<scm>
<connection>scm:git:ssh://github.com/rometools/rome-opml.git</connection>
<developerConnection>scm:git:ssh://git@github.com/rometools/rome-opml.git</developerConnection>
<url>https://github.com/rometools/rome-opml</url>
</scm>
<developers>
<developer>
<id>kebernet</id>
<email>kebernet@gmail.com</email>
<name>Robert Cooper</name>
</developer>
</developers>
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-publish-plugin</artifactId>
<configuration>
<scmBranch>gh-pages</scmBranch>
<pubScmUrl>${project.scm.developerConnection}</pubScmUrl>
<content>${project.build.directory}/site</content>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.rometools</groupId>
<artifactId>rome</artifactId>
<version>1.6.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</dependency>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,87 @@
/*
* Attribute.java
*
* Created on April 24, 2006, 11:11 PM
*
* 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.opml;
import java.io.Serializable;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ToStringBean;
/**
* This is a simple name-value pair attribute for outlines.
*
* @author <a href="mailto:cooper@screaming-penguin.com">Robert "kebernet" Cooper</a>
*/
public class Attribute implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String value;
/**
* Creates a new instance of Attribute.
*
* @param name name of the attribute.
* @param value value of the attribute.
*/
public Attribute(final String name, final String value) {
if (name == null || value == null) {
throw new NullPointerException("Name and value are required.");
}
setName(name);
setValue(value);
}
public void setName(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setValue(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public Object clone() {
return new Attribute(name, value);
}
@Override
public boolean equals(final Object obj) {
return new EqualsBean(Attribute.class, this).beanEquals(obj);
}
@Override
public int hashCode() {
return new EqualsBean(Attribute.class, this).beanHashCode();
}
@Override
public String toString() {
return new ToStringBean(Attribute.class, this).toString();
}
}

View file

@ -0,0 +1,322 @@
/*
* Opml.java
*
* Created on April 24, 2006, 11:00 PM
*
* 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.opml;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.rometools.rome.feed.WireFeed;
/**
* This class represents the root of an OPML 1/2 feed and contains the elements that may appear in the &lt;head&gt; tag
* of the feed.
*
* @author <a href="mailto:cooper@screaming-penguin.com"> Robert "kebernet" Cooper</a>
*/
public class Opml extends WireFeed {
private static final long serialVersionUID = 1L;
private Date created;
private Date modified;
private Integer verticalScrollState;
private Integer windowBottom;
private Integer windowLeft;
private Integer windowRight;
private Integer windowTop;
private List<Outline> outlines;
private String docs;
private String ownerEmail;
private String ownerId;
private String ownerName;
private String title;
private int[] expansionState;
/**
* <dateCreated> is a date-time, indicating when the document was created.
*
* @param created date-time, indicating when the document was created.
*/
public void setCreated(final Date created) {
this.created = created;
}
/**
* &lt;dateCreated&gt; is a date-time, indicating when the document was created.
*
* @return date-time, indicating when the document was created.
*/
public Date getCreated() {
return created;
}
/**
* (OPML 2) &lt;docs&gt; is the http address of documentation for the format used in the OPML file. It's probably a
* pointer to <a href="http://www.opml.org/spec2">this page</a> for people who might stumble across the file on a
* web server 25 years from now and wonder what it is.
*
* @param docs http address of documentation for the format used
*/
public void setDocs(final String docs) {
this.docs = docs;
}
/**
* (OPML 2) &lt;docs&gt; is the http address of documentation for the format used in the OPML file. It's probably a
* pointer to <a href="http://www.opml.org/spec2">this page</a> for people who might stumble across the file on a
* web server 25 years from now and wonder what it is.
*
* @return http address of documentation for the format used
*/
public String getDocs() {
return docs;
}
/**
* &lt;expansionState&gt;is a comma-separated list of line numbers that are expanded. The line numbers in the list
* tell you which headlines to expand. The order is important. For each element in the list, X, starting at the
* first summit, navigate flatdown X times and expand. Repeat for each element in the list.
*
* @param expansionState int array containing expanded elements.
*/
public void setExpansionState(final int[] expansionState) {
this.expansionState = expansionState;
}
/**
* &lt;expansionState&gt; is a comma-separated list of line numbers that are expanded. The line numbers in the list
* tell you which headlines to expand. The order is important. For each element in the list, X, starting at the
* first summit, navigate flatdown X times and expand. Repeat for each element in the list.
*
* @return int array containing expanded elements.
*/
public int[] getExpansionState() {
return expansionState;
}
/**
* &lt;dateModified&gt; is a date-time, indicating when the document was last modified.
*
* @param modified date-time, indicating when the document was last modified.
*/
public void setModified(final Date modified) {
this.modified = modified;
}
/**
* &lt;dateModified&gt; is a date-time, indicating when the document was last modified.
*
* @return date-time, indicating when the document was last modified.
*/
public Date getModified() {
return modified;
}
/**
* Root level Outline object that should appear in the &lt;body&gt;
*
* @param outlines Root level Outline object that should appear in the &lt;body&gt;
*/
public void setOutlines(final List<Outline> outlines) {
this.outlines = outlines;
}
/**
* Root level Outline object that should appear in the &lt;body&gt;
*
* @return Root level Outline object that should appear in the &lt;body&gt;
*/
public List<Outline> getOutlines() {
if (outlines == null) {
outlines = new ArrayList<Outline>();
}
return outlines;
}
/**
* &lt;ownerEmail&gt; is a string, the email address of the owner of the document.
*
* @param ownerEmail the email address of the owner of the document.
*/
public void setOwnerEmail(final String ownerEmail) {
this.ownerEmail = ownerEmail;
}
/**
* &lt;ownerEmail&gt; is a string, the email address of the owner of the document.
*
* @return the email address of the owner of the document.
*/
public String getOwnerEmail() {
return ownerEmail;
}
/**
* (OPML 2) &lt;ownerId&gt; is the http address of a web page that contains <strike>an HTML</strike> a form that
* allows a human reader to communicate with the author of the document via email or other means.
*
* @param ownerId http address of a web page that contains <strike>an HTML</strike> a form that allows a human
* reader to communicate with the author of the document via email or other means.
*/
public void setOwnerId(final String ownerId) {
this.ownerId = ownerId;
}
/**
* (OPML 2) &lt;ownerId&gt; is the http address of a web page that contains <strike>an HTML</strike> a form that
* allows a human reader to communicate with the author of the document via email or other means.
*
* @return http address of a web page that contains <strike>an HTML</strike> a form that allows a human reader to
* communicate with the author of the document via email or other means.
*/
public String getOwnerId() {
return ownerId;
}
/**
* &lt;ownerName&gt; is a string, the owner of the document.
*
* @param ownerName the owner of the document.
*/
public void setOwnerName(final String ownerName) {
this.ownerName = ownerName;
}
/**
* &lt;ownerName&gt; is a string, the owner of the document.
*
* @return the owner of the document.
*/
public String getOwnerName() {
return ownerName;
}
/**
* &lt;title&gt; is the title of the document.
*
* @param title title of the document.
*/
public void setTitle(final String title) {
this.title = title;
}
/**
* &lt;title&gt; is the title of the document.
*
* @return title of the document.
*/
public String getTitle() {
return title;
}
/**
* &lt;vertScrollState&gt; is a number, saying which line of the outline is displayed on the top line of the window.
* This number is calculated with the expansion state already applied.
*
* @param verticalScrollState which line of the outline is displayed on the top line of the window.
*/
public void setVerticalScrollState(final Integer verticalScrollState) {
this.verticalScrollState = verticalScrollState;
}
/**
* &lt;vertScrollState&gt; is a number, saying which line of the outline is displayed on the top line of the window.
* This number is calculated with the expansion state already applied.
*
* @return which line of the outline is displayed on the top line of the window. This number is calculated with the
* expansion state already applied.
*/
public Integer getVerticalScrollState() {
return verticalScrollState;
}
/**
* &lt;windowBottom&gt; is a number, the pixel location of the bottom edge of the window.
*
* @param windowBottom the pixel location of the bottom edge of the window.
*/
public void setWindowBottom(final Integer windowBottom) {
this.windowBottom = windowBottom;
}
/**
* &lt;windowBottom&gt; is a number, the pixel location of the bottom edge of the window.
*
* @return the pixel location of the bottom edge of the window.
*/
public Integer getWindowBottom() {
return windowBottom;
}
/**
* &lt;windowLeft&gt; is a number, the pixel location of the left edge of the window.
*
* @param windowLeft the pixel location of the left edge of the window.
*/
public void setWindowLeft(final Integer windowLeft) {
this.windowLeft = windowLeft;
}
/**
* &lt;windowLeft&gt; is a number, the pixel location of the left edge of the window.
*
* @return the pixel location of the left edge of the window.
*/
public Integer getWindowLeft() {
return windowLeft;
}
/**
* &lt;windowRight&gt; is a number, the pixel location of the right edge of the window.
*
* @param windowRight the pixel location of the right edge of the window.
*/
public void setWindowRight(final Integer windowRight) {
this.windowRight = windowRight;
}
/**
* &lt;windowRight&gt; is a number, the pixel location of the right edge of the window.
*
* @return the pixel location of the right edge of the window.
*/
public Integer getWindowRight() {
return windowRight;
}
/**
* &lt;windowTop&gt; is a number, the pixel location of the top edge of the window.
*
* @param windowTop the pixel location of the top edge of the window.
*/
public void setWindowTop(final Integer windowTop) {
this.windowTop = windowTop;
}
/**
* &lt;windowTop&gt; is a number, the pixel location of the top edge of the window.
*
* @return the pixel location of the top edge of the window.
*/
public Integer getWindowTop() {
return windowTop;
}
}

View file

@ -0,0 +1,367 @@
/*
* Outline.java
*
* Created on April 24, 2006, 11:04 PM
*
* 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.opml;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ToStringBean;
import com.rometools.rome.feed.module.Module;
/**
* This class represents an OPML outline element.
*
* @author <a href="mailto:cooper@screaming-penguin.com">Robert "kebernet" Cooper</a>
*/
public class Outline implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private Date created;
private List<Attribute> attributes;
private List<String> categories;
private List<Outline> children;
private List<Module> modules;
private String text;
private String title;
private String type;
private boolean breakpoint;
private boolean comment;
public Outline() {
}
/**
* Creates a new outline with the specified type and text values.
*
* @param type type attribute value/
* @param text text attribute value
*/
public Outline(final String type, final String text) {
setType(type);
setText(text);
}
/**
* Creates an outline with the given title, xmlUrl and htmlUrl. This is traditionally used for aggregator feed lists
* and will get a type of "rss".
*
* @param title Title of the entry.
* @param xmlUrl link to XML file.
* @param htmlUrl link to html page.
*/
public Outline(final String title, final URL xmlUrl, final URL htmlUrl) {
super();
setType("rss");
setTitle(title);
setAttributes(new ArrayList<Attribute>());
if (xmlUrl != null) {
getAttributes().add(new Attribute("xmlUrl", xmlUrl.toString()));
}
if (htmlUrl != null) {
getAttributes().add(new Attribute("htmlUrl", htmlUrl.toString()));
}
}
/**
* List of attributes on this outline excluding the "common types" for the specification.
*
* @param attributes List of attributes on this outline.
*/
public void setAttributes(final List<Attribute> attributes) {
this.attributes = attributes;
}
/**
* List of attributes on this outline excluding the "common types" for the specification.
*
* @return List of attributes on this outline.
*/
public List<Attribute> getAttributes() {
if (attributes == null) {
attributes = new ArrayList<Attribute>();
}
return attributes;
}
/**
* isBreakpoint is a string, either "true" or "false", indicating whether a breakpoint is set on this outline. This
* attribute is mainly necessary for outlines used to edit scripts. If it's not present, the value is false.
*
* @param breakpoint whether a breakpoint is set on this outline.
*/
public void setBreakpoint(final boolean breakpoint) {
this.breakpoint = breakpoint;
}
/**
* isBreakpoint is a string, either "true" or "false", indicating whether a breakpoint is set on this outline. This
* attribute is mainly necessary for outlines used to edit scripts. If it's not present, the value is false.
*
* @return whether a breakpoint is set on this outline
*/
public boolean isBreakpoint() {
return breakpoint;
}
/**
* (OPML 2) A List of Strings indicating values in the category attribute.
*
* @param categories (OPML 2) A List of Strings indicating values in the category attribute.
*/
public void setCategories(final List<String> categories) {
this.categories = categories;
}
/**
* (OPML 2) A List of Strings indicating values in the category attribute.
*
* @return (OPML 2) A List of Strings indicating values in the category attribute.
*/
public List<String> getCategories() {
if (categories == null) {
categories = new ArrayList<String>();
}
return categories;
}
/**
* A list of sub-outlines for this entry.
*
* @param children A list of sub-outlines for this entry.
*/
public void setChildren(final List<Outline> children) {
this.children = children;
}
/**
* A list of sub-outlines for this entry.
*
* @return A list of sub-outlines for this entry.
*/
public List<Outline> getChildren() {
if (children == null) {
children = new ArrayList<Outline>();
}
return children;
}
/**
* isComment is a string, either "true" or "false", indicating whether the outline is commented or not. By
* convention if an outline is commented, all subordinate outlines are considered to also be commented. If it's not
* present, the value is false.
*
* @param comment whether the outline is commented
*/
public void setComment(final boolean comment) {
this.comment = comment;
}
/**
* isComment is a string, either "true" or "false", indicating whether the outline is commented or not. By
* convention if an outline is commented, all subordinate outlines are considered to also be commented. If it's not
* present, the value is false.
*
* @return whether the outline is commented
*/
public boolean isComment() {
return comment;
}
/**
* (OPML 2) created is the date-time that the outline node was created.
*
* @param created date-time that the outline node was created.
*/
public void setCreated(final Date created) {
this.created = created;
}
/**
* (OPML 2) created is the date-time that the outline node was created.
*
* @return date-time that the outline node was created.
*/
public Date getCreated() {
return created;
}
/**
* A convenience method to return the value of the url attribute.
*
* @return value of the htmlUrl attribute.
*/
public String getUrl() {
return getAttributeValue("url");
}
/**
* A convenience method to return the value of the htmlUrl attribute.
*
* @return value of the htmlUrl attribute.
*/
public String getHtmlUrl() {
return getAttributeValue("htmlUrl");
}
public void setModules(final List<Module> modules) {
this.modules = modules;
}
public List<Module> getModules() {
if (modules == null) {
modules = new ArrayList<Module>();
}
return modules;
}
/**
* The "text" attribute of the outline.
*
* @param text The "text" attribute of the outline.
*/
public void setText(final String text) {
this.text = text;
}
/**
* The "text" attribute of the outline.
*
* @return The "text" attribute of the outline.
*/
public String getText() {
return text;
}
/**
* The "title" attribute of the outline.
*
* @param title The "title" attribute of the outline.
*/
public void setTitle(final String title) {
this.title = title;
}
/**
* The "title" attribute of the outline.
*
* @return The "title" attribute of the outline.
*/
public String getTitle() {
return title;
}
/**
* The "type" attribute of the outline.
*
* @param type The "type" attribute of the outline.
*/
public void setType(final String type) {
this.type = type;
}
/**
* The "type" attribute of the outline.
*
* @return The "type" attribute of the outline.
*/
public String getType() {
return type;
}
/**
* A convenience method to return the value of the xmlUrl attribute.
*
* @return value of the xmlUrl attribute.
*/
public String getXmlUrl() {
return getAttributeValue("xmlUrl");
}
/**
* Returns the value of an attribute on the outline or null.
*
* @param name name of the attribute.
*/
public String getAttributeValue(final String name) {
final List<Attribute> attributes = Collections.synchronizedList(getAttributes());
for (int i = 0; i < attributes.size(); i++) {
final Attribute a = attributes.get(i);
if (a.getName() != null && a.getName().equals(name)) {
return a.getValue();
}
}
return null;
}
@Override
public Object clone() {
final Outline o = new Outline();
o.setBreakpoint(isBreakpoint());
o.setCategories(new ArrayList<String>(getCategories()));
o.setComment(isComment());
o.setCreated(created != null ? (Date) created.clone() : null);
o.setModules(new ArrayList<Module>(getModules()));
o.setText(getText());
o.setTitle(getTitle());
o.setType(getType());
final ArrayList<Outline> children = new ArrayList<Outline>();
for (int i = 0; i < getChildren().size(); i++) {
children.add((Outline) this.children.get(i).clone());
}
o.setChildren(children);
final ArrayList<Attribute> attributes = new ArrayList<Attribute>();
for (int i = 0; i < getAttributes().size(); i++) {
attributes.add((Attribute) this.attributes.get(i).clone());
}
o.setAttributes(attributes);
return o;
}
@Override
public boolean equals(final Object obj) {
return new EqualsBean(Outline.class, this).beanEquals(obj);
}
@Override
public int hashCode() {
return new EqualsBean(Outline.class, this).beanHashCode();
}
@Override
public String toString() {
return new ToStringBean(Outline.class, this).toString();
}
}

View file

@ -0,0 +1,47 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
</head>
<body>
This package represents the base objects for OPML support for ROME.
<p>
There are three classes here that are relevant. <code>Opml</code>,
which represents the root document and <code>head</code> information,
<code>Outline</code> which represents a single node on an Opml
outline tree, and provides convenience methods for commonly used
attributes such as <code>xmlUrl</code>. Finally, the
<code>Attribute</code> class, which represents a specific attribute
on an Outline object. Since OPML supports free-form attribute
assignments, this is a very multi-purpose class.
</p>
<p>Sample Usage:
To use this parser, simply include the jar file in your classpath
as you are using ROME. Be sure it exists at the same level as ROME,
such that, if ROME is in the common classpath of an application server,
don't include this jar in your webapps WEB-INF/lib.
<code><pre>
WireFeedInput input = new WireFeedInput();
Opml feed = (Opml) input.build( new File("myOpml.xml") );
List&lt;Outline&gt; outlines = (List&lt;Outline&gt;) feed.getOutlines();
</pre></code>
</p>
<pre>
*
* 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.
</pre>
</body>
</html>

View file

@ -0,0 +1,349 @@
/*
* 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;
/**
* @author cooper
*/
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(new Integer(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;
}
}
}

View file

@ -0,0 +1,50 @@
package com.rometools.opml.feed.synd.impl;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.feed.synd.SyndFeed;
/**
* @author cooper
*/
public class ConverterForOPML20 extends ConverterForOPML10 {
/**
* Returns the type (version) of the real feed this converter handles.
* <p>
*
* @return the real feed type.
* @see WireFeed for details on the format of this string.
* <p>
*/
@Override
public String getType() {
return "opml_2.0";
}
/**
* 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) {
super.copyInto(feed, syndFeed);
}
/**
* 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) {
return super.createRealFeed(syndFeed);
}
}

View file

@ -0,0 +1,23 @@
package com.rometools.opml.feed.synd.impl;
import com.rometools.rome.feed.synd.SyndCategory;
import com.rometools.rome.feed.synd.SyndCategoryImpl;
/**
* @author cooper
*/
public class TreeCategoryImpl extends SyndCategoryImpl {
private static final long serialVersionUID = 1L;
@Override
public boolean equals(final Object o) {
final SyndCategory c = (SyndCategory) o;
if (c.getTaxonomyUri() != null && c.getTaxonomyUri().equals(getTaxonomyUri())) {
return true;
} else {
return false;
}
}
}

View file

@ -0,0 +1,30 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
</head>
<body>
This packages contains the SyndFeed converters for the
OPML module. For information on how Opml gets turned into
other feed types through the Synd structures, see the OPML
Subproject page on the ROME wiki.
<pre>
*
* 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.
</pre>
</body>
</html>

View file

@ -0,0 +1,186 @@
/*
* Opml10Generator.java
*
* Created on April 24, 2006, 11:35 PM
*
* 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.io.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import org.jdom2.Document;
import org.jdom2.Element;
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.io.FeedException;
import com.rometools.rome.io.WireFeedGenerator;
import com.rometools.rome.io.impl.BaseWireFeedGenerator;
import com.rometools.rome.io.impl.DateParser;
/**
* @author <a href="mailto:cooper@screaming-penguin.com">Robert "kebernet" Cooper</a>
*/
public class OPML10Generator extends BaseWireFeedGenerator implements WireFeedGenerator {
public OPML10Generator() {
super("opml_1.0");
}
public OPML10Generator(final String type) {
super(type);
}
/**
* Creates an XML document (JDOM) for the given feed bean.
*
* @param feed the feed bean to generate the XML document from.
* @return the generated XML document (JDOM).
* @throws IllegalArgumentException thrown if the type of the given feed bean does not match with the type of the
* WireFeedGenerator.
* @throws FeedException thrown if the XML Document could not be created.
*/
@Override
public Document generate(final WireFeed feed) throws IllegalArgumentException, FeedException {
if (!(feed instanceof Opml)) {
throw new IllegalArgumentException("Not an OPML file");
}
final Opml opml = (Opml) feed;
final Document doc = new Document();
final Element root = new Element("opml");
root.setAttribute("version", "1.0");
doc.addContent(root);
final Element head = generateHead(opml);
if (head != null) {
root.addContent(head);
}
final Element body = new Element("body");
root.addContent(body);
super.generateFeedModules(opml.getModules(), root);
body.addContent(generateOutlines(opml.getOutlines()));
return doc;
}
protected boolean addNotNullAttribute(final Element target, final String name, final Object value) {
if (target == null || name == null || value == null) {
return false;
}
target.setAttribute(name, value.toString());
return true;
}
protected boolean addNotNullSimpleElement(final Element target, final String name, final Object value) {
if (target == null || name == null || value == null) {
return false;
}
final Element e = new Element(name);
e.addContent(value.toString());
target.addContent(e);
return true;
}
protected Element generateHead(final Opml opml) {
final Element head = new Element("head");
boolean hasHead = false;
if (opml.getCreated() != null) {
hasHead |= addNotNullSimpleElement(head, "dateCreated", DateParser.formatRFC822(opml.getCreated(), Locale.US));
}
hasHead |= addNotNullSimpleElement(head, "expansionState", intArrayToCsvString(opml.getExpansionState()));
if (opml.getModified() != null) {
hasHead |= addNotNullSimpleElement(head, "dateModified", DateParser.formatRFC822(opml.getModified(), Locale.US));
}
hasHead |= addNotNullSimpleElement(head, "ownerEmail", opml.getOwnerEmail());
hasHead |= addNotNullSimpleElement(head, "ownerName", opml.getOwnerName());
hasHead |= addNotNullSimpleElement(head, "title", opml.getTitle());
hasHead |= addNotNullSimpleElement(head, "vertScrollState", opml.getVerticalScrollState());
hasHead |= addNotNullSimpleElement(head, "windowBottom", opml.getWindowBottom());
hasHead |= addNotNullSimpleElement(head, "windowLeft", opml.getWindowLeft());
hasHead |= addNotNullSimpleElement(head, "windowRight", opml.getWindowRight());
hasHead |= addNotNullSimpleElement(head, "windowTop", opml.getWindowTop());
if (hasHead) {
return head;
} else {
return null;
}
}
protected Element generateOutline(final Outline outline) {
final Element e = new Element("outline");
addNotNullAttribute(e, "text", outline.getText());
addNotNullAttribute(e, "type", outline.getType());
addNotNullAttribute(e, "title", outline.getTitle());
if (outline.isBreakpoint()) {
addNotNullAttribute(e, "isBreakpoint", "true");
}
if (outline.isComment()) {
addNotNullAttribute(e, "isComment", "true");
}
final List<Attribute> atts = Collections.synchronizedList(outline.getAttributes());
for (int i = 0; i < atts.size(); i++) {
final Attribute att = atts.get(i);
addNotNullAttribute(e, att.getName(), att.getValue());
}
super.generateItemModules(outline.getModules(), e);
e.addContent(generateOutlines(outline.getChildren()));
return e;
}
protected List<Element> generateOutlines(final List<Outline> outlines) {
final ArrayList<Element> elements = new ArrayList<Element>();
for (int i = 0; outlines != null && i < outlines.size(); i++) {
elements.add(generateOutline(outlines.get(i)));
}
return elements;
}
protected String intArrayToCsvString(final int[] value) {
if (value == null || value.length == 0) {
return null;
}
final StringBuffer sb = new StringBuffer();
sb.append(value[0]);
for (int i = 1; i < value.length; i++) {
sb.append(",");
sb.append(value[i]);
}
return sb.toString();
}
}

View file

@ -0,0 +1,260 @@
/*
* Opml10Parser.java
*
* Created on April 24, 2006, 11:34 PM
*
* 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.io.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import org.jdom2.Document;
import org.jdom2.Element;
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.io.FeedException;
import com.rometools.rome.io.WireFeedParser;
import com.rometools.rome.io.impl.BaseWireFeedParser;
import com.rometools.rome.io.impl.DateParser;
/**
* @author <a href="mailto:cooper@screaming-penguin.com">Robert "kebernet" Cooper</a>
*/
public class OPML10Parser extends BaseWireFeedParser implements WireFeedParser {
private static Logger LOG = LoggerFactory.getLogger(OPML10Parser.class);
public OPML10Parser() {
super("opml_1.0", null);
}
public OPML10Parser(final String type) {
super(type, null);
}
/**
* Inspects an XML Document (JDOM) to check if it can parse it.
* <p>
* It checks if the given document if the type of feeds the parser understands.
* <p>
*
* @param document XML Document (JDOM) to check if it can be parsed by this parser.
* @return <b>true</b> if the parser know how to parser this feed, <b>false</b> otherwise.
*/
@Override
public boolean isMyType(final Document document) {
final Element e = document.getRootElement();
if (e.getName().equals("opml") && (e.getChild("head") == null || e.getChild("head").getChild("docs") == null)
&& (e.getAttributeValue("version") == null || e.getAttributeValue("version").equals("1.0"))) {
return true;
}
return false;
}
/**
* Parses an XML document (JDOM Document) into a feed bean.
* <p>
*
* @param document XML document (JDOM) to parse.
* @param validate indicates if the feed should be strictly validated (NOT YET IMPLEMENTED).
* @return the resulting feed bean.
* @throws IllegalArgumentException thrown if the parser cannot handle the given feed type.
* @throws FeedException thrown if a feed bean cannot be created out of the XML document (JDOM).
*/
@Override
public WireFeed parse(final Document document, final boolean validate, final Locale locale) throws IllegalArgumentException, FeedException {
final Opml opml = new Opml();
opml.setFeedType("opml_1.0");
final Element root = document.getRootElement();
final Element head = root.getChild("head");
if (head != null) {
opml.setTitle(head.getChildText("title"));
if (head.getChildText("dateCreated") != null) {
opml.setCreated(DateParser.parseRFC822(head.getChildText("dateCreated"), Locale.US));
}
if (head.getChildText("dateModified") != null) {
opml.setModified(DateParser.parseRFC822(head.getChildText("dateModified"), Locale.US));
}
opml.setOwnerName(head.getChildTextTrim("ownerName"));
opml.setOwnerEmail(head.getChildTextTrim("ownerEmail"));
opml.setVerticalScrollState(readInteger(head.getChildText("vertScrollState")));
try {
opml.setWindowBottom(readInteger(head.getChildText("windowBottom")));
} catch (final NumberFormatException nfe) {
LOG.warn("Unable to parse windowBottom", nfe);
if (validate) {
throw new FeedException("Unable to parse windowBottom", nfe);
}
}
try {
opml.setWindowLeft(readInteger(head.getChildText("windowLeft")));
} catch (final NumberFormatException nfe) {
LOG.warn("Unable to parse windowLeft", nfe);
if (validate) {
throw new FeedException("Unable to parse windowLeft", nfe);
}
}
try {
opml.setWindowRight(readInteger(head.getChildText("windowRight")));
} catch (final NumberFormatException nfe) {
LOG.warn("Unable to parse windowRight", nfe);
if (validate) {
throw new FeedException("Unable to parse windowRight", nfe);
}
}
try {
opml.setWindowLeft(readInteger(head.getChildText("windowLeft")));
} catch (final NumberFormatException nfe) {
LOG.warn("Unable to parse windowLeft", nfe);
if (validate) {
throw new FeedException("Unable to parse windowLeft", nfe);
}
}
try {
opml.setWindowTop(readInteger(head.getChildText("windowTop")));
} catch (final NumberFormatException nfe) {
LOG.warn("Unable to parse windowTop", nfe);
if (validate) {
throw new FeedException("Unable to parse windowTop", nfe);
}
}
try {
opml.setExpansionState(readIntArray(head.getChildText("expansionState")));
} catch (final NumberFormatException nfe) {
LOG.warn("Unable to parse expansionState", nfe);
if (validate) {
throw new FeedException("Unable to parse expansionState", nfe);
}
}
}
opml.setOutlines(parseOutlines(root.getChild("body").getChildren("outline"), validate, locale));
opml.setModules(parseFeedModules(root, locale));
return opml;
}
protected Outline parseOutline(final Element e, final boolean validate, final Locale locale) throws FeedException {
if (!e.getName().equals("outline")) {
throw new RuntimeException("Not an outline element.");
}
final Outline outline = new Outline();
outline.setText(e.getAttributeValue("text"));
outline.setType(e.getAttributeValue("type"));
outline.setTitle(e.getAttributeValue("title"));
final List<org.jdom2.Attribute> jAttributes = e.getAttributes();
final ArrayList<Attribute> attributes = new ArrayList<Attribute>();
for (int i = 0; i < jAttributes.size(); i++) {
final org.jdom2.Attribute a = jAttributes.get(i);
if (!a.getName().equals("isBreakpoint") && !a.getName().equals("isComment") && !a.getName().equals("title") && !a.getName().equals("text")
&& !a.getName().equals("type")) {
attributes.add(new Attribute(a.getName(), a.getValue()));
}
}
outline.setAttributes(attributes);
try {
outline.setBreakpoint(readBoolean(e.getAttributeValue("isBreakpoint")));
} catch (final Exception ex) {
LOG.warn("Unable to parse isBreakpoint value", ex);
if (validate) {
throw new FeedException("Unable to parse isBreakpoint value", ex);
}
}
try {
outline.setComment(readBoolean(e.getAttributeValue("isComment")));
} catch (final Exception ex) {
LOG.warn("Unable to parse isComment value", ex);
if (validate) {
throw new FeedException("Unable to parse isComment value", ex);
}
}
final List<Element> children = e.getChildren("outline");
outline.setModules(parseItemModules(e, locale));
outline.setChildren(parseOutlines(children, validate, locale));
return outline;
}
protected List<Outline> parseOutlines(final List<Element> elements, final boolean validate, final Locale locale) throws FeedException {
final ArrayList<Outline> results = new ArrayList<Outline>();
for (int i = 0; i < elements.size(); i++) {
results.add(parseOutline(elements.get(i), validate, locale));
}
return results;
}
protected boolean readBoolean(final String value) {
if (value == null) {
return false;
} else {
return Boolean.getBoolean(value.trim());
}
}
protected int[] readIntArray(final String value) {
if (value == null) {
return null;
} else {
final StringTokenizer tok = new StringTokenizer(value, ",");
final int[] result = new int[tok.countTokens()];
int count = 0;
while (tok.hasMoreElements()) {
result[count] = Integer.parseInt(tok.nextToken().trim());
count++;
}
return result;
}
}
protected Integer readInteger(final String value) {
if (value != null) {
return new Integer(value);
} else {
return null;
}
}
}

View file

@ -0,0 +1,106 @@
package com.rometools.opml.io.impl;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.jdom2.Document;
import org.jdom2.Element;
import com.rometools.opml.feed.opml.Opml;
import com.rometools.opml.feed.opml.Outline;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.io.FeedException;
import com.rometools.rome.io.impl.DateParser;
/**
* Generator for OPML 2.0 documents.
*
* @see <a href="http://dev.opml.org/spec2.html">http://dev.opml.org/spec2.html</a>
*/
public class OPML20Generator extends OPML10Generator {
public OPML20Generator() {
}
/**
* Returns the type of feed the generator creates.
*
* @return the type of feed the generator creates.
* @see WireFeed for details on the format of this string.
*/
@Override
public String getType() {
return "opml_2.0";
}
/**
* Creates an XML document (JDOM) for the given feed bean.
*
* @param feed the feed bean to generate the XML document from.
* @return the generated XML document (JDOM).
* @throws IllegalArgumentException thrown if the type of the given feed bean does not match with the type of the
* WireFeedGenerator.
* @throws FeedException thrown if the XML Document could not be created.
*/
@Override
public Document generate(final WireFeed feed) throws IllegalArgumentException, FeedException {
final Document document = super.generate(feed);
document.getRootElement().setAttribute("version", "2.0");
return document;
}
@Override
protected Element generateHead(final Opml opml) {
final Element docsElement = new Element("docs");
docsElement.setText(opml.getDocs());
final Element headElement = super.generateHead(opml);
headElement.addContent(docsElement);
return headElement;
}
@Override
protected Element generateOutline(final Outline outline) {
final Element outlineElement = super.generateOutline(outline);
if (outline.getCreated() != null) {
outlineElement.setAttribute("created", DateParser.formatRFC822(outline.getCreated(), Locale.US));
}
final List<String> categories = outline.getCategories();
final String categoryValue = generateCategoryValue(categories);
addNotNullAttribute(outlineElement, "category", categoryValue);
return outlineElement;
}
private String generateCategoryValue(final Collection<String> categories) {
final StringBuilder builder = new StringBuilder();
boolean first = true;
for (final String category : categories) {
if (category != null && !category.trim().isEmpty()) {
if (first) {
first = false;
} else {
builder.append(",");
}
builder.append(category.trim());
}
}
if (builder.length() > 0) {
return builder.toString();
} else {
return null;
}
}
}

View file

@ -0,0 +1,120 @@
/*
* Opml20Parser.java
*
* Created on April 25, 2006, 1:04 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.io.impl;
import java.util.List;
import java.util.Locale;
import org.jdom2.Document;
import org.jdom2.Element;
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.io.FeedException;
import com.rometools.rome.io.impl.DateParser;
/**
*
* @author cooper
*/
public class OPML20Parser extends OPML10Parser {
/** Creates a new instance of Opml20Parser */
public OPML20Parser() {
super("opml_2.0");
}
/**
* Inspects an XML Document (JDOM) to check if it can parse it.
* <p>
* It checks if the given document if the type of feeds the parser understands.
* <p>
*
* @param document XML Document (JDOM) to check if it can be parsed by this parser.
* @return <b>true</b> if the parser know how to parser this feed, <b>false</b> otherwise.
*/
@Override
public boolean isMyType(final Document document) {
final Element e = document.getRootElement();
if (e.getName().equals("opml")
&& (e.getChild("head") != null && e.getChild("head").getChild("docs") != null || e.getAttributeValue("version") != null
&& e.getAttributeValue("version").equals("2.0") || e.getChild("head") != null && e.getChild("head").getChild("ownerId") != null)) {
return true;
}
return false;
}
/**
* Parses an XML document (JDOM Document) into a feed bean.
* <p>
*
* @param document XML document (JDOM) to parse.
* @param validate indicates if the feed should be strictly validated (NOT YET IMPLEMENTED).
* @return the resulting feed bean.
* @throws IllegalArgumentException thrown if the parser cannot handle the given feed type.
* @throws FeedException thrown if a feed bean cannot be created out of the XML document (JDOM).
*/
@Override
public WireFeed parse(final Document document, final boolean validate, final Locale locale) throws IllegalArgumentException, FeedException {
Opml opml;
opml = (Opml) super.parse(document, validate, locale);
final Element head = document.getRootElement().getChild("head");
if (head != null) {
opml.setOwnerId(head.getChildTextTrim("ownerId"));
opml.setDocs(head.getChildTextTrim("docs"));
if (opml.getDocs() == null) {
opml.setDocs("http://www.opml.org/spec2");
}
}
opml.setFeedType("opml_2.0");
return opml;
}
@Override
protected Outline parseOutline(final Element e, final boolean validate, final Locale locale) throws FeedException {
Outline retValue;
retValue = super.parseOutline(e, validate, locale);
if (e.getAttributeValue("created") != null) {
retValue.setCreated(DateParser.parseRFC822(e.getAttributeValue("created"), locale));
}
final List<Attribute> atts = retValue.getAttributes();
for (int i = 0; i < atts.size(); i++) {
final Attribute a = atts.get(i);
if (a.getName().equals("created")) {
retValue.getAttributes().remove(a);
break;
}
}
return retValue;
}
}

View file

@ -0,0 +1,28 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
</head>
<body>
This package contains the feed parsers for OPML1 and OPML2.
<pre>
*
* 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.
</pre>
</body>
</html>

View file

@ -0,0 +1,22 @@
# 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.
#
WireFeedGenerator.classes=com.rometools.opml.io.impl.OPML10Generator \
com.rometools.opml.io.impl.OPML20Generator
WireFeedParser.classes=com.rometools.opml.io.impl.OPML10Parser \
com.rometools.opml.io.impl.OPML20Parser
Converter.classes=com.rometools.opml.feed.synd.impl.ConverterForOPML10 \
com.rometools.opml.feed.synd.impl.ConverterForOPML20

125
src/site/apt/index.apt Normal file
View file

@ -0,0 +1,125 @@
-----
Home
-----
kwebble
-----
2011-09-23 14:15:28.442
-----
ROME OPML
This project provides support for {{{http://www.opml.org/}OPML}} in ROME.
*Downloads
* {{{./apidocs/index.html}JavaDocs}}
[]
*Sample Usage
To use this parser, simply include the jar file in your classpath as you are using ROME. Be sure it exists at the same level as ROME,
such that, if ROME is in the common classpath of an application server, don't include this jar in your webapps WEB\-INF/lib.
+------+
WireFeedInput input = new WireFeedInput();
Opml feed = (Opml) input.build( new File("myOpml.xml") );
List<Outline> outlines = (List<Outline>) feed.getOutlines();
+------+
*Hierarchy vs Flat
Since OPML is a hierarchical format, some magic is required to preserve this information when they feed is moved to a Synd\* structure.
This is accomplished by adding categories indicating the tree structure to the {{{http://wiki.java.net/bin/edit/Javawsxml/SyndEntries?topicparent\=Javawsxml.OPML;nowysiwyg\=0}SyndEntries}} ...
For example:
+------+
<opml version="1.0">
<head>
<title>Top Ten Sources for podcasting</title>
<ownerName>Newsilike Media Group</ownerName>
<ownerEmail>opml@TopTenSources.com</ownerEmail>
</head>
<body>
<outline type="link" text="TopTenSources: podcasting"
url="http://podcasting.TopTenSources.com/TopTenSources/" />
<outline text="CBS Technology News Podcast - Larry Magid' Tech Report">
<outline type="link" text="Larry Magid's Tech Report" url="http://www.cbsnews.com" />
</outline>
<outline text="Adam Curry: Daily Source Code">
<outline type="link" text="#374 Daily Source Code for Tuesday April 25th 2006"
url="http://radio.weblogs.com/0001014/2006/04/26.html#a7304" />
<outline type="link" text="#373 Daily Source Code for Monday April 24th 2006"
url="http://radio.weblogs.com/0001014/2006/04/24.html#a7303" />
<outline type="link" text="#372 Daily Source Code for Friday April 21st 2006"
url="http://radio.weblogs.com/0001014/2006/04/21.html#a7302" />
<outline type="link" text="#371 Daily Source Code for Thursday April 20th 2006"
url="http://radio.weblogs.com/0001014/2006/04/20.html#a7301" />
<outline type="link" text="#370 Daily Source Code for Wednesday April 19th 2006"
url="http://radio.weblogs.com/0001014/2006/04/19.html#a7300" />
</outline>
<outline text="Gillmor Gang">
<outline type="link" text="Syndicate Gang Part I" url="http://gillmorgang.podshow.com/?p=44" />
<outline type="link" text="HughTrain Gang" url="http://gillmorgang.podshow.com/?p=43" />
<outline type="link" text="Phlegm at 11 Gang" url="http://gillmorgang.podshow.com/?p=42" />
<outline type="link" text="NDA Gang" url="http://gillmorgang.podshow.com/?p=41" />
<outline type="link" text="When the Music?s Over Gang" url="http://gillmorgang.podshow.com/?p=40" />
</outline>
+------+
...\
When converted to RSS2 becomes:
+------+
<channel>
<title>Top Ten Sources for podcasting</title>
<link>http://foo.com</link>
<description />
<managingEditor>Newsilike Media Group</managingEditor>
<item>
<title>TopTenSources: podcasting</title>
<link>http://podcasting.TopTenSources.com/TopTenSources/</link>
<category domain="urn:rome.tree">node.-1732517202</category>
<category domain="urn:rome.attribute#url">http://podcasting.TopTenSources.com/TopTenSources/</category>
<guid>http://podcasting.TopTenSources.com/TopTenSources/</guid>
</item>
<item>
<title>CBS Technology News Podcast - Larry Magid' Tech Report</title>
<category domain="urn:rome.tree">node.1353657827</category>
</item>
<item>
<title>Larry Magid's Tech Report</title>
<link>http://www.cbsnews.com</link>
<category domain="urn:rome.tree">node.-4085850</category>
<category domain="urn:rome.tree">parent.1353657827</category>
<category domain="urn:rome.attribute#url">http://www.cbsnews.com</category>
<guid>http://www.cbsnews.com</guid>
</item>
<item>
<title>Adam Curry: Daily Source Code</title>
<category domain="urn:rome.tree">node.835791399</category>
</item>
<item>
<title>#374 Daily Source Code for Tuesday April 25th 2006</title>
<link>http://radio.weblogs.com/0001014/2006/04/26.html#a7304</link>
<category domain="urn:rome.tree">node.222050897</category>
<category domain="urn:rome.tree">parent.835791399</category>
<category domain="urn:rome.attribute#url">http://radio.weblogs.com/0001014/2006/04/26.html#a7304</category>
<guid>http://radio.weblogs.com/0001014/2006/04/26.html#a7304</guid>
</item>
<item>
<title>#373 Daily Source Code for Monday April 24th 2006</title>
<link>http://radio.weblogs.com/0001014/2006/04/24.html#a7303</link>
<category domain="urn:rome.tree">node.2088220478</category>
<category domain="urn:rome.tree">parent.835791399</category>
<category domain="urn:rome.attribute#url">http://radio.weblogs.com/0001014/2006/04/24.html#a7303</category>
<guid>http://radio.weblogs.com/0001014/2006/04/24.html#a7303</guid>
</item>
+------+
Nodes get categories with the "urn:rome.tree" URI that is used to maintain the tree structure.
The other thing you will notice is the "urn:rome.attribute#url". Since OPML allows you to add arbitrary attributes to each outline element, these are used to preserve these values.

View file

View file

@ -0,0 +1,8 @@
h1 {
padding: 4px 4px 4px 6px;
border: 1px solid #999;
color: #900;
background-color: #ddd;
font-weight:900;
font-size: x-large;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

30
src/site/site.xml Normal file
View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/DECORATION/1.3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.3.0 http://maven.apache.org/xsd/decoration-1.3.0.xsd"
name="ROME OPML">
<skin>
<groupId>org.apache.maven.skins</groupId>
<artifactId>maven-fluido-skin</artifactId>
<version>1.3.0</version>
</skin>
<bannerLeft>
<name>ROME</name>
<src>images/romelogo.png</src>
<href>http://github.com/rometools/</href>
</bannerLeft>
<publishDate position="right" />
<version position="right" />
<body>
<menu name="ROME OPML">
<item name="Overview" href="index.html" />
</menu>
<menu ref="reports" />
</body>
</project>

View file

@ -0,0 +1,146 @@
package com.rometools.opml;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.jdom2.Document;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl;
import com.rometools.rome.io.WireFeedOutput;
/**
*
* <p>
*
* @author Alejandro Abdelnur
*
*/
public abstract class FeedOpsTest extends FeedTest {
protected FeedOpsTest(final String feedType) {
super(feedType + ".xml");
new File("target/test-reports").mkdirs();
}
// 1.2a
public void testWireFeedEquals() throws Exception {
final WireFeed feed1 = getCachedWireFeed();
final WireFeed feed2 = getWireFeed();
assertTrue(feed1.equals(feed2));
}
// 1.2b
public void testWireFeedNotEqual() throws Exception {
final WireFeed feed1 = getCachedWireFeed();
final WireFeed feed2 = getWireFeed();
feed2.setFeedType("dummy");
assertFalse(feed1.equals(feed2));
}
// 1.3
public void testWireFeedCloning() throws Exception {
final WireFeed feed1 = getCachedWireFeed();
final WireFeed feed2 = (WireFeed) feed1.clone();
;
assertTrue(feed1.equals(feed2));
}
// 1.4
public void testWireFeedSerialization() throws Exception {
final WireFeed feed1 = getCachedWireFeed();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(feed1);
oos.close();
final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
final ObjectInputStream ois = new ObjectInputStream(bais);
final WireFeed feed2 = (WireFeed) ois.readObject();
ois.close();
assertTrue(feed1.equals(feed2));
}
// 1.5
public void testWireFeedJDOMSerialization() throws Exception {
Document inputDoc = getCachedJDomDoc();
final WireFeed feed = getCachedWireFeed();
WireFeedOutput output = new WireFeedOutput();
Document outputDoc = output.outputJDom(feed);
XMLOutputter outputter = new XMLOutputter(Format.getCompactFormat());
String inputString = outputter.outputString(inputDoc);
String outputString = outputter.outputString(outputDoc);
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreAttributeOrder(true);
Diff diff = XMLUnit.compareXML(inputString, outputString);
// ignore elements order
diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
XMLAssert.assertXMLEqual(diff, true);
}
// 1.6
public void testWireFeedSyndFeedConversion() throws Exception {
final SyndFeed sFeed1 = getCachedSyndFeed();
final WireFeed wFeed1 = sFeed1.createWireFeed();
final SyndFeed sFeed2 = new SyndFeedImpl(wFeed1);
assertEquals(sFeed1, sFeed2);
}
// 1.7a
public void testSyndFeedEquals() throws Exception {
final SyndFeed feed1 = getCachedSyndFeed();
final SyndFeed feed2 = getSyndFeed();
assertTrue(feed1.equals(feed2));
}
// 1.7b
public void testSyndFeedNotEqual() throws Exception {
final SyndFeed feed1 = getCachedSyndFeed();
final SyndFeed feed2 = getSyndFeed();
feed2.setFeedType("dummy");
assertFalse(feed1.equals(feed2));
}
// 1.8
public void testSyndFeedCloning() throws Exception {
final SyndFeed feed1 = getCachedSyndFeed();
final SyndFeed feed2 = (SyndFeed) feed1.clone();
;
assertTrue(feed1.equals(feed2));
}
// 1.9
public void testSyndFeedSerialization() throws Exception {
final SyndFeed feed1 = getCachedSyndFeed();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(feed1);
oos.close();
final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
final ObjectInputStream ois = new ObjectInputStream(bais);
final SyndFeed feed2 = (SyndFeed) ois.readObject();
ois.close();
assertTrue(feed1.equals(feed2));
}
}

View file

@ -0,0 +1,79 @@
package com.rometools.opml;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import junit.framework.TestCase;
import org.jdom2.Document;
import org.jdom2.input.SAXBuilder;
import org.jdom2.input.sax.XMLReaders;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.io.SyndFeedInput;
import com.rometools.rome.io.WireFeedInput;
/**
* @author pat, tucu
*
*/
public abstract class FeedTest extends TestCase {
private final String fileName;
private Document jDomDoc = null;
private WireFeed wireFeed = null;
private SyndFeed syndFeed = null;
protected FeedTest(final String feedFileName) {
fileName = feedFileName;
}
protected String getFeedFileName() {
return fileName;
}
protected Reader getFeedReader() throws Exception {
final InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(getFeedFileName());
assertNotNull("Could not find resource " + getFeedFileName(), resource);
return new InputStreamReader(resource);
}
protected Document getJDomDoc() throws Exception {
final SAXBuilder saxBuilder = new SAXBuilder(XMLReaders.NONVALIDATING);
return saxBuilder.build(getFeedReader());
}
protected WireFeed getWireFeed() throws Exception {
final WireFeedInput in = new WireFeedInput();
return in.build(getFeedReader());
}
protected SyndFeed getSyndFeed() throws Exception {
final SyndFeedInput in = new SyndFeedInput();
return in.build(getFeedReader());
}
protected Document getCachedJDomDoc() throws Exception {
if (jDomDoc == null) {
jDomDoc = getJDomDoc();
}
return jDomDoc;
}
protected WireFeed getCachedWireFeed() throws Exception {
if (wireFeed == null) {
wireFeed = getWireFeed();
}
return wireFeed;
}
protected SyndFeed getCachedSyndFeed() throws Exception {
if (syndFeed == null) {
syndFeed = getSyndFeed();
}
return syndFeed;
}
}

View file

@ -0,0 +1,136 @@
package com.rometools.opml;
/**
* @author pat
*
*/
public abstract class SyndFeedTest extends FeedTest {
private String _prefix = null;
protected SyndFeedTest(final String feedType) {
this(feedType, feedType + ".xml");
}
protected SyndFeedTest(final String feedType, final String feedFileName) {
super(feedFileName);
_prefix = feedType;
}
protected String getPrefix() {
return _prefix;
}
protected void assertProperty(final String property, final String value) {
assertEquals(property, getPrefix() + "." + value);
}
public void testType() throws Exception {
assertEquals(getCachedSyndFeed().getFeedType(), getPrefix());
}
/*
* public void testType() throws Exception { assertEquals(getPrefix(),
* getCachedSyndFeed().getFeedType()); } public void testTitle() throws Exception {
* assertEqualsStr("channel.title", getCachedSyndFeed().getTitle()); } public void testLink()
* throws Exception { assertEqualsStr("channel.link", getCachedSyndFeed().getLink()); } public
* void testDescription() throws Exception { assertEqualsStr("channel.description",
* getCachedSyndFeed().getDescription()); } public void testLanguage() throws Exception {
* assertEqualsStr("channel.language", getCachedSyndFeed().getLanguage()); } public void
* testCategories() throws Exception { List catlist = getCachedSyndFeed().getCategories();
* //don't understand why this one fails assertEquals(2, catlist.size()); SyndCategory cat =
* (SyndCategory)catlist.get(0); assertEqualsStr("channel.category[0]", cat.getName());
* assertEqualsStr("channel.category[0]^domain", cat.getTaxonomyUri()); cat =
* (SyndCategory)catlist.get(1); assertEqualsStr("channel.category[1]", cat.getName());
* assertEqualsStr("channel.category[1]^domain", cat.getTaxonomyUri()); } public void
* testPublishedDate() throws Exception {
* assertEquals(DateParser.parseRFC822("Mon, 01 Jan 2001 00:00:00 GMT"),
* getCachedSyndFeed().getPublishedDate()); } //how do i get height and width? public void
* testImage() throws Exception { SyndImage img = getCachedSyndFeed().getImage();
* assertEqualsStr("channel.image.description", img.getDescription());
* assertEqualsStr("channel.image.link", img.getLink()); assertEqualsStr("channel.image.title",
* img.getTitle()); assertEqualsStr("channel.image.url", img.getUrl()); } public void
* testEntries() throws Exception { List entrylist = getCachedSyndFeed().getEntries();
* assertEquals(2, entrylist.size()); } public void testEntryTitle() throws Exception {
* assertEqualsStr("channel.item[0].title",
* getEntryTitle(getCachedSyndFeed().getEntries().get(0)));
* assertEqualsStr("channel.item[1].title",
* getEntryTitle(getCachedSyndFeed().getEntries().get(1))); } public String getEntryTitle(Object
* o) throws Exception { SyndEntry e = (SyndEntry) o; return e.getTitle(); } public void
* testEntryDescription() throws Exception { assertEqualsStr("channel.item[0].description",
* getEntryDescription(getCachedSyndFeed().getEntries().get(0)));
* assertEqualsStr("channel.item[1].description",
* getEntryDescription(getCachedSyndFeed().getEntries().get(1))); } public String
* getEntryDescription(Object o) throws Exception { SyndEntry e = (SyndEntry) o; return
* e.getDescription().getValue(); } public void testEntryLink() throws Exception {
* assertEqualsStr("channel.item[0].link",
* getEntryLink(getCachedSyndFeed().getEntries().get(0)));
* assertEqualsStr("channel.item[1].link",
* getEntryLink(getCachedSyndFeed().getEntries().get(1))); } public String getEntryLink(Object
* o) { SyndEntry e = (SyndEntry) o; return e.getLink(); } public void testEntryPublishedDate()
* throws Exception { assertEquals(DateParser.parseRFC822("Mon, 01 Jan 2001 00:00:00 GMT"),
* getEntryPublishedDate(getCachedSyndFeed().getEntries().get(0)));
* assertEquals(DateParser.parseRFC822("Mon, 01 Jan 2001 00:00:00 GMT"),
* getEntryPublishedDate(getCachedSyndFeed().getEntries().get(1))); } public Date
* getEntryPublishedDate(Object o) { SyndEntry e = (SyndEntry) o; return e.getPublishedDate(); }
* public void testEntryCategories() throws Exception { SyndEntry e =
* (SyndEntry)getCachedSyndFeed().getEntries().get(0); List catlist = e.getCategories(); //don't
* understand why this one fails assertEquals(2, catlist.size()); SyndCategory cat =
* (SyndCategory)catlist.get(0); assertEqualsStr("channel.item[0].category[0]", cat.getName());
* assertEqualsStr("channel.item[0].category[0]^domain", cat.getTaxonomyUri()); cat =
* (SyndCategory)catlist.get(1); assertEqualsStr("channel.item[0].category[1]", cat.getName());
* assertEqualsStr("channel.item[0].category[1]^domain", cat.getTaxonomyUri()); //DO 2nd set of
* items } public void testEntryAuthor() throws Exception {
* assertEqualsStr("channel.item[0].author",
* getEntryAuthor(getCachedSyndFeed().getEntries().get(0)));
* assertEqualsStr("channel.item[1].author",
* getEntryAuthor(getCachedSyndFeed().getEntries().get(1))); } public String
* getEntryAuthor(Object o) { SyndEntry e = (SyndEntry) o; return e.getAuthor(); }
*/
/*
* //things you cannot get from SyndEntryImpl // <source
* url="http://localhost:8080/item0/source.url">item[0].source</source> // <enclosure
* url="http://localhost:8080/item0/enclosure0.url" length="100" type="audio/mpeg"/> //
* <enclosure url="http://localhost:8080/item0/enclosure1.url" length="1000" type="audio/mpeg"/>
* <category domain="item0.domain0">item0.category0</category> <category
* domain="item0.domain1">item0.category1</category> <pubDate>Thu, 08 Jul 1999 08:00:00
* GMT</pubDate> <expirationDate>Thu, 08 Jul 1999 09:00:00 GMT</.expirationDate>
* <author>item0.author</author> <comments>http://localhost:8080/item0/comments</comments> <guid
* isPermaLink="true">http://localhost:8080/item0/guid</guid> //TODO: I still have the elements
* to test
*/
/*
* public void test() { assertEqualsStr(feed, ""); } public void test() { assertEqualsStr(feed,
* ""); }
*/
// Things that you cannot get form a SyndFeedImpl today
// these need to be put in a RSS 2.0 module
// or is a roundtrip to wirefeed the right way to do this?
/*
* <textInput> <title>Search</title> <description>Search this site:</description> <name>q</name>
* <link>http://example.org/mt/mt-search.cgi</link> </textInput> image height and width
* //<copyright>Copyright 2004, Mark Pilgrim</copyright> public void test() {
* assertEqualsStr(getCachedSyndFeed()., ""); } //<generator>Sample Toolkit</generator> public
* void test() { assertEqualsStr(feed, ""); } //
* <managingEditor>editor@example.org</managingEditor> public void test() {
* assertEqualsStr(feed, ""); } // <webMaster>webmaster@example.org</webMaster> public void
* test() { assertEqualsStr(feed, ""); } <docs>http://blogs.law.harvard.edu/tech/rss</docs>
* <cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="pingMe"
* protocol="soap"/> <ttl>60</ttl> <rating></rating> <skiphours> <hour>0</hour> <hour>1</hour>
* <hour>2</hour> <hour>3</hour> <hour>4</hour> <hour>5</hour> <hour>6</hour> <hour>7</hour>
* <hour>8</hour> <hour>9.5</hour> <hour>10</hour> <hour>11</hour> <hour>12</hour>
* <hour>13</hour> <hour>14</hour> <hour>15</hour> <hour>16</hour> <hour>17</hour>
* <hour>18</hour> <hour>19</hour> <hour>20</hour> <hour>21</hour> <hour>22</hour>
* <hour>23</hour> </skiphours> <skipdays> <day>Monday</day> <day>Tuesday</day>
* <day>Wednesday</day> <day>Thursday</day> <day>Friday</day> <day>Saturday</day>
* <day>Sunday</day> </skipdays>
*/
/*
* @see TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
}

View file

@ -0,0 +1,46 @@
/*
* TestOpsOPML10.java
*
* Created on April 25, 2006, 4:26 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package com.rometools.opml;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl;
/**
*
* @author cooper
*/
public class TestOpsOPML10 extends FeedOpsTest {
/** Creates a new instance of TestOpsOPML10 */
public TestOpsOPML10() {
super("opml_1.0");
}
// 1.6
@Override
public void testWireFeedSyndFeedConversion() throws Exception {
final SyndFeed sFeed1 = getCachedSyndFeed();
final WireFeed wFeed1 = sFeed1.createWireFeed();
final SyndFeed sFeed2 = new SyndFeedImpl(wFeed1);
PrintWriter w = new PrintWriter(new FileOutputStream("target/test-reports/1"));
w.println(sFeed1.toString());
w.close();
w = new PrintWriter(new FileOutputStream("target/test-reports/2"));
w.println(sFeed2.toString());
w.close();
assertEquals(sFeed1, sFeed2);
}
}

View file

@ -0,0 +1,63 @@
/*
* TestOpsOPML10.java
*
* Created on April 25, 2006, 4:26 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package com.rometools.opml;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import com.rometools.opml.test.NullWriter;
import com.rometools.opml.test.TestUtil;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl;
import com.rometools.rome.io.WireFeedInput;
import com.rometools.rome.io.WireFeedOutput;
/**
*
* @author cooper
*/
public class TestOpsOPML10links extends FeedOpsTest {
/** Creates a new instance of TestOpsOPML10 */
public TestOpsOPML10links() {
super("opml_1.0_links");
}
// 1.6
@Override
public void testWireFeedSyndFeedConversion() throws Exception {
final SyndFeed sFeed1 = getCachedSyndFeed();
final WireFeed wFeed1 = sFeed1.createWireFeed();
final SyndFeed sFeed2 = new SyndFeedImpl(wFeed1);
PrintWriter w = new PrintWriter(new FileOutputStream("target/test-reports/1"));
w.println(sFeed1.toString());
w.close();
w = new PrintWriter(new FileOutputStream("target/test-reports/2"));
w.println(sFeed2.toString());
w.close();
assertEquals(sFeed2.createWireFeed(), sFeed1.createWireFeed());
}
public void testTemp() throws Exception {
final WireFeedInput input = new WireFeedInput();
final WireFeed wf = input.build(TestUtil.loadFile("/opml_1.0_links.xml"));
final WireFeedOutput output = new WireFeedOutput();
final SyndFeedImpl sf = new SyndFeedImpl(wf);
sf.setFeedType("rss_2.0");
sf.setDescription("");
sf.setLink("http://foo.com");
sf.setFeedType("opml_1.0");
output.output(sf.createWireFeed(), new NullWriter());
}
}

View file

@ -0,0 +1,44 @@
/*
* TestOpsOPML20.java
*
* Created on April 25, 2006, 5:38 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package com.rometools.opml;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import com.rometools.rome.feed.WireFeed;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl;
/**
*
* @author cooper
*/
public class TestOpsOPML20 extends FeedOpsTest {
/** Creates a new instance of TestOpsOPML20 */
public TestOpsOPML20() {
super("opml_2.0");
}
@Override
public void testWireFeedSyndFeedConversion() throws Exception {
final SyndFeed sFeed1 = getCachedSyndFeed();
final WireFeed wFeed1 = sFeed1.createWireFeed();
final SyndFeed sFeed2 = new SyndFeedImpl(wFeed1);
PrintWriter w = new PrintWriter(new FileOutputStream("target/test-reports/3"));
w.println(sFeed1.toString());
w.close();
w = new PrintWriter(new FileOutputStream("target/test-reports/4"));
w.println(sFeed2.toString());
w.close();
assertEquals(sFeed1, sFeed2);
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright 2004 Sun Microsystems, Inc.
*
* 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;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import junit.framework.TestCase;
import org.jdom2.input.SAXBuilder;
import com.rometools.rome.io.XmlReader;
import com.rometools.rome.io.impl.XmlFixerReader;
/**
* @author pat, tucu
*
*/
public class TestXmlFixerReader extends TestCase {
private static final String XML_PROLOG = "<?xml version=\"1.0\" ?>";
public void testTrim() throws Exception {
_testValidTrim("", "<hello></hello>");
_testValidTrim("", XML_PROLOG + "<hello></hello>");
_testValidTrim(" ", "<hello></hello>");
_testValidTrim(" ", XML_PROLOG + "<hello></hello>");
_testValidTrim(" \n", "<hello></hello>");
_testValidTrim(" \n", XML_PROLOG + "<hello></hello>");
_testValidTrim("<!-- - -- -->", "<hello></hello>");
_testValidTrim("<!-- - -- -->", XML_PROLOG + "<hello></hello>");
_testValidTrim(" <!-- - -- -->", "<hello></hello>");
_testValidTrim(" <!-- - -- -->", XML_PROLOG + "<hello></hello>");
_testValidTrim(" <!-- - -- --> ", "<hello></hello>");
_testValidTrim(" <!-- - -- --> ", XML_PROLOG + "<hello></hello>");
_testValidTrim(" <!-- - -- --> <!-- - -- --> ", "<hello></hello>");
_testValidTrim(" <!-- - -- --> <!-- - -- --> ", XML_PROLOG + "<hello></hello>");
_testValidTrim(" <!-- - -- --> \n <!-- - -- --> ", "<hello></hello>");
_testValidTrim(" <!-- - -- --> \n <!-- - -- --> ", XML_PROLOG + "<hello></hello>");
_testInvalidTrim("x", "<hello></hello>");
_testInvalidTrim("x", XML_PROLOG + "<hello></hello>");
_testInvalidTrim(" x", "<hello></hello>");
_testInvalidTrim(" x", XML_PROLOG + "<hello></hello>");
_testInvalidTrim(" x\n", "<hello></hello>");
_testInvalidTrim(" x\n", XML_PROLOG + "<hello></hello>");
_testInvalidTrim("<!-- - -- - ->", "<hello></hello>");
_testInvalidTrim("<!-- - -- - ->", XML_PROLOG + "<hello></hello>");
_testInvalidTrim(" <!-- - -- -- >", "<hello></hello>");
_testInvalidTrim(" <!-- - -- -- >", XML_PROLOG + "<hello></hello>");
_testInvalidTrim(" <!-- - -- -->x ", "<hello></hello>");
_testInvalidTrim(" <!-- - -- -->x ", XML_PROLOG + "<hello></hello>");
_testInvalidTrim(" <!-- - -- --> x <!-- - -- --> ", "<hello></hello>");
_testInvalidTrim(" <!-- - -- --> x <!-- - -- --> ", XML_PROLOG + "<hello></hello>");
_testInvalidTrim(" <!-- - -- --> x\n <!-- - -- --> ", "<hello></hello>");
_testInvalidTrim(" <!-- - -- --> x\n <!-- - -- --> ", XML_PROLOG + "<hello></hello>");
}
public void testHtmlEntities() throws Exception {
_testValidEntities("<hello></hello>");
_testValidEntities(XML_PROLOG + "<hello></hello>");
_testValidEntities(" <!-- just in case -->\n" + XML_PROLOG + "<hello></hello>");
_testValidEntities("<hello>&apos;&yen;&#250;&yen;</hello>");
_testValidEntities(XML_PROLOG + "<hello>&apos;&yen;&#250;&yen;</hello>");
_testValidEntities(" <!-- just in case -->\n" + XML_PROLOG + "<hello>&apos;&yen;&#250;&yen;</hello>");
_testInvalidEntities("<hello>&apos;&yexn;&#250;&yen;</hello>");
_testInvalidEntities(XML_PROLOG + "<hello>&apos;&yexn;&#250;&yen;</hello>");
_testInvalidEntities(" <!-- just in case -->\n" + XML_PROLOG + "<hello>&apos;&yexn;&#250;&yen;</hello>");
_testInvalidEntities("<hello>&apos;&yen;&#2x50;&yen;</hello>");
_testInvalidEntities(XML_PROLOG + "<hello>&apos;&yen;&#2x50;&yen;</hello>");
_testInvalidEntities(" <!-- just in case -->\n" + XML_PROLOG + "<hello>&apos;&yen;&#2x50;&yen;</hello>");
}
protected void _testXmlParse(final String garbish, final String xmlDoc) throws Exception {
final InputStream is = getStream(garbish, xmlDoc);
Reader reader = new XmlReader(is);
reader = new XmlFixerReader(reader);
final SAXBuilder saxBuilder = new SAXBuilder();
saxBuilder.build(reader);
}
protected void _testValidTrim(final String garbish, final String xmlDoc) throws Exception {
_testXmlParse(garbish, xmlDoc);
}
protected void _testInvalidTrim(final String garbish, final String xmlDoc) throws Exception {
try {
_testXmlParse(garbish, xmlDoc);
assertTrue(false);
} catch (final Exception ex) {
}
}
protected void _testValidEntities(final String xmlDoc) throws Exception {
_testXmlParse("", xmlDoc);
}
protected void _testInvalidEntities(final String xmlDoc) throws Exception {
try {
_testXmlParse("", xmlDoc);
assertTrue(false);
} catch (final Exception ex) {
}
}
// XML Stream generator
protected InputStream getStream(final String garbish, final String xmlDoc) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
final Writer writer = new OutputStreamWriter(baos);
writer.write(garbish);
writer.write(xmlDoc);
writer.close();
return new ByteArrayInputStream(baos.toByteArray());
}
}

View file

@ -0,0 +1,269 @@
/*
* Copyright 2004 Sun Microsystems, Inc.
*
* 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;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
import com.rometools.rome.io.XmlReader;
/**
* @author pat, tucu
*
*/
public class TestXmlReader extends TestCase {
public static void main(final String[] args) throws Exception {
final TestXmlReader test = new TestXmlReader();
test.testRawBom();
test.testRawNoBom();
test.testHttp();
}
protected void _testRawNoBomValid(final String encoding) throws Exception {
// TODO review this test (XmlReader is never closed, test fails when using a new XmlReader
// for each Assert)
InputStream is = getXmlStream("no-bom", "xml", encoding, encoding);
XmlReader xmlReader = new XmlReader(is, false);
assertEquals(xmlReader.getEncoding(), "UTF-8");
xmlReader.close();
is = getXmlStream("no-bom", "xml-prolog", encoding, encoding);
xmlReader = new XmlReader(is);
assertEquals(xmlReader.getEncoding(), "UTF-8");
xmlReader.close();
is = getXmlStream("no-bom", "xml-prolog-encoding", encoding, encoding);
xmlReader = new XmlReader(is);
assertEquals(xmlReader.getEncoding(), encoding);
xmlReader.close();
}
protected void _testRawNoBomInvalid(final String encoding) throws Exception {
final InputStream is = getXmlStream("no-bom", "xml-prolog-encoding", encoding, encoding);
try {
final XmlReader xmlReader = new XmlReader(is, false);
fail("It should have failed");
xmlReader.close();
} catch (final IOException ex) {
assertTrue(ex.getMessage().indexOf("Invalid encoding,") > -1);
}
}
public void testRawNoBom() throws Exception {
_testRawNoBomValid("US-ASCII");
_testRawNoBomValid("UTF-8");
_testRawNoBomValid("ISO-8859-1");
}
protected void _testRawBomValid(final String encoding) throws Exception {
final InputStream is = getXmlStream(encoding + "-bom", "xml-prolog-encoding", encoding, encoding);
final XmlReader xmlReader = new XmlReader(is, false);
if (!encoding.equals("UTF-16")) {
assertEquals(xmlReader.getEncoding(), encoding);
} else {
assertEquals(xmlReader.getEncoding().substring(0, encoding.length()), encoding);
}
xmlReader.close();
}
protected void _testRawBomInvalid(final String bomEnc, final String streamEnc, final String prologEnc) throws Exception {
final InputStream is = getXmlStream(bomEnc, "xml-prolog-encoding", streamEnc, prologEnc);
try {
final XmlReader xmlReader = new XmlReader(is, false);
fail("It should have failed for BOM " + bomEnc + ", streamEnc " + streamEnc + " and prologEnc " + prologEnc);
xmlReader.close();
} catch (final IOException ex) {
assertTrue(ex.getMessage().indexOf("Invalid encoding,") > -1);
}
}
public void testRawBom() throws Exception {
_testRawBomValid("UTF-8");
_testRawBomValid("UTF-16BE");
_testRawBomValid("UTF-16LE");
_testRawBomValid("UTF-16");
_testRawBomInvalid("UTF-8-bom", "US-ASCII", "US-ASCII");
_testRawBomInvalid("UTF-8-bom", "ISO-8859-1", "ISO-8859-1");
_testRawBomInvalid("UTF-8-bom", "UTF-8", "UTF-16");
_testRawBomInvalid("UTF-8-bom", "UTF-8", "UTF-16BE");
_testRawBomInvalid("UTF-8-bom", "UTF-8", "UTF-16LE");
_testRawBomInvalid("UTF-16BE-bom", "UTF-16BE", "UTF-16LE");
_testRawBomInvalid("UTF-16LE-bom", "UTF-16LE", "UTF-16BE");
_testRawBomInvalid("UTF-16LE-bom", "UTF-16LE", "UTF-8");
}
public void testHttp() throws Exception {
_testHttpValid("application/xml", "no-bom", "US-ASCII", null);
_testHttpValid("application/xml", "UTF-8-bom", "US-ASCII", null);
_testHttpValid("application/xml", "UTF-8-bom", "UTF-8", null);
_testHttpValid("application/xml", "UTF-8-bom", "UTF-8", "UTF-8");
_testHttpValid("application/xml;charset=UTF-8", "UTF-8-bom", "UTF-8", null);
_testHttpValid("application/xml;charset=UTF-8", "UTF-8-bom", "UTF-8", "UTF-8");
_testHttpValid("application/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", null);
_testHttpValid("application/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", "UTF-16");
_testHttpValid("application/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", "UTF-16BE");
_testHttpInvalid("application/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", null);
_testHttpInvalid("application/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", "UTF-16");
_testHttpInvalid("application/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", "UTF-16BE");
_testHttpInvalid("application/xml", "UTF-8-bom", "US-ASCII", "US-ASCII");
_testHttpInvalid("application/xml;charset=UTF-16", "UTF-16LE", "UTF-8", "UTF-8");
_testHttpInvalid("application/xml;charset=UTF-16", "no-bom", "UTF-16BE", "UTF-16BE");
_testHttpValid("text/xml", "no-bom", "US-ASCII", null);
_testHttpValid("text/xml;charset=UTF-8", "UTF-8-bom", "UTF-8", "UTF-8");
_testHttpValid("text/xml;charset=UTF-8", "UTF-8-bom", "UTF-8", null);
_testHttpValid("text/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", null);
_testHttpValid("text/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", "UTF-16");
_testHttpValid("text/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", "UTF-16BE");
_testHttpValid("text/xml", "UTF-8-bom", "US-ASCII", null);
_testHttpInvalid("text/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", null);
_testHttpInvalid("text/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", "UTF-16");
_testHttpInvalid("text/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", "UTF-16BE");
_testHttpInvalid("text/xml;charset=UTF-16", "no-bom", "UTF-16BE", "UTF-16BE");
_testHttpInvalid("text/xml;charset=UTF-16", "no-bom", "UTF-16BE", null);
_testHttpLenient("text/xml", "no-bom", "US-ASCII", null, "US-ASCII");
_testHttpLenient("text/xml;charset=UTF-8", "UTF-8-bom", "UTF-8", "UTF-8", "UTF-8");
_testHttpLenient("text/xml;charset=UTF-8", "UTF-8-bom", "UTF-8", null, "UTF-8");
_testHttpLenient("text/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", null, "UTF-16BE");
_testHttpLenient("text/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", "UTF-16", "UTF-16");
_testHttpLenient("text/xml;charset=UTF-16", "UTF-16BE-bom", "UTF-16BE", "UTF-16BE", "UTF-16BE");
_testHttpLenient("text/xml", "UTF-8-bom", "US-ASCII", null, "US-ASCII");
_testHttpLenient("text/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", null, "UTF-16BE");
_testHttpLenient("text/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", "UTF-16", "UTF-16");
_testHttpLenient("text/xml;charset=UTF-16BE", "UTF-16BE-bom", "UTF-16BE", "UTF-16BE", "UTF-16BE");
_testHttpLenient("text/xml;charset=UTF-16", "no-bom", "UTF-16BE", "UTF-16BE", "UTF-16BE");
_testHttpLenient("text/xml;charset=UTF-16", "no-bom", "UTF-16BE", null, "UTF-16");
_testHttpLenient("text/html", "no-bom", "US-ASCII", "US-ASCII", "US-ASCII");
_testHttpLenient("text/html", "no-bom", "US-ASCII", null, "US-ASCII");
_testHttpLenient("text/html;charset=UTF-8", "no-bom", "US-ASCII", "UTF-8", "UTF-8");
_testHttpLenient("text/html;charset=UTF-16BE", "no-bom", "US-ASCII", "UTF-8", "UTF-8");
}
public void _testHttpValid(final String cT, final String bomEnc, final String streamEnc, final String prologEnc) throws Exception {
final InputStream is = getXmlStream(bomEnc, prologEnc == null ? "xml" : "xml-prolog-encoding", streamEnc, prologEnc);
final XmlReader xmlReader = new XmlReader(is, cT, false);
if (!streamEnc.equals("UTF-16")) {
// we can not assert things here becuase UTF-8, US-ASCII and ISO-8859-1 look alike for
// the chars used for detection
} else {
assertEquals(xmlReader.getEncoding().substring(0, streamEnc.length()), streamEnc);
}
xmlReader.close();
}
protected void _testHttpInvalid(final String cT, final String bomEnc, final String streamEnc, final String prologEnc) throws Exception {
final InputStream is = getXmlStream(bomEnc, prologEnc == null ? "xml-prolog" : "xml-prolog-encoding", streamEnc, prologEnc);
try {
final XmlReader xmlReader = new XmlReader(is, cT, false);
fail("It should have failed for HTTP Content-type " + cT + ", BOM " + bomEnc + ", streamEnc " + streamEnc + " and prologEnc " + prologEnc);
xmlReader.close();
} catch (final IOException ex) {
assertTrue(ex.getMessage().indexOf("Invalid encoding,") > -1);
}
}
protected void _testHttpLenient(final String cT, final String bomEnc, final String streamEnc, final String prologEnc, final String shouldbe)
throws Exception {
final InputStream is = getXmlStream(bomEnc, prologEnc == null ? "xml-prolog" : "xml-prolog-encoding", streamEnc, prologEnc);
final XmlReader xmlReader = new XmlReader(is, cT, true);
assertEquals(xmlReader.getEncoding(), shouldbe);
xmlReader.close();
}
// XML Stream generator
private static final int[] NO_BOM_BYTES = {};
private static final int[] UTF_16BE_BOM_BYTES = { 0xFE, 0xFF };
private static final int[] UTF_16LE_BOM_BYTES = { 0xFF, 0XFE };
private static final int[] UTF_8_BOM_BYTES = { 0xEF, 0xBB, 0xBF };
private static final Map<String, int[]> BOMs = new HashMap<String, int[]>();
static {
BOMs.put("no-bom", NO_BOM_BYTES);
BOMs.put("UTF-16BE-bom", UTF_16BE_BOM_BYTES);
BOMs.put("UTF-16LE-bom", UTF_16LE_BOM_BYTES);
BOMs.put("UTF-16-bom", NO_BOM_BYTES); // it's added by the writer
BOMs.put("UTF-8-bom", UTF_8_BOM_BYTES);
}
private static final MessageFormat XML = new MessageFormat("<root>{2}</root>");
private static final MessageFormat XML_WITH_PROLOG = new MessageFormat("<?xml version=\"1.0\"?>\n<root>{2}</root>");
private static final MessageFormat XML_WITH_PROLOG_AND_ENCODING = new MessageFormat("<?xml version=\"1.0\" encoding=\"{1}\"?>\n<root>{2}</root>");
private static final MessageFormat INFO = new MessageFormat("\nBOM : {0}\nDoc : {1}\nStream Enc : {2}\nProlog Enc : {3}\n");
private static final Map<String, MessageFormat> XMLs = new HashMap<String, MessageFormat>();
static {
XMLs.put("xml", XML);
XMLs.put("xml-prolog", XML_WITH_PROLOG);
XMLs.put("xml-prolog-encoding", XML_WITH_PROLOG_AND_ENCODING);
}
/**
*
* @param bomType no-bom, UTF-16BE-bom, UTF-16LE-bom, UTF-8-bom
* @param xmlType xml, xml-prolog, xml-prolog-charset
* @return XML stream
*/
protected InputStream getXmlStream(final String bomType, final String xmlType, final String streamEnc, final String prologEnc) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
int[] bom = BOMs.get(bomType);
if (bom == null) {
bom = new int[0];
}
final MessageFormat xml = XMLs.get(xmlType);
for (final int element : bom) {
baos.write(element);
}
final Writer writer = new OutputStreamWriter(baos, streamEnc);
final String info = INFO.format(new Object[] { bomType, xmlType, prologEnc });
final String xmlDoc = xml.format(new Object[] { streamEnc, prologEnc, info });
writer.write(xmlDoc);
// PADDDING TO TEST THINGS WORK BEYOND PUSHBACK_SIZE
writer.write("<da>\n");
for (int i = 0; i < 10000; i++) {
writer.write("<do/>\n");
}
writer.write("</da>\n");
writer.close();
return new ByteArrayInputStream(baos.toByteArray());
}
}

View file

@ -0,0 +1,73 @@
package com.rometools.opml.io.impl;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import java.util.Arrays;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import com.rometools.opml.feed.opml.Opml;
import com.rometools.opml.feed.opml.Outline;
import com.rometools.rome.io.WireFeedOutput;
public class OPML20GeneratorTest {
@Test
public void testOutputOfNullCategory() {
assertThat(categoryOf((String) null).getLength(), is(equalTo(0)));
}
@Test
public void testOutputOfEmptyCategory() {
assertThat(categoryOf("").getLength(), is(equalTo(0)));
}
@Test
public void testOutputOfBlankCategory() {
assertThat(categoryOf(" ").getLength(), is(equalTo(0)));
}
@Test
public void testOutputOfOneCategory() {
assertThat(categoryValueOf("category1"), is(equalTo("category1")));
}
@Test
public void testOutputOfMultipleCategories() {
assertThat(categoryValueOf("category1", "category2"), is(equalTo("category1,category2")));
}
private NodeList categoryOf(final String... categories) {
try {
final Outline outline = new Outline("outline1", null);
outline.setCategories(Arrays.asList(categories));
final Opml opml = new Opml();
opml.setFeedType("opml_2.0");
opml.setTitle("title");
opml.setOutlines(Arrays.asList(outline));
final WireFeedOutput output = new WireFeedOutput();
final String xml = output.outputString(opml);
final Document document = XMLUnit.buildControlDocument(xml);
return XMLUnit.newXpathEngine().getMatchingNodes("/opml/body/outline/@category", document);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private String categoryValueOf(final String... categories) {
return categoryOf(categories).item(0).getNodeValue();
}
}

View file

@ -0,0 +1,20 @@
package com.rometools.opml.test;
import java.io.IOException;
import java.io.Writer;
public class NullWriter extends Writer {
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
}
@Override
public void flush() throws IOException {
}
@Override
public void close() throws IOException {
}
}

View file

@ -0,0 +1,15 @@
package com.rometools.opml.test;
import java.io.InputStreamReader;
import java.io.Reader;
public final class TestUtil {
private TestUtil() {
}
public static Reader loadFile(final String path) {
return new InputStreamReader(TestUtil.class.getResourceAsStream(path));
}
}

View file

@ -0,0 +1,13 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<root level="warn">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
<head>
<title>Bloglines Subscriptions</title>
<dateCreated>Tue, 25 Apr 2006 19:19:28 GMT</dateCreated>
<ownerName/>
</head>
<body>
<outline title="Subscriptions">
<outline title="Bloglines | News" htmlUrl="http://www.bloglines.com" type="rss" xmlUrl="http://www.bloglines.com/rss/about/news"/>
<outline title="screaming-penguin.com" htmlUrl="http://www.screaming-penguin.com" type="rss" xmlUrl="http://screaming-penguin.com/screamRDF.txt"/>
<outline title="Food" htmlUrl="http://www.livejournal.com/users/vrikodhara/" type="rss" xmlUrl="http://www.livejournal.com/users/vrikodhara/data/rss"/>
<outline title="Fafblog" htmlUrl="http://fafblog.blogspot.com" type="rss" xmlUrl="http://fafblog.blogspot.com/atom.xml"/>
<outline title="nerdy">
<outline title="The Register" htmlUrl="http://www.theregister.co.uk/" type="rss" xmlUrl="http://www.theregister.co.uk/feeds/latest.rdf"/>
<outline title="Plastic: Most Recent" htmlUrl="http://www.plastic.com/" type="rss" xmlUrl="http://www.plastic.com/plastic.xml"/>
<outline title="CNET News.com" htmlUrl="http://news.com.com/" type="rss" xmlUrl="http://news.com.com/2547-1_3-0-5.xml"/>
<outline title="The String Coffee Table" htmlUrl="http://golem.ph.utexas.edu/string/" type="rss" xmlUrl="http://golem.ph.utexas.edu/string/rss2.xml"/>
<outline title="Musings" htmlUrl="http://golem.ph.utexas.edu/~distler/blog/" type="rss" xmlUrl="http://golem.ph.utexas.edu/~distler/blog/rss2.xml"/>
<outline title="New Scientist - Latest Headlines" htmlUrl="http://www.newscientist.com/news.ns" type="rss" xmlUrl="http://www.newscientist.com/feed.ns?index=online-news"/>
<outline title="Slashdot" htmlUrl="http://slashdot.org/" type="rss" xmlUrl="http://rss.slashdot.org/slashdot/eqWf"/>
</outline>
<outline title="tech people">
</outline>
<outline title="podcasts">
</outline>
</outline>
</body>
</opml>

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<opml version="1.0">
<head>
<title>Top Ten Sources for podcasting</title>
<ownerName>Newsilike Media Group</ownerName>
<ownerEmail>opml@TopTenSources.com</ownerEmail>
<dateCreated>Thu, 27 Apr 2006 05:47:27 GMT</dateCreated>
<dateModified>Thu, 27 Apr 2006 05:47:27 GMT</dateModified>
<expansionState>0</expansionState>
<vertScrollState>0</vertScrollState>
<windowLeft>0</windowLeft>
<windowTop>0</windowTop>
<windowRight>100</windowRight>
<windowBottom>100</windowBottom>
</head>
<body>
<outline type="link" text="TopTenSources: podcasting" url="http://podcasting.TopTenSources.com/TopTenSources/" />
<outline text="CBS Technology News Podcast - Larry Magid' Tech Report">
<outline type="link" text="Larry Magid's Tech Report" url="http://www.cbsnews.com" />
</outline>
<outline text="Adam Curry: Daily Source Code">
<outline type="link" text="#374 Daily Source Code for Tuesday April 25th 2006" url="http://radio.weblogs.com/0001014/2006/04/26.html#a7304" />
<outline type="link" text="#373 Daily Source Code for Monday April 24th 2006" url="http://radio.weblogs.com/0001014/2006/04/24.html#a7303" />
<outline type="link" text="#372 Daily Source Code for Friday April 21st 2006" url="http://radio.weblogs.com/0001014/2006/04/21.html#a7302" />
<outline type="link" text="#371 Daily Source Code for Thursday April 20th 2006" url="http://radio.weblogs.com/0001014/2006/04/20.html#a7301" />
<outline type="link" text="#370 Daily Source Code for Wednesday April 19th 2006" url="http://radio.weblogs.com/0001014/2006/04/19.html#a7300" />
</outline>
<outline text="Gillmor Gang">
<outline type="link" text="Syndicate Gang Part I" url="http://gillmorgang.podshow.com/?p=44" />
<outline type="link" text="HughTrain Gang" url="http://gillmorgang.podshow.com/?p=43" />
<outline type="link" text="Phlegm at 11 Gang" url="http://gillmorgang.podshow.com/?p=42" />
<outline type="link" text="NDA Gang" url="http://gillmorgang.podshow.com/?p=41" />
<outline type="link" text="When the Musics Over Gang" url="http://gillmorgang.podshow.com/?p=40" />
</outline>
<outline text="ITC: All Programs">
<outline type="link" text="Doc Searls: Syndicate" url="http://feeds.feedburner.com/ITConversations-EverythingMP3?m=689" />
<outline type="link" text="Business Panel: The Podcast Academy" url="http://feeds.feedburner.com/ITConversations-EverythingMP3?m=688" />
<outline type="link" text="CN Update: April 24, 2006" url="http://feeds.feedburner.com/ITConversations-EverythingMP3?m=687" />
<outline type="link" text="Hossein Eslambolchi: AT T: The Networked Future is Open" url="http://feeds.feedburner.com/ITConversations-EverythingMP3?m=686" />
<outline type="link" text="Dileep George: Understanding the Neocortex to Accelerate our Understanding of Intelligence" url="http://feeds.feedburner.com/ITConversations-EverythingMP3?m=685" />
</outline>
<outline text="FamilyTechTalk">
<outline type="link" text="FamilyTechTalk 24 April 15, 2006" url="http://familytechtalk.com/podcasts/ftt24.mp3" />
<outline type="link" text="FamilyTechTalk 23 April 7, 2006" url="http://familytechtalk.com/podcasts/ftt23.mp3" />
<outline type="link" text="FamilyTechTalk 22 March 31, 2006" url="http://familytechtalk.com/podcasts/ftt22.mp3" />
<outline type="link" text="FamilyTechTalk 20 March 17, 2006" url="http://familytechtalk.com/podcasts/ftt20.mp3" />
<outline type="link" text="FamilyTechTalk 21 March 24, 2006" url="http://familytechtalk.com/podcasts/ftt21.mp3" />
</outline>
<outline text="On The Media from NPR/WNYC">
<outline type="link" text="On The Media - April 14, 2006" url="http://feeds.feedburner.com/onthemedia?m=35" />
<outline type="link" text="On The Media - April 21, 2006" url="http://feeds.wnyc.org/onthemedia?m=37" />
<outline type="link" text="On The Media - April 07, 2006" url="http://feeds.feedburner.com/onthemedia?m=34" />
</outline>
<outline text="Morning Coffee Notes">
<outline type="link" text="OPML 2.0 is easy to understand if you're intelligent, have common sense and are patient. It's really simple. I explain, in this podcast, why the improvements in OPML 2.0 will help users. " url="http://static2.podcatch.com/blogs/gems/snedit/cn06mar01.mp3" />
<outline type="link" text="Today's podcast is about WordPress, Google and the OPML community. It's especially important for the leaders of the OPML community to listen to this podcast, because I want to explore ways for the support system to grow. I think we just had a very good experience and I want to build on it." url="http://static2.podcatch.com/blogs/gems/snedit/cn06feb01.mp3" />
<outline type="link" text="I just recorded a segment for Chris Lydon's show with my state of the union speech, above, and also recorded it as an MP3, so here it is the second Morning Coffee Notes of 2006. &quot;;-&gt;&quot;" url="http://static2.podcatch.com/blogs/gems/snedit/cn06jan31.mp3" />
<outline type="link" text="Morning Coffee Notes interview with John Palfrey, founder of toptensources.com, and director of Berkman Center." url="http://static2.podcatch.com/blogs/gems/snedit/cn05Jan02.mp3" />
<outline type="link" text="A Morning Coffee Notes podcast with thoughts about the scene in New Orleans and on the Gulf Coast. " url="http://static2.podcatch.com/blogs/gems/snedit/cn05Dec16.mp3" />
</outline>
<outline text="Gillmor Daily">
<outline type="link" text="Death Cab for Links Part II" url="http://gillmordaily.podshow.com/?p=66" />
<outline type="link" text="Death Cab for Links Part I" url="http://gillmordaily.podshow.com/?p=65" />
<outline type="link" text="Javas Open Sores? Part II" url="http://gillmordaily.podshow.com/?p=64" />
<outline type="link" text="Javas Open Sores? Part I" url="http://gillmordaily.podshow.com/?p=63" />
<outline type="link" text="Gillmor Daily 1 Redux Part II" url="http://gillmordaily.podshow.com/?p=62" />
</outline>
<outline text="Latest ZDNet Podcasts">
<outline type="link" text="Sling Media CEO Blake Krikorian unplugged" url="http://blogs.zdnet.com/BTL/index.php?p=2924&amp;part=rss&amp;tag=feed&amp;subj=zdblog" />
<outline type="link" text="Video goes Internet--the future of what you watch" url="http://blogs.zdnet.com/BTL/index.php?p=2920&amp;part=rss&amp;tag=feed&amp;subj=zdblog" />
<outline type="link" text="Will or could RSS get forked?" url="http://blogs.zdnet.com/BTL/index.php?p=2906&amp;part=rss&amp;tag=feed&amp;subj=zdblog" />
<outline type="link" text="Learning from the masters: Creating a $1 billion company" url="http://blogs.zdnet.com/BTL/index.php?p=2910&amp;part=rss&amp;tag=feed&amp;subj=zdblog" />
<outline type="link" text="Apple vs. bloggers, AMD vs. Intel, DRM vs. consumers Yahoo vs. China, Microsoft vs. EU and more..." url="http://blogs.zdnet.com/BTL/index.php?p=2901&amp;part=rss&amp;tag=feed&amp;subj=zdblog" />
</outline>
</body>
</opml>

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<opml version="2.0">
<head>
<title>Bloglines Subscriptions</title>
<dateCreated>Tue, 25 Apr 2006 19:19:28 GMT</dateCreated>
<docs>http://foo.com</docs>
<ownerName>Robert Cooper</ownerName>
</head>
<body>
<outline title="Subscriptions">
<outline title="Bloglines | News" htmlUrl="http://www.bloglines.com" type="rss" xmlUrl="http://www.bloglines.com/rss/about/news"/>
<outline title="screaming-penguin.com" htmlUrl="http://www.screaming-penguin.com" type="rss" xmlUrl="http://screaming-penguin.com/screamRDF.txt"/>
<outline title="Food" htmlUrl="http://www.livejournal.com/users/vrikodhara/" type="rss" xmlUrl="http://www.livejournal.com/users/vrikodhara/data/rss"/>
<outline title="Fafblog" htmlUrl="http://fafblog.blogspot.com" type="rss" xmlUrl="http://fafblog.blogspot.com/atom.xml"/>
<outline title="nerdy">
<outline title="The Register" htmlUrl="http://www.theregister.co.uk/" type="rss" xmlUrl="http://www.theregister.co.uk/feeds/latest.rdf"/>
<outline title="Plastic: Most Recent" htmlUrl="http://www.plastic.com/" type="rss" xmlUrl="http://www.plastic.com/plastic.xml"/>
<outline title="CNET News.com" htmlUrl="http://news.com.com/" type="rss" xmlUrl="http://news.com.com/2547-1_3-0-5.xml"/>
<outline title="The String Coffee Table" htmlUrl="http://golem.ph.utexas.edu/string/" type="rss" xmlUrl="http://golem.ph.utexas.edu/string/rss2.xml"/>
<outline title="Musings" htmlUrl="http://golem.ph.utexas.edu/~distler/blog/" type="rss" xmlUrl="http://golem.ph.utexas.edu/~distler/blog/rss2.xml"/>
<outline title="New Scientist - Latest Headlines" htmlUrl="http://www.newscientist.com/news.ns" type="rss" xmlUrl="http://www.newscientist.com/feed.ns?index=online-news"/>
<outline category="foo, bar, baz" title="Slashdot" htmlUrl="http://slashdot.org/" type="rss" xmlUrl="http://rss.slashdot.org/slashdot/eqWf"/>
</outline>
<outline title="tech people">
</outline>
<outline title="podcasts">
</outline>
</outline>
</body>
</opml>