| @@ -1,9 +1,9 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class AtomCategory { | |||
| String term; | |||
| String scheme; | |||
| String label; | |||
| final String term; | |||
| final String scheme; | |||
| final String label; | |||
| AtomCategory(this.term, this.scheme, this.label); | |||
| @@ -11,15 +11,6 @@ class AtomCategory { | |||
| var term = element.getAttribute("term"); | |||
| var scheme = element.getAttribute("scheme"); | |||
| var label = element.getAttribute("label"); | |||
| return new AtomCategory(term, scheme, label); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| term: $term | |||
| scheme: $scheme | |||
| label: $label | |||
| '''; | |||
| return AtomCategory(term, scheme, label); | |||
| } | |||
| } | |||
| @@ -7,20 +7,20 @@ import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class AtomFeed { | |||
| String id; | |||
| String title; | |||
| String updated; | |||
| List<AtomItem> items; | |||
| final String id; | |||
| final String title; | |||
| final String updated; | |||
| final List<AtomItem> items; | |||
| List<AtomLink> links; | |||
| List<AtomPerson> authors; | |||
| List<AtomPerson> contributors; | |||
| List<AtomCategory> categories; | |||
| AtomGenerator generator; | |||
| String icon; | |||
| String logo; | |||
| String rights; | |||
| String subtitle; | |||
| final List<AtomLink> links; | |||
| final List<AtomPerson> authors; | |||
| final List<AtomPerson> contributors; | |||
| final List<AtomCategory> categories; | |||
| final AtomGenerator generator; | |||
| final String icon; | |||
| final String logo; | |||
| final String rights; | |||
| final String subtitle; | |||
| AtomFeed({ | |||
| this.id, | |||
| @@ -46,73 +46,32 @@ class AtomFeed { | |||
| } on StateError { | |||
| throw new ArgumentError("feed not found"); | |||
| } | |||
| var id = xmlGetString(feedElement, "id", strict: false); | |||
| var title = xmlGetString(feedElement, "title", strict: false); | |||
| var updated = xmlGetString(feedElement, "updated", strict: false); | |||
| var items = feedElement.findElements("entry").map((element) { | |||
| return new AtomItem.parse(element); | |||
| }).toList(); | |||
| var links = feedElement.findElements("link").map((element) { | |||
| return new AtomLink.parse(element); | |||
| }).toList(); | |||
| var authors = feedElement.findElements("author").map((element) { | |||
| return new AtomPerson.parse(element); | |||
| }).toList(); | |||
| var contributors = feedElement.findElements("contributor").map((element) { | |||
| return new AtomPerson.parse(element); | |||
| }).toList(); | |||
| var categories = feedElement.findElements("category").map((element) { | |||
| return new AtomCategory.parse(element); | |||
| }).toList(); | |||
| AtomGenerator generator; | |||
| try { | |||
| generator = | |||
| new AtomGenerator.parse(feedElement.findElements("generator").first); | |||
| } on StateError {} | |||
| var icon = xmlGetString(feedElement, "icon", strict: false); | |||
| var logo = xmlGetString(feedElement, "logo", strict: false); | |||
| var rights = xmlGetString(feedElement, "rights", strict: false); | |||
| var subtitle = xmlGetString(feedElement, "subtitle", strict: false); | |||
| return new AtomFeed( | |||
| id: id, | |||
| title: title, | |||
| updated: updated, | |||
| items: items, | |||
| links: links, | |||
| authors: authors, | |||
| contributors: contributors, | |||
| categories: categories, | |||
| generator: generator, | |||
| icon: icon, | |||
| logo: logo, | |||
| rights: rights, | |||
| subtitle: subtitle); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| id: $id | |||
| title: $title | |||
| updated: $updated | |||
| items: $items | |||
| links: $links | |||
| authors: $authors | |||
| contributors: $contributors | |||
| categories: $categories | |||
| generator: $generator | |||
| icon: $icon | |||
| logo: $logo | |||
| rights: $rights | |||
| subtitle: $subtitle | |||
| '''; | |||
| return AtomFeed( | |||
| id: findElementOrNull(feedElement, "id")?.text, | |||
| title: findElementOrNull(feedElement, "title")?.text, | |||
| updated: findElementOrNull(feedElement, "updated")?.text, | |||
| items: feedElement.findElements("entry").map((element) { | |||
| return AtomItem.parse(element); | |||
| }).toList(), | |||
| links: feedElement.findElements("link").map((element) { | |||
| return AtomLink.parse(element); | |||
| }).toList(), | |||
| authors: feedElement.findElements("author").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| contributors: feedElement.findElements("contributor").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| categories: feedElement.findElements("category").map((element) { | |||
| return AtomCategory.parse(element); | |||
| }).toList(), | |||
| generator: | |||
| AtomGenerator.parse(findElementOrNull(feedElement, "generator")), | |||
| icon: findElementOrNull(feedElement, "icon")?.text, | |||
| logo: findElementOrNull(feedElement, "logo")?.text, | |||
| rights: findElementOrNull(feedElement, "rights")?.text, | |||
| subtitle: findElementOrNull(feedElement, "subtitle")?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,25 +1,19 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class AtomGenerator { | |||
| String uri; | |||
| String version; | |||
| String value; | |||
| final String uri; | |||
| final String version; | |||
| final String value; | |||
| AtomGenerator(this.uri, this.version, this.value); | |||
| factory AtomGenerator.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var uri = element.getAttribute("uri"); | |||
| var version = element.getAttribute("version"); | |||
| var value = element.text; | |||
| return new AtomGenerator(uri, version, value); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| uri: $uri | |||
| version: $version | |||
| value: $value | |||
| '''; | |||
| } | |||
| } | |||
| @@ -39,68 +39,28 @@ class AtomItem { | |||
| }); | |||
| factory AtomItem.parse(XmlElement element) { | |||
| var id = xmlGetString(element, "id", strict: false); | |||
| var title = xmlGetString(element, "title", strict: false); | |||
| var updated = xmlGetString(element, "updated", strict: false); | |||
| var authors = element.findElements("author").map((element) { | |||
| return new AtomPerson.parse(element); | |||
| }).toList(); | |||
| var links = element.findElements("link").map((element) { | |||
| return new AtomLink.parse(element); | |||
| }).toList(); | |||
| var categories = element.findElements("category").map((element) { | |||
| return new AtomCategory.parse(element); | |||
| }).toList(); | |||
| var contributors = element.findElements("contributor").map((element) { | |||
| return new AtomPerson.parse(element); | |||
| }).toList(); | |||
| AtomSource source; | |||
| try { | |||
| source = new AtomSource.parse(element.findElements("source").first); | |||
| } on StateError {} | |||
| var published = xmlGetString(element, "published", strict: false); | |||
| var content = xmlGetString(element, "content", strict: false); | |||
| var summary = xmlGetString(element, "summary", strict: false); | |||
| var rights = xmlGetString(element, "rights", strict: false); | |||
| return new AtomItem( | |||
| id: id, | |||
| title: title, | |||
| updated: updated, | |||
| authors: authors, | |||
| links: links, | |||
| categories: categories, | |||
| contributors: contributors, | |||
| source: source, | |||
| published: published, | |||
| content: content, | |||
| summary: summary, | |||
| rights: rights, | |||
| media: new Media.parse(element), | |||
| return AtomItem( | |||
| id: findElementOrNull(element, "id")?.text, | |||
| title: findElementOrNull(element, "title")?.text, | |||
| updated: findElementOrNull(element, "updated")?.text, | |||
| authors: element.findElements("author").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| links: element.findElements("link").map((element) { | |||
| return AtomLink.parse(element); | |||
| }).toList(), | |||
| categories: element.findElements("category").map((element) { | |||
| return AtomCategory.parse(element); | |||
| }).toList(), | |||
| contributors: element.findElements("contributor").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| source: AtomSource.parse(findElementOrNull(element, "source")), | |||
| published: findElementOrNull(element, "published")?.text, | |||
| content: findElementOrNull(element, "content")?.text, | |||
| summary: findElementOrNull(element, "summary")?.text, | |||
| rights: findElementOrNull(element, "rights")?.text, | |||
| media: Media.parse(element), | |||
| ); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| id: $id | |||
| title: $title | |||
| updated: $updated | |||
| authors: $authors | |||
| links: $links | |||
| categories: $categories | |||
| contributors: $contributors | |||
| source: $source | |||
| published: $published | |||
| content: $content | |||
| summary: $summary | |||
| rights: $rights | |||
| '''; | |||
| } | |||
| } | |||
| @@ -1,12 +1,12 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class AtomLink { | |||
| String href; | |||
| String rel; | |||
| String type; | |||
| String hreflang; | |||
| String title; | |||
| int length; | |||
| final String href; | |||
| final String rel; | |||
| final String type; | |||
| final String hreflang; | |||
| final String title; | |||
| final int length; | |||
| AtomLink( | |||
| this.href, | |||
| @@ -27,18 +27,6 @@ class AtomLink { | |||
| if (element.getAttribute("length") != null) { | |||
| length = int.parse(element.getAttribute("length")); | |||
| } | |||
| return new AtomLink(href, rel, type, hreflang, title, length); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| href: $href | |||
| rel: $rel | |||
| type: $type | |||
| hreflang: $hreflang | |||
| title: $title | |||
| length: $length | |||
| '''; | |||
| return AtomLink(href, rel, type, hreflang, title, length); | |||
| } | |||
| } | |||
| @@ -2,25 +2,16 @@ import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class AtomPerson { | |||
| String name; | |||
| String uri; | |||
| String email; | |||
| final String name; | |||
| final String uri; | |||
| final String email; | |||
| AtomPerson(this.name, this.uri, this.email); | |||
| factory AtomPerson.parse(XmlElement element) { | |||
| var name = xmlGetString(element, "name"); | |||
| var uri = xmlGetString(element, "uri", strict: false); | |||
| var email = xmlGetString(element, "email", strict: false); | |||
| return new AtomPerson(name, uri, email); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| name: $name | |||
| uri: $uri | |||
| email: $email | |||
| '''; | |||
| var name = findElementOrNull(element, "name")?.text; | |||
| var uri = findElementOrNull(element, "uri")?.text; | |||
| var email = findElementOrNull(element, "email")?.text; | |||
| return AtomPerson(name, uri, email); | |||
| } | |||
| } | |||
| @@ -2,26 +2,20 @@ import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class AtomSource { | |||
| String id; | |||
| String title; | |||
| String updated; | |||
| final String id; | |||
| final String title; | |||
| final String updated; | |||
| AtomSource(this.id, this.title, this.updated); | |||
| factory AtomSource.parse(XmlElement element) { | |||
| var id = xmlGetString(element, "id", strict: false); | |||
| var title = xmlGetString(element, "title", strict: false); | |||
| var updated = xmlGetString(element, "updated", strict: false); | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var id = findElementOrNull(element, "id")?.text; | |||
| var title = findElementOrNull(element, "title")?.text; | |||
| var updated = findElementOrNull(element, "updated")?.text; | |||
| return new AtomSource(id, title, updated); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| id: $id | |||
| title: $title | |||
| updated: $updated | |||
| '''; | |||
| return AtomSource(id, title, updated); | |||
| } | |||
| } | |||
| @@ -13,6 +13,6 @@ class RssCategory { | |||
| var domain = element.getAttribute("domain"); | |||
| var value = element.text; | |||
| return new RssCategory(domain, value); | |||
| return RssCategory(domain, value); | |||
| } | |||
| } | |||
| @@ -24,17 +24,6 @@ class RssCloud { | |||
| var path = node.getAttribute("path"); | |||
| var registerProcedure = node.getAttribute("registerProcedure"); | |||
| var protocol = node.getAttribute("protocol"); | |||
| return new RssCloud(domain, port, path, registerProcedure, protocol); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| domain: $domain | |||
| port: $port | |||
| path: $path | |||
| registerProcedure: $registerProcedure | |||
| protocol: $protocol | |||
| '''; | |||
| return RssCloud(domain, port, path, registerProcedure, protocol); | |||
| } | |||
| } | |||
| @@ -25,14 +25,6 @@ class RssContent { | |||
| _imagesRegExp.allMatches(content).forEach((match) { | |||
| images.add(match.group(1)); | |||
| }); | |||
| return new RssContent(content, images); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| content: $value | |||
| images: $images | |||
| '''; | |||
| return RssContent(content, images); | |||
| } | |||
| } | |||
| @@ -14,6 +14,6 @@ class RssEnclosure { | |||
| var url = element.getAttribute("url"); | |||
| var type = element.getAttribute("type"); | |||
| var length = int.tryParse(element.getAttribute("length") ?? "0"); | |||
| return new RssEnclosure(url, type, length); | |||
| return RssEnclosure(url, type, length); | |||
| } | |||
| } | |||
| @@ -77,12 +77,12 @@ class RssFeed { | |||
| ?.findAllElements("day") | |||
| ?.map((element) { | |||
| return element.text; | |||
| })?.toList(), | |||
| })?.toList() ?? [], | |||
| skipHours: findElementOrNull(channelElement, "skipHours") | |||
| ?.findAllElements("hour") | |||
| ?.map((element) { | |||
| return int.tryParse(element.text ?? "0"); | |||
| })?.toList(), | |||
| })?.toList() ?? [], | |||
| lastBuildDate: findElementOrNull(channelElement, "lastBuildDate")?.text, | |||
| language: findElementOrNull(channelElement, "language")?.text, | |||
| generator: findElementOrNull(channelElement, "generator")?.text, | |||
| @@ -12,19 +12,10 @@ class RssImage { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var title = xmlGetString(element, "title", strict: false); | |||
| var url = xmlGetString(element, "url", strict: false); | |||
| var link = xmlGetString(element, "link", strict: false); | |||
| var title = findElementOrNull(element, "title")?.text; | |||
| var url = findElementOrNull(element, "url")?.text; | |||
| var link = findElementOrNull(element, "link")?.text; | |||
| return new RssImage(title, url, link); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| title: $title | |||
| url: $url | |||
| link: $link | |||
| '''; | |||
| return RssImage(title, url, link); | |||
| } | |||
| } | |||
| @@ -6,7 +6,6 @@ import 'package:webfeed/domain/rss_enclosure.dart'; | |||
| import 'package:webfeed/domain/rss_source.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| class RssItem { | |||
| final String title; | |||
| @@ -1,8 +1,8 @@ | |||
| import 'package:xml/xml/nodes/element.dart'; | |||
| class RssSource { | |||
| String url; | |||
| String value; | |||
| final String url; | |||
| final String value; | |||
| RssSource(this.url, this.value); | |||
| @@ -13,6 +13,6 @@ class RssSource { | |||
| var url = element.getAttribute("url"); | |||
| var value = element.text; | |||
| return new RssSource(url, value); | |||
| return RssSource(url, value); | |||
| } | |||
| } | |||
| @@ -9,28 +9,3 @@ XmlElement findElementOrNull(XmlElement element, String name) { | |||
| return null; | |||
| } | |||
| } | |||
| String xmlGetString(XmlElement element, String name, {strict: true}) { | |||
| try { | |||
| return element.findElements(name).first.text; | |||
| } on StateError { | |||
| if (strict) { | |||
| throw new ArgumentError("$name not found"); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| int xmlGetInt(XmlElement element, String name, {strict: true}) { | |||
| var value = xmlGetString(element, name, strict: strict); | |||
| if (value != null) { | |||
| try { | |||
| return int.parse(value); | |||
| } on FormatException { | |||
| if (strict) { | |||
| throw new ArgumentError("$name has invalid format"); | |||
| } | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| @@ -219,4 +219,38 @@ void main() { | |||
| expect(item.media.scenes.first.startTime, "00:15"); | |||
| expect(item.media.scenes.first.endTime, "00:45"); | |||
| }); | |||
| test("parse Atom-Empty.xml", () { | |||
| var xmlString = File("test/xml/Atom-Empty.xml").readAsStringSync(); | |||
| var feed = AtomFeed.parse(xmlString); | |||
| expect(feed.id, null); | |||
| expect(feed.title, null); | |||
| expect(feed.updated, null); | |||
| expect(feed.links.length, 0); | |||
| expect(feed.authors.length, 0); | |||
| expect(feed.contributors.length, 0); | |||
| expect(feed.categories.length, 0); | |||
| expect(feed.generator, null); | |||
| expect(feed.icon, null); | |||
| expect(feed.logo, null); | |||
| expect(feed.subtitle, null); | |||
| expect(feed.items.length, 1); | |||
| var item = feed.items.first; | |||
| expect(item.authors.length, 0); | |||
| expect(item.links.length, 0); | |||
| expect(item.categories.length, 0); | |||
| expect(item.contributors.length, 0); | |||
| expect(item.published, null); | |||
| expect(item.summary, null); | |||
| expect(item.content, null); | |||
| expect(item.rights, null); | |||
| }); | |||
| } | |||
| @@ -250,4 +250,48 @@ void main() { | |||
| expect(feed.items.first.dc.coverage, "coverage"); | |||
| expect(feed.items.first.dc.rights, "rights"); | |||
| }); | |||
| test("parse RSS-Empty.xml", () { | |||
| var xmlString = File("test/xml/RSS-Empty.xml").readAsStringSync(); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.title, null); | |||
| expect(feed.description, null); | |||
| expect(feed.link, null); | |||
| expect(feed.language, null); | |||
| expect(feed.lastBuildDate, null); | |||
| expect(feed.generator, null); | |||
| expect(feed.copyright, null); | |||
| expect(feed.docs, null); | |||
| expect(feed.managingEditor, null); | |||
| expect(feed.rating, null); | |||
| expect(feed.webMaster, null); | |||
| expect(feed.ttl, 0); | |||
| expect(feed.image, null); | |||
| expect(feed.cloud, null); | |||
| expect(feed.categories.length, 0); | |||
| expect(feed.skipDays.length, 0); | |||
| expect(feed.skipHours.length, 0); | |||
| expect(feed.items.length, 1); | |||
| expect(feed.items.first.title, null); | |||
| expect(feed.items.first.description, null); | |||
| expect(feed.items.first.link, null); | |||
| expect(feed.items.first.guid, null); | |||
| expect(feed.items.first.pubDate, null); | |||
| expect(feed.items.first.categories.length, 0); | |||
| expect(feed.items.first.author, null); | |||
| expect(feed.items.first.source, null); | |||
| expect(feed.items.first.comments, null); | |||
| expect(feed.items.first.enclosure, null); | |||
| expect(feed.items.first.content, null); | |||
| }); | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <feed xmlns="http://www.w3.org/2005/Atom"> | |||
| <entry> | |||
| </entry> | |||
| </feed> | |||
| @@ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <rss version="2.0" | |||
| xmlns:content="http://purl.org/rss/1.0/modules/content/"> | |||
| <channel> | |||
| <item> | |||
| </item> | |||
| </channel> | |||
| </rss> | |||