001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://uio.imagero.com
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * o Redistributions of source code must retain the above copyright notice,
010 * this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright notice,
013 * this list of conditions and the following disclaimer in the documentation
014 * and/or other materials provided with the distribution.
015 *
016 * o Neither the name of Andrey Kuznetsov nor the names of
017 * its contributors may be used to endorse or promote products derived
018 * from this software without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032
033 package com.imagero.uio.impl;
034
035 import com.imagero.uio.RandomAccessIO;
036 import com.imagero.uio.RandomAccessInput;
037 import com.imagero.uio.RandomAccessOutput;
038 import com.imagero.uio.Transformer;
039 import com.imagero.uio.UIOStreamBuilder;
040 import com.imagero.uio.bio.BufferedRandomAccessIO;
041 import com.imagero.uio.bio.IOCInputStream;
042 import com.imagero.uio.bio.IOCOutputStream;
043 import com.imagero.uio.bio.IOController;
044 import com.imagero.uio.bio.content.Content;
045 import com.imagero.uio.bio.content.RandomAccessFileContent;
046 import com.imagero.uio.io.IOutils;
047
048 import java.io.EOFException;
049 import java.io.IOException;
050 import java.io.InputStream;
051 import java.io.OutputStream;
052 import java.io.RandomAccessFile;
053 import java.lang.reflect.Method;
054
055 /**
056 * Wrap RandomAccessFile in RandomAccessIO<br>
057 * Attention - this class is not buffered.
058 * That means if you make extensive use of writeInt, writeLong, writeChar, ...,
059 * then performance will be pretty poor. Use buffered classes instead.
060 *
061 * @author Andrei Kouznetsov
062 * Date: 08.11.2003
063 * Time: 13:04:44
064 */
065 public class RandomAccessFileWrapper extends AbstractRandomAccessIO {
066
067 RandomAccessFile in;
068
069 IOController controller;
070
071 long offset;
072 Long length;
073
074 public RandomAccessFileWrapper(RandomAccessFile in, int byteOrder) throws IOException {
075 this.in = in;
076 setByteOrder(byteOrder);
077 }
078
079 public RandomAccessFileWrapper(RandomAccessFile in, long offset, int byteOrder) throws IOException {
080 if (offset < 0 || offset >= in.length()) {
081 throw new IndexOutOfBoundsException();
082 }
083 this.in = in;
084 this.offset = offset;
085 setByteOrder(byteOrder);
086 }
087
088 public RandomAccessFileWrapper(RandomAccessFile in, long offset, long length, int byteOrder) throws IOException {
089 if (offset < 0 || offset >= in.length()) {
090 throw new IndexOutOfBoundsException();
091 }
092 this.in = in;
093 this.offset = offset;
094 this.length = new Long(length);
095 setByteOrder(byteOrder);
096 }
097
098 protected int _read() throws IOException {
099 if (getFilePointer() >= length()) {
100 throw new EOFException();
101 }
102 int i = in.read();
103 if (i < 0) {
104 throw new EOFException();
105 }
106 return i;
107 }
108
109 public void write(int b) throws IOException {
110 if (length != null && getFilePointer() >= length()) {
111 // throw new EOFException();
112 }
113 else {
114 in.write(b);
115 }
116 }
117
118 public void write(byte b[], int off, int len) throws IOException {
119 int max = len;
120 if (length != null) {
121 max = (int) Math.min(Math.max(length() - getFilePointer(), 0), len);
122 }
123 in.write(b, off, max);
124 }
125
126 public long getFilePointer() throws IOException {
127 if (length == null) {
128 return in.getFilePointer() - offset;
129 }
130 else {
131 return Math.min(length.longValue(), in.getFilePointer() - offset);
132 }
133 }
134
135 public long length() throws IOException {
136 if (length == null) {
137 return in.length() - offset;
138 }
139 else {
140 return Math.min(length.longValue(), in.length() - offset);
141 }
142 }
143
144 public void seek(long pos) throws IOException {
145 long max = pos;
146 if (length != null) {
147 max = Math.min(Math.max(pos, 0), length.longValue());
148 }
149 in.seek(max + offset);
150 }
151
152 public int read() throws IOException {
153 if (getFilePointer() >= length()) {
154 return -1;
155 }
156 return in.read();
157 }
158
159 public long skip(long n) throws IOException {
160 int max = (int) Math.min(Math.max(length() - getFilePointer(), 0), n);
161 return in.skipBytes(max);
162 }
163
164 public void close() throws IOException {
165 IOutils.closeStream(in);
166 in = null;
167 }
168
169 public int read(byte[] b, int off, int len) throws IOException {
170 int max = (int) Math.min(Math.max(length() - getFilePointer(), 0), len);
171 if(max == 0) {
172 return -1;
173 }
174 return in.read(b, off, max);
175 }
176
177 /**
178 * Set length - only possible if length was not set in constructor.
179 * With 1.2 and later this method works as expected,
180 * with 1.1 it can only grow the file, but can not truncate it.
181 *
182 * @param newLength
183 *
184 * @throws java.io.IOException
185 */
186 public void setLength(long newLength) throws IOException {
187 if (length == null) {
188 try {
189 Class aClass = Class.forName("java.io.RandomAccessFile");
190 Method method = aClass.getMethod("setLength", new Class[]{Long.class});
191 method.invoke(in, new Object[]{new Long(newLength + offset)});
192 }
193 catch (Exception ex) {
194 if (newLength > in.length()) {
195 long pos = getFilePointer();
196 seek(newLength);
197 write(0);
198 seek(pos);
199 }
200 }
201 }
202 }
203
204 public int read(short[] dest, int offset, int length, int byteOrder) throws IOException {
205 byte [] b = new byte[dest.length << 1];
206 int read = read(b);
207 if((read & 1) != 0) {
208 seek(getFilePointer() - 1);
209 }
210 final int count = read >> 1;
211 com.imagero.uio.Transformer.byteToShort(b, 0, count, dest, offset, byteOrder == BIG_ENDIAN);
212 return count;
213 }
214
215 public int read(char[] dest, int offset, int length, int byteOrder) throws IOException {
216 byte [] b = new byte[dest.length << 1];
217 int read = read(b);
218 if((read & 1) != 0) {
219 seek(getFilePointer() - 1);
220 }
221 final int count = read >> 1;
222 com.imagero.uio.Transformer.byteToChar(b, 0, count, dest, offset, byteOrder == BIG_ENDIAN);
223 return count;
224 }
225
226 public int read(int[] dest, int offset, int length, int byteOrder) throws IOException {
227 byte [] b = new byte[dest.length << 2];
228 int read = read(b);
229 final int r3 = read & 3;
230 if(r3 != 0) {
231 seek(getFilePointer() - r3);
232 }
233 final int count = read >> 2;
234 com.imagero.uio.Transformer.byteToInt(b, 0, count, dest, offset, byteOrder == BIG_ENDIAN);
235 return count;
236 }
237
238 public int read(long[] dest, int offset, int length, int byteOrder) throws IOException {
239 byte [] b = new byte[dest.length << 3];
240 int read = read(b);
241 final int r7 = read & 7;
242 if(r7 != 0) {
243 seek(getFilePointer() - r7);
244 }
245 final int count = read >> 3;
246 com.imagero.uio.Transformer.byteToLong(b, 0, count, dest, offset, byteOrder == BIG_ENDIAN);
247 return count;
248 }
249
250 public int read(float[] dest, int offset, int length, int byteOrder) throws IOException {
251 byte [] b = new byte[dest.length << 2];
252 int read = read(b);
253 final int r3 = read & 3;
254 if(r3 != 0) {
255 seek(getFilePointer() - r3);
256 }
257 final int count = read >> 2;
258 com.imagero.uio.Transformer.byteToFloat(b, 0, count, dest, offset, byteOrder == BIG_ENDIAN);
259 return count;
260 }
261
262 public int read(double[] dest, int offset, int length, int byteOrder) throws IOException {
263 byte [] b = new byte[dest.length << 3];
264 int read = read(b);
265 final int r7 = read & 7;
266 if(r7 != 0) {
267 seek(getFilePointer() - r7);
268 }
269 final int count = read >> 3;
270 com.imagero.uio.Transformer.byteToDouble(b, 0, count, dest, offset, byteOrder == BIG_ENDIAN);
271 return count;
272 }
273
274 public void readFully(byte b[], int off, int len) throws IOException {
275 in.readFully(b, off, len);
276 }
277
278 public RandomAccessIO createIOChild(long offset, int byteOrder, boolean syncPointer) throws IOException {
279 if (controller == null) {
280 Content content = new RandomAccessFileContent(in);
281 controller = new IOController(com.imagero.uio.UIOStreamBuilder.DEFAULT_CHUNK_SIZE, content);
282 }
283 return new BufferedRandomAccessIO(controller, offset);
284 }
285
286 public RandomAccessInput createInputChild(long offset, int byteOrder, boolean syncPointer) throws IOException {
287 return createIOChild(offset, byteOrder, syncPointer);
288 }
289
290 public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) throws IOException {
291 return createIOChild(offset, byteOrder, syncPointer);
292 }
293
294 public InputStream createInputStream(long offset) {
295 if(controller == null) {
296 Content bc = new RandomAccessFileContent(in);
297 controller = new IOController(com.imagero.uio.UIOStreamBuilder.DEFAULT_CHUNK_SIZE, bc);
298 }
299 return new IOCInputStream(controller, offset);
300 }
301
302 public OutputStream createOutputStream(long offset) {
303 if (controller == null) {
304 Content bc = new RandomAccessFileContent(in);
305 controller = new IOController(com.imagero.uio.UIOStreamBuilder.DEFAULT_CHUNK_SIZE, bc);
306 }
307 return new IOCOutputStream(controller, offset);
308 }
309 }