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    }