Skip to content

Commit 3bf20e3

Browse files
authored
Merge pull request #140 from w3stling/support-for-multiple-enclosures
Support for multiple enclosures
2 parents 16bf235 + d76e48a commit 3bf20e3

File tree

6 files changed

+97
-7
lines changed

6 files changed

+97
-7
lines changed

src/main/java/com/apptasticsoftware/rssreader/AbstractRssReader.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public abstract class AbstractRssReader<C extends Channel, I extends Item> {
7070
private final Map<String, String> headers = new HashMap<>();
7171
private final HashMap<String, BiConsumer<C, String>> channelTags = new HashMap<>();
7272
private final HashMap<String, Map<String, BiConsumer<C, String>>> channelAttributes = new HashMap<>();
73+
private final HashMap<String, Consumer<I>> onItemTags = new HashMap<>();
7374
private final HashMap<String, BiConsumer<I, String>> itemTags = new HashMap<>();
7475
private final HashMap<String, Map<String, BiConsumer<I, String>>> itemAttributes = new HashMap<>();
7576
private final Set<String> collectChildNodesForTag = Set.of("content", "summary");
@@ -204,6 +205,8 @@ protected void registerItemTags() {
204205
itemTags.putIfAbsent("comments", Item::setComments);
205206
itemTags.putIfAbsent("dc:creator", (i, v) -> Mapper.mapIfEmpty(v, i::getAuthor, i::setAuthor));
206207
itemTags.putIfAbsent("dc:date", (i, v) -> Mapper.mapIfEmpty(v, i::getPubDate, i::setPubDate));
208+
209+
onItemTags.put("enclosure", (i) -> i.addEnclosure(new Enclosure()));
207210
}
208211

209212
/**
@@ -214,9 +217,9 @@ protected void registerItemAttributes() {
214217
itemAttributes.computeIfAbsent("guid", k -> new HashMap<>()).putIfAbsent("isPermaLink", (i, v) -> i.setIsPermaLink(Boolean.parseBoolean(v)) );
215218

216219
var enclosureAttributes = itemAttributes.computeIfAbsent("enclosure", k -> new HashMap<>());
217-
enclosureAttributes.putIfAbsent("url", (i, v) -> createIfNull(i::getEnclosure, i::setEnclosure, Enclosure::new).setUrl(v));
218-
enclosureAttributes.putIfAbsent("type", (i, v) -> createIfNull(i::getEnclosure, i::setEnclosure, Enclosure::new).setType(v));
219-
enclosureAttributes.putIfAbsent("length", (i, v) -> createIfNullOptional(i::getEnclosure, i::setEnclosure, Enclosure::new).ifPresent(e -> mapLong(v, e::setLength)));
220+
enclosureAttributes.putIfAbsent("url", (i, v) -> i.getEnclosure().ifPresent(a -> a.setUrl(v)));
221+
enclosureAttributes.putIfAbsent("type", (i, v) -> i.getEnclosure().ifPresent(a -> a.setType(v)));
222+
enclosureAttributes.putIfAbsent("length", (i, v) -> i.getEnclosure().ifPresent(e -> mapLong(v, e::setLength)));
220223
}
221224

222225
/**
@@ -671,6 +674,8 @@ void parseAttributes() {
671674
mapChannelAttributes(elementFullPath);
672675
}
673676
else if (isItemPart) {
677+
onItemTags.computeIfPresent(nsTagName, (k, f) -> { f.accept(item); return f; });
678+
onItemTags.computeIfPresent(getElementFullPath(), (k, f) -> { f.accept(item); return f; });
674679
// Map item attributes
675680
mapItemAttributes(nsTagName);
676681
mapItemAttributes(elementFullPath);

src/main/java/com/apptasticsoftware/rssreader/Item.java

+21-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class Item implements Comparable<Item> {
4747
private String pubDate;
4848
private String comments;
4949
private Enclosure enclosure;
50+
private final List<Enclosure> enclosures = new ArrayList<>();
5051
private Channel channel;
5152
private final DateTimeParser dateTimeParser;
5253

@@ -288,7 +289,25 @@ public Optional<Enclosure> getEnclosure() {
288289
* @param enclosure enclosure
289290
*/
290291
public void setEnclosure(Enclosure enclosure) {
292+
addEnclosure(enclosure);
293+
}
294+
295+
/**
296+
* Get enclosures for item.
297+
* Use this method if multiple enclosures exist per item.
298+
* @return list of enclosures
299+
*/
300+
public List<Enclosure> getEnclosures() {
301+
return Collections.unmodifiableList(enclosures);
302+
}
303+
304+
/**
305+
* Add enclosure for item.
306+
* @param enclosure enclosure
307+
*/
308+
public void addEnclosure(Enclosure enclosure) {
291309
this.enclosure = enclosure;
310+
enclosures.add(enclosure);
292311
}
293312

294313
/**
@@ -323,14 +342,14 @@ public boolean equals(Object o) {
323342
Objects.equals(getIsPermaLink(), item.getIsPermaLink()) &&
324343
Objects.equals(getPubDate(), item.getPubDate()) &&
325344
Objects.equals(getComments(), item.getComments()) &&
326-
Objects.equals(getEnclosure(), item.getEnclosure()) &&
345+
getEnclosures().equals(item.getEnclosures()) &&
327346
Objects.equals(getChannel(), item.getChannel());
328347
}
329348

330349
@Override
331350
public int hashCode() {
332351
return Objects.hash(getTitle(), getDescription(), getLink(), getAuthor(), getCategories(),
333-
getGuid(), getIsPermaLink(), getPubDate(), getComments(), getEnclosure(), getChannel());
352+
getGuid(), getIsPermaLink(), getPubDate(), getComments(), getEnclosures(), getChannel());
334353
}
335354

336355
/**

src/test/java/com/apptasticsoftware/integrationtest/RssReaderIntegrationTest.java

+27
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,33 @@ void testMultipleCategories() {
657657
assertTrue(item.getCategory().isPresent());
658658
}
659659

660+
@Test
661+
void testMultipleEnclosures() {
662+
var list = new RssReader().read(fromFile("multiple-enclosures.xml")).collect(Collectors.toList());
663+
664+
assertEquals(2, list.size());
665+
var item = list.get(0);
666+
assertEquals(3, item.getEnclosures().size());
667+
assertEquals("https://url1", item.getEnclosures().get(0).getUrl());
668+
assertEquals("image/jpeg", item.getEnclosures().get(0).getType());
669+
assertEquals(1L, item.getEnclosures().get(0).getLength().orElse(-1L));
670+
assertEquals("https://url2", item.getEnclosures().get(1).getUrl());
671+
assertEquals("image/png", item.getEnclosures().get(1).getType());
672+
assertEquals(2L, item.getEnclosures().get(1).getLength().orElse(-1L));
673+
assertEquals("https://url3", item.getEnclosures().get(2).getUrl());
674+
assertEquals("image/gif", item.getEnclosures().get(2).getType());
675+
assertEquals(3L, item.getEnclosures().get(2).getLength().orElse(-1L));
676+
677+
item = list.get(1);
678+
assertEquals(2, item.getEnclosures().size());
679+
assertEquals("https://url4", item.getEnclosures().get(0).getUrl());
680+
assertEquals("image/svg", item.getEnclosures().get(0).getType());
681+
assertEquals(4L, item.getEnclosures().get(0).getLength().orElse(-1L));
682+
assertEquals("https://url5", item.getEnclosures().get(1).getUrl());
683+
assertEquals("image/webp", item.getEnclosures().get(1).getType());
684+
assertEquals(5L, item.getEnclosures().get(1).getLength().orElse(-1L));
685+
}
686+
660687
@Test
661688
void testImageBadWidthHeight() {
662689
var list = new RssReader().read(fromFile("bad-image-width-height.xml")).collect(Collectors.toList());

src/test/java/com/apptasticsoftware/rssreader/RssReaderTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ void enclosureEqualsTest() {
507507
@Test
508508
void equalsContract() {
509509
EqualsVerifier.simple().forClass(Channel.class).withIgnoredFields("dateTimeParser").withIgnoredFields("category").withNonnullFields("categories").verify();
510-
EqualsVerifier.simple().forClass(Item.class).withIgnoredFields("defaultComparator").withIgnoredFields("dateTimeParser").withIgnoredFields("category").withNonnullFields("categories").verify();
510+
EqualsVerifier.simple().forClass(Item.class).withIgnoredFields("defaultComparator").withIgnoredFields("dateTimeParser").withIgnoredFields("category").withNonnullFields("categories").withIgnoredFields("enclosure").withNonnullFields("enclosures").verify();
511511
EqualsVerifier.simple().forClass(Enclosure.class).verify();
512512
EqualsVerifier.simple().forClass(Image.class).verify();
513513
}

src/test/java/com/apptasticsoftware/rssreader/module/itunes/ItunesRssReaderTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void readItunesPodcastFeed2() throws IOException {
3333
@Test
3434
void equalsContract() {
3535
EqualsVerifier.simple().forClass(ItunesChannel.class).withIgnoredFields("dateTimeParser").withIgnoredFields("category").withNonnullFields("categories").withNonnullFields("itunesCategories").verify();
36-
EqualsVerifier.simple().forClass(ItunesItem.class).withIgnoredFields("defaultComparator").withIgnoredFields("dateTimeParser").withIgnoredFields("category").withNonnullFields("categories").verify();
36+
EqualsVerifier.simple().forClass(ItunesItem.class).withIgnoredFields("defaultComparator").withIgnoredFields("dateTimeParser").withIgnoredFields("category").withNonnullFields("categories").withIgnoredFields("enclosure").withNonnullFields("enclosures").verify();
3737
EqualsVerifier.simple().forClass(ItunesOwner.class).verify();
3838
}
3939

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<?xml-stylesheet type="text/xsl" href="https://worldoftanks.eu/static/5.132.2_d0fd33/portalnews/css/rss.xsl" media="screen"?>
3+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
4+
<channel>
5+
<atom:link href="https://worldoftanks.eu/en/news/" rel="self" type="application/rss+xml" />
6+
<title>World of Tanks News | World of Tanks</title>
7+
<link>https://worldoftanks.eu/en/news/</link>
8+
<description>The latest news, updates, specials, and events for World of Tanks, the team-based, MMO tank battle game from Wargaming. Everything about WoT in one place.</description>
9+
<language>en</language>
10+
<pubDate>Fri, 02 Feb 2024 11:05:26 GMT</pubDate>
11+
<image>
12+
<url>https://worldoftanks.eu/static/5.132.2_d0fd33/portalnews/img/news.png</url>
13+
<title>World of Tanks News | World of Tanks</title>
14+
<link>https://worldoftanks.eu/en/news/</link>
15+
</image>
16+
<item>
17+
<title>A Warrior&#39;s Path: Unleash the Power of Japanese Tanks With Special Bundles!</title>
18+
<link>https://worldoftanks.eu/en/news/specials/a-warriors-path-sale-feb-2024/</link>
19+
<description>Embark on an epic journey with the A Warrior's Path event, featuring exclusive bundle sales for the newly introduced Japanese heavy tanks.</description>
20+
<guid isPermaLink="true">https://worldoftanks.eu/en/news/specials/a-warriors-path-sale-feb-2024/</guid>
21+
<pubDate>Thu, 01 Feb 2024 09:00:00 GMT</pubDate>
22+
<category>Specials</category>
23+
<enclosure url="https://url1" length="1" type="image/jpeg" />
24+
<enclosure url="https://url2" length="2" type="image/png" />
25+
<enclosure url="https://url3" length="3" type="image/gif" />
26+
</item>
27+
<item>
28+
<title>WoT Monthly: Valentine&#39;s Day and More in February 2024</title>
29+
<link>https://worldoftanks.eu/en/news/general-news/wot-monthly-february-2024/</link>
30+
<description>A quick look at World of Tanks events during the month of love!</description>
31+
<guid isPermaLink="true">https://worldoftanks.eu/en/news/general-news/wot-monthly-february-2024/</guid>
32+
<pubDate>Wed, 31 Jan 2024 14:00:00 GMT</pubDate>
33+
<category>General News</category>
34+
<enclosure url="https://url4" length="4" type="image/svg" />
35+
<enclosure url="https://url5" length="5" type="image/webp" />
36+
</item>
37+
38+
</channel>
39+
</rss>

0 commit comments

Comments
 (0)