| @@ -13,4 +13,12 @@ class RssCategory { | |||||
| return new RssCategory(domain, value); | return new RssCategory(domain, value); | ||||
| } | } | ||||
| @override | |||||
| String toString() { | |||||
| return ''' | |||||
| domain: $domain | |||||
| value: $value | |||||
| '''; | |||||
| } | |||||
| } | } | ||||
| @@ -17,4 +17,15 @@ class RssCloud { | |||||
| var protocol = node.getAttribute("protocol"); | var protocol = node.getAttribute("protocol"); | ||||
| return new RssCloud(domain, port, path, registerProcedure, protocol); | return new RssCloud(domain, port, path, registerProcedure, protocol); | ||||
| } | } | ||||
| @override | |||||
| String toString() { | |||||
| return ''' | |||||
| domain: $domain | |||||
| port: $port | |||||
| path: $path | |||||
| registerProcedure: $registerProcedure | |||||
| protocol: $protocol | |||||
| '''; | |||||
| } | |||||
| } | } | ||||
| @@ -17,13 +17,33 @@ class RssFeed { | |||||
| final RssImage image; | final RssImage image; | ||||
| final RssCloud cloud; | final RssCloud cloud; | ||||
| final List<RssCategory> categories; | final List<RssCategory> categories; | ||||
| final List<String> skipDays; | |||||
| final List<int> skipHours; | |||||
| final String lastBuildDate; | final String lastBuildDate; | ||||
| final String language; | final String language; | ||||
| final String generator; | final String generator; | ||||
| final String copyright; | final String copyright; | ||||
| final String docs; | |||||
| final String managingEditor; | |||||
| final String rating; | |||||
| final String webMaster; | |||||
| final int ttl; | |||||
| RssFeed(this.title, this.description, this.link, this.items, | RssFeed(this.title, this.description, this.link, this.items, | ||||
| {this.image, this.cloud, this.categories, this.lastBuildDate, this.language, this.generator, this.copyright}); | |||||
| {this.image, | |||||
| this.cloud, | |||||
| this.categories, | |||||
| this.skipDays, | |||||
| this.skipHours, | |||||
| this.lastBuildDate, | |||||
| this.language, | |||||
| this.generator, | |||||
| this.copyright, | |||||
| this.docs, | |||||
| this.managingEditor, | |||||
| this.rating, | |||||
| this.webMaster, | |||||
| this.ttl}); | |||||
| factory RssFeed.parse(XmlDocument document) { | factory RssFeed.parse(XmlDocument document) { | ||||
| XmlElement channelElement; | XmlElement channelElement; | ||||
| @@ -54,19 +74,50 @@ class RssFeed { | |||||
| return new RssCategory.parse(element); | return new RssCategory.parse(element); | ||||
| }).toList(); | }).toList(); | ||||
| List<String> skipDays = new List<String>(); | |||||
| var skipDaysNodes = channelElement.findElements("skipDays"); | |||||
| if (skipDaysNodes.isNotEmpty) { | |||||
| skipDays = skipDaysNodes.first.findAllElements("day").map((element) { | |||||
| return element.text; | |||||
| }).toList(); | |||||
| } | |||||
| List<int> skipHours = new List<int>(); | |||||
| var skipHoursNodes = channelElement.findElements("skipHours"); | |||||
| if (skipHoursNodes.isNotEmpty) { | |||||
| skipHours = skipHoursNodes.first.findAllElements("hour").map((element) { | |||||
| try { | |||||
| return int.parse(element.text); | |||||
| } on FormatException { | |||||
| return null; | |||||
| } | |||||
| }).toList(); | |||||
| } | |||||
| var lastBuildDate = xmlGetString(channelElement, "lastBuildDate", strict: false); | var lastBuildDate = xmlGetString(channelElement, "lastBuildDate", strict: false); | ||||
| var language = xmlGetString(channelElement, "language", strict: false); | var language = xmlGetString(channelElement, "language", strict: false); | ||||
| var generator = xmlGetString(channelElement, "generator", strict: false); | var generator = xmlGetString(channelElement, "generator", strict: false); | ||||
| var copyright = xmlGetString(channelElement, "copyright", strict: false); | var copyright = xmlGetString(channelElement, "copyright", strict: false); | ||||
| var docs = xmlGetString(channelElement, "docs", strict: false); | |||||
| var managingEditor = xmlGetString(channelElement, "managingEditor", strict: false); | |||||
| var rating = xmlGetString(channelElement, "rating", strict: false); | |||||
| var webMaster = xmlGetString(channelElement, "webMaster", strict: false); | |||||
| var ttl = xmlGetInt(channelElement, "ttl", strict: false); | |||||
| return new RssFeed(title, description, link, feeds, | return new RssFeed(title, description, link, feeds, | ||||
| image: image, | image: image, | ||||
| cloud: cloud, | cloud: cloud, | ||||
| categories: categories, | categories: categories, | ||||
| skipDays: skipDays, | |||||
| skipHours: skipHours, | |||||
| lastBuildDate: lastBuildDate, | lastBuildDate: lastBuildDate, | ||||
| language: language, | language: language, | ||||
| generator: generator, | generator: generator, | ||||
| copyright: copyright); | |||||
| copyright: copyright, | |||||
| docs: docs, | |||||
| managingEditor: managingEditor, | |||||
| rating: rating, | |||||
| webMaster: webMaster, | |||||
| ttl: ttl); | |||||
| } | } | ||||
| @override | @override | ||||
| @@ -75,8 +126,21 @@ class RssFeed { | |||||
| title: $title | title: $title | ||||
| description: $description | description: $description | ||||
| link: $link | link: $link | ||||
| lastBuildDate: $lastBuildDate | |||||
| items: $items | items: $items | ||||
| image: $image | |||||
| cloud: $cloud | |||||
| categories: $categories | |||||
| skipDays: $skipDays | |||||
| skipHours: $skipHours | |||||
| lastBuildDate: $lastBuildDate | |||||
| language: $language | |||||
| generator: $generator | |||||
| copyright: $copyright | |||||
| docs: $docs | |||||
| managingEditor: $managingEditor | |||||
| rating: $rating | |||||
| webMaster: $webMaster | |||||
| ttl: $ttl | |||||
| '''; | '''; | ||||
| } | } | ||||
| } | } | ||||
| @@ -4,10 +4,7 @@ import 'package:xml/xml.dart'; | |||||
| String xmlGetString(XmlElement element, String name, {strict: true}) { | String xmlGetString(XmlElement element, String name, {strict: true}) { | ||||
| try { | try { | ||||
| return element | |||||
| .findElements(name) | |||||
| .first | |||||
| .text; | |||||
| return element.findElements(name).first.text; | |||||
| } on StateError { | } on StateError { | ||||
| if (strict) { | if (strict) { | ||||
| throw new ArgumentError("$name not found"); | throw new ArgumentError("$name not found"); | ||||
| @@ -15,3 +12,17 @@ String xmlGetString(XmlElement element, String name, {strict: true}) { | |||||
| } | } | ||||
| return null; | 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; | |||||
| } | |||||
| @@ -27,6 +27,11 @@ void main() { | |||||
| expect(feed.lastBuildDate, "Mon, 26 Mar 2018 14:00:00 PDT"); | expect(feed.lastBuildDate, "Mon, 26 Mar 2018 14:00:00 PDT"); | ||||
| expect(feed.generator, "Custom"); | expect(feed.generator, "Custom"); | ||||
| expect(feed.copyright, "Copyright 2018, Foo bar Inc."); | expect(feed.copyright, "Copyright 2018, Foo bar Inc."); | ||||
| expect(feed.docs, "https://foo.bar.news/docs"); | |||||
| expect(feed.managingEditor, "alice@foo.bar.news"); | |||||
| expect(feed.rating, "The PICS rating of the feed"); | |||||
| expect(feed.webMaster, "webmaster@foo.bar.news"); | |||||
| expect(feed.ttl, 60); | |||||
| expect(feed.image.title, "Foo bar News"); | expect(feed.image.title, "Foo bar News"); | ||||
| expect(feed.image.url, "https://foo.bar.news/logo.gif"); | expect(feed.image.url, "https://foo.bar.news/logo.gif"); | ||||
| @@ -44,6 +49,18 @@ void main() { | |||||
| expect(feed.categories[1].domain, "news"); | expect(feed.categories[1].domain, "news"); | ||||
| expect(feed.categories[1].value, "Lorem Ipsum"); | expect(feed.categories[1].value, "Lorem Ipsum"); | ||||
| expect(feed.skipDays.length, 3); | |||||
| expect(feed.skipDays.contains("Monday"), true); | |||||
| expect(feed.skipDays.contains("Tuesday"), true); | |||||
| expect(feed.skipDays.contains("Sunday"), true); | |||||
| expect(feed.skipHours.length, 5); | |||||
| expect(feed.skipHours.contains(0), true); | |||||
| expect(feed.skipHours.contains(1), true); | |||||
| expect(feed.skipHours.contains(2), true); | |||||
| expect(feed.skipHours.contains(3), true); | |||||
| expect(feed.skipHours.contains(4), true); | |||||
| expect(feed.items.length, 2); | expect(feed.items.length, 2); | ||||
| expect(feed.items.first.title, "The standard Lorem Ipsum passage, used since the 1500s"); | expect(feed.items.first.title, "The standard Lorem Ipsum passage, used since the 1500s"); | ||||
| @@ -17,6 +17,23 @@ | |||||
| <lastBuildDate>Mon, 26 Mar 2018 14:00:00 PDT</lastBuildDate> | <lastBuildDate>Mon, 26 Mar 2018 14:00:00 PDT</lastBuildDate> | ||||
| <generator>Custom</generator> | <generator>Custom</generator> | ||||
| <copyright>Copyright 2018, Foo bar Inc.</copyright> | <copyright>Copyright 2018, Foo bar Inc.</copyright> | ||||
| <docs>https://foo.bar.news/docs</docs> | |||||
| <managingEditor>alice@foo.bar.news</managingEditor> | |||||
| <rating>The PICS rating of the feed</rating> | |||||
| <webMaster>webmaster@foo.bar.news</webMaster> | |||||
| <ttl>60</ttl> | |||||
| <skipDays> | |||||
| <day>Monday</day> | |||||
| <day>Tuesday</day> | |||||
| <day>Sunday</day> | |||||
| </skipDays> | |||||
| <skipHours> | |||||
| <hour>0</hour> | |||||
| <hour>1</hour> | |||||
| <hour>2</hour> | |||||
| <hour>3</hour> | |||||
| <hour>4</hour> | |||||
| </skipHours> | |||||
| <item> | <item> | ||||
| <title>The standard Lorem Ipsum passage, used since the 1500s</title> | <title>The standard Lorem Ipsum passage, used since the 1500s</title> | ||||
| <link>https://foo.bar.news/1</link> | <link>https://foo.bar.news/1</link> | ||||