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 java.util.Hashtable;
030: import java.util.Enumeration;
031: import java.util.Vector;
032: import javax.microedition.io.file.*;
033: import java.io.*;
034: import javax.microedition.io.Connector;
035: import javax.microedition.pim.PIMException;
036:
037: import com.sun.midp.log.*;
038: import com.sun.midp.security.*;
039: import com.sun.midp.main.Configuration;
040:
041: /**
042: * Handles reading and writing PIM data.
043: *
044: */
045: public class PIMDatabase {
046:
047: /**
048: * Inner class to request security token from SecurityInitializer.
049: * SecurityInitializer should be able to check this inner class name.
050: */
051: static private class SecurityTrusted implements
052: ImplicitlyTrustedClass {
053: };
054:
055: /** Security token to allow access to implementation APIs */
056: private static SecurityToken classSecurityToken = SecurityInitializer
057: .requestToken(new SecurityTrusted());
058:
059: /** Name of root dir. */
060: private String dir;
061: /** Hashtable of categories. */
062: private Hashtable categoriesMap = new Hashtable();
063: /** File separator symbol. */
064: private final String fileSep = "/";
065:
066: // private static final Debug debug = Debug.create(PIMDatabase.class);
067:
068: /** Directory structure of lists. */
069: private static final String[] DIRECTORIES = { "contacts", "events",
070: "todo" };
071:
072: /** Default names of lists. */
073: String[][] LISTS = { { "DefaultContactList" },
074: { "DefaultEventList" }, { "DefaultTodoList" } };
075:
076: /**
077: * Default constructor.
078: *
079: */
080: public PIMDatabase() {
081: }
082:
083: /**
084: * Constructor for creating a database object for the given path.
085: *
086: * @param inpDir path of root directory
087: * @throws IOException if an error occurs accessing the file
088: */
089: public PIMDatabase(String inpDir) throws IOException {
090: this .dir = inpDir;
091: String fcPrefix = "file:";
092: if (dir.startsWith(fcPrefix)) { // remove file prefix
093: dir = dir.substring(fcPrefix.length());
094: }
095: FileConnection tmpDir;
096: boolean tmpCond;
097: String tmpStr, tmpStr1;
098:
099: com.sun.midp.io.j2me.file.Protocol conn = new com.sun.midp.io.j2me.file.Protocol();
100:
101: tmpDir = (FileConnection) conn
102: .openPrim(classSecurityToken, dir);
103: tmpCond = tmpDir.exists();
104: tmpDir.close();
105:
106: if (!tmpCond) { // Create directories if need
107: synchronized (this ) {
108: tmpDir = (FileConnection) conn.openPrim(
109: classSecurityToken, dir);
110: tmpDir.mkdir();
111: tmpStr = tmpDir.getURL().substring(fcPrefix.length());
112: tmpDir.close();
113: // create list type directories with default lists
114: for (int i = 0; i < DIRECTORIES.length; i++) {
115: tmpDir = (FileConnection) conn
116: .openPrim(classSecurityToken, tmpStr
117: + DIRECTORIES[i]);
118: tmpDir.mkdir();
119: tmpStr1 = tmpDir.getURL().substring(
120: fcPrefix.length());
121: tmpDir.close();
122: for (int j = 0; j < LISTS[i].length; j++) {
123: String listName = Configuration
124: .getProperty(LISTS[i][j]);
125: tmpDir = (FileConnection) conn.openPrim(
126: classSecurityToken, tmpStr1 + listName);
127: tmpDir.mkdir();
128: tmpDir.close();
129: }
130: }
131: }
132: }
133: }
134:
135: /**
136: * Gets the path for given list type.
137: *
138: * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST
139: *
140: * @return the path
141: */
142: private String getTypeDir(int listType) {
143: return dir + fileSep + DIRECTORIES[listType - 1];
144: }
145:
146: /**
147: * Gets the path for given list name.
148: *
149: * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST
150: * @param listName name of list
151: *
152: * @return the path
153: */
154: private String getListDir(int listType, String listName) {
155: return getTypeDir(listType) + fileSep + listName;
156: }
157:
158: /**
159: * Gets the list of names for given list type.
160: *
161: * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST
162: *
163: * @return array of names
164: */
165: public String[] getListNames(int listType) {
166: Vector vect_names = new Vector();
167: Enumeration enListType = null;
168: FileConnection tmpDir;
169:
170: com.sun.midp.io.j2me.file.Protocol conn = new com.sun.midp.io.j2me.file.Protocol();
171:
172: try {
173: tmpDir = (FileConnection) conn.openPrim(classSecurityToken,
174: getTypeDir(listType));
175: enListType = tmpDir.list();
176: tmpDir.close();
177: } catch (IOException e) {
178: if (Logging.TRACE_ENABLED) {
179: Logging
180: .trace(e,
181: "getListNames: FileConnection problem");
182: }
183: }
184: while (enListType.hasMoreElements()) {
185: String curr_name = (enListType.nextElement()).toString();
186: if (curr_name.endsWith(fileSep)) { // The last symbol is "/"
187: vect_names.addElement(curr_name.substring(0, curr_name
188: .length() - 1)); // save the list name
189: }
190: }
191: String[] names = new String[vect_names.size()];
192: Enumeration dir_names = vect_names.elements();
193: int i;
194: for (i = 0; i < names.length; i++) {
195: names[i] = (dir_names.nextElement()).toString();
196: }
197: return names;
198: }
199:
200: /**
201: * Gets the default list name for given list type.
202: *
203: * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST
204: *
205: * @return default name
206: */
207: public String getDefaultListName(int listType) {
208: return Configuration.getProperty(LISTS[listType - 1][0]);
209: }
210:
211: /**
212: * Gets the key table for given list name.
213: *
214: * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST
215: * @param listName name of list
216: *
217: * @return hashkey of keys
218: * @throws PIMException in case of I/O error
219: */
220: public Hashtable getKeys(int listType, String listName)
221: throws PIMException {
222: Hashtable keys = new Hashtable();
223: Enumeration enListDir = null;
224: FileConnection tmpDir;
225:
226: com.sun.midp.io.j2me.file.Protocol conn = new com.sun.midp.io.j2me.file.Protocol();
227:
228: try {
229: tmpDir = (FileConnection) conn.openPrim(classSecurityToken,
230: getTypeDir(listType) + "/" + listName);
231: enListDir = tmpDir.list();
232: tmpDir.close();
233: } catch (IOException e) {
234: if (Logging.TRACE_ENABLED) {
235: Logging.trace(e, "getKeys: FileConnection problem");
236: }
237: throw new PIMException("getKeys: " + e.getMessage());
238: }
239: while (enListDir.hasMoreElements()) {
240: String curr_name = (enListDir.nextElement()).toString();
241: curr_name = curr_name.toLowerCase();
242: if (curr_name.endsWith(".vcf")
243: || curr_name.endsWith(".vcs")) {
244: keys.put(curr_name, curr_name);
245: }
246: }
247: return keys;
248: }
249:
250: /**
251: * Gets the element by key.
252: *
253: * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST
254: * @param listName name of list
255: * @param key key value
256: *
257: * @return data by key
258: * @throws PIMException in case of I/O error
259: */
260: public byte[] getElement(int listType, String listName, String key)
261: throws PIMException {
262: FileConnection file = null;
263: byte[] ret_v = null;
264:
265: com.sun.midp.io.j2me.file.Protocol conn = new com.sun.midp.io.j2me.file.Protocol();
266:
267: try {
268: file = (FileConnection) conn.openPrim(classSecurityToken,
269: getListDir(listType, listName) + fileSep + key,
270: Connector.READ);
271: long elemSize = file.fileSize();
272: if (elemSize <= 0) { // no categories
273: ret_v = new byte[0];
274: } else { // read categories
275: byte[] buffer = new byte[(int) elemSize];
276: DataInputStream in = file.openDataInputStream();
277: in.readFully(buffer);
278: in.close();
279: ret_v = buffer;
280: }
281: file.close();
282: } catch (IOException e) {
283: if (Logging.TRACE_ENABLED) {
284: Logging.trace(e, "getElement: FileConnection problem");
285: }
286: throw new PIMException("getElement: " + e.getMessage());
287: } finally {
288: try {
289: if (file.isOpen()) {
290: file.close();
291: }
292: } catch (IOException e) {
293: // do nothing
294: }
295: }
296: return ret_v;
297: }
298:
299: /**
300: * Commits the PIM item by key.
301: *
302: * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST
303: * @param listName name of list
304: * @param key key of PIM item
305: * @param data data of PIM item
306: *
307: * @return key of PIM item
308: * @throws PIMException in case of I/O error
309: */
310: public synchronized String commitElement(int listType,
311: String listName, String key, byte[] data)
312: throws PIMException {
313: String ret_v = null;
314: if (key == null) {
315: // make a new key
316: Hashtable keySet = getKeys(listType, listName);
317: String extension;
318: switch (listType) {
319: case PIMBridge.CONTACT_LIST:
320: extension = ".vcf";
321: break;
322: default:
323: extension = ".vcs";
324: break;
325: }
326: key = 1 + extension;
327: int i = 1;
328: while (keySet.containsKey(i + extension)) {
329: key = (++i) + extension;
330: // keep looking for a free file name
331: }
332: }
333: String listDir = getListDir(listType, listName);
334: FileConnection file = null;
335: com.sun.midp.io.j2me.file.Protocol conn = new com.sun.midp.io.j2me.file.Protocol();
336: try {
337: file = (FileConnection) conn.openPrim(classSecurityToken,
338: listDir + fileSep + key);
339: if (data == null) {
340: if (file.exists()) {
341: file.delete();
342: }
343: file.close();
344: } else {
345: if (!file.exists()) {
346: file.create();
347: }
348: DataOutputStream out = file.openDataOutputStream();
349: out.write(data);
350: out.close();
351: file.close();
352: ret_v = new String(key);
353: }
354: } catch (IOException e) {
355: if (Logging.TRACE_ENABLED) {
356: Logging.trace(e,
357: "commitElement: FileConnection problem");
358: }
359: throw new PIMException("commitElement: " + e.getMessage());
360: } finally {
361: try {
362: if (file.isOpen()) {
363: file.close();
364: }
365: } catch (IOException e) {
366: // do nothing
367: }
368: }
369: return ret_v;
370: }
371:
372: /**
373: * Create the key string by the given PIM list.
374: *
375: * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST
376: * @param listName name of list
377: *
378: * @return key string
379: */
380: private String categoryKey(int listType, String listName) {
381: return "" + listType + "." + listName;
382: }
383:
384: /**
385: * Sets the new categories.
386: *
387: * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST
388: * @param listName name of list
389: * @param categories string of new categories
390: * @throws PIMException in case of I/O error
391: * @see #getCategories
392: *
393: */
394: public synchronized void setCategories(int listType,
395: String listName, String categories) throws PIMException {
396: FileConnection file = null;
397:
398: com.sun.midp.io.j2me.file.Protocol conn = new com.sun.midp.io.j2me.file.Protocol();
399:
400: try {
401: file = (FileConnection) conn.openPrim(classSecurityToken,
402: getListDir(listType, listName) + fileSep
403: + "categories.txt");
404: if (!file.exists()) {
405: file.create();
406: } else {
407: file.truncate(0);
408: }
409: DataOutputStream out = file.openDataOutputStream();
410: out.write(categories.getBytes());
411: out.close();
412: file.close();
413: categoriesMap.put(categoryKey(listType, listName),
414: categories);
415: } catch (IOException e) {
416: if (Logging.TRACE_ENABLED) {
417: Logging.trace(e,
418: "setCategories: FileConnection problem");
419: }
420: throw new PIMException("setCategories: " + e.getMessage());
421: } finally {
422: try {
423: if (file.isOpen()) {
424: file.close();
425: }
426: } catch (IOException e) {
427: // do nothing
428: }
429: }
430: }
431:
432: /**
433: * Gets the string of supported categories.
434: *
435: * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST
436: * @param listName name of list
437: *
438: * @return string of supported categories
439: * @throws PIMException in case of I/O error
440: * @see #setCategories
441: */
442: public synchronized String getCategories(int listType,
443: String listName) throws PIMException {
444: String key = categoryKey(listType, listName);
445: String categories = (String) categoriesMap.get(key);
446:
447: com.sun.midp.io.j2me.file.Protocol conn = new com.sun.midp.io.j2me.file.Protocol();
448:
449: if (categories == null) {
450: FileConnection file = null;
451: try {
452: file = (FileConnection) conn.openPrim(
453: classSecurityToken, getListDir(listType,
454: listName)
455: + fileSep + "categories.txt");
456: if (!file.exists()) {
457: file.create();
458: }
459: long catSize = file.fileSize();
460: if (catSize <= 0) { // no categories
461: categories = "";
462: } else { // read categories
463: byte[] buffer = new byte[(int) catSize];
464: DataInputStream in = file.openDataInputStream();
465: in.readFully(buffer);
466: in.close();
467: categories = new String(buffer);
468: }
469: file.close();
470: categoriesMap.put(key, categories);
471: } catch (IOException e) {
472: throw new PIMException("getCategories: "
473: + e.getMessage());
474: } finally {
475: try {
476: if (file.isOpen()) {
477: file.close();
478: }
479: } catch (IOException e) {
480: throw new PIMException("getCategories: "
481: + e.getMessage());
482: }
483: }
484: }
485: return categories;
486: }
487:
488: }
|