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.cldc.io;
028:
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.util.Vector;
032:
033: /**
034: * Input stream class for accessing resource files in classpath.
035: */
036: public class ResourceInputStream extends InputStream {
037: private Object fileDecoder;
038: private Object savedDecoder; // used for mark/reset functionality
039:
040: /**
041: * Fixes the resource name to be conformant with the CLDC 1.0
042: * specification. We are not allowed to use "../" to get outside
043: * of the .jar file.
044: *
045: * @param name the name of the resource in classpath to access.
046: * @return the fixed string.
047: * @exception IOException if the resource name points to a
048: * classfile, as determined by the resource name's
049: * extension.
050: */
051: private static String fixResourceName(String name)
052: throws IOException {
053: Vector dirVector = new Vector();
054: int startIdx = 0;
055: int endIdx = 0;
056: String curDir;
057:
058: while ((endIdx = name.indexOf('/', startIdx)) != -1) {
059: if (endIdx == startIdx) {
060: // We have a leading '/' or two consecutive '/'s
061: startIdx++;
062: continue;
063: }
064:
065: curDir = name.substring(startIdx, endIdx);
066: startIdx = endIdx + 1;
067:
068: if (curDir.equals(".")) {
069: // Ignore a single '.' directory
070: continue;
071: }
072: if (curDir.equals("..")) {
073: // Go up a level
074: try {
075: dirVector.removeElementAt(dirVector.size() - 1);
076: } catch (ArrayIndexOutOfBoundsException aioobe) {
077: // "/../resource" Not allowed!
078: throw new IOException();
079: }
080: continue;
081: }
082: dirVector.addElement(curDir);
083: }
084:
085: // save directory structure
086: StringBuffer dirName = new StringBuffer();
087:
088: int nelements = dirVector.size();
089: for (int i = 0; i < nelements; ++i) {
090: dirName.append((String) dirVector.elementAt(i));
091: dirName.append("/");
092: }
093:
094: // save filename
095: if (startIdx < name.length()) {
096: String filename = name.substring(startIdx);
097: // Throw IOE if the resource ends with ".class", but, not
098: // if the entire name is ".class"
099: if ((filename.endsWith(".class"))
100: && (!".class".equals(filename))) {
101: throw new IOException();
102: }
103: dirName.append(name.substring(startIdx));
104: }
105: return dirName.toString();
106: }
107:
108: /**
109: * Construct a resource input stream for accessing objects in the jar file.
110: *
111: * @param name the name of the resource in classpath to access. The
112: * name must not have a leading '/'.
113: * @exception IOException if an I/O error occurs.
114: */
115: public ResourceInputStream(String name) throws IOException {
116: String fixedName = fixResourceName(name);
117: fileDecoder = open(fixedName);
118: if (fileDecoder == null) {
119: throw new IOException();
120: }
121: }
122:
123: /**
124: * Reads the next byte of data from the input stream.
125: *
126: * @return the next byte of data, or <code>-1</code> if the end
127: * of the stream is reached.
128: * @exception IOException if an I/O error occurs.
129: */
130: public int read() throws IOException {
131: // Fix for CR 6303054
132: if (fileDecoder == null) {
133: throw new IOException();
134: }
135: return readByte(fileDecoder);
136: }
137:
138: /**
139: * Gets the number of bytes remaining to be read.
140: *
141: * @return the number of bytes remaining in the resource.
142: * @exception IOException if an I/O error occurs.
143: */
144: public int available() throws IOException {
145: if (fileDecoder == null) {
146: throw new IOException();
147: }
148: return bytesRemain(fileDecoder);
149: }
150:
151: /**
152: * Reads bytes into a byte array.
153: *
154: * @param b the buffer to read into.
155: * @param off offset to start at in the buffer.
156: * @param len number of bytes to read.
157: * @return the number of bytes read, or <code>-1</code> if the end
158: * of the stream is reached.
159: * @exception IOException if an I/O error occurs.
160: */
161: public int read(byte b[], int off, int len) throws IOException {
162: // Fix for CR 6303054
163: if (fileDecoder == null) {
164: throw new IOException();
165: }
166: if (b == null) {
167: throw new NullPointerException();
168: } else if ((off < 0) || (off > b.length) || (len < 0)
169: || ((off + len) > b.length) || ((off + len) < 0)) {
170: throw new IndexOutOfBoundsException();
171: }
172: return readBytes(fileDecoder, b, off, len);
173: }
174:
175: public void close() throws IOException {
176: fileDecoder = null;
177: }
178:
179: /**
180: * Remembers current position in ResourceInputStream so that
181: * subsequent call to <code>reset</code> will rewind the stream
182: * to the saved position.
183: *
184: * @param readlimit affects nothing
185: * @see java.io.InputStream#reset()
186: */
187: public void mark(int readlimit) {
188: if (fileDecoder != null) {
189: savedDecoder = clone(fileDecoder);
190: }
191: }
192:
193: /**
194: * Repositions this stream to the position at the time the
195: * <code>mark</code> method was last called on this input stream.
196: *
197: * @exception IOException if this stream has not been marked
198: * @see java.io.InputStream#mark(int)
199: */
200: public void reset() throws IOException {
201: if (fileDecoder == null || savedDecoder == null) {
202: throw new IOException();
203: }
204: fileDecoder = clone(savedDecoder);
205: }
206:
207: /**
208: * Indicates that this ResourceInputStream supports mark/reset
209: * functionality
210: *
211: * @return true
212: */
213: public boolean markSupported() {
214: return true;
215: }
216:
217: // OS-specific interface to underlying file system.
218: private static native Object open(String name);
219:
220: private static native int bytesRemain(Object fileDecoder);
221:
222: private static native int readByte(Object fileDecoder);
223:
224: private static native int readBytes(Object fileDecoder, byte b[],
225: int off, int len);
226:
227: /*
228: * Copies all fields from one FileDecoder object to another -
229: * used remember or restore current ResourceInputStream state.
230: */
231: private static native Object clone(Object source);
232: }
|