| @@ -0,0 +1,61 @@ | |||||
| import 'package:webfeed/util/helpers.dart'; | |||||
| import 'package:xml/xml.dart'; | |||||
| class DublinCore { | |||||
| final String title; | |||||
| final String description; | |||||
| final String creator; | |||||
| final String subject; | |||||
| final String publisher; | |||||
| final String contributor; | |||||
| final String date; | |||||
| final String type; | |||||
| final String format; | |||||
| final String identifier; | |||||
| final String source; | |||||
| final String language; | |||||
| final String relation; | |||||
| final String coverage; | |||||
| final String rights; | |||||
| DublinCore({ | |||||
| this.title, | |||||
| this.description, | |||||
| this.creator, | |||||
| this.subject, | |||||
| this.publisher, | |||||
| this.contributor, | |||||
| this.date, | |||||
| this.type, | |||||
| this.format, | |||||
| this.identifier, | |||||
| this.source, | |||||
| this.language, | |||||
| this.relation, | |||||
| this.coverage, | |||||
| this.rights, | |||||
| }); | |||||
| factory DublinCore.parse(XmlElement element) { | |||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return DublinCore( | |||||
| title: findElementOrNull(element, "dc:title")?.text, | |||||
| description: findElementOrNull(element, "dc:description")?.text, | |||||
| creator: findElementOrNull(element, "dc:creator")?.text, | |||||
| subject: findElementOrNull(element, "dc:subject")?.text, | |||||
| publisher: findElementOrNull(element, "dc:publisher")?.text, | |||||
| contributor: findElementOrNull(element, "dc:contributor")?.text, | |||||
| date: findElementOrNull(element, "dc:date")?.text, | |||||
| type: findElementOrNull(element, "dc:type")?.text, | |||||
| format: findElementOrNull(element, "dc:format")?.text, | |||||
| identifier: findElementOrNull(element, "dc:identifier")?.text, | |||||
| source: findElementOrNull(element, "dc:source")?.text, | |||||
| language: findElementOrNull(element, "dc:language")?.text, | |||||
| relation: findElementOrNull(element, "dc:relation")?.text, | |||||
| coverage: findElementOrNull(element, "dc:coverage")?.text, | |||||
| rights: findElementOrNull(element, "dc:rights")?.text, | |||||
| ); | |||||
| } | |||||
| } | |||||
| @@ -1,11 +1,11 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssCloud { | class RssCloud { | ||||
| String domain; | |||||
| String port; | |||||
| String path; | |||||
| String registerProcedure; | |||||
| String protocol; | |||||
| final String domain; | |||||
| final String port; | |||||
| final String path; | |||||
| final String registerProcedure; | |||||
| final String protocol; | |||||
| RssCloud( | RssCloud( | ||||
| this.domain, | this.domain, | ||||
| @@ -16,6 +16,9 @@ class RssCloud { | |||||
| ); | ); | ||||
| factory RssCloud.parse(XmlElement node) { | factory RssCloud.parse(XmlElement node) { | ||||
| if (node == null) { | |||||
| return null; | |||||
| } | |||||
| var domain = node.getAttribute("domain"); | var domain = node.getAttribute("domain"); | ||||
| var port = node.getAttribute("port"); | var port = node.getAttribute("port"); | ||||
| var path = node.getAttribute("path"); | var path = node.getAttribute("path"); | ||||
| @@ -1,5 +1,6 @@ | |||||
| import 'dart:core'; | import 'dart:core'; | ||||
| import 'package:webfeed/domain/dublin_core/dublin_core.dart'; | |||||
| import 'package:webfeed/domain/rss_category.dart'; | import 'package:webfeed/domain/rss_category.dart'; | ||||
| import 'package:webfeed/domain/rss_cloud.dart'; | import 'package:webfeed/domain/rss_cloud.dart'; | ||||
| import 'package:webfeed/domain/rss_image.dart'; | import 'package:webfeed/domain/rss_image.dart'; | ||||
| @@ -27,6 +28,7 @@ class RssFeed { | |||||
| final String rating; | final String rating; | ||||
| final String webMaster; | final String webMaster; | ||||
| final int ttl; | final int ttl; | ||||
| final DublinCore dc; | |||||
| RssFeed({ | RssFeed({ | ||||
| this.title, | this.title, | ||||
| @@ -47,6 +49,7 @@ class RssFeed { | |||||
| this.rating, | this.rating, | ||||
| this.webMaster, | this.webMaster, | ||||
| this.ttl, | this.ttl, | ||||
| this.dc, | |||||
| }); | }); | ||||
| factory RssFeed.parse(String xmlString) { | factory RssFeed.parse(String xmlString) { | ||||
| @@ -55,105 +58,41 @@ class RssFeed { | |||||
| try { | try { | ||||
| channelElement = document.findAllElements("channel").first; | channelElement = document.findAllElements("channel").first; | ||||
| } on StateError { | } on StateError { | ||||
| throw new ArgumentError("channel not found"); | |||||
| throw ArgumentError("channel not found"); | |||||
| } | } | ||||
| var title = xmlGetString(channelElement, "title", strict: false); | |||||
| var description = | |||||
| xmlGetString(channelElement, "description", strict: false); | |||||
| var link = xmlGetString(channelElement, "link", strict: false); | |||||
| var feeds = channelElement.findElements("item").map((element) { | |||||
| return new RssItem.parse(element); | |||||
| }).toList(); | |||||
| RssImage image; | |||||
| try { | |||||
| image = new RssImage.parse(channelElement.findElements("image").first); | |||||
| } on StateError {} | |||||
| RssCloud cloud; | |||||
| try { | |||||
| cloud = new RssCloud.parse(channelElement.findElements("cloud").first); | |||||
| } on StateError {} | |||||
| var categories = channelElement.findElements("category").map((element) { | |||||
| return new RssCategory.parse(element); | |||||
| }).toList(); | |||||
| var skipDays = new List<String>(); | |||||
| var skipDaysNodes = channelElement.findElements("skipDays"); | |||||
| if (skipDaysNodes.isNotEmpty) { | |||||
| skipDays = skipDaysNodes.first.findAllElements("day").map((element) { | |||||
| return RssFeed( | |||||
| title: findElementOrNull(channelElement, "title")?.text, | |||||
| description: findElementOrNull(channelElement, "description")?.text, | |||||
| link: findElementOrNull(channelElement, "link")?.text, | |||||
| items: channelElement.findElements("item").map((element) { | |||||
| return RssItem.parse(element); | |||||
| }).toList(), | |||||
| image: RssImage.parse(findElementOrNull(channelElement, "image")), | |||||
| cloud: RssCloud.parse(findElementOrNull(channelElement, "cloud")), | |||||
| categories: channelElement.findElements("category").map((element) { | |||||
| return RssCategory.parse(element); | |||||
| }).toList(), | |||||
| skipDays: findElementOrNull(channelElement, "skipDays") | |||||
| ?.findAllElements("day") | |||||
| ?.map((element) { | |||||
| return element.text; | return element.text; | ||||
| }).toList(); | |||||
| } | |||||
| var 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 language = xmlGetString(channelElement, "language", strict: false); | |||||
| var generator = xmlGetString(channelElement, "generator", 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: title, | |||||
| description: description, | |||||
| link: link, | |||||
| items: feeds, | |||||
| 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, | |||||
| })?.toList(), | |||||
| skipHours: findElementOrNull(channelElement, "skipHours") | |||||
| ?.findAllElements("hour") | |||||
| ?.map((element) { | |||||
| return int.tryParse(element.text ?? "0"); | |||||
| })?.toList(), | |||||
| lastBuildDate: findElementOrNull(channelElement, "lastBuildDate")?.text, | |||||
| language: findElementOrNull(channelElement, "language")?.text, | |||||
| generator: findElementOrNull(channelElement, "generator")?.text, | |||||
| copyright: findElementOrNull(channelElement, "copyright")?.text, | |||||
| docs: findElementOrNull(channelElement, "docs")?.text, | |||||
| managingEditor: findElementOrNull(channelElement, "managingEditor")?.text, | |||||
| rating: findElementOrNull(channelElement, "rating")?.text, | |||||
| webMaster: findElementOrNull(channelElement, "webMaster")?.text, | |||||
| ttl: int.tryParse(findElementOrNull(channelElement, "ttl")?.text ?? "0"), | |||||
| dc: DublinCore.parse(channelElement), | |||||
| ); | ); | ||||
| } | } | ||||
| @override | |||||
| String toString() { | |||||
| return ''' | |||||
| title: $title | |||||
| description: $description | |||||
| link: $link | |||||
| 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 | |||||
| '''; | |||||
| } | |||||
| } | } | ||||
| @@ -9,6 +9,9 @@ class RssImage { | |||||
| RssImage(this.title, this.url, this.link); | RssImage(this.title, this.url, this.link); | ||||
| factory RssImage.parse(XmlElement element) { | factory RssImage.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| var title = xmlGetString(element, "title", strict: false); | var title = xmlGetString(element, "title", strict: false); | ||||
| var url = xmlGetString(element, "url", strict: false); | var url = xmlGetString(element, "url", strict: false); | ||||
| var link = xmlGetString(element, "link", strict: false); | var link = xmlGetString(element, "link", strict: false); | ||||
| @@ -1,3 +1,4 @@ | |||||
| import 'package:webfeed/domain/dublin_core/dublin_core.dart'; | |||||
| import 'package:webfeed/domain/media/media.dart'; | import 'package:webfeed/domain/media/media.dart'; | ||||
| import 'package:webfeed/domain/rss_category.dart'; | import 'package:webfeed/domain/rss_category.dart'; | ||||
| import 'package:webfeed/domain/rss_content.dart'; | import 'package:webfeed/domain/rss_content.dart'; | ||||
| @@ -21,6 +22,7 @@ class RssItem { | |||||
| final RssContent content; | final RssContent content; | ||||
| final Media media; | final Media media; | ||||
| final RssEnclosure enclosure; | final RssEnclosure enclosure; | ||||
| final DublinCore dc; | |||||
| RssItem({ | RssItem({ | ||||
| this.title, | this.title, | ||||
| @@ -35,15 +37,16 @@ class RssItem { | |||||
| this.content, | this.content, | ||||
| this.media, | this.media, | ||||
| this.enclosure, | this.enclosure, | ||||
| this.dc, | |||||
| }); | }); | ||||
| factory RssItem.parse(XmlElement element) { | factory RssItem.parse(XmlElement element) { | ||||
| return new RssItem( | |||||
| return RssItem( | |||||
| title: findElementOrNull(element, "title")?.text, | title: findElementOrNull(element, "title")?.text, | ||||
| description: findElementOrNull(element, "description")?.text, | description: findElementOrNull(element, "description")?.text, | ||||
| link: findElementOrNull(element, "link")?.text, | link: findElementOrNull(element, "link")?.text, | ||||
| categories: element.findElements("category").map((element) { | categories: element.findElements("category").map((element) { | ||||
| return new RssCategory.parse(element); | |||||
| return RssCategory.parse(element); | |||||
| }).toList(), | }).toList(), | ||||
| guid: findElementOrNull(element, "guid")?.text, | guid: findElementOrNull(element, "guid")?.text, | ||||
| pubDate: findElementOrNull(element, "pubDate")?.text, | pubDate: findElementOrNull(element, "pubDate")?.text, | ||||
| @@ -53,21 +56,7 @@ class RssItem { | |||||
| content: RssContent.parse(findElementOrNull(element, "content:encoded")), | content: RssContent.parse(findElementOrNull(element, "content:encoded")), | ||||
| media: Media.parse(element), | media: Media.parse(element), | ||||
| enclosure: RssEnclosure.parse(findElementOrNull(element, "enclosure")), | enclosure: RssEnclosure.parse(findElementOrNull(element, "enclosure")), | ||||
| dc: DublinCore.parse(element), | |||||
| ); | ); | ||||
| } | } | ||||
| @override | |||||
| String toString() { | |||||
| return ''' | |||||
| title: $title | |||||
| description: $description | |||||
| link: $link | |||||
| guid: $guid | |||||
| pubDate: $pubDate | |||||
| author: $author | |||||
| comments: $comments | |||||
| source: $source | |||||
| content: $content | |||||
| '''; | |||||
| } | |||||
| } | } | ||||
| @@ -213,4 +213,41 @@ void main() { | |||||
| expect(item.media.scenes.first.startTime, "00:15"); | expect(item.media.scenes.first.startTime, "00:15"); | ||||
| expect(item.media.scenes.first.endTime, "00:45"); | expect(item.media.scenes.first.endTime, "00:45"); | ||||
| }); | }); | ||||
| test("parse RSS-DC.xml", (){ | |||||
| var xmlString = File("test/xml/RSS-DC.xml").readAsStringSync(); | |||||
| var feed = RssFeed.parse(xmlString); | |||||
| expect(feed.dc.title, "title"); | |||||
| expect(feed.dc.creator, "creator"); | |||||
| expect(feed.dc.subject, "subject"); | |||||
| expect(feed.dc.description, "description"); | |||||
| expect(feed.dc.publisher, "publisher"); | |||||
| expect(feed.dc.contributor, "contributor"); | |||||
| expect(feed.dc.date, "2000-01-01T12:00+00:00"); | |||||
| expect(feed.dc.type, "type"); | |||||
| expect(feed.dc.format, "format"); | |||||
| expect(feed.dc.identifier, "identifier"); | |||||
| expect(feed.dc.source, "source"); | |||||
| expect(feed.dc.language, "language"); | |||||
| expect(feed.dc.relation, "relation"); | |||||
| expect(feed.dc.coverage, "coverage"); | |||||
| expect(feed.dc.rights, "rights"); | |||||
| expect(feed.items.first.dc.title, "title"); | |||||
| expect(feed.items.first.dc.creator, "creator"); | |||||
| expect(feed.items.first.dc.subject, "subject"); | |||||
| expect(feed.items.first.dc.description, "description"); | |||||
| expect(feed.items.first.dc.publisher, "publisher"); | |||||
| expect(feed.items.first.dc.contributor, "contributor"); | |||||
| expect(feed.items.first.dc.date, "2000-01-01T12:00+00:00"); | |||||
| expect(feed.items.first.dc.type, "type"); | |||||
| expect(feed.items.first.dc.format, "format"); | |||||
| expect(feed.items.first.dc.identifier, "identifier"); | |||||
| expect(feed.items.first.dc.source, "source"); | |||||
| expect(feed.items.first.dc.language, "language"); | |||||
| expect(feed.items.first.dc.relation, "relation"); | |||||
| expect(feed.items.first.dc.coverage, "coverage"); | |||||
| expect(feed.items.first.dc.rights, "rights"); | |||||
| }); | |||||
| } | } | ||||
| @@ -0,0 +1,38 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <rss version="2.0" | |||||
| xmlns:dc="http://purl.org/dc/elements/1.1/"> | |||||
| <channel> | |||||
| <dc:title>title</dc:title> | |||||
| <dc:creator>creator</dc:creator> | |||||
| <dc:subject>subject</dc:subject> | |||||
| <dc:description>description</dc:description> | |||||
| <dc:publisher>publisher</dc:publisher> | |||||
| <dc:contributor>contributor</dc:contributor> | |||||
| <dc:date>2000-01-01T12:00+00:00</dc:date> | |||||
| <dc:type>type</dc:type> | |||||
| <dc:format>format</dc:format> | |||||
| <dc:identifier>identifier</dc:identifier> | |||||
| <dc:source>source</dc:source> | |||||
| <dc:language>language</dc:language> | |||||
| <dc:relation>relation</dc:relation> | |||||
| <dc:coverage>coverage</dc:coverage> | |||||
| <dc:rights>rights</dc:rights> | |||||
| <item> | |||||
| <dc:title>title</dc:title> | |||||
| <dc:creator>creator</dc:creator> | |||||
| <dc:subject>subject</dc:subject> | |||||
| <dc:description>description</dc:description> | |||||
| <dc:publisher>publisher</dc:publisher> | |||||
| <dc:contributor>contributor</dc:contributor> | |||||
| <dc:date>2000-01-01T12:00+00:00</dc:date> | |||||
| <dc:type>type</dc:type> | |||||
| <dc:format>format</dc:format> | |||||
| <dc:identifier>identifier</dc:identifier> | |||||
| <dc:source>source</dc:source> | |||||
| <dc:language>language</dc:language> | |||||
| <dc:relation>relation</dc:relation> | |||||
| <dc:coverage>coverage</dc:coverage> | |||||
| <dc:rights>rights</dc:rights> | |||||
| </item> | |||||
| </channel> | |||||
| </rss> | |||||