| 2 |      1 | /*
 | 
|  |      2 | * Copyright (c) 2007-2010 Sebastian Brannstrom, Lars Persson, EmbedDev AB
 | 
|  |      3 | *
 | 
|  |      4 | * All rights reserved.
 | 
|  |      5 | * This component and the accompanying materials are made available
 | 
|  |      6 | * under the terms of the License "Eclipse Public License v1.0"
 | 
|  |      7 | * which accompanies this distribution, and is available
 | 
|  |      8 | * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 | 
|  |      9 | *
 | 
|  |     10 | * Initial Contributors:
 | 
|  |     11 | * EmbedDev AB - initial contribution.
 | 
|  |     12 | *
 | 
|  |     13 | * Contributors:
 | 
|  |     14 | *
 | 
|  |     15 | * Description:
 | 
|  |     16 | *
 | 
|  |     17 | */
 | 
|  |     18 | 
 | 
|  |     19 | #include "OpmlParser.h"
 | 
|  |     20 | #include "debug.h"
 | 
|  |     21 | #include <f32file.h>
 | 
|  |     22 | #include <bautils.h>
 | 
|  |     23 | #include <s32file.h>
 | 
|  |     24 | #include <charconv.h>
 | 
|  |     25 | #include <xml/stringdictionarycollection.h>
 | 
|  |     26 | #include <utf.h>
 | 
|  |     27 | 
 | 
|  |     28 | using namespace Xml;
 | 
|  |     29 | const TInt KMaxParseBuffer = 2048;
 | 
|  |     30 | const TInt KMaxStringBuffer = 1024;
 | 
|  |     31 | COpmlParser::COpmlParser(CFeedEngine& aFeedEngine, RFs& aFs) : iFeedEngine(aFeedEngine),iFs(aFs)
 | 
|  |     32 | {	
 | 
|  |     33 | }
 | 
|  |     34 | 
 | 
|  |     35 | COpmlParser::~COpmlParser()
 | 
|  |     36 | {	
 | 
|  |     37 | }
 | 
|  |     38 | 
 | 
|  |     39 | void COpmlParser::ParseOpmlL(const TFileName &feedFileName, TBool aSearching)
 | 
|  |     40 | 	{
 | 
|  |     41 | 	DP1("ParseOpmlL BEGIN: %S", &feedFileName);
 | 
|  |     42 | 	
 | 
|  |     43 | 	iSearching = aSearching;
 | 
|  |     44 | 	_LIT8(KXmlMimeType, "text/xml");
 | 
|  |     45 | 	// Contruct the parser object
 | 
|  |     46 | 	CParser* parser = CParser::NewLC(KXmlMimeType, *this);
 | 
|  |     47 | 	iOpmlState = EStateOpmlRoot;
 | 
|  |     48 | 	iEncoding = EUtf8;
 | 
|  |     49 | 	iNumFeedsAdded = 0;
 | 
|  |     50 | 	ParseL(*parser, iFs, feedFileName);
 | 
|  |     51 | 
 | 
|  |     52 | 	CleanupStack::PopAndDestroy(parser);	
 | 
|  |     53 | 	//DP("ParseFeedL END");
 | 
|  |     54 | 	}
 | 
|  |     55 | 
 | 
|  |     56 | // from MContentHandler
 | 
|  |     57 | void COpmlParser::OnStartDocumentL(const RDocumentParameters& aDocParam, TInt /*aErrorCode*/)
 | 
