/* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * This code 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 in the LICENSE file that * accompanied this code). * * 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 Oracle, 500 Oracle Parkway, Redwood Shores * CA 94065 USA or visit www.oracle.com if you need additional information or * have any questions. */ package com.sun.lwuit.io.impl; import com.sun.lwuit.Display; import com.sun.lwuit.events.ActionEvent; import com.sun.lwuit.events.ActionListener; import com.sun.lwuit.io.Cookie; import com.sun.lwuit.io.FileSystemStorage; import com.sun.lwuit.io.NetworkManager; import com.sun.lwuit.io.Storage; import com.sun.lwuit.util.EventDispatcher; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; /** * Represents the implementation of the networking/filesystem abstraction layer. * This class can be replaced to implement various supported device types and hide * device bugs * * @author Shai Almog */ public abstract class IOImplementation { private static IOImplementation INSTANCE; static { new PlatformImplementation(); } private Object storageData; private Hashtable cookies; private ActionListener logger; /** * Creating the subclass instance allows the singleton to initialize */ protected IOImplementation() { INSTANCE = this; } public void addCookie(Cookie [] cookiesArray) { if(cookies == null){ cookies = new Hashtable(); } for (int i = 0; i < cookiesArray.length; i++) { Cookie cookie = cookiesArray[i]; Hashtable h = (Hashtable)cookies.get(cookie.getDomain()); if(h == null){ h = new Hashtable(); cookies.put(cookie.getDomain(), h); } h.put(cookie.getName(), cookie); } if(Cookie.isAutoStored()){ if(Storage.isInitialized()){ if(Storage.getInstance().exists(Cookie.STORAGE_NAME)){ Storage.getInstance().deleteStorageFile(Cookie.STORAGE_NAME); } Storage.getInstance().writeObject(Cookie.STORAGE_NAME, cookies); }else{ System.out.println("Warning: Storage is not initialized"); } } } /** * Adds/replaces a cookie to be sent to the given domain * * @param c cookie to add */ public void addCookie(Cookie c) { if(cookies == null){ cookies = new Hashtable(); } Hashtable h = (Hashtable)cookies.get(c.getDomain()); if(h == null){ h = new Hashtable(); cookies.put(c.getDomain(), h); } h.put(c.getName(), c); if(Cookie.isAutoStored()){ if(Storage.isInitialized()){ if(Storage.getInstance().exists(Cookie.STORAGE_NAME)){ Storage.getInstance().deleteStorageFile(Cookie.STORAGE_NAME); } Storage.getInstance().writeObject(Cookie.STORAGE_NAME, cookies); }else{ System.out.println("Warning: Storage is not initialized"); } } } /** * Returns the domain for the given URL * * @param url a url * @return the domain */ public String getURLDomain(String url) { String domain = url.substring(url.indexOf("//") + 2); int i = domain.indexOf('/'); if(i > -1) { domain = domain.substring(0, i); } return domain; } /** * Returns the cookies for this URL * * @param url the url on which we are checking for cookies * @return the cookies to submit to the given URL */ public Vector getCookiesForURL(String url) { Vector response = null; if (Cookie.isAutoStored()) { if (Storage.isInitialized()) { cookies = (Hashtable) Storage.getInstance().readObject(Cookie.STORAGE_NAME); } else { System.out.println("Warning: Storage is not initialized"); } } if(cookies != null && cookies.size() > 0) { String domain = getURLDomain(url); Enumeration e = cookies.keys(); while (e.hasMoreElements()) { String domainKey = (String) e.nextElement(); if (domain.indexOf(domainKey) > -1) { Hashtable h = (Hashtable) cookies.get(domainKey); if (h != null) { Enumeration enumCookies = h.elements(); if(response == null){ response = new Vector(); } while (enumCookies.hasMoreElements()) { response.addElement(enumCookies.nextElement()); } } } } } return response; } /** * Returns the instance of the IO implementation * * @return the io implementation instance */ public static IOImplementation getInstance() { return INSTANCE; } /** * Connects to a given URL, returns a connection object to be used with the implementation * later * * @param url the URL to connect to * @param read indicates wheher the connection will be read from * @param write indicates whether writing will ocurre into the connection * @return a URL instance */ public abstract Object connect(String url, boolean read, boolean write) throws IOException; /** * Indicates the HTTP header value for an HTTP connection * * @param connection the connection object * @param key the key for the header * @param val the value for the header */ public abstract void setHeader(Object connection, String key, String val); /** * Closes the object (connection, stream etc.) without throwing any exception, even if the * object is null * * @param o Connection, Stream or other closeable object */ public void cleanup(Object o) { try { if(o != null) { if(o instanceof InputStream) { ((InputStream) o).close(); } if(o instanceof OutputStream) { ((OutputStream) o).close(); } } } catch (Throwable ex) { ex.printStackTrace(); } } /** * Returns the content length for this connection * * @param connection the connection * @return the content length */ public abstract int getContentLength(Object connection); /** * Returns an output stream for the given connection * * @param connection the connection to open an output stream on * @return the created output stream * @throws IOException thrown by underlying implemnetation */ public abstract OutputStream openOutputStream(Object connection) throws IOException; /** * Returns an output stream for the given connection * * @param connection the connection to open an output stream on * @param offset position in the file * @return the created output stream * @throws IOException thrown by underlying implemnetation */ public abstract OutputStream openOutputStream(Object connection, int offset) throws IOException; /** * Returns an input stream for the given connection * * @param connection the connection to open an input stream on * @return the created input stream * @throws IOException thrown by underlying implemnetation */ public abstract InputStream openInputStream(Object connection) throws IOException; /** * Returns an output stream for the given file * * @param file the file to which we should open a stream * @return the created output stream * @throws IOException thrown by underlying implemnetation */ public OutputStream openFileOutputStream(String file) throws IOException { return openOutputStream(file); } /** * Returns an input stream for the given connection * * @param file the file to which we should open a stream * @return the created input stream * @throws IOException thrown by underlying implemnetation */ public InputStream openFileInputStream(String file) throws IOException { return openInputStream(file); } /** * Indicates the whether the request method is GET or POST * * @param connection the connection object * @param p true for post false for get */ public abstract void setPostRequest(Object connection, boolean p); /** * Returns the server response code for the request * * @param connection the connection object * @return a numeric HTTP response code * @throws IOException if the request failed */ public abstract int getResponseCode(Object connection) throws IOException; /** * Returns the server response message for the request * * @param connection the connection object * @return a text message to go along with the response code * @throws IOException if the request failed */ public abstract String getResponseMessage(Object connection) throws IOException; /** * Returns the HTTP response header field * * @param name field name for http header * @param connection the connection object * @return the value of the header field * @throws IOException if the request failed */ public abstract String getHeaderField(String name, Object connection) throws IOException; /** * Returns the HTTP response header fields, returns optionally more than one result or null if * no field is present. * * @param name field name for http header * @param connection the connection object * @return the values of the header fields * @throws IOException if the request failed */ public abstract String[] getHeaderFields(String name, Object connection) throws IOException; /** * Indicates whether the underlying implementation supports the notion of a network operation * timeout. If not timeout is "faked" * @return true if HTTP timeout can be configured for this IO implementation */ public boolean isTimeoutSupported() { return false; } /** * This will work only if http timeout is supported * * @param t time in milliseconds */ public void setTimeout(int t) { } /** * Flush the storage cache allowing implementations that cache storage objects * to store */ public void flushStorageCache() { } /** * The storage data is used by some storage implementations (e.g. CDC) to place the * storage object in a "proper" location matching the application name. This needs to * be set by the user, the name might be ignored in platforms (such as MIDP) where storage * is mapped to a native application specific storage. * * @param storageData the name for the storage or its context */ public void setStorageData(Object storageData) { this.storageData = storageData; } /** * The storage data is used by some storage implementations (e.g. CDC) to place the * storage object in a "proper" location matching the application name. This needs to * be set by the user, the name might be ignored in platforms (such as MIDP) where storage * is mapped to a native application specific storage. * * @return the name for the storage */ public Object getStorageData() { return storageData; } /** * Deletes the given file name from the storage * * @param name the name of the storage file */ public abstract void deleteStorageFile(String name); /** * Deletes all the files in the application storage */ public void clearStorage() { String[] l = listStorageEntries(); for(int iter = 0 ; iter < l.length ; iter++) { deleteStorageFile(l[iter]); } } /** * Creates an output stream to the storage with the given name * * @param name the storage file name * @return an output stream of limited capcity */ public abstract OutputStream createStorageOutputStream(String name) throws IOException; /** * Creates an input stream to the given storage source file * * @param name the name of the source file * @return the input stream */ public abstract InputStream createStorageInputStream(String name) throws IOException; /** * Returns true if the given storage file exists * * @param name the storage file name * @return true if it exists */ public abstract boolean storageFileExists(String name); /** * Lists the names of the storage files * * @return the names of all the storage files */ public abstract String[] listStorageEntries(); /** * Returns the filesystem roots from which the structure of the file system * can be traversed * * @return the roots of the filesystem */ public abstract String[] listFilesystemRoots(); /** * Lists the files within the given directory, returns relative file names and not * full file names. * * @param directory the directory in which files should be listed * @return array of file names */ public abstract String[] listFiles(String directory) throws IOException; /** * Returns the size of the given root directory * * @param root the root directory in the filesystem * @return the byte size of the directory */ public abstract long getRootSizeBytes(String root); /** * Returns the available space in the given root directory * * @param root the root directory in the filesystem * @return the bytes available in the directory */ public abstract long getRootAvailableSpace(String root); /** * Creates the given directory * * @param directory the directory name to create */ public abstract void mkdir(String directory); /** * Deletes the specific file * * @param file file to delete */ public abstract void deleteFile(String file); /** * Indicates the hidden state of the file * * @param file file * @return true for a hidden file */ public abstract boolean isHidden(String file); /** * Toggles the hidden state of the file * * @param file file * @param h hidden state */ public abstract void setHidden(String file, boolean h); /** * Returns the length of the file * * @param file file * @return length of said file */ public abstract long getFileLength(String file); /** * Indicates whether the given file is a directory * * @param file file * @return true if its a directory */ public abstract boolean isDirectory(String file); /** * Indicates whether the given file exists * * @param file file * @return true if it exists */ public abstract boolean exists(String file); /** * Renames a file to the given name, expects the new name to be relative to the * current directory * * @param file absolute file name * @param newName relative new name */ public abstract void rename(String file, String newName); /** * Returns the file system separator char normally '/' * * @return the separator char */ public abstract char getFileSystemSeparator(); /** * Indicates whether looking up an access point is supported by this device * * @return true if access point lookup is supported */ public boolean isAPSupported() { return false; } /** * Returns the ids of the access points available if supported * * @return ids of access points */ public String[] getAPIds() { return null; } /** * Returns the type of the access point * * @param id access point id * @return one of the supported access point types from network manager */ public int getAPType(String id) { return NetworkManager.ACCESS_POINT_TYPE_UNKNOWN; } /** * Returns the user displayable name for the given access point * * @param id the id of the access point * @return the name of the access point */ public String getAPName(String id) { return null; } /** * Returns the id of the current access point * * @return id of the current access point */ public String getCurrentAccessPoint() { return null; } /** * Returns the id of the current access point * * @param id id of the current access point */ public void setCurrentAccessPoint(String id) { } /** * For some reason the standard code for writing UTF8 output in a server request * doesn't work as expected on SE/CDC stacks. * * @return true if the getBytes() approach should be used */ public boolean shouldWriteUTFAsGetBytes() { return false; } /** * Some devices need more elaborate thread creation logic e.g. to increase the * default stack size or might use a pooling strategy * * @param name the name of the thread * @param r the runnable */ public void startThread(String name, Runnable r) { new Thread(r, name).start(); } /** * Allows binding logic to occur before closing the output stream * such as syncing * * @param s the closing stream */ public void closingOutput(OutputStream s) { } /** * Allows the logger to print the stack trace into the log when the native * platform supports that * * @param t the exception * @param o the writer */ public void printStackTraceToStream(Throwable t, Writer o) { } /** * This method is useful strictly for debugging, the logger can use it to track * file opening/closing thus detecting potential file resource leaks that * can cause serious problems in some OS's. * * @param al action listener to receive the callback */ public void setLogListener(ActionListener al) { logger = al; } /** * Indicates whether logging is turned on * @return true or false */ protected boolean isLogged() { return logger != null; } /** * Dispatch the message to the logger * @param content content of the message */ protected void log(String content) { logger.actionPerformed(new ActionEvent(content)); } /** * Logs the creation of a stream * * @param name the name of the stream * @param isInput whether the stream is an input or output stream * @param count the number of streams of this type */ public void logStreamCreate(String name, boolean isInput, int count) { if(isLogged()) { if(isInput) { log("Creating input stream " + name + " total streams: " + count); } else { log("Creating output stream " + name + " total streams: " + count); } } } /** * Logs the closing of a stream * * @param name the name of the stream * @param isInput whether the stream is an input or output stream * @param count the number of streams of this type */ public void logStreamClose(String name, boolean isInput, int count) { if(isLogged()) { if(isInput) { log("Closing input stream " + name + " remaining streams: " + count); } else { log("Closing output stream " + name + " remaining streams: " + count); } } } /** * Logs the closing of a stream * * @param name the name of the stream * @param isInput whether the stream is an input or output stream */ public void logStreamDoubleClose(String name, boolean isInput) { if(isLogged()) { if(isInput) { log("Double closing input stream " + name); } else { log("Double closing output stream " + name); } } } /** * Returns the type of the root often by guessing * * @param root the root whose type we are checking * @return one of the type constants above */ public int getRootType(String root) { root = root.toLowerCase(); String sdCard = Display.getInstance().getProperty("sdcard", null); if(sdCard != null) { if(root.indexOf(sdCard) > -1) { return FileSystemStorage.ROOT_TYPE_SDCARD; } } else { if(root.indexOf("file:///f:") > -1 || root.indexOf("file:///e:") > -1 || root.indexOf("memorycard") > -1 || root.indexOf("mmc") > -1 || root.indexOf("sdcard") > -1 || root.indexOf("store") > -1) { return FileSystemStorage.ROOT_TYPE_SDCARD; } } if(root.indexOf("c:") > -1 || root.indexOf("phone memory") > -1 || root.indexOf("store") > -1) { return FileSystemStorage.ROOT_TYPE_MAINSTORAGE; } return FileSystemStorage.ROOT_TYPE_UNKNOWN; } }