BREW RSS Reader - 5 / 9 -
Analyze RSS Data
Data Structure of RssReader
// RSSItem structure ( one item ) SFMTYPEDEFSTRUCT(RSSItem) struct RSSItem { SFXAnsiString title; SFXAnsiString description; SFXAnsiString topic; SFXDate date; // For saving date in format of SFXDate Bool alreadyRead; // Flag showing whether it has been read or not RSSItem(SFXAnsiStringConstRef rsstitle, SFXAnsiStringConstRef rssdescription, SFXAnsiStringConstRef rsstopic, SFXDateConstRef rssdate, Bool rssalreadyRead = false); }; // RSSFeed class ( one RSS feed ) SFMTYPEDEFCLASS(RSSFeed) class RSSFeed { public: SFXAnsiString _title; // Site title SFXAnsiString _url; // Site URL SFXDate _date; // Latest updated date private: SFXAnsiString _filename; // Name of file to save RSS data SFXList<RSSItemPtr> _itemList; // List of RSS items ... * public: RSSFeed(SFXAnsiStringConstRef title, SFXAnsiStringConstRef url, SFXDateConstRef date = SFXDate()); ~RSSFeed(Void); SFCError Parse(SFXAnsiStringConstRef xml); SFCError SaveToFile(Void); Void LoadFromFile(Void); Bool Find(RSSItemConstRef item); RSSItemPtr GetItem(SInt32 index) { return _itemList.Get(index); } SFXAnsiStringConstRef GetFilename(Void) { return _filename; } Void SetFilename(SFXAnsiStringConstRef filename) { _filename = filename; } SInt32 GetSize(Void) { return _itemList.GetSize(); } friend class RSSFeedList; }; // RSSFeedList class ( list of RSS feeds ) SFMTYPEDEFCLASS(RSSFeedList) class RSSFeedList { private: SFXArray<RSSFeedPtr> _feedArray; // array of RSS feeds public: ~RSSFeedList(Void); Bool Add(RSSFeedPtr feed); SFCError SaveToFile(Void); Void LoadFromFile(Void); SInt32 GetSize(Void) { return _feedArray.GetSize(); } SInt32 IndexOf(RSSFeedPtr item) { return _feedArray.IndexOf(item); } RSSFeedPtr operator[](SInt32 index) { return _feedArray[index]; } };
* "SFXList<RSSItemPtr> _itemList;"
As elements of SophiaFramework UNIVERSE Collection class should not be bigger than 4 bytes in size, SFXList is used to save the data of an RSSItem through an RSSItemPtr.
Append Item to RSSItemPtr
RSSItemPtr item; // generate an RSS item by using new item = ::new RSSItem("title", "desc", "topic", SFXDate::CurrentDate()); // append the item _itemList.Append(item);
Refer to the Item of the RSSItemPtr
// refer to the first item _itemList.Get(0);
Write the Variables of RSSFeed onto File
// data format for saving RSSFeed class as file title<>description<>topic<>date<>alreadyRead\r\n title<>description<>topic<>date<>alreadyRead\r\n ...
The variable date is an integer of type UInt32. As for alreadyRead, it will be 1 if true and 0 if false.
#define DIRECTORY_NAME "/data" #define DELIMITER_STR "<>" #define DELIMITER_LEN 2 SFCError RSSFeed::SaveToFile(Void) { SFCError error; SFXPath temp; SFXFile file; SFXAnsiStringStreamWriter writer; RSSItemPtr item; SFXPath filename(DIRECTORY_NAME + _filename); SFXPath dir(DIRECTORY_NAME); // Get path for temporary saving error = SFXFile::GetTemporaryPath(dir, &temp); if (error == SFERR_NO_ERROR) { // open file error = file.OpenReadWrite(temp); if (error == SFERR_NO_ERROR) { // Get the writing stream error = file.GetStreamWriter(4096, &writer); if (error == SFERR_NO_ERROR) { // Get iterator SFXList<RSSItemPtr>::Iterator itor(_itemList.GetFirstIterator()); while (itor.HasNext()) { // Write RSSItem onto file item = itor.GetNext(); writer << item->title << DELIMITER_STR << item->description << DELIMITER_STR << item->topic << DELIMITER_STR << SFXAnsiString::Format("%u", item->date.AsUInt32()) << DELIMITER_STR << ((item->alreadyRead) ? "1" : "0") << "\r\n"; // The data is actually written //when Flush() is called writer.Flush(); } } file.Close(); } } if (error == SFERR_NO_ERROR) { // Delete file SFXFile::Remove(filename); // Rename file error = SFXFile::Rename(temp, filename); } return error; }
SFXFile::GetTemporaryPath
You should get a temporary file name by calling SFXFile::GetTemporaryPath(). Once you have finished writing data in the temporary file, change its name to the proper one.
This system is safe even if no data is writen in the temporary file.
Read from File
Void RSSFeed::LoadFromFile(Void) { SFCError error; SFXFile file; SFXAnsiStringStreamReader reader; SFXAnsiString string; SFXPath path(DIRECTORY_NAME + _filename); UInt32 size; SFXFile::GetSize(path, &size); // Get file size error = file.OpenReadOnly(path); // Open file if (error == SFERR_NO_ERROR) { // if open succeed // Get reading stream error = file.GetStreamReader(size + 1, &reader); if (error == SFERR_NO_ERROR) { // Read from file reader.Fetch(); // Save to string reader.ReadSFXAnsiString(&string); SInt32 c1 = 0; SInt32 c2, c3, c4, c5, c6; while (true) { // Search for first line delimiter (end of line) c2 = string.IndexOf(DELIMITER_STR, c1); if (c2 < 0) { // If not found, there is no line break; } c3 = string.IndexOf(DELIMITER_STR, c2 + 1); c4 = string.IndexOf(DELIMITER_STR, c3 + 1); c5 = string.IndexOf(DELIMITER_STR, c4 + 1); c6 = string.IndexOf('\n', c5) + 1; SFXAnsiString dateNumber = string.Substring(c4 + DELIMITER_LEN, c5); SFXDate date; // Get the date in a number type date.Set(dateNumber.AsUInt32()); RSSItemPtr item = new RSSItem(string.Substring(c1, c2), string.Substring(c2 + DELIMITER_LEN, c3), string.Substring(c3 + DELIMITER_LEN, c4), date, string.Substring(c5 + DELIMITER_LEN, c6).Trim() == "1"); // Append to the list if (_itemList.Append(item) != SFERR_NO_ERROR) { ::delete item; break; } c1 = c6; } } file.Close(); } return; }
* In file processing, registering a callback function is not needed because Fetch() will be finished immediately.
Analyzing the RSS Data
Here, the SFXXMLDOMParser for DOM structure is used as an XML parser.
You should get the XML document analyzed by the Parse() function of SFXXMLDOMParser using GetDocument().
When processing nodes, use GetFirstChild() to get the first child node, GetNextSibling() to get the sibling nodes, GetNodeName() to get the node names etc.
SFCError RSSFeed::Parse(SFXAnsiStringConstRef xml) { SFCError error; SFXXMLDOMParser parser; SFXXMLDocumentPtr root; SFXXMLNodePtr child; SFXAnsiString title; SFXAnsiString description; SFXDate date; RSSItemPtr item; SInt32 listcount; SInt32 count = 0; SFXAnsiString temp; // Analyze XML document error = parser.Parse(xml); // If parse failed if (error != SFERR_NO_ERROR) return SFERR_FAILED; // Get the root root = parser.GetDocument(); if (root == null) return SFERR_FAILED; // Search for title tag under channel tag SFXList<SFXXMLNodePtr>* list = root->GetElementsByTagName("channel"); if (list != null && list->GetSize() > 0) { child = list->Get(0); for (child = child->GetFirstChild() ; child != null && error == SFERR_NO_ERROR ; child = child->GetNextSibling()) { // If title tag if (SFXAnsiString(child->GetNodeName()) == "title") { temp = child->GetText(); if (_title.IsEmptyCString()) { error = SFXTextEncoding::UTF8ToShiftJIS(temp, &_title); } } else if (SFXAnsiString(child->GetNodeName()) == "dc:date") { date.Parse("YYYY-MM-DD%Thh:mm:ss+", child->GetText()); } } // For each item tag SFXList<SFXXMLNodePtr>* list = root->GetElementsByTagName("item"); if (list != null && list->GetSize() > 0) { for (listcount=0; listcount < (list->GetSize()); listcount++) { child = list->Get(listcount); for (child = child->GetFirstChild() ; child != null && error == SFERR_NO_ERROR ; child = child->GetNextSibling()) { // If title tag if (SFXAnsiString(child->GetNodeName()) == "title") { // Get text node temp = child->GetText(); // Convert UTF-8 into Shift_JIS error = SFXTextEncoding::UTF8ToShiftJIS(temp, &title); } // If description tag else if (SFXAnsiString(child->GetNodeName()) == "description") { // Get text node temp = child->GetText(); // Convert UTF-8 into Shift_JIS error = SFXTextEncoding::UTF8ToShiftJIS(temp, &description); } // If dc: date tag else if (SFXAnsiString(child->GetNodeName()) == "dc: date") { // Convert string into date date.Parse("YYYY-MM-DD%Thh:mm:ss+", child->GetText()); } } item = ::new RSSItem(title, description, date); if (item != null) { // If not duplicated if (!Find(*item)) { // Insert item error = _itemList.Insert(count, item); if (error == SFERR_NO_ERROR) { ++count; } else { ::delete item; } } else { ::delete item; } } else { error = SFERR_NO_MEMORY; } } } } else { TRACE ("list empty"); } // Error if (error != SFERR_NO_ERROR) { _title = "Failed"; } SFXHelper::dbgprintf("[SGXAWSParser] ParseXML END %d", error); if (error == SFERR_NO_ERROR) { // Save to file error = RSSFeed::SaveToFile(); } return error; }