//********************************************************************************************** //* Rss Class //* --------- //* RSS 2.0 Web content syndication format encapsulation for U++ //* For more information on RSS format, //* See: //* http://www.rss-specifications.com/ //* http://www.rssboard.org/rss-specification //* #include "Rss.h" RssNode RssItem(const char* title, const char* link, const char* desc, const char* author, const char* comments, const char* pubdate, RssTag* category, RssTag* guid, RssTag* source, RssTag* enclosure) { return RssNode(RSS_ITEM) << RssTag(RSS_TITLE, title) << RssTag(RSS_LINK, link) << RssTag(RSS_DESCRIPTION, desc) << (author ? RssTag(RSS_AUTHOR, author) : Null) << (comments ? RssTag(RSS_COMMENTS, comments) : Null) << (pubdate ? RssTag(RSS_PUBDATE, pubdate) : Null) << (category ? *category : NULL) << (guid ? *guid : NULL) << (source ? *source : NULL) << (enclosure ? *enclosure : NULL); } RssNode RssImage(const char* title, const char* link, const char* url, const char* desc, Size sz) { return RssNode(RSS_IMAGE) << RssTag(RSS_TITLE, title) << RssTag(RSS_LINK, link) << RssTag(RSS_URL, url) << (desc ? RssTag(RSS_DESCRIPTION, desc) : Null) << (sz.cx > 0 ? RssTag("width", AsString(sz.cx)) : Null) << (sz.cy > 0 ? RssTag("height", AsString(sz.cy)) : Null); } RssNode RssTextInput(const char* title, const char* link, const char* desc, const char* name) { return RssNode(RSS_TEXTINPUT) << RssTag(RSS_TITLE, title) << RssTag(RSS_LINK, link) << RssTag(RSS_DESCRIPTION, desc) << RssTag(RSS_NAME, name); } RssTag RssCloud(const char* domain, int port, const char* path, const char* registerProcedure, const char* protocol) { return RssTag(RSS_CLOUD) ("domain", domain) ("port", port) ("path", path) ("registerProcedure", registerProcedure) ("protocol", protocol); } RssTag RssCategory(const char* category, const char* domain) { return RssTag(RSS_CATEGORY, category) ("domain", domain); } RssTag RssGuid(const char *id, bool islink) { return RssTag(RSS_GUID, id) ("isPermaLink", (islink ? "true" : "false")); } RssTag RssSource(const char* url, const char* src) { return RssTag(RSS_SOURCE, src) (RSS_URL, url); } RssTag RssEnclosure(const char* url, double length, const char* type) { return RssTag(RSS_ENCLOSURE) ("url", url) ("length", length) ("type", type); } void RssTag::Copy(const RssTag& src) { tag0 = src.tag0; txt0 = src.txt0; attrs <<= src.attrs; } void RssNode::Copy(const RssNode& src) { RssTag::operator=(src); tags <<= src.tags; nodes <<= src.nodes; } void RssChannel::Copy(const RssChannel& src) { tags <<= src.tags; nodes <<= src.nodes; skipdays <<= src.skipdays; skiphours <<= src.skiphours; } void Rss::Copy(const Rss& src) { channels <<= src.channels; charset = src.charset; version = src.version; error = src.error; } RssTag RssChannel::GetTag(VectorMap& t, const char* tag) { RssTag tg; return tg = t.Get(tag, Null); } RssNode RssChannel::GetNode(VectorMap& n, const char* tag) { RssNode nd; return nd = n.Get(tag, Null); } RssChannel& RssChannel::Tag(const RssTag& t) { int n = tags.Find(t.Tag()); if(n >= 0 && t.Tag() == RSS_SKIPDAYS || t.Tag() == RSS_SKIPHOURS) tags.Get(t.Tag()) = t; else tags.Add(t.Tag(), t); return *this; } RssChannel& RssChannel::Node(const RssNode& t) { int n = nodes.Find(t.Tag()); if(n >= 0 && t.Tag() == RSS_SKIPDAYS || t.Tag() == RSS_SKIPHOURS) nodes.Get(t.Tag()) = t; else nodes.Add(t.Tag(), t); return *this; } RssChannel& RssChannel::SkipDays(const char* d) { int n = skipdays.GetCount(); if(n < 7) { if(n > 0) { for(int i = 0; i < n; i++) if(skipdays.At(i) == d) return *this; } skipdays.Add(d); } return *this; } RssChannel& RssChannel::SkipHours(int h) { int n = skiphours.GetCount(); if(n < 24 && (h < 25 && h > 0)) { if(n > 0) { for(int i = 0; i < n; i++) if(skiphours.At(i) == h) return *this; } skiphours.Add(h); } return *this; } //********************************************************************************************** //* Rss class implementation //* bool Rss::Compose(String& out) { try { String feed, channels, nodes, items; if(IsNull(Version())) Version("2.0"); if(IsNull(Encoding())) Encoding(CHARSET_DEFAULT); if(!HasChannel()) throw XmlError(t_("No channel node.")); RssChannels &chs = Channels(); for(int i = 0; i < chs.GetCount(); i++) { if(!chs[i].HasItem()) throw XmlError(Format(t_("Channel '%d' has not items"), i)); items = MakeTags(chs[i].Tags()); nodes = MakeNodes(chs[i].Nodes()); Vector& skipdays = chs[i].SkipDays(); Vector& skiphours = chs[i].SkipHours(); for(int sdc = 0; sdc < skipdays.GetCount(); sdc++) items << XmlTag(RSS_SKIPDAYS) .Text(skipdays[sdc], CharsetByName(Encoding())); for(int shc = 0; shc < skiphours.GetCount(); shc++) items << XmlTag(RSS_SKIPHOURS) .Text(AsString(skiphours[shc]), CharsetByName(Encoding())); XmlTag ch = XmlTag(); channels << ch.Tag(RSS_CHANNEL)(items + nodes); } out << XmlHeader(Encoding()); out << XmlTag(RSS_HEADER)(RSS_VERSION, Version())(channels); } catch(XmlError e) { error = e; return false; } return true; } bool Rss::Parse(String& in) { Clear(); try { XmlNode n = ParseXML(in); if(n.GetCount() == 0 || n[0].GetTag() != RSS_HEADER) throw XmlError(t_(" node not found.")); const XmlNode& rssnode = n[0]; const char* ver = "= 0) for(int i = ver0 + strlen(ver); i; i++) { if(buf[i] == '\"' || buf[i] == '>') break; else xmlver.Cat(buf[i]); // TODO: add Xml version var. } if(enc0 >= 0) for(int i = enc0 + strlen(enc); i; i++) { if(buf[i] == '\"' || buf[i] == '>') break; else encoding.Cat(buf[i]); } if(!IsNull(encoding)) charset = encoding; version = rssnode.Attr(RSS_VERSION); for(int i = 0; i < rssnode.GetCount(); i++) { const XmlNode& channelnode = rssnode[i]; if(channelnode.IsTag(RSS_CHANNEL)) { RssChannel& channel = channels.Add(); for(int cc = 0; cc < channelnode.GetCount(); cc++) { const XmlNode& ch = channelnode[cc]; if(ch.IsTag(RSS_SKIPDAYS)) channel.SkipDays(ch[0].GetText()); else if(ch.IsTag(RSS_SKIPHOURS)) channel.SkipHours(StrInt(ch[0].GetText())); else if(ch.IsTag(RSS_CATEGORY)) channel.Category(RssCategory(ch[0].GetText(), ch.Attr("domain"))); else if(ch.IsTag(RSS_CLOUD)) channel.Cloud(RssCloud(ch.Attr("domain"), StrInt(ch.Attr("port")), ch.Attr("path"), ch.Attr("registerProcedure"), ch.Attr("protocol")) ); else if(ch.IsTag(RSS_IMAGE)) { RssNode imgnode; imgnode.Tag(RSS_IMAGE); for(int iic = 0; iic < ch.GetCount(); iic++) { const XmlNode& img = ch[iic]; imgnode << RssTag(img.GetTag(), img[0].GetText()); } channel.Image(imgnode); } else if(ch.IsTag(RSS_TEXTINPUT)) { RssNode tinode; tinode.Tag(RSS_TEXTINPUT); for(int tic = 0; tic < ch.GetCount(); tic++) { const XmlNode& tex = ch[tic]; tinode << RssTag(tex.GetTag(), tex[0].GetText()); } channel.TextInput(tinode); } else if(ch.IsTag(RSS_ITEM)) { RssNode inode; inode.Tag(RSS_ITEM); for(int itc = 0; itc < ch.GetCount(); itc++) { const XmlNode& itm = ch[itc]; if(itm.IsTag(RSS_GUID)) inode << RssTag(itm.GetTag(), itm[0].GetText()) ("isPermaLink", itm.Attr("isPermaLink")); else if(itm.IsTag(RSS_CATEGORY)) inode << RssCategory(itm[0].GetText(), itm.Attr("domain")); else if(itm.IsTag(RSS_SOURCE)) inode << RssSource(itm.Attr("url"), itm[0].GetText()); else if(itm.IsTag(RSS_ENCLOSURE)) inode << RssEnclosure(itm.Attr("url"), ::StrDbl(itm.Attr("length")), itm.Attr("type")); else inode << RssTag(itm.GetTag(), itm[0].GetText()); } channel.Item(inode); } else channel.Tag(RssTag(ch.GetTag(), ch[0].GetText())); } } else throw XmlError(t_(" node not found. Faulty Rss structure.")); } } catch(XmlError e) { error = e; return false; } return true; } String Rss::MakeTags(RssTags& tags) { String out; for(int tc = 0; tc < tags.GetCount(); tc++) { RssTag& t = tags[tc]; XmlTag x; if(t.HasAttr()) { RssAttributes& attrs = t.Attrs(); for(int ac = 0; ac < attrs.GetCount(); ac++) { const char* attr = attrs.GetKey(ac); const char *val = attrs[ac]; x(attr, val); } } out << x.Tag(t.Tag()).Text(t.Text(), CharsetByName(Encoding())); } return out; } String Rss::MakeNodes(RssNodes& nodes) { String out, items; for(int nc = 0; nc < nodes.GetCount(); nc++) { RssNode& n = nodes[nc]; XmlTag x; items = Null; if(n.HasTag()) items << MakeTags(n.Tags()); if(n.HasNode()) items << MakeNodes(n.Nodes()); out << x.Tag(n.Tag())(items); } return out; } Rss& Rss::Channel(const RssChannel& channel) { for(int i = 0; i < ChannelCount(); i++) { RssChannel& ch = channels.At(i); if(&ch == &channel) return *this; } channels.Add(channel); return *this; }