diff -r 000000000000 -r dd21522fd290 browserutilities/feedsengine/FeedsServer/FolderHandler/src/OpmlParser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/browserutilities/feedsengine/FeedsServer/FolderHandler/src/OpmlParser.cpp Mon Mar 30 12:54:55 2009 +0300 @@ -0,0 +1,430 @@ +/* +* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Opml parser. +* +*/ + + +#include "OpmlParser.h" +#include "CleanupLibXml2.h" +#include "LeakTracker.h" +#include "PackedFolder.h" +#include "XmlUtils.h" + + +// Element and attribute names used by this parser. +_LIT8(KBody, "body"); +_LIT8(KHead, "head"); +_LIT8(KTitle, "title"); +_LIT8(KIsComment, "isComment"); +_LIT8(KOml, "oml"); +_LIT8(KOpml, "opml"); +_LIT8(KOutlineDocument, "outlineDocument"); +_LIT8(KOutline, "outline"); +_LIT8(KText, "text"); +_LIT8(KUrl, "url"); +_LIT8(KXmlUrl, "xmlUrl"); + +_LIT(KRootFolder, "Root Folder"); + +_LIT(KComma, " , "); +_LIT(KBackQuote, " ` "); + + + +const TInt KMaxURLLength = 256; + +// ----------------------------------------------------------------------------- +// RssFeedParser::NewL +// +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +COpmlParser* COpmlParser::NewL(CXmlUtils& aXmlUtils) + { + COpmlParser* self = new (ELeave) COpmlParser(aXmlUtils); + + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + + return self; + } + + +// ----------------------------------------------------------------------------- +// COpmlParser::COpmlParser +// +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +COpmlParser::COpmlParser(CXmlUtils& aXmlUtils): + iLeakTracker(CLeakTracker::EOpmlParser), iXmlUtils(aXmlUtils) + { + } + + +// ----------------------------------------------------------------------------- +// COpmlParser::ConstructL +// +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void COpmlParser::ConstructL() + { + } + + +// ----------------------------------------------------------------------------- +// COpmlParser::~COpmlParser +// +// Deconstructor. +// ----------------------------------------------------------------------------- +// +COpmlParser::~COpmlParser() + { + } + + +// ----------------------------------------------------------------------------- +// COpmlParser::IsSupported +// +// Returns true if this parser can process the given document. +// ----------------------------------------------------------------------------- +// +TBool COpmlParser::IsSupported(RXmlEngDocument aDocument, const TDesC& /*aContentType*/) const + { + TXmlEngElement node = NULL; + + // Get the root element. + node = iXmlUtils.GetDocumentFirstElement(aDocument); + + // If the root node is missing the feed isn't supported. + if (node.IsNull()) + { + return EFalse; + } + + // If the root node is not HEAD_STR the feed isn't supported. + else if (!iXmlUtils.IsNamed(node, KOpml) && + !iXmlUtils.IsNamed(node, KOml) && + !iXmlUtils.IsNamed(node, KOutlineDocument)) + { + return EFalse; + } + + return ETrue; + } + + +// ----------------------------------------------------------------------------- +// COpmlParser::ParseL +// +// Creates a FolderItem instance from the given document. +// ----------------------------------------------------------------------------- +// +CPackedFolder* COpmlParser::ParseL(TDesC8& aBuffer, const TDesC& aContentType, + const TDesC& aCharSet, const TDesC& aImportRootFolder) const + { + RXmlEngDocument document; + TXmlEngElement rootNode; + TXmlEngElement bodyNode; + CPackedFolder* packedFolder = NULL; + + // Parse the buffer. + document = iXmlUtils.ParseBufferL(aBuffer, aCharSet); + CleanupClosePushL(document); + + if (!IsSupported(document, aContentType)) + { + User::Leave(KErrNotSupported); + } + + // Get the root node. + rootNode = iXmlUtils.GetDocumentFirstElement(document); + + // Find the body element. + bodyNode = iXmlUtils.GetFirstNamedChild(rootNode, KBody()); + + if (bodyNode.IsNull()) + { + User::Leave(KErrCorrupt); + } + + // Process the body. + packedFolder = CPackedFolder::NewL(); + CleanupStack::PushL(packedFolder); + + TBuf OPMLRootFolderName; + + if(aImportRootFolder.Length()<=0) + { + // This will happen only while importing default OPML file - Do nothing + } + else + { + // Query for the opml/head/title + + // Find the head/title element. + TXmlEngElement headNode; + HBufC* Title = NULL; + headNode = iXmlUtils.GetFirstNamedChild(rootNode, KHead()); + + if(!headNode.IsNull()) + { + TXmlEngElement titleNode; + titleNode = iXmlUtils.GetFirstNamedChild(headNode, KTitle()); + + if(!titleNode.IsNull()) + Title = iXmlUtils.ExtractTextL(titleNode); + } + + CleanupStack::PushL(Title); + + if(Title == NULL) + OPMLRootFolderName.Append(aImportRootFolder); + else + OPMLRootFolderName.Append(Title->Des()); + + CleanupStack::PopAndDestroy(/*Title*/); + } + + packedFolder->FolderBeginsL(KRootFolder); + + if(OPMLRootFolderName.Length() > 0) + packedFolder->FolderBeginsL(OPMLRootFolderName); + + ProcessFolderL(bodyNode, *packedFolder); + + if(OPMLRootFolderName.Length() > 0) + packedFolder->FolderEndsL(); + + packedFolder->FolderEndsL(); + packedFolder->Trim(); + packedFolder->DoneL(); + + CleanupStack::Pop(packedFolder); + CleanupStack::PopAndDestroy(/*document*/); + + return packedFolder; + } + + +// ----------------------------------------------------------------------------- +// COpmlParser::ProcessFolderL +// +// Populates the FolderItem instance with the values from the given node. +// +// TODO: Factor out the recursion. +// ----------------------------------------------------------------------------- +// +void COpmlParser::ProcessFolderL(TXmlEngElement aNode, CPackedFolder& aPackedFolder) const + { + _LIT(KTrue, "true"); + const TTime KTimeZero(0); + + TXmlEngElement node; + TBool isFolder; + HBufC* itemName = NULL; + HBufC* itemUrl = NULL; + HBufC* isComment = NULL; + RPointerArray titleArray; + const TInt KAutoUpdatingOff = 0; + + HBufC* FeedUrlsInCurrentFolder = NULL; + + // Process the body's children. + node = iXmlUtils.GetFirstElementChild(aNode); + + while (node.NotNull()) + { + isFolder = EFalse; + + // Process an outline element. + if (iXmlUtils.IsNamed(node, KOutline)) + { + // Skip the element and any children if its a comment. + if ((isComment = iXmlUtils.AttributeL(node, KIsComment)) != NULL) + { + if (isComment->CompareF(KTrue()) == 0) + { + delete isComment; + + // Skip the element. + node = iXmlUtils.GetNextSiblingElement(node); + continue; + } + + delete isComment; + } + + // If the node has a outline child its a folder. + if (iXmlUtils.GetFirstNamedChild(node, KOutline).NotNull()) + { + isFolder = ETrue; + } + + // Extract the name. + itemName = iXmlUtils.AttributeL(node, KText); + if (itemName == NULL) + { + itemName = KNullDesC().AllocL(); + } + itemName->Des().Trim(); + CleanupStack::PushL(itemName); + + // If the node isn't a folder then add a feed item + if (!isFolder) + { + TBool found = EFalse; + titleArray.AppendL(itemName); + CleanupStack::Pop(itemName); + // Extract the url. + itemUrl = iXmlUtils.AttributeL(node, KUrl); + if (itemUrl == NULL) + { + itemUrl = iXmlUtils.AttributeL(node, KXmlUrl); + if (itemUrl == NULL) + { + itemUrl = KNullDesC().AllocL(); + } + } + + CleanupStack::PushL(itemUrl); + + // Clean up the url. + TPtr ptr(itemUrl->Des()); + iXmlUtils.CleanupUrlL(ptr); + + itemUrl->Des().Trim(); + + for(TInt i = 0;iDes().Compare(titleArray[i]->Des())==0) + found = ETrue; + } + + HBufC* titleAndUrl = HBufC::NewL(itemUrl->Length()+itemName->Length()+KBackQuote().Length()); + CleanupStack::PushL(titleAndUrl); + titleAndUrl->Des().Copy(itemName->Des()); + titleAndUrl->Des().Append(KBackQuote()); + titleAndUrl->Des().Append(itemUrl->Des()); + if(found && itemUrl->Length() <= KMaxURLLength) + { + found = EFalse; + SearchURLInCurrentFolderL(FeedUrlsInCurrentFolder,titleAndUrl,found); + if(!found) + { + aPackedFolder.AddFeedL(*itemName, *itemUrl, KTimeZero, KAutoUpdatingOff, KErrNotFound); + } + } + else if(itemUrl->Length() <= KMaxURLLength) + { + aPackedFolder.AddFeedL(*itemName, *itemUrl, KTimeZero, KAutoUpdatingOff, KErrNotFound); + } + if(!FeedUrlsInCurrentFolder) + { + FeedUrlsInCurrentFolder = HBufC::NewL(itemUrl->Length()+itemName->Length()+KBackQuote().Length()); + /* + Cleanupstack manipulation, since we are popping itemUrl & itemName before FeedUrlsInCurrentFolder + */ + CleanupStack::Pop(2); + CleanupStack::PushL(FeedUrlsInCurrentFolder); + CleanupStack::PushL(itemUrl); + CleanupStack::PushL(titleAndUrl); + } + else + { + /* + Cleanupstack manipulation, since we are popping itemUrl & itemName before FeedUrlsInCurrentFolder + */ + + HBufC* temp = FeedUrlsInCurrentFolder->ReAllocL(FeedUrlsInCurrentFolder->Length() + itemUrl->Length()+itemName->Length()+KBackQuote().Length() + KComma().Length()); + CleanupStack::Pop(3); + FeedUrlsInCurrentFolder = temp; + CleanupStack::PushL(FeedUrlsInCurrentFolder); + CleanupStack::PushL(itemUrl); + CleanupStack::PushL(titleAndUrl); + FeedUrlsInCurrentFolder->Des().Append(KComma()); + } + FeedUrlsInCurrentFolder->Des().Append(titleAndUrl->Des()); + CleanupStack::PopAndDestroy(titleAndUrl); + CleanupStack::PopAndDestroy(itemUrl); + + } + + // Otherwise add a folder item. + else + { + aPackedFolder.FolderBeginsL(*itemName); + CleanupStack::PopAndDestroy(itemName); + ProcessFolderL(node, aPackedFolder); + aPackedFolder.FolderEndsL(); + } + } + + // Get the next element. + node = iXmlUtils.GetNextSiblingElement(node); + } + + if(FeedUrlsInCurrentFolder) + { + CleanupStack::PopAndDestroy(FeedUrlsInCurrentFolder); + } + titleArray.ResetAndDestroy(); + } + +// ----------------------------------------------------------------------------- +// COpmlParser::SearchURLInCurrentFolderL +// +// Search for the feed URL from a set of feed URLs present in the current folder seperated by commas +// +// ----------------------------------------------------------------------------- +// +void COpmlParser::SearchURLInCurrentFolderL(HBufC* aURLsInCurFolder, HBufC* aFeedURL, TBool& aFound) const + { + + aFound = EFalse; + + if(!aURLsInCurFolder) + { + aFound = EFalse; + return; + } + + HBufC *temp = HBufC::NewLC(aURLsInCurFolder->Length()); + temp->Des().Copy(aURLsInCurFolder->Des()); + + TInt curPos = 0; + TInt commaPos = 0; + while(ETrue) + { + commaPos = temp->Find(KComma()); + if(commaPos == KErrNotFound) + { + aFound = !temp->Compare(aFeedURL->Des()); + break; + } + TPtrC curURL = temp->Mid(curPos, commaPos); + if(curURL == aFeedURL->Des()) + { + aFound = ETrue; + break; + } + + temp->Des().Copy(temp->Mid(commaPos+KComma().Length())); + } + CleanupStack::PopAndDestroy(/*temp*/); + }