diff -r f5050f1da672 -r 04becd199f91 javaextensions/pim/framework/src.s60/cpimlist.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaextensions/pim/framework/src.s60/cpimlist.cpp Tue Apr 27 16:30:29 2010 +0300 @@ -0,0 +1,1441 @@ +/* +* Copyright (c) 2008 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: Abstract PIMList base class. + * +*/ + + +// INCLUDE FILES +#include "cpimlist.h" +#include "mpimadaptermanager.h" +#include "mpimlistadapter.h" +#include "mpimlocalizationdata.h" +#include "cpimvalidator.h" +#include "cpimitem.h" +#include "cpimitemmatcher.h" +#include "cpimstringmatcher.h" +#include "cleanupresetanddestroy.h" +#include "pimexternalchanges.h" +#include "pimpanics.h" +#include "pimjnitools.h" +#include "pimutils.h" +#include "s60commonutils.h" +#include "jstringutils.h" +#include "logger.h" + +// CONSTANTS +/** Item array granularity. */ +const TInt KItemArrayGranularity = 8; + +CPIMList::CPIMList(const CPIMValidator& aValidator) : + iItems(KItemArrayGranularity), iValidator(aValidator) // not owned +{ + JELOG2(EPim); +} + +void CPIMList::ConstructL(MPIMLocalizationData* aLocalizationData, + MPIMAdapterManager* aAdapterManager, MPIMListAdapter* aListAdapter) +{ + JELOG2(EPim); + iLocalizationData = aLocalizationData; // not owned + iAdapterManager = aAdapterManager; // not owned + iListAdapter = aListAdapter; // not owned +} + +CPIMList::~CPIMList() +{ + JELOG2(EPim); + iItems.Close(); +} + +pimbaseitem* CPIMList::createItem() +{ + JELOG2(EPim); + TInt error = KErrNone; + CPIMItem* item = NULL; + TRAP(error, item = DoCreateItemL(KPIMNullItemID(), NULL)); + if (error != KErrNone) + throw error; + item->SetModified(ETrue); + return item; +} + +void CPIMList::removeItem(pimbaseitem* aItem) +{ + JELOG2(EPim); + TInt error = KErrNone; + TRAP(error, + { + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + CPIMItem* item = static_cast(aItem); + // Find the item in the list + TInt itemIndex = iItems.Find(item); + + if (itemIndex == KErrNotFound) + { + // The item is not in the list + User::Leave(KErrArgument); + } + + // Remove item from native database. + // If the item does not exist any more in the database, we will + // not touch but let the update mechanism clean it away at + // some other time. + DoDeleteItemL(*item); + + // Remove item from the list + item->RemoveAdapterAssociation(); + iItems.Remove(itemIndex); + + // The item is owned by its Java side peer, so we won't + // delete it here. + } + ); + if (error != KErrNone) + throw error; +} + +void CPIMList::addCommittedItem(pimbaseitem* aItem) +{ + JELOG2(EPim); + CPIMItem* item = static_cast(aItem); + TInt error = KErrNone; + TRAP(error, + { + __ASSERT_DEBUG( + iItems.Find(item)== KErrNotFound, + User::Panic(KPIMPanicCategory, EPIMPanicCommittedExists)); + + User::LeaveIfError(iItems.Append(item)); + } + ); + if (error != KErrNone) + throw error; +} + +jstring CPIMList::getName(JNIEnv* aJniEnv) +{ + JELOG2(EPim); + HBufC* name = NULL; + TInt error = KErrNone; + TRAP(error, name = iAdapterManager->ListNameL().AllocL()); + if (error != KErrNone || !name) + { + // If a leave occurred the name was never created + return NULL; + } + jstring javaName = java::util::S60CommonUtils::NativeToJavaString( + *aJniEnv, *name); + delete name; + return javaName; // if NULL, it indicates error +} + +void CPIMList::close() +{ + JELOG2(EPim); + if (!iListAdapter) + { + throw KErrSessionClosed; + } + + // Close the list adapter + iListAdapter->Close(); + + // Notify items about closing list + const TInt n = iItems.Count(); + for (TInt i = 0; i < n; i++) + { + iItems[i]->ListClosed(); + } + + // Setting the list adapter NULL denotes closed list + iListAdapter = NULL; + +} + +jintArray CPIMList::callItemsByEnumerationType(JNIEnv* aJniEnv, + int aEnumerationType, int aMatchingItemHandle, jstring aStringArg, + jintArray aError) +{ + JELOG2(EPim); + const JStringUtils stringArg(*aJniEnv, aStringArg); + const TDesC* nativeStringArg = (aStringArg ? &stringArg : NULL); + TInt error = KErrNone; + jintArray itemHandles = NULL; + TRAP(error, itemHandles = CallItemsByEnumerationTypeL(aJniEnv, + aEnumerationType, aMatchingItemHandle, nativeStringArg)); + SetJavaErrorCode(aJniEnv, aError, error); + return itemHandles; +} + +jintArray CPIMList::CallItemsByEnumerationTypeL(JNIEnv* aJniEnv, + int aEnumerationType, int aMatchingItemHandle, + const TDesC* aStringArg) +{ + JELOG2(EPim); + RPointerArray* items = NULL; + + if (aEnumerationType == EPIMItemAll) + { + items = ItemsL(); + } + else if (aEnumerationType == EPIMItemMatchingItem) + { + pimbaseitem * baseMatchingItem = + reinterpret_cast(aMatchingItemHandle); + CPIMItem* matchingItem = + static_cast(baseMatchingItem); + items = ItemsL(*matchingItem); + } + else if (aEnumerationType == EPIMItemMatchingString) + { + __ASSERT_DEBUG(aStringArg, User::Panic(KPIMPanicCategory, + EPIMPanicNullArgument)); + + items = ItemsL(*aStringArg); + } + else if (aEnumerationType == EPIMItemMatchingCategory) + { + // Category argument may be NULL, indicating "uncategorized" + items = ItemsByCategoryL(aStringArg); + } + else + { + User::Leave(KErrCorrupt); // Error + } + + // We now own the items array + + jintArray itemHandles = GetJavaItemHandles(*aJniEnv, *items); + items->Close(); + delete items; + items = NULL; + + if (!itemHandles) + { + User::Leave(KErrNoMemory); + } + + return itemHandles; +} + +RPointerArray* CPIMList::ItemsL() +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + // Put all items in an array as MPIMItemData objects + RPointerArray tempArr(KItemArrayGranularity); + CleanupClosePushL(tempArr); + + const TInt n = iItems.Count(); + for (TInt i = 0; i < n; i++) + { + CPIMItem* item = iItems[i]; + User::LeaveIfError(tempArr.Append(item)); + } + + TLinearOrder order(iAdapterManager->ItemOrder()); + + RPointerArray* retArr = SortAndConvertToCPIMItemsL(order, + tempArr); + + CleanupStack::PopAndDestroy(); // tempArr cleanup close + return retArr; +} + +RPointerArray* CPIMList::ItemsL(const CPIMItem& aMatchingItem) +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + // Matching items are inserted temporarily into this array for sorting + RPointerArray tempArr(KItemArrayGranularity); + CleanupClosePushL(tempArr); + + CPIMItemMatcher* itemMatcher = CPIMItemMatcher::NewLC(iValidator, + *iAdapterManager, aMatchingItem); + + const TInt numItems = iItems.Count(); + for (TInt itemIndex = 0; itemIndex < numItems; itemIndex++) + { + CPIMItem* listItem = iItems[itemIndex]; + + if (itemMatcher->MatchL(*listItem)) + { + User::LeaveIfError(tempArr.Append(listItem)); + } + } + + CleanupStack::PopAndDestroy(itemMatcher); + TLinearOrder order(iAdapterManager->ItemOrder()); + + RPointerArray* retArr = SortAndConvertToCPIMItemsL(order, + tempArr); + + CleanupStack::PopAndDestroy(); // tempArr cleanup close + return retArr; +} + +RPointerArray* CPIMList::ItemsL(const TDesC& aMatchingValue) +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + // Matching items are inserted temporarily into this array for sorting + RPointerArray tempArr(KItemArrayGranularity); + CleanupClosePushL(tempArr); + + CPIMStringMatcher* stringMatcher = + new(ELeave) CPIMStringMatcher(iValidator); + + CleanupStack::PushL(stringMatcher); + + // Iterate through all items in the list + const TInt numItems = iItems.Count(); + for (TInt itemIndex = 0; itemIndex < numItems; itemIndex++) + { + CPIMItem* listItem = iItems[itemIndex]; + + if (stringMatcher->MatchL(aMatchingValue, *listItem)) + { + User::LeaveIfError(tempArr.Append(listItem)); + } + } + + CleanupStack::PopAndDestroy(stringMatcher); + TLinearOrder order(iAdapterManager->ItemOrder()); + + RPointerArray* retArr = SortAndConvertToCPIMItemsL(order, + tempArr); + + CleanupStack::PopAndDestroy(); // tempArr cleanup close + return retArr; +} + +RPointerArray* CPIMList::ItemsByCategoryL( + const TDesC* aCategory) +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + // Matching items are inserted temporarily into this array for sorting + RPointerArray tempArr(KItemArrayGranularity); + CleanupClosePushL(tempArr); + + // Iterate through all items in the list + const TInt numItems = iItems.Count(); + for (TInt itemIndex = 0; itemIndex < numItems; itemIndex++) + { + MPIMItem* listItem = iItems[itemIndex]; + + // Iterate through the categories in the list item + const CDesCArray& categories = listItem->GetCategoriesL(); + + if (!aCategory) + { + // We are matching non-assigned items + if (categories.Count() == 0) + { + User::LeaveIfError(tempArr.Append(listItem)); + } + } + else // aCategory is non-NULL + { + // We are matching items assigned to given category + // Compare case-sensitive with accents etc. + TInt dummyPosition = 0; + if (0 == categories.Find(*aCategory, dummyPosition, + ECmpNormal)) + { + User::LeaveIfError(tempArr.Append(listItem)); + } + } + } + + TLinearOrder order(iAdapterManager->ItemOrder()); + + RPointerArray* retArr = SortAndConvertToCPIMItemsL(order, tempArr); + + CleanupStack::PopAndDestroy(); // tempArr cleanup close + return retArr; +} + +const CDesCArray& CPIMList::GetCategoriesL() +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); // codescanner::leave + } + + return iListAdapter->GetCategoriesL(); // codescanner::leave +} + +jobjectArray CPIMList::getCategories(JNIEnv* aJniEnv, jintArray aError) +{ + JELOG2(EPim); + const CDesCArray* categories = NULL; + TInt error = KErrNone; + TRAP(error, categories = &(GetCategoriesL())); + + SetJavaErrorCode(aJniEnv, aError, error); + + if (error != KErrNone) + { + return NULL; + } + + jobjectArray javaCategories = CreateJavaStringArray(aJniEnv, *categories, + EFalse); // (do not handle KPIMNullArrayElement elements specially) + + if (!javaCategories) + { + SetJavaErrorCode(aJniEnv, aError, KErrNoMemory); + } + + return javaCategories; +} + +TBool CPIMList::IsCategoryL(const TDesC& aCategory) +{ + JELOG2(EPim); + TBool retVal = EFalse; + + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + const CDesCArray& categories = iListAdapter->GetCategoriesL(); + TInt dummyIndex = -1; + + if (0 == categories.Find(aCategory, dummyIndex, ECmpNormal)) + { + retVal = ETrue; + } + + return retVal; +} + +jboolean CPIMList::isCategory(jstring aCategory, JNIEnv* aJniEnv, + jintArray aError) +{ + JELOG2(EPim); + __ASSERT_DEBUG(aCategory, User::Panic(KPIMPanicCategory, + EPIMPanicNullArgument)); + const JStringUtils nativeCategory(*aJniEnv, aCategory); + TBool retVal = EFalse; + TInt error = KErrNone; + TRAP(error, retVal = IsCategoryL(*(static_cast(&nativeCategory)))); + SetJavaErrorCode(aJniEnv, aError, error); + return static_cast(retVal); +} + +void CPIMList::AddCategoryL(const TDesC& aCategory) +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + iListAdapter->AddCategoryL(aCategory); +} + +void CPIMList::addCategory(jstring aCategory, JNIEnv* aJniEnv, + jintArray aError) +{ + JELOG2(EPim); + __ASSERT_DEBUG(aCategory, User::Panic(KPIMPanicCategory, + EPIMPanicNullArgument)); + const JStringUtils category(*aJniEnv, aCategory); + TInt error = KErrNone; + TRAP(error, AddCategoryL(*(static_cast(&category)))); + SetJavaErrorCode(aJniEnv, aError, error); +} + +RPointerArray* CPIMList::DeleteCategoryL( + const TDesC& aCategory) +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + // The adapter category deletion operation does not tell whether + // the category did exist, so we'll put it down here. + TBool categoryExists = EFalse; + TInt dummyPosition = 0; + if (0 == iListAdapter->GetCategoriesL().Find(aCategory, + dummyPosition, ECmpNormal)) + { + categoryExists = ETrue; + } + + iListAdapter->DeleteCategoryL(aCategory); + + // NULL return value indicates that the category was not really + // deleted (because it did not exist). + RPointerArray* unassignedItems = NULL; + + if (categoryExists) + { + RPointerArray categoryMembers(KItemArrayGranularity); + CleanupClosePushL(categoryMembers); + + const TInt n = iItems.Count(); + for (TInt i = 0; i < n; i++) + { + TBool wasMember = iItems[i]->CategoryDeleted(aCategory); + if (wasMember) + { + User::LeaveIfError(categoryMembers.Append(iItems[i])); + } + } + + unassignedItems = ResolveUnassignedItemsL(categoryMembers); + CleanupStack::PopAndDestroy(); // categoryMembers cleanup close + } + + return unassignedItems; +} + +jintArray CPIMList::deleteCategory(jstring aCategory, JNIEnv* aJniEnv, + jintArray aError) +{ + JELOG2(EPim); + __ASSERT_DEBUG(aCategory, User::Panic(KPIMPanicCategory, + EPIMPanicNullArgument)); + + RPointerArray* unassignedItems = NULL; + const JStringUtils category(*aJniEnv, aCategory); + TInt error = KErrNone; + TRAP(error, unassignedItems = DeleteCategoryL( + *(static_cast(&category)))); + SetJavaErrorCode(aJniEnv, aError, error); + if (error != KErrNone || !unassignedItems) + { + // If the operation leaved, unassignedItems was never created. + // If no unassignedItems are present, we're OK but no return + // value is needed. + return NULL; + } + + // We now own the array of removed items + + jintArray itemHandles = GetJavaItemHandles(*aJniEnv, *unassignedItems); + unassignedItems->Close(); + delete unassignedItems; + + if (!itemHandles) + { + SetJavaErrorCode(aJniEnv, aError, KErrNoMemory); + return NULL; + } + return itemHandles; + +} + +void CPIMList::RenameCategoryL(const TDesC& aCurrentCategory, + const TDesC& aNewCategory) +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + iListAdapter->RenameCategoryL(aCurrentCategory, aNewCategory); + + const TInt n = iItems.Count(); + for (TInt i = 0; i < n; i++) + { + iItems[i]->CategoryRenamedL(aCurrentCategory, aNewCategory); + } +} + +jint CPIMList::renameCategory(jstring aCurrentCategory, + jstring aNewCategory, JNIEnv* aJniEnv) +{ + JELOG2(EPim); + __ASSERT_DEBUG(aCurrentCategory, User::Panic(KPIMPanicCategory, + EPIMPanicNullArgument)); + + __ASSERT_DEBUG(aNewCategory, User::Panic(KPIMPanicCategory, + EPIMPanicNullArgument)); + + const JStringUtils currentCategory(*aJniEnv, aCurrentCategory); + const JStringUtils newCategory(*aJniEnv, aNewCategory); + TInt error = KErrNone; + + TRAP(error, RenameCategoryL( + *(static_cast(¤tCategory)), + *(static_cast(&newCategory)))); + + return error; +} + +jint CPIMList::maxCategories() +{ + JELOG2(EPim); + return iAdapterManager->MaxCategories(); +} + +jboolean CPIMList::isSupportedField(TPIMField aField) +{ + JELOG2(EPim); + TBool isSupportedField = iAdapterManager->IsSupportedField(aField); + return static_cast(isSupportedField); +} + +jintArray CPIMList::getSupportedFields(JNIEnv* aJniEnv) +{ + JELOG2(EPim); + const CArrayFix* fields = NULL; + TInt error = KErrNone; + TRAP(error, fields = &(iAdapterManager->GetSupportedFieldsL())); + if (error != KErrNone) + { + // If a leave occurred the field array was never created + return NULL; // Error + } + jintArray javaFields = ConvertToJavaIntArray(*aJniEnv, *fields); + return javaFields; // NULL indicates error +} + +jboolean CPIMList::isSupportedAttribute(TPIMField aField, + TPIMAttribute aAttribute) +{ + JELOG2(EPim); + TBool isSupportedAttribute = iAdapterManager->IsSupportedAttribute(aField, aAttribute); + return static_cast(isSupportedAttribute); +} + +const CArrayFix& CPIMList::GetSupportedAttributesL( + TPIMField aField) +{ + JELOG2(EPim); + if (!iValidator.IsValidField(aField)) + { + User::Leave(KErrArgument); // codescanner::leave + } + + if (!iAdapterManager->IsSupportedField(aField)) + { + User::Leave(KErrNotSupported); // codescanner::leave + } + + return iAdapterManager->GetSupportedAttributesL(aField); // codescanner::leave +} + +jintArray CPIMList::getSupportedAttributes(TPIMField aField, + JNIEnv* aJniEnv, jintArray aError) +{ + JELOG2(EPim); + const CArrayFix* attributes = NULL; + TInt error = KErrNone; + TRAP(error, attributes = &(GetSupportedAttributesL(aField))); + SetJavaErrorCode(aJniEnv, aError, error); + + if (error != KErrNone) + { + return NULL; + } + + jintArray javaAttributes = ConvertToJavaIntArray(*aJniEnv, + *attributes); + + if (!javaAttributes) + { + SetJavaErrorCode(aJniEnv, aError, KErrNoMemory); + } + return javaAttributes; +} + +jboolean CPIMList::isSupportedArrayElement(TPIMField aStringArrayField, + TPIMArrayElement aArrayElement) +{ + JELOG2(EPim); + TBool retVal = iAdapterManager->IsSupportedArrayElement( + aStringArrayField, aArrayElement); + return static_cast(retVal); +} + +const CArrayFix& CPIMList::GetSupportedArrayElementsL( + TPIMField aStringArrayField) +{ + JELOG2(EPim); + if (iValidator.FieldDataType(aStringArrayField) + != EPIMFieldStringArray) + { + User::Leave(KErrArgument); // codescanner::leave + } + + if (!iAdapterManager->IsSupportedField(aStringArrayField)) + { + User::Leave(KErrNotSupported); // codescanner::leave + } + + return iAdapterManager->GetSupportedArrayElementsL( // codescanner::leave + aStringArrayField); +} + +jintArray CPIMList::getSupportedArrayElements( + TPIMField aStringArrayField, JNIEnv* aJniEnv, jintArray aError) +{ + JELOG2(EPim); + const CArrayFix* elements = NULL; + TInt error = KErrNone; + TRAP(error, elements = &(GetSupportedArrayElementsL( + aStringArrayField))); + SetJavaErrorCode(aJniEnv, aError, error); + + if (error != KErrNone) + { + return NULL; + } + + jintArray javaElements = ConvertToJavaIntArray(*aJniEnv, *elements); + + if (!javaElements) + { + SetJavaErrorCode(aJniEnv, aError, KErrNoMemory); + } + return javaElements; +} + +TPIMFieldDataType CPIMList::GetFieldDataTypeL(TPIMField aField) +{ + JELOG2(EPim); + TPIMFieldDataType fieldDataType = iValidator.FieldDataType(aField); + + if (fieldDataType == EPIMFieldInvalid) + { + User::Leave(KErrArgument); + } + + if (!iAdapterManager->IsSupportedField(aField)) + { + User::Leave(KErrNotSupported); + } + + return fieldDataType; +} + +jint CPIMList::getFieldDataType(TPIMField aField, JNIEnv* aJniEnv, + jintArray aError) +{ + JELOG2(EPim); + TPIMFieldDataType fieldDataType = EPIMFieldInvalid; + TInt error = KErrNone; + TRAP(error, fieldDataType = GetFieldDataTypeL(aField)); + SetJavaErrorCode(aJniEnv, aError, error); + return fieldDataType; +} + +HBufC* CPIMList::GetFieldLabelL(TPIMField aField) +{ + JELOG2(EPim); + if (!iValidator.IsValidField(aField)) + { + User::Leave(KErrArgument); + } + + if (!iAdapterManager->IsSupportedField(aField)) + { + User::Leave(KErrNotSupported); + } + + return iLocalizationData->GetFieldLabelL(aField); +} + +HBufC* CPIMList::GetAttributeLabelL(TPIMAttribute aAttribute) +{ + JELOG2(EPim); + if (!IS_SINGLE_BIT(aAttribute) || !(iValidator.ValidAttributes()& aAttribute)) + { + User::Leave(KErrArgument); + } + + if (!(iAdapterManager->GetAllSupportedAttributesCombined() & aAttribute)) + { + User::Leave(KErrNotSupported); + } + + return iLocalizationData->GetAttributeLabelL(aAttribute); +} + +HBufC* CPIMList::GetArrayElementLabelL(TPIMField aStringArrayField, + TPIMArrayElement aArrayElement) +{ + JELOG2(EPim); + if ((!iValidator.IsValidField(aStringArrayField)) || (aArrayElement < 0) + || (aArrayElement >= iValidator.NumElementsL(aStringArrayField))) + { + User::Leave(KErrArgument); + } + + if (!iAdapterManager->IsSupportedArrayElement(aStringArrayField, + aArrayElement)) + { + User::Leave(KErrNotSupported); + } + + return iLocalizationData->GetArrayElementLabelL(aStringArrayField, + aArrayElement); +} + +TInt CPIMList::MaxValuesL(TPIMField aField) +{ + JELOG2(EPim); + if (!iValidator.IsValidField(aField)) + { + User::Leave(KErrArgument); + } + + return iAdapterManager->MaxValues(aField); +} + +jint CPIMList::maxValues(TPIMField aField, JNIEnv* aJniEnv, + jintArray aError) +{ + JELOG2(EPim); + jint maxVal = 0; + TInt error = KErrNone; + TRAP(error, maxVal = MaxValuesL(aField)); + SetJavaErrorCode(aJniEnv, aError, error); + return maxVal; +} + +int CPIMList::StringArraySizeL(TPIMField aStringArrayField) +{ + JELOG2(EPim); + if (iValidator.FieldDataType(aStringArrayField) + != EPIMFieldStringArray) + { + User::Leave(KErrArgument); + } + + return iValidator.NumElementsL(aStringArrayField); +} + +int CPIMList::stringArraySize(TPIMField aStringArrayField, + jintArray aError, JNIEnv* aJniEnv) +{ + JELOG2(EPim); + TInt error = KErrNone; + int retVal = 0; + TRAP(error, retVal = StringArraySizeL(aStringArrayField)); + + SetJavaErrorCode(aJniEnv, aError, error); + return retVal; +} + +RPointerArray* CPIMList::UpdateListL(CPIMItem* aMatchingItem) +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + RPointerArray tempNewItems(KItemArrayGranularity); + CleanupClosePushL(tempNewItems); + + RPointerArray tempRemovedItems(KItemArrayGranularity); + CleanupClosePushL(tempRemovedItems); + + MPIMListAdapter::TExternalItemChangeClass changeClass = + iListAdapter->IsItemsExternallyModified(); + + if (changeClass == MPIMListAdapter::EExternalChangesNone) + { + // No changes, skip. + } + else if (changeClass == MPIMListAdapter::EExternalChangesMinor) + { + HandleMinorItemChangesL(tempNewItems, tempRemovedItems, + aMatchingItem); + } + else if (changeClass == MPIMListAdapter::EExternalChangesMajor) + { + HandleMajorItemChangesL(tempNewItems, tempRemovedItems, + aMatchingItem); + } + else + { + // Should never happen + User::Panic(KPIMPanicCategory, + EPIMPanicInvalidNativeChangeClass); + } + + UpdateCategoriesL(); + + // Return value + RPointerArray * newAndRemovedItems = + new(ELeave) RPointerArray (KItemArrayGranularity); + + // The array object needs to be pushed for deletion and closing + // separately. + CleanupStack::PushL(newAndRemovedItems); + CleanupClosePushL(*newAndRemovedItems); + + // Copy the pointers to new and removed items to the result array. + // First go the new items, then NULL, and then removed items. + + TInt i = 0; // reused iterator + const TInt numNewItems = tempNewItems.Count(); + for (i = 0; i < numNewItems; i++) + { + User::LeaveIfError(newAndRemovedItems->Append(tempNewItems[i])); + } + + // Add boundary element + User::LeaveIfError(newAndRemovedItems->Append(NULL)); + + const TInt numRemovedItems = tempRemovedItems.Count(); + for (i = 0; i < numRemovedItems; i++) + { + User::LeaveIfError(newAndRemovedItems->Append( + tempRemovedItems[i])); + } + + CleanupStack::Pop(); // newAndRemovedItems cleanup close + CleanupStack::Pop(newAndRemovedItems); + + CleanupStack::PopAndDestroy(); // tempRemovedItems cleanup close + CleanupStack::PopAndDestroy(); // tempNewItems cleanup close + + return newAndRemovedItems; +} + +TInt CPIMList::FindItemByItemID(const TPIMItemID& aItemId) +{ + JELOG2(EPim); + // Any RPointerArray.Find() variant cannot be used because we're + // matching with just an Item ID, not another item. + + const TInt n = iItems.Count(); + for (TInt i = 0; i < n; i++) + { + if (iItems[i]->GetId() == aItemId) + { + return i; + } + } + + return KErrNotFound; +} + +RPointerArray* CPIMList::SortAndConvertToCPIMItemsL( + TLinearOrder aOrder, + RPointerArray& aSourceArray) +{ + JELOG2(EPim); + aSourceArray.Sort(aOrder); + + RPointerArray * retArr = new(ELeave) RPointerArray< + CPIMItem> (KItemArrayGranularity); + + CleanupStack::PushL(retArr); + CleanupClosePushL(*retArr); + + const TInt n = aSourceArray.Count(); + for (TInt i = 0; i < n; i++) + { + User::LeaveIfError(retArr->Append( + static_cast(aSourceArray[i]))); + } + + CleanupStack::Pop(); // *retArr + CleanupStack::Pop(retArr); + return retArr; +} + +void CPIMList::HandleMinorItemChangesL( + RPointerArray& aTempNewItems, + RPointerArray& aTempRemovedItems, CPIMItem* aMatchingItem) +{ + JELOG2(EPim); + RPointerArray* itemChanges = + iListAdapter->GetExternalItemModificationsL(); + + __ASSERT_DEBUG(itemChanges, User::Panic(KPIMPanicCategory, + EPIMPanicUnexpectedNullExternalChangeList)); + + __ASSERT_ALWAYS(itemChanges, User::Leave(KErrCorrupt)); + + CleanupStack::PushL(itemChanges); + CleanupResetAndDestroyPushL(*itemChanges); + + const TInt n = itemChanges->Count(); + for (TInt i = 0; i < n; i++) + { + const CPIMItemStateChange& itemChange = *(*itemChanges)[i]; + switch (itemChange.ChangeType()) + { + case EPIMExternalChangeNew: + { + // Check that the item is really new and not e.g. resulting from + // our own action. + if (KErrNotFound == FindItemByItemID(itemChange.ItemID())) + { + HandleItemChangeNewL(itemChange.ItemID(), + aTempNewItems, aMatchingItem); + } + break; + } + + case EPIMExternalChangeModified: + { + // Find the index. The item should be present when this happens + const TInt itemIndex = + FindItemByItemID(itemChange.ItemID()); + + __ASSERT_DEBUG((itemIndex != -1), User::Panic( + KPIMPanicCategory, + EPIMPanicExternalChangeUpdatingNonExistentItem)); + + if (itemIndex == KErrNotFound) + { + User::Leave(KErrCorrupt); + } + + CPIMItem* item = iItems[itemIndex]; + HandleItemChangeModifiedL(*item); + + break; + } + + case EPIMExternalChangeRemoved: + { + // Find the item index by Item ID and remove only if it really + // is present (and not e.g. already removed by us). + const TInt itemIndex = + FindItemByItemID(itemChange.ItemID()); + if (itemIndex != KErrNotFound) + { + HandleItemChangeRemovedL(itemIndex, aTempRemovedItems); + } + break; + } + + default: + { + User::Panic(KPIMPanicCategory, + EPIMPanicInvalidNativeChangeType); + break; + } + } + } + + CleanupStack::PopAndDestroy(2, itemChanges); + + // Refresh any items that have been modified from the Java side. + // This is done after getting the removed and modified items above + // because we don't want to try to update removed or already + // updated items. However, if such situations occur, we just + // ignore them at this point and handle them during next update. + RefreshModifiedItemsL(); +} + +void CPIMList::HandleMajorItemChangesL( + RPointerArray& aTempNewItems, + RPointerArray& aTempRemovedItems, CPIMItem* aMatchingItem) +{ + JELOG2(EPim); + // The situation here is that the underlying list adapter + // cannot classify the changes occurred in the native database. + // We need to refresh the whole set of items and try to figure + // out which entries are new and which entries have been removed. + + // At the time of writing handles to both new and removed native + // side items are passed to the Java side in return. We want to + // do the major update so that it does not differ in any way for + // that mechanism. We also want to be memory-efficient rather than + // fast, so we'll reuse and refresh the existing items that still + // have their counterparts in the database and only create the + // truely new items. Removed entries will, of course, be removed + // from the framework, too. + + RPointerArray* itemChanges = + iListAdapter->GetExternalItemModificationsL(); + + __ASSERT_DEBUG(itemChanges, User::Panic(KPIMPanicCategory, + EPIMPanicUnexpectedNullExternalChangeList)); + + if (!itemChanges) + { + // No changes (although there should be some!) + User::Leave(KErrCorrupt); + } + + CleanupStack::PushL(itemChanges); + CleanupResetAndDestroyPushL(*itemChanges); + + // Iterate through the item list. Some of the items will be removed + // during the iteration, so the item count must be resolved on + // every iteration. + TInt itemIndex = 0; + while (itemIndex < iItems.Count()) + { + TPIMItemID itemId = iItems[itemIndex]->GetId(); + + TBool exists = EFalse; + + // Iterate through the change list. One of the changes may be + // removed during the iteration. + for (TInt changeIndex = 0; changeIndex < itemChanges->Count(); changeIndex++) + { + CPIMItemStateChange* itemChange = + (*itemChanges)[changeIndex]; + + const TPIMItemID changedItemId = itemChange->ItemID(); + + if (itemId == changedItemId) + { + // Exists already - update + exists = ETrue; + HandleItemChangeModifiedL(*(iItems[itemIndex])); + + // This change has been handled + delete itemChange; + itemChanges->Remove(changeIndex); + + // Next item + itemIndex++; + break; // out of the change loop + } + } + + if (!exists) + { + // The item didn't match a change entry - remove. + // The item index used in the loop must not be updated, because + // After the following operation the index will refer to the + // next item. + HandleItemChangeRemovedL(itemIndex, aTempRemovedItems); + } + } + + // Now the changes in the change list are truely new entries + for (TInt changeIndex = 0; changeIndex < itemChanges->Count(); changeIndex++) + { + const CPIMItemStateChange& itemChange = + *(*itemChanges)[changeIndex]; + + const TPIMItemID newItemId = itemChange.ItemID(); + HandleItemChangeNewL(newItemId, aTempNewItems, aMatchingItem); + } + + CleanupStack::PopAndDestroy(2, itemChanges); +} + +void CPIMList::HandleItemChangeNewL(const TPIMItemID aNewItemId, + RPointerArray& aTempNewItems, CPIMItem* aMatchingItem) +{ + JELOG2(EPim); + // Create new item + CPIMItem* newItem = NULL; + TRAPD(errCreateItem, newItem = DoCreateItemL(aNewItemId, + aMatchingItem)); + + if (errCreateItem == KErrNotFound) + { + // The item was not in the database. The case is + // probably that the item was created and removed + // after last update. Skip. + return; + } + else + { + User::LeaveIfError(errCreateItem); + } + + // OK + newItem->SetModified(EFalse); + CleanupStack::PushL(newItem); + User::LeaveIfError(iItems.Append(newItem)); + CleanupStack::Pop(newItem); + + // Add to list of new items + TInt errAddToNewItems = aTempNewItems.Append(newItem); + if (errAddToNewItems != KErrNone) + { + iItems.Remove(iItems.Find(newItem)); + delete newItem; + User::Leave(errAddToNewItems); + } +} + +void CPIMList::HandleItemChangeModifiedL(CPIMItem& aModifiedItem) +{ + JELOG2(EPim); + // Any Java-originated modifications will be discarded. + TRAPD(errUpdateItem, DoUpdateItemL(aModifiedItem)); + + if (errUpdateItem == KErrNotFound) + { + // The item was not in the database. The case is + // probably that the item was changed and removed + // after last update. Let's pretend that the item + // is up-to-date and skip it. + aModifiedItem.SetModified(EFalse); + return; + } + else + { + User::LeaveIfError(errUpdateItem); + } + + // OK + aModifiedItem.SetModified(EFalse); +} + +void CPIMList::HandleItemChangeRemovedL(TInt aRemovedItemIndex, + RPointerArray& aTempRemovedItems) +{ + JELOG2(EPim); + CPIMItem* removedItem = iItems[aRemovedItemIndex]; + + // Add to list of removed items + TInt errAddToRemovedItems = aTempRemovedItems.Append(removedItem); + + if (errAddToRemovedItems != KErrNone) + { + User::Leave(KErrCorrupt); + } + + // Remove from item list and remove adapter association + iItems.Remove(aRemovedItemIndex); + removedItem->RemoveAdapterAssociation(); +} + +void CPIMList::RefreshModifiedItemsL() +{ + JELOG2(EPim); + const TInt numItems = iItems.Count(); + for (TInt itemIndex = 0; itemIndex < numItems; itemIndex++) + { + CPIMItem& item = *(iItems[itemIndex]); + if (item.isModified()) + { + TRAPD(errUpdate, DoUpdateItemL(item)); + + // If the update succeeds, we're OK. If the entry is not + // found, we'll pretend that the entry is present until + // next update (or failing commit). In other error, + // leave. + if (errUpdate == KErrNone || errUpdate == KErrNotFound) + { + item.SetModified(EFalse); + } + else + { + User::Leave(errUpdate); + } + } + } +} + +void CPIMList::UpdateCategoriesL() +{ + JELOG2(EPim); + if (!iListAdapter) + { + User::Leave(KErrSessionClosed); + } + + RPointerArray* categoryChanges = + iListAdapter->GetExternalCategoryModificationsL(); + + if (!categoryChanges) + { + // Nothing to do + return; + } + + CleanupStack::PushL(categoryChanges); + CleanupResetAndDestroyPushL(*categoryChanges); + + const TInt n = categoryChanges->Count(); + for (TInt i = 0; i < n; i++) + { + const CPIMCategoryStateChange& change = *(*categoryChanges)[i]; + const TPIMExternalChangeType changeType = change.ChangeType(); + + switch (changeType) + { + case EPIMExternalChangeNew: + { + // Nothing to do. + break; + } + + case EPIMExternalChangeModified: + { + // Update items + const TInt numItems = iItems.Count(); + for (TInt itemIndex = 0; itemIndex < numItems; itemIndex++) + { + iItems[itemIndex]->CategoryRenamedL(change.Category(), + change.NewCategoryNameL()); + } + break; + } + + case EPIMExternalChangeRemoved: + { + // Update items + const TInt numItems = iItems.Count(); + for (TInt itemIndex = 0; itemIndex < numItems; itemIndex++) + { + iItems[itemIndex]->CategoryDeleted(change.Category()); + } + break; + } + + default: + { + // Should never happen + User::Panic(KPIMPanicCategory, + EPIMPanicInvalidNativeChangeClass); + } + } + } + + CleanupStack::PopAndDestroy(); // categoryChanges cleanup close + CleanupStack::PopAndDestroy(categoryChanges); +} + +RPointerArray* CPIMList::ResolveUnassignedItemsL( + RPointerArray& aSearchedItems) +{ + JELOG2(EPim); + RPointerArray* unassignedItems = new(ELeave) RPointerArray< + CPIMItem> (KItemArrayGranularity); + + CleanupStack::PushL(unassignedItems); + CleanupClosePushL(*unassignedItems); + + // Find out the items that do not belong to any category and add + // them to the returned array. + const TInt n = aSearchedItems.Count(); + for (TInt i = 0; i < n; i++) + { + if (aSearchedItems[i]->GetCategoriesL().Count() == 0) + { + CPIMItem* unassignedItem = aSearchedItems[i]; + User::LeaveIfError(unassignedItems->Append(unassignedItem)); + } + } + + CleanupStack::Pop(); // unassignedItems cleanup close + CleanupStack::Pop(unassignedItems); + return unassignedItems; +} + +jintArray CPIMList::updateList(JNIEnv* aJniEnv, + pimbaseitem* aMatchingItem) +{ + JELOG2(EPim); + TInt error = KErrNone; + jintArray itemHandles = NULL; + TRAP(error, + { + CPIMItem* matchingItem = static_cast(aMatchingItem); + RPointerArray< CPIMItem>* newAndRemovedItems = + UpdateListL(matchingItem); + // We now own the array + CleanupStack::PushL(newAndRemovedItems); + CleanupClosePushL(*newAndRemovedItems); + const TInt numItems = newAndRemovedItems->Count(); + + // Make a temporary integer array which we can push to cleanup stack + TInt* tempElems = new(ELeave) TInt[ numItems ]; + CleanupArrayDeletePushL(tempElems); + + // Iterate through the item array in two parts. + // First make and set handles to the new items + // then get the handles of the removed items. + // Put all handles to the result Java int array, separated by + // a zero. + TInt i = 0; // reused iterator + + + for (i = 0; i < numItems; i++) + { + pimbaseitem* item = (*newAndRemovedItems)[ i ]; + + if (!item) + { + // Reached the boundary; the next item is the first + // removed item. Index variable is reused. + break; + } + + TInt newItemHandle = reinterpret_cast(item); + + tempElems[ i ] = newItemHandle; + } + + // The boundary element + tempElems[ i++ ] = 0; // boundary mark + + // For each removed item, get its JNI handle and append it to the + // result array. + for (/* reuse i with its current value */; i < numItems; i++) + { + pimbaseitem* item = (*newAndRemovedItems)[ i ]; + tempElems[ i ] = reinterpret_cast(item); + } + + // Create the result (Java) array and copy the handles in it + itemHandles = aJniEnv->NewIntArray(numItems); + if (!itemHandles) + { + User::Leave(KErrNoMemory); // codescanner::leave + } + + aJniEnv->SetIntArrayRegion(itemHandles, 0, numItems, tempElems); + CleanupStack::PopAndDestroy(tempElems); + + CleanupStack::Pop(); // newAndRemovedItems cleanup close + CleanupStack::PopAndDestroy(newAndRemovedItems); + + } + ); + if (error != KErrNone) + throw error; + return itemHandles; +} + +// End of File