diff -r 000000000000 -r e686773b3f54 phonebookengines/contactsmodel/cntplsql/src/cplcollectioniterator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookengines/contactsmodel/cntplsql/src/cplcollectioniterator.cpp Tue Feb 02 10:12:17 2010 +0200 @@ -0,0 +1,1828 @@ +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "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: +// + +/** + @file + @internalComponent + @released +*/ + +#include "persistencelayerimpl.h" +#include "clplcontactproperties.h" +#include +#include "cntsqlprovider.h" +#include "dbsqlconstants.h" +#include "cntpersistenceutility.h" +#include "CNTSTD.H" +#include + +// +// The TFindFieldFlags enumeration is duplicated in cviewiterator.h. It should +// be refactored into a shared header if possible. +// +enum TFindFieldFlags + { + EFindInAllFields =0x00000001, + EFindFirstName =0x00000002, + EFindLastName =0x00000004, + EFindCompanyName =0x00000008, + EFindInEmailField =0x00000010, + EFindInAnyIdentityField =0x00000020, + EFindFirstNamePronunciation =0x00000040, + EFindLastNamePronunciation =0x00000080, + EFindCompanyNamePronunciation =0x00000100, + EFindInSIPField =0x00000200 + }; + + +// Maximum number of find iterations to perform in the CPlCollection::FindL() +// method before returning. +const TInt KFindIterations = 16; + +// These flags must be in the same order as the corresponding fields in +// RPplIdentityTable::TIdentityField. +const TFindFieldFlags KNameFlags[] = + { + EFindFirstName, + EFindLastName, + EFindCompanyName, + EFindFirstNamePronunciation, + EFindLastNamePronunciation, + EFindCompanyNamePronunciation + }; + +// Collation level that ignore accents (i.e. 'a' == 'ä'). +const TInt KCollationLevel= 0; + +// +// The TKeyCmpTextLength class is duplicated in ccontactprivate.cpp. It should +// be refactored into shared code/library if possible. +// +NONSHARABLE_CLASS(TKeyCmpTextLength) : public TKeyArrayFix + { +public: + TKeyCmpTextLength(); +protected: + // From TKey. + virtual TInt Compare(TInt aLeft,TInt aRight) const; + }; + + +TKeyCmpTextLength::TKeyCmpTextLength() + // Doesn't matter using ECmpNormal, it's ignored. + : + TKeyArrayFix(0,ECmpNormal,0) + { + } + + +/** +Compare the lengths of the words. +*/ +TInt TKeyCmpTextLength::Compare(TInt aLeft,TInt aRight) const + { + TDesC* left=(*((TDesC**)At(aLeft))); + TDesC* right=(*((TDesC**)At(aRight))); + return right->Length()-left->Length(); + } + + +/** +Used as a cleanup item for buffer allocation in CreateFindTextL(). + +@param aHBufC The buffer to be cleaned up (deleted). +*/ +GLDEF_C void DestroyHBufC(TAny* aHBufC) + { + delete *STATIC_CAST(HBufC**, aHBufC); + } + + +/** +Create the "find text", used for the SQL query on the view, from the given text. + +@param aText Text to turn into "find text". + +@return "Find text" form of the given text. +*/ +HBufC* CreateFindTextL(const TDesC& aText) + { + _LIT(KApostrophe, "'"); + TInt location = aText.Find(KApostrophe); + + HBufC* buf=HBufC::NewL(aText.Length()+2); + TPtr ptr=buf->Des(); + ptr.Zero(); + ptr.Append('%'); + ptr += aText; + ptr.Append('%'); + + // Insert additional apostrophes if required. This is so the SQL search + // string behaves correctly. + if(location != KErrNotFound) + { + CleanupStack::PushL(buf); + + TPtrC rightBuf = *buf; + HBufC* leftBuf=HBufC::NewL(0); + CleanupStack::PushL(TCleanupItem(DestroyHBufC, &leftBuf)); + + // Increment location because we have added the * character. + location++; + + while (location != KErrNotFound) + { + // Copy left hand string up to the first apostrophe into a new + // buffer and append an apostrophe. + leftBuf = leftBuf->ReAllocL(leftBuf->Length()+location+2); + TPtr ptrLeftBuf = leftBuf->Des(); + ptrLeftBuf.Append(rightBuf.Left(location+1)); + ptrLeftBuf += (KApostrophe); + + // Make a copy of the right hand part. + TInt rightLength = rightBuf.Length()-location-1; + rightBuf.Set(rightBuf.Right(rightLength)); + + // Search for another apostrophe. + location = rightBuf.Find(KApostrophe); + } + + // Now append the rest of the right hand part. + leftBuf = leftBuf->ReAllocL(leftBuf->Length()+rightBuf.Length()); + TPtr ptrLeftBuf = leftBuf->Des(); + ptrLeftBuf.Append(rightBuf); + + CleanupStack::Pop(&leftBuf); + CleanupStack::PopAndDestroy(buf); + + return (leftBuf); + } + + return (buf); + } + + +/** +For the given field type UID, increase aIdentityColumnsCount if the field maps +to one of the columns in the Identity table and add the relevant find flag. + +@param aUid Field type UID. +@param aFindFlags Updated with find flag which maps to the given field type UID. +@param aIdentityColumnsCount Incremented if the field type UID maps to a column +in the Identity table. +*/ +// +// This method is duplicated in cviewiterator.cpp. It should be refactored into +// shared code/library if possible. +// +void SetFindFlagsAndColumnsCount(TInt32 aUid,TInt& aFindFlags,TInt& aIdentityColumnsCount) + { + switch(aUid) + { + case KUidContactFieldGivenNameValue: + { + ++aIdentityColumnsCount; + aFindFlags |= EFindFirstName; + break; + } + case KUidContactFieldFamilyNameValue: + { + ++aIdentityColumnsCount; + aFindFlags |= EFindLastName; + break; + } + case KUidContactFieldCompanyNameValue: + { + ++aIdentityColumnsCount; + aFindFlags |= EFindCompanyName; + break; + } + case KUidContactFieldGivenNamePronunciationValue: + { + ++aIdentityColumnsCount; + aFindFlags |= EFindFirstNamePronunciation; + break; + } + case KUidContactFieldFamilyNamePronunciationValue: + { + ++aIdentityColumnsCount; + aFindFlags |= EFindLastNamePronunciation; + break; + } + case KUidContactFieldCompanyNamePronunciationValue: + { + ++aIdentityColumnsCount; + aFindFlags |= EFindCompanyNamePronunciation; + break; + } + case KUidContactFieldEMailValue: + { + aFindFlags |= EFindInEmailField; + break; + } + case KUidContactFieldSIPIDValue: + { + aFindFlags |= EFindInSIPField; + break; + } + case KUidContactFieldMatchAllValue: + { + //added to address issues raised in INC049017 + //needed as a seperate case instead of modifying the default value because Identitys should not always be searched + //if a valid KUid is used. e.g.: KUidContactFieldAddress. If the default value was modified and someone wanted to + //perform an address search using Elizabeth (as in elizabeth road) contacts of that name would also be found. + aFindFlags |= EFindInAllFields|EFindInAnyIdentityField; + break; + } + default: + aFindFlags |= EFindInAllFields; + } + } + + +/** +Determine if the Identity table requires to be searched for the given find +flags. + +@param aFindFlags Set of find flags describing which fields will be searched. + +@return ETrue If one of the flags in aFindFlags maps to a column in the Identity +table, EFalse otherwise. +*/ +// +// This method is duplicated in cviewiterator.cpp. It should be refactored into +// shared code/library if possible. +// +TBool SearchIdentityFieldsRequired(TInt aFindFlags) + { + return aFindFlags & + ( EFindInAnyIdentityField | EFindFirstName | EFindLastName | EFindCompanyName | + EFindFirstNamePronunciation | EFindLastNamePronunciation | EFindCompanyNamePronunciation); + } + + +/** +Determine if only the Identity table requires to be searched for the given find +flags. + +@param aFindFlags Set of find flags describing which fields will be searched. + +@return ETrue If one of the flags in aFindFlags maps to a column in the Identity +table and no columns in any other table, EFalse otherwise. +*/ +// +// This method is duplicated in cviewiterator.cpp. It should be refactored into +// shared code/library if possible. +// +TBool CPlCollection::UsesIdentityFieldsOnly(TInt aFindFlags) + { + return (aFindFlags & (EFindFirstName | EFindLastName | EFindCompanyName | + EFindFirstNamePronunciation |EFindLastNamePronunciation |EFindCompanyNamePronunciation) ) && + ! (aFindFlags & (EFindInAllFields | EFindInEmailField | EFindInSIPField) ); + } + + +/** +Update a set of find flags and an Identity table column count from the field +type UIDs in the given field definition. + +@param aFindFlags Updated with those flags which map to the field type UIDs in +aFieldDef. +@param aIdentityColumnsCount Updated with the number of field type UIDs in +aFieldDef which map to columns in the Identity table. +@param aFieldDef Field definition from which to create set of find flags and +Identity table column count. +*/ +// +// This method is duplicated in cviewiterator.cpp. It should be refactored into +// shared code/library if possible. +// +void ConstructBitwiseFindFlags(TInt& aFindFlags,TInt& aIdentityColumnsCount,const CContactItemFieldDef* aFieldDef) + { + if(aFieldDef!=NULL && aFieldDef->Count()>0) + { + for(TInt ii = 0;ii < aFieldDef->Count();ii++) + { + SetFindFlagsAndColumnsCount(aFieldDef->At(ii).iUid,aFindFlags,aIdentityColumnsCount); + } + } + else + { + aFindFlags |= EFindInAllFields|EFindInAnyIdentityField; + } + } + + +/** +Update a set of find flags and an Identity table column count from the field +type UIDs in the given text definition. This can be used to tell the find +method what tables need to be searched for a given text definition. If the text +definition is NULL we search in all tables. + +@param aFindFlags Updated with those flags which map to the field type UIDs in +aTextDef. +@param aIdentityColumnsCount Updated with the number of field type UIDs in +aTextDef which map to columns in the Identity table. +@param aTextDef Text definition from which to create set of find flags and +Identity table column count. +*/ +// +// This method is duplicated in cviewiterator.cpp. It should be refactored into +// shared code/library if possible. +// +void CPlCollection::ConstructBitwiseFlagsFromTextDef(TInt& aFindFlags,TInt& aIdentityColumnsCount,const CContactTextDef* aTextDef) + { + if(aTextDef != NULL && aTextDef->Count()>0) + { + for(TInt ii = 0;ii < aTextDef->Count();ii++) + { + SetFindFlagsAndColumnsCount(aTextDef->At(ii).iFieldType.iUid,aFindFlags,aIdentityColumnsCount); + } + TFieldType fallback = aTextDef->FallbackField(); + if (fallback != KUidContactFieldNone) + SetFindFlagsAndColumnsCount(fallback.iUid,aFindFlags,aIdentityColumnsCount); + } + else + { + aFindFlags |= EFindInAllFields|EFindInAnyIdentityField; + } + } + + +/** +Utility method used to search a contact id in contact database. + +@param aReqId requested contact id to look for +@param aId out parameter; will be filled with the actual found contact item id +@param aContactType specifies contact type to be search +@param aDeleted set to ETrue if a search through deleted contacts is required + +@return ETrue if a contact item with contact id greater or equal with aReqId exists in contact database + EFalse otherwise +*/ +TBool CPlCollection::SeekContactL(TContactItemId aReqId,TContactItemId& aId,TUid& aContactType, TBool& aDeleted) + { + TBool ret = ETrue; + + HBufC* selectString = HBufC::NewLC(KSelectTwoFieldsWithGreater().Length() + + KContactId().Length() + + KContactTypeFlags().Length() + + KSqlContactTableName().Length() + + KContactId().Length() + 3); + TPtr ptrSelectString = selectString->Des(); + ptrSelectString.Format(KSelectTwoFieldsWithGreater, &KContactId, &KContactTypeFlags, &KSqlContactTableName, &KContactId, aReqId); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), ptrSelectString)); + + const TInt idIndex = selectStatement.ColumnIndex(KContactId); + User::LeaveIfError(idIndex); + const TInt typeIndex = selectStatement.ColumnIndex(KContactTypeFlags); + User::LeaveIfError(typeIndex); + TInt err; + if((err = selectStatement.Next()) == KSqlAtRow) + { + aId = selectStatement.ColumnInt(idIndex); + TInt typeFlags = selectStatement.ColumnInt(typeIndex); + aContactType = TCntPersistenceUtility::TypeFlagsToContactTypeUid(typeFlags); + TInt attr = (typeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift; + aDeleted = (attr == EContactAttrsFlags_Deleted); + } + else + { + User::LeaveIfError(err); + ret = EFalse; + } + + CleanupStack::PopAndDestroy(2, selectString); //selectStatement, selectString + return ret; + } + +/** +CPlCollection constructor. + +@param aContactsFile Contacts file object from the Persistence Layer. +*/ +CPlCollection::CPlCollection(CPplContactsFile& aContactsFile) + : + iContactsFile(aContactsFile) + { + } + +/** +CPlCollection ConstructL +*/ +void CPlCollection::ConstructL() + { + TCntSqlStatementType statementType(ESelect, KSqlContactTableName); + iSelectStatement = TSqlProvider::GetSqlStatementL(statementType); + } + +/** +CPlCollection NewL +*/ +CPlCollection* CPlCollection::NewL(CPplContactsFile& aContactsFile) + { + CPlCollection* self = new (ELeave) CPlCollection(aContactsFile); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +/** +CPlCollection destructor. +*/ +CPlCollection::~CPlCollection() + { + Reset(); + delete iSelectStatement; + iSelectStatement = NULL; + } + + +/** +Get the count of contacts in the Contacts table. + +@return Count of contacts in the Contacts table. +*/ +TInt CPlCollection::ContactCountL() + { + TInt count = 0; + + //count contact cards + HBufC* selectString = HBufC::NewLC(KCountTypeSelect().Length() + + KSqlContactTableName().Length() + KContactTypeFlags().Length() + KContactTypeFlags().Length() + 6); + TPtr ptrSelectString = selectString->Des(); + ptrSelectString.Format(KCountTypeSelect, &KSqlContactTableName, &KContactTypeFlags, EContactType_Shift, EContactTypeFlags_ContactCard, + &KContactTypeFlags, EContactAttributes_Shift, EContactAttrsFlags_Deleted); + + TSqlScalarFullSelectQuery scalarQuery(iContactsFile.NamedDatabase()); + + count = scalarQuery.SelectIntL(ptrSelectString); + CleanupStack::PopAndDestroy(selectString); + + //count own card + selectString = HBufC::NewLC(KCountTypeSelect().Length() + + KSqlContactTableName().Length() + KContactTypeFlags().Length() + KContactTypeFlags().Length() + 6); + ptrSelectString = selectString->Des(); + ptrSelectString.Format(KCountTypeSelect, &KSqlContactTableName, &KContactTypeFlags, EContactType_Shift, EContactTypeFlags_OwnCard, + &KContactTypeFlags, EContactAttributes_Shift, EContactAttrsFlags_Deleted); + + count += scalarQuery.SelectIntL(ptrSelectString); + CleanupStack::PopAndDestroy(selectString); + + //count groups + selectString = HBufC::NewLC(KCountTypeSelect().Length() + + KSqlContactTableName().Length() + KContactTypeFlags().Length() + KContactTypeFlags().Length() + 6); + ptrSelectString = selectString->Des(); + ptrSelectString.Format(KCountTypeSelect, &KSqlContactTableName, &KContactTypeFlags, EContactType_Shift, EContactTypeFlags_Group, + &KContactTypeFlags, EContactAttributes_Shift, EContactAttrsFlags_Deleted); + + count += scalarQuery.SelectIntL(ptrSelectString); + CleanupStack::PopAndDestroy(selectString); + + //card template + selectString = HBufC::NewLC(KCountTypeSelect().Length() + + KSqlContactTableName().Length() + KContactTypeFlags().Length() + KContactTypeFlags().Length() + 6); + ptrSelectString = selectString->Des(); + ptrSelectString.Format(KCountTypeSelect, &KSqlContactTableName, &KContactTypeFlags, EContactType_Shift, EContactTypeFlags_CardTemplate, + &KContactTypeFlags, EContactAttributes_Shift, EContactAttrsFlags_Deleted); + + count += scalarQuery.SelectIntL(ptrSelectString); + CleanupStack::PopAndDestroy(selectString); + + return count; + } + + +/** +Get a collection of contact IDs using the specified view/collection parameters. + +@param aViewType Type of view/collection. +@param aTime Used if view/collection type is "changed since". +@param aGuid Used if view/collection type is "find GUID". + +@return Contact ID array of those contacts which match the specified collection +parameters. +*/ +CContactIdArray* CPlCollection::CollectionL(TLplViewType aViewType,TTime aTime,const TDesC& aGuid) + { + CContactIdArray* idArray=NULL; + switch(aViewType) + { + case EChangedSince : + { + idArray = ChangedSinceL(aTime); + } + break; + case EDeleted : + { + idArray = DeletedL(); + } + break; + case EUnfiled : + { + idArray = UnfiledL(); + } + break; + case EFindGuid : + { + idArray = GuidL(aGuid); + } + break; + default: + { + User::Leave(KErrNotFound); + } + break; + } + return idArray; + } + + +/** +Utility method used to serach hint field for provided contact id + +@param aBitWiseFileter filter used for hint matching +@param aContactId contact item id + +@return ETrue if a hint is found + EFalse otherwise +*/ +TBool CPlCollection::ContactMatchesHintFieldL(TInt aBitWiseFilter, TContactItemId aContactId) + { + TBool hintMatch = EFalse; + + HBufC* searchCond = HBufC::NewLC(KConditionClause().Length() + KContactId().Length() + 32); + searchCond->Des().Format(KConditionClause, &KContactId, aContactId); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlagsParam, KSpace); + iSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + TInt err; + if((err = selectStatement.Next()) == KSqlAtRow) + { + const TInt typeFlagsIndex = iSelectStatement->ParameterIndex(KContactTypeFlags); + TInt typeFlags = selectStatement.ColumnInt(typeFlagsIndex); + TUid contactType = TCntPersistenceUtility::TypeFlagsToContactTypeUid(typeFlags); + if (iContactsFile.ContactProperties().CheckType(contactType)) + { + TInt hint = typeFlags & EContactHintFlags_Mask; + hintMatch = HintFieldMatchesFilter(hint, aBitWiseFilter); + } + } + else + { + User::LeaveIfError(err); + } + + CleanupStack::PopAndDestroy(2, searchCond); //searchCond, selectStatement + return hintMatch; + } + +/** +Utility method used for phone matching + +@param aNumber phone number to be matched +@param aMatchLengthFromRight specifies the number of relevant characters to be matched +@return array with contact ids matching passed phone number +*/ +CContactIdArray* CPlCollection::MatchPhoneNumberL(const TDesC& aNumber, const TInt aMatchLengthFromRight) + { + return iContactsFile.FieldMatcher().MatchPhoneNumberL(aNumber, aMatchLengthFromRight); + } + + +/** +Get a collection of contact IDs which have the given GUID. Since GUIDs are +unique (i.e. no two contacts can have the same GUID) there will only ever be +one contact ID in this collection. + +@param aGuid Contact GUID. + +@return Contact ID array containing the contact with the given GUID. +*/ +CContactIdArray* CPlCollection::GuidL(const TDesC& aGuid) + { + // Create the database query string. + TBuf<256> searchCond; + _LIT(KFormat,"%S = '%S'"); + searchCond.Format(KFormat, &KContactGuidString, &aGuid); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetConditionL(searchCond); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + CContactIdArray* array = CContactIdArray::NewL(); + CleanupStack::PushL(array); + + TInt idIndex = iSelectStatement->ParameterIndex(KContactId); + TInt err; + while((err = selectStatement.Next()) == KSqlAtRow) + { + array->AddL(selectStatement.ColumnInt(idIndex)); + } + + if(err != KSqlAtEnd) + { + User::LeaveIfError(err); + } + + CleanupStack::Pop(array); + CleanupStack::PopAndDestroy(&selectStatement); + + return array; + } + + +/** +Get a collection of contact IDs which have changed since the given time. + +@param aTime Changed since time. + +@return Contact ID array of those contacts which have changed since the given +time. +*/ +CContactIdArray* CPlCollection::ChangedSinceL(TTime aTime) + { + // Create the database query string. + _LIT(KFormat,"%S >= %LD or %S >= %LD"); + HBufC* searchCond = HBufC::NewLC(KFormat().Length()+ + KContactCreationDate().Length()+KContactLastModified().Length()+ + 2 * 64); + + TPtr ptr = searchCond->Des(); + ptr.Format(KFormat, &KContactCreationDate, aTime.Int64(), &KContactLastModified, aTime.Int64()); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + iSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + CContactIdArray* array = CContactIdArray::NewL(); + CleanupStack::PushL(array); + + const TInt idIndex = iSelectStatement->ParameterIndex(KContactId); + const TInt typeFlagsIndex = iSelectStatement->ParameterIndex(KContactTypeFlags); + TInt err; + + // Iterate through the view and include only those contacts of the desired + // type (as determined by CheckType()). + while((err = selectStatement.Next()) == KSqlAtRow) + { + TInt typeFlags = selectStatement.ColumnInt(typeFlagsIndex); + TUid contactType = TCntPersistenceUtility::TypeFlagsToContactTypeUid(typeFlags); + if (iContactsFile.ContactProperties().CheckType(contactType)) + { + array->AddL(selectStatement.ColumnInt(idIndex)); + } + } + + if(err != KSqlAtEnd) + { + User::LeaveIfError(err); + } + + CleanupStack::Pop(array); + CleanupStack::PopAndDestroy(2,searchCond); // searchCond, selectStatement + + return array; + } + + +/** +Get a collection of contact IDs which are 'unfiled'. + +@return Contact ID array of those contacts which are 'unfiled'. +*/ +CContactIdArray* CPlCollection::UnfiledL() + { + RSqlStatement selectStmt; + CleanupClosePushL(selectStmt); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + + User::LeaveIfError(selectStmt.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + CContactIdArray* array = CContactIdArray::NewL(); + CleanupStack::PushL(array); + + TInt idIndex = iSelectStatement->ParameterIndex(KContactId); + TInt typeFlagsIndex = iSelectStatement->ParameterIndex(KContactTypeFlags); + + // for WHERE contact_group_member_id = [contact id value] + const TInt KWhereMemberClauseBufSize(KGroupContactGroupMemberId().Size() + KWhereStringEqualsStringFormatText().Size() + KGroupContactGroupMemberIdParam().Size() ); + HBufC* whereMemberIdClause = HBufC::NewLC(KWhereMemberClauseBufSize); + whereMemberIdClause->Des().AppendFormat(KWhereStringEqualsStringFormatText, &KGroupContactGroupMemberId, &KGroupContactGroupMemberIdParam ); + + // select group id + // For a statement in the following format: + // SELECT contact_group_id FROM groups + // WHERE contact_group_member_id = [contact id value]; + // + TCntSqlStatementType statementType(ESelect, KSqlContactGroupTableName); + CCntSqlStatement* selectGroupsStmnt = TSqlProvider::GetSqlStatementL(statementType); + CleanupStack::PushL(selectGroupsStmnt); + selectGroupsStmnt->SetParamL(KGroupContactGroupId(), KNullDesC() ); + selectGroupsStmnt->SetConditionL(*whereMemberIdClause); + const TInt KGroupContactGroupMemberIdParamIndex(KFirstIndex); // second parameter in the query + + TInt err; + while((err = selectStmt.Next()) == KSqlAtRow) + { + TInt typeFlags = selectStmt.ColumnInt(typeFlagsIndex); + TUid contactType = TCntPersistenceUtility::TypeFlagsToContactTypeUid(typeFlags); + TInt contactAttrib = (typeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift; + + if (contactAttrib & CContactItem::EDeleted) + { + continue; + } + + if (contactType == KUidContactCard || contactType == KUidContactOwnCard) + { + TInt contactId = selectStmt.ColumnInt(idIndex); + + RSqlStatement selectGroupSqlStmt; + CleanupClosePushL(selectGroupSqlStmt); + + selectGroupSqlStmt.PrepareL(iContactsFile.NamedDatabase(), selectGroupsStmnt->SqlStringL() ); + User::LeaveIfError(selectGroupSqlStmt.BindInt(KGroupContactGroupMemberIdParamIndex, contactId)); + if((err = selectGroupSqlStmt.Next()) == KSqlAtEnd) + { + //add the contact id which is not in any group + array->AddL(contactId); + } + + User::LeaveIfError(err); + CleanupStack::PopAndDestroy(&selectGroupSqlStmt); + } + } + + if(err != KSqlAtEnd) + { + User::LeaveIfError(err); + } + + CleanupStack::PopAndDestroy(2, whereMemberIdClause); //whereMemberIdClause, selectGroupsStmnt # + CleanupStack::Pop(array); + CleanupStack::PopAndDestroy(&selectStmt); + return array; + } + + +/** +Get a collection of contact IDs which are marked as deleted. + +@return Contact ID array of those contacts which are marked as deleted. +*/ +CContactIdArray* CPlCollection::DeletedL() + { + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + CContactIdArray* array = CContactIdArray::NewL(); + CleanupStack::PushL(array); + + TInt idIndex = iSelectStatement->ParameterIndex(KContactId); + TInt typeFlagsIndex = iSelectStatement->ParameterIndex(KContactTypeFlags); + TInt err; + while((err = selectStatement.Next()) == KSqlAtRow) + { + TInt typeFlags = selectStatement.ColumnInt(typeFlagsIndex); + TInt contactAttrib = (typeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift; + + if (contactAttrib & CContactItem::EDeleted) + { + array->AddL(selectStatement.ColumnInt(idIndex)); + } + } + + if(err != KSqlAtEnd) + { + User::LeaveIfError(err); + } + + CleanupStack::Pop(array); + CleanupStack::PopAndDestroy(&selectStatement); + + return array; + } + + +/** +Perform a synchronous find for the given text and field definition. + +@param aText Text to find. +@param aFieldDef Field definition to use. +@param aSessionID + +@return Array of contact IDs resulting from the search. +*/ +CContactIdArray* CPlCollection::FindL(const TDesC& aText, const CContactItemFieldDef* aFieldDef, TUint aSessionId) + { + CContactIdArray* idsFound = CContactIdArray::NewL(); + CleanupStack::PushL(idsFound); + + // Create the "find text" from the given text. + HBufC *findText = CreateFindTextL(aText); + CleanupStack::PushL(findText); + + // Find out what tables need to included in the search as determined by the + // given field definition. + TInt findFlags = 0; + TInt identityTableFieldsCount = 0; + ConstructBitwiseFindFlags(findFlags,identityTableFieldsCount,aFieldDef); + + // Search through the first/last name (and their pronunciation) and company + // names fields in the Identity table. + if ( SearchIdentityFieldsRequired(findFlags) ) + { + _LIT(KWhereClause,"(%S LIKE '%S') OR (%S LIKE '%S') OR (%S LIKE '%S') OR (%S LIKE '%S') OR (%S LIKE '%S') OR (%S LIKE '%S')"); + HBufC* searchCond = HBufC::NewLC(KWhereClause().Length() + + KContactFirstName().Length() + findText->Length() + + KContactLastName().Length() + findText->Length() + + KContactCompanyName().Length() + findText->Length() + + KContactFirstNamePrn().Length() + findText->Length() + + KContactLastNamePrn().Length() + findText->Length() + + KContactCompanyNamePrn().Length() + findText->Length()); + searchCond->Des().Format(KWhereClause, &KContactFirstName, findText, + &KContactLastName, findText, &KContactCompanyName, findText, + &KContactFirstNamePrn, findText, &KContactLastNamePrn, findText, + &KContactCompanyNamePrn, findText); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + iSelectStatement->SetParamL(KContactFirstName, KSpace); + iSelectStatement->SetParamL(KContactLastName, KSpace); + iSelectStatement->SetParamL(KContactCompanyName, KSpace); + iSelectStatement->SetParamL(KContactFirstNamePrn, KSpace); + iSelectStatement->SetParamL(KContactLastNamePrn, KSpace); + iSelectStatement->SetParamL(KContactCompanyNamePrn, KSpace); + iSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + while(PerformFindIterationL(idsFound, aText, selectStatement, findFlags, aSessionId)) + { + // Keep performing find iteration until EFalse is returned. + } + + CleanupStack::PopAndDestroy(2, searchCond); // searchCond, selectStatement + } + + // Search through the email address fields + if (findFlags & (EFindInAllFields | EFindInEmailField)) + { + TCntSqlStatementType statementType(ESelect, KSqlContactCommAddrTableName); + CCntSqlStatement* cntSelectStatement = TSqlProvider::GetSqlStatementL(statementType); + CleanupStack::PushL(cntSelectStatement); + + _LIT(KWhereClause, "(%S = %d) AND (%S LIKE '%S')"); + HBufC* searchCond = HBufC::NewLC(KWhereClause().Length()+ + KCommAddrType().Length() + + KCommAddrValue().Length() + findText->Length()); + searchCond->Des().Format(KWhereClause, + &KCommAddrType, CPplCommAddrTable::EEmailAddress, + &KCommAddrValue, findText); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + cntSelectStatement->SetParamL(KContactId, KSpace); + cntSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), cntSelectStatement->SqlStringL())); + + while(PerformIdFindIterationL(idsFound, selectStatement)) + { + // Keep performing find iteration until EFalse is returned. + } + + CleanupStack::PopAndDestroy(3, cntSelectStatement); //selectStatement, searchCond, cntSelectStatement + } + + // Search through the sip address fields + if (findFlags & (EFindInAllFields | EFindInSIPField)) + { + TCntSqlStatementType statementType(ESelect, KSqlContactCommAddrTableName); + CCntSqlStatement* cntSelectStatement = TSqlProvider::GetSqlStatementL(statementType); + CleanupStack::PushL(cntSelectStatement); + + _LIT(KWhereClause, "(%S = %d) AND (%S LIKE '%S')"); + HBufC* searchCond = HBufC::NewLC(KWhereClause().Length()+ + KCommAddrType().Length() + + KCommAddrValue().Length() + findText->Length()); + searchCond->Des().Format(KWhereClause, + &KCommAddrType, CPplCommAddrTable::ESipAddress, + &KCommAddrValue, findText); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + cntSelectStatement->SetParamL(KContactId, KSpace); + cntSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), cntSelectStatement->SqlStringL())); + + while(PerformIdFindIterationL(idsFound, selectStatement)) + { + // Keep performing find iteration until EFalse is returned. + } + + CleanupStack::PopAndDestroy(3, cntSelectStatement); //selectStatement, searchCond, cntSelectStatement + } + + // Search through the searchable text for any other fields. + if (findFlags & EFindInAllFields) + { + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + iSelectStatement->SetParamL(KContactTextFieldHeader, KSpace); + iSelectStatement->SetParamL(KContactTextFields, KSpace); + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + while(FindL(idsFound,*findText,aFieldDef, selectStatement, aSessionId)) + { + // Keep performing find iteration until EFalse is returned. + } + + CleanupStack::PopAndDestroy(&selectStatement); + } + + CleanupStack::PopAndDestroy(findText); + CleanupStack::Pop(idsFound); + + return(idsFound); + } + + +/** +Get a collection of contact IDs which match the given filter. + +@param aFilter Filter to match. + +@return Contact ID array of those contacts which match the given filter. +*/ +CContactIdArray* CPlCollection::FilterDatabaseL(CCntFilter& aFilter) + { + _LIT(KWhereCondition,"%S >= %LD"); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + + if (aFilter.IncludeModifiedContacts() || aFilter.IncludeDeletedContacts()) + { + // Create query to search for modified/deleted contacts. + HBufC* searchCond=HBufC::NewLC(KWhereCondition().Length() + + KContactLastModified().Length() + 64); + + TPtr ptr=searchCond->Des(); + ptr.Format(KWhereCondition, &KContactLastModified, aFilter.GetFilterDateTime().Int64()); + + iSelectStatement->SetConditionL(*searchCond); + + CleanupStack::PopAndDestroy(searchCond); + } + else if (aFilter.IncludeNewContacts()) + { + // Create query to search for inserted contacts. + HBufC* searchCond=HBufC::NewLC(KWhereCondition().Length() + + KContactCreationDate().Length() + 64); + + TPtr ptr=searchCond->Des(); + ptr.Format(KWhereCondition, &KContactCreationDate, aFilter.GetFilterDateTime().Int64()); + + iSelectStatement->SetConditionL(*searchCond); + + CleanupStack::PopAndDestroy(searchCond); + } + + RSqlStatement selectStatement; + CleanupClosePushL(selectStatement); + + const TInt idIndex = iSelectStatement->ParameterIndex(KContactId); + const TInt typeFlagsIndex = iSelectStatement->ParameterIndex(KContactTypeFlags); + + TBool filterIncludesAllContacts = !(aFilter.IncludeDeletedContacts() || + aFilter.IncludeNewContacts() || aFilter.IncludeModifiedContacts()); + + CContactIdArray* idArray = CContactIdArray::NewL(); + CleanupStack::PushL(idArray); + + User::LeaveIfError(selectStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + + TInt err; + while((err = selectStatement.Next()) == KSqlAtRow) + { + TInt typeFlags = selectStatement.ColumnInt(typeFlagsIndex); + + TUid type = TCntPersistenceUtility::TypeFlagsToContactTypeUid(typeFlags); + TUint32 attribs = (typeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift; + + if (((aFilter.IncludeDeletedContacts() && (attribs & CContactItem::EDeleted)) + || (aFilter.IncludeNewContacts() + || aFilter.IncludeModifiedContacts()) && (aFilter.TestContactFilterType(type))) + || (filterIncludesAllContacts && aFilter.TestContactFilterType(type))) + { + TContactItemId id = selectStatement.ColumnInt(idIndex); + idArray->AddL(id); + } + } + + if(err != KSqlAtEnd) + { + User::LeaveIfError(err); + } + + CleanupStack::Pop(idArray); + CleanupStack::PopAndDestroy(&selectStatement); + + return idArray; + } + + +/** +Perform a find interation based on given sql statement for the given text. + +@param aIdsFound On return the contact IDs found. +@param aText Text to find. +@param aStatement sql statement +@param aFieldsToSearch Fields within the view to search. +@param aSessionId Session ID (only relevant for ICC contacts). + +@return ETrue if further iterations are required (i.e. view has not been fully +evaluated and searched), EFalse otherwise. +*/ +TBool CPlCollection::PerformFindIterationL(CContactIdArray* aIdsFound,const TDesC& aText,RSqlStatement aStatement, TInt aFieldsToSearch, TUint aSessionId) + { + TContactItemId contactId; + + const TInt idIndex = aStatement.ColumnIndex(KContactId); + User::LeaveIfError(idIndex); + const TInt typeFlagsIndex = aStatement.ColumnIndex(KContactTypeFlags); + User::LeaveIfError(typeFlagsIndex); + TInt err; + + const TInt firstNameIndex = aStatement.ColumnIndex(KContactFirstName); + User::LeaveIfError(firstNameIndex); + const TInt lastNameIndex = aStatement.ColumnIndex(KContactLastName); + User::LeaveIfError(lastNameIndex); + const TInt companyNameIndex = aStatement.ColumnIndex(KContactCompanyName); + User::LeaveIfError(companyNameIndex); + const TInt firstNamePrnIndex = aStatement.ColumnIndex(KContactFirstNamePrn); + User::LeaveIfError(firstNamePrnIndex); + const TInt lastNamePrnIndex = aStatement.ColumnIndex(KContactLastNamePrn); + User::LeaveIfError(lastNamePrnIndex); + const TInt companyNamePrnIndex = aStatement.ColumnIndex(KContactCompanyNamePrn); + User::LeaveIfError(companyNamePrnIndex); + + while((err = aStatement.Next()) == KSqlAtRow) + { + // Do not include contact if it is not of the desired type. + TInt typeFlags = aStatement.ColumnInt(typeFlagsIndex); + TUid contactType = TCntPersistenceUtility::TypeFlagsToContactTypeUid(typeFlags); + + if(iContactsFile.ContactProperties().CheckType(contactType) == EFalse) + { + continue; + } + + contactId = aStatement.ColumnInt(idIndex); + + // Check that the contact is not already present in the given contact + // IDs before searching for the given text. + if(aIdsFound->Find(contactId)==KErrNotFound) + { + TBool findTextInFields = ((aFieldsToSearch & EFindInAllFields) || (aFieldsToSearch & EFindFirstName)) ? (aStatement.ColumnTextL(firstNameIndex).FindC(aText.Ptr(), aText.Length(), KCollationLevel) != KErrNotFound ) : EFalse; + findTextInFields = findTextInFields || (((aFieldsToSearch & EFindInAllFields) || (aFieldsToSearch & EFindLastName)) ? (aStatement.ColumnTextL(lastNameIndex).FindC(aText.Ptr(), aText.Length(), KCollationLevel) != KErrNotFound ) : EFalse); + findTextInFields = findTextInFields || (((aFieldsToSearch & EFindInAllFields) || (aFieldsToSearch & EFindCompanyName)) ? (aStatement.ColumnTextL(companyNameIndex).FindC(aText.Ptr(), aText.Length(), KCollationLevel) != KErrNotFound ) : EFalse); + findTextInFields = findTextInFields || (((aFieldsToSearch & EFindInAllFields) || (aFieldsToSearch & EFindFirstNamePronunciation)) ? (aStatement.ColumnTextL(firstNamePrnIndex).FindC(aText.Ptr(), aText.Length(), KCollationLevel) != KErrNotFound ) :EFalse); + findTextInFields = findTextInFields || (((aFieldsToSearch & EFindInAllFields) || (aFieldsToSearch & EFindLastNamePronunciation)) ? (aStatement.ColumnTextL(lastNamePrnIndex).FindC(aText.Ptr(), aText.Length(), KCollationLevel) != KErrNotFound ) :EFalse); + findTextInFields = findTextInFields || (((aFieldsToSearch & EFindInAllFields) || (aFieldsToSearch & EFindCompanyNamePronunciation)) ? (aStatement.ColumnTextL(companyNamePrnIndex).FindC(aText.Ptr(), aText.Length(), KCollationLevel) != KErrNotFound ) : EFalse); + + if(findTextInFields) + { + if (contactType == KUidContactICCEntry) + { + TInt error(iContactsFile.ContactProperties().ContactSynchroniserL(aSessionId).ValidateContact(MContactSynchroniser::ESearch,contactId)); + User::LeaveIfError(error); + } + aIdsFound->AddL(contactId); + } + } + } + User::LeaveIfError(err); + + return (EFalse); + } + +/** +Perform a find interation based on the given sql statement for the given text. + +@param aIdsFound On return the contact IDs found. +@param aStatement sql statement + +@return ETrue if further iterations are required (i.e. view has not been fully +evaluated and searched), EFalse otherwise. +*/ +TBool CPlCollection::PerformIdFindIterationL(CContactIdArray *aIdsFound, RSqlStatement aStatement) + { + const TInt idIndex = aStatement.ColumnIndex(KContactId); + User::LeaveIfError(idIndex); + TInt err; + while((err = aStatement.Next()) == KSqlAtRow) + { + TInt contactId = aStatement.ColumnInt(idIndex); + + // Check that the contact is not already present in the given contact + // IDs before adding it to the array of contact IDs. + if(aIdsFound->Find(contactId) == KErrNotFound) + { + aIdsFound->AddL(contactId); + } + } + User::LeaveIfError(err); + + return (EFalse); + } + +/** +Utility method used to build the search string +*/ +void AppendSearchStringSegment(TDes16& aOrderFields,const TDesC& aColName, + const TPtrC aTxtPtr, TBool aAppendOr) + { + aOrderFields.Append(KLeftBracket); + aOrderFields.Append(aColName); + aOrderFields.Append(KLikeString); + aOrderFields.Append(aTxtPtr); + aOrderFields.Append(KLikeStringRight); + if(aAppendOr) + { + aOrderFields.Append(KOR); + } + } + +/** +Utility method used to build search string +*/ +void ConstructSearchString(TInt aFieldDef,TInt aNoFields,TDes16& aOrderFields,TPtrC aTxtPtr) + { + const void* KColNamesFields[] = + { + &KContactFirstName, &KContactLastName, &KContactCompanyName, + &KContactFirstNamePrn, &KContactLastNamePrn, + &KContactLastNamePrn + }; + + for(TInt ii=0;iiLength() + + KContactLastName().Length() + findText->Length() + + KContactCompanyName().Length() + findText->Length() + + KContactFirstNamePrn().Length() + findText->Length() + + KContactLastNamePrn().Length() + findText->Length() + + KContactCompanyNamePrn().Length() + findText->Length()); + searchCond->Des().Format(KWhereClause, &KContactFirstName, findText, + &KContactLastName, findText, &KContactCompanyName, findText, + &KContactFirstNamePrn, findText, &KContactLastNamePrn, findText, + &KContactCompanyNamePrn, findText); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + iSelectStatement->SetParamL(KContactFirstName, KSpace); + iSelectStatement->SetParamL(KContactLastName, KSpace); + iSelectStatement->SetParamL(KContactCompanyName, KSpace); + iSelectStatement->SetParamL(KContactFirstNamePrn, KSpace); + iSelectStatement->SetParamL(KContactLastNamePrn, KSpace); + iSelectStatement->SetParamL(KContactCompanyNamePrn, KSpace); + iSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectIdentityStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + CleanupStack::PopAndDestroy(searchCond); + } + //end construction for statement for searching in identity fields + + //construct statement for search email + if (iFindFlags & (EFindInAllFields | EFindInEmailField)) + { + TCntSqlStatementType statementType(ESelect, KSqlContactCommAddrTableName); + CCntSqlStatement* cntSelectStatement = TSqlProvider::GetSqlStatementL(statementType); + CleanupStack::PushL(cntSelectStatement); + + HBufC* searchCond = HBufC::NewLC(KWhereEmailSIPClause().Length()+ + KCommAddrType().Length() + + KCommAddrValue().Length() + findText->Length()); + searchCond->Des().Format(KWhereEmailSIPClause, + &KCommAddrType, CPplCommAddrTable::EEmailAddress, + &KCommAddrValue, findText); + + cntSelectStatement->SetParamL(KContactId, KSpace); + cntSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectEmailStatement.Prepare(iContactsFile.NamedDatabase(), cntSelectStatement->SqlStringL())); + CleanupStack::PopAndDestroy(2, cntSelectStatement); // cntSelectStatement, searchCond + } + //end construction for statement for search email + + //construct statement for search sip values + if (iFindFlags & (EFindInAllFields | EFindInSIPField)) + { + TCntSqlStatementType statementType(ESelect, KSqlContactCommAddrTableName); + CCntSqlStatement* cntSelectStatement = TSqlProvider::GetSqlStatementL(statementType); + CleanupStack::PushL(cntSelectStatement); + + HBufC* searchCond = HBufC::NewLC(KWhereEmailSIPClause().Length()+ + KCommAddrType().Length() + + KCommAddrValue().Length() + findText->Length()); + searchCond->Des().Format(KWhereEmailSIPClause, + &KCommAddrType, CPplCommAddrTable::ESipAddress, + &KCommAddrValue, findText); + + cntSelectStatement->SetParamL(KContactId, KSpace); + cntSelectStatement->SetConditionL(*searchCond); + + User::LeaveIfError(selectSIPStatement.Prepare(iContactsFile.NamedDatabase(), cntSelectStatement->SqlStringL())); + CleanupStack::PopAndDestroy(2, cntSelectStatement); // cntSelectStatement, searchCond + } + //end construction for statement for search sip values + + //construct statement for search in blob + if (iFindFlags & EFindInAllFields) + { + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + iSelectStatement->SetParamL(KContactTextFieldHeader, KSpace); + iSelectStatement->SetParamL(KContactTextFields, KSpace); + + User::LeaveIfError(selectBlobStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + } + //end construction for statement for search in blob + + CleanupStack::PopAndDestroy(findText); + } + + +/** +Add the required fields and search text for SQL query. Used for asynchronous +find which uses a text definition. + +@see CPlCollection::FindAsyncTextDefInitL() + +@param aOrderFields On return contains the required fields and search text for +SQL query. +*/ +void CPlCollection::doAppendFieldsToSearchString(HBufC* aOrderFields) const + { + TInt noOfFindWords = iFindWords2->Count(); + TPtr16 orderFieldPtr = aOrderFields->Des(); + for(TInt searchWord = 0;searchWord < noOfFindWords; ++searchWord) + { + ConstructSearchString(iFindFlags,iNoIdentitySearchColumns,orderFieldPtr,(*iFindWords2)[searchWord]); + if ((searchWord+1) < noOfFindWords) + { + orderFieldPtr.Append(KOR); + } + } + } + + +/** +Initialise the Persistence Layer collection class ready for iterative calls to +the FindAsyncL() method. This form of initialisation is for an asynchronous +find which uses a text definition and an array of "find words". + +@param aWords "Find words" array. +@param aTextDef Text definition to use in find. +*/ +void CPlCollection::FindAsyncTextDefInitL(const CDesCArray& aWords,CContactTextDef* aTextDef) + { + // Persistence Layer CPlCollection is not deleted but Reset()'s the member + // variables for iterative FindAsyncL() calls. + Reset(); + + iFindWords = new(ELeave) CDesCArrayFlat(5); + iFindWords2 = new(ELeave) CDesCArrayFlat(5); + for(TInt loop = 0;loop < aWords.MdcaCount();++loop) + { + // Construct iFindWords2 which contains all the strings in the search + // surrounded by a %. + TPtrC findWord(aWords.MdcaPoint(loop)); + TKeyCmpTextLength key; + HBufC* bufPtr = findWord.AllocLC(); + iFindWords->CArrayFixBase::InsertIsqAllowDuplicatesL(&bufPtr,key); + CleanupStack::Pop(); // bufPtr + HBufC *findText = CreateFindTextL(findWord); + CleanupStack::PushL(findText); + iFindWords2->AppendL(*findText); + CleanupStack::PopAndDestroy(); // findText + } + + iTextDef = aTextDef; + ConstructBitwiseFlagsFromTextDef(iFindFlags,iNoIdentitySearchColumns,iTextDef); + + iSelectStatement->Reset(); + iSelectStatement->SetParamL(KContactId, KSpace); + iSelectStatement->SetParamL(KContactTypeFlags, KSpace); + iSelectStatement->SetParamL(KContactFirstName, KSpace); + iSelectStatement->SetParamL(KContactLastName, KSpace); + iSelectStatement->SetParamL(KContactCompanyName, KSpace); + iSelectStatement->SetParamL(KContactFirstNamePrn, KSpace); + iSelectStatement->SetParamL(KContactLastNamePrn, KSpace); + iSelectStatement->SetParamL(KContactCompanyNamePrn, KSpace); + + if(UsesIdentityFieldsOnly(iFindFlags)) + { + TInt querySize = ApproximateSizeOfSearchString(); + HBufC* searchCond = HBufC::NewLC(querySize); + doAppendFieldsToSearchString(searchCond); + iSelectStatement->SetConditionL(*searchCond); + CleanupStack::PopAndDestroy(searchCond); + } + + User::LeaveIfError(selectIdFromIdentityStatement.Prepare(iContactsFile.NamedDatabase(), iSelectStatement->SqlStringL())); + } + + +/** +This method does not do any searching. It simply extracts the contact IDs from +the view and (ultimately) returns them to the CIdleFinder object on the client +side. + +The client can then call CIdleFinder::CheckFindL() to perform the actual search. +Searching cannot be performed server-side because of the need to call a client +supplied callback method (the "find words parser" callback). + +@param aIdArray Contact IDs extracted from the view. +@param aSessionId Session ID (only relevant for ICC contacts). + +@return ETrue if further iterations are required (i.e. view has not been fully +evaluated), EFalse otherwise. +*/ +TBool CPlCollection::GetContactIdsForTextDefFindL(CContactIdArray* aIdArray, TUint aSessionId) + { + TInt iterCount = 0; + TInt err; + + const TInt idIndex = selectIdFromIdentityStatement.ColumnIndex(KContactId); + User::LeaveIfError(idIndex); + const TInt typeFlagsIndex = selectIdFromIdentityStatement.ColumnIndex(KContactTypeFlags); + User::LeaveIfError(typeFlagsIndex); + + while(iterCountFind(contactId) == KErrNotFound) + { + aIdArray->AddL(contactId); + } + } + + return(iterCount == KFindIterations); + } + + +/** +Perform an asynchronous find iteration. + +@param aMoreToGo On return ETrue if further iterations are required to complete +the asynchronous find, EFalse otherwise. +@param aSessionId Session ID (only relevant for ICC contacts). + +@return Array of contact ID found in this find iteration. +*/ +CContactIdArray* CPlCollection::FindAsyncL(TBool& aMoreToGo, TUint aSessionId) + { + CContactIdArray* idArray = CContactIdArray::NewLC(); + TBool moreToGo = EFalse; + + if (iFindWords) // Text definition and "find words". + { + TRAPD(err, moreToGo=GetContactIdsForTextDefFindL(idArray, aSessionId)); + + if (err != KErrNone) + { + moreToGo = EFalse; + Reset(); + User::Leave(err); + } + + if(moreToGo == EFalse) + { + iFindState |= EFindInTextDefFinished; + } + } + else // Search for a single string. + { + if(!(iFindState & EFindInBlobFinished) && (iFindFlags & EFindInAllFields)) + { + // Search in searchable text (contents of BLOB). + if(!FindL(idArray, *iText, iFieldDef, selectBlobStatement, aSessionId)) + { + iFindState |= EFindInBlobFinished; + } + else + { + moreToGo = ETrue; + } + } + + if(!(iFindState & EFindInIdentityFinished)) + { + if(SearchIdentityFieldsRequired(iFindFlags)) + { + // Search in identity fields. + if(!PerformFindIterationL(idArray, *iOriginalText, selectIdentityStatement, iFindFlags, aSessionId)) + { + iFindState |= EFindInIdentityFinished; + } + else + { + moreToGo = ETrue; + } + } + } + + if(!(iFindState & EFindInEmailFinished)) + { + if(iFindFlags & (EFindInAllFields | EFindInEmailField)) + { + // Search in email fields. + if(!PerformIdFindIterationL(idArray, selectEmailStatement)) + { + iFindState |= EFindInEmailFinished; + } + else + { + moreToGo = ETrue; + } + } + } + + if(!(iFindState & EFindInSIPFinished)) + { + if(iFindFlags & (EFindInAllFields | EFindInSIPField)) + { + // Search in email fields. + if(!PerformIdFindIterationL(idArray, selectSIPStatement)) + { + iFindState |= EFindInSIPFinished; + } + else + { + moreToGo = ETrue; + } + } + } + } + + + if(!moreToGo) + { + Reset(); + } + +/* + // Check if we have finished searching. + if ((iFindState&(EFindInBlobFinished|EFindInIdentityFinished|EFindInEmailFinished)) == (EFindInBlobFinished|EFindInIdentityFinished|EFindInEmailFinished) + || iFindState&EFindInTextDefFinished) + { + moreToGo = EFalse; + Reset(); + } +*/ + aMoreToGo = moreToGo; + + // Pop this off the cleanup stack, as we are passing on ownership to the calling method + CleanupStack::Pop(idArray); + + return idArray; + } + + +/** +Perform a synchronous find for the given text and field definition. + +@param aIdsFound Contacts IDs found. +@param aText Text to search for. +@param aFieldDef Field definition to use. +@param aSessionId Session ID (only relevant for ICC contacts). + +@return ETrue if all iterations have been completed, EFalse otherwise. +*/ +TBool CPlCollection::FindL(CContactIdArray *aIdsFound, const TDesC& aText,const CContactItemFieldDef *aFieldDef, RSqlStatement aStatement, TUint aSessionId) + { + const CContactTemplate& systemTemplate = iContactsFile.ContactProperties().SystemTemplateManager().TemplateL(); + + CContactItemField* field = CContactItemField::NewLC(); // construct just once + + TInt endOfQueryString = aText.Length()-2; + TPtrC originalFindText = aText.Mid(1,endOfQueryString); + + TInt iterCount = 0; + TInt err; + + const TInt idIndex = aStatement.ColumnIndex(KContactId); + User::LeaveIfError(idIndex); + const TInt typeFlagsIndex = aStatement.ColumnIndex(KContactTypeFlags); + User::LeaveIfError(typeFlagsIndex); + const TInt textHeaderIndex = aStatement.ColumnIndex(KContactTextFieldHeader); + User::LeaveIfError(textHeaderIndex); + const TInt textFieldsIndex = aStatement.ColumnIndex(KContactTextFields); + User::LeaveIfError(textFieldsIndex); + + while(iterCountDes().Copy(textFieldPtrC); + + RStoreReadStream rootStream; + rootStream.OpenLC(*store ,store->Root()); + + TCardinality count; + rootStream >> count; + + for (TInt i = 0;i < count; ++i) + { + field->Reset(); + if (field->RestoreIfMatchL(rootStream,aFieldDef,&systemTemplate.CardFields(),textBuf,i)) + { + if (field->StorageType() == KStorageTypeText && + field->TextStorage()->Text().FindC(originalFindText.Ptr(), + originalFindText.Length(), KCollationLevel) != KErrNotFound) + { + TContactItemId id = aStatement.ColumnInt(idIndex); + if(aIdsFound->Find(id) == KErrNotFound) + { + if (contactType == KUidContactICCEntry) + { + TInt error(iContactsFile.ContactProperties().ContactSynchroniserL(aSessionId).ValidateContact(MContactSynchroniser::ESearch,id)); + User::LeaveIfError(error); + } + aIdsFound->AddL(id); + } + break; + } + } + } + CleanupStack::PopAndDestroy(4, &textHeaderStream); // rootStream, textBuf, textHeader, textHeaderStream + } + ++iterCount; + } + + CleanupStack::PopAndDestroy(field); + + return(iterCount == KFindIterations); + } + +/** +Return an over-estimated string size for the amount of syntax required per field +per search string for the Identity table. + +@return Maximum string size required for Identity table search syntax. +*/ +TInt CPlCollection::MaximumSizeOfIdentitySearchSyntax() + { + TInt sqlLitSize; + + sqlLitSize = KLeftBracket().Length() + KLikeString().Length() + + KLikeStringRight().Length() + KOR().Length(); + + sqlLitSize *= iNoIdentitySearchColumns; + + sqlLitSize += KContactFirstName().Length() + KContactLastName().Length() + + KContactCompanyName().Length(); + + sqlLitSize += KContactFirstNamePrn().Length() + + KContactLastNamePrn().Length() + + KContactCompanyNamePrn().Length(); + + return(sqlLitSize); + } + +/** +Determine the approximate size of the string required to construct the SQL query +on the Identity table when using the "find words". + +@return Approximate size of search string. +*/ +TInt CPlCollection::ApproximateSizeOfSearchString() + { + TInt sqlLitSize = MaximumSizeOfIdentitySearchSyntax(); + + TInt noOfFindWords=iFindWords2->Count(); + TInt size=0; + + for(TInt searchWord=0;searchWord < noOfFindWords; ++searchWord) + { + size += (*iFindWords2)[searchWord].Length(); + } + + TInt totalSize = noOfFindWords*sqlLitSize + iNoIdentitySearchColumns*size + 256; + + return totalSize; + } + +/** +Utility method used to filter hint fields +*/ +TBool CPlCollection::HintFieldMatchesFilter(TInt aHintField, TInt aFilter) + { + TBool match=EFalse; + + if (aFilter==CContactDatabase::EUnfiltered) + { + return ETrue; + } + + if (aFilter & ~aHintField & (CContactDatabase::EWork | CContactDatabase::EHome)) + { + return EFalse; + } + + match = aFilter & aHintField + & ( + CContactDatabase::ELandLine + | CContactDatabase::ESmsable + | CContactDatabase::EMailable + | CContactDatabase::EFaxable + | CContactDatabase::EPhonable + | CContactDatabase::ERingTone + | CContactDatabase::EVoiceDial + | CContactDatabase::EIMAddress + | CContactDatabase::EWirelessVillage + | CContactDatabase::ECustomFilter1 + | CContactDatabase::ECustomFilter2 + | CContactDatabase::ECustomFilter3 + | CContactDatabase::ECustomFilter4 + ); + + return match; + }