001/*
002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     matic
018 */
019package org.nuxeo.ecm.automation.jaxrs.io;
020
021import java.io.BufferedInputStream;
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.IOException;
025import java.io.InputStream;
026
027import javax.mail.internet.SharedInputStream;
028
029public class SharedFileInputStream extends InputStream implements SharedInputStream {
030
031    protected final InputStream in;
032
033    protected final SharedFileInputStream parent;
034
035    protected final File file;
036
037    protected final long length;
038
039    protected final long start;
040
041    protected long current;
042
043    protected long marked;
044
045    public SharedFileInputStream(File file) throws IOException {
046        this.in = new BufferedInputStream(new FileInputStream(file));
047        this.parent = null;
048        this.file = file;
049        this.start = 0;
050        this.length = file.length();
051    }
052
053    protected SharedFileInputStream(SharedFileInputStream parent, long start, long len) throws IOException {
054        this.in = new BufferedInputStream(new FileInputStream(parent.file));
055        this.parent = parent;
056        this.file = parent.file;
057        this.start = start;
058        this.length = len;
059        skip(this.in, start);
060    }
061
062    /** Skip reliably; this is more efficient than IOUtils.skip which calls read(). */
063    protected static void skip(InputStream in, long n) throws IOException {
064        long todo = n;
065        while (todo > 0) {
066            long skipped = in.skip(todo);
067            if (skipped <= 0) {
068                throw new IOException("Failed to skip " + n + " bytes");
069            }
070            todo -= skipped;
071        }
072    }
073
074    @Override
075    public long getPosition() {
076        return this.current;
077    }
078
079    @Override
080    public SharedFileInputStream newStream(long start, long end) {
081        try {
082            long length;
083            if (end == -1L) {
084                length = this.length - start;
085            } else {
086                length = end - start;
087            }
088            return new SharedFileInputStream(this, this.start + start, length);
089        } catch (IOException localIOException) {
090            throw new IllegalStateException("unable to create shared stream: " + localIOException);
091        }
092    }
093
094    @Override
095    public int read(byte[] buffer) throws IOException {
096        return read(buffer, 0, buffer.length);
097    }
098
099    @Override
100    public int read(byte[] buffer, int offset, int len) throws IOException {
101        int i = 0;
102        if (len == 0)
103            return 0;
104        while (i < len) {
105            int j = read();
106            if (j < 0)
107                break;
108            buffer[(offset + i)] = (byte) j;
109            ++i;
110        }
111        if (i == 0)
112            return -1;
113        return i;
114    }
115
116    @Override
117    public int read() throws IOException {
118        if (this.current == this.length)
119            return -1;
120        this.current += 1L;
121        return this.in.read();
122    }
123
124    @Override
125    public boolean markSupported() {
126        return true;
127    }
128
129    @Override
130    public long skip(long len) throws IOException {
131        for (int count = 0; count < len; ++count) {
132            if (read() < 0)
133                return count;
134        }
135        return len;
136    }
137
138    @Override
139    public void mark(int limit) {
140        this.marked = this.current;
141        this.in.mark(limit);
142    }
143
144    @Override
145    public void reset() throws IOException {
146        current = marked;
147        in.reset();
148    }
149
150    public SharedFileInputStream getRoot() {
151        if (parent != null)
152            return parent.getRoot();
153        return this;
154    }
155}