// 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:
//
#include "pltables.h"
#include "dbsqlconstants.h"
#include "cntpersistenceutility.h"
#include <cntdef.h>
// forward declaration to allow this to compile.
// Which header file is this declared in and do we actually still need the properties here?
class CLplContactProperties;
/**
NewL
@param aDatabase A handle to the database.
@param aProperties A contact properties object.
@return A pointer to a new CPplContactTable object.
*/
CPplContactTable* CPplContactTable::NewL(RSqlDatabase& aDatabase, CLplContactProperties& aProperties)
{
CPplContactTable* self = CPplContactTable::NewLC(aDatabase, aProperties);
CleanupStack::Pop(self);
return self;
}
/**
NewLC
@param aDatabase A handle to the database.
@param aProperties A contact properties object.
@return A pointer to a new CPplContactTable object.
*/
CPplContactTable* CPplContactTable::NewLC(RSqlDatabase& aDatabase, CLplContactProperties& aProperties)
{
CPplContactTable* self = new (ELeave) CPplContactTable(aDatabase, aProperties);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
/**
Set up the CCntSqlStatement objects held by the class.
*/
void CPplContactTable::ConstructL()
{
_LIT(KOwnCardInvariant, "((((%S>>%d==%d)*%d) | ((NOT(%S>>%d==%d))*%S))<<%d)| %S");
iCardTemplateIds = CContactIdArray::NewL();
// Statement types
TCntSqlStatementType insertType(EInsert, KSqlContactTableName);
TCntSqlStatementType selectType(ESelect, KSqlContactTableName);
TCntSqlStatementType updateType(EUpdate, KSqlContactTableName);
TCntSqlStatementType deleteType(EDelete, KSqlContactTableName);
// Where clause
// sizes of the clause
const TInt KWhereClauseBufSize(KContactId().Size() +
KWhereStringEqualsStringFormatText().Size() + KContactIdParam().Size() );
// for WHERE contact_id = [contact id value]
HBufC* whereIdClause = HBufC::NewLC(KWhereClauseBufSize);
whereIdClause->Des().AppendFormat(KWhereStringEqualsStringFormatText, &KContactId,
&KContactIdParam );
// INSERT
// insert record into contact table
// For a statement in the following format:
// INSERT INTO contact
// (contact_id, template_id...[etc...], binary_fields)
// VALUES (NULL, 37, .......);
//
iInsertStmnt = TSqlProvider::GetSqlStatementL(insertType);
iInsertStmnt->SetParamL(KContactId, KContactIdParam);//KNullText() );
iInsertStmnt->SetParamL(KContactTemplateId, KContactTemplateIdParam);
iInsertStmnt->SetParamL(KContactTypeFlags, KContactTypeFlagsParam);
iInsertStmnt->SetParamL(KContactAccessCount, KContactAccessCountParam);
iInsertStmnt->SetParamL(KContactCreationDate, KContactCreationDateParam);
iInsertStmnt->SetParamL(KContactLastModified, KContactLastModifiedParam);
iInsertStmnt->SetParamL(KContactGuidString, KContactGuidStringParam);
iInsertStmnt->SetParamL(KContactFirstName, KContactFirstNameParam);
iInsertStmnt->SetParamL(KContactLastName, KContactLastNameParam);
iInsertStmnt->SetParamL(KContactCompanyName, KContactCompanyNameParam);
iInsertStmnt->SetParamL(KContactFirstNamePrn, KContactFirstNamePrnParam);
iInsertStmnt->SetParamL(KContactLastNamePrn, KContactLastNamePrnParam);
iInsertStmnt->SetParamL(KContactCompanyNamePrn, KContactCompanyNamePrnParam);
iInsertStmnt->SetParamL(KContactTextFieldHeader, KContactTextFieldHeaderParam);
iInsertStmnt->SetParamL(KContactBinaryFieldHeader, KContactBinaryFieldHeaderParam);
iInsertStmnt->SetParamL(KContactTextFields, KContactTextFieldsParam);
iInsertStmnt->SetParamL(KContactBinaryFields, KContactBinaryFieldsParam);
// SELECT
// delete select
// For a statement in the following format:
// SELECT type_flags, access_count, template_id FROM contact
// WHERE contact_id = [contact id value];
//
iDeleteSelectStmnt = TSqlProvider::GetSqlStatementL(selectType);
iDeleteSelectStmnt->SetParamL(KContactTypeFlags, KNullDesC() );
iDeleteSelectStmnt->SetParamL(KContactAccessCount, KNullDesC() );
iDeleteSelectStmnt->SetParamL(KContactTemplateId, KNullDesC() );
iDeleteSelectStmnt->SetConditionL(*whereIdClause);
// UPDATE
// update record from contact table
// For a statement in the following format:
// UPDATE contact SET template_id = [new template id value]
// ...,
// binary_fields = [new binary fields value]
// WHERE contact_id = [contact id value];
//
iUpdateStmnt = TSqlProvider::GetSqlStatementL(updateType);
iUpdateStmnt->SetParamL(KContactTemplateId, KContactTemplateIdParam);
HBufC* typeFlagsParameter = HBufC::NewLC(KOwnCardInvariant().Size() + KContactTypeFlags().Size() + KContactTypeFlags().Size() +
KContactTypeParam().Size() + KAttributesAndHintParam().Size() + 6*sizeof(TInt));
typeFlagsParameter->Des().AppendFormat(KOwnCardInvariant, &KContactTypeFlags, EContactType_Shift, EContactTypeFlags_OwnCard, EContactTypeFlags_OwnCard,
&KContactTypeFlags, EContactType_Shift, EContactTypeFlags_OwnCard, &KContactTypeParam, EContactType_Shift, &KAttributesAndHintParam);
iUpdateStmnt->SetParamL(KContactTypeFlags, *typeFlagsParameter);
iUpdateStmnt->SetParamL(KContactAccessCount, KContactAccessCountParam);
iUpdateStmnt->SetParamL(KContactLastModified, KContactLastModifiedParam);
iUpdateStmnt->SetParamL(KContactGuidString, KContactGuidStringParam);
iUpdateStmnt->SetParamL(KContactFirstName, KContactFirstNameParam);
iUpdateStmnt->SetParamL(KContactLastName, KContactLastNameParam);
iUpdateStmnt->SetParamL(KContactCompanyName, KContactCompanyNameParam);
iUpdateStmnt->SetParamL(KContactFirstNamePrn, KContactFirstNamePrnParam);
iUpdateStmnt->SetParamL(KContactLastNamePrn, KContactLastNamePrnParam);
iUpdateStmnt->SetParamL(KContactCompanyNamePrn, KContactCompanyNamePrnParam);
iUpdateStmnt->SetParamL(KContactTextFieldHeader, KContactTextFieldHeaderParam);
iUpdateStmnt->SetParamL(KContactBinaryFieldHeader, KContactBinaryFieldHeaderParam);
iUpdateStmnt->SetParamL(KContactTextFields, KContactTextFieldsParam);
iUpdateStmnt->SetParamL(KContactBinaryFields, KContactBinaryFieldsParam);
iUpdateStmnt->SetConditionL(*whereIdClause);
// type_flags update
// For a statement in the following format:
// UPDATE contact SET type_flags = [new type flags value]
// WHERE contact_id = [contact id value];
//
iUpdateFlagsStmnt = TSqlProvider::GetSqlStatementL(updateType);
iUpdateFlagsStmnt->SetParamL(KContactTypeFlags, KContactTypeFlagsParam);
iUpdateFlagsStmnt->SetConditionL(*whereIdClause);
// AccessCount update
// For a statement in the following format:
// UPDATE contact SET access_count = [new access count value]
// WHERE contact_id = [contact id value];
//
iAccessCountUpdateStmnt = TSqlProvider::GetSqlStatementL(updateType);
iAccessCountUpdateStmnt->SetParamL(KContactAccessCount, KContactAccessCountParam);
iAccessCountUpdateStmnt->SetConditionL(*whereIdClause);
// DELETE
// delete record from contact table
// For a statement in the following format:
// DELETE FROM contact WHERE contact_id = [contact id value];
//
iDeleteStmnt = TSqlProvider::GetSqlStatementL(deleteType);
iDeleteStmnt->SetConditionL(*whereIdClause);
// Set up the field lookup table hash map
iFieldMap.InsertL(KUidContactFieldGivenNameValue, KContactFirstNameParam() );
iFieldMap.InsertL(KUidContactFieldGivenNamePronunciationValue, KContactFirstNamePrnParam() );
iFieldMap.InsertL(KUidContactFieldFamilyNameValue, KContactLastNameParam() );
iFieldMap.InsertL(KUidContactFieldFamilyNamePronunciationValue, KContactLastNamePrnParam() );
iFieldMap.InsertL(KUidContactFieldCompanyNameValue, KContactCompanyNameParam() );
iFieldMap.InsertL(KUidContactFieldCompanyNamePronunciationValue, KContactCompanyNamePrnParam() );
CleanupStack::PopAndDestroy(2, whereIdClause); //whereIdClause, typeFlagsParameter
}
/**
Destructor
Tidy up CCntSqlStatement objects
*/
CPplContactTable::~CPplContactTable()
{
delete iInsertStmnt;
delete iDeleteSelectStmnt;
delete iUpdateFlagsStmnt;
delete iUpdateStmnt;
delete iAccessCountUpdateStmnt;
delete iDeleteStmnt;
iFieldMap.Close();
delete iCardTemplateIds;
}
/**
CPplContactTable constructor
*/
CPplContactTable::CPplContactTable(RSqlDatabase& aDatabase, CLplContactProperties& aProperties):
iProperties(aProperties),
iDatabase(aDatabase)
{
}
/**
Update the access count for the given template ID. If the template is marked for deletion and
no one is referencing it any longer, it is deleted.
@param aTemplateRefId
@param aIncrement If ETrue increase the reference count otherwise decrease the reference count.
*/
void CPplContactTable::UpdateTemplateAccessCounterL(TContactItemId aTemplateRefId, TBool aIncrement)
{
if (aTemplateRefId == KGoldenTemplateId || aTemplateRefId == KNullContactId)
{
return;
}
// Get the access count and type flags for the template
RSqlStatement selectStmnt;
CleanupClosePushL(selectStmnt);
selectStmnt.PrepareL(iDatabase, iDeleteSelectStmnt->SqlStringL() );
const TInt KContactIdParamIndex(KFirstIndex); // first and only parameter in the query
User::LeaveIfError(selectStmnt.BindInt(KContactIdParamIndex, aTemplateRefId) );
TInt err(selectStmnt.Next() );
if (err != KSqlAtRow)
{
User::Leave(err);
}
TInt oldAccessCount(selectStmnt.ColumnInt(iDeleteSelectStmnt->ParameterIndex(KContactAccessCount() ) ) );
TInt newAccessCount = oldAccessCount + (aIncrement ? 1 : -1);
TInt typeFlags(selectStmnt.ColumnInt(iDeleteSelectStmnt->ParameterIndex(KContactTypeFlags() ) ) );
TInt attributes((typeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift);
CleanupStack::PopAndDestroy(&selectStmnt);
// set the new access count for the template
RSqlStatement updateStmnt;
CleanupClosePushL(updateStmnt);
updateStmnt.PrepareL(iDatabase, iAccessCountUpdateStmnt->SqlStringL() );
const TInt KAccessCountParamIndex(KFirstParam); // first parameter in query...
const TInt KTemplateIdParamIndex(KAccessCountParamIndex + 1); // ...and the second.
User::LeaveIfError(updateStmnt.BindInt(KAccessCountParamIndex, newAccessCount) );
User::LeaveIfError(updateStmnt.BindInt(KTemplateIdParamIndex, aTemplateRefId) );
User::LeaveIfError(updateStmnt.Exec() );
CleanupStack::PopAndDestroy(&updateStmnt);
// should the template be deleted -- i.e. nobody accessing it and marked as deleted?
if (newAccessCount <= 0 && (attributes & CContactItem::EDeleted) )
{
TBool lowDiskErr(EFalse);
CContactItem* templ = DeleteLC(aTemplateRefId, lowDiskErr);
CleanupStack::PopAndDestroy(templ);
if (lowDiskErr)
{
User::Leave(KErrDiskFull);
}
}
}
void CPplContactTable::CreateInDbL(CContactItem& aItem)
{
CreateInDbL(aItem, 0);
}
/**
Add the given contact item to the database.
@param aItem The contact item to be added to the database.
@param aSessionId The ID of the session that issued the request. Used to
prevent Phonebook Synchroniser deadlock.
@return Contact item ID of the contact added to the database.
*/
void CPplContactTable::CreateInDbL(CContactItem& aItem, TUint aSessionId)
{
if (aItem.Type() != KUidContactTemplate) // Not creating system template?
{
// Make sure the System template is loaded.
iProperties.SystemTemplateL();
}
if (aItem.Type() == KUidContactICCEntry)
{
TInt ret = iProperties.ContactSynchroniserL(aSessionId).ValidateWriteContact(static_cast<CContactICCEntry&>(aItem) );
User::LeaveIfError(ret);
}
// Mark fields as template fields if the contact item is a contact card template.
const TBool KIsTemplateCard = (aItem.Type() == KUidContactCardTemplate);
CContactItemFieldSet& fieldset = aItem.CardFields();
for (TInt i = fieldset.Count() - 1; i >= 0; --i)
{
fieldset[i].SetTemplateField(KIsTemplateCard);
}
// Set iLastModified and iCreationDate to current time.
TTime lastModified = aItem.LastModified();
lastModified.UniversalTime();
aItem.SetLastModified(lastModified);
aItem.SetCreationDate(lastModified);
// ID of newly created item obtained from autoincrementing ID column.
// -- get an id from the database
RSqlStatement selectIdStatement;
CleanupClosePushL(selectIdStatement);
User::LeaveIfError(selectIdStatement.Prepare(iDatabase, KSelectLastIdSqlStmnt()));
TInt err;
TInt lastId((-1));
if((err = selectIdStatement.Next()) == KSqlAtRow)
{
lastId = selectIdStatement.ColumnInt(0);
}
if(err == KSqlAtEnd)
{
lastId = -1;
}
aItem.SetId(lastId + 1);
CleanupStack::PopAndDestroy(&selectIdStatement);
// Increment Template reference counter.
UpdateTemplateAccessCounterL(aItem.TemplateRefId(), ETrue);
// Write contact data to the database.
WriteContactItemL(aItem, EInsert);
}
/**
Updates the given contact item in the database.
@param aItem The contact item to be updated
*/
void CPplContactTable::UpdateL(const CContactItem& aItem)
{
// If contact is marked as deleted and access count is zero then delete the
// contact otherwise update it.
if (aItem.IsDeleted() && (aItem.AccessCount() == 0) )
{
//update access field in contact table
RSqlStatement updateStmnt;
CleanupClosePushL(updateStmnt);
updateStmnt.PrepareL(iDatabase, iAccessCountUpdateStmnt->SqlStringL() );
const TInt KAccessCountParamIndex(KFirstParam); // first parameter in query...
const TInt KContactIdParamIndex(KAccessCountParamIndex + 1); // ...and the second.
User::LeaveIfError(updateStmnt.BindInt(KAccessCountParamIndex, 0) );
User::LeaveIfError(updateStmnt.BindInt(KContactIdParamIndex, aItem.Id()) );
User::LeaveIfError(updateStmnt.Exec() );
CleanupStack::PopAndDestroy(&updateStmnt);
TBool lowDiskErr(EFalse);
CContactItem* templ = DeleteLC(aItem.Id(), lowDiskErr);
CleanupStack::PopAndDestroy(templ);
if (lowDiskErr)
{
User::Leave(KErrDiskFull);
}
}
else
{
WriteContactItemL(aItem, EUpdate);
}
}
/**
Performs database write operations for the given contact item.
@param aItem the contact item to be written
@param aType the type of operation (insert or update) to
*/
void CPplContactTable::WriteContactItemL(const CContactItem& aItem, TCntSqlStatement aType)
{
// check the correct type of statement/operation has been supplied
if (aType != EInsert && aType != EUpdate)
{
User::Leave(KErrArgument);
}
// temporary reference to the CCntSqlStatements member variables to take advantage
// of the commonality between update and insert operations.
CCntSqlStatement* tempCntStmnt = iUpdateStmnt;
if (aType == EInsert)
{
tempCntStmnt = iInsertStmnt;
}
RSqlStatement stmnt;
CleanupClosePushL(stmnt);
stmnt.PrepareL(iDatabase, tempCntStmnt->SqlStringL() );
// get the identity-type fields and build the hint fields
THintField hint;
const RArray<TUid>* custFiltFields = NULL;
if (aItem.Type() != KUidContactTemplate)
{
custFiltFields = &iProperties.CustomFilterableFieldsL(); // doesn't take ownership
}
for (TInt fieldNum = aItem.CardFields().Count() - 1; fieldNum >= 0; --fieldNum)
{
const CContactItemField& field = aItem.CardFields()[fieldNum];
const TInt nameFieldUid = CPplContactTable::NameFieldUid(field);
// check if field should be stored in the Identity table.
if (nameFieldUid != KErrNotFound)
{
TPtrC textToSet = field.TextStorage()->Text();
if(textToSet.Length() > 0)
{
const TInt KParamIndex(User::LeaveIfError(stmnt.ParameterIndex(iFieldMap.FindL(nameFieldUid) ) ) );
User::LeaveIfError(stmnt.BindText(KParamIndex, textToSet));
} }
else if (field.StorageType() == KStorageTypeText && // the field is textual
field.TextStorage()->Text().Length() ) // ignore empty fields
{
// the field is not stored in contact table but potentially maps to a hint
hint.UpdateHintL(field, *custFiltFields);
}
}
// bind other values to statement
if(aType == EInsert)
{
TInt typeFlags(GenerateTypeFlags(aItem.Type(), aItem.Attributes(), hint.iValue) );
User::LeaveIfError(stmnt.BindInt(
User::LeaveIfError(stmnt.ParameterIndex(KContactTypeFlagsParam() ) ), typeFlags) );
}
else
{
User::LeaveIfError(stmnt.BindInt(
User::LeaveIfError(stmnt.ParameterIndex(KContactTypeParam() ) ), TCntPersistenceUtility::ContactTypeUidToTypeFlags(aItem.Type()) >> EContactType_Shift ) );
TInt attrHint = (aItem.Attributes() << EContactAttributes_Shift) & EContactAttributes_Mask;
attrHint |= hint.iValue & EContactHintFlags_Mask;
User::LeaveIfError(stmnt.BindInt(
User::LeaveIfError(stmnt.ParameterIndex(KAttributesAndHintParam() ) ), attrHint ) );
}
User::LeaveIfError(stmnt.BindInt(
User::LeaveIfError(stmnt.ParameterIndex(KContactTemplateIdParam() ) ), aItem.TemplateRefId()) );
User::LeaveIfError(stmnt.BindInt(
User::LeaveIfError(stmnt.ParameterIndex(KContactAccessCountParam() ) ), aItem.AccessCount()) );
if (aType == EInsert)
{
User::LeaveIfError(stmnt.BindInt64(
User::LeaveIfError(stmnt.ParameterIndex(KContactCreationDateParam() ) ), aItem.LastModified().Int64() ) );
}
TTime time;
time.UniversalTime();
User::LeaveIfError(stmnt.BindInt64(
User::LeaveIfError(stmnt.ParameterIndex(KContactLastModifiedParam() ) ), time.Int64() ) );
User::LeaveIfError(stmnt.BindText(
User::LeaveIfError(stmnt.ParameterIndex(KContactGuidStringParam() ) ), const_cast<CContactItem&>(aItem).Guid() ) );
User::LeaveIfError(stmnt.BindInt(
User::LeaveIfError(stmnt.ParameterIndex(KContactIdParam() ) ), aItem.Id() ) );
// build the clob/blob parts of the update statement
RSqlParamWriteStream textHeader;
User::LeaveIfError(textHeader.BindBinary(stmnt,
User::LeaveIfError(stmnt.ParameterIndex(KContactTextFieldHeaderParam() ) ) ) );
CEmbeddedStore* textEmbeddedStore = CEmbeddedStore::NewLC(textHeader);
RSqlParamWriteStream binHeader;
User::LeaveIfError(binHeader.BindBinary(stmnt,
User::LeaveIfError(stmnt.ParameterIndex(KContactBinaryFieldHeaderParam() ) ) ) );
CEmbeddedStore* binaryEmbeddedStore = CEmbeddedStore::NewLC(binHeader);
RSqlParamWriteStream textFields;
User::LeaveIfError(textFields.BindText(stmnt,
User::LeaveIfError(stmnt.ParameterIndex(KContactTextFieldsParam() ) ) ) );
CleanupClosePushL(textFields);
RSqlParamWriteStream binFields;
User::LeaveIfError(binFields.BindBinary(stmnt,
User::LeaveIfError(stmnt.ParameterIndex(KContactBinaryFieldsParam() ) ) ) );
CEmbeddedStore* binaryEmbeddedBlobStore=CEmbeddedStore::NewLC(binFields);
const CContactTemplate* sysTemplate = NULL;
if (aItem.Type() != KUidContactTemplate && aItem.Type() != KUidContactGroup)
{
// System template is needed unless we are creating a template.
sysTemplate = &iProperties.SystemTemplateL();
}
TCntPersistenceUtility::WriteBlobL(*textEmbeddedStore, textFields, *binaryEmbeddedStore, *binaryEmbeddedBlobStore, aItem, sysTemplate);
textHeader.CommitL();
textFields.CommitL();
binHeader.CommitL();
binFields.CommitL();
// execute the statement
User::LeaveIfError(stmnt.Exec() );
textHeader.Close();
binHeader.Close();
textFields.Close();
binFields.Close();
CleanupStack::PopAndDestroy(5, &stmnt); //binaryEmbeddedBlobStore, textFields, binaryEmbeddedStore, textEmbeddedStore, stmnt
}
/**
Gets the variables for the three contact item fields represented by the type flags database field.
@param aTypeFlags the type flags field to be decoded
@param aType the type variable to be got
@param aAttributes the attribute variable to be got
@param aHintFields the hint fields variable to be got
*/
void CPplContactTable::GetTypeFlagFields(TInt aTypeFlags, TUid& aType, TUint& aAttributes, TUint& aHintFields)
{
aType = TCntPersistenceUtility::TypeFlagsToContactTypeUid(aTypeFlags);
aAttributes = (aTypeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift;
aHintFields = TCntPersistenceUtility::TypeFlagsToHint(aTypeFlags);
}
/**
Creates a TInt of type_flags for insertion into the database.
@return A bitmap representing all three parameter values ready to be inserted
into the database as a type_flags column value.
@param aType
@param aAttributes
@param aHintFields
*/
TInt CPplContactTable::GenerateTypeFlags(TUid aType, TUint aAttributes, TUint aHintFields)
{
TInt tmpVal = TCntPersistenceUtility::ContactTypeUidToTypeFlags(aType);
tmpVal |= (aAttributes << EContactAttributes_Shift) & EContactAttributes_Mask;
tmpVal |= aHintFields & EContactHintFlags_Mask;
return tmpVal;
}
void CPplContactTable::DeleteL(const CContactItem& aItem, TBool& aLowDiskErrorOccurred)
{
CContactItem* item = DeleteLC(aItem.Id(), aLowDiskErrorOccurred);
CleanupStack::PopAndDestroy(item);
}
/**
Delete a contact item from the contact table
@param aItemId contact item id
@param aLowDiskErrorOccurred outparameter; will be set to ETrue if an attempt to delete in
low disk condition occured
*/
CContactItem* CPplContactTable::DeleteLC(TContactItemId aItemId, TBool& aLowDiskErrorOccurred)
{
// You can't delete the system template, because you couldn't read any cards otherwise.
__ASSERT_ALWAYS(aItemId != KGoldenTemplateId, User::Leave(KErrNotSupported) );
// select the relevant bits from the contact table for the contact item
// and put them in a new contact item
RSqlStatement selectStmnt;
CleanupClosePushL(selectStmnt);
selectStmnt.PrepareL(iDatabase, iDeleteSelectStmnt->SqlStringL() );
const TInt KContactIdParamIndex(KFirstIndex); // first and only parameter in the query
User::LeaveIfError(selectStmnt.BindInt(KContactIdParamIndex, aItemId ) );
// execute query
TInt err(selectStmnt.Next() );
if (err != KSqlAtRow)
{
if (err == KSqlAtEnd)
{
User::Leave(KErrNotFound);
}
User::LeaveIfError(err);
}
// Obtain the contact type and create a new contact item of this type.
TInt typeFlags = selectStmnt.ColumnInt(iDeleteSelectStmnt->ParameterIndex(KContactTypeFlags() ) ) ;
TInt accessCount = selectStmnt.ColumnInt(iDeleteSelectStmnt->ParameterIndex(KContactAccessCount() ) ) ;
TContactItemId templateId = selectStmnt.ColumnInt(iDeleteSelectStmnt->ParameterIndex(KContactTemplateId() ) ) ;
CleanupStack::PopAndDestroy(&selectStmnt);
TUid type = TCntPersistenceUtility::TypeFlagsToContactTypeUid(typeFlags);
CContactItem* item = CContactItem::NewLC(type);
item->SetId(aItemId);
item->SetAttributes((typeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift);
item->SetAccessCount(accessCount);
item->SetTemplateRefId(templateId);
if (item->IsDeletable() )
{
// delete it here
RSqlStatement deleteStmnt;
CleanupClosePushL(deleteStmnt);
deleteStmnt.PrepareL(iDatabase, iDeleteStmnt->SqlStringL() );
User::LeaveIfError(deleteStmnt.BindInt(KContactIdParamIndex, aItemId) );
TInt err = deleteStmnt.Exec();
CleanupStack::PopAndDestroy(&deleteStmnt);
if (err == KErrDiskFull)
{
aLowDiskErrorOccurred = ETrue;
}
else
{
User::LeaveIfError(err);
}
}
else // Not deletable because of access count > 0.
{
if (!item->IsDeleted() )
{
// Set and persist the deleted flag.
item->SetDeleted(ETrue);
// update attributes in the contact table
typeFlags |= (item->Attributes()) << EContactAttributes_Shift;
RSqlStatement updateStmnt;
CleanupClosePushL(updateStmnt);
updateStmnt.PrepareL(iDatabase, iUpdateFlagsStmnt->SqlStringL() );
const TInt KTypeFlagsParamIndex(KFirstParam); // first parameter in query...
const TInt KContactIdParamIndex(KTypeFlagsParamIndex + 1); // ...and the second.
User::LeaveIfError(updateStmnt.BindInt(KTypeFlagsParamIndex, typeFlags) );
User::LeaveIfError(updateStmnt.BindInt(KContactIdParamIndex, item->Id()) );
User::LeaveIfError(updateStmnt.Exec() );
CleanupStack::PopAndDestroy(&updateStmnt);
}
}
if (!item->IsDeleted() && !aLowDiskErrorOccurred)
{
// Decrement Template reference counter.
UpdateTemplateAccessCounterL(item->TemplateRefId(), EFalse);
}
// Ownership is passed to the caller, who can do additional analysis of the item.
return item;
}
/**
Create the contact table in the database.
*/
void CPplContactTable::CreateTableL()
{
User::LeaveIfError(iDatabase.Exec(KContactCreateStmnt) );
}
/**
Change the type of the given contact item to the given type.
@param aItemId Contact item ID whose type is to be changed.
@param aNewType New type for contact item.
*/
void CPplContactTable::ChangeTypeL(TContactItemId aItemId, TUid aNewType)
{
if(aItemId == KNullContactId)
{
return;
}
// get the type flags for the contact item
const TInt bufSize(KSelectFlagsSqlStmntFormat().Size() + NumDigits(aItemId) );
HBufC* buf = HBufC::NewLC(bufSize);
TPtr selectTypeFlagsSqlStmnt(buf->Des() );
selectTypeFlagsSqlStmnt.AppendFormat(KSelectFlagsSqlStmntFormat, aItemId);
TSqlScalarFullSelectQuery selectTypeFlagsQuery(iDatabase);
TInt typeFlags = selectTypeFlagsQuery.SelectIntL(selectTypeFlagsSqlStmnt);
CleanupStack::PopAndDestroy(buf);
// deconstruct the type flags into the different fields and then rebuild it with the new type
TUid type;
TUint attributes;
TUint hintFields;
GetTypeFlagFields(typeFlags, type, attributes, hintFields);
typeFlags = GenerateTypeFlags(aNewType, attributes, hintFields); // use new type
// update type flags in the contact table
RSqlStatement updateStmnt;
CleanupClosePushL(updateStmnt);
updateStmnt.PrepareL(iDatabase, iUpdateFlagsStmnt->SqlStringL() );
User::LeaveIfError(updateStmnt.BindInt(updateStmnt.ParameterIndex(KContactTypeFlagsParam() ), typeFlags) );
User::LeaveIfError(updateStmnt.BindInt(updateStmnt.ParameterIndex(KContactIdParam()), aItemId) );
User::LeaveIfError(updateStmnt.Exec() );
CleanupStack::PopAndDestroy(&updateStmnt);
}
/**
CPplContactTable::THintField constructor.
*/
CPplContactTable::THintField::THintField()
: iValue(0)
{
}
/**
CPplContactTable::THintField constructor.
*/
CPplContactTable::THintField::THintField(TUint16 aExtHint, TUint8 aHint)
{
iValue = ((aExtHint << 8) | aHint);
}
/**
Update the hint value using the given contact field and custom filterable
fields.
@param aField Contact field.
@param aCustFiltFields Custom filterable fields.
*/
void CPplContactTable::THintField::UpdateHintL(const CContactItemField& aField, const RArray<TUid>& aCustFiltFields)
{
const CContentType& type = aField.ContentType();
if (type.ContainsFieldType(KUidContactFieldVCardMapWORK) )
{
iValue |= CContactDatabase::EWork;
}
if (type.ContainsFieldType(KUidContactFieldVCardMapHOME) )
{
iValue |= CContactDatabase::EHome;
}
if (type.ContainsFieldType(KUidContactFieldPhoneNumber) )
{
iValue |= CContactDatabase::EPhonable;
if(type.ContainsFieldType(KUidContactFieldVCardMapCELL) )
{
iValue |= CContactDatabase::ESmsable;
}
else if(!type.ContainsFieldType(KUidContactFieldVCardMapPAGER) )
{
iValue |= CContactDatabase::ELandLine;
}
}
if (type.ContainsFieldType(KUidContactFieldRingTone) )
{
CContactFieldStorage* storage = aField.Storage();
if ( storage && storage->IsFull() )
{
iValue |= CContactDatabase::ERingTone;
}
}
if (type.ContainsFieldType( KUidContactsVoiceDialField ) )
{
CContactFieldStorage* storage = aField.Storage();
if (storage && storage->IsFull() )
{
iValue |= CContactDatabase::EVoiceDial;
}
}
if(type.ContainsFieldType(KUidContactFieldFax) )
{
iValue |= CContactDatabase::EFaxable;
iValue |= CContactDatabase::EPhonable;
}
if(type.ContainsFieldType(KUidContactFieldIMAddress) )
{
CContactFieldStorage* storage = aField.Storage();
if(storage && storage->IsFull() )
{
iValue |= CContactDatabase::EIMAddress;
if(type.ContainsFieldType(KUidContactFieldVCardMapWV) )
{
iValue |= CContactDatabase::EWirelessVillage;
}
}
}
if (type.ContainsFieldType(KUidContactFieldEMail) )
{
iValue |= CContactDatabase::EMailable;
}
// Now check if given field type maps to one of the given (licensee) custom
// filterable fields.
TInt index(KErrNotFound);
const TInt KCount = aCustFiltFields.Count();
for (TInt i = 0; i < KCount; ++i)
{
if (type.ContainsFieldType(aCustFiltFields[i]) )
{
index = i;
break;
}
}
if (index != KErrNotFound)
{
switch (index)
{
case 0:
iValue |= CContactDatabase::ECustomFilter1;
break;
case 1:
iValue |= CContactDatabase::ECustomFilter2;
break;
case 2:
iValue |= CContactDatabase::ECustomFilter3;
break;
case 3:
iValue |= CContactDatabase::ECustomFilter4;
break;
default:
__ASSERT_DEBUG(EFalse, User::Leave(KErrNotSupported) );
break;
}
}
}
/**
Get the hint extension value (upper 8 bits).
@return Hint extension value.
*/
TUint16 CPplContactTable::THintField::ExtHint()
{
return static_cast<TUint16>((iValue >> 8) & KMaxTUint16);
}
/**
Get the non-extended hint value (lower 8 bits).
@return Non-extended hint value.
*/
TInt8 CPplContactTable::THintField::Hint()
{
return static_cast<TInt8>(iValue & KMaxTUint8);
}
/**
Return the Identity field uid for the given contact field.
@param Contact field.
@return Identity field index corresponding to the given contact field.
*/
TInt CPplContactTable::NameFieldUid(const CContactItemField& nameField)
{
// For all the name fields stored in the Identity table...
for (TInt nameFieldNum = 0; nameFieldNum < sizeof(KFastAccessFieldUids) / sizeof(TInt); ++nameFieldNum)
{
if (nameField.ContentType().ContainsFieldType(TUid::Uid(KFastAccessFieldUids[nameFieldNum]) ) )
{
return KFastAccessFieldUids[nameFieldNum];
}
}
return KErrNotFound;
}
/**
Utility function to calculate the number of digits in an integer.
If a negative number, counts the - sign as an extra digit.
@param aNum an integer
@return TUint the number of digits
*/
TUint CPplContactTable::NumDigits(TInt aNum)
{
TInt numDig(1);
if (aNum < 0)
{
++numDig;
aNum = -aNum;
}
while( (aNum /= 10) > 0)
{
++numDig;
}
return numDig;
}
/**
Utility method used to check if contact table is empty or not
@return ETrue is the contact table is empty and EFalse otherwise
*/
TBool CPplContactTable::IsTableEmptyL()
{
TInt count = 0;
HBufC* selectString = HBufC::NewLC(KCountSelect().Length() +
KSqlContactTableName().Length());
TPtr ptrSelectString = selectString->Des();
ptrSelectString.Format(KCountSelect, &KSqlContactTableName);
TSqlScalarFullSelectQuery scalarQuery(iDatabase);
count = scalarQuery.SelectIntL(ptrSelectString);
CleanupStack::PopAndDestroy(selectString);
return (count == 0);
}
/**
Utility method used to retrieve card template ids
*/
CContactIdArray& CPplContactTable::CardTemplateIdsL()
{
iCardTemplateIds->Reset();
HBufC* selectString = HBufC::NewLC(KTwoTypeField().Length() + KContactId().Length() + KContactTypeFlags().Length() +
KSqlContactTableName().Length() + KContactTypeFlags().Length() + 3);
TPtr ptrSelectString = selectString->Des();
ptrSelectString.Format(KTwoTypeField, &KContactId, &KContactTypeFlags, &KSqlContactTableName, &KContactTypeFlags, EContactType_Shift, EContactTypeFlags_CardTemplate);
RSqlStatement selectStmnt;
CleanupClosePushL(selectStmnt);
selectStmnt.PrepareL(iDatabase, ptrSelectString );
const TInt KIdIndex = selectStmnt.ColumnIndex(KContactId);
const TInt KTypeFlagsIndex = selectStmnt.ColumnIndex(KContactTypeFlags);
TInt err;
while((err = selectStmnt.Next()) == KSqlAtRow)
{
TInt typeFlags = selectStmnt.ColumnInt(KTypeFlagsIndex);
TInt attr = (typeFlags & EContactAttributes_Mask) >> EContactAttributes_Shift;
if((attr & EContactAttrsFlags_Deleted) == 0)
{
//Only add templates are not marked as deleted.
iCardTemplateIds->AddL(selectStmnt.ColumnInt(KIdIndex));
}
}
if (err != KSqlAtEnd)
{
User::Leave(err);
}
CleanupStack::PopAndDestroy(2,selectString);
return *iCardTemplateIds;
}
/**
Utility method used to retrieve own card id
*/
TContactItemId CPplContactTable::OwnCardIdL()
{
TContactItemId ownCardId = 0;
HBufC* selectString = HBufC::NewLC(KOneTypeField().Length() + KContactId().Length() +
KSqlContactTableName().Length() + KContactTypeFlags().Length() + 3);
TPtr ptrSelectString = selectString->Des();
ptrSelectString.Format(KOneTypeField, &KContactId, &KSqlContactTableName, &KContactTypeFlags, EContactType_Shift, EContactTypeFlags_OwnCard);
RSqlStatement selectStmnt;
CleanupClosePushL(selectStmnt);
selectStmnt.PrepareL(iDatabase, ptrSelectString );
const TInt idIndex = selectStmnt.ColumnIndex(KContactId);
TInt err;
if((err = selectStmnt.Next()) == KSqlAtRow)
{
ownCardId = selectStmnt.ColumnInt(idIndex);
}
else
{
User::LeaveIfError(err);
ownCardId = KErrNotFound;
}
CleanupStack::PopAndDestroy(2,selectString);
return ownCardId;
}
/**
Utility method used to set own card id
*/
void CPplContactTable::SetOwnCardIdL(TContactItemId aId)
{
TContactItemId oldOwnCardId = OwnCardIdL();
if(oldOwnCardId != aId)
{
//Change old own card to be contact card.
ChangeTypeL(oldOwnCardId, KUidContactCard);
//set given card as own card.
ChangeTypeL(aId, KUidContactOwnCard);
}
}