00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "WikiDb.h"
00019
00020 _LIT(KCopyleftNotice, "<hr><small>All text is available under the terms of the <a href=http://en.wikipedia.org/wiki/Wikipedia:Text_of_the_GNU_Free_Documentation_License>GNU Free Documentation License</a>.<br>(See <a href=http://en.wikipedia.org/wiki/Wikipedia:Copyrights>Copyrights</a> for details.)</small>");
00021
00022 CWikiDb::CWikiDb() : CActive( EPriorityIdle )
00023 {
00024 CActiveScheduler::Add( this );
00025 }
00026
00027 CWikiDb::~CWikiDb()
00028 {
00029 Cancel();
00030 iQueryBuf.Close();
00031 iCountBuf.Close();
00032 iUtf8Chars.Close();
00033 iArticleText.Close();
00034 iStatement.Close();
00035 iCountStatement.Close();
00036 iDatabase.Close();
00037 delete iCurArticleTitle;
00038 }
00039
00040 CWikiDb* CWikiDb::NewL()
00041 {
00042 CWikiDb* db = new (ELeave)CWikiDb();
00043 CleanupStack::PushL(db);
00044 db->ConstructL();
00045 CleanupStack::Pop(db);
00046 return db;
00047 }
00048
00049 void CWikiDb::ConstructL()
00050 {
00051 User::LeaveIfError( iDatabase.Open( KSqlDbName ) );
00052 iQueryBuf.CreateL(KMaxCharByCharSqlQueryLen);
00053 iCountBuf.CreateL(KMaxCharByCharSqlQueryLen);
00054 iUtf8Chars.CreateL(KMaxCharByCharQueryLen);
00055 iArticleText.CreateL(KMaxArticleTextLen);
00056 iCurArticleTitle = HBufC::NewL( 0 );
00057 }
00058
00059
00060
00061 void CWikiDb::SearchL(const TDesC& aPrefix, MWikiCountObserver* aObserver)
00062 {
00063 iObserver = aObserver;
00064 SetPageAndItemL( EInitialise );
00065 HBufC* quoted = EscapeL(aPrefix);
00066 CleanupStack::PushL(quoted);
00067 User::LeaveIfError(CnvUtfConverter::ConvertFromUnicodeToUtf8(iUtf8Chars, *quoted));
00068 CleanupStack::PopAndDestroy(quoted);
00069 iQueryBuf.SetLength(0);
00070 iQueryBuf.Format(KWikiSqlStmtJustTitle, &iUtf8Chars);
00071 RunQueryL();
00072 StartRecordCountL();
00073 }
00074
00075 void CWikiDb::PageUpL( const TDesC& aPrefix )
00076 {
00077 TInt prevPage = iCurPage - 1;
00078 TInt offset = prevPage * KItemsPerPage;
00079 SetPageAndItemL( EPageUp );
00080 HBufC* quoted = EscapeL( aPrefix );
00081 CleanupStack::PushL( quoted );
00082 User::LeaveIfError( CnvUtfConverter::ConvertFromUnicodeToUtf8( iUtf8Chars, *quoted ) );
00083 CleanupStack::PopAndDestroy( quoted );
00084 iQueryBuf.SetLength(0);
00085 iQueryBuf.Format( KWikiSqlStmtTitleOffset, &iUtf8Chars, offset );
00086 RunQueryL();
00087 }
00088
00093 TBool CWikiDb::Next()
00094 {
00095 TBool found = ETrue;
00096 if ( iAtNextItem )
00097 {
00098
00099 iAtNextItem = EFalse;
00100 }
00101 else
00102 {
00103
00104 TInt res = iStatement.Next();
00105 found = ( res == KSqlAtRow );
00106 }
00107 if ( found )
00108 {
00109 SetPageAndItemL( ENextItem );
00110 }
00111 return found;
00112 }
00113
00114
00118 const TInt CWikiDb::GetTitle(TPtrC& aTitle)
00119 {
00120 return iStatement.ColumnText(0, aTitle);
00121 }
00122
00126 const TDesC8& CWikiDb::GetArticleContentL()
00127 {
00128 TPtrC ptr;
00129 GetTitle(ptr);
00130 EnsureArticleFormattedL(ptr);
00131 return iArticleText;
00132 }
00133
00134
00138 const TDesC8& CWikiDb::GetArticleContentL(const TDesC& aTitle)
00139 {
00140 EnsureArticleFormattedL(aTitle);
00141 return iArticleText;
00142 }
00143
00147 const TDesC8& CWikiDb::GetArticleContentL(const TDesC8& aTitle)
00148 {
00149 EnsureArticleFormattedL(aTitle);
00150 return iArticleText;
00151 }
00152
00153
00158 void CWikiDb::EnsureArticleFormattedL(const TDesC& aTitle)
00159 {
00160 TBuf8<128> titleUtf8;
00161 User::LeaveIfError(CnvUtfConverter::ConvertFromUnicodeToUtf8(titleUtf8, aTitle));
00162 EnsureArticleFormattedL(titleUtf8);
00163 }
00164
00165 void CWikiDb::EnsureArticleFormattedL(const TDesC8& aTitleUtf8)
00166 {
00167 HBufC8* quoted = EscapeL(aTitleUtf8);
00168 CleanupStack::PushL(quoted);
00169 RSqlStatement stmt;
00170 CleanupClosePushL(stmt);
00171 TBuf8<256> getArticleQuery;
00172 getArticleQuery.Format(KWikiSqlStmtGetArticle, quoted);
00173
00174 stmt.PrepareL(iDatabase, getArticleQuery);
00175 TInt res = stmt.Next();
00176 if ( res != KSqlAtRow )
00177 {
00178 User::Leave(KErrNotFound);
00179 }
00180 TPtrC8 content;
00181 TPtrC8 links;
00182 stmt.ColumnBinary(0, content);
00183 stmt.ColumnBinary(1, links);
00184
00185
00186 iArticleText.SetLength(0);
00187 _LIT(KHtmlEncodingMeta, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
00188 iArticleText.Append(KHtmlEncodingMeta);
00189 iArticleText.AppendFormat(KTitleFormat, &aTitleUtf8);
00190 iArticleText.Append(content);
00191 iArticleText.Append(KHtmlParagpraph);
00192
00193 _LIT8(KChars, " _|");
00194 TBuf8<3> chars;
00195 chars.Copy(KChars);
00196
00197 TBuf8<256> articleNameWithUnderscores;
00198 ReplaceChar(aTitleUtf8, articleNameWithUnderscores, chars[0], chars[1]);
00199
00200
00201 TInt linksLen = links.Length();
00202 TInt lastLinkEnd = 0;
00203 for ( TInt i = 0; i < linksLen; i++ ) {
00204 if ( links[i] == chars[2] )
00205 {
00206
00207 TPtrC8 link = links.Mid(lastLinkEnd, i - lastLinkEnd);
00208 lastLinkEnd = i+1;
00209 TBuf8<256> linkWithUnderscores;
00210 ReplaceChar(link, linkWithUnderscores, chars[0], chars[1]);
00211
00212 iArticleText.AppendFormat(KLinkFormat, &articleNameWithUnderscores, &linkWithUnderscores, &link);
00213 }
00214 }
00215
00216 iArticleText.AppendFormat(KFullArticleLinkFormat, &articleNameWithUnderscores);
00217 iArticleText.Append(KCopyleftNotice);
00218
00219 CleanupStack::PopAndDestroy( &stmt );
00220 CleanupStack::PopAndDestroy(quoted);
00221 }
00222
00223 void CWikiDb::ReplaceChar(const TDesC8& aSource, TDes8& aTarget, const TChar& aToReplace, const TChar& aReplacement)
00224 {
00225 TInt len = aSource.Length();
00226 for ( TInt i = 0 ; i < len; i++ )
00227 {
00228 TChar ch = aSource[i];
00229 if ( ch == aToReplace )
00230 {
00231 aTarget.Append(aReplacement);
00232 }
00233 else
00234 {
00235 aTarget.Append(ch);
00236 }
00237 }
00238 }
00239
00240 void CWikiDb::DoCancel()
00241 {
00242 iRequestCode = ECancel;
00243 }
00244
00245 void CWikiDb::RunL()
00246 {
00247 switch ( iRequestCode )
00248 {
00249 case ECancel:
00250 break;
00251
00252 case ERecordCount:
00253 {
00254 RunRecCountQueryL();
00255 TraverseRecCountL();
00256 }
00257 break;
00258
00259 case ERecordCountNext:
00260 {
00261 TInt found = iCountStatement.Next();
00262 if ( found == KSqlAtRow )
00263 {
00264 iNumRecords++;
00265 TraverseRecCountL();
00266 }
00267 else
00268 {
00269 iObserver->CountUpdatedL( iStatus.Int() );
00270 }
00271 }
00272 break;
00273 }
00274 }
00275
00276 void CWikiDb::SetCurrentArticleL( const TDesC& aTitle )
00277 {
00278 delete iCurArticleTitle;
00279 iCurArticleTitle = aTitle.AllocL();
00280 }
00281
00282 const TDesC8& CWikiDb::GetCurrentArticleContentL()
00283 {
00284 return GetArticleContentL( *iCurArticleTitle );
00285 }
00286
00287 TBool CWikiDb::DoesNextItemExist()
00288 {
00289 if ( iAtNextItem )
00290 {
00291 return iAtNextItem;
00292 }
00293 TInt res = iStatement.Next();
00294 iAtNextItem = ( res == KSqlAtRow );
00295 return iAtNextItem;
00296 }
00297
00298 void CWikiDb::RunQueryL()
00299 {
00300 iStatement.Close();
00301 iStatement.PrepareL( iDatabase, iQueryBuf );
00302 }
00303
00304 void CWikiDb::RunRecCountQueryL()
00305 {
00306 iCountStatement.Close();
00307 iCountStatement.PrepareL( iDatabase, iCountBuf );
00308 }
00309
00310 void CWikiDb::SetPageAndItemL( const TPageState aPageState )
00311 {
00312 switch ( aPageState )
00313 {
00314 case EInitialise:
00315 {
00316
00317
00318 iCurPage = -1;
00319 iCurItem = -1;
00320 iAtNextItem = EFalse;
00321 iNumRecords = 0;
00322 }
00323 break;
00324
00325 case EPageUp:
00326 {
00327
00328 TInt prevPage = iCurPage - 1;
00329
00330
00331
00332
00333 iCurItem = ( prevPage * KItemsPerPage ) -1;
00334
00335
00336
00337
00338 iCurPage = prevPage - 1;
00339 iAtNextItem = EFalse;
00340 }
00341 break;
00342
00343 case ENextItem:
00344 {
00345
00346 iCurItem++;
00347
00348
00349 if ( iCurItem % KItemsPerPage == 0 )
00350 {
00351 iCurPage++;
00352 }
00353 }
00354 break;
00355
00356 default:
00357 break;
00358 }
00359 }
00360
00361 const TUint16 KSingleQuote = 0x0027;
00362 HBufC* CWikiDb::EscapeL( const TDesC& aQuery) const
00363 {
00364 TInt len = aQuery.Length();
00365 HBufC* buf = HBufC::NewLC( len * 2 + 4 );
00366 TPtr ptr = (buf->Des());
00367 for( TInt i=0;i<len;i++ )
00368 {
00369 if( aQuery[i] == KSingleQuote )
00370 {
00371 ptr.Append(aQuery[i]);
00372 ptr.Append(aQuery[i]);
00373 }
00374 else
00375 {
00376 ptr.Append(aQuery[ i ]);
00377 }
00378 }
00379 CleanupStack::Pop(buf);
00380 return buf;
00381 }
00382
00383 HBufC8* CWikiDb::EscapeL( const TDesC8& aQuery) const
00384 {
00385 TInt len = aQuery.Length();
00386 HBufC8* buf = HBufC8::NewLC( len * 2 + 4 );
00387 TPtr8 ptr = (buf->Des());
00388 for( TInt i=0;i<len;i++ )
00389 {
00390 if( aQuery[i] == KSingleQuote )
00391 {
00392 ptr.Append(aQuery[i]);
00393 ptr.Append(aQuery[i]);
00394 }
00395 else
00396 {
00397 ptr.Append(aQuery[ i ]);
00398 }
00399 }
00400 CleanupStack::Pop(buf);
00401 return buf;
00402 }
00403
00404 void CWikiDb::StartRecordCountL()
00405 {
00406 if ( IsActive() )
00407 {
00408 Cancel();
00409 }
00410
00411 TBool syncCount = ETrue;
00412 TSqlScalarFullSelectQuery fullSelectQuery(iDatabase);
00413 iCountBuf.Zero();
00414 switch (iUtf8Chars.Length())
00415 {
00416 case 0:
00417 {
00418 iCountBuf.Append(KCountAll);
00419 break;
00420 }
00421 case 1:
00422 {
00423 iCountBuf.AppendFormat(KCountWith1Char, &iUtf8Chars);
00424 break;
00425 }
00426 case 2:
00427 {
00428 iCountBuf.AppendFormat(KCountWith2Chars, &iUtf8Chars);
00429 break;
00430 }
00431 case 3:
00432 {
00433 iCountBuf.AppendFormat(KCountWith3Chars, &iUtf8Chars);
00434 break;
00435 }
00436 case 4:
00437 {
00438 iCountBuf.AppendFormat(KCountWith4Chars, &iUtf8Chars);
00439 break;
00440 }
00441 case 5:
00442 {
00443 iCountBuf.AppendFormat(KCountWith5Chars, &iUtf8Chars);
00444 break;
00445 }
00446 default:
00447 {
00448 iCountBuf.SetLength( 0 );
00449 iCountBuf.Copy( iQueryBuf );
00450 TRequestStatus* status = &iStatus;
00451 iStatus = KRequestPending;
00452 iRequestCode = ERecordCount;
00453 SetActive();
00454 User::RequestComplete( status, KErrNone );
00455 syncCount = EFalse;
00456 break;
00457 }
00458 }
00459
00460 if ( syncCount )
00461 {
00462 TRAPD( err, iNumRecords = fullSelectQuery.SelectIntL( iCountBuf ) );
00463 switch ( err )
00464 {
00465 case KErrNotFound:
00466 iNumRecords = 0;
00467 break;
00468
00469 case KErrNone:
00470
00471 break;
00472
00473 default:
00474 User::Leave( err );
00475 break;
00476 }
00477 iObserver->CountUpdatedL( KErrNone );
00478 }
00479 }
00480
00481 void CWikiDb::TraverseRecCountL()
00482 {
00483 if ( IsActive() )
00484 {
00485 Cancel();
00486 }
00487 TRequestStatus* status = &iStatus;
00488 iStatus = KRequestPending;
00489 iRequestCode = ERecordCountNext;
00490 SetActive();
00491 User::RequestComplete( status, KErrNone );
00492 }
00493
00494