/* * Copyright 1990-2009 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.j2me.pim; import com.sun.j2me.io.ConnectionBaseAdapter; import com.sun.j2me.security.SecurityTokenInitializer; import com.sun.j2me.security.Token; import com.sun.j2me.security.TrustedClass; import com.sun.j2me.log.Logging; import com.sun.j2me.main.Configuration; import com.sun.cdc.io.j2me.file.Protocol; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Hashtable; import java.util.Enumeration; import java.util.Vector; import javax.microedition.io.Connector; import javax.microedition.io.file.FileConnection; import javax.microedition.pim.PIMException; /** * Handles reading and writing PIM data. * */ public class PIMDatabase { /* * Inner class to request security token from SecurityTokenInitializer. * SecurityTokenInitializer should be able to check this inner class name. */ static private class SecurityTrusted implements TrustedClass { }; /** Security token to allow access to implementation APIs */ private static Token classSecurityToken = SecurityTokenInitializer.requestToken(new SecurityTrusted()); /** Name of root dir. */ private String dir; /** Hashtable of categories. */ private Hashtable categoriesMap = new Hashtable(); /** File separator symbol. */ private final String fileSep = "/"; // private static final Debug debug = Debug.create(PIMDatabase.class); /** Directory structure of lists. */ private static final String[] DIRECTORIES = { "contacts", "events", "todo" }; /** Default names of lists. */ String[][] LISTS = { { "DefaultContactList" }, { "DefaultEventList" }, { "DefaultTodoList" } }; /** * Default constructor. * */ public PIMDatabase() { } /** * Constructor for creating a database object for the given path. * * @param inpDir path of root directory * @throws IOException if an error occurs accessing the file */ public PIMDatabase(String inpDir) throws IOException { this.dir = inpDir; String fcPrefix = "file:"; if (dir.startsWith(fcPrefix)) { // remove file prefix dir = dir.substring(fcPrefix.length()); } FileConnection tmpDir; boolean tmpCond; String tmpStr, tmpStr1; Protocol conn = new Protocol(); tmpDir = (FileConnection)conn.openPrim(classSecurityToken, dir); tmpCond = tmpDir.exists(); tmpDir.close(); if (!tmpCond) { // Create directories if need synchronized (this) { tmpDir = (FileConnection)conn.openPrim(classSecurityToken, dir); tmpDir.mkdir(); tmpStr = tmpDir.getURL().substring(fcPrefix.length()); tmpDir.close(); // create list type directories with default lists for (int i = 0; i < DIRECTORIES.length; i++) { tmpDir = (FileConnection)conn.openPrim(classSecurityToken, tmpStr + DIRECTORIES[i]); tmpDir.mkdir(); tmpStr1 = tmpDir.getURL().substring(fcPrefix.length()); tmpDir.close(); for (int j = 0; j < LISTS[i].length; j++) { String listName = Configuration.getProperty(LISTS[i][j]); tmpDir = (FileConnection)conn.openPrim( classSecurityToken, tmpStr1 + listName); tmpDir.mkdir(); tmpDir.close(); } } } } } /** * Gets the path for given list type. * * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST * * @return the path */ private String getTypeDir(int listType) { return dir + fileSep + DIRECTORIES[listType - 1]; } /** * Gets the path for given list name. * * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST * @param listName name of list * * @return the path */ private String getListDir(int listType, String listName) { return getTypeDir(listType) + fileSep + listName; } /** * Gets the list of names for given list type. * * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST * * @return array of names */ public String[] getListNames(int listType) { Vector vect_names = new Vector(); Enumeration enListType = null; FileConnection tmpDir; Protocol conn = new Protocol(); try { tmpDir = (FileConnection)conn.openPrim(classSecurityToken, getTypeDir(listType)); enListType = tmpDir.list(); tmpDir.close(); } catch (IOException e) { if (Logging.TRACE_ENABLED) { Logging.trace(e, "getListNames: FileConnection problem"); } } while (enListType.hasMoreElements()) { String curr_name = (enListType.nextElement()).toString(); if (curr_name.endsWith(fileSep)) { // The last symbol is "/" vect_names.addElement(curr_name.substring(0, curr_name.length() - 1)); // save the list name } } String[] names = new String[vect_names.size()]; Enumeration dir_names = vect_names.elements(); int i; for (i = 0; i < names.length; i++) { names[i] = (dir_names.nextElement()).toString(); } return names; } /** * Gets the default list name for given list type. * * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST * * @return default name */ public String getDefaultListName(int listType) { return Configuration.getProperty(LISTS[listType - 1][0]); } /** * Gets the key table for given list name. * * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST * @param listName name of list * * @return hashkey of keys * @throws PIMException in case of I/O error */ public Hashtable getKeys(int listType, String listName) throws PIMException { Hashtable keys = new Hashtable(); Enumeration enListDir = null; FileConnection tmpDir; Protocol conn = new Protocol(); try { tmpDir = (FileConnection)conn.openPrim(classSecurityToken, getTypeDir(listType) + "/" + listName); enListDir = tmpDir.list(); tmpDir.close(); } catch (IOException e) { if (Logging.TRACE_ENABLED) { Logging.trace(e, "getKeys: FileConnection problem"); } throw new PIMException("getKeys: " + e.getMessage()); } while (enListDir.hasMoreElements()) { String curr_name = (enListDir.nextElement()).toString(); curr_name = curr_name.toLowerCase(); if (curr_name.endsWith(".vcf") || curr_name.endsWith(".vcs")) { keys.put(curr_name, curr_name); } } return keys; } /** * Gets the element by key. * * @param listType - CONTACT_LIST, EVENT_LIST or TODO_LIST * @param listName name of list * @param key key value * * @return data by key * @throws PIMException in case of I/O error */ public byte[] getElement(int listType, String listName, String key) throws PIMException { FileConnection file = null; byte[] ret_v = null; Protocol conn = new Protocol(); try { file = (FileConnection)conn.openPrim(classSecurityToken, getListDir(listType, listName) + fileSep + key, Connector.READ); long elemSize = file.fileSize(); if (elemSize <= 0) { // no categories ret_v = new byte[0]; } else { // read categories byte[] buffer = new byte[(int)elemSize]; DataInputStream in = file.openDataInputStream(); in.readFully(buffer); in.close(); ret_v = buffer; } file.close(); } catch (IOException e) { if (Logging.TRACE_ENABLED) { Logging.trace(e, "getElement: FileConnection problem"); } throw new PIMException("getElement: " + e.getMessage()); } finally { try { if (file.isOpen()) { file.close(); } } catch (IOException e) { // do nothing } } return ret_v; } /** * Commits the PIM item by key. * * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST * @param listName name of list * @param key key of PIM item * @param data data of PIM item * * @return key of PIM item * @throws PIMException in case of I/O error */ public synchronized String commitElement(int listType, String listName, String key, byte[] data) throws PIMException { String ret_v = null; if (key == null) { // make a new key Hashtable keySet = getKeys(listType, listName); String extension; switch (listType) { case PIMBridge.CONTACT_LIST: extension = ".vcf"; break; default: extension = ".vcs"; break; } key = 1 + extension; int i = 1; while (keySet.containsKey(i + extension)) { key = (++i) + extension; // keep looking for a free file name } } String listDir = getListDir(listType, listName); FileConnection file = null; Protocol conn = new Protocol(); try { file = (FileConnection)conn.openPrim(classSecurityToken, listDir + fileSep + key); if (data == null) { if (file.exists()) { file.delete(); } file.close(); } else { if (!file.exists()) { file.create(); } DataOutputStream out = file.openDataOutputStream(); out.write(data); out.close(); file.close(); ret_v = key; } } catch (IOException e) { if (Logging.TRACE_ENABLED) { Logging.trace(e, "commitElement: FileConnection problem"); } throw new PIMException("commitElement: " + e.getMessage()); } finally { try { if (file.isOpen()) { file.close(); } } catch (IOException e) { // do nothing } } return ret_v; } /** * Create the key string by the given PIM list. * * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST * @param listName name of list * * @return key string */ private String categoryKey(int listType, String listName) { return "" + listType + "." + listName; } /** * Sets the new categories. * * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST * @param listName name of list * @param categories string of new categories * @throws PIMException in case of I/O error * @see #getCategories * */ public synchronized void setCategories(int listType, String listName, String categories) throws PIMException { FileConnection file = null; Protocol conn = new Protocol(); try { file = (FileConnection)conn.openPrim(classSecurityToken, getListDir(listType, listName) + fileSep + "categories.txt"); if (!file.exists()) { file.create(); } else { file.truncate(0); } DataOutputStream out = file.openDataOutputStream(); out.write(categories.getBytes()); out.close(); file.close(); categoriesMap.put(categoryKey(listType, listName), categories); } catch (IOException e) { if (Logging.TRACE_ENABLED) { Logging.trace(e, "setCategories: FileConnection problem"); } throw new PIMException("setCategories: " + e.getMessage()); } finally { try { if (file.isOpen()) { file.close(); } } catch (IOException e) { // do nothing } } } /** * Gets the string of supported categories. * * @param listType CONTACT_LIST, EVENT_LIST or TODO_LIST * @param listName name of list * * @return string of supported categories * @throws PIMException in case of I/O error * @see #setCategories */ public synchronized String getCategories(int listType, String listName) throws PIMException { String key = categoryKey(listType, listName); String categories = (String) categoriesMap.get(key); Protocol conn = new Protocol(); if (categories == null) { FileConnection file = null; try { file = (FileConnection)conn.openPrim(classSecurityToken, getListDir(listType, listName) + fileSep + "categories.txt"); if (!file.exists()) { file.create(); } long catSize = file.fileSize(); if (catSize <= 0) { // no categories categories = ""; } else { // read categories byte[] buffer = new byte[(int)catSize]; DataInputStream in = file.openDataInputStream(); in.readFully(buffer); in.close(); categories = new String(buffer); } file.close(); categoriesMap.put(key, categories); } catch (IOException e) { throw new PIMException("getCategories: " + e.getMessage()); } finally { try { if (file.isOpen()) { file.close(); } } catch (IOException e) { throw new PIMException("getCategories: " + e.getMessage()); } } } return categories; } }