diff --git a/src/main/java/com/rometools/modules/atomthread/ThreadModule.java b/src/main/java/com/rometools/modules/atomthread/ThreadModule.java
deleted file mode 100644
index 09e0f2a..0000000
--- a/src/main/java/com/rometools/modules/atomthread/ThreadModule.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2011 robert.cooper.
- *
- * 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.
- * under the License.
- */
-
-package com.rometools.modules.atomthread;
-
-/**
- *
- * @author robert.cooper
- */
-public interface ThreadModule {
-
-}
diff --git a/src/main/java/com/rometools/modules/thr/ThreadingModule.java b/src/main/java/com/rometools/modules/thr/ThreadingModule.java
new file mode 100644
index 0000000..98f19b9
--- /dev/null
+++ b/src/main/java/com/rometools/modules/thr/ThreadingModule.java
@@ -0,0 +1,29 @@
+package com.rometools.modules.thr;
+
+import com.rometools.rome.feed.module.Module;
+
+/**
+ * Currently no support for thr:count, thr:updated, thr:total link attributes.
+ *
+ * @author Andreas Feldschmid
+ */
+public interface ThreadingModule extends Module {
+
+ public static final String URI = "http://purl.org/syndication/thread/1.0";
+
+ public String getRef();
+
+ public void setRef(String ref);
+
+ public String getType();
+
+ public void setType(String type);
+
+ public String getHref();
+
+ public void setHref(String href);
+
+ public String getSource();
+
+ public void setSource(String source);
+}
diff --git a/src/main/java/com/rometools/modules/thr/ThreadingModuleImpl.java b/src/main/java/com/rometools/modules/thr/ThreadingModuleImpl.java
new file mode 100644
index 0000000..e0a44c3
--- /dev/null
+++ b/src/main/java/com/rometools/modules/thr/ThreadingModuleImpl.java
@@ -0,0 +1,110 @@
+package com.rometools.modules.thr;
+
+import com.rometools.rome.feed.CopyFrom;
+import com.rometools.rome.feed.impl.EqualsBean;
+import com.rometools.rome.feed.impl.ToStringBean;
+import com.rometools.rome.feed.module.ModuleImpl;
+
+/**
+ * Currently no support for thr:count, thr:updated, thr:total link attributes.
+ *
+ * @author Andreas Feldschmid
+ */
+public class ThreadingModuleImpl extends ModuleImpl implements ThreadingModule {
+
+ private static final long serialVersionUID = 1L;
+
+ private String ref;
+ private String href;
+ private String type;
+ private String source;
+
+ public ThreadingModuleImpl() {
+ super(ThreadingModule.class, ThreadingModule.URI);
+ }
+
+ @Override
+ public Class extends CopyFrom> getInterface() {
+ return ThreadingModule.class;
+ }
+
+ @Override
+ public void copyFrom(CopyFrom copyFrom) {
+ if (copyFrom instanceof ThreadingModule) {
+ ThreadingModule module = (ThreadingModule) copyFrom;
+ setHref(module.getHref());
+ setRef(module.getRef());
+ setType(module.getType());
+ setSource(module.getSource());
+ }
+ }
+
+ @Override
+ public String getRef() {
+ return ref;
+ }
+
+ @Override
+ public void setRef(String ref) {
+ this.ref = ref;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String getHref() {
+ return href;
+ }
+
+ @Override
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ @Override
+ public String getSource() {
+ return source;
+ }
+
+ @Override
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ @Override
+ public Object clone() {
+ final ThreadingModule m = new ThreadingModuleImpl();
+ m.setHref(href);
+ m.setRef(ref);
+ m.setSource(source);
+ m.setType(type);
+ return m;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ final EqualsBean eBean = new EqualsBean(ThreadingModuleImpl.class, this);
+ return eBean.beanEquals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ final EqualsBean equals = new EqualsBean(ThreadingModuleImpl.class, this);
+ return equals.beanHashCode();
+ }
+
+ @Override
+ public String toString() {
+ final ToStringBean tsBean = new ToStringBean(ThreadingModuleImpl.class, this);
+ return tsBean.toString();
+ }
+
+}
diff --git a/src/main/java/com/rometools/modules/thr/io/ThreadingModuleGenerator.java b/src/main/java/com/rometools/modules/thr/io/ThreadingModuleGenerator.java
new file mode 100644
index 0000000..af51f83
--- /dev/null
+++ b/src/main/java/com/rometools/modules/thr/io/ThreadingModuleGenerator.java
@@ -0,0 +1,62 @@
+package com.rometools.modules.thr.io;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jdom2.Element;
+import org.jdom2.Namespace;
+
+import com.rometools.modules.thr.ThreadingModule;
+import com.rometools.rome.feed.module.Module;
+import com.rometools.rome.io.ModuleGenerator;
+
+/**
+ * Currently no support for thr:count, thr:updated, thr:total link attributes.
+ *
+ * @author Andreas Feldschmid
+ */
+public class ThreadingModuleGenerator implements ModuleGenerator {
+
+ private static final Namespace NAMESPACE = Namespace.getNamespace("thr", ThreadingModule.URI);
+ private static final Set NAMESPACES;
+
+ static {
+ Set namespaces = new HashSet();
+ namespaces.add(NAMESPACE);
+ NAMESPACES = Collections.unmodifiableSet(namespaces);
+ }
+
+ @Override
+ public String getNamespaceUri() {
+ return ThreadingModule.URI;
+ }
+
+ @Override
+ public Set getNamespaces() {
+ return NAMESPACES;
+ }
+
+ @Override
+ public void generate(Module module, Element element) {
+ if (module != null && module instanceof ThreadingModule) {
+ ThreadingModule threadedModule = (ThreadingModule) module;
+ Element inReplyTo = new Element("in-reply-to", NAMESPACE);
+
+ if (threadedModule.getHref() != null) {
+ inReplyTo.setAttribute("href", threadedModule.getHref());
+ }
+ if (threadedModule.getRef() != null) {
+ inReplyTo.setAttribute("ref", threadedModule.getRef());
+ }
+ if (threadedModule.getType() != null) {
+ inReplyTo.setAttribute("type", threadedModule.getType());
+ }
+ if (threadedModule.getSource() != null) {
+ inReplyTo.setAttribute("source", threadedModule.getSource());
+ }
+
+ element.addContent(inReplyTo);
+ }
+ }
+}
diff --git a/src/main/java/com/rometools/modules/thr/io/ThreadingModuleParser.java b/src/main/java/com/rometools/modules/thr/io/ThreadingModuleParser.java
new file mode 100644
index 0000000..ecba871
--- /dev/null
+++ b/src/main/java/com/rometools/modules/thr/io/ThreadingModuleParser.java
@@ -0,0 +1,42 @@
+package com.rometools.modules.thr.io;
+
+import java.util.Locale;
+
+import org.jdom2.Element;
+import org.jdom2.Namespace;
+
+import com.rometools.modules.thr.ThreadingModule;
+import com.rometools.modules.thr.ThreadingModuleImpl;
+import com.rometools.rome.feed.module.Module;
+import com.rometools.rome.io.ModuleParser;
+
+/**
+ * Currently no support for thr:count, thr:updated, thr:total link attributes.
+ *
+ * @author Andreas Feldschmid
+ */
+public class ThreadingModuleParser implements ModuleParser {
+
+ private static final Namespace NS = Namespace.getNamespace(ThreadingModule.URI);
+
+ @Override
+ public String getNamespaceUri() {
+ return ThreadingModule.URI;
+ }
+
+ @Override
+ public Module parse(final Element element, final Locale locale) {
+ final ThreadingModule tm = new ThreadingModuleImpl();
+ Element inReplyTo = element.getChild("in-reply-to", ThreadingModuleParser.NS);
+
+ if (inReplyTo != null) {
+ tm.setHref(inReplyTo.getAttributeValue("href"));
+ tm.setRef(inReplyTo.getAttributeValue("ref"));
+ tm.setSource(inReplyTo.getAttributeValue("source"));
+ tm.setType(inReplyTo.getAttributeValue("type"));
+ return tm;
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/resources/rome.properties b/src/main/resources/rome.properties
index 0fb85df..1a8707f 100644
--- a/src/main/resources/rome.properties
+++ b/src/main/resources/rome.properties
@@ -76,7 +76,8 @@ atom_1.0.item.ModuleParser.classes=com.rometools.modules.cc.io.ModuleParserRSS2
com.rometools.modules.georss.W3CGeoParser \
com.rometools.modules.photocast.io.Parser \
com.rometools.modules.mediarss.io.MediaModuleParser \
- com.rometools.modules.mediarss.io.AlternateMediaModuleParser
+ com.rometools.modules.mediarss.io.AlternateMediaModuleParser \
+ com.rometools.modules.thr.io.ThreadingModuleParser
rss_2.0.feed.ModuleGenerator.classes=com.rometools.modules.cc.io.CCModuleGenerator \
com.rometools.modules.content.io.ContentModuleGenerator \
@@ -141,7 +142,8 @@ atom_1.0.item.ModuleGenerator.classes=com.rometools.modules.cc.io.CCModuleGenera
com.rometools.modules.georss.SimpleGenerator \
com.rometools.modules.georss.W3CGeoGenerator \
com.rometools.modules.photocast.io.Generator \
- com.rometools.modules.mediarss.io.MediaModuleGenerator
+ com.rometools.modules.mediarss.io.MediaModuleGenerator \
+ com.rometools.modules.thr.io.ThreadingModuleGenerator
rss_2.0wNS.feed.ModuleParser.classes=com.rometools.modules.mediarss.io.MediaModuleParser
rss_2.0wNS.item.ModuleParser.classes=com.rometools.modules.mediarss.io.MediaModuleParser
diff --git a/src/test/java/com/rometools/modules/thr/io/ThreadingModuleTest.java b/src/test/java/com/rometools/modules/thr/io/ThreadingModuleTest.java
new file mode 100644
index 0000000..3c01c8d
--- /dev/null
+++ b/src/test/java/com/rometools/modules/thr/io/ThreadingModuleTest.java
@@ -0,0 +1,111 @@
+package com.rometools.modules.thr.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import com.rometools.modules.AbstractTestCase;
+import com.rometools.modules.thr.ThreadingModule;
+import com.rometools.modules.thr.ThreadingModuleImpl;
+import com.rometools.rome.feed.module.Module;
+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.io.FeedException;
+import com.rometools.rome.io.SyndFeedInput;
+import com.rometools.rome.io.SyndFeedOutput;
+
+/**
+ *
+ * @author Andreas Feldschmid
+ */
+public class ThreadingModuleTest extends AbstractTestCase {
+
+ public ThreadingModuleTest(String testName) {
+ super(testName);
+ }
+
+ public void testReadMainSpec() throws IOException, FeedException {
+ final SyndFeed feed = getSyndFeed("thr/threading-main.xml");
+ List entries = feed.getEntries();
+ SyndEntry parentEntry = entries.get(0);
+ assertEquals("should be the parent entry", "My original entry", parentEntry.getTitle());
+ assertNull(parentEntry.getModule(ThreadingModule.URI));
+
+ SyndEntry replyEntry = entries.get(1);
+ assertEquals("should be the reply entry", "A response to the original", replyEntry.getTitle());
+ Module module = replyEntry.getModule(ThreadingModule.URI);
+ assertNotNull(module);
+ ThreadingModule threadingModule = (ThreadingModule) module;
+ assertEquals("tag:example.org,2005:1", threadingModule.getRef());
+ assertEquals("application/xhtml+xml", threadingModule.getType());
+ assertEquals("http://www.example.org/entries/1", threadingModule.getHref());
+ assertNull(threadingModule.getSource());
+ }
+
+ public void testGenerate() throws IOException, FeedException {
+ final SyndFeed feed = getSyndFeed("thr/threading-main.xml");
+ List entries = feed.getEntries();
+
+ // create a new "root" entry that the next entry will reference to
+ SyndEntry newRootEntry = new SyndEntryImpl();
+ newRootEntry.setTitle("New, 2nd root entry");
+ newRootEntry.setUri("tag:example.org,2005:2");
+ newRootEntry.setLink("http://www.example.org/entries/2");
+ entries.add(newRootEntry);
+
+ // create a new reply entry that will reference the new root entry
+ SyndEntry newReplyEntry = new SyndEntryImpl();
+ newReplyEntry.setTitle("New test reply entry");
+ newReplyEntry.setUri("tag:example.org,2005:2,1");
+
+ ThreadingModule threadingModule = new ThreadingModuleImpl();
+ threadingModule.setRef("tag:example.org,2005:2");
+ threadingModule.setType("application/xhtml+xml");
+ threadingModule.setHref("http://www.example.org/entries/2");
+ threadingModule.setSource("http://example.org/entries/2");
+
+ newReplyEntry.getModules().add(threadingModule);
+ entries.add(newReplyEntry);
+
+ File outputFile = new File("target/threading-testGenerate.xml");
+ final SyndFeedOutput output = new SyndFeedOutput();
+ output.output(feed, outputFile);
+
+ // read back in and validate
+ final SyndFeed generatedFeed = getSyndFeed(outputFile);
+ SyndEntry generatedReplyEntry = generatedFeed.getEntries().get(3);
+ assertNotNull(generatedReplyEntry);
+ ThreadingModule generatedReplyThreadingModule = (ThreadingModule) generatedReplyEntry.getModule(ThreadingModule.URI);
+ assertEquals("tag:example.org,2005:2", generatedReplyThreadingModule.getRef());
+ assertEquals("application/xhtml+xml", generatedReplyThreadingModule.getType());
+ assertEquals("http://www.example.org/entries/2", generatedReplyThreadingModule.getHref());
+ assertEquals("http://example.org/entries/2", generatedReplyThreadingModule.getSource());
+ }
+
+ public void testEnd2End() throws IOException, FeedException {
+ final SyndFeed feed = getSyndFeed("thr/threading-main.xml");
+ final SyndFeedOutput output = new SyndFeedOutput();
+ File outputFile = new File("target/threading-main-generated.xml");
+ output.output(feed, outputFile);
+
+ final SyndFeed feedOut = getSyndFeed(outputFile);
+
+ ThreadingModule moduleSrc = (ThreadingModule) feed.getEntries().get(1).getModule(ThreadingModule.URI);
+ ThreadingModule moduleOut = (ThreadingModule) feedOut.getEntries().get(1).getModule(ThreadingModule.URI);
+ assertEquals(moduleSrc, moduleOut);
+ assertEquals("tag:example.org,2005:1", moduleSrc.getRef());
+ assertEquals("tag:example.org,2005:1", moduleOut.getRef());
+ }
+
+ private SyndFeed getSyndFeed(final File file) throws IOException, FeedException {
+ return new SyndFeedInput().build(file);
+ }
+
+ private SyndFeed getSyndFeed(final String filePath) throws IOException, FeedException {
+ final String fullPath = getTestFile(filePath);
+ final File file = new File(fullPath);
+ return new SyndFeedInput().build(file);
+ }
+
+}
diff --git a/src/test/resources/thr/threading-main.xml b/src/test/resources/thr/threading-main.xml
new file mode 100644
index 0000000..6d22220
--- /dev/null
+++ b/src/test/resources/thr/threading-main.xml
@@ -0,0 +1,25 @@
+
+ http://www.example.org/myfeed
+ My Example Feed
+ 2005-07-28T12:00:00Z
+
+
+ James
+
+
+ tag:example.org,2005:1
+ My original entry
+ 2006-03-01T12:12:12Z
+
+ This is my original entry
+
+
+ tag:example.org,2005:1,1
+ A response to the original
+ 2006-03-01T12:12:12Z
+
+
+ This is a response to the original entry
+
+