javaextensions/pim/javasrc/com/nokia/mj/impl/pim/PIMListImpl.java
branchRCL_3
changeset 19 04becd199f91
child 67 63b81d807542
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javaextensions/pim/javasrc/com/nokia/mj/impl/pim/PIMListImpl.java	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,1029 @@
+/*
+* 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:  PIM list implementation base.
+ *
+*/
+
+
+// PACKAGE
+package com.nokia.mj.impl.pim;
+
+// IMPORTS
+
+import javax.microedition.pim.FieldFullException;
+import javax.microedition.pim.PIM;
+import javax.microedition.pim.PIMException;
+import javax.microedition.pim.PIMItem;
+import javax.microedition.pim.PIMList;
+import javax.microedition.pim.UnsupportedFieldException;
+import java.util.Enumeration;
+import java.util.Date;
+import java.util.Vector;
+import com.nokia.mj.impl.pim.ErrorString;
+import com.nokia.mj.impl.pim.GenericException;
+import com.nokia.mj.impl.rt.support.ApplicationUtils;
+import com.nokia.mj.impl.rt.support.Finalizer;
+import com.nokia.mj.impl.rt.support.ShutdownListener;
+import com.nokia.mj.impl.pim.utils.NativeError;
+import com.nokia.mj.impl.utils.Logger;
+
+// CLASS DEFINITION
+/**
+ * Base class for specialized PIMList classes.
+ *
+ * @par About item creation: New PIMItem-derived classes are created during list
+ *      update. The list is updated whenever items are enumerated with one of
+ *      the items() methods. During the update native-originated changes are
+ *      synchronized. When items are enumerated for the first time from the
+ *      list, whole content of the corresponding native database is considered
+ *      as new items and are created at that time. See updateList() method.
+ */
+public abstract class PIMListImpl implements PIMList
+{
+    // Constants
+
+    /** Label type Field. */
+    private static final int LABEL_TYPE_FIELD = 1;
+
+    /** Label type Attribute. */
+    private static final int LABEL_TYPE_ATTRIBUTE = 2;
+
+    /** Label type Array Element. */
+    private static final int LABEL_TYPE_ARRAY_ELEMENT = 3;
+
+    /** Enumeration flag: all items. See items(). */
+    protected final static int ENUMERATION_ITEMS_ALL = 0;
+
+    /** Enumeration flag: items matching given item. See items(). */
+    protected final static int ENUMERATION_ITEMS_MATCHING_ITEM = 1;
+
+    /** Enumeration flag: items matching given string. See items(). */
+    protected final static int ENUMERATION_ITEMS_MATCHING_STRING = 2;
+
+    /** Enumeration flag: items matching given category. See items(). */
+    protected final static int ENUMERATION_ITEMS_MATCHING_CATEGORY = 3;
+
+    // Member data
+    private Finalizer iFinalizer;
+
+    /** JNI native handle to the native side peer object. */
+    protected int iListHandle;
+
+    /**
+     * Items.
+     */
+    protected ItemTable iItems;
+
+    /**
+     * Access mode, one of:
+     *
+     * @li PIM.READ_ONLY
+     * @li PIM.WRITE_ONLY
+     * @li PIM.READ_WRITE.
+     */
+    private int iMode = 0;
+
+    /**
+     * State flag, open or closed. New instance of PIMListImpl is always
+     * initially open.
+     */
+    private boolean iIsOpen = true;
+
+    // Constructors
+
+    /**
+     * Initializes the list with native side Event Source and list peer object
+     * handles and registers the object for finalization.
+     *
+     * Given mode applies to all security vulnerable operations.
+     */
+    PIMListImpl(int aListHandle, int aMode)
+    {
+        iListHandle = aListHandle;
+        Logger.LOG(Logger.EPim,Logger.EInfo,"+PIMListImpl() = iListHandle = "+iListHandle);
+        setShutdownListener();
+        iFinalizer = registerForFinalization();
+        iMode = aMode;
+
+        iItems = new ItemTable();
+    }
+
+    public Finalizer registerForFinalization()
+    {
+        return new Finalizer()
+        {
+            public void finalizeImpl()
+            {
+
+                doFinalize();
+
+            }
+        };
+
+    }
+
+    void doFinalize()
+    {
+        if (iFinalizer == null)
+        {
+            return;
+        }
+        iFinalizer = null;
+
+        if (iListHandle != 0)
+        {
+            _dispose(iListHandle);
+            iListHandle = 0;
+        }
+    }
+
+    /**
+     * Registers for shutdown listener
+     */
+    private void setShutdownListener()
+    {
+        // Get the insatnce of ApplicationUtils.
+        ApplicationUtils appUtils = ApplicationUtils.getInstance();
+
+        // Get the name of the application.
+        appUtils.addShutdownListener(new ShutdownListener()
+        {
+            public void shuttingDown()
+            {
+                if (iListHandle != 0)
+                {
+                    _dispose(iListHandle);
+                    iListHandle = 0;
+                }
+            }
+        });
+    }
+
+    // New methods
+
+    /**
+     * Gets the list type. Used for resolving permissions.
+     */
+    abstract int getListType();
+
+    /**
+     * Determines whether this list is still open or has it been closed.
+     */
+    boolean isOpen()
+    {
+        return iIsOpen;
+    }
+
+    /**
+     * Items that are associated with this list call this method when they are
+     * committed. When an item is committed it is checked whether belongs to the
+     * list already and added if not. If the item has been recently created and
+     * never committed, it does not yet belong to the list.
+     */
+    void itemCommitted(PIMItemImpl aCommittedItem)
+    {
+        if (!iItems.containsItem(aCommittedItem))
+        {
+            int committedItemHandle = aCommittedItem.jniNativeHandle();
+
+            int error = _addCommittedItem(iListHandle, committedItemHandle);
+            if (!NativeError.checkSuccess(error))
+            {
+                throw new GenericException(ErrorString.GENERAL_ERROR_COLON + error);
+            }
+
+            iItems.addItem(aCommittedItem);
+        }
+    }
+
+    /**
+     * Creates a new specialized item and associates it with the list. This is
+     * the common implementation of createXyz() methods in specialized lists.
+     * The polymorphic behaviour is implemented in the native side.
+     */
+    protected PIMItemImpl createItem()
+    {
+        int[] err = new int[1];
+        int itemHandle = _createItem(iListHandle, err);
+        if (!NativeError.checkSuccess(err[0]))
+        {
+            throw new GenericException(ErrorString.GENERAL_ERROR_COLON + err[0]);
+        }
+
+        PIMItemImpl item = createAssociatedItem(itemHandle);
+        return item;
+    }
+
+    protected void removeItem(PIMItem aItem) throws PIMException
+    {
+        if (aItem == null)
+        {
+            throw new NullPointerException(ErrorString.ITEM_IS_NULL);
+        }
+
+        if (!(aItem instanceof PIMItemImpl))
+        {
+            throw new PIMException(ErrorString.REMOVAL_FAILED_COLON +
+                                   ErrorString.INVALID_ITEM,
+                                   PIMException.GENERAL_ERROR);
+        }
+
+        // Check that list is not closed
+        if (!isOpen())
+        {
+            throw new PIMException(ErrorString.REMOVAL_FAILED_COLON +
+                                   ErrorString.LIST_IS_CLOSED, PIMException.LIST_CLOSED);
+        }
+
+        PIMItemImpl item = (PIMItemImpl) aItem;
+
+        if (!iItems.containsItem(item))
+        {
+            throw new PIMException(ErrorString.REMOVAL_FAILED_COLON +
+                                   ErrorString.ITEM_IS_NOT_IN_THE_LIST,
+                                   PIMException.GENERAL_ERROR);
+        }
+        ApplicationUtils appUtils = ApplicationUtils.getInstance();
+        String action = null;
+        switch (getListType())
+        {
+        case PIM.CONTACT_LIST:
+            action = PIMPermissionImpl.ACTION_WRITE_CONTACTS;
+            break;
+        case PIM.EVENT_LIST:
+            action = PIMPermissionImpl.ACTION_WRITE_EVENTS;
+            break;
+        case PIM.TODO_LIST:
+            action = PIMPermissionImpl.ACTION_WRITE_TODOS;
+            break;
+        }
+
+        // Get localized text info for the security dialog
+        PIMPermissionImpl per = new PIMPermissionImpl(action, item.getShortInfo(), getName(), PIMPermissionImpl.OPERATION_ITEM_DELETION);
+
+        // Ensure permission from PIM API security
+        appUtils.checkPermission(per);
+
+
+        // First remove the native side item and only if that
+        // succeeds, the java side item.
+
+        int itemHandle = item.jniNativeHandle();
+        int err = _removeItem(iListHandle, itemHandle);
+        NativeError.handleRemoveItemError(err);
+
+        PIMItemImpl removedItem = iItems.removeItem(item);
+
+        if (removedItem != null) // shouldn't be null
+        {
+            removedItem.removeListAssociation();
+        }
+    }
+
+    /**
+     * Adds or sets new value for the REVISION field. This function is used when
+     * item is imported and after an import it should countain a new REVISION
+     * field
+     *
+     * @param aItem
+     *            Item to which the field is to be set
+     * @param aField
+     *            A specific REVISION field
+     * @param aAttributes
+     *            Attributes if any
+     */
+    protected void updateRevisionField(PIMItem aItem, int aField,
+                                       int aAttributes)
+    {
+        // Get the associated list of this item
+        PIMList list = aItem.getPIMList();
+
+        // If the field is supported it can be added
+        if (list.isSupportedField(aField))
+        {
+            Date thisDate = new Date();
+            try
+            {
+                // Check that if there is a value already
+                if (aItem.countValues(aField) > 0)
+                {
+                    aItem.setDate(aField, 0, aAttributes, thisDate.getTime());
+                }
+                else
+                {
+                    aItem.addDate(aField, aAttributes, thisDate.getTime());
+                }
+            }
+            catch (UnsupportedFieldException ufe)
+            {
+                // Ignore; the value is not supported in the list
+                // this item belongs to. This exception should not
+                // be thrown but we still have to catch it
+            }
+            catch (FieldFullException ffe)
+            {
+                // Ignore; no more than supported maximum number
+                // of values can be added. This exception should
+                // not be thrown but we still have catch it
+            }
+        }
+    }
+
+    // Methods from PIMList
+
+    public synchronized String getName()
+    {
+        String name = _getName(iListHandle);
+
+        if (name == null)
+        {
+            throw new java.lang.OutOfMemoryError();
+        }
+
+        return name;
+    }
+
+    public synchronized void close() throws PIMException
+    {
+        if (iIsOpen == true)
+        {
+            iIsOpen = false;
+            int err = _close(iListHandle);
+            NativeError.handlePIMListCloseError(err);
+            iItems = null;
+        }
+        else
+        {
+            throw new PIMException(ErrorString.LIST_IS_ALREADY_CLOSED,
+                                   PIMException.LIST_CLOSED);
+        }
+    }
+
+    public synchronized Enumeration items() throws PIMException
+    {
+        // security checks must be done here because of TCK
+        ensurePimPermission(PIM.READ_ONLY);
+        return doItems(ENUMERATION_ITEMS_ALL, 0, null);
+    }
+
+    /**
+     * @par Notes: The given item must originate from this list, which is
+     *      required in the description of this method in the PIM API spec.
+     *      Ambiguously, the ContactList, EventList and ToDoList permit the item
+     *      to be basically any item that implements the Contact, Event or ToDo
+     *      interface, respectively. However we will live up to the tighter
+     *      restrictions - for which the TCK seems to test, too.
+     */
+    public synchronized Enumeration items(PIMItem aMatchingItem)
+    throws PIMException
+    {
+        // security checks must be done here because of TCK
+        ensurePimPermission(PIM.READ_ONLY);
+
+        if (aMatchingItem == null)
+        {
+            throw new NullPointerException(ErrorString.ITEM_IS_NULL);
+        }
+
+        if (!(aMatchingItem instanceof PIMItemImpl))
+        {
+            throw new PIMException(ErrorString.INVALID_ITEM,
+                                   PIMException.GENERAL_ERROR);
+        }
+
+        if (aMatchingItem.getPIMList() != this)
+        {
+            throw new IllegalArgumentException(
+                ErrorString.ITEM_DOES_NOT_ORIGINATE_FROM_THIS_LIST);
+        }
+
+        PIMItemImpl matchingItemImpl = (PIMItemImpl) aMatchingItem;
+        Enumeration items = null;
+
+        // The matching item is synchronized because, in some cases, the garbage
+        // collector may collect the item before the native operation gets
+        // executed in the JNI context. This synchronization prevents the GC to
+        // collect
+        // the object before the operation gets fully executed. Note that
+        // without
+        // the synchronization, aMatchingItem would be available for the GC
+        // after
+        // matchingItemImpl.jniNativeHandle() function call and because the
+        // handle
+        // is still used, it may cause that null is referenced in the native
+        // side
+        synchronized (matchingItemImpl)
+        {
+            int matchingItemHandle = matchingItemImpl.jniNativeHandle();
+            items = doItems(ENUMERATION_ITEMS_MATCHING_ITEM,
+                            matchingItemHandle, null);
+        }
+
+        return items;
+    }
+
+    public synchronized Enumeration items(String aMatchingValue)
+    throws PIMException
+    {
+        // Ensure security permission
+        ensurePimPermission(PIM.READ_ONLY);
+
+        if (aMatchingValue == null)
+        {
+            throw new NullPointerException(ErrorString.SEARCHING_FAILED_COLON +
+                                           ErrorString.STRING_VALUE_IS_NULL);
+        }
+
+        return doItems(ENUMERATION_ITEMS_MATCHING_STRING, 0, aMatchingValue);
+    }
+
+    public synchronized Enumeration itemsByCategory(String aCategory)
+    throws PIMException
+    {
+        // aCategory may be null (PIMList.UNCATEGORIZED, which is null)
+        // Ensure security permission
+        ensurePimPermission(PIM.READ_ONLY);
+
+        return doItems(ENUMERATION_ITEMS_MATCHING_CATEGORY, 0, aCategory);
+    }
+
+    public synchronized String[] getCategories() throws PIMException
+    {
+        int[] error = new int[1];
+
+        String[] categories = _getCategories(iListHandle, error);
+
+        NativeError.handleCategoryHandlingError(error[0]);
+        return categories;
+    }
+
+    public synchronized boolean isCategory(String aCategory)
+    throws PIMException
+    {
+        if (aCategory == null)
+        {
+            throw new NullPointerException(ErrorString.CATEGORY_MISSING);
+        }
+
+        int[] error = new int[1];
+
+        boolean isCategory = _isCategory(iListHandle, aCategory, error);
+
+        NativeError.handleCategoryHandlingError(error[0]);
+        return isCategory;
+    }
+
+    public synchronized void addCategory(String aCategory) throws PIMException
+    {
+        // Ensure security permission
+        ensurePimPermission(PIM.WRITE_ONLY);
+
+        if (aCategory == null)
+        {
+            throw new NullPointerException(ErrorString.CATEGORY_IS_NULL);
+        }
+
+        int[] error = new int[1];
+
+        _addCategory(iListHandle, aCategory, error);
+
+        NativeError.handleCategoryHandlingError(error[0], aCategory);
+    }
+
+    public synchronized void deleteCategory(String aCategory,
+                                            boolean aDeleteUnassignedItems) throws PIMException
+    {
+        checkModeMinimum(PIM.WRITE_ONLY);
+        if (aCategory == null)
+        {
+            throw new NullPointerException(ErrorString.CATEGORY_IS_NULL);
+        }
+        ApplicationUtils appUtils = ApplicationUtils.getInstance();
+
+        // Get localized text info for the security dialog
+        String action = null;
+        switch (getListType())
+        {
+        case PIM.CONTACT_LIST:
+            action = PIMPermissionImpl.ACTION_WRITE_CONTACTS;
+            break;
+        case PIM.EVENT_LIST:
+            action = PIMPermissionImpl.ACTION_WRITE_EVENTS;
+            break;
+        case PIM.TODO_LIST:
+            action = PIMPermissionImpl.ACTION_WRITE_TODOS;
+            break;
+        }
+
+        PIMPermissionImpl per = new PIMPermissionImpl(action, aCategory, getName(), PIMPermissionImpl.OPERATION_CATEGORY_DELETION);
+
+        // Ensure permission from PIM API security
+        appUtils.checkPermission(per);
+
+        int[] error = new int[1];
+
+        // Returns items that were assigned to the deleted category
+        // but after that do not belong to any category
+        int[] unassignedItemHandles = _deleteCategory(iListHandle, aCategory, error);
+
+        NativeError.handleCategoryHandlingError(error[0], aCategory);
+
+        // If deleting unassigned items was requested, delete them
+        // one by one (if any present)
+        if (aDeleteUnassignedItems && (unassignedItemHandles != null))
+        {
+            for (int i = 0; i < unassignedItemHandles.length; i++)
+            {
+                int itemHandle = unassignedItemHandles[i];
+                PIMItemImpl item = iItems.getItem(itemHandle);
+                if (item != null)
+                {
+                    // pops up the deletion confirmation dialogue, too
+                    removeItem(item);
+                }
+            }
+        }
+    }
+
+    public synchronized void renameCategory(String aCurrentCategory,
+                                            String aNewCategory) throws PIMException
+    {
+        // Ensure security permission
+        ensurePimPermission(PIM.WRITE_ONLY);
+
+        if (aCurrentCategory == null || aNewCategory == null)
+        {
+            throw new NullPointerException(ErrorString.CATEGORY_IS_NULL);
+        }
+
+        int error = _renameCategory(iListHandle, aCurrentCategory, aNewCategory);
+
+        if (NativeError.checkArgumentError(error))
+        {
+            // We don't know which argument is invalid
+            NativeError.handleCategoryHandlingError(error);
+        }
+        else
+        {
+            NativeError.handleCategoryHandlingError(error, aCurrentCategory);
+        }
+    }
+
+    public synchronized int maxCategories()
+    {
+        return _maxCategories(iListHandle);
+    }
+
+    public synchronized boolean isSupportedField(int aField)
+    {
+        return _isSupportedField(iListHandle, aField);
+    }
+
+    public synchronized int[] getSupportedFields()
+    {
+        Logger.LOG(Logger.EPim,Logger.EInfo,"+getSupportedFields() = iListHandle = "+iListHandle);
+        int[] supportedFields = _getSupportedFields(iListHandle);
+
+        if (supportedFields == null)
+        {
+            throw new OutOfMemoryError();
+        }
+
+        return supportedFields;
+    }
+
+    public synchronized boolean isSupportedAttribute(int aField, int aAttribute)
+    {
+        return _isSupportedAttribute(iListHandle, aField, aAttribute);
+    }
+
+    public synchronized int[] getSupportedAttributes(int aField)
+    {
+        int[] error = new int[1];
+
+        int[] supportedAttributes = _getSupportedAttributes(iListHandle, aField,
+                                    error);
+
+        NativeError.handleFieldHandlingError(error[0], aField);
+        return supportedAttributes;
+    }
+
+    public synchronized boolean isSupportedArrayElement(int aStringArrayField,
+            int aArrayElement)
+    {
+        return _isSupportedArrayElement(iListHandle, aStringArrayField, aArrayElement);
+    }
+
+    public synchronized int[] getSupportedArrayElements(int aStringArrayField)
+    {
+        int[] error = new int[1];
+
+        int[] supportedArrayElements = _getSupportedArrayElements(iListHandle,
+                                       aStringArrayField, error);
+
+        NativeError.handleFieldHandlingError(error[0], aStringArrayField);
+        return supportedArrayElements;
+    }
+
+    public synchronized int getFieldDataType(int aField)
+    {
+        int[] error = new int[1];
+
+        int fieldDataType = _getFieldDataType(iListHandle, aField, error);
+
+        NativeError.handleFieldHandlingError(error[0], aField);
+        return fieldDataType;
+    }
+
+    public synchronized String getFieldLabel(int aField)
+    {
+        int[] labelSpec = new int[1];
+        labelSpec[0] = aField;
+
+        int[] error = new int[1];
+
+        String label = _getLabel(iListHandle, LABEL_TYPE_FIELD, labelSpec, error);
+
+        NativeError.handleFieldHandlingError(error[0], aField);
+        return label;
+    }
+
+    public synchronized String getAttributeLabel(int aAttribute)
+    {
+        int[] labelSpec = new int[1];
+        labelSpec[0] = aAttribute;
+
+        int[] error = new int[1];
+
+        String label = _getLabel(iListHandle, LABEL_TYPE_ATTRIBUTE, labelSpec, error);
+
+        // Note that we use unusual exception descriptions here.
+        NativeError.handlegetAttributeLabelError(error[0], aAttribute);
+
+        return label;
+    }
+
+    public synchronized String getArrayElementLabel(int aStringArrayField,
+            int aArrayElement)
+    {
+        int[] labelSpec = new int[2];
+        labelSpec[0] = aStringArrayField;
+        labelSpec[1] = aArrayElement;
+
+        int[] error = new int[1];
+
+        String label = _getLabel(
+
+                           iListHandle, LABEL_TYPE_ARRAY_ELEMENT, labelSpec, error);
+
+        // Note that we use unusual exception descriptions here.
+        NativeError.handleArrayElementLabelError(error[0]);
+
+        return label;
+    }
+
+    public synchronized int maxValues(int aField)
+    {
+        int[] error = new int[1];
+
+        int maxValues = _maxValues(iListHandle, aField, error);
+
+        NativeError.handleFieldHandlingError(error[0], aField);
+        return maxValues;
+    }
+
+    public synchronized int stringArraySize(int aStringArrayField)
+    {
+        int[] error = new int[1];
+
+        int stringArraySize = _stringArraySize(iListHandle, aStringArrayField,
+                                               error);
+
+        NativeError.handleFieldHandlingError(error[0], aStringArrayField);
+        return stringArraySize;
+    }
+
+    // New methods
+
+    /**
+     * Updates list of items according to the changes in the native database.
+     * Changes in items and categories are reflected appropriately.
+     *
+     * @par aMatchingItemHandle Handle to matching item if any
+     *
+     * @par NOTES: The handle to a matching item provides information that which
+     *      fields must be loaded from the items if the list supports partial
+     *      item loading. This is done because of the performance improvement of
+     *      item handling with large databases
+     *
+     * @par About errors: Generally all errors are fatal during the update,
+     *      because in case of out-of-memory situation the list is probably
+     *      corrupt and it would be dangerous to continue execution.
+     */
+    protected void updateList(int aMatchingItemHandle) throws PIMException
+    {
+        int[] error = new int[1];
+
+        int[] newAndRemovedItemHandles = _updateList(
+
+                                             iListHandle, aMatchingItemHandle, error);
+        NativeError.handleUpdateListError(error[0]);
+
+        if (newAndRemovedItemHandles != null) // shouldn't be null
+        {
+            int removedItemsStart = 0;
+
+            // Create and add new items and find the start of removed
+            // item handles.
+            for (int i = 0; i < newAndRemovedItemHandles.length; i++)
+            {
+                if (newAndRemovedItemHandles[i] != 0)
+                {
+                    int itemHandle = newAndRemovedItemHandles[i];
+                    PIMItemImpl newItem = createAssociatedItem(itemHandle);
+                    iItems.addItem(newItem);
+                }
+                else
+                {
+                    // boundary element; start of removed item handles
+                    removedItemsStart = i + 1;
+                    break;
+                }
+            }
+
+            // Remove removed items
+            // Loop from the removed items start to the end of the list.
+            // May result to zero iterations.
+            for (int i = removedItemsStart; i < newAndRemovedItemHandles.length; i++)
+            {
+                int itemHandle = newAndRemovedItemHandles[i];
+                PIMItemImpl item = iItems.removeItem(itemHandle);
+
+                if (item != null) // shouldn't be null
+                {
+                    item.removeListAssociation();
+                }
+            }
+        }
+    }
+
+    /**
+     * Common implementation of items() calls.
+     *
+     * This is a binary size optimization of the four different PIMList.items()
+     * calls, reducing the number of necessary JNI functions from four to one.
+     *
+     * Optimization adds complexity to the operation signature. Enumeration type
+     * and associated matching arguments must be given.
+     *
+     * @param aEnumerationType
+     *            Enumeration type, one of: ENUMERATION_ITEMS_ALL,
+     *            ENUMERATION_ITEMS_MATCHING_ITEM,
+     *            ENUMERATION_ITEMS_MATCHING_STRING or
+     *            ENUMERATION_ITEMS_MATCHING_CATEGORY.
+     *
+     * @param aMatchingItemHandle
+     *            Handle to matching item. Used with
+     *            ENUMERATION_ITEMS_MATCHING_ITEM; ignored otherwise.
+     *
+     * @param aStringArg
+     *            String argument. Used with ENUMERATION_ITEMS_MATCHING_STRING
+     *            and ENUMERATION_ITEMS_MATCHING_CATEGORY; ignored otherwise.
+     *            May be null for category matching.
+     *
+     * Arguments must be pre-checked.
+     */
+    protected Enumeration doItems(int aEnumerationType,
+                                  int aMatchingItemHandle, String aStringArg) throws PIMException
+    {
+        updateList(aMatchingItemHandle);
+        int[] error = new int[1];
+
+        int[] itemHandles = _items(iListHandle, aEnumerationType,
+                                   aMatchingItemHandle, // used for item matching
+                                   aStringArg, // used for value or category matching
+                                   error);
+
+        return processItemsResults(itemHandles, error[0]);
+    }
+
+    /**
+     * Creates an enumeration of items from an array of item handles and
+     * processes any errors resulting from obtaining item handles from native
+     * side.
+     *
+     * Note: Used by EventListImpl and ToDoListImpl, too.
+     */
+    protected Enumeration processItemsResults(int[] aItemHandles, int aError)
+    throws PIMException
+    {
+        if (aItemHandles == null)
+        {
+            throw new OutOfMemoryError();
+        }
+        NativeError.handleProcessItemResultsError(aError);
+
+        Vector items = new Vector(aItemHandles.length);
+
+        for (int i = 0; i < aItemHandles.length; i++)
+        {
+            int itemHandle = aItemHandles[i];
+            PIMItemImpl item = iItems.getItem(itemHandle);
+
+            if (item != null) // shouldn't be null
+            {
+                items.addElement(item);
+            }
+        }
+
+        return items.elements();
+    }
+
+    /**
+     * ensurePimPermission
+     * Ensures indicated PIM API security permission for this list
+     * This function is common for all list which need to verify PIM API
+     * security permissions. It also check that the list has at least
+     * the requested mode
+     *
+     * @param aMode The mode which is requested to be permitted
+     */
+    protected void ensurePimPermission(int aMode)
+    {
+        // Ensure that the list has at least the requested mode
+        checkModeMinimum(aMode);
+
+        PIMManager manager = (PIMManager)PIMManager.getInstance();
+
+        //ensure permission
+        manager.getPermission(getListType(), aMode);
+
+    }
+
+
+    /**
+     * Creates new item of specialized type.
+     */
+    protected abstract PIMItemImpl createAssociatedItem(int aItemHandle);
+
+    /**
+     * Determines whether the list has <em>at least</em> given access mode.
+     *
+     * @return \c true if all modes in \a aMode are present; \c false otherwise.
+     */
+    boolean isModeMinimum(int aMode)
+    {
+        return ((iMode & aMode) == aMode);
+    }
+
+    /**
+     * Checks whether the list has <em>at least</em> given access mode and
+     * throws SecurityException if not.
+     */
+    void checkModeMinimum(int aMode)
+    {
+        if (!isModeMinimum(aMode))
+        {
+            throw new SecurityException();
+        }
+    }
+
+    // Native operations
+
+    private native void _dispose(int aPIMManagerHandle);
+
+    /**
+     * Creates a new native side specialized item that is associated with the
+     * native side list but is not added to the list.
+     *
+     * @return Handle to the native side item or < 0 in error.
+     */
+    private native int _createItem(int aListHandle, int[] aError);
+
+    /**
+     * Removes given item from native database
+     *
+     * @return < 0 for error; KERR_NONE otherwise.
+     */
+    private native int _removeItem(int aListHandle, int aItemHandle);
+
+    /**
+     * Adds a new, first time committed item to the list.
+     */
+    private native int _addCommittedItem(int aListHandle, int aItemHandle);
+
+    /**
+     * @return List name, or null in error (taken as "out of memory").
+     */
+    private native String _getName(int aListHandle);
+
+    /**
+     * Closes the list.
+     */
+    private native int _close(int aListHandle);
+
+    /**
+     * Common JNI function for the four different items() operations. See
+     * doItems().
+     */
+    private native int[] _items(int aListHandle, int aEnumerationType,
+                                int aMatchingItemHandle, String aStringArg, int[] aError);
+
+    private native String[] _getCategories(int aListHandle, int[] aError);
+
+    private native boolean _isCategory(int aListHandle, String aCategory,
+                                       int[] aError);
+
+    private native void _addCategory(int aListHandle, String aCategory,
+                                     int[] aError);
+
+    /**
+     * @return The native handles of items that were in the removed category and
+     *         do not belong to any category any more; null if the category was
+     *         not removed (i.e. it did not exist).
+     */
+    private native int[] _deleteCategory(int aListHandle, String aCategory,
+                                         int[] aError);
+
+    /**
+     * @return Error code.
+     */
+    private native int _renameCategory(int aListHandle, String aCurrentCategory,
+                                       String aNewCategory);
+
+    private native int _maxCategories(int aListHandle);
+
+    private native boolean _isSupportedField(int aListHandle, int aField);
+
+    /**
+     * @return null on error.
+     */
+    private native int[] _getSupportedFields(int aListHandle);
+
+    private native boolean _isSupportedAttribute(int aListHandle, int aField,
+            int aAttribute);
+
+    private native int[] _getSupportedAttributes(int aListHandle, int aField, int[] aError);
+
+    private native boolean _isSupportedArrayElement(int aListHandle, int aStringArrayField,
+            int aArrayElement);
+
+    private native int[] _getSupportedArrayElements(int aListHandle, int aStringArrayField,
+            int[] aError);
+
+    private native int _getFieldDataType(int aListHandle, int aField, int[] aError);
+
+    /**
+     * Common implementation for all label getting operations.
+     *
+     * Contents of \a aLabelSpec argument:
+     *
+     * @li If \a aLabelType is LABEL_TYPE_FIELD, field constant (1 element).
+     *
+     * @li If \a aLabelType is LABEL_TYPE_ATTRIBUTE, attribute constant (1
+     *     element)
+     *
+     * @li If \a aLabelType is LABEL_TYPE_ARRAY_ELEMENT, string array field
+     *     constant and an array element field constant (2 elements).
+     *
+     * @param aLabelType
+     *            Label type (field, attribute or array element label).
+     *
+     * @param aLabelSpec
+     *            Constants used as arguments for label getting functions.
+     *
+     * @return The label.
+     */
+    private native String _getLabel(int aListHandle, int aLabelType,
+                                    int[] aLabelSpec, int[] aError);
+
+    private native int _maxValues(int aListHandle, int aField, int[] aError);
+
+    private native int _stringArraySize(int aListHandle, int aStringArrayField,
+                                        int[] aError);
+
+    /**
+     * Updates the list according to native-originated external changes. Makes
+     * the native side to resolve new, modified and removed items items in the
+     * native database and to create new native side peer objects, updating
+     * modified and removing removed items. Category changes are also reflected,
+     * but this does not affect the Java side.
+     *
+     * @return An array containing handles to new items, zero and handles to
+     *         removed items, contiguously, in that order.
+     */
+    private native int[] _updateList(int aListHandle, int aMatchingItemHandle,
+                                     int[] aError);
+
+}