--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/javaextensions/pim/framework/src.s60/cpimlist.cpp Mon May 03 12:27:20 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<CPIMItem*>(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<CPIMItem*>(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<CPIMItem>* items = NULL;
+
+ if (aEnumerationType == EPIMItemAll)
+ {
+ items = ItemsL();
+ }
+ else if (aEnumerationType == EPIMItemMatchingItem)
+ {
+ pimbaseitem * baseMatchingItem =
+ reinterpret_cast<pimbaseitem *>(aMatchingItemHandle);
+ CPIMItem* matchingItem =
+ static_cast<CPIMItem *>(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<CPIMItem>* CPIMList::ItemsL()
+{
+ JELOG2(EPim);
+ if (!iListAdapter)
+ {
+ User::Leave(KErrSessionClosed);
+ }
+
+ // Put all items in an array as MPIMItemData objects
+ RPointerArray<MPIMItem> 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<MPIMItem> order(iAdapterManager->ItemOrder());
+
+ RPointerArray<CPIMItem>* retArr = SortAndConvertToCPIMItemsL(order,
+ tempArr);
+
+ CleanupStack::PopAndDestroy(); // tempArr cleanup close
+ return retArr;
+}
+
+RPointerArray<CPIMItem>* CPIMList::ItemsL(const CPIMItem& aMatchingItem)
+{
+ JELOG2(EPim);
+ if (!iListAdapter)
+ {
+ User::Leave(KErrSessionClosed);
+ }
+
+ // Matching items are inserted temporarily into this array for sorting
+ RPointerArray<MPIMItem> 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<MPIMItem> order(iAdapterManager->ItemOrder());
+
+ RPointerArray<CPIMItem>* retArr = SortAndConvertToCPIMItemsL(order,
+ tempArr);
+
+ CleanupStack::PopAndDestroy(); // tempArr cleanup close
+ return retArr;
+}
+
+RPointerArray<CPIMItem>* CPIMList::ItemsL(const TDesC& aMatchingValue)
+{
+ JELOG2(EPim);
+ if (!iListAdapter)
+ {
+ User::Leave(KErrSessionClosed);
+ }
+
+ // Matching items are inserted temporarily into this array for sorting
+ RPointerArray<MPIMItem> 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<MPIMItem> order(iAdapterManager->ItemOrder());
+
+ RPointerArray<CPIMItem>* retArr = SortAndConvertToCPIMItemsL(order,
+ tempArr);
+
+ CleanupStack::PopAndDestroy(); // tempArr cleanup close
+ return retArr;
+}
+
+RPointerArray<CPIMItem>* CPIMList::ItemsByCategoryL(
+ const TDesC* aCategory)
+{
+ JELOG2(EPim);
+ if (!iListAdapter)
+ {
+ User::Leave(KErrSessionClosed);
+ }
+
+ // Matching items are inserted temporarily into this array for sorting
+ RPointerArray<MPIMItem> 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<MPIMItem> order(iAdapterManager->ItemOrder());
+
+ RPointerArray<CPIMItem>* 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<const TDesC*>(&nativeCategory))));
+ SetJavaErrorCode(aJniEnv, aError, error);
+ return static_cast<jboolean>(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<const TDesC*>(&category))));
+ SetJavaErrorCode(aJniEnv, aError, error);
+}
+
+RPointerArray<CPIMItem>* 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<CPIMItem>* unassignedItems = NULL;
+
+ if (categoryExists)
+ {
+ RPointerArray<CPIMItem> 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<CPIMItem>* unassignedItems = NULL;
+ const JStringUtils category(*aJniEnv, aCategory);
+ TInt error = KErrNone;
+ TRAP(error, unassignedItems = DeleteCategoryL(
+ *(static_cast<const TDesC*>(&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<const TDesC*>(¤tCategory)),
+ *(static_cast<const TDesC*>(&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<jboolean>(isSupportedField);
+}
+
+jintArray CPIMList::getSupportedFields(JNIEnv* aJniEnv)
+{
+ JELOG2(EPim);
+ const CArrayFix<TPIMField>* 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<jboolean>(isSupportedAttribute);
+}
+
+const CArrayFix<TPIMAttribute>& 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<TPIMAttribute>* 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<jboolean>(retVal);
+}
+
+const CArrayFix<TPIMArrayElement>& 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<TPIMArrayElement>* 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<CPIMItem>* CPIMList::UpdateListL(CPIMItem* aMatchingItem)
+{
+ JELOG2(EPim);
+ if (!iListAdapter)
+ {
+ User::Leave(KErrSessionClosed);
+ }
+
+ RPointerArray<CPIMItem> tempNewItems(KItemArrayGranularity);
+ CleanupClosePushL(tempNewItems);
+
+ RPointerArray<CPIMItem> 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<CPIMItem> * newAndRemovedItems =
+ new(ELeave) RPointerArray<CPIMItem> (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<CPIMItem>* CPIMList::SortAndConvertToCPIMItemsL(
+ TLinearOrder<MPIMItem> aOrder,
+ RPointerArray<MPIMItem>& aSourceArray)
+{
+ JELOG2(EPim);
+ aSourceArray.Sort(aOrder);
+
+ RPointerArray<CPIMItem> * 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<CPIMItem*>(aSourceArray[i])));
+ }
+
+ CleanupStack::Pop(); // *retArr
+ CleanupStack::Pop(retArr);
+ return retArr;
+}
+
+void CPIMList::HandleMinorItemChangesL(
+ RPointerArray<CPIMItem>& aTempNewItems,
+ RPointerArray<CPIMItem>& aTempRemovedItems, CPIMItem* aMatchingItem)
+{
+ JELOG2(EPim);
+ RPointerArray<CPIMItemStateChange>* 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<CPIMItem>& aTempNewItems,
+ RPointerArray<CPIMItem>& 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<CPIMItemStateChange>* 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<CPIMItem>& 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<CPIMItem>& 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<CPIMCategoryStateChange>* 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<CPIMItem>* CPIMList::ResolveUnassignedItemsL(
+ RPointerArray<CPIMItem>& aSearchedItems)
+{
+ JELOG2(EPim);
+ RPointerArray<CPIMItem>* 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<CPIMItem*>(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<int>(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<int>(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