browserutilities/feedsengine/FeedsServer/FolderHandler/src/OpmlParser.cpp
changeset 0 dd21522fd290
child 36 0ed94ceaa377
--- /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<KMaxFileName> 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<HBufC> 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;i<titleArray.Count()-1;i++)
+                	{
+                	if(itemName->Des().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*/);	
+	}