/*
* Copyright (c) 2007-2010 Sebastian Brannstrom, Lars Persson, EmbedDev AB
*
* 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:
* EmbedDev AB - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include "OpmlParser.h"
#include "debug.h"
#include <f32file.h>
#include <bautils.h>
#include <s32file.h>
#include <charconv.h>
#include <xml/stringdictionarycollection.h>
#include <utf.h>
using namespace Xml;
const TInt KMaxParseBuffer = 2048;
const TInt KMaxStringBuffer = 1024;
COpmlParser::COpmlParser(CFeedEngine& aFeedEngine, RFs& aFs) : iFeedEngine(aFeedEngine),iFs(aFs)
{
}
COpmlParser::~COpmlParser()
{
}
void COpmlParser::ParseOpmlL(const TFileName &feedFileName, TBool aSearching)
{
DP1("ParseOpmlL BEGIN: %S", &feedFileName);
iSearching = aSearching;
_LIT8(KXmlMimeType, "text/xml");
// Contruct the parser object
CParser* parser = CParser::NewLC(KXmlMimeType, *this);
iOpmlState = EStateOpmlRoot;
iEncoding = EUtf8;
iNumFeedsAdded = 0;
ParseL(*parser, iFs, feedFileName);
CleanupStack::PopAndDestroy(parser);
//DP("ParseFeedL END");
}
// from MContentHandler
void COpmlParser::OnStartDocumentL(const RDocumentParameters& aDocParam, TInt /*aErrorCode*/)
{
DP("OnStartDocumentL()");
HBufC* charset = HBufC::NewLC(KMaxParseBuffer);
charset->Des().Copy(aDocParam.CharacterSetName().DesC());
iEncoding = EUtf8;
if (charset->CompareF(_L("utf-8")) == 0) {
DP("setting UTF8");
iEncoding = EUtf8;
} else if (charset->CompareF(_L("ISO-8859-1")) == 0) {
iEncoding = EUtf8; //Latin1;
} else {
DP1("unknown charset: %S", &charset);
}
CleanupStack::PopAndDestroy(charset);
}
void COpmlParser::OnEndDocumentL(TInt aErrorCode)
{
iFeedEngine.OpmlParsingCompleteL(aErrorCode, iNumFeedsAdded);
//DP("OnEndDocumentL()");
}
void COpmlParser::OnStartElementL(const RTagInfo& aElement, const RAttributeArray& aAttributes, TInt /*aErrorCode*/)
{
TBuf<KMaxStringBuffer> str;
str.Copy(aElement.LocalName().DesC());
DP2("OnStartElementL START state=%d, element=%S", iOpmlState, &str);
iBuffer.Zero();
switch (iOpmlState) {
case EStateOpmlRoot:
// <body>
if (str.CompareF(KTagBody) == 0) {
iOpmlState = EStateOpmlBody;
}
break;
case EStateOpmlBody:
// <body> <outline>
if(str.CompareF(KTagOutline) == 0) {
iOpmlState = EStateOpmlOutline;
}
// there are two variations on OPML, where the <outline> tags sit either
// directly below <body>, or inside a collective <body> <outline>
// by checking if the <body> <outline> has arguments, we can support both
// by falling through to the nextstate
if (aAttributes.Count() == 0)
{
break;
}
case EStateOpmlOutline:
// <body> <outline> <outline...
if(str.CompareF(KTagOutline) == 0) {
iOpmlState=EStateOpmlOutlineOutline;
CFeedInfo* newFeed = CFeedInfo::NewLC();
TBool hasTitle = EFalse;
TBool hasUrl = EFalse;
for (int i=0;i<aAttributes.Count();i++) {
RAttribute attr = aAttributes[i];
TBuf<KMaxStringBuffer> attr16;
attr16.Copy(attr.Attribute().LocalName().DesC().Left(KMaxStringBuffer));
HBufC* val16 = CnvUtfConverter::ConvertToUnicodeFromUtf8L(
attr.Value().DesC().Left(KMaxParseBuffer));
CleanupStack::PushL(val16);
// xmlUrl=...
if (attr16.Compare(KTagXmlUrl) == 0 || attr16.Compare(KTagUrl) == 0) {
newFeed->SetUrlL(*val16);
hasUrl = ETrue;
// htmlUrl
} else if (attr16.Compare(KTagHtmlUrl) == 0) {
newFeed->SetLinkL(*val16);
hasUrl = ETrue;
// title=...
} else if (attr16.Compare(KTagTitle) == 0) {
newFeed->SetTitleL(*val16);
hasTitle = ETrue;
// description=
} else if (attr16.Compare(KTagDescription) == 0) {
newFeed->SetDescriptionL(*val16);
// text=
} else if (attr16.Compare(KTagText) == 0) {
if (!hasTitle) {
newFeed->SetTitleL(*val16);
hasTitle = ETrue;
}
}
CleanupStack::PopAndDestroy(val16);
}
if (!hasUrl) {
break;
}
if (!hasTitle) {
newFeed->SetTitleL(newFeed->Url());
}
// if the title is the same as the URL, it is hardly a custom
// title, so let's replace it on update
if (newFeed->Title().Length() &&
newFeed->Url().Length() &&
newFeed->Title().Compare(newFeed->Url()) != 0) {
newFeed->SetCustomTitle();
}
if (iSearching) {
iFeedEngine.AddSearchResultL(newFeed);
CleanupStack::Pop(newFeed);
} else {
TRAPD(err, iFeedEngine.AddFeedL(*newFeed))
if (err == KErrNone)
{
iNumFeedsAdded++;
}
CleanupStack::PopAndDestroy(newFeed);
}
}
break;
default:
DP2("Ignoring tag %S when in state %d", &str, iOpmlState);
break;
}
DP1("OnStartElementL END state=%d", iOpmlState);
}
void COpmlParser::OnEndElementL(const RTagInfo& aElement, TInt /*aErrorCode*/)
{
TDesC8 lName = aElement.LocalName().DesC();
TBuf<KMaxStringBuffer> str;
str.Copy(aElement.LocalName().DesC());
DP2("OnEndElementL START state=%d, element=%S", iOpmlState, &str);
switch (iOpmlState) {
case EStateOpmlOutlineOutline:
iOpmlState=EStateOpmlOutline;
break;
case EStateOpmlOutline:
iOpmlState=EStateOpmlBody;
break;
case EStateOpmlBody:
iOpmlState = EStateOpmlRoot;
break;
default:
// fall back to body level when in doubt
iOpmlState = EStateOpmlBody;
//DP("Don't know how to handle end tag %S when in state %d"), &str, iFeedState);
break;
}
DP1("OnEndElementL END state=%d", iOpmlState);
}
void COpmlParser::OnContentL(const TDesC8& /*aBytes*/, TInt /*aErrorCode*/)
{
//DP("OnContentL()");
}
void COpmlParser::OnStartPrefixMappingL(const RString& /*aPrefix*/, const RString& /*aUri*/, TInt /*aErrorCode*/)
{
//DP("OnStartPrefixMappingL()");
}
void COpmlParser::OnEndPrefixMappingL(const RString& /*aPrefix*/, TInt /*aErrorCode*/)
{
//DP("OnEndPrefixMappingL()");
}
void COpmlParser::OnIgnorableWhiteSpaceL(const TDesC8& /*aBytes*/, TInt /*aErrorCode*/)
{
//DP("OnIgnorableWhiteSpaceL()");
}
void COpmlParser::OnSkippedEntityL(const RString& /*aName*/, TInt /*aErrorCode*/)
{
//DP("OnSkippedEntityL()");
}
void COpmlParser::OnProcessingInstructionL(const TDesC8& /*aTarget*/, const TDesC8& /*aData*/, TInt /*aErrorCode*/)
{
//DP("OnProcessingInstructionL()");
}
void COpmlParser::OnError(TInt aErrorCode)
{
DP1("COpmlParser::OnError %d", aErrorCode);
TRAP_IGNORE(iFeedEngine.OpmlParsingCompleteL(aErrorCode, iNumFeedsAdded));
}
TAny* COpmlParser::GetExtendedInterface(const TInt32 /*aUid*/)
{
//DP("GetExtendedInterface()");
return NULL;
}