/* * @(#)ProtocolBase.java 1.13 06/10/10 * * Copyright 1990-2008 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. * */ /* * NOTE - This code is written a somewhat unusual, and not very object oriented, way. * The principal reason for this is that we intend to translate much of this * protocol into C and place it in the KVM kernel where is will be much smaller * and faster. */ package com.sun.cdc.io.j2me.file; import java.io.*; import javax.microedition.io.*; import com.sun.cdc.io.*; import com.sun.cdc.io.j2me.file.*; import java.lang.SecurityManager; /** * This implements the default "file:" protocol J2ME * * @version 2.0 2/20/2000 */ public abstract class ProtocolBase extends ConnectionBase implements InputConnection, OutputConnection, StreamConnection { /** The string used to open */ private boolean isOpen = true; /** Keep track of the directory */ private String protocolBaseDirectory = null; /** Open count */ private int opens = 1; /** Open mode */ protected int openMode; /** Operation mode */ protected static final int O_RAND = 0; protected static final int O_READ = 1; protected static final int O_WRITE = 2; protected int operationMode = O_RAND; /**********************************************************************\ * Methods from Connection * \**********************************************************************/ /** * @param name the target of the connection * @param mode the access mode * @param timeouts A flag to indicate that the called wants timeout exceptions */ public void open(String name, int mode, boolean timeouts) throws IOException { throw new RuntimeException(); } /* * Check permissions and throw a SecurityException if the * permission check fails. The CDC prim_OpenProtocol method * will be invoked directly and does the proper SecurityManager * check, so the CDC version of this method can be empty. * Override in the MIDP protocol handler to make the proper * MIDP security check. */ protected void checkPermission(String name, String params, int mode) { return; } /* * Check permission when opening an OutputStream. MIDP * versions of the protocol handler should override this * with an empty method. */ protected void outputStreamPermissionCheck() throws IOException { // Check for SecurityManager permission java.lang.SecurityManager sm = System.getSecurityManager(); String itemName = getItemName(); if (itemName != null){ String tmpName = protocolBaseDirectory+itemName; String name = (new File(tmpName)).getAbsolutePath(); if (sm != null) { sm.checkWrite(name); } } return; } /* * Check permission when opening an InputStream. MIDP * versions of the protocol handler should override this * with an empty method. A SecurityException will be * raised if the connection is not allowed. Currently the * file protocol handler does not make a permission * check at this point so this method is empty. */ protected void inputStreamPermissionCheck() { return; } /** * @param name the target of the connection * @param mode the access mode * @param timeouts A flag to indicate that the called wants timeout exceptions */ public Connection openPrim(String name, int mode, boolean timeouts) throws IOException { openMode = mode; int semi = name.indexOf(';'); if(semi == -1) { checkPermission(name, null, mode); return open0(name, "", mode); } else { if(name.endsWith(";")) { throw new IllegalArgumentException("Bad options "+name); } String params = name.substring(semi+1); checkPermission(name, params, mode); return open0(name.substring(0, semi), params, mode); } } protected void ensureOpen() throws IOException { if(!isOpen) { throw new IOException("Connection closed"); } if(operationMode != O_RAND) { throw new IOException("Open mode conflict"); } } protected void ensureOpenForReading() throws IOException { ensureOpen(); if((openMode&Connector.READ) == 0) { throw new SecurityException("Connection not open for reading"); } } protected void ensureOpenForWriting() throws IOException { ensureOpen(); if((openMode&Connector.WRITE) == 0) { throw new SecurityException("Connection not open for writing"); } } protected void ensureOpenAndSelected() throws IOException { ensureOpen(); if(!isSelected0()) { throw new IOException("No selection"); } } protected void ensureOpenForReadingAndSelected() throws IOException { ensureOpenForReading(); if(!isSelected0()) { throw new IOException("No selection"); } } protected void ensureOpenForWritingAndSelected() throws IOException { ensureOpenForWriting(); if(!isSelected0()) { throw new IOException("No selection"); } } protected void ensureDirectory() throws IOException { if(!isDirectory()) { throw new IOException("Selection not a directory"); } } protected void ensureNotDirectory() throws IOException { if(isDirectory()) { throw new IOException("Selection is a directory"); } } /** * Close the connection. * * @exception IOException if an I/O error occurs when closing the * connection. */ public void close() throws IOException { if(isOpen) { isOpen = false; realClose(); } } protected void setProtocolBaseDirectory(String name) { protocolBaseDirectory = name; } /** * Close the connection. * * @exception IOException if an I/O error occurs. */ void realClose() throws IOException { if(--opens == 0) { close0(); } } /**********************************************************************\ * Methods from InputConnection * \**********************************************************************/ /** * Returns an input stream for a database record * * @return an input stream for reading bytes from this record. * @exception IOException if an I/O error occurs when creating the * input stream. */ public InputStream openInputStream() throws IOException { inputStreamPermissionCheck(); ensureOpenForReadingAndSelected(); ensureNotDirectory(); opens++; operationMode = O_READ; return new PrivateFileInputStream(this); } /**********************************************************************\ * Methods from OutputConnection * \**********************************************************************/ /** * Returns an output stream for this socket. * * @param True if appending * @return an output stream for writing bytes to this socket. * @exception IOException if an I/O error occurs when creating the * output stream. */ public OutputStream openOutputStream() throws IOException { outputStreamPermissionCheck(); ensureOpenForWritingAndSelected(); ensureNotDirectory(); opens++; operationMode = O_WRITE; return new PrivateFileOutputStream(this); } /**********************************************************************\ * Methods from RandomAccessConnection * \**********************************************************************/ // // Selection // /** * Test to see if a record in the collection is selected. Until this is done none of the * data access methods below will work. Some random access connection are always * selected, but other ones, such as a MetadataConncetion may require a specific * record to be selected before I/O can commence. * * @return true if the connection is selected, otherwise false. */ public boolean isSelected() throws IOException { ensureOpen(); return isSelected0(); } // // Seeking // /** * Sets the position pointer offset, measured from the beginning of the * data, at which the next read or write occurs. The offset may be * set beyond the end of the data. Setting the offset beyond the end * of the data does not change the data length. The data length will * change only by writing after the offset has been set beyond the end * of the data. * * @param pos the offset position, measured in bytes from the * beginning of the data, at which to set the position * pointer. * @exception IOException if <code>pos</code> is less than * <code>0</code>, if an I/O error occurs, or * there is an input or output stream open on the data. */ public void seek(long pos) throws IOException { ensureOpenAndSelected(); ensureNotDirectory(); seek0(pos); } /** * Returns the current offset into the data. * * @return the offset from the beginning of the data, in bytes, * at which the next read or write occurs. * @exception IOException if an I/O error occurs. */ public long getPosition() throws IOException { ensureOpenAndSelected(); ensureNotDirectory(); return getPosition0(); } // // Size management // /** * Get the length of the data * * @return the length of the data */ public long getLength() throws IOException { ensureOpenForReadingAndSelected(); ensureNotDirectory(); return getLength0(); } /** * Set the length of the data (for truncation). * * @param len the new length of the data */ public void setLength(long len) throws IOException { ensureOpenForWritingAndSelected(); ensureNotDirectory(); setLength0(len); } // // I/O // /** * Reads a byte of data. The byte is returned as an * integer in the range 0 to 255 (<code>0x00-0x0ff</code>). This * method blocks if no input is yet available. * <p> * @return the next byte of data, or <code>-1</code> if the end of the * data has been reached. * @exception IOException if an I/O error occurs. Not thrown if * end-of-data has been reached. */ public int read() throws IOException { ensureOpenForReadingAndSelected(); ensureNotDirectory(); return read0(); } /** * Reads up to <code>len</code> bytes of data into an * array of bytes. This method blocks until at least one byte of input * is available. * * @param b the buffer into which the data is read. * @param off the start offset of the data. * @param len the maximum number of bytes read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of * the data has been reached. * @exception IOException if an I/O error occurs. */ public int read(byte b[], int off, int len) throws IOException { ensureOpenForReadingAndSelected(); ensureNotDirectory(); return read0(b, off, len); } /** * Reads up to <code>len</code> bytes of data into an * array of bytes. This method blocks until at least one byte of input * is available. * * @param b the buffer into which the data is read. * @param off the start offset of the data. * @param len the maximum number of bytes read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of * the data has been reached. * @exception IOException if an I/O error occurs. */ public int read0(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } return readBytes0(b, off, len); } /** * Writes the specified byte to this file. The write starts at * the current position pointer. * * @param b the <code>byte</code> to be written. * @exception IOException if an I/O error occurs. */ public void write(int b) throws IOException { ensureOpenForWritingAndSelected(); ensureNotDirectory(); write0(b); } /** * Writes <code>len</code> bytes from the specified byte array * starting at offset <code>off</code> to the data * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @exception IOException if an I/O error occurs. */ public void write(byte b[], int off, int len) throws IOException { ensureOpenForWritingAndSelected(); ensureNotDirectory(); write0(b, off, len); } /** * Writes <code>len</code> bytes from the specified byte array * starting at offset <code>off</code> to the data * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @exception IOException if an I/O error occurs. */ void write0(byte b[], int off, int len) throws IOException { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return; } writeBytes0(b, off, len); } /**********************************************************************\ * Methods from DirectoryConnection * \**********************************************************************/ // // Collection size functions // /** * Return the size in bytes that the collection can grow to * * @return size */ public long getAvailableSpace() throws IOException { ensureOpen(); return getAvailableSpace0(); } /** * Return the number of items in the collection * * @return size */ public int getItemCount() throws IOException { ensureOpen(); return getItemCount0(); } // // Item selection // /** * Select the first record int the database * * @return true if there was a record false if the collection was empty * @exception IOException if an I/O error occurs. */ public boolean selectFirstItem() throws IOException { ensureOpen(); return selectFirstItem0(); } /** * Select the next record int the database * * @return true if there was another record false otherwise * @exception IOException if an I/O error occurs. */ public boolean selectNextItem() throws IOException { ensureOpen(); return selectNextItem0(); } /** * Select an item in the collection * @param name the name of the item to select * @return true if the record exists * @exception IOException if an I/O error occurs. */ public boolean selectItem(String name) throws IOException { ensureOpen(); return selectItem0(name); } /** * Select an item in the collection * @param i the record number * @return true if the record exists * @exception IOException if an I/O error occurs. */ public boolean selectItem(int i) throws IOException { ensureOpen(); return selectItemByInt0(i); } /** * Unselect the current item. This is a way to unlock a record without locking another one. * @exception IOException if an I/O error occurs. */ public void deselectItem() throws IOException { ensureOpen(); deselectItem0(); } // // Metadata management // /** * Test to see if the current item a directory * @return true if it is, false if it is not */ public boolean isDirectory() throws IOException { ensureOpen(); return isDirectory0(); } /** * Create a data item in the collection with a randomly chosen unique name and select it * @exception IOException if an I/O error occurs. */ public void create() throws IOException { ensureOpenForWriting(); create0(); } /** * Create a data item with the supplied name and select it * @param name the name of the item to create * @exception IOException if an I/O error occurs. */ public void create(String name) throws IOException { ensureOpenForWriting(); createName0(name); } /** * Create an item with the supplied number * @param i the record number * @exception IOException if an I/O error occurs. */ public void create(int i) throws IOException { ensureOpenForWriting(); createNameByInt0(i); } /** * Create a directory with the supplied name * @param name the name of the directory to create * @exception IOException if an I/O error occurs. */ public void createDirectory(String name) throws IOException { ensureOpenForWriting(); createDirectory0(name); } /** * Delete the current data item from the collection * @exception IOException if an I/O error occurs. */ public void delete() throws IOException { ensureOpenForWritingAndSelected(); ensureNotDirectory(); delete0(); } /** * Delete the current data item from the collection * @exception IOException if an I/O error occurs. */ public void deleteDirectory() throws IOException { ensureOpenForWriting(); ensureDirectory(); delete0(); } /** * Rename the current data item * @param name the new name for the item * @exception IOException if an I/O error occurs. */ public void rename(String newName) throws IOException { ensureOpenForWritingAndSelected(); ensureNotDirectory(); rename0(newName); } /** * Rename the current data item * @param name the new name for the item * @exception IOException if an I/O error occurs. */ public void rename(int i) throws IOException { ensureOpenForWritingAndSelected(); ensureNotDirectory(); renameByInt0(i); } /** * Rename a directory with the supplied name * @param newName the new name for the directory * @exception IOException if an I/O error occurs. */ public void renameDirectory(String newName) throws IOException { ensureOpenForWritingAndSelected(); ensureDirectory(); renameDirectory0(newName); } /** * Return the name of the currently selected item * @return the name of the item or null if no item is selected * @exception IOException if an I/O error occurs. */ public String getItemName() throws IOException { ensureOpenAndSelected(); return getItemName0(); } /** * Return the number of the current item * @return the current item number * @exception IOException if an I/O error occurs or if the item does not have a numeric name. */ public int getItemNumber() throws IOException { ensureOpenAndSelected(); return getItemNumber0(); } /** * Return the date that the item was last modified * @return date or -1 if the record was undated. * @exception IOException if an I/O error occurs. */ public long getModificationDate() throws IOException { ensureOpenAndSelected(); return getModificationDate0(); } // // R/W bits // /** * Test to see if the selected item can be read * @return true if the item can be read, else false. * @exception IOException if an I/O error occurs. */ public boolean canRead() throws IOException { ensureOpenAndSelected(); return canRead0(); } /** * Set or clear the read bit * @param tf the new value for the read bit * @exception IOException if an I/O error occurs. */ public void setReadable(boolean tf) throws IOException { ensureOpenAndSelected(); setReadable0(tf); } /** * Test to see if the selected item can be written * @return true if the item can be written, else false. * @exception IOException if an I/O error occurs. */ public boolean canWrite() throws IOException { ensureOpenAndSelected(); return canWrite0(); } /** * Set or clear the write bit * @param tf the new value for the write bit * @exception IOException if an I/O error occurs. */ public void setWritable(boolean tf) throws IOException { ensureOpenAndSelected(); setWritable0(tf); } /**********************************************************************\ * Native Methods * \**********************************************************************/ public abstract Connection open0(String openName, String parms, int mode) throws IOException; public abstract void close0() throws IOException; public abstract long getAvailableSpace0() throws IOException; public abstract int getItemCount0() throws IOException; public abstract boolean selectFirstItem0() throws IOException; public abstract boolean selectNextItem0() throws IOException; public abstract boolean selectItem0(String name) throws IOException; public abstract boolean selectItemByInt0(int i) throws IOException; public abstract void deselectItem0() throws IOException; public abstract boolean isSelected0() throws IOException; public abstract void create0() throws IOException; public abstract void createName0(String name) throws IOException; public abstract void createNameByInt0(int i) throws IOException; public abstract void createDirectory0(String name) throws IOException; public abstract void delete0() throws IOException; public abstract void rename0(String name2) throws IOException; public abstract void renameByInt0(int i) throws IOException; public abstract void renameDirectory0(String name2) throws IOException; public abstract long getLength0() throws IOException; public abstract void setLength0(long len) throws IOException; public abstract long getModificationDate0() throws IOException; public abstract String getItemName0() throws IOException; public abstract int getItemNumber0() throws IOException; public abstract boolean isDirectory0() throws IOException; public abstract boolean canRead0() throws IOException; public abstract boolean canWrite0() throws IOException; public abstract void setReadable0(boolean tf) throws IOException; public abstract void setWritable0(boolean tf) throws IOException; public abstract int available0() throws IOException; public abstract void seek0(long pos) throws IOException; public abstract long getPosition0() throws IOException; public abstract int read0() throws IOException; public abstract int readBytes0(byte b[], int off, int len) throws IOException; public abstract void write0(int b) throws IOException; public abstract void writeBytes0(byte b[], int off, int len) throws IOException; } /**********************************************************************\ * PrivateFileInputStream * \**********************************************************************/ class PrivateFileInputStream extends InputStream { ProtocolBase parent; public PrivateFileInputStream(ProtocolBase parent) throws IOException { this.parent = parent; } void ensureOpen() throws IOException { if(parent == null) { throw new IOException("Stream closed"); } } public int read() throws IOException { ensureOpen(); return parent.read0(); } public int read(byte b[], int off, int len) throws IOException { ensureOpen(); return parent.read0(b, off, len); } public int available() throws IOException { ensureOpen(); return parent.available0(); } public void close() throws IOException { ensureOpen(); parent.operationMode = ProtocolBase.O_RAND; parent.realClose(); parent = null; } } /**********************************************************************\ * PrivateFileOutputStream * \**********************************************************************/ class PrivateFileOutputStream extends OutputStream { ProtocolBase parent; public PrivateFileOutputStream(ProtocolBase parent) throws IOException { this.parent = parent; } void ensureOpen() throws IOException { if(parent == null) { throw new IOException("Stream closed"); } } public void write(int b) throws IOException { ensureOpen(); parent.write0(b); } public void write(byte b[], int off, int len) throws IOException { ensureOpen(); parent.write0(b, off, len); } public void close() throws IOException { ensureOpen(); parent.operationMode = ProtocolBase.O_RAND; parent.realClose(); parent = null; } }