Implemented FeedDiscoverer

This commit is contained in:
daniel oeh 2014-06-16 00:16:48 +02:00
parent 859eabb7a3
commit 7fc0e73ea7
2 changed files with 187 additions and 0 deletions

View File

@ -0,0 +1,78 @@
package de.danoeh.antennapod.util.syndication;
import android.net.Uri;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Finds RSS/Atom URLs in a HTML document using the auto-discovery techniques described here:
* <p/>
* http://www.rssboard.org/rss-autodiscovery
* <p/>
* http://blog.whatwg.org/feed-autodiscovery
*/
public class FeedDiscoverer {
private static final String MIME_RSS = "application/rss+xml";
private static final String MIME_ATOM = "application/atom+xml";
/**
* Discovers links to RSS and Atom feeds in the given File which must be a HTML document.
*
* @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if
* a title cannot be found).
*/
public Map<String, String> findLinks(File in, String baseUrl) throws IOException {
return findLinks(Jsoup.parse(in, null), baseUrl);
}
/**
* Discovers links to RSS and Atom feeds in the given File which must be a HTML document.
*
* @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if
* a title cannot be found).
*/
public Map<String, String> findLinks(String in, String baseUrl) throws IOException {
return findLinks(Jsoup.parse(in), baseUrl);
}
private Map<String, String> findLinks(Document document, String baseUrl) {
Map<String, String> res = new LinkedHashMap<String, String>();
Elements links = document.head().getElementsByTag("link");
for (Element link : links) {
String rel = link.attr("rel");
String href = link.attr("href");
if (!StringUtils.isEmpty(href) &&
(rel.equals("alternate") || rel.equals("feed"))) {
String type = link.attr("type");
if (type.equals(MIME_RSS) || type.equals(MIME_ATOM)) {
String title = link.attr("title");
String processedUrl = processURL(baseUrl, href);
if (processedUrl != null) {
res.put(processedUrl,
(StringUtils.isEmpty(title)) ? href : title);
}
}
}
}
return res;
}
private String processURL(String baseUrl, String strUrl) {
Uri uri = Uri.parse(strUrl);
if (uri.isRelative()) {
Uri res = Uri.parse(baseUrl).buildUpon().path(strUrl).build();
return (res != null) ? res.toString() : null;
} else {
return strUrl;
}
}
}

View File

@ -0,0 +1,109 @@
package instrumentationTest.de.test.antennapod.util.syndication;
import android.test.InstrumentationTestCase;
import de.danoeh.antennapod.util.syndication.FeedDiscoverer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Map;
/**
* Test class for FeedDiscoverer
*/
public class FeedDiscovererTest extends InstrumentationTestCase {
private FeedDiscoverer fd;
private File testDir;
@Override
public void setUp() throws Exception {
super.setUp();
fd = new FeedDiscoverer();
testDir = getInstrumentation().getTargetContext().getExternalFilesDir("FeedDiscovererTest");
testDir.mkdir();
assertTrue(testDir.exists());
}
@Override
protected void tearDown() throws Exception {
FileUtils.deleteDirectory(testDir);
super.tearDown();
}
private String createTestHtmlString(String rel, String type, String href, String title) {
return String.format("<html><head><title>Test</title><link rel=\"%s\" type=\"%s\" href=\"%s\" title=\"%s\"></head><body></body></html>",
rel, type, href, title);
}
private String createTestHtmlString(String rel, String type, String href) {
return String.format("<html><head><title>Test</title><link rel=\"%s\" type=\"%s\" href=\"%s\"></head><body></body></html>",
rel, type, href);
}
private void checkFindUrls(boolean isAlternate, boolean isRss, boolean withTitle, boolean isAbsolute, boolean fromString) throws Exception {
final String title = "Test title";
final String hrefAbs = "http://example.com/feed";
final String hrefRel = "/feed";
final String base = "http://example.com";
final String rel = (isAlternate) ? "alternate" : "feed";
final String type = (isRss) ? "application/rss+xml" : "application/atom+xml";
final String href = (isAbsolute) ? hrefAbs : hrefRel;
Map<String, String> res;
String html = (withTitle) ? createTestHtmlString(rel, type, href, title)
: createTestHtmlString(rel, type, href);
if (fromString) {
res = fd.findLinks(html, base);
} else {
File testFile = new File(testDir, "feed");
FileOutputStream out = new FileOutputStream(testFile);
IOUtils.write(html, out);
out.close();
res = fd.findLinks(testFile, base);
}
assertNotNull(res);
assertEquals(1, res.size());
for (String key : res.keySet()) {
assertEquals(hrefAbs, key);
}
assertTrue(res.containsKey(hrefAbs));
if (withTitle) {
assertEquals(title, res.get(hrefAbs));
} else {
assertEquals(href, res.get(hrefAbs));
}
}
public void testAlternateRSSWithTitleAbsolute() throws Exception {
checkFindUrls(true, true, true, true, true);
}
public void testAlternateRSSWithTitleRelative() throws Exception {
checkFindUrls(true, true, true, false, true);
}
public void testAlternateRSSNoTitleAbsolute() throws Exception {
checkFindUrls(true, true, false, true, true);
}
public void testAlternateRSSNoTitleRelative() throws Exception {
checkFindUrls(true, true, false, false, true);
}
public void testAlternateAtomWithTitleAbsolute() throws Exception {
checkFindUrls(true, false, true, true, true);
}
public void testFeedAtomWithTitleAbsolute() throws Exception {
checkFindUrls(false, false, true, true, true);
}
public void testAlternateRSSWithTitleAbsoluteFromFile() throws Exception {
checkFindUrls(true, true, true, true, false);
}
}