FileDocCategorySizeDatePackage
AbstractPIMList.javaAPI DocphoneME MR2 API (J2ME)26466Wed May 02 18:00:28 BST 2007com.sun.kvem.midp.pim

AbstractPIMList.java

/*
 *   
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */

package com.sun.kvem.midp.pim;

import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Vector;
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;

/**
 * Generic PIM list.
 *
 */
public abstract class AbstractPIMList implements PIMList {
    /** Label for the list. */    
    private final String name;

    /** Items in the list. */
    private Vector items = new Vector();
    /** Read or write mode allowed for the list. */
    private int mode;
    /** Current state of the list. */
    private boolean open = true;
    /** The type of the list. */
    private final int type;
    /** List handle */
    private Object handle;
    /**
     * Base type for PIM list structures.
     * @param type identifier for list type
     * @param name label for the list
     * @param mode readable or writable mode
     * @param handle handle of the list
     */    
    AbstractPIMList(int type, String name, int mode, Object handle) {
        this.type   = type;
        this.name   = name;
        this.mode   = mode;
        this.handle = handle;
    }
    
    // JAVADOC COMMENT ELIDED
    public void addCategory(String category) throws PIMException {
        checkWritePermission();
        checkOpen();
        checkNullCategory(category);
        PIMHandler handler = PIMHandler.getInstance();
        String[] categories = handler.getCategories(handle);
        for (int i = 0; i < categories.length; i++) {
            if (category.equals(categories[i])) {
                return;
            }
        }
        handler.addCategory(handle, category);
        String[] newCategories = new String[categories.length + 1];
        System.arraycopy(categories, 0, newCategories, 0, categories.length);
        newCategories[categories.length] = category;
    }
    
    // JAVADOC COMMENT ELIDED
    public Enumeration itemsByCategory(String category) throws PIMException {
        checkReadPermission();
        checkOpen();
        Vector v = new Vector();
        if (category == null || category.equals("UNCATEGORIZED")) {
            for (Enumeration e = items.elements(); e.hasMoreElements(); ) {
                AbstractPIMItem item = (AbstractPIMItem) e.nextElement();
                if (item.getCategoriesRaw() == null) {
                    v.addElement(item);
                }
            }
        } else {
            for (Enumeration e = items.elements(); e.hasMoreElements(); ) {
                AbstractPIMItem item = (AbstractPIMItem) e.nextElement();
                if (item.isInCategory(category)) {
                    v.addElement(item);
                }
            }
        }
        return v.elements();
    }
    