|  |     58 | 	{
 | 
|  |     59 | 	DP("OnStartDocumentL()");
 | 
|  |     60 | 	HBufC* charset = HBufC::NewLC(KMaxParseBuffer);
 | 
|  |     61 | 	charset->Des().Copy(aDocParam.CharacterSetName().DesC());
 | 
|  |     62 | 	iEncoding = EUtf8;
 | 
|  |     63 | 	if (charset->CompareF(_L("utf-8")) == 0) {
 | 
|  |     64 | 		DP("setting UTF8");
 | 
|  |     65 | 		iEncoding = EUtf8;
 | 
|  |     66 | 	} else if (charset->CompareF(_L("ISO-8859-1")) == 0) {
 | 
|  |     67 | 		iEncoding = EUtf8; //Latin1;
 | 
|  |     68 | 	} else {
 | 
|  |     69 | 		DP1("unknown charset: %S", &charset);
 | 
|  |     70 | 	}
 | 
|  |     71 | 	CleanupStack::PopAndDestroy(charset);
 | 
|  |     72 | 	}
 | 
|  |     73 | 
 | 
|  |     74 | void COpmlParser::OnEndDocumentL(TInt /*aErrorCode*/)
 | 
|  |     75 | 	{
 | 
|  |     76 | 	iFeedEngine.OpmlParsingComplete(iNumFeedsAdded);
 | 
|  |     77 | 	//DP("OnEndDocumentL()");
 | 
|  |     78 | 	}
 | 
|  |     79 | 
 | 
|  |     80 | void COpmlParser::OnStartElementL(const RTagInfo& aElement, const RAttributeArray& aAttributes, TInt /*aErrorCode*/)
 | 
