diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/georss/GeoRssExtensions.java b/src/main/java/com/apptasticsoftware/rssreader/module/georss/GeoRssExtensions.java
index 17215e69..a66f3013 100644
--- a/src/main/java/com/apptasticsoftware/rssreader/module/georss/GeoRssExtensions.java
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/georss/GeoRssExtensions.java
@@ -65,7 +65,7 @@ private static void itemTagExtensions(FeedExtensionRegistry extends GeoRssChan
registry.addItemExtension("georss:polygon", GeoRssItem::setGeoRssPolygon);
registry.addItemExtension("georss:box", GeoRssItem::setGeoRssBox);
registry.addItemExtension("georss:elev", (item, value) -> mapDouble(value, item::setGeoRssElevation));
- registry.addItemExtension("georss:floor", (item, value) -> mapInteger(value, item::setGeoRssFloor));
+ registry.addItemExtension("georss:floor", (item, value) -> mapInteger(value, item::setGeoRssFloor));
registry.addItemExtension("georss:radius", (item, value) -> mapDouble(value, item::setGeoRssRadius));
registry.addItemExtension("georss:featuretypetag", GeoRssItem::setGeoRssFeatureTypeTag);
registry.addItemExtension("georss:relationshiptag", GeoRssItem::setGeoRssRelationshipTag);
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashChannel.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashChannel.java
new file mode 100644
index 00000000..3102a798
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashChannel.java
@@ -0,0 +1,13 @@
+package com.apptasticsoftware.rssreader.module.slash;
+
+import com.apptasticsoftware.rssreader.Channel;
+
+/**
+ * Slash RSS module channel interface extending the core Channel with Slash-specific metadata.
+ * Slash is a module for RSS feeds from Slash-based sites (like Slashdot).
+ *
+ * @see SlashItem for item-level Slash extensions
+ * @see Slash RSS Module Specification
+ */
+public interface SlashChannel extends Channel {
+}
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashExtensions.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashExtensions.java
new file mode 100644
index 00000000..b909e328
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashExtensions.java
@@ -0,0 +1,38 @@
+package com.apptasticsoftware.rssreader.module.slash;
+
+import com.apptasticsoftware.rssreader.FeedExtensionRegistry;
+
+import static com.apptasticsoftware.rssreader.util.Mapper.mapInteger;
+
+/**
+ * Utility class for registering Slash RSS module extensions.
+ * Registers handlers for Slash-specific XML elements during RSS feed parsing.
+ */
+public class SlashExtensions {
+
+ private SlashExtensions() {
+ // Prevent instantiation
+ }
+
+ /**
+ * Registers all Slash module extensions with the provided feed extension registry.
+ *
+ * @param registry the feed extension registry to register handlers with
+ */
+ public static void register(FeedExtensionRegistry extends SlashChannel, ? extends SlashItem> registry) {
+ itemTagExtensions(registry);
+ }
+
+ /**
+ * Registers Slash item-level tag extensions.
+ *
+ * @param registry the feed extension registry to register handlers with
+ */
+ private static void itemTagExtensions(FeedExtensionRegistry extends SlashChannel, ? extends SlashItem> registry) {
+ registry.addItemExtension("slash:section", SlashItem::setSlashSection);
+ registry.addItemExtension("slash:department", SlashItem::setSlashDepartment);
+ registry.addItemExtension("slash:comments", (item, value) -> mapInteger(value, item::setSlashComments));
+ registry.addItemExtension("slash:hit_parade", SlashItem::setSlashHitParade);
+ }
+
+}
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashFeedReader.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashFeedReader.java
new file mode 100644
index 00000000..9dae78fa
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashFeedReader.java
@@ -0,0 +1,33 @@
+package com.apptasticsoftware.rssreader.module.slash;
+
+import com.apptasticsoftware.rssreader.AbstractRssReader;
+import com.apptasticsoftware.rssreader.DateTimeParser;
+import com.apptasticsoftware.rssreader.module.slash.internal.SlashChannelImpl;
+import com.apptasticsoftware.rssreader.module.slash.internal.SlashItemImpl;
+
+/**
+ * RSS feed reader with Slash module extensions.
+ * Provides parsing of RSS feeds that include Slash-specific metadata for items.
+ *
+ * @see SlashItem for item-level Slash properties
+ * @see SlashChannel for channel-level Slash properties
+ */
+public class SlashFeedReader extends AbstractRssReader {
+
+ @Override
+ protected SlashChannel createChannel(DateTimeParser dateTimeParser) {
+ return new SlashChannelImpl(dateTimeParser);
+ }
+
+ @Override
+ protected SlashItem createItem(DateTimeParser dateTimeParser) {
+ return new SlashItemImpl(dateTimeParser);
+ }
+
+ @Override
+ protected void registerChannelTags() {
+ super.registerChannelTags();
+ var registry = getFeedExtensionRegistry();
+ SlashExtensions.register(registry);
+ }
+}
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashItem.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashItem.java
new file mode 100644
index 00000000..1e7b13db
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashItem.java
@@ -0,0 +1,13 @@
+package com.apptasticsoftware.rssreader.module.slash;
+
+import com.apptasticsoftware.rssreader.Item;
+
+/**
+ * Slash RSS module item interface extending the core Item with Slash-specific metadata.
+ * Provides access to item-level Slash elements including section, department, comments count, and hit parade.
+ *
+ * @see SlashItemData for the data accessor methods
+ * @see Slash RSS Module Specification
+ */
+public interface SlashItem extends Item, SlashItemData {
+}
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashItemData.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashItemData.java
new file mode 100644
index 00000000..1d780a9d
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/SlashItemData.java
@@ -0,0 +1,91 @@
+package com.apptasticsoftware.rssreader.module.slash;
+
+import java.util.Optional;
+
+/**
+ * Interface for accessing Slash RSS module item data.
+ * Provides methods to get and set Slash-specific metadata including section, department, comments, and hit parade.
+ *
+ * @see Slash RSS Module Specification
+ */
+public interface SlashItemData {
+
+ /**
+ * Returns the underlying Slash item data object.
+ *
+ * @return the Slash item data
+ */
+ SlashItemData getSlashItemData();
+
+ /**
+ * Returns the Slash section (category) of the item.
+ *
+ * @return an Optional containing the section, or empty if not set
+ */
+ default Optional getSlashSection() {
+ return getSlashItemData().getSlashSection();
+ }
+
+ /**
+ * Sets the Slash section (category) of the item.
+ *
+ * @param slashSection the section to set
+ */
+ default void setSlashSection(String slashSection) {
+ getSlashItemData().setSlashSection(slashSection);
+ }
+
+ /**
+ * Returns the Slash department of the item.
+ *
+ * @return an Optional containing the department, or empty if not set
+ */
+ default Optional getSlashDepartment() {
+ return getSlashItemData().getSlashDepartment();
+ }
+
+ /**
+ * Sets the Slash department of the item.
+ *
+ * @param slashDepartment the department to set
+ */
+ default void setSlashDepartment(String slashDepartment) {
+ getSlashItemData().setSlashDepartment(slashDepartment);
+ }
+
+ /**
+ * Returns the number of comments for the item.
+ *
+ * @return an Optional containing the comment count, or empty if not set
+ */
+ default Optional getSlashComments() {
+ return getSlashItemData().getSlashComments();
+ }
+
+ /**
+ * Sets the number of comments for the item.
+ *
+ * @param slashComments the comment count to set
+ */
+ default void setSlashComments(Integer slashComments) {
+ getSlashItemData().setSlashComments(slashComments);
+ }
+
+ /**
+ * Returns the Slash hit parade (comma-separated list of view counts).
+ *
+ * @return an Optional containing the hit parade, or empty if not set
+ */
+ default Optional getSlashHitParade() {
+ return getSlashItemData().getSlashHitParade();
+ }
+
+ /**
+ * Sets the Slash hit parade (comma-separated list of view counts).
+ *
+ * @param slashHitParade the hit parade to set
+ */
+ default void setSlashHitParade(String slashHitParade) {
+ getSlashItemData().setSlashHitParade(slashHitParade);
+ }
+}
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashChannelImpl.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashChannelImpl.java
new file mode 100644
index 00000000..6d1f572e
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashChannelImpl.java
@@ -0,0 +1,20 @@
+package com.apptasticsoftware.rssreader.module.slash.internal;
+
+import com.apptasticsoftware.rssreader.DateTimeParser;
+import com.apptasticsoftware.rssreader.internal.ChannelImpl;
+import com.apptasticsoftware.rssreader.module.slash.SlashChannel;
+
+/**
+ * Implementation of SlashChannel extending core channel functionality.
+ */
+public class SlashChannelImpl extends ChannelImpl implements SlashChannel {
+
+ /**
+ * Constructs a SlashChannelImpl with the provided date-time parser.
+ *
+ * @param dateTimeParser the parser for parsing date-time values
+ */
+ public SlashChannelImpl(DateTimeParser dateTimeParser) {
+ super(dateTimeParser);
+ }
+}
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashItemDataImpl.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashItemDataImpl.java
new file mode 100644
index 00000000..c2007275
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashItemDataImpl.java
@@ -0,0 +1,113 @@
+package com.apptasticsoftware.rssreader.module.slash.internal;
+
+import com.apptasticsoftware.rssreader.module.slash.SlashItemData;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Implementation of SlashItemData storing Slash-specific item metadata.
+ */
+public class SlashItemDataImpl implements SlashItemData {
+ private String slashSection;
+ private String slashDepartment;
+ private Integer slashComments;
+ private String slashHitParade;
+
+ @Override
+ public SlashItemData getSlashItemData() {
+ return this;
+ }
+
+ /**
+ * Returns the Slash section of the item.
+ *
+ * @return an Optional containing the section, or empty if not set
+ */
+ @Override
+ public Optional getSlashSection() {
+ return Optional.ofNullable(slashSection);
+ }
+
+ /**
+ * Sets the Slash section of the item.
+ *
+ * @param slashSection the section to set
+ */
+ @Override
+ public void setSlashSection(String slashSection) {
+ this.slashSection = slashSection;
+ }
+
+ /**
+ * Returns the Slash department of the item.
+ *
+ * @return an Optional containing the department, or empty if not set
+ */
+ @Override
+ public Optional getSlashDepartment() {
+ return Optional.ofNullable(slashDepartment);
+ }
+
+ /**
+ * Sets the Slash department of the item.
+ *
+ * @param slashDepartment the department to set
+ */
+ @Override
+ public void setSlashDepartment(String slashDepartment) {
+ this.slashDepartment = slashDepartment;
+ }
+
+ /**
+ * Returns the number of comments for the item.
+ *
+ * @return an Optional containing the comment count, or empty if not set
+ */
+ @Override
+ public Optional getSlashComments() {
+ return Optional.ofNullable(slashComments);
+ }
+
+ /**
+ * Sets the number of comments for the item.
+ *
+ * @param slashComments the comment count to set
+ */
+ @Override
+ public void setSlashComments(Integer slashComments) {
+ this.slashComments = slashComments;
+ }
+
+ /**
+ * Returns the Slash hit parade (comma-separated list of view counts).
+ *
+ * @return an Optional containing the hit parade, or empty if not set
+ */
+ @Override
+ public Optional getSlashHitParade() {
+ return Optional.ofNullable(slashHitParade);
+ }
+
+ /**
+ * Sets the Slash hit parade (comma-separated list of view counts).
+ *
+ * @param slashHitParade the hit parade to set
+ */
+ @Override
+ public void setSlashHitParade(String slashHitParade) {
+ this.slashHitParade = slashHitParade;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) return false;
+ SlashItemDataImpl that = (SlashItemDataImpl) o;
+ return Objects.equals(getSlashSection(), that.getSlashSection()) && Objects.equals(getSlashDepartment(), that.getSlashDepartment()) && Objects.equals(getSlashComments(), that.getSlashComments()) && Objects.equals(getSlashHitParade(), that.getSlashHitParade());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getSlashSection(), getSlashDepartment(), getSlashComments(), getSlashHitParade());
+ }
+}
diff --git a/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashItemImpl.java b/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashItemImpl.java
new file mode 100644
index 00000000..0244fc4f
--- /dev/null
+++ b/src/main/java/com/apptasticsoftware/rssreader/module/slash/internal/SlashItemImpl.java
@@ -0,0 +1,42 @@
+package com.apptasticsoftware.rssreader.module.slash.internal;
+
+import com.apptasticsoftware.rssreader.DateTimeParser;
+import com.apptasticsoftware.rssreader.internal.ItemImpl;
+import com.apptasticsoftware.rssreader.module.slash.SlashItem;
+import com.apptasticsoftware.rssreader.module.slash.SlashItemData;
+
+import java.util.Objects;
+
+/**
+ * Implementation of SlashItem combining core item functionality with Slash-specific metadata.
+ */
+public class SlashItemImpl extends ItemImpl implements SlashItem {
+ private final SlashItemData slashData = new SlashItemDataImpl();
+
+ /**
+ * Constructs a SlashItemImpl with the provided date-time parser.
+ *
+ * @param dateTimeParser the parser for parsing date-time values
+ */
+ public SlashItemImpl(DateTimeParser dateTimeParser) {
+ super(dateTimeParser);
+ }
+
+ @Override
+ public SlashItemData getSlashItemData() {
+ return slashData;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SlashItemImpl)) return false;
+ if (!super.equals(o)) return false;
+ SlashItemImpl slashItem = (SlashItemImpl) o;
+ return Objects.equals(getSlashItemData(), slashItem.getSlashItemData());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), getSlashItemData());
+ }
+}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 170855c0..1b80bbd4 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -38,6 +38,7 @@
exports com.apptasticsoftware.rssreader.module.opensearch;
exports com.apptasticsoftware.rssreader.module.podcast;
exports com.apptasticsoftware.rssreader.module.psc;
+ exports com.apptasticsoftware.rssreader.module.slash;
exports com.apptasticsoftware.rssreader.module.spotify;
exports com.apptasticsoftware.rssreader.module.wfw;
exports com.apptasticsoftware.rssreader.module.youtube;
diff --git a/src/test/java/com/apptasticsoftware/rssreader/module/slash/SlashFeedReaderTest.java b/src/test/java/com/apptasticsoftware/rssreader/module/slash/SlashFeedReaderTest.java
new file mode 100644
index 00000000..8ddc2d51
--- /dev/null
+++ b/src/test/java/com/apptasticsoftware/rssreader/module/slash/SlashFeedReaderTest.java
@@ -0,0 +1,48 @@
+package com.apptasticsoftware.rssreader.module.slash;
+
+import com.apptasticsoftware.rssreader.module.slash.internal.SlashItemDataImpl;
+import com.apptasticsoftware.rssreader.module.slash.internal.SlashItemImpl;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.util.stream.Collectors;
+
+import static com.github.npathai.hamcrestopt.OptionalMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@SuppressWarnings("java:S5961")
+class SlashFeedReaderTest {
+
+ @Test
+ void example1() {
+ var items = new SlashFeedReader().read(fromFile("module/slash/example1.xml")).collect(Collectors.toList());
+ assertEquals(1, items.size());
+
+ var item = items.get(0);
+ var channel = item.getChannel();
+ assertThat(channel.getTitle(), is("Slashdot"));
+ assertThat(channel.getDescription(), is("News for nerds, stuff that matters"));
+
+ assertThat(item.getTitle(), isPresentAndIs("Jupiter Moon Ganymede May Have An Ocean"));
+ assertThat(item.getLink(), isPresentAndIs("http://slashdot.org/article.pl?sid=00/12/17/0622203"));
+ assertThat(item.getDescription(), isPresentAnd(startsWith("This article talks about how Jupiter's moon, Ganymede, may have a")));
+ assertThat(item.getSlashSection(), isPresentAndIs("articles"));
+ assertThat(item.getSlashDepartment(), isPresentAndIs("not-an-ocean-unless-there-are-lobsters"));
+ assertThat(item.getSlashComments(), isPresentAndIs(177));
+ assertThat(item.getSlashHitParade(), isPresentAndIs("177,155,105,33,6,3,0"));
+ }
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.simple().forClass(SlashItemImpl.class).withNonnullFields("slashData").withIgnoredFields("defaultComparator").withIgnoredFields("dateTimeParser").withIgnoredFields("category").withNonnullFields("categories").withIgnoredFields("enclosure").withNonnullFields("enclosures").verify();
+ EqualsVerifier.simple().forClass(SlashItemDataImpl.class).verify();
+ }
+
+ private InputStream fromFile(String fileName) {
+ return getClass().getClassLoader().getResourceAsStream(fileName);
+ }
+}
diff --git a/src/test/resources/module/slash/example1.xml b/src/test/resources/module/slash/example1.xml
new file mode 100644
index 00000000..04499de4
--- /dev/null
+++ b/src/test/resources/module/slash/example1.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ Slashdot
+ http://slashdot.org
+ News for nerds, stuff that matters
+ OSDN
+ pater@slashdot.org
+ Copyright © 2000 Slashdot
+ en-us
+ 2000-12-17T01:17-05:00
+
+ -
+ Jupiter Moon Ganymede May Have An Ocean
+ http://slashdot.org/article.pl?sid=00/12/17/0622203
+
+ This article talks about how Jupiter's moon, Ganymede, may have a
+ salt water ocean on it. Kind of ...
+
+ timothy
+ space
+ 2000-12-17T01:17
+ articles
+ not-an-ocean-unless-there-are-lobsters
+ 177
+ 177,155,105,33,6,3,0
+
+
+
\ No newline at end of file