    // JAVADOC COMMENT ELIDED
    public Enumeration items(PIMItem matchingItem) throws PIMException {
        checkReadPermission();
        checkOpen();
        if (!equals(matchingItem.getPIMList())) {
            throw new IllegalArgumentException("Cannot match item "
					       + "from another list");
        }
        int[] searchFields = matchingItem.getFields();
        int[] searchFieldDataTypes = new int[searchFields.length];
        Object[][] searchData = new Object[searchFields.length][];
        for (int i = 0; i < searchFields.length; i++) {
            searchFieldDataTypes[i] = getFieldDataType(searchFields[i]);
            searchData[i] = new Object[matchingItem
				       .countValues(searchFields[i])];
            for (int j = 0; j < searchData[i].length; j++) {
                searchData[i][j] = ((AbstractPIMItem) matchingItem)
                    .getField(searchFields[i], false, false).getValue(j);
                // convert strings to upper case so that they can be matched
                // case-insensitively
                switch (searchFieldDataTypes[i]) {
                    case PIMItem.STRING: {
                        String s = (String) searchData[i][j];
                        if (s != null) {
                            searchData[i][j] = s.toUpperCase();
                        }
                        break;
                    }
                    case PIMItem.STRING_ARRAY: {
                        String[] a = (String[]) searchData[i][j];
                        if (a != null) {
                            for (int k = 0; k < a.length; k++) {
                                if (a[k] != null) {
                                    a[k] = a[k].toUpperCase();
                                }
                            }
                        }
                        break;
                    }
                }
            }
        }
        Vector v = new Vector();
        nextItem: 
	for (Enumeration e = items.elements(); e.hasMoreElements(); ) {
            AbstractPIMItem item = (AbstractPIMItem) e.nextElement();
            // For each field, make sure all data has a match in "item".
            for (int i = 0; i < searchFields.length; i++) {
                // make sure that every value searchData[i][j]
                // (0 < j < matchingItem.countValues())
                // has a match in "item".
                int field = searchFields[i];
                int itemIndices = item.countValues(field);
                for (int j = 0; j < searchData[i].length; j++) {
                    boolean matchedThisIndex = false;
                    // see if there is a match for searchData[i][j]
                    for (int k = 0; k < itemIndices && !matchedThisIndex; k++) {
                        Object value = item.getField(field, false, false)
			    .getValue(k);
                        // see if "value" matches searchData[i][j]
                        switch (searchFieldDataTypes[i]) {
                            case PIMItem.DATE:
                            case PIMItem.INT:
                            case PIMItem.BOOLEAN: {
                                if (searchData[i][j].equals(value)) {
                                    matchedThisIndex = true;
                                }
                                break;
                            }
                            case PIMItem.BINARY: {
                                byte[] a = (byte[]) searchData[i][j];
                                byte[] b = (byte[]) value;
                                if (b != null && a.length == b.length) {
                                    boolean arrayMatches = true;
                                    for (int m = 0; m < a.length
					     && arrayMatches;
					 m++) {
                                        arrayMatches = (a[m] == b[m]);
                                    }
                                    matchedThisIndex = arrayMatches;
                                }
                                break;
                            }
                            case PIMItem.STRING: {
                                // strings are matched case-insensitively
                                // and using contains(), not equals()
                                String s1 = (String) searchData[i][j];
                                String s2 = (String) value;
                                if (s2 == null) {
                                    if (s1 == null) {
                                        matchedThisIndex = true;
                                    }
                                } else if (s2.toUpperCase().indexOf(s1) != -1) {
                                    matchedThisIndex = true;
                                }
                                break;
                            }
                            case PIMItem.STRING_ARRAY: {
                                // each element of the string array is matched
                                // in the same way as a string (above) would be.
                                String[] a = (String[]) searchData[i][j];
                                String[] b = (String[]) value;
                                if (a == null) {
                                    if (b == null) {
                                        matchedThisIndex = true;
                                    }
                                } else if (b != null && a.length == b.length) {
                                    boolean arrayMatches = true;
                                    for (int m = 0; m < a.length
					     && arrayMatches;
					 m++) {
                                        if (a[m] != null && a[m].length() > 0) {
                                            arrayMatches = (b[m] != null)
                                                && b[m].toUpperCase()
						.indexOf(a[m])
						!= -1;
                                        }
                                    }
                                    matchedThisIndex = arrayMatches;
                                }
                                break;
                            }
                        } // end switch data type
                    } // end loop over item's indices
                    if (!matchedThisIndex) {
                        // no match found for searchData[i][j].
                        // this means that "item" does not match "matchingItem"
                        continue nextItem;
                    }
                } // end iteration over matchingItem's indices
            } // end iteration over fields being searched
            v.addElement(item);
        } // end enumeration of items
        return v.elements();
    }
    
    // JAVADOC COMMENT ELIDED
    public void close() throws PIMException {
        checkOpen();
        this.open = false;
        this.items = null;
        PIMHandler.getInstance().closeList(handle);
    }
    
    // JAVADOC COMMENT ELIDED
    public String getArrayElementLabel(int stringArrayField, int arrayElement) {
        if (getFieldDataType(stringArrayField) != PIMItem.STRING_ARRAY) {
            throw new IllegalArgumentException("Not a string array field");
        }
        if (!isSupportedArrayElement(stringArrayField, arrayElement)) {
            // throw new UnsupportedFieldException("Unsupported array element "
	    // + arrayElement);
            throw new IllegalArgumentException("Invalid array element "
					       + arrayElement);
        }
        return PIMHandler.getInstance()
	    .getArrayElementLabel(handle,
				  stringArrayField, arrayElement);
    }
    
    // JAVADOC COMMENT ELIDED
    public int[] getSupportedArrayElements(int stringArrayField) {
        if (getFieldDataType(stringArrayField) != PIMItem.STRING_ARRAY) {
            throw new IllegalArgumentException("Not a string array field");
        }
        return PIMHandler.getInstance()
	    .getSupportedArrayElements(handle,
				       stringArrayField);
    }
    