|  |     81 | 	{
 | 
|  |     82 | 	TBuf<KMaxStringBuffer> str;
 | 
|  |     83 | 	str.Copy(aElement.LocalName().DesC());
 | 
|  |     84 | 	DP2("OnStartElementL START state=%d, element=%S", iOpmlState, &str);
 | 
|  |     85 | 	iBuffer.Zero();
 | 
|  |     86 | 	switch (iOpmlState) {
 | 
|  |     87 | 	case EStateOpmlRoot:
 | 
|  |     88 | 		// <body>
 | 
|  |     89 | 		if (str.CompareF(KTagBody) == 0) {
 | 
|  |     90 | 			iOpmlState = EStateOpmlBody;
 | 
|  |     91 | 		}
 | 
|  |     92 | 		break;
 | 
|  |     93 | 	case EStateOpmlBody:
 | 
|  |     94 | 		// <body> <outline>
 | 
|  |     95 | 		
 | 
|  |     96 | 		if(str.CompareF(KTagOutline) == 0) {
 | 
|  |     97 | 			iOpmlState = EStateOpmlOutline;
 | 
|  |     98 | 		}
 | 
|  |     99 | 		break;
 | 
|  |    100 | 	case EStateOpmlOutline:
 | 
|  |    101 | 		// <body> <outline> <outline...
 | 
|  |    102 | 		if(str.CompareF(KTagOutline) == 0) {
 | 
|  |    103 | 			iOpmlState=EStateOpmlOutlineOutline;
 | 
|  |    104 | 			CFeedInfo* newFeed = CFeedInfo::NewLC();
 | 
|  |    105 | 			
 | 
|  |    106 | 			TBool hasTitle = EFalse;
 | 
|  |    107 | 			TBool hasUrl = EFalse;
 | 
|  |    108 | 			
 | 
|  |    109 | 			for (int i=0;i<aAttributes.Count();i++) {
 | 
|  |    110 | 				RAttribute attr = aAttributes[i];
 | 
|  |    111 | 				TBuf<KMaxStringBuffer> attr16;
 | 
|  |    112 | 				attr16.Copy(attr.Attribute().LocalName().DesC().Left(KMaxStringBuffer));
 | 
|  |    113 | 				HBufC* val16 = CnvUtfConverter::ConvertToUnicodeFromUtf8L(
 | 
|  |    114 | 						attr.Value().DesC().Left(KMaxParseBuffer));
 | 
|  |    115 | 				CleanupStack::PushL(val16);
 | 
|  |    116 | 				
 | 
|  |    117 | 				// xmlUrl=...
 | 
|  |    118 | 				if (attr16.Compare(KTagXmlUrl) == 0 || attr16.Compare(KTagUrl) == 0) {
 | 
|  |    119 | 					newFeed->SetUrlL(*val16);
 | 
|  |    120 | 					hasUrl = ETrue;			
 | 
|  |    121 | 				// htmlUrl
 | 
|  |    122 | 				} else if (attr16.Compare(KTagHtmlUrl) == 0) {
 | 
|  |    123 | 					newFeed->SetLinkL(*val16);
 | 
|  |    124 | 					hasUrl = ETrue;
 | 
|  |    125 | 				// text=...
 | 
|  |    126 | 				} else if (attr16.Compare(KTagTitle) == 0) {
 | 
|  |    127 | 					newFeed->SetTitleL(*val16);
 | 
|  |    128 | 					newFeed->SetCustomTitle();
 | 
|  |    129 | 					hasTitle = ETrue;
 | 
|  |    130 | 				// description=
 | 
|  |    131 | 				} else if (attr16.Compare(KTagDescription) == 0) {
 | 
|  |    132 | 					newFeed->SetDescriptionL(*val16);
 | 
|  |    133 | 				} else if (attr16.Compare(KTagText) == 0) {
 | 
|  |    134 | 					if (!hasTitle) {
 | 
|  |    135 | 						newFeed->SetTitleL(*val16);
 | 
|  |    136 | 						newFeed->SetCustomTitle();
 | 
|  |    137 | 						hasTitle = ETrue;
 | 
|  |    138 | 					}
 | 
|  |    139 | 				} 
 | 
|  |    140 | 				CleanupStack::PopAndDestroy(val16);
 | 
|  |    141 | 			}
 | 
|  |    142 | 
 | 
|  |    143 | 			if (!hasUrl) {
 | 
|  |    144 | 				break;
 | 
|  |    145 | 			}
 | 
|  |    146 | 			
 | 
|  |    147 | 			if (!hasTitle) {
 | 
|  |    148 | 				newFeed->SetTitleL(newFeed->Url());
 | 
|  |    149 | 			}
 | 
|  |    150 | 			
 | 
|  |    151 | 			if (iSearching) {
 | 
|  |    152 | 				iFeedEngine.AddSearchResultL(newFeed);
 | 
|  |    153 | 				CleanupStack::Pop(newFeed);
 | 
|  |    154 | 			} else {
 | 
|  |    155 | 				iFeedEngine.AddFeedL(*newFeed);
 | 
|  |    156 | 				CleanupStack::PopAndDestroy(newFeed);
 | 
|  |    157 | 				iNumFeedsAdded++;
 | 
|  |    158 | 			}
 | 
|  |    159 | 		}
 | 
|  |    160 | 		break;
 | 
|  |    161 | 	default:
 | 
|  |    162 | 		DP2("Ignoring tag %S when in state %d", &str, iOpmlState);
 | 
|  |    163 | 		break;
 | 
|  |    164 | 	}
 | 
|  |    165 | 	DP1("OnStartElementL END state=%d", iOpmlState);
 | 
|  |    166 | 	}
 | 
|  |    167 | 
 | 
|  |    168 | void COpmlParser::OnEndElementL(const RTagInfo& aElement, TInt /*aErrorCode*/)
 | 
