0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package com.sun.kvem.midp.pim;
0028:
0029: import java.io.ByteArrayOutputStream;
0030: import java.io.IOException;
0031: import javax.microedition.pim.Contact;
0032: import javax.microedition.pim.FieldFullException;
0033: import javax.microedition.pim.PIM;
0034: import javax.microedition.pim.PIMException;
0035: import javax.microedition.pim.PIMItem;
0036: import javax.microedition.pim.PIMList;
0037: import javax.microedition.pim.UnsupportedFieldException;
0038:
0039: /**
0040: * Partial implementation of PIMItem.
0041: * Extended by ContactImpl, EventImpl and ToDoImpl.
0042: *
0043: */
0044: public abstract class AbstractPIMItem implements PIMItem {
0045:
0046: /** Sorted list of defined field codes. */
0047: private int[] fieldKeys = new int[0];
0048:
0049: /** List of defined PIMFields. Indexed by fieldKeys. */
0050:
0051: private PIMField[] fieldValues = new PIMField[0];
0052:
0053: /** List of categories to which this item belongs */
0054: private String[] categories = null;
0055:
0056: /** Has this item been modified since it was last committed? */
0057: private boolean modified = true;
0058:
0059: /** List which this item belongs to. May be null for a deserialized item. */
0060: private AbstractPIMList pimList;
0061:
0062: /**
0063: * Type of list this item belongs to (e.g. PIM.CONTACT_LIST).
0064: * Note: valid even if pimList is null.
0065: */
0066: private final int listType;
0067:
0068: /**
0069: * Internal list used for item description getting. May not be null.
0070: * For a deserialized item represents default dummy list
0071: */
0072: private Object pimListHandle;
0073:
0074: /** Key for the use of commit() */
0075: private Object key = null;
0076:
0077: /** Instance of PIMHandler to simplify access to PIM storage */
0078: private PIMHandler pimHandler;
0079:
0080: /**
0081: * Constructs a PIM list.
0082: * @param pimList initial list
0083: * @param type type of list
0084: */
0085: protected AbstractPIMItem(AbstractPIMList pimList, int type) {
0086: this .pimList = pimList;
0087: this .listType = type;
0088: pimHandler = PIMHandler.getInstance();
0089:
0090: try {
0091: pimListHandle = pimList != null ? pimList.getHandle()
0092: : pimHandler.openList(type, null, PIM.READ_ONLY);
0093: } catch (PIMException e) {
0094: throw new RuntimeException(
0095: "Error while opening default list");
0096: }
0097: }
0098:
0099: /**
0100: * This constructor is used when importing an item.
0101: * @param pimList initial list
0102: * @param baseItem initial entry
0103: */
0104: protected AbstractPIMItem(AbstractPIMList pimList, PIMItem baseItem) {
0105: this (pimList, pimList.getType());
0106: // copy fields
0107: int[] fields = baseItem.getFields();
0108: for (int i = 0; i < fields.length; i++) {
0109: int field = fields[i];
0110: if (!pimList.isSupportedField(field)) {
0111: // skip field
0112: continue;
0113: }
0114: int dataType = pimList.getFieldDataType(field);
0115: int indices = baseItem.countValues(field);
0116: for (int index = 0; index < indices; index++) {
0117: int attributes = baseItem.getAttributes(field, index);
0118: Object value = null;
0119: switch (dataType) {
0120: case PIMItem.BINARY: {
0121: value = baseItem.getBinary(field, index);
0122: break;
0123: }
0124: case PIMItem.BOOLEAN: {
0125: value = new Boolean(baseItem.getBoolean(field,
0126: index));
0127: break;
0128: }
0129: case PIMItem.DATE: {
0130: value = new Long(baseItem.getDate(field, index));
0131: break;
0132: }
0133: case PIMItem.INT: {
0134: value = new Integer(baseItem.getInt(field, index));
0135: break;
0136: }
0137: case PIMItem.STRING: {
0138: value = baseItem.getString(field, index);
0139: break;
0140: }
0141: case PIMItem.STRING_ARRAY: {
0142: value = baseItem.getStringArray(field, index);
0143: break;
0144: }
0145: default: {
0146: // cannot import this data. Not a problem, since
0147: // this method is called when importing a PIMItem
0148: // from another list. In this case, it is valid
0149: // to ignore data of an unknown type.
0150: }
0151: } // end switch (dataType)
0152: try {
0153: addValue(field, attributes, value, true);
0154: } catch (FieldFullException ffe) {
0155: // Too many values. It's OK to ignore values that
0156: // don't fit.
0157: } catch (IllegalArgumentException iae) {
0158: // illegal data in this field. It's OK not to import it.
0159: }
0160: } // finish iterating over indices
0161: } // finish iterating over fields
0162: updateRevision();
0163: }
0164:
0165: /**
0166: * Gets the field entry.
0167: * @param field identifier for the field
0168: * @param create if <code>true</code> create the field
0169: * if it doesn't already exist
0170: * @param check if <code>true</code> check that the field is
0171: * supported before attempting to get it
0172: * @return the request field entry
0173: */
0174: PIMField getField(int field, boolean create, boolean check) {
0175: PIMField f = getField(field);
0176: if (f == null) {
0177: if (check
0178: && !pimHandler.isSupportedField(pimListHandle,
0179: field)) {
0180: throw complaintAboutField(listType, field);
0181: }
0182: if (create) {
0183: f = new EmptyPIMField();
0184: putField(field, f);
0185: }
0186: }
0187: return f;
0188: }
0189:
0190: /**
0191: * Sets the field value.
0192: * @param field identifier of field
0193: * @param index value offset
0194: * @param attributes field properties
0195: * @param value field to update
0196: * @param force if <code>true</code> create the value
0197: */
0198: private void setValue(int field, int index, int attributes,
0199: Object value, boolean force) {
0200: try {
0201: checkType(field, value);
0202: PIMField pimField = getField(field, false, true);
0203: if (pimField == null) {
0204: throw new IndexOutOfBoundsException("Empty field: "
0205: + field);
0206: }
0207: int currentValues = pimField.getValueCount();
0208: if (index < 0 || index >= currentValues) {
0209: throw new IndexOutOfBoundsException("0 <= index < "
0210: + currentValues + ", " + index
0211: + " not in range");
0212: }
0213: if (!force) {
0214: checkReadOnlyFields(field);
0215: }
0216: if (value instanceof Integer) {
0217: checkIntValue(field, ((Integer) value).intValue());
0218: }
0219: attributes = filterAttributes(field, attributes);
0220: pimField.setValue(attributes, value, index);
0221: modified = true;
0222: } catch (ClassCastException e) {
0223: throw new IllegalArgumentException("Wrong type for field");
0224: }
0225: }
0226:
0227: /**
0228: * Adds a value to a field.
0229: *
0230: * @param field identifier of field
0231: * @param attributes field properties
0232: * @param value field to update
0233: * @param force if <code>true</code> create the value
0234: * @throws FieldFullException if no more values can be added to the field
0235: */
0236: private void addValue(int field, int attributes, Object value,
0237: boolean force) {
0238:
0239: checkType(field, value);
0240: PIMField pimField = getField(field, true, true);
0241: int maxValues = pimHandler.getMaximumValues(pimListHandle,
0242: field);
0243: int currentValues = pimField.getValueCount();
0244: if (maxValues != -1 && currentValues >= maxValues) {
0245: throw new FieldFullException("Can only store " + maxValues
0246: + " in field", field);
0247: }
0248: if (!force) {
0249: checkReadOnlyFields(field);
0250: }
0251: if (value instanceof Integer) {
0252: checkIntValue(field, ((Integer) value).intValue());
0253: }
0254: if (pimField.isScalar()) {
0255: // upgrade PIM field
0256: if (currentValues == 0) {
0257: pimField = new ScalarPIMField();
0258: putField(field, pimField);
0259: } else {
0260: Object value0 = pimField.getValue(0);
0261: int attributes0 = pimField.getAttributes(0);
0262: pimField = new VectorPIMField();
0263: pimField.addValue(attributes0, value0);
0264: putField(field, pimField);
0265: }
0266: }
0267: attributes = filterAttributes(field, attributes);
0268: pimField.addValue(attributes, value);
0269: modified = true;
0270: }
0271:
0272: private void checkIntValue(int field, int value) {
0273: if ((listType == PIM.CONTACT_LIST && field == Contact.CLASS)
0274: || (listType == PIM.EVENT_LIST && field == Event.CLASS)
0275: || (listType == PIM.TODO_LIST && field == ToDo.CLASS)) {
0276: validateClass(value);
0277: }
0278: if (listType == PIM.TODO_LIST && field == ToDo.PRIORITY) {
0279: validatePriority(value);
0280: }
0281: }
0282:
0283: /**
0284: * Filters attributes to include only the supported ones.
0285: * @param field identifier of field
0286: * @param attributes field properties
0287: * @return filtered attributes
0288: */
0289: private int filterAttributes(int field, int attributes) {
0290: if (attributes == 0) {
0291: return 0;
0292: } else {
0293: return attributes
0294: & pimHandler.getSupportedAttributesMask(
0295: pimListHandle, field);
0296: }
0297: }
0298:
0299: /**
0300: * Gets current value.
0301: * @param field identifier of field
0302: * @param index field identifier
0303: * @return requested field
0304: */
0305: private Object getValue(int field, int index) {
0306: PIMField pimField = getField(field, false, true);
0307: if (pimField == null) {
0308: throw new IndexOutOfBoundsException("Empty field: " + field);
0309: }
0310: int currentValues = pimField.getValueCount();
0311: if (index < 0 || index >= currentValues) {
0312: throw new IndexOutOfBoundsException("0 <= index < "
0313: + currentValues + ", " + index + " not in range");
0314: }
0315: return pimField.getValue(index);
0316: }
0317:
0318: // JAVADOC COMMENT ELIDED
0319: public void addStringArray(int field, int attributes, String[] value) {
0320: checkType(field, STRING_ARRAY);
0321: validateStringArray(field, value);
0322: addValue(field, attributes, value, false);
0323: }
0324:
0325: // JAVADOC COMMENT ELIDED
0326: public void addBoolean(int field, int attributes, boolean value) {
0327: addValue(field, attributes, new Boolean(value), false);
0328: }
0329:
0330: // JAVADOC COMMENT ELIDED
0331: public void removeFromCategory(String category) {
0332: if (category == null) {
0333: throw new NullPointerException("Null category");
0334: }
0335: if (categories != null) {
0336: for (int i = 0; i < categories.length; i++) {
0337: if (category.equals(categories[i])) {
0338: if (categories.length == 1) {
0339: this .categories = null;
0340: } else {
0341: String[] a = new String[categories.length - 1];
0342: System.arraycopy(categories, 0, a, 0, i);
0343: System.arraycopy(categories, i + 1, a, i,
0344: a.length - i);
0345: this .categories = a;
0346: }
0347: this .modified = true;
0348: return;
0349: }
0350: }
0351: }
0352: }
0353:
0354: // JAVADOC COMMENT ELIDED
0355: public int[] getFields() {
0356: int emptyFields = 0;
0357: // make sure all these fields have defined values
0358: for (int i = 0; i < fieldValues.length; i++) {
0359: if (fieldValues[i].getValueCount() == 0) {
0360: emptyFields++;
0361: }
0362: }
0363: int[] keys = new int[fieldKeys.length - emptyFields];
0364: for (int i = 0, j = 0; i < keys.length; i++) {
0365: if (emptyFields == 0 || fieldValues[i].getValueCount() != 0) {
0366: keys[j++] = fieldKeys[i];
0367: } else {
0368: emptyFields--;
0369: }
0370: }
0371: return keys;
0372: }
0373:
0374: // JAVADOC COMMENT ELIDED
0375: public boolean getBoolean(int field, int index) {
0376: checkType(field, BOOLEAN);
0377: return ((Boolean) getValue(field, index)).booleanValue();
0378: }
0379:
0380: // JAVADOC COMMENT ELIDED
0381: public void addDate(int field, int attributes, long value) {
0382: addValue(field, attributes, new Long(value), false);
0383: }
0384:
0385: // JAVADOC COMMENT ELIDED
0386: public int maxCategories() {
0387: return -1;
0388: }
0389:
0390: // JAVADOC COMMENT ELIDED
0391: public void setDate(int field, int index, int attributes, long value) {
0392: setValue(field, index, attributes, new Long(value), false);
0393: }
0394:
0395: // JAVADOC COMMENT ELIDED
0396: public int getInt(int field, int index) {
0397: checkType(field, INT);
0398: try {
0399: return ((Integer) getValue(field, index)).intValue();
0400: } catch (ClassCastException e) {
0401: String message = "Cannot convert to integer on field "
0402: + field + ": " + getValue(field, index).getClass();
0403: throw new ClassCastException(message);
0404: }
0405: }
0406:
0407: // JAVADOC COMMENT ELIDED
0408: public void setBinary(int field, int index, int attributes,
0409: byte[] value, int offset, int length) {
0410: validateBinaryValue(value, offset, length);
0411: length = Math.min(length, value.length - offset);
0412: byte[] b = new byte[length];
0413: System.arraycopy(value, offset, b, 0, length);
0414: setValue(field, index, attributes, b, false);
0415: }
0416:
0417: // JAVADOC COMMENT ELIDED
0418: public int getAttributes(int field, int index) {
0419: return getField(field, true, true).getAttributes(index);
0420: }
0421:
0422: // JAVADOC COMMENT ELIDED
0423: public int countValues(int field) {
0424: PIMField pimField = getField(field, false, true);
0425: return pimField == null ? 0 : pimField.getValueCount();
0426: }
0427:
0428: // JAVADOC COMMENT ELIDED
0429: public void addString(int field, int attributes, String value) {
0430: validateString(value);
0431: addValue(field, attributes, value, false);
0432: }
0433:
0434: // JAVADOC COMMENT ELIDED
0435: public String[] getCategories() {
0436: if (categories == null) {
0437: return new String[0];
0438: }
0439: String[] cs = new String[categories.length];
0440: System.arraycopy(categories, 0, cs, 0, categories.length);
0441: return cs;
0442: }
0443:
0444: // JAVADOC COMMENT ELIDED
0445: String[] getCategoriesRaw() {
0446: return categories;
0447: }
0448:
0449: // JAVADOC COMMENT ELIDED
0450: public void setInt(int field, int index, int attributes, int value) {
0451: setValue(field, index, attributes, new Integer(value), false);
0452: }
0453:
0454: // JAVADOC COMMENT ELIDED
0455: public void setStringArray(int field, int index, int attributes,
0456: String[] value) {
0457: checkType(field, STRING_ARRAY);
0458: validateStringArray(field, value);
0459: setValue(field, index, attributes, value, false);
0460: }
0461:
0462: /**
0463: * Makes sure that
0464: * <ul>
0465: * <li>The string array is not null
0466: * <li>At least one string in the array is not null
0467: * <li>The string array has the correct length
0468: * </ul>
0469: * @param field identifier for field
0470: * @param a string array to be checked
0471: */
0472: private void validateStringArray(int field, String[] a) {
0473: int requiredLength = pimHandler.getStringArraySize(
0474: pimListHandle, field);
0475: if (a.length != requiredLength) {
0476: throw new IllegalArgumentException(
0477: "String array length incorrect: should be "
0478: + requiredLength);
0479: }
0480: for (int i = 0; i < a.length; i++) {
0481: if (a[i] != null) {
0482: return;
0483: }
0484: }
0485: throw new IllegalArgumentException(
0486: "No non-null elements in array");
0487: }
0488:
0489: /**
0490: * Makes sure that a string is not null.
0491: * @param value string to be checked
0492: */
0493: private void validateString(String value) {
0494: if (value == null) {
0495: throw new NullPointerException(
0496: "String field value should not be null");
0497: }
0498: }
0499:
0500: // JAVADOC COMMENT ELIDED
0501: public long getDate(int field, int index) {
0502: checkType(field, DATE);
0503: try {
0504: return ((Long) getValue(field, index)).longValue();
0505: } catch (ClassCastException e) {
0506: throw e;
0507: }
0508: }
0509:
0510: // JAVADOC COMMENT ELIDED
0511: public void addToCategory(String category) throws PIMException {
0512: if (category == null) {
0513: throw new NullPointerException("Null category");
0514: }
0515: if (categories == null) {
0516: this .categories = new String[] { category };
0517: this .modified = true;
0518: } else {
0519: for (int i = 0; i < categories.length; i++) {
0520: if (categories[i].equals(category)) {
0521: return;
0522: }
0523: }
0524: String[] a = new String[categories.length + 1];
0525: System.arraycopy(categories, 0, a, 0, categories.length);
0526: a[categories.length] = category;
0527: this .categories = a;
0528: this .modified = true;
0529: }
0530: }
0531:
0532: // JAVADOC COMMENT ELIDED
0533: public void addInt(int field, int attributes, int value) {
0534: addValue(field, attributes, new Integer(value), false);
0535: }
0536:
0537: // JAVADOC COMMENT ELIDED
0538: public byte[] getBinary(int field, int index) {
0539: checkType(field, BINARY);
0540: return (byte[]) getValue(field, index);
0541: }
0542:
0543: // JAVADOC COMMENT ELIDED
0544: public void addBinary(int field, int attributes, byte[] value,
0545: int offset, int length) {
0546: validateBinaryValue(value, offset, length);
0547: length = Math.min(length, value.length - offset);
0548: byte[] b = new byte[length];
0549: System.arraycopy(value, offset, b, 0, length);
0550: addValue(field, attributes, b, false);
0551: }
0552:
0553: /**
0554: * Ensures that binary parameters are in range.
0555: * @param value binary data to be checked
0556: * @param offset index into byte array
0557: * @param length of data to be checked
0558: * @throws NullPointerException if value is null
0559: * @throws IllegalArgumentException if offset or length
0560: * are not valid
0561: */
0562: private void validateBinaryValue(byte[] value, int offset,
0563: int length) {
0564: if (value == null) {
0565: throw new NullPointerException("Binary field value"
0566: + " should not be null");
0567: }
0568: if (offset < 0) {
0569: throw new IllegalArgumentException("Negative offset");
0570: }
0571: if (offset + length > value.length) {
0572: throw new IllegalArgumentException("Offset out of range");
0573: }
0574: if (length <= 0) {
0575: throw new IllegalArgumentException(
0576: "Length must be at least 1");
0577: }
0578: if (value.length == 0) {
0579: throw new IllegalArgumentException("Binary array value "
0580: + "has zero length");
0581: }
0582: }
0583:
0584: // JAVADOC COMMENT ELIDED
0585: public String[] getStringArray(int field, int index) {
0586: checkType(field, STRING_ARRAY);
0587: return (String[]) getValue(field, index);
0588: }
0589:
0590: // JAVADOC COMMENT ELIDED
0591: public void setBoolean(int field, int index, int attributes,
0592: boolean value) {
0593: setValue(field, index, attributes, new Boolean(value), false);
0594: }
0595:
0596: // JAVADOC COMMENT ELIDED
0597: public PIMList getPIMList() {
0598: return pimList;
0599: }
0600:
0601: /**
0602: * Returns the handle of the PIMList associated with this item.
0603: *
0604: * @return the handle of the PIMList that this item belongs to. If the
0605: * item does not belong to any list, handle of default dummy
0606: * list is returned.
0607: */
0608: public Object getPIMListHandle() {
0609: return pimListHandle;
0610: }
0611:
0612: /**
0613: * Set the PIMList of this item.
0614: * @param list data to be saved
0615: */
0616: void setPIMList(AbstractPIMList list) {
0617: this .pimList = list;
0618: pimListHandle = list.getHandle();
0619: }
0620:
0621: // JAVADOC COMMENT ELIDED
0622: public void removeValue(int field, int index) {
0623: PIMField pimField = getField(field, false, true);
0624: if (pimField == null) {
0625: throw new IndexOutOfBoundsException("Empty field: " + field);
0626: }
0627: int currentValues = pimField.getValueCount();
0628: if (index < 0 || index >= currentValues) {
0629: throw new IndexOutOfBoundsException("0 <= index < "
0630: + currentValues + ", " + index + " not in range");
0631: }
0632: checkReadOnlyFields(field);
0633: pimField.removeValue(index);
0634: currentValues--;
0635: if (currentValues == 0) {
0636: removeField(field);
0637: } else if (currentValues == 1) {
0638: // downgrade field
0639: Object value = pimField.getValue(0);
0640: int attributes = pimField.getAttributes(0);
0641: pimField = new ScalarPIMField();
0642: pimField.addValue(attributes, value);
0643: putField(field, pimField);
0644: }
0645: modified = true;
0646: }
0647:
0648: // JAVADOC COMMENT ELIDED
0649: public String getString(int field, int index) {
0650: checkType(field, STRING);
0651: return (String) getValue(field, index);
0652: }
0653:
0654: // JAVADOC COMMENT ELIDED
0655: public void setString(int field, int index, int attributes,
0656: String value) {
0657: validateString(value);
0658: setValue(field, index, attributes, value, false);
0659: }
0660:
0661: // JAVADOC COMMENT ELIDED
0662: public boolean isModified() {
0663: return modified;
0664: }
0665:
0666: /**
0667: * Sets the modified flag.
0668: * @param modified flag to be saved
0669: */
0670: void setModified(boolean modified) {
0671: this .modified = modified;
0672: }
0673:
0674: // JAVADOC COMMENT ELIDED
0675: public void commit() throws PIMException {
0676: if (pimList == null) {
0677: throw new PIMException("Item is not in a list");
0678: }
0679: pimList.checkWritePermission();
0680: pimList.checkOpen();
0681: updateRevision();
0682: setDefaultValues();
0683: try {
0684: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0685: PIMFormat format = getEncodingFormat();
0686: format.encode(baos, "UTF-8", this );
0687: Object newKey = pimList.commit(key, baos.toByteArray(),
0688: categories);
0689: if (key == null) {
0690: pimList.addItem(this );
0691: }
0692: setKey(newKey);
0693: updateUID();
0694: modified = false;
0695: } catch (IOException e) {
0696: throw new PIMException("Error persisting PIMItem");
0697: }
0698: }
0699:
0700: /**
0701: * Gets the format codec used to encode and decode this object
0702: * for storage.
0703: * @return an instance of PIMFormat that can deal with this item
0704: */
0705: abstract PIMFormat getEncodingFormat();
0706:
0707: /**
0708: * Checks the category is valid.
0709: * @param category name of category to check
0710: * @return <code>true</code> if is int category
0711: */
0712: boolean isInCategory(String category) {
0713: if (categories == null) {
0714: return false;
0715: } else {
0716: for (int i = 0; i < categories.length; i++) {
0717: if (categories[i].equals(category)) {
0718: return true;
0719: }
0720: }
0721: return false;
0722: }
0723: }
0724:
0725: /**
0726: * Sets the key that identifies this item in the PIM database
0727: * @param key an Object used to index this item
0728: */
0729: void setKey(Object key) {
0730: this .key = key;
0731: if (key != null) {
0732: updateUID();
0733: }
0734: }
0735:
0736: /**
0737: * Gets the key that identifies this item in the PIM database
0738: * @return an Object used to index this item
0739: */
0740: Object getKey() {
0741: return key;
0742: }
0743:
0744: /**
0745: * Removed this PIMItem from its list
0746: */
0747: void remove() throws PIMException {
0748: if (pimList == null) {
0749: throw new PIMException("Item is not in a list");
0750: }
0751: pimList.checkWritePermission();
0752: pimList.commit(key, null, null);
0753: setKey(null);
0754: pimList = null;
0755: }
0756:
0757: /**
0758: * Sets default values for this item.
0759: */
0760: protected void setDefaultValues() {
0761: int[] supportedFields = pimList.getSupportedFields();
0762: for (int i = 0; i < supportedFields.length; i++) {
0763: int field = supportedFields[i];
0764: PIMField pimField = getField(field, false, true);
0765: if ((pimField == null || pimField.getValueCount() == 0)
0766: && pimHandler.hasDefaultValue(pimListHandle, field)) {
0767:
0768: Object value = null;
0769: switch (pimList.getFieldDataType(field)) {
0770: case PIMItem.BOOLEAN:
0771: value = new Boolean(pimHandler
0772: .getDefaultBooleanValue(pimListHandle,
0773: field));
0774: break;
0775: case PIMItem.BINARY:
0776: value = pimHandler.getDefaultBinaryValue(
0777: pimListHandle, field);
0778: break;
0779: case PIMItem.DATE:
0780: value = new Long(pimHandler.getDefaultDateValue(
0781: pimListHandle, field));
0782: break;
0783: case PIMItem.INT:
0784: value = new Integer(pimHandler.getDefaultIntValue(
0785: pimListHandle, field));
0786: break;
0787: case PIMItem.STRING:
0788: value = pimHandler.getDefaultStringValue(
0789: pimListHandle, field);
0790: break;
0791: case PIMItem.STRING_ARRAY:
0792: value = pimHandler.getDefaultStringArrayValue(
0793: pimListHandle, field);
0794: break;
0795: default:
0796: continue;
0797: }
0798: addValue(field, PIMItem.ATTR_NONE, value, false);
0799: }
0800: }
0801: }
0802:
0803: /**
0804: * Checks for valid PIM field.
0805: * @param type list type
0806: * @param field identifier for field
0807: * @return <code>true</code> if field is valid
0808: */
0809: static boolean isValidPIMField(int type, int field) {
0810: switch (type) {
0811: case PIM.CONTACT_LIST:
0812: return ContactImpl.isValidPIMField(field);
0813: case PIM.EVENT_LIST:
0814: return EventImpl.isValidPIMField(field);
0815: case PIM.TODO_LIST:
0816: return ToDoImpl.isValidPIMField(field);
0817: default:
0818: return false;
0819: }
0820: }
0821:
0822: /**
0823: * Checks the type of a field, throwing an IllegalArgumentException
0824: * if given or if the field number is invalid.
0825: * @param field identifier for field
0826: * @param value data to be checked
0827: * @throws IllegalArgumentException if data type is not known
0828: */
0829: private void checkType(int field, Object value) {
0830: try {
0831: int dataType = pimHandler.getFieldDataType(pimListHandle,
0832: field);
0833: switch (dataType) {
0834: case PIMItem.BINARY: {
0835: byte[] b = (byte[]) value;
0836: break;
0837: }
0838: case PIMItem.BOOLEAN: {
0839: Boolean b = (Boolean) value;
0840: break;
0841: }
0842: case PIMItem.DATE: {
0843: Long l = (Long) value;
0844: break;
0845: }
0846: case PIMItem.INT: {
0847: Integer i = (Integer) value;
0848: break;
0849: }
0850: case PIMItem.STRING: {
0851: String s = (String) value;
0852: break;
0853: }
0854: case PIMItem.STRING_ARRAY: {
0855: String[] s = (String[]) value;
0856: break;
0857: }
0858: default:
0859: throw complaintAboutField(listType, field);
0860: }
0861: } catch (ClassCastException cce) {
0862: throw new IllegalArgumentException(cce.getMessage());
0863: }
0864: }
0865:
0866: /**
0867: * Checks the type of a field, throwing an IllegalArgumentException
0868: * if given or if the field number is invalid.
0869: * @param field identifier of field
0870: * @param dataType data type for field value
0871: */
0872: private void checkType(int field, int dataType) {
0873: int correctDataType = pimHandler.getFieldDataType(
0874: pimListHandle, field);
0875: if (dataType != correctDataType && correctDataType != -1) {
0876: throw new IllegalArgumentException("Wrong data type");
0877: }
0878: if (correctDataType == -1) {
0879: throw complaintAboutField(listType, field);
0880: }
0881: }
0882:
0883: /**
0884: * Throw an exception based on field failure type.
0885: * @param type list type
0886: * @param field identifier of field
0887: * @return UnsupportedFieldException if the field value is not supported
0888: * in the field or IllegalArgumentException, if the field is not valid
0889: */
0890: static RuntimeException complaintAboutField(int type, int field) {
0891: if (isValidPIMField(type, field)) {
0892: return new UnsupportedFieldException(String.valueOf(field));
0893: } else {
0894: return new IllegalArgumentException("Invalid field "
0895: + field);
0896: }
0897: }
0898:
0899: /**
0900: * Returns the index of the given key, if it is present in
0901: * fieldKeys[]. If it is not present, returns the binary
0902: * complement of the index before which
0903: * the key could be inserted. O(log fieldKeys.length) in time.
0904: * @param key property key for requested field
0905: * @return the index of the field key
0906: */
0907: private int findFieldKey(int key) {
0908: int lowerBound = 0;
0909: int upperBound = fieldKeys.length;
0910: while (lowerBound != upperBound) {
0911: int index = lowerBound + (upperBound - lowerBound) / 2;
0912: int indexKey = fieldKeys[index];
0913: if (indexKey > key) {
0914: if (index == upperBound) {
0915: upperBound--;
0916: } else {
0917: upperBound = index;
0918: }
0919: } else if (indexKey == key) {
0920: return index;
0921: } else {
0922: if (index == lowerBound) {
0923: lowerBound++;
0924: } else {
0925: lowerBound = index;
0926: }
0927: }
0928: }
0929: return ~lowerBound;
0930: }
0931:
0932: /**
0933: * Stores a PIMField. O(fieldKeys.length) in space and time.
0934: * @param key property key for requested field
0935: * @param field identifier of field
0936: */
0937: public void putField(int key, PIMField field) {
0938: int index = findFieldKey(key);
0939: if (index >= 0) {
0940: fieldValues[index] = field;
0941: } else {
0942: index = ~index;
0943: int[] newKeys = new int[fieldKeys.length + 1];
0944: PIMField[] newFields = new PIMField[fieldValues.length + 1];
0945: System.arraycopy(fieldKeys, 0, newKeys, 0, index);
0946: System.arraycopy(fieldValues, 0, newFields, 0, index);
0947: newKeys[index] = key;
0948: newFields[index] = field;
0949: System.arraycopy(fieldKeys, index, newKeys, index + 1,
0950: fieldKeys.length - index);
0951: System.arraycopy(fieldValues, index, newFields, index + 1,
0952: fieldKeys.length - index);
0953: this .fieldKeys = newKeys;
0954: this .fieldValues = newFields;
0955: }
0956: }
0957:
0958: /**
0959: * Looks up a PIMField. O(log fieldKeys.length) in time.
0960: * @param key property key for requested field
0961: * @return PIM field requested
0962: */
0963: public PIMField getField(int key) {
0964: int index = findFieldKey(key);
0965: if (index >= 0) {
0966: return fieldValues[index];
0967: } else {
0968: return null;
0969: }
0970: }
0971:
0972: /**
0973: * Removes a PIMField. O(fieldKeys.length) in space and time.
0974: * @param key property key for requested field
0975: */
0976: public void removeField(int key) {
0977: int index = findFieldKey(key);
0978: if (index >= 0) {
0979: int[] newKeys = new int[fieldKeys.length - 1];
0980: PIMField[] newFields = new PIMField[fieldValues.length - 1];
0981: System.arraycopy(fieldKeys, 0, newKeys, 0, index);
0982: System.arraycopy(fieldValues, 0, newFields, 0, index);
0983: System.arraycopy(fieldKeys, index + 1, newKeys, index,
0984: newKeys.length - index);
0985: System.arraycopy(fieldValues, index + 1, newFields, index,
0986: newKeys.length - index);
0987: this .fieldKeys = newKeys;
0988: this .fieldValues = newFields;
0989: }
0990: }
0991:
0992: /**
0993: * Checks the read only fields.
0994: * @param field identifier of the field
0995: * @throws IllegalArgumentException if field is not read only
0996: */
0997: private void checkReadOnlyFields(int field) {
0998: if (key != null) {
0999: if (field == getRevisionField()) {
1000: throw new IllegalArgumentException(
1001: "REVISION field is read only"
1002: + " except on newly created PIMItems");
1003: } else if (field == getUIDField()) {
1004: throw new IllegalArgumentException(
1005: "UID field is read only except on newly created PIMItems");
1006: }
1007: }
1008: }
1009:
1010: /**
1011: * Update the revision time.
1012: */
1013: private void updateRevision() {
1014: Long value = new Long(System.currentTimeMillis());
1015: int field = getRevisionField();
1016: if (countValues(field) == 0) {
1017: addValue(field, 0, value, true);
1018: } else {
1019: setValue(field, 0, 0, value, true);
1020: }
1021: }
1022:
1023: /**
1024: * Update the UID field.
1025: */
1026: private void updateUID() {
1027: String value = key.toString();
1028: int field = getUIDField();
1029: if (countValues(field) == 0) {
1030: addValue(field, 0, value, true);
1031: } // don't change UID value, it can be set by user
1032: }
1033:
1034: /**
1035: * Gets the field that corresponds to the REVISION of this PIMItem.
1036: *
1037: * REVISION fields have special handling; they are set automatically
1038: * on commit and import, and are read only to the application once
1039: * the item has been committed.
1040: * @return revision field
1041: */
1042: protected abstract int getRevisionField();
1043:
1044: /**
1045: * Gets the field that corresponds to the UID of this PIMItem.
1046: *
1047: * UIDfields have special handling; they are set automatically
1048: * on commit and import.
1049: * @return UID field
1050: */
1051: protected abstract int getUIDField();
1052:
1053: /**
1054: * Format the data for output.
1055: * @return formatted data
1056: */
1057: protected String formatData() {
1058: StringBuffer sb = new StringBuffer();
1059: for (int i = 0; i < fieldValues.length; i++) {
1060: if (fieldValues[i].getValueCount() != 0) {
1061: PIMField pimField = fieldValues[i];
1062: int field = fieldKeys[i];
1063: int valueCount = pimField.getValueCount();
1064: if (valueCount == 0) {
1065: continue;
1066: }
1067: if (i != 0) {
1068: sb.append(", ");
1069: }
1070: String label = pimHandler.getFieldLabel(pimListHandle,
1071: field);
1072: int dataType = pimHandler.getFieldDataType(
1073: pimListHandle, field);
1074: for (int j = 0; j < valueCount; j++) {
1075: sb.append(label);
1076: if (valueCount != 1) {
1077: sb.append("[");
1078: sb.append(j);
1079: sb.append("]");
1080: }
1081: sb.append("=");
1082: Object value = pimField.getValue(j);
1083: if (value == null) {
1084: sb.append("null");
1085: continue;
1086: }
1087: switch (dataType) {
1088: case STRING_ARRAY: {
1089: String[] aValue = (String[]) value;
1090: sb.append("[");
1091: for (int k = 0; k < aValue.length; k++) {
1092: if (k != 0) {
1093: sb.append(",");
1094: }
1095: sb.append(aValue[k]);
1096: }
1097: sb.append("]");
1098: break;
1099: }
1100: case BINARY: {
1101: byte[] bValue = (byte[]) value;
1102: sb.append("<" + bValue.length + " bytes>");
1103: break;
1104: }
1105: case DATE: {
1106: long dValue = ((Long) value).longValue();
1107: sb.append(pimHandler.composeDateTime(dValue));
1108: break;
1109: }
1110: default:
1111: sb.append(value);
1112: }
1113: }
1114: }
1115: }
1116: if (categories != null && categories.length != 0) {
1117: if (sb.length() > 0) {
1118: sb.append(", ");
1119: }
1120: sb.append("Categories=[");
1121: for (int i = 0; i < categories.length; i++) {
1122: if (i > 0) {
1123: sb.append(",");
1124: }
1125: sb.append(categories[i]);
1126: }
1127: sb.append("]");
1128: }
1129: return sb.toString();
1130: }
1131:
1132: /**
1133: * Converts the record to a printable format.
1134: * @return formatted record
1135: */
1136: protected abstract String toDisplayableString();
1137:
1138: /**
1139: * Convert the data to a String.
1140: * @return formatted data
1141: */
1142: public String toString() {
1143: return "true".equals(System.getProperty("pim.debug")) ? toDisplayableString()
1144: : super .toString();
1145: }
1146:
1147: /**
1148: * Ensures valid class identifier.
1149: * @param value class identifier to validate
1150: * @throws IllegalArgumentException if value is not supported
1151: */
1152: private void validateClass(int value) {
1153: switch (value) {
1154: case javax.microedition.pim.Contact.CLASS_CONFIDENTIAL:
1155: case Contact.CLASS_PRIVATE:
1156: case Contact.CLASS_PUBLIC:
1157: return;
1158: default:
1159: throw new IllegalArgumentException("Invalid CLASS value: "
1160: + value);
1161: }
1162: }
1163:
1164: /**
1165: * Ensures valid priority identifier.
1166: * @param value priority identifier to validate
1167: * @throws IllegalArgumentException if value is not supported
1168: */
1169: private void validatePriority(int value) {
1170: if (value < 0 || value > 9) {
1171: throw new IllegalArgumentException(
1172: "Invalid PRIORITY value: " + value);
1173: }
1174: }
1175: }
|