    // JAVADOC COMMENT ELIDED
    public String getAttributeLabel(int attribute) {
        checkAttribute(attribute);
        String label =
            PIMHandler.getInstance().getAttributeLabel(handle, attribute);
        if (label == null) {
            throw new IllegalArgumentException("Invalid attribute: "
					       + attribute);
        }
        return label;
    }
    
    // JAVADOC COMMENT ELIDED
    public int maxValues(int field) {
        try {
            checkField(field);
        } catch (UnsupportedFieldException e) {
            return 0;
        }
        return PIMHandler.getInstance().getMaximumValues(handle, field);
    }
    
    // JAVADOC COMMENT ELIDED
    public boolean isSupportedAttribute(int field, int attribute) {
        if (!isSupportedField(field)) {
            return false;
        }
        // ATTR_NONE is supported for all fields
        if (attribute == PIMItem.ATTR_NONE) {
            return true;
        }
        // if attribute is not a power of 2, forget it
        int i = attribute;
        while ((i & 1) == 0 && i != 0) {
            i >>= 1;
        }
        if (i != 1) {
            return false;
        }
        return PIMHandler.getInstance()
            .isSupportedAttribute(handle, field, attribute);
    }
    
    // JAVADOC COMMENT ELIDED
    public int maxCategories() {
        return -1;
    }
    
    // JAVADOC COMMENT ELIDED
    public String[] getCategories() throws PIMException {
        checkOpen();
        return PIMHandler.getInstance().getCategories(handle);
    }
    
    // JAVADOC COMMENT ELIDED
    public boolean isCategory(String category) throws PIMException {
        checkOpen();
        checkNullCategory(category);
        String[] categories =
            PIMHandler.getInstance().getCategories(handle);
        for (int i = 0; i < categories.length; i++) {
            if (category.equals(categories[i])) {
                return true;
            }
        }
        return false;
    }
    
    // JAVADOC COMMENT ELIDED
    public String getName() {
        return name;
    }
    
    // JAVADOC COMMENT ELIDED
    public int stringArraySize(int stringArrayField) {
        if (getFieldDataType(stringArrayField) != PIMItem.STRING_ARRAY) {
            throw new IllegalArgumentException("Not a string array field");
        }
        return PIMHandler.getInstance().getStringArraySize(handle,
							   stringArrayField);
    }
    
    // JAVADOC COMMENT ELIDED
    public boolean isSupportedField(int field) {
        return PIMHandler.getInstance().isSupportedField(handle, field);
    }
    
    // JAVADOC COMMENT ELIDED
    public void deleteCategory(String category, boolean deleteUnassignedItems)
    throws PIMException {
        checkWritePermission();
        checkOpen();
        checkNullCategory(category);
        String[] categories =
            PIMHandler.getInstance().getCategories(handle);
        int categoryIndex = -1;
        for (int i = 0; i < categories.length && categoryIndex == -1; i++) {
            if (category.equals(categories[i])) {
                categoryIndex = i;
            }
        }
        if (categoryIndex == -1) {
            return;
        }
        String[] newCategories = new String[categories.length - 1];
        System.arraycopy(categories, 0, newCategories, 0, categoryIndex);
        System.arraycopy(categories, categoryIndex + 1,
            newCategories, categoryIndex,
            newCategories.length - categoryIndex);
        PIMHandler.getInstance().deleteCategory(handle, category);
        final AbstractPIMItem[] a = new AbstractPIMItem[items.size()];
        items.copyInto(a);
        for (int i = 0; i < a.length; i++) {
            if (a[i].isInCategory(category)) {
                a[i].removeFromCategory(category);
                if (deleteUnassignedItems && a[i].getCategories().length == 0) {
                    items.removeElement(a[i]);
                }
            }
        }
    }
    
    // JAVADOC COMMENT ELIDED
    public Enumeration items() throws PIMException {
        checkReadPermission();
        checkOpen();
        final PIMItem[] data = new PIMItem[items.size()];
        items.copyInto(data);
        return new 
	    /**
	     * Inner class for List Enumeration.
	     */
	    Enumeration() {
		/** Initial offset int the list. */
		int index = 0;
		/**
		 * Checks for more elements int the list.
		 * @return <code>true</code> if more elements exist
		 */
            public boolean hasMoreElements() {
                return index < data.length;
            }
		/**
		 * Fetched the next element int the list.
		 * @return next element int the list
		 */
            public Object nextElement() {
                try {
                    return data[index++];
                } catch (ArrayIndexOutOfBoundsException e) {
                    throw new NoSuchElementException();
                }
            }
        };
    }
    