|  |    169 | 	{
 | 
|  |    170 | 	
 | 
|  |    171 | 	TDesC8 lName = aElement.LocalName().DesC();
 | 
|  |    172 | 	TBuf<KMaxStringBuffer> str;
 | 
|  |    173 | 	str.Copy(aElement.LocalName().DesC());
 | 
|  |    174 | 
 | 
|  |    175 | 	DP2("OnEndElementL START state=%d, element=%S", iOpmlState, &str);
 | 
|  |    176 | 
 | 
|  |    177 | 	switch (iOpmlState) {
 | 
|  |    178 | 		case EStateOpmlOutlineOutline:
 | 
|  |    179 | 			iOpmlState=EStateOpmlOutline;
 | 
|  |    180 | 			break;
 | 
|  |    181 | 		case EStateOpmlOutline:
 | 
|  |    182 | 			iOpmlState=EStateOpmlBody;
 | 
|  |    183 | 			break;
 | 
|  |    184 | 		case EStateOpmlBody:
 | 
|  |    185 | 			iOpmlState = EStateOpmlRoot;
 | 
|  |    186 | 			break;
 | 
|  |    187 | 		default:
 | 
|  |    188 | 			// fall back to body level when in doubt
 | 
|  |    189 | 			iOpmlState = EStateOpmlBody;
 | 
|  |    190 | 			//DP("Don't know how to handle end tag %S when in state %d"), &str, iFeedState);
 | 
|  |    191 | 			break;
 | 
|  |    192 | 	}
 | 
|  |    193 | 
 | 
|  |    194 | 	DP1("OnEndElementL END state=%d", iOpmlState);	
 | 
|  |    195 | 	}
 | 
|  |    196 | 
 | 
|  |    197 | void COpmlParser::OnContentL(const TDesC8& /*aBytes*/, TInt /*aErrorCode*/)
 | 
|  |    198 | 	{
 | 
|  |    199 | 	//DP("OnContentL()");
 | 
|  |    200 | 	}
 | 
|  |    201 | 
 | 
|  |    202 | void COpmlParser::OnStartPrefixMappingL(const RString& /*aPrefix*/, const RString& /*aUri*/, TInt /*aErrorCode*/)
 | 
|  |    203 | 	{
 | 
|  |    204 | 	//DP("OnStartPrefixMappingL()");
 | 
|  |    205 | 	}
 | 
|  |    206 | 
 | 
|  |    207 | void COpmlParser::OnEndPrefixMappingL(const RString& /*aPrefix*/, TInt /*aErrorCode*/)
 | 
|  |    208 | 	{
 | 
|  |    209 | 	//DP("OnEndPrefixMappingL()");
 | 
|  |    210 | 	}
 | 
|  |    211 | 
 | 
|  |    212 | void COpmlParser::OnIgnorableWhiteSpaceL(const TDesC8& /*aBytes*/, TInt /*aErrorCode*/)
 | 
|  |    213 | 	{
 | 
|  |    214 | 	//DP("OnIgnorableWhiteSpaceL()");
 | 
|  |    215 | 	}
 | 
|  |    216 | 
 | 
|  |    217 | void COpmlParser::OnSkippedEntityL(const RString& /*aName*/, TInt /*aErrorCode*/)
 | 
|  |    218 | 	{
 | 
|  |    219 | 	//DP("OnSkippedEntityL()");
 | 
|  |    220 | 	}
 | 
|  |    221 | 
 | 
|  |    222 | void COpmlParser::OnProcessingInstructionL(const TDesC8& /*aTarget*/, const TDesC8& /*aData*/, TInt /*aErrorCode*/)
 | 
|  |    223 | 	{
 | 
|  |    224 | 	//DP("OnProcessingInstructionL()");
 | 
|  |    225 | 	}
 | 
|  |    226 | 
 | 
|  |    227 | void COpmlParser::OnError(TInt aErrorCode)
 | 
|  |    228 | 	{
 | 
|  |    229 | 	DP1("COpmlParser::OnError %d", aErrorCode);
 | 
|  |    230 | 	iFeedEngine.OpmlParsingComplete(iNumFeedsAdded);
 | 
|  |    231 | 	}
 | 
|  |    232 | 
 | 
|  |    233 | TAny* COpmlParser::GetExtendedInterface(const TInt32 /*aUid*/)
 | 
|  |    234 | 	{
 | 
|  |    235 | 	//DP("GetExtendedInterface()");
 | 
|  |    236 | 	return NULL;
 | 
|  |    237 | 	}
 |