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.midp.io.j2me.storage;
028:
029: import java.io.IOException;
030:
031: import java.util.Vector;
032:
033: import com.sun.midp.security.SecurityToken;
034: import com.sun.midp.security.Permissions;
035:
036: import com.sun.midp.midlet.Scheduler;
037: import com.sun.midp.midlet.MIDletSuite;
038:
039: import com.sun.midp.io.Util;
040:
041: /**
042: * Provide the methods to manage files in a device's persistant storage.
043: */
044: public class File {
045: /** Table to speed up the unicodeToAsciiFilename conversion method. */
046: private static final char NUMS[] = { '0', '1', '2', '3', '4', '5',
047: '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
048:
049: /** Caches the storage root to save repeated native method calls. */
050: private static String storageRoot = null;
051:
052: /** Caches the configuration root to save repeated native method calls. */
053: private static String configRoot = null;
054:
055: /**
056: * Returns the root to build storage filenames including an needed
057: * file separators, abstracting difference of the file systems
058: * of development and device platforms. Note the root is never null.
059: *
060: * @param storageId ID of the storage the root of which should be returned
061: *
062: * @return root of any filename for accessing device persistant
063: * storage.
064: */
065: public static String getStorageRoot(int storageId) {
066: if (storageRoot == null) {
067: storageRoot = initStorageRoot(storageId);
068: }
069:
070: return storageRoot;
071: }
072:
073: /**
074: * Returns the root to build configuration filenames including an needed
075: * file separators, abstracting difference of the file systems
076: * of development and device platforms. Note the root is never null.
077: *
078: * @param storageId ID of the storage the config root of which
079: * should be returned
080: *
081: * @return root of any configuration filename for accessing device
082: * persistant storage.
083: */
084: public static String getConfigRoot(int storageId) {
085: if (configRoot == null) {
086: configRoot = initConfigRoot(storageId);
087: }
088:
089: return configRoot;
090: }
091:
092: /**
093: * Convert a file name into a form that can be safely stored on
094: * an ANSI-compatible file system. All characters that are not
095: * [A-Za-z0-9] are converted into %uuuu, where uuuu is the hex
096: * representation of the character's unicode value. Note even
097: * though "_" is allowed it is converted because we use it for
098: * for internal purposes. Potential file separators are converted
099: * so the native layer does not have deal with sub-directory hierarchies.
100: *
101: * @param str a string that may contain any character
102: * @return an equivalent string that contains only the "safe" characters.
103: */
104: public static String unicodeToAsciiFilename(String str) {
105: StringBuffer sbuf = new StringBuffer();
106:
107: for (int i = 0; i < str.length(); i++) {
108: char c = str.charAt(i);
109: if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
110: sbuf.append(c);
111: } else if (c >= 'A' && c <= 'Z') {
112: sbuf.append('#');
113: sbuf.append(c);
114: } else {
115: int v = (int) (c & 0xffff);
116: sbuf.append('%');
117: sbuf.append(NUMS[(v & 0xf000) >> 12]);
118: sbuf.append(NUMS[(v & 0x0f00) >> 8]);
119: sbuf.append(NUMS[(v & 0x00f0) >> 4]);
120: sbuf.append(NUMS[(v & 0x000f) >> 0]);
121: }
122: }
123:
124: return sbuf.toString();
125: }
126:
127: /**
128: * Perform the reverse conversion of unicodeToAscii().
129: *
130: * @param str a string previously returned by escape()
131: * @return the original string before the conversion by escape().
132: */
133: public static String asciiFilenameToUnicode(String str) {
134: StringBuffer sbuf = new StringBuffer();
135:
136: for (int i = 0; i < str.length(); i++) {
137: char c = str.charAt(i);
138: if (c == '%') {
139: int v = 0;
140:
141: v <<= 4;
142: v += hexValue(str.charAt(i + 1));
143: v <<= 4;
144: v += hexValue(str.charAt(i + 2));
145: v <<= 4;
146: v += hexValue(str.charAt(i + 3));
147: v <<= 4;
148: v += hexValue(str.charAt(i + 4));
149:
150: i += 4;
151: sbuf.append((char) (v & 0x0000ffff));
152: } else if (c == '#') {
153: // drop c
154: } else {
155: sbuf.append(c);
156: }
157: }
158:
159: return sbuf.toString();
160: }
161:
162: /**
163: * A utility method that convert a hex character 0-9a-f to the
164: * numerical value represented by this hex char.
165: *
166: * @param c the character to convert
167: * @return the number represented by the character. E.g., '0' represents
168: * the number 0x0, 'a' represents the number 0x0a, etc.
169: */
170: private static int hexValue(char c) {
171: if (c >= '0' && c <= '9') {
172: return ((int) c) - '0';
173: } else {
174: return ((int) c) - 'a' + 10;
175: }
176: }
177:
178: /**
179: * Constructs a file object.
180: */
181: public File() {
182: MIDletSuite midletSuite = Scheduler.getScheduler()
183: .getMIDletSuite();
184:
185: // if a MIDlet suite is not scheduled, assume the JAM is calling.
186: if (midletSuite != null) {
187: midletSuite.checkIfPermissionAllowed(Permissions.AMS);
188: }
189: }
190:
191: /**
192: * Constructs a file object.
193: *
194: * @param callerSecurityToken security token of the caller
195: */
196: public File(SecurityToken callerSecurityToken) {
197: callerSecurityToken.checkIfPermissionAllowed(Permissions.AMS);
198: }
199:
200: /**
201: * Replaces the current name of storage, <code>oldName</code>
202: * with <code>newName</code>.
203: *
204: * @param oldName original name of storage file
205: * @param newName new name for storage file
206: * @exception IOException if an error occurs during rename
207: */
208: public synchronized void rename(String oldName, String newName)
209: throws IOException {
210: renameStorage(oldName, newName);
211: }
212:
213: /**
214: * Returns <code>true</code> if storage file <code>name</code>
215: * exists.
216: *
217: * @param name name of storage file
218: *
219: * @return <code>true</code> if the named storage file exists
220: */
221: public synchronized boolean exists(String name) {
222: return storageExists(name);
223: }
224:
225: /**
226: * Remove a file from storage if it exists.
227: *
228: * @param name name of the file to delete
229: * @exception IOException if an error occurs during delete
230: */
231: public synchronized void delete(String name) throws IOException {
232: deleteStorage(name);
233: }
234:
235: /**
236: * Retrieves the approximate space available to grow or
237: * create new storage files.
238: *
239: * @param storageId ID of the storage to check for available space
240: *
241: * @return approximate number of free bytes in storage
242: */
243: public long getBytesAvailableForFiles(int storageId) {
244: return availableStorage(storageId);
245: }
246:
247: /**
248: * Initializes storage root for this file instance.
249: *
250: * @param storageId ID of the storage the root of which should be returned
251: *
252: * @return path of the storage root
253: */
254: private static native String initStorageRoot(int storageId);
255:
256: /**
257: * Initializes the configuration root for this file instance.
258: *
259: * @param storageId ID of the storage the config root of which
260: * should be returned
261: *
262: * @return path of the configuration root
263: */
264: private static native String initConfigRoot(int storageId);
265:
266: /**
267: * Renames storage file.
268: *
269: * @param oldName old name of storage file
270: * @param newName new name for storage file
271: */
272: private static native void renameStorage(String oldName,
273: String newName) throws IOException;
274:
275: /**
276: * Determines if a storage file matching filename exists.
277: *
278: * @param filename storage file to match
279: *
280: * @return <code>true</code> if storage indicated by
281: * <code>szFilename</code> exists
282: */
283: private static native boolean storageExists(String filename);
284:
285: /**
286: * Removes a file from storage.
287: *
288: * @param filename storage file to delete
289: *
290: * @exception IOException if an error occurs during deletion
291: */
292: private static native void deleteStorage(String filename)
293: throws IOException;
294:
295: /**
296: * Gets the approximate number of free storage bytes remaining.
297: *
298: * @param storageId ID of the storage to check for available space
299: *
300: * @return free storage space remaining, in bytes
301: */
302: private static native long availableStorage(int storageId);
303: }
|