    // JAVADOC COMMENT ELIDED
    public int[] getSupportedAttributes(int field) {
        checkField(field);
        return PIMHandler.getInstance().getSupportedAttributes(handle, field);
    }
    
    // JAVADOC COMMENT ELIDED
    public int getFieldDataType(int field) {
        int dataType = PIMHandler.getInstance().getFieldDataType(handle, field);
        if (dataType == -1) {
            throw AbstractPIMItem.complaintAboutField(type, field);
        }
        return dataType;
    }
    
    // JAVADOC COMMENT ELIDED
    public String getFieldLabel(int field) {
        checkField(field);
        return PIMHandler.getInstance().getFieldLabel(handle, field);
    }
    
    // JAVADOC COMMENT ELIDED
    public void renameCategory(String currentCategory, String newCategory)
    throws PIMException {
        checkWritePermission();
        checkOpen();
        if (currentCategory == null || newCategory == null) {
            throw new NullPointerException("Null category");
        }
        String[] categories =
			PIMHandler.getInstance().getCategories(handle);
        if (newCategory.equals(currentCategory)) {
            return;
        }
        int oldCategoryIndex = -1;
        int newCategoryIndex = -1;
        for (int i = 0; i < categories.length; i++) {
            if (currentCategory.equals(categories[i])) {
                oldCategoryIndex = i;
            } else if (newCategory.equals(categories[i])) {
                newCategoryIndex = i;
            }
        }
        if (oldCategoryIndex == -1) {
            throw new PIMException("No such category: "  + currentCategory);
        }
        if (newCategoryIndex == -1) {
			categories[oldCategoryIndex] = newCategory;
            PIMHandler.getInstance().renameCategory(handle, currentCategory,
                newCategory);
        } else {
            String[] a = new String[categories.length - 1];
            System.arraycopy(categories, 0, a, 0, oldCategoryIndex);
            System.arraycopy(categories, oldCategoryIndex + 1,
                a, oldCategoryIndex, a.length - oldCategoryIndex);
            categories = a;
            PIMHandler.getInstance().deleteCategory(handle, currentCategory);
        }
        for (Enumeration e = items.elements(); e.hasMoreElements(); ) {
            AbstractPIMItem item = (AbstractPIMItem) e.nextElement();
            if (item.isInCategory(currentCategory)) {
                item.removeFromCategory(currentCategory);
                item.addToCategory(newCategory);
            }
        }
    }
    
    // JAVADOC COMMENT ELIDED
    public boolean isSupportedArrayElement(int stringArrayField,
					   int arrayElement) {
        int dataType =
            PIMHandler.getInstance().getFieldDataType(handle, stringArrayField);
        if (dataType != PIMItem.STRING_ARRAY) {
            return false;
        }
        return PIMHandler.getInstance()
            .isSupportedArrayElement(handle, stringArrayField, arrayElement);
    }
    
    // JAVADOC COMMENT ELIDED
    public int[] getSupportedFields() {
        return PIMHandler.getInstance().getSupportedFields(handle);
    }
    
    // JAVADOC COMMENT ELIDED
    public Enumeration items(String matchingValue) throws PIMException {
        checkReadPermission();
        checkOpen();
        matchingValue = matchingValue.toUpperCase();
        Vector v = new Vector();
        nextItem: 
	for (Enumeration e = items.elements(); e.hasMoreElements(); ) {
            AbstractPIMItem item = (AbstractPIMItem) e.nextElement();
            int[] fields = item.getFields();
            for (int i = 0; i < fields.length; i++) {
                switch (getFieldDataType(fields[i])) {
                    case PIMItem.STRING:
                        for (int j = item.countValues(fields[i]) - 1;
			     j >= 0; j--) {
                            String value = item.getString(fields[i], j);
                            if (value != null) {
                                if (value.toUpperCase().indexOf(matchingValue)
				    != -1) {
                                    v.addElement(item);
                                    continue nextItem;
                                }
                            }
                        }
                        break;
                    case PIMItem.STRING_ARRAY:
                        for (int j = item.countValues(fields[i]) - 1;
			     j >= 0; j--) {
                            String[] a = item.getStringArray(fields[i], j);
                            if (a == null) {
                                continue;
                            }
                            for (int k = 0; k < a.length; k++) {
                                if (a[k] != null) {
                                    if (a[k].toUpperCase()
					.indexOf(matchingValue)
					!= -1) {
                                        v.addElement(item);
                                        continue nextItem;
                                    }
                                }
                            }
                        }
                        break;
                }
            }
        }
        return v.elements();
    }

    /**
     * Verifies read permission.
     * @throws SecurityException if operation is not permitted
     */
    protected void checkReadPermission() throws SecurityException {
        if (mode == PIM.WRITE_ONLY) {
            throw new SecurityException("List cannot be read");
        }
    }

    /**
     * Verifies write permission.
     * @throws SecurityException if operation is not permitted
     */    
    protected void checkWritePermission() throws SecurityException {
        if (mode == PIM.READ_ONLY) {
            throw new SecurityException("List cannot be written");
        }
    }

    /**
     * Ensures that the list is already open.
     * @throws PIMException if list is closed
     */    
    protected void checkOpen() throws PIMException {
        if (!open) {
            throw new PIMException("List is closed.");
        }
    }

    /**    
     * Ensure the category is not missing.
     * @throws NullPointerException if category is 
     * <code>null</code>
     * @param category data to validate
     */
    protected void checkNullCategory(String category) {
        if (category == null) {
            throw new NullPointerException("Null category");
        }
    }
    
    /**
     * Adds an item to a PIM list.
     * @param item item to add to the list
     */
    public void addItem(AbstractPIMItem item) {
        this.items.addElement(item);
        item.setPIMList(this);
    }
    
    /**
     * Removes an item from a PIM list.
     * @param item to add to the list
     * @throws  PIMException if the item was not int the list
     * @throws SecurityException if operation is not permitted
     * @throws NullPointerException if item is <code>null</code>
     */
    void removeItem(PIMItem item) throws PIMException {
        checkWritePermission();
        checkOpen();
        if (item == null) {
            throw new NullPointerException("Null item");
        }
        if (!this.items.removeElement(item)) {
            throw new PIMException("Item not in list");
        }
        ((AbstractPIMItem) item).remove();
    }

    /**
     * Commits an item to the database
     *
     * @param key the key of the item to commit. This can be null.
     * @param data the binary data for the item
     * @param categories list of item's categories
     * @return an updated non-null key for the item
     * @throws PIMException in case of I/O error
     */
    Object commit(Object key, byte[] data, String[] categories)
        throws PIMException {
        return PIMHandler.getInstance()
            .commitListElement(handle, key, data, categories);
    }

    /**
     * Gets the type of this PIM list (PIM.CONTACT_LIST, PIM.EVENT_LIST or
     * PIM.TODO_LIST).
     * @return the type of the PIM list
     */
    int getType() {
        return type;
    }

    /**
     * Returns list handle
     *
     * @return handle of the list
     */
    public Object getHandle() {
        return handle;
    }

    /**
     * Ensures the field is valid.   
     * @throws   IllegalArgumentException if field is not a valid
     *           field (i.e. not a standard field and not an extended field).
     *           IllegalArgumentException takes precedence over
     *           UnsupportedFieldException when checking the provided field.
     * @throws   UnsupportedFieldException if the field is not supported in
     *           the implementing instance of the class.
     * @param field Field to validate
     */
    private void checkField(int field) {
        if (PIMHandler.getInstance().getFieldDataType(handle, field) == -1) {
            throw AbstractPIMItem.complaintAboutField(type, field);
        }
    }

    /**    
     * Ensures the attribute is valid.
     * @throws IllegalArgumentException more than one bit set
     * in attribute selector
     * @param attribute identifier for specific attribute
     */
    private void checkAttribute(int attribute) {
        if (attribute == 0) {
            // OK
            return;
        } else {
            // make sure only one bit is set
            while ((attribute & 1) == 0 && attribute != 0) {
                attribute >>= 1;
            }
            if (attribute != 1) {
                // more than one bit is set in attribute
                throw new IllegalArgumentException(
                    "Invalid attribute: " + attribute);
            }
        }
    }
    
}