001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.kvem.midp.pim;
028:
029: import com.sun.kvem.midp.pim.formats.VCalendar10Format;
030: import com.sun.kvem.midp.pim.formats.VCard21Format;
031: import com.sun.kvem.midp.pim.formats.VCard30Format;
032: import com.sun.midp.midlet.MIDletSuite;
033: import com.sun.midp.midlet.Scheduler;
034: import com.sun.midp.security.Permissions;
035: import java.io.ByteArrayInputStream;
036: import java.io.IOException;
037: import java.io.InputStream;
038: import java.io.OutputStream;
039: import java.io.UnsupportedEncodingException;
040: import javax.microedition.pim.PIM;
041: import javax.microedition.pim.PIMException;
042: import javax.microedition.pim.PIMItem;
043: import javax.microedition.pim.PIMList;
044:
045: /**
046: * Implementation of PIM.
047: *
048: */
049: public final class PIMImpl extends PIM {
050: /**
051: * Supported serial formats.
052: */
053: private static final PIMFormat[] formats = {
054: new VCalendar10Format(), new VCard21Format(),
055: new VCard30Format() };
056:
057: /** PIMImpl constructor, called from PIM.getInstance(). */
058: public PIMImpl() {
059: }
060:
061: /**
062: * Gets an array of PIM items from an encoded input
063: * stream.
064: * @param is data input stream
065: * @param enc character encoding of stream data
066: * @return array of PIM items
067: * @throws PIMException if any error reading the PIM data
068: * @throws UnsupportedEncodingException if encoding is not supported
069: */
070: public PIMItem[] fromSerialFormat(InputStream is, String enc)
071: throws PIMException, UnsupportedEncodingException {
072: return fromSerialFormat(is, enc, null);
073: }
074:
075: /**
076: * Gets an array of PIM items from an encoded input
077: * stream and list.
078: * @param is data input stream
079: * @param enc character encoding of stream data
080: * @param list populate from list
081: * @return array of PIM items
082: * @throws PIMException if any error reading the PIM data
083: * @throws UnsupportedEncodingException if encoding is not supported
084: */
085: private PIMItem[] fromSerialFormat(InputStream is, String enc,
086: PIMList list) throws PIMException,
087: UnsupportedEncodingException {
088:
089: if (enc == null) {
090: enc = "UTF-8" /* NO I18N */;
091: }
092: InputStream in = new MarkableInputStream(is);
093: in.mark(Integer.MAX_VALUE);
094: try {
095: for (int i = 0; i < formats.length; i++) {
096: try {
097: PIMItem[] items = formats[i].decode(in, enc, list);
098: if (items == null) {
099: throw new PIMException(
100: "Empty stream or insufficient data");
101: }
102: return items;
103: } catch (UnsupportedPIMFormatException e) {
104: in.reset();
105: in.mark(Integer.MAX_VALUE);
106: }
107: }
108: throw new PIMException("Format is not recognized");
109: } catch (UnsupportedEncodingException e) {
110: throw e;
111: } catch (IOException e) {
112: throw new PIMException(e.getMessage());
113: }
114: }
115:
116: /**
117: * Gets the current PIM lists.
118: * @param pimListType type of list to return
119: * @return array of list names
120: */
121: public String[] listPIMLists(int pimListType) {
122: checkPermissions(pimListType, PIM.READ_ONLY);
123: validatePimListType(pimListType);
124: return PIMHandler.getInstance().getListNames(pimListType);
125: }
126:
127: /**
128: * Gets the permissions that need to be present to open a list
129: *
130: * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST
131: * @param mode READ_ONLY, WRITE_ONLY or READ_WRITE
132: * @return list of permissions to be checked
133: * @throws IllegalArgumentException if one of the parameters
134: * is out of bounds
135: */
136: private int[] getPermissions(int listType, int mode) {
137: switch (listType) {
138: case CONTACT_LIST:
139: switch (mode) {
140: case READ_ONLY:
141: return new int[] { Permissions.PIM_CONTACT_READ };
142: case WRITE_ONLY:
143: return new int[] { Permissions.PIM_CONTACT_WRITE };
144: case READ_WRITE:
145: return new int[] { Permissions.PIM_CONTACT_READ,
146: Permissions.PIM_CONTACT_WRITE };
147: default:
148: throw new IllegalArgumentException("Not a valid mode: "
149: + mode);
150: }
151: case EVENT_LIST:
152: switch (mode) {
153: case READ_ONLY:
154: return new int[] { Permissions.PIM_EVENT_READ };
155: case WRITE_ONLY:
156: return new int[] { Permissions.PIM_EVENT_WRITE };
157: case READ_WRITE:
158: return new int[] { Permissions.PIM_EVENT_READ,
159: Permissions.PIM_EVENT_WRITE };
160: default:
161: throw new IllegalArgumentException("Not a valid mode: "
162: + mode);
163: }
164: case TODO_LIST:
165: switch (mode) {
166: case READ_ONLY:
167: return new int[] { Permissions.PIM_TODO_READ };
168: case WRITE_ONLY:
169: return new int[] { Permissions.PIM_TODO_WRITE };
170: case READ_WRITE:
171: return new int[] { Permissions.PIM_TODO_READ,
172: Permissions.PIM_TODO_WRITE };
173: default:
174: throw new IllegalArgumentException("Not a valid mode: "
175: + mode);
176: }
177: default:
178: throw new IllegalArgumentException(
179: "Not a valid list type: " + listType);
180: }
181: }
182:
183: /**
184: * Checks for all the permissions that need to be present to open a list
185: *
186: * @param pimListType CONTACT_LIST, EVENT_LIST or TODO_LIST
187: * @param mode READ_ONLY, WRITE_ONLY or READ_WRITE
188: * @throws IllegalArgumentException if one of the parameters is out of
189: * bounds
190: * @throws SecurityException if the application does not have the required
191: * permissions
192: */
193: private void checkPermissions(int pimListType, int mode) {
194: int[] permissions = getPermissions(pimListType, mode);
195: MIDletSuite suite = Scheduler.getScheduler().getMIDletSuite();
196: /*
197: * Do a first pass on the permissions to make sure that none is
198: * automatically denied. This is for the case when both read permission
199: * and write permission are required. It is possible that, for example,
200: * read permission is granted only after asking the user, but write
201: * permission is automatically denied. In this case, the user should
202: * not be asked a question at all.
203: */
204: for (int i = 0; i < permissions.length; i++) {
205: String name = Permissions.getName(permissions[i]);
206: int status = suite.checkPermission(name);
207: if (status == 0) {
208: // throw an exception
209: suite.checkIfPermissionAllowed(permissions[i]);
210: } else if (status == 1) {
211: // don't check this permission again
212: permissions[i] = -1;
213: }
214: }
215: for (int i = 0; i < permissions.length; i++) {
216: if (permissions[i] != -1) {
217: try {
218: suite.checkForPermission(permissions[i], null);
219: } catch (InterruptedException e) {
220: throw new SecurityException(
221: "Security check interrupted: "
222: + e.getMessage());
223: }
224: }
225: }
226: }
227:
228: /**
229: * Opens the PIM list.
230: *
231: * @param pimListType CONTACT_LIST, EVENT_LIST or TODO_LIST
232: * @param mode READ_ONLY, WRITE_ONLY or READ_WRITE
233: * @return handle to opened PIM list
234: * @throws PIMException if the list is not found
235: */
236: public PIMList openPIMList(int pimListType, int mode)
237: throws PIMException {
238: validatePimListType(pimListType);
239: validateMode(mode);
240: checkPermissions(pimListType, mode);
241: String listName = PIMHandler.getInstance().getDefaultListName(
242: pimListType);
243: if (listName == null) {
244: throw new PIMException("List not available");
245: }
246: return openPIMListImpl(pimListType, mode, listName);
247: }
248:
249: /**
250: * Opens the PIM list.
251: *
252: * @param pimListType CONTACT_LIST, EVENT_LIST or TODO_LIST
253: * @param mode READ_ONLY, WRITE_ONLY or READ_WRITE
254: * @param name name of the list
255: * @return handle to opened PIM list
256: * @throws PIMException if the list is not found
257: */
258: public PIMList openPIMList(int pimListType, int mode, String name)
259: throws PIMException {
260: if (name == null) {
261: throw new NullPointerException(
262: "PIM list name cannot be null");
263: }
264: validatePimListType(pimListType);
265: validateMode(mode);
266: checkPermissions(pimListType, mode);
267: validateName(pimListType, name);
268: return openPIMListImpl(pimListType, mode, name);
269: }
270:
271: /**
272: * Does the same as openPIMList, without any validation
273: *
274: * @param pimListType CONTACT_LIST, EVENT_LIST or TODO_LIST
275: * @param mode READ_ONLY, WRITE_ONLY or READ_WRITE
276: * @param name name of the list
277: * @return handle to opened PIM list
278: * @throws PIMException if the list is not found
279: */
280: private PIMList openPIMListImpl(int pimListType, int mode,
281: String name) throws PIMException {
282:
283: AbstractPIMList list;
284: PIMHandler handler = PIMHandler.getInstance();
285: Object listHandle = handler.openList(pimListType, name, mode);
286:
287: switch (pimListType) {
288: case PIM.CONTACT_LIST:
289: list = new ContactListImpl(name, mode, listHandle);
290: break;
291: case PIM.EVENT_LIST:
292: list = new EventListImpl(name, mode, listHandle);
293: break;
294: case PIM.TODO_LIST:
295: list = new ToDoListImpl(name, mode, listHandle);
296: break;
297: default:
298: // pimListType has been verified
299: throw new Error("Unreachable code");
300: }
301: Object[] keys = handler.getListKeys(listHandle);
302: for (int i = 0; i < keys.length; i++) {
303: byte[] data = handler.getListElement(listHandle, keys[i]);
304: String[] categories = handler.getListElementCategories(
305: listHandle, keys[i]);
306: try {
307: PIMItem[] items = fromSerialFormat(
308: new ByteArrayInputStream(data), "UTF-8", list);
309: for (int j = 0; j < items.length; j++) {
310: AbstractPIMItem item = (AbstractPIMItem) items[j];
311: item.setKey(keys[i]);
312: list.addItem(item);
313: item.setDefaultValues();
314: for (int index = 0; index < categories.length; index++) {
315: item.addToCategory(categories[index]);
316: }
317: item.setModified(false);
318: }
319: } catch (UnsupportedEncodingException e) {
320: throw new Error("UTF-8 not supported");
321: } catch (PIMException e) {
322: // skip element
323: }
324: }
325: return list;
326: }
327:
328: /**
329: * Gets the list of supported serial formats.
330: *
331: * @param pimListType CONTACT_LIST, EVENT_LIST or TODO_LIST
332: * @return array of format names
333: */
334: public String[] supportedSerialFormats(int pimListType) {
335: validatePimListType(pimListType);
336: int supportedFormatCount = 0;
337: for (int i = 0; i < formats.length; i++) {
338: if (formats[i].isTypeSupported(pimListType)) {
339: supportedFormatCount++;
340: }
341: }
342: String[] supportedFormats = new String[supportedFormatCount];
343: for (int i = 0; i < formats.length; i++) {
344: if (formats[i].isTypeSupported(pimListType)) {
345: supportedFormats[--supportedFormatCount] = formats[i]
346: .getName();
347: }
348: }
349: return supportedFormats;
350: }
351:
352: /**
353: * Converts to serial format.
354: * @param item the PIM item to be processed
355: * @param os the target output stream
356: * @param enc the character encoding for output strings
357: * @param dataFormat the serialized format
358: * @throws PIMException if any error writing the PIM data
359: * @throws UnsupportedEncodingException if encoding is not supported
360: */
361: public void toSerialFormat(PIMItem item, OutputStream os,
362: String enc, String dataFormat) throws PIMException,
363: UnsupportedEncodingException {
364:
365: if (enc == null) {
366: enc = "UTF-8" /* NO I18N */;
367: }
368: if (dataFormat == null) {
369: throw new NullPointerException("Null data format");
370: }
371: if (dataFormat.trim().length() == 0) {
372: throw new IllegalArgumentException("Empty data format");
373: }
374: if (item == null) {
375: throw new NullPointerException("Null PIM item");
376: }
377:
378: try {
379: for (int i = 0; i < formats.length; i++) {
380: if (formats[i].getName().equals(dataFormat)) {
381: formats[i].encode(os, enc, item);
382: return;
383: }
384: }
385: throw new IllegalArgumentException("Data format '"
386: + dataFormat + "' is not supported");
387: } catch (UnsupportedEncodingException e) {
388: throw e;
389: } catch (IOException e) {
390: throw new PIMException(e.getMessage());
391: }
392: }
393:
394: /**
395: * Ensures that the given PIM list type is valid.
396: * @param pimListType a PIM list type
397: * @throws IllegalArgumentException if the list type is not valid.
398: */
399: private void validatePimListType(int pimListType) {
400: switch (pimListType) {
401: case PIM.CONTACT_LIST:
402: case PIM.EVENT_LIST:
403: case PIM.TODO_LIST:
404: // ok
405: break;
406: default:
407: throw new IllegalArgumentException(
408: "Not a valid PIM list type: " + pimListType);
409: }
410: }
411:
412: /**
413: * Ensures that the given PIM list mode is valid.
414: * @param mode READ_ONLY, WRITE_ONLY or READ_WRITE
415: */
416: private void validateMode(int mode) {
417: switch (mode) {
418: case READ_ONLY:
419: case WRITE_ONLY:
420: case READ_WRITE:
421: break;
422: default:
423: throw new IllegalArgumentException(
424: "Invalid PIM list mode: " + mode);
425: }
426: }
427:
428: /**
429: * Ensures that the given PIM list name is valid.
430: *
431: * @param pimListType CONTACT_LIST, EVENT_LIST or TODO_LIST
432: * @param name name of the list
433: * @throws PIMException if list with the specified name is not found
434: */
435: private void validateName(int pimListType, String name)
436: throws PIMException {
437: String[] names = PIMHandler.getInstance().getListNames(
438: pimListType);
439: for (int i = 0; i < names.length; i++) {
440: if (name.equals(names[i])) {
441: return;
442: }
443: }
444: throw new PIMException("PIM list does not exist: '" + name
445: + "'");
446: }
447:
448: }
|