ultimatepp/bazaar/Rss/Rss.cpp
oblivion a449ed1eb3 Bazaar: RSS 2. 0 encapsulation class added.
git-svn-id: svn://ultimatepp.org/upp/trunk@460 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-09-18 20:47:21 +00:00

382 lines
9.7 KiB
C++

//**********************************************************************************************
//* 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<String, RssTag>& t, const char* tag)
{
RssTag tg;
return tg = t.Get(tag, Null);
}
RssNode RssChannel::GetNode(VectorMap<String, RssNode>& 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<String>& skipdays = chs[i].SkipDays();
Vector<int>& 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_("<Rss> node not found."));
const XmlNode& rssnode = n[0];
const char* ver = "<?xml version=\""; const char* enc = "encoding=\"";
const char* buf = in;
String xmlver, encoding;
int ver0 = in.Find(ver);
int enc0 = in.Find(enc);
if(ver0 >= 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_("<Channel> 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;
}