/*
*
*
* 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.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.microedition.pim.Contact;
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;
/**
* Partial implementation of PIMItem.
* Extended by ContactImpl, EventImpl and ToDoImpl.
*
*/
public abstract class AbstractPIMItem implements PIMItem {
/** Sorted list of defined field codes. */
private int[] fieldKeys = new int[0];
/** List of defined PIMFields. Indexed by fieldKeys. */
private PIMField[] fieldValues = new PIMField[0];
/** List of categories to which this item belongs */
private String[] categories = null;
/** Has this item been modified since it was last committed? */
private boolean modified = true;
/** List which this item belongs to. May be null for a deserialized item. */
private AbstractPIMList pimList;
/**
* Type of list this item belongs to (e.g. PIM.CONTACT_LIST).
* Note: valid even if pimList is null.
*/
private final int listType;
/**
* Internal list used for item description getting. May not be null.
* For a deserialized item represents default dummy list
*/
private Object pimListHandle;
/** Key for the use of commit() */
private Object key = null;
/** Instance of PIMHandler to simplify access to PIM storage */
private PIMHandler pimHandler;
/**
* Constructs a PIM list.
* @param pimList initial list
* @param type type of list
*/
protected AbstractPIMItem(AbstractPIMList pimList, int type) {
this.pimList = pimList;
this.listType = type;
pimHandler = PIMHandler.getInstance();
try {
pimListHandle = pimList != null ? pimList.getHandle() :
pimHandler.openList(type, null, PIM.READ_ONLY);
} catch (PIMException e) {
throw new RuntimeException("Error while opening default list");
}
}
/**
* This constructor is used when importing an item.
* @param pimList initial list
* @param baseItem initial entry
*/
protected AbstractPIMItem(AbstractPIMList pimList, PIMItem baseItem) {
this(pimList, pimList.getType());
// copy fields
int[] fields = baseItem.getFields();
for (int i = 0; i < fields.length; i++) {
int field = fields[i];
if (!pimList.isSupportedField(field)) {
// skip field
continue;
}
int dataType = pimList.getFieldDataType(field);
int indices = baseItem.countValues(field);
for (int index = 0; index < indices; index++) {
int attributes = baseItem.getAttributes(field, index);
Object value = null;
switch (dataType) {
case PIMItem.BINARY: {
value = baseItem.getBinary(field, index);
break;
}
case PIMItem.BOOLEAN: {
value = new Boolean(baseItem.getBoolean(field, index));
break;
}
case PIMItem.DATE: {
value = new Long(baseItem.getDate(field, index));
break;
}
case PIMItem.INT: {
value = new Integer(baseItem.getInt(field, index));
break;
}
case PIMItem.STRING: {
value = baseItem.getString(field, index);
break;
}
case PIMItem.STRING_ARRAY: {
value = baseItem.getStringArray(field, index);
break;
}
default: {
// cannot import this data. Not a problem, since
// this method is called when importing a PIMItem
// from another list. In this case, it is valid
// to ignore data of an unknown type.
}
} // end switch (dataType)
try {
addValue(field, attributes, value, true);
} catch (FieldFullException ffe) {
// Too many values. It's OK to ignore values that
// don't fit.
} catch (IllegalArgumentException iae) {
// illegal data in this field. It's OK not to import it.
}
} // finish iterating over indices
} // finish iterating over fields
updateRevision();
}
/**
* Gets the field entry.
* @param field identifier for the field
* @param create if <code>true</code> create the field
* if it doesn't already exist
* @param check if <code>true</code> check that the field is
* supported before attempting to get it
* @return the request field entry
*/
PIMField getField(int field, boolean create, boolean check) {
PIMField f = getField(field);
if (f == null) {
if (check && !pimHandler.isSupportedField(pimListHandle, field)) {
throw complaintAboutField(listType, field);
}
if (create) {
f = new EmptyPIMField();
putField(field, f);
}
}
return f;
}
/**
* Sets the field value.
* @param field identifier of field
* @param index value offset
* @param attributes field properties
* @param value field to update
* @param force if <code>true</code> create the value
*/
private void setValue(
int field, int index, int attributes, Object value, boolean force) {
try {
checkType(field, value);
PIMField pimField = getField(field, false, true);
if (pimField == null) {
throw new IndexOutOfBoundsException("Empty field: " + field);
}
int currentValues = pimField.getValueCount();
if (index < 0 || index >= currentValues) {
throw new IndexOutOfBoundsException("0 <= index < "
+ currentValues + ", " + index + " not in range");
}
if (!force) {
checkReadOnlyFields(field);
}
if (value instanceof Integer) {
checkIntValue(field, ((Integer)value).intValue());
}
attributes = filterAttributes(field, attributes);
pimField.setValue(attributes, value, index);
modified = true;
} catch (ClassCastException e) {
throw new IllegalArgumentException("Wrong type for field");
}
}
/**
* Adds a value to a field.
*
* @param field identifier of field
* @param attributes field properties
* @param value field to update
* @param force if <code>true</code> create the value
* @throws FieldFullException if no more values can be added to the field
*/
private void addValue(
int field, int attributes, Object value, boolean force) {
checkType(field, value);
PIMField pimField = getField(field, true, true);
int maxValues =
pimHandler.getMaximumValues(pimListHandle, field);
int currentValues = pimField.getValueCount();
if (maxValues != -1 && currentValues >= maxValues) {
throw new FieldFullException("Can only store "
+ maxValues + " in field", field);
}
if (!force) {
checkReadOnlyFields(field);
}
if (value instanceof Integer) {
checkIntValue(field, ((Integer)value).intValue());
}
if (pimField.isScalar()) {
// upgrade PIM field
if (currentValues == 0) {
pimField = new ScalarPIMField();
putField(field, pimField);
} else {
Object value0 = pimField.getValue(0);
int attributes0 = pimField.getAttributes(0);
pimField = new VectorPIMField();
pimField.addValue(attributes0, value0);
putField(field, pimField);
}
}
attributes = filterAttributes(field, attributes);
pimField.addValue(attributes, value);
modified = true;
}
private void checkIntValue(int field, int value) {
if ((listType == PIM.CONTACT_LIST && field == Contact.CLASS) ||
(listType == PIM.EVENT_LIST && field == Event.CLASS) ||
(listType == PIM.TODO_LIST && field == ToDo.CLASS)) {
validateClass(value);
}
if (listType == PIM.TODO_LIST && field == ToDo.PRIORITY) {
validatePriority(value);
}
}
/**
* Filters attributes to include only the supported ones.
* @param field identifier of field
* @param attributes field properties
* @return filtered attributes
*/
private int filterAttributes(int field, int attributes) {
if (attributes == 0) {
return 0;
} else {
return attributes
& pimHandler
.getSupportedAttributesMask(pimListHandle, field);
}
}
/**
* Gets current value.
* @param field identifier of field
* @param index field identifier
* @return requested field
*/
private Object getValue(int field, int index) {
PIMField pimField = getField(field, false, true);
if (pimField == null) {
throw new IndexOutOfBoundsException("Empty field: " + field);
}
int currentValues = pimField.getValueCount();
if (index < 0 || index >= currentValues) {
throw new IndexOutOfBoundsException("0 <= index < "
+ currentValues + ", " + index + " not in range");
}
return pimField.getValue(index);
}
// JAVADOC COMMENT ELIDED
public void addStringArray(int field, int attributes, String[] value) {
checkType(field, STRING_ARRAY);
validateStringArray(field, value);
addValue(field, attributes, value, false);
}
// JAVADOC COMMENT ELIDED
public void addBoolean(int field, int attributes, boolean value) {
addValue(field, attributes, new Boolean(value), false);
}
// JAVADOC COMMENT ELIDED
public void removeFromCategory(String category) {
if (category == null) {
throw new NullPointerException("Null category");
}
if (categories != null) {
for (int i = 0; i < categories.length; i++) {
if (category.equals(categories[i])) {
if (categories.length == 1) {
this.categories = null;
} else {
String[] a = new String[categories.length - 1];
System.arraycopy(categories, 0, a, 0, i);
System.arraycopy(categories, i + 1, a, i,
a.length - i);
this.categories = a;
}
this.modified = true;
return;
}
}
}
}
// JAVADOC COMMENT ELIDED
public int[] getFields() {
int emptyFields = 0;
// make sure all these fields have defined values
for (int i = 0; i < fieldValues.length; i++) {
if (fieldValues[i].getValueCount() == 0) {
emptyFields ++;
}
}
int[] keys = new int[fieldKeys.length - emptyFields];
for (int i = 0, j = 0; i < keys.length; i++) {
if (emptyFields == 0 || fieldValues[i].getValueCount() != 0) {
keys[j++] = fieldKeys[i];
} else {
emptyFields --;
}
}
return keys;
}
// JAVADOC COMMENT ELIDED
public boolean getBoolean(int field, int index) {
checkType(field, BOOLEAN);
return ((Boolean) getValue(field, index)).booleanValue();
}
// JAVADOC COMMENT ELIDED
public void addDate(int field, int attributes, long value) {
addValue(field, attributes, new Long(value), false);
}
// JAVADOC COMMENT ELIDED
public int maxCategories() {
return -1;
}
// JAVADOC COMMENT ELIDED
public void setDate(int field, int index, int attributes, long value) {
setValue(field, index, attributes, new Long(value), false);
}
// JAVADOC COMMENT ELIDED
public int getInt(int field, int index) {
checkType(field, INT);
try {
return ((Integer) getValue(field, index)).intValue();
} catch (ClassCastException e) {
String message = "Cannot convert to integer on field "
+ field + ": "
+ getValue(field, index).getClass();
throw new ClassCastException(message);
}
}
// JAVADOC COMMENT ELIDED
public void setBinary(int field, int index, int attributes, byte[] value,
int offset, int length) {
validateBinaryValue(value, offset, length);
length = Math.min(length, value.length - offset);
byte[] b = new byte[length];
System.arraycopy(value, offset, b, 0, length);
setValue(field, index, attributes, b, false);
}
// JAVADOC COMMENT ELIDED
public int getAttributes(int field, int index) {
return getField(field, true, true).getAttributes(index);
}
// JAVADOC COMMENT ELIDED
public int countValues(int field) {
PIMField pimField = getField(field, false, true);
return pimField == null ? 0 : pimField.getValueCount();
}
// JAVADOC COMMENT ELIDED
public void addString(int field, int attributes, String value) {
validateString(value);
addValue(field, attributes, value, false);
}
// JAVADOC COMMENT ELIDED
public String[] getCategories() {
if (categories == null) {
return new String[0];
}
String[] cs = new String[categories.length];
System.arraycopy(categories, 0, cs, 0, categories.length);
return cs;
}
// JAVADOC COMMENT ELIDED
String[] getCategoriesRaw() {
return categories;
}
// JAVADOC COMMENT ELIDED
public void setInt(int field, int index, int attributes, int value) {
setValue(field, index, attributes, new Integer(value), false);
}
// JAVADOC COMMENT ELIDED
public void setStringArray(int field, int index, int attributes,
String[] value) {
checkType(field, STRING_ARRAY);
validateStringArray(field, value);
setValue(field, index, attributes, value, false);
}
/**
* Makes sure that
* <ul>
* <li>The string array is not null
* <li>At least one string in the array is not null
* <li>The string array has the correct length
* </ul>
* @param field identifier for field
* @param a string array to be checked
*/
private void validateStringArray(int field, String[] a) {
int requiredLength =
pimHandler.getStringArraySize(pimListHandle, field);
if (a.length != requiredLength) {
throw new IllegalArgumentException(
"String array length incorrect: should be " + requiredLength);
}
for (int i = 0; i < a.length; i++) {
if (a[i] != null) {
return;
}
}
throw new IllegalArgumentException("No non-null elements in array");
}
/**
* Makes sure that a string is not null.
* @param value string to be checked
*/
private void validateString(String value) {
if (value == null) {
throw new NullPointerException(
"String field value should not be null");
}
}
// JAVADOC COMMENT ELIDED
public long getDate(int field, int index) {
checkType(field, DATE);
try {
return ((Long) getValue(field, index)).longValue();
} catch (ClassCastException e) {
throw e;
}
}
// JAVADOC COMMENT ELIDED
public void addToCategory(String category) throws PIMException {
if (category == null) {
throw new NullPointerException("Null category");
}
if (categories == null) {
this.categories = new String[] { category };
this.modified = true;
} else {
for (int i = 0; i < categories.length; i++) {
if (categories[i].equals(category)) {
return;
}
}
String[] a = new String[categories.length + 1];
System.arraycopy(categories, 0, a, 0, categories.length);
a[categories.length] = category;
this.categories = a;
this.modified = true;
}
}
// JAVADOC COMMENT ELIDED
public void addInt(int field, int attributes, int value) {
addValue(field, attributes, new Integer(value), false);
}
// JAVADOC COMMENT ELIDED
public byte[] getBinary(int field, int index) {
checkType(field, BINARY);
return (byte[]) getValue(field, index);
}
// JAVADOC COMMENT ELIDED
public void addBinary(int field, int attributes, byte[] value,
int offset, int length) {
validateBinaryValue(value, offset, length);
length = Math.min(length, value.length - offset);
byte[] b = new byte[length];
System.arraycopy(value, offset, b, 0, length);
addValue(field, attributes, b, false);
}
/**
* Ensures that binary parameters are in range.
* @param value binary data to be checked
* @param offset index into byte array
* @param length of data to be checked
* @throws NullPointerException if value is null
* @throws IllegalArgumentException if offset or length
* are not valid
*/
private void validateBinaryValue(byte[] value, int offset, int length) {
if (value == null) {
throw new NullPointerException("Binary field value"
+" should not be null");
}
if (offset < 0) {
throw new IllegalArgumentException("Negative offset");
}
if (offset + length > value.length) {
throw new IllegalArgumentException("Offset out of range");
}
if (length <= 0) {
throw new IllegalArgumentException("Length must be at least 1");
}
if (value.length == 0) {
throw new IllegalArgumentException("Binary array value "
+ "has zero length");
}
}
// JAVADOC COMMENT ELIDED
public String[] getStringArray(int field, int index) {
checkType(field, STRING_ARRAY);
return (String[]) getValue(field, index);
}
// JAVADOC COMMENT ELIDED
public void setBoolean(int field, int index, int attributes,
boolean value) {
setValue(field, index, attributes, new Boolean(value), false);
}
// JAVADOC COMMENT ELIDED
public PIMList getPIMList() {
return pimList;
}
/**
* Returns the handle of the PIMList associated with this item.
*
* @return the handle of the PIMList that this item belongs to. If the
* item does not belong to any list, handle of default dummy
* list is returned.
*/
public Object getPIMListHandle() {
return pimListHandle;
}
/**
* Set the PIMList of this item.
* @param list data to be saved
*/
void setPIMList(AbstractPIMList list) {
this.pimList = list;
pimListHandle = list.getHandle();
}
// JAVADOC COMMENT ELIDED
public void removeValue(int field, int index) {
PIMField pimField = getField(field, false, true);
if (pimField == null) {
throw new IndexOutOfBoundsException("Empty field: " + field);
}
int currentValues = pimField.getValueCount();
if (index < 0 || index >= currentValues) {
throw new IndexOutOfBoundsException("0 <= index < "
+ currentValues + ", " + index + " not in range");
}
checkReadOnlyFields(field);
pimField.removeValue(index);
currentValues --;
if (currentValues == 0) {
removeField(field);
} else if (currentValues == 1) {
// downgrade field
Object value = pimField.getValue(0);
int attributes = pimField.getAttributes(0);
pimField = new ScalarPIMField();
pimField.addValue(attributes, value);
putField(field, pimField);
}
modified = true;
}
// JAVADOC COMMENT ELIDED
public String getString(int field, int index) {
checkType(field, STRING);
return (String) getValue(field, index);
}
// JAVADOC COMMENT ELIDED
public void setString(int field, int index, int attributes, String value) {
validateString(value);
setValue(field, index, attributes, value, false);
}
// JAVADOC COMMENT ELIDED
public boolean isModified() {
return modified;
}
/**
* Sets the modified flag.
* @param modified flag to be saved
*/
void setModified(boolean modified) {
this.modified = modified;
}
// JAVADOC COMMENT ELIDED
public void commit() throws PIMException {
if (pimList == null) {
throw new PIMException("Item is not in a list");
}
pimList.checkWritePermission();
pimList.checkOpen();
updateRevision();
setDefaultValues();
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PIMFormat format = getEncodingFormat();
format.encode(baos, "UTF-8", this);
Object newKey = pimList.commit(key, baos.toByteArray(), categories);
if (key == null) {
pimList.addItem(this);
}
setKey(newKey);
updateUID();
modified = false;
} catch (IOException e) {
throw new PIMException("Error persisting PIMItem");
}
}
/**
* Gets the format codec used to encode and decode this object
* for storage.
* @return an instance of PIMFormat that can deal with this item
*/
abstract PIMFormat getEncodingFormat();
/**
* Checks the category is valid.
* @param category name of category to check
* @return <code>true</code> if is int category
*/
boolean isInCategory(String category) {
if (categories == null) {
return false;
} else {
for (int i = 0; i < categories.length; i++) {
if (categories[i].equals(category)) {
return true;
}
}
return false;
}
}
/**
* Sets the key that identifies this item in the PIM database
* @param key an Object used to index this item
*/
void setKey(Object key) {
this.key = key;
if (key != null) {
updateUID();
}
}
/**
* Gets the key that identifies this item in the PIM database
* @return an Object used to index this item
*/
Object getKey() {
return key;
}
/**
* Removed this PIMItem from its list
*/
void remove() throws PIMException {
if (pimList == null) {
throw new PIMException("Item is not in a list");
}
pimList.checkWritePermission();
pimList.commit(key, null, null);
setKey(null);
pimList = null;
}
/**
* Sets default values for this item.
*/
protected void setDefaultValues() {
int[] supportedFields = pimList.getSupportedFields();
for (int i = 0; i < supportedFields.length; i++) {
int field = supportedFields[i];
PIMField pimField = getField(field, false, true);
if ((pimField == null || pimField.getValueCount() == 0)
&& pimHandler.hasDefaultValue(pimListHandle, field)) {
Object value = null;
switch (pimList.getFieldDataType(field)) {
case PIMItem.BOOLEAN:
value = new Boolean(pimHandler
.getDefaultBooleanValue(pimListHandle, field));
break;
case PIMItem.BINARY:
value = pimHandler
.getDefaultBinaryValue(pimListHandle, field);
break;
case PIMItem.DATE:
value = new Long(pimHandler
.getDefaultDateValue(pimListHandle, field));
break;
case PIMItem.INT:
value = new Integer(pimHandler
.getDefaultIntValue(pimListHandle, field));
break;
case PIMItem.STRING:
value = pimHandler
.getDefaultStringValue(pimListHandle, field);
break;
case PIMItem.STRING_ARRAY:
value = pimHandler
.getDefaultStringArrayValue(pimListHandle, field);
break;
default: continue;
}
addValue(field, PIMItem.ATTR_NONE, value, false);
}
}
}
/**
* Checks for valid PIM field.
* @param type list type
* @param field identifier for field
* @return <code>true</code> if field is valid
*/
static boolean isValidPIMField(int type, int field) {
switch (type) {
case PIM.CONTACT_LIST:
return ContactImpl.isValidPIMField(field);
case PIM.EVENT_LIST:
return EventImpl.isValidPIMField(field);
case PIM.TODO_LIST:
return ToDoImpl.isValidPIMField(field);
default:
return false;
}
}
/**
* Checks the type of a field, throwing an IllegalArgumentException
* if given or if the field number is invalid.
* @param field identifier for field
* @param value data to be checked
* @throws IllegalArgumentException if data type is not known
*/
private void checkType(int field, Object value) {
try {
int dataType =
pimHandler.getFieldDataType(pimListHandle, field);
switch (dataType) {
case PIMItem.BINARY: {
byte[] b = (byte[]) value;
break;
}
case PIMItem.BOOLEAN: {
Boolean b = (Boolean) value;
break;
}
case PIMItem.DATE: {
Long l = (Long) value;
break;
}
case PIMItem.INT: {
Integer i = (Integer) value;
break;
}
case PIMItem.STRING: {
String s = (String) value;
break;
}
case PIMItem.STRING_ARRAY: {
String[] s = (String[]) value;
break;
}
default:
throw complaintAboutField(listType, field);
}
} catch (ClassCastException cce) {
throw new IllegalArgumentException(cce.getMessage());
}
}
/**
* Checks the type of a field, throwing an IllegalArgumentException
* if given or if the field number is invalid.
* @param field identifier of field
* @param dataType data type for field value
*/
private void checkType(int field, int dataType) {
int correctDataType =
pimHandler.getFieldDataType(pimListHandle, field);
if (dataType != correctDataType && correctDataType != -1) {
throw new IllegalArgumentException("Wrong data type");
}
if (correctDataType == -1) {
throw complaintAboutField(listType, field);
}
}
/**
* Throw an exception based on field failure type.
* @param type list type
* @param field identifier of field
* @return UnsupportedFieldException if the field value is not supported
* in the field or IllegalArgumentException, if the field is not valid
*/
static RuntimeException complaintAboutField(int type, int field) {
if (isValidPIMField(type, field)) {
return new UnsupportedFieldException(String.valueOf(field));
} else {
return new IllegalArgumentException("Invalid field " + field);
}
}
/**
* Returns the index of the given key, if it is present in
* fieldKeys[]. If it is not present, returns the binary
* complement of the index before which
* the key could be inserted. O(log fieldKeys.length) in time.
* @param key property key for requested field
* @return the index of the field key
*/
private int findFieldKey(int key) {
int lowerBound = 0;
int upperBound = fieldKeys.length;
while (lowerBound != upperBound) {
int index = lowerBound + (upperBound - lowerBound) / 2;
int indexKey = fieldKeys[index];
if (indexKey > key) {
if (index == upperBound) {
upperBound --;
} else {
upperBound = index;
}
} else if (indexKey == key) {
return index;
} else {
if (index == lowerBound) {
lowerBound ++;
} else {
lowerBound = index;
}
}
}
return ~lowerBound;
}
/**
* Stores a PIMField. O(fieldKeys.length) in space and time.
* @param key property key for requested field
* @param field identifier of field
*/
public void putField(int key, PIMField field) {
int index = findFieldKey(key);
if (index >= 0) {
fieldValues[index] = field;
} else {
index = ~index;
int[] newKeys = new int[fieldKeys.length + 1];
PIMField[] newFields = new PIMField[fieldValues.length + 1];
System.arraycopy(fieldKeys, 0, newKeys, 0, index);
System.arraycopy(fieldValues, 0, newFields, 0, index);
newKeys[index] = key;
newFields[index] = field;
System.arraycopy(fieldKeys, index, newKeys, index + 1,
fieldKeys.length - index);
System.arraycopy(fieldValues, index, newFields, index + 1,
fieldKeys.length - index);
this.fieldKeys = newKeys;
this.fieldValues = newFields;
}
}
/**
* Looks up a PIMField. O(log fieldKeys.length) in time.
* @param key property key for requested field
* @return PIM field requested
*/
public PIMField getField(int key) {
int index = findFieldKey(key);
if (index >= 0) {
return fieldValues[index];
} else {
return null;
}
}
/**
* Removes a PIMField. O(fieldKeys.length) in space and time.
* @param key property key for requested field
*/
public void removeField(int key) {
int index = findFieldKey(key);
if (index >= 0) {
int[] newKeys = new int[fieldKeys.length - 1];
PIMField[] newFields = new PIMField[fieldValues.length - 1];
System.arraycopy(fieldKeys, 0, newKeys, 0, index);
System.arraycopy(fieldValues, 0, newFields, 0, index);
System.arraycopy(fieldKeys, index + 1, newKeys, index,
newKeys.length - index);
System.arraycopy(fieldValues, index + 1, newFields, index,
newKeys.length - index);
this.fieldKeys = newKeys;
this.fieldValues = newFields;
}
}
/**
* Checks the read only fields.
* @param field identifier of the field
* @throws IllegalArgumentException if field is not read only
*/
private void checkReadOnlyFields(int field) {
if (key != null) {
if (field == getRevisionField()) {
throw new IllegalArgumentException(
"REVISION field is read only"
+ " except on newly created PIMItems");
} else if (field == getUIDField()) {
throw new IllegalArgumentException(
"UID field is read only except on newly created PIMItems");
}
}
}
/**
* Update the revision time.
*/
private void updateRevision() {
Long value = new Long(System.currentTimeMillis());
int field = getRevisionField();
if (countValues(field) == 0) {
addValue(field, 0, value, true);
} else {
setValue(field, 0, 0, value, true);
}
}
/**
* Update the UID field.
*/
private void updateUID() {
String value = key.toString();
int field = getUIDField();
if (countValues(field) == 0) {
addValue(field, 0, value, true);
} // don't change UID value, it can be set by user
}
/**
* Gets the field that corresponds to the REVISION of this PIMItem.
*
* REVISION fields have special handling; they are set automatically
* on commit and import, and are read only to the application once
* the item has been committed.
* @return revision field
*/
protected abstract int getRevisionField();
/**
* Gets the field that corresponds to the UID of this PIMItem.
*
* UIDfields have special handling; they are set automatically
* on commit and import.
* @return UID field
*/
protected abstract int getUIDField();
/**
* Format the data for output.
* @return formatted data
*/
protected String formatData() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < fieldValues.length; i++) {
if (fieldValues[i].getValueCount() != 0) {
PIMField pimField = fieldValues[i];
int field = fieldKeys[i];
int valueCount = pimField.getValueCount();
if (valueCount == 0) {
continue;
}
if (i != 0) {
sb.append(", ");
}
String label =
pimHandler.getFieldLabel(pimListHandle, field);
int dataType =
pimHandler.getFieldDataType(pimListHandle, field);
for (int j = 0; j < valueCount; j++) {
sb.append(label);
if (valueCount != 1) {
sb.append("[");
sb.append(j);
sb.append("]");
}
sb.append("=");
Object value = pimField.getValue(j);
if (value == null) {
sb.append("null");
continue;
}
switch (dataType) {
case STRING_ARRAY: {
String[] aValue = (String[]) value;
sb.append("[");
for (int k = 0; k < aValue.length; k++) {
if (k != 0) {
sb.append(",");
}
sb.append(aValue[k]);
}
sb.append("]");
break;
}
case BINARY: {
byte[] bValue = (byte[]) value;
sb.append("<" + bValue.length + " bytes>");
break;
}
case DATE: {
long dValue = ((Long) value).longValue();
sb.append(pimHandler.composeDateTime(dValue));
break;
}
default:
sb.append(value);
}
}
}
}
if (categories != null && categories.length != 0) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append("Categories=[");
for (int i = 0; i < categories.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(categories[i]);
}
sb.append("]");
}
return sb.toString();
}
/**
* Converts the record to a printable format.
* @return formatted record
*/
protected abstract String toDisplayableString();
/**
* Convert the data to a String.
* @return formatted data
*/
public String toString() {
return "true".equals(System.getProperty("pim.debug"))
? toDisplayableString()
: super.toString();
}
/**
* Ensures valid class identifier.
* @param value class identifier to validate
* @throws IllegalArgumentException if value is not supported
*/
private void validateClass(int value) {
switch (value) {
case javax.microedition.pim.Contact.CLASS_CONFIDENTIAL:
case Contact.CLASS_PRIVATE:
case Contact.CLASS_PUBLIC:
return;
default:
throw new IllegalArgumentException(
"Invalid CLASS value: " + value);
}
}
/**
* Ensures valid priority identifier.
* @param value priority identifier to validate
* @throws IllegalArgumentException if value is not supported
*/
private void validatePriority(int value) {
if (value < 0 || value > 9) {
throw new IllegalArgumentException(
"Invalid PRIORITY value: " + value);
}
}
}
|