From 4998f26ba148ce507911612780169bea2c0c749a Mon Sep 17 00:00:00 2001 From: Antonio Santos Izaguirre Date: Thu, 20 Apr 2023 01:00:05 +0200 Subject: [PATCH] create OPML 1.1 parser/generator/converter (#660) Signed-off-by: Antonio Santos Izaguirre --- .../com/rometools/opml/feed/opml/Cloud.java | 87 ++++++++++++++ .../com/rometools/opml/feed/opml/Opml.java | 16 +++ .../feed/synd/impl/ConverterForOPML11.java | 62 ++++++++++ .../opml/io/impl/OPML11Generator.java | 81 +++++++++++++ .../rometools/opml/io/impl/OPML11Parser.java | 107 ++++++++++++++++++ rome-opml/src/main/resources/rome.properties | 3 + .../rometools/opml/TestOpsOPML11states.java | 71 ++++++++++++ .../src/test/resources/opml_1.1_states.xml | 93 +++++++++++++++ 8 files changed, 520 insertions(+) create mode 100644 rome-opml/src/main/java/com/rometools/opml/feed/opml/Cloud.java create mode 100644 rome-opml/src/main/java/com/rometools/opml/feed/synd/impl/ConverterForOPML11.java create mode 100644 rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Generator.java create mode 100644 rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Parser.java create mode 100644 rome-opml/src/test/java/com/rometools/opml/TestOpsOPML11states.java create mode 100644 rome-opml/src/test/resources/opml_1.1_states.xml diff --git a/rome-opml/src/main/java/com/rometools/opml/feed/opml/Cloud.java b/rome-opml/src/main/java/com/rometools/opml/feed/opml/Cloud.java new file mode 100644 index 000000000..d8df647a4 --- /dev/null +++ b/rome-opml/src/main/java/com/rometools/opml/feed/opml/Cloud.java @@ -0,0 +1,87 @@ +package com.rometools.opml.feed.opml; + +import java.io.Serializable; + +import com.rometools.rome.feed.impl.EqualsBean; +import com.rometools.rome.feed.impl.ToStringBean; + +public class Cloud implements Cloneable, Serializable { + + private static final long serialVersionUID = 1L; + + private String domain; + private String port; + private String path; + private String registerProcedure; + private String protocol; + + public Cloud() { + + } + + public final String getDomain() { + return this.domain; + } + + public final void setDomain(final String domain) { + this.domain = domain; + } + + public final String getPort() { + return this.port; + } + + public final void setPort(final String port) { + this.port = port; + } + + public final String getPath() { + return this.path; + } + + public final void setPath(final String path) { + this.path = path; + } + + public final String getRegisterProcedure() { + return this.registerProcedure; + } + + public final void setRegisterProcedure(final String registerProcedure) { + this.registerProcedure = registerProcedure; + } + + public final String getProtocol() { + return this.protocol; + } + + public final void setProtocol(final String protocol) { + this.protocol = protocol; + } + + @Override + public Object clone() { + final Cloud c = new Cloud(); + c.setDomain(getDomain()); + c.setPort(getPort()); + c.setPath(getPath()); + c.setRegisterProcedure(getRegisterProcedure()); + c.setProtocol(getProtocol()); + return c; + } + + @Override + public boolean equals(final Object obj) { + return EqualsBean.beanEquals(Cloud.class, this, obj); + } + + @Override + public int hashCode() { + return EqualsBean.beanHashCode(this); + } + + @Override + public String toString() { + return ToStringBean.toString(Cloud.class, this); + } +} diff --git a/rome-opml/src/main/java/com/rometools/opml/feed/opml/Opml.java b/rome-opml/src/main/java/com/rometools/opml/feed/opml/Opml.java index 428c70694..63cf1b3cc 100644 --- a/rome-opml/src/main/java/com/rometools/opml/feed/opml/Opml.java +++ b/rome-opml/src/main/java/com/rometools/opml/feed/opml/Opml.java @@ -45,6 +45,7 @@ public class Opml extends WireFeed { private String ownerName; private String title; private int[] expansionState; + private Cloud cloud; /** * is a date-time, indicating when the document was created. @@ -317,4 +318,19 @@ public Integer getWindowTop() { return windowTop; } + /** + * <cloud> is a Cloud object, contains information about cloud (spec 1.1) + * @return the cloud element + */ + public Cloud getCloud() { + return this.cloud; + } + + /** + * <cloud> is a Cloud object, contains information about cloud (spec 1.1) + * @param c the cloud element + */ + public void setCloud(final Cloud c) { + this.cloud = c; + } } diff --git a/rome-opml/src/main/java/com/rometools/opml/feed/synd/impl/ConverterForOPML11.java b/rome-opml/src/main/java/com/rometools/opml/feed/synd/impl/ConverterForOPML11.java new file mode 100644 index 000000000..1151e1fe4 --- /dev/null +++ b/rome-opml/src/main/java/com/rometools/opml/feed/synd/impl/ConverterForOPML11.java @@ -0,0 +1,62 @@ +/* + * + * 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 com.rometools.rome.feed.WireFeed; +import com.rometools.rome.feed.synd.SyndFeed; + +public class ConverterForOPML11 extends ConverterForOPML10 { + + /** + * 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.1"; + } + + /** + * Makes a deep copy/conversion of the values of a real feed into a SyndFeedImpl. + *

+ * It assumes the given SyndFeedImpl has no properties set. + *

+ * + * @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. + *

+ * + * @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); + } + +} diff --git a/rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Generator.java b/rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Generator.java new file mode 100644 index 000000000..1596ba56b --- /dev/null +++ b/rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Generator.java @@ -0,0 +1,81 @@ +/* + * + * 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 org.jdom2.Document; +import org.jdom2.Element; + +import com.rometools.opml.feed.opml.Opml; +import com.rometools.rome.feed.WireFeed; +import com.rometools.rome.io.FeedException; + +/** + * Generator for OPML 1.1 documents. + * + * @see http://dev.opml.org/spec2.html + */ +public class OPML11Generator extends OPML10Generator { + + public OPML11Generator() { + } + + /** + * 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_1.1"; + } + + /** + * 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", "1.1"); + return document; + } + + @Override + protected Element generateHead(final Opml opml) { + + final Element headElement = super.generateHead(opml); + + if (null != opml && null != opml.getCloud()) { + final Element cloudElement = new Element("cloud"); + addNotNullAttribute(cloudElement, "domain", opml.getCloud().getDomain()); + addNotNullAttribute(cloudElement, "port", opml.getCloud().getPort()); + addNotNullAttribute(cloudElement, "path", opml.getCloud().getPath()); + addNotNullAttribute(cloudElement, "registerProcedure", opml.getCloud().getRegisterProcedure()); + addNotNullAttribute(cloudElement, "protocol", opml.getCloud().getProtocol()); + headElement.addContent(cloudElement); + } + + return headElement; + + } + +} diff --git a/rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Parser.java b/rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Parser.java new file mode 100644 index 000000000..27866131d --- /dev/null +++ b/rome-opml/src/main/java/com/rometools/opml/io/impl/OPML11Parser.java @@ -0,0 +1,107 @@ +/* + * Opml11Parser.java + * + * Created on April 19, 2023, 11:49 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.Locale; + +import org.jdom2.Document; +import org.jdom2.Element; + +import com.rometools.opml.feed.opml.Cloud; +import com.rometools.opml.feed.opml.Opml; +import com.rometools.rome.feed.WireFeed; +import com.rometools.rome.io.FeedException; + +public class OPML11Parser extends OPML10Parser { + + public OPML11Parser() { + super("opml_1.1"); + } + + /** + * Inspects an XML Document (JDOM) to check if it can parse it. + *

+ * It checks if the given document if the type of feeds the parser understands. + *

+ * + * @param document XML Document (JDOM) to check if it can be parsed by this parser. + * @return true if the parser know how to parser this feed, false otherwise. + */ + @Override + public boolean isMyType(final Document document) { + final Element e = document.getRootElement(); + + return e.getName().equals("opml") + && (null != e.getChild("head") && + null != e.getChild("head").getChild("cloud") || + null != e.getAttributeValue("version") && + "1.1".equals(e.getAttributeValue("version"))); + } + + /** + * Parses an XML document (JDOM Document) into a feed bean. + *

+ * + * @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); + opml.setFeedType("opml_1.1"); + + final Element root = document.getRootElement(); + final Element head = root.getChild("head"); + final Element cloud = head.getChild("cloud"); + + if (null != cloud) { + if (null != cloud.getAttribute("domain")) { + checkNullCloud(opml); + opml.getCloud().setDomain(cloud.getAttributeValue("domain")); + } + if (null != cloud.getAttribute("port")) { + checkNullCloud(opml); + opml.getCloud().setPort(cloud.getAttributeValue("port")); + } + if (null != cloud.getAttribute("path")) { + checkNullCloud(opml); + opml.getCloud().setPath(cloud.getAttributeValue("path")); + } + if (null != cloud.getAttribute("registerProcedure")) { + checkNullCloud(opml); + opml.getCloud().setRegisterProcedure(cloud.getAttributeValue("registerProcedure")); + } + if (null != cloud.getAttribute("protocol")) { + checkNullCloud(opml); + opml.getCloud().setProtocol(cloud.getAttributeValue("protocol")); + } + } + + return opml; + } + + private void checkNullCloud(Opml opml) { + if (null == opml.getCloud()) { + opml.setCloud(new Cloud()); + } + } +} diff --git a/rome-opml/src/main/resources/rome.properties b/rome-opml/src/main/resources/rome.properties index f57e9e3c8..02efca00c 100644 --- a/rome-opml/src/main/resources/rome.properties +++ b/rome-opml/src/main/resources/rome.properties @@ -13,10 +13,13 @@ WireFeedGenerator.classes=com.rometools.opml.io.impl.OPML10Generator \ + com.rometools.opml.io.impl.OPML11Generator \ com.rometools.opml.io.impl.OPML20Generator WireFeedParser.classes=com.rometools.opml.io.impl.OPML10Parser \ + com.rometools.opml.io.impl.OPML11Parser \ com.rometools.opml.io.impl.OPML20Parser Converter.classes=com.rometools.opml.feed.synd.impl.ConverterForOPML10 \ + com.rometools.opml.feed.synd.impl.ConverterForOPML11 \ com.rometools.opml.feed.synd.impl.ConverterForOPML20 diff --git a/rome-opml/src/test/java/com/rometools/opml/TestOpsOPML11states.java b/rome-opml/src/test/java/com/rometools/opml/TestOpsOPML11states.java new file mode 100644 index 000000000..25e576cab --- /dev/null +++ b/rome-opml/src/test/java/com/rometools/opml/TestOpsOPML11states.java @@ -0,0 +1,71 @@ +/* + * TestOpsOPML11states.java + * + * Created on April 19, 2023, 11:43 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. + * + * 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; + + public class TestOpsOPML11states extends FeedOpsTest { + + public TestOpsOPML11states() { + super("opml_1.1_states"); + } + + // 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.1_states.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.1"); + output.output(sf.createWireFeed(), new NullWriter()); + } + + } + \ No newline at end of file diff --git a/rome-opml/src/test/resources/opml_1.1_states.xml b/rome-opml/src/test/resources/opml_1.1_states.xml new file mode 100644 index 000000000..6762368a4 --- /dev/null +++ b/rome-opml/src/test/resources/opml_1.1_states.xml @@ -0,0 +1,93 @@ + + + + states + Fri, 21 Dec 2001 20:46:26 GMT + Sat, 22 Dec 2001 22:41:18 GMT + Dave Winer + dave@userland.com + 1,12 + 1 + 92 + 252 + 558 + 736 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +