0N/A/*
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0N/A *
678N/A * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * The contents of this file are subject to the terms of either the GNU
0N/A * General Public License Version 2 only ("GPL") or the Common Development
0N/A * and Distribution License("CDDL") (collectively, the "License"). You
292N/A * may not use this file except in compliance with the License. You can
292N/A * obtain a copy of the License at
292N/A * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
292N/A * or packager/legal/LICENSE.txt. See the License for the specific
0N/A * language governing permissions and limitations under the License.
0N/A *
0N/A * When distributing the software, include this License Header Notice in each
292N/A * file and include the License file at packager/legal/LICENSE.txt.
292N/A *
292N/A * GPL Classpath Exception:
292N/A * Oracle designates this particular file as subject to the "Classpath"
292N/A * exception as provided by Oracle in the GPL Version 2 section of the License
292N/A * file that accompanied this code.
292N/A *
292N/A * Modifications:
292N/A * If applicable, add the following below the License Header, with the fields
292N/A * enclosed by brackets [] replaced by your own identifying information:
292N/A * "Portions Copyright [year] [name of copyright owner]"
0N/A *
0N/A * Contributor(s):
0N/A * If you wish your version of this file to be governed by only the CDDL or
0N/A * only the GPL Version 2, indicate your decision by adding "[Contributor]
0N/A * elects to include this software in this distribution under the [CDDL or GPL
0N/A * Version 2] license." If you don't indicate a single choice of license, a
0N/A * recipient has the option to distribute your version of this file under
0N/A * either the CDDL, the GPL Version 2 or to extend the choice of license to
0N/A * its licensees as provided above. However, if you add GPL Version 2 code
0N/A * and therefore, elected the GPL Version 2 license, then the option applies
0N/A * only if the new code is made subject to such option by the copyright
0N/A * holder.
0N/A */
0N/A
0N/Apackage javax.mail.util;
0N/A
0N/Aimport java.io.*;
0N/Aimport javax.mail.internet.SharedInputStream;
0N/A
0N/A/**
0N/A * A <code>SharedFileInputStream</code> is a
0N/A * <code>BufferedInputStream</code> that buffers
0N/A * data from the file and supports the <code>mark</code>
0N/A * and <code>reset</code> methods. It also supports the
0N/A * <code>newStream</code> method that allows you to create
0N/A * other streams that represent subsets of the file.
0N/A * A <code>RandomAccessFile</code> object is used to
0N/A * access the file data. <p>
0N/A *
0N/A * Note that when the SharedFileInputStream is closed,
0N/A * all streams created with the <code>newStream</code>
0N/A * method are also closed. This allows the creator of the
0N/A * SharedFileInputStream object to control access to the
0N/A * underlying file and ensure that it is closed when
0N/A * needed, to avoid leaking file descriptors. Note also
0N/A * that this behavior contradicts the requirements of
0N/A * SharedInputStream and may change in a future release.
0N/A *
0N/A * @author Bill Shannon
0N/A * @since JavaMail 1.4
0N/A */
0N/Apublic class SharedFileInputStream extends BufferedInputStream
0N/A implements SharedInputStream {
0N/A
0N/A private static int defaultBufferSize = 2048;
0N/A
0N/A /**
0N/A * The file containing the data.
0N/A * Shared by all related SharedFileInputStreams.
0N/A */
0N/A protected RandomAccessFile in;
0N/A
0N/A /**
0N/A * The normal size of the read buffer.
0N/A */
0N/A protected int bufsize;
0N/A
0N/A /**
0N/A * The file offset that corresponds to the first byte in
0N/A * the read buffer.
0N/A */
0N/A protected long bufpos;
0N/A
0N/A /**
0N/A * The file offset of the start of data in this subset of the file.
0N/A */
0N/A protected long start = 0;
0N/A
0N/A /**
0N/A * The amount of data in this subset of the file.
0N/A */
0N/A protected long datalen;
0N/A
0N/A /**
0N/A * True if this is a top level stream created directly by "new".
0N/A * False if this is a derived stream created by newStream.
0N/A */
0N/A private boolean master = true;
0N/A
0N/A /**
0N/A * A shared class that keeps track of the references
0N/A * to a particular file so it can be closed when the
0N/A * last reference is gone.
0N/A */
0N/A static class SharedFile {
0N/A private int cnt;
0N/A private RandomAccessFile in;
0N/A
0N/A SharedFile(String file) throws IOException {
0N/A this.in = new RandomAccessFile(file, "r");
0N/A }
0N/A
0N/A SharedFile(File file) throws IOException {
0N/A this.in = new RandomAccessFile(file, "r");
0N/A }
0N/A
338N/A public synchronized RandomAccessFile open() {
0N/A cnt++;
0N/A return in;
0N/A }
0N/A
0N/A public synchronized void close() throws IOException {
0N/A if (cnt > 0 && --cnt <= 0)
0N/A in.close();
0N/A }
0N/A
0N/A public synchronized void forceClose() throws IOException {
0N/A if (cnt > 0) {
0N/A // normal case, close exceptions propagated
0N/A cnt = 0;
0N/A in.close();
0N/A } else {
0N/A // should already be closed, ignore exception
0N/A try {
0N/A in.close();
0N/A } catch (IOException ioex) { }
0N/A }
0N/A }
0N/A
0N/A protected void finalize() throws Throwable {
741N/A try {
741N/A in.close();
741N/A } finally {
741N/A super.finalize();
741N/A }
0N/A }
0N/A }
0N/A
0N/A private SharedFile sf;
0N/A
0N/A /**
0N/A * Check to make sure that this stream has not been closed
0N/A */
0N/A private void ensureOpen() throws IOException {
0N/A if (in == null)
0N/A throw new IOException("Stream closed");
0N/A }
0N/A
0N/A /**
0N/A * Creates a <code>SharedFileInputStream</code>
0N/A * for the file.
0N/A *
0N/A * @param file the file
610N/A * @exception IOException for errors opening the file
0N/A */
0N/A public SharedFileInputStream(File file) throws IOException {
0N/A this(file, defaultBufferSize);
0N/A }
0N/A
0N/A /**
0N/A * Creates a <code>SharedFileInputStream</code>
0N/A * for the named file
0N/A *
0N/A * @param file the file
610N/A * @exception IOException for errors opening the file
0N/A */
0N/A public SharedFileInputStream(String file) throws IOException {
0N/A this(file, defaultBufferSize);
0N/A }
0N/A
0N/A /**
0N/A * Creates a <code>SharedFileInputStream</code>
0N/A * with the specified buffer size.
0N/A *
0N/A * @param file the file
0N/A * @param size the buffer size.
610N/A * @exception IOException for errors opening the file
610N/A * @exception IllegalArgumentException if size &le; 0.
0N/A */
0N/A public SharedFileInputStream(File file, int size) throws IOException {
0N/A super(null); // XXX - will it NPE?
0N/A if (size <= 0)
0N/A throw new IllegalArgumentException("Buffer size <= 0");
0N/A init(new SharedFile(file), size);
0N/A }
0N/A
0N/A /**
0N/A * Creates a <code>SharedFileInputStream</code>
0N/A * with the specified buffer size.
0N/A *
0N/A * @param file the file
0N/A * @param size the buffer size.
610N/A * @exception IOException for errors opening the file
610N/A * @exception IllegalArgumentException if size &le; 0.
0N/A */
0N/A public SharedFileInputStream(String file, int size) throws IOException {
0N/A super(null); // XXX - will it NPE?
0N/A if (size <= 0)
0N/A throw new IllegalArgumentException("Buffer size <= 0");
0N/A init(new SharedFile(file), size);
0N/A }
0N/A
0N/A private void init(SharedFile sf, int size) throws IOException {
0N/A this.sf = sf;
0N/A this.in = sf.open();
0N/A this.start = 0;
0N/A this.datalen = in.length(); // XXX - file can't grow
0N/A this.bufsize = size;
0N/A buf = new byte[size];
0N/A }
0N/A
0N/A /**
0N/A * Used internally by the <code>newStream</code> method.
0N/A */
0N/A private SharedFileInputStream(SharedFile sf, long start, long len,
0N/A int bufsize) {
0N/A super(null);
0N/A this.master = false;
0N/A this.sf = sf;
0N/A this.in = sf.open();
0N/A this.start = start;
0N/A this.bufpos = start;
0N/A this.datalen = len;
0N/A this.bufsize = bufsize;
0N/A buf = new byte[bufsize];
0N/A }
0N/A
0N/A /**
0N/A * Fills the buffer with more data, taking into account
0N/A * shuffling and other tricks for dealing with marks.
0N/A * Assumes that it is being called by a synchronized method.
0N/A * This method also assumes that all data has already been read in,
0N/A * hence pos > count.
0N/A */
0N/A private void fill() throws IOException {
0N/A if (markpos < 0) {
0N/A pos = 0; /* no mark: throw away the buffer */
0N/A bufpos += count;
0N/A } else if (pos >= buf.length) /* no room left in buffer */
0N/A if (markpos > 0) { /* can throw away early part of the buffer */
0N/A int sz = pos - markpos;
0N/A System.arraycopy(buf, markpos, buf, 0, sz);
0N/A pos = sz;
0N/A bufpos += markpos;
0N/A markpos = 0;
0N/A } else if (buf.length >= marklimit) {
0N/A markpos = -1; /* buffer got too big, invalidate mark */
0N/A pos = 0; /* drop buffer contents */
0N/A bufpos += count;
0N/A } else { /* grow buffer */
0N/A int nsz = pos * 2;
0N/A if (nsz > marklimit)
0N/A nsz = marklimit;
0N/A byte nbuf[] = new byte[nsz];
0N/A System.arraycopy(buf, 0, nbuf, 0, pos);
0N/A buf = nbuf;
0N/A }
0N/A count = pos;
0N/A // limit to datalen
0N/A int len = buf.length - pos;
0N/A if (bufpos - start + pos + len > datalen)
0N/A len = (int)(datalen - (bufpos - start + pos));
568N/A synchronized (in) {
568N/A in.seek(bufpos + pos);
568N/A int n = in.read(buf, pos, len);
568N/A if (n > 0)
568N/A count = n + pos;
568N/A }
0N/A }
0N/A
0N/A /**
0N/A * See the general contract of the <code>read</code>
0N/A * method of <code>InputStream</code>.
0N/A *
0N/A * @return the next byte of data, or <code>-1</code> if the end of the
0N/A * stream is reached.
0N/A * @exception IOException if an I/O error occurs.
0N/A */
0N/A public synchronized int read() throws IOException {
0N/A ensureOpen();
0N/A if (pos >= count) {
0N/A fill();
0N/A if (pos >= count)
0N/A return -1;
0N/A }
0N/A return buf[pos++] & 0xff;
0N/A }
0N/A
0N/A /**
0N/A * Read characters into a portion of an array, reading from the underlying
0N/A * stream at most once if necessary.
0N/A */
0N/A private int read1(byte[] b, int off, int len) throws IOException {
0N/A int avail = count - pos;
0N/A if (avail <= 0) {
0N/A if (false) {
0N/A /* If the requested length is at least as large as the buffer, and
0N/A if there is no mark/reset activity, do not bother to copy the
0N/A bytes into the local buffer. In this way buffered streams will
0N/A cascade harmlessly. */
0N/A if (len >= buf.length && markpos < 0) {
0N/A // XXX - seek, update bufpos - how?
0N/A return in.read(b, off, len);
0N/A }
0N/A }
0N/A fill();
0N/A avail = count - pos;
0N/A if (avail <= 0) return -1;
0N/A }
0N/A int cnt = (avail < len) ? avail : len;
0N/A System.arraycopy(buf, pos, b, off, cnt);
0N/A pos += cnt;
0N/A return cnt;
0N/A }
0N/A
0N/A /**
0N/A * Reads bytes from this stream into the specified byte array,
0N/A * starting at the given offset.
0N/A *
0N/A * <p> This method implements the general contract of the corresponding
0N/A * <code>{@link java.io.InputStream#read(byte[], int, int) read}</code>
0N/A * method of the <code>{@link java.io.InputStream}</code> class.
0N/A *
0N/A * @param b destination buffer.
0N/A * @param off offset at which to start storing bytes.
0N/A * @param len maximum number of bytes to read.
0N/A * @return the number of bytes read, or <code>-1</code> if the end of
0N/A * the stream has been reached.
0N/A * @exception IOException if an I/O error occurs.
0N/A */
0N/A public synchronized int read(byte b[], int off, int len)
0N/A throws IOException
0N/A {
0N/A ensureOpen();
0N/A if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
0N/A throw new IndexOutOfBoundsException();
0N/A } else if (len == 0) {
0N/A return 0;
0N/A }
0N/A
0N/A int n = read1(b, off, len);
0N/A if (n <= 0) return n;
0N/A while ((n < len) /* && (in.available() > 0) */) {
0N/A int n1 = read1(b, off + n, len - n);
0N/A if (n1 <= 0) break;
0N/A n += n1;
0N/A }
0N/A return n;
0N/A }
0N/A
0N/A /**
0N/A * See the general contract of the <code>skip</code>
0N/A * method of <code>InputStream</code>.
0N/A *
0N/A * @param n the number of bytes to be skipped.
0N/A * @return the actual number of bytes skipped.
0N/A * @exception IOException if an I/O error occurs.
0N/A */
0N/A public synchronized long skip(long n) throws IOException {
0N/A ensureOpen();
0N/A if (n <= 0) {
0N/A return 0;
0N/A }
0N/A long avail = count - pos;
0N/A
0N/A if (avail <= 0) {
0N/A // If no mark position set then don't keep in buffer
0N/A /*
0N/A if (markpos <0)
0N/A return in.skip(n);
0N/A */
0N/A
0N/A // Fill in buffer to save bytes for reset
0N/A fill();
0N/A avail = count - pos;
0N/A if (avail <= 0)
0N/A return 0;
0N/A }
0N/A
0N/A long skipped = (avail < n) ? avail : n;
0N/A pos += skipped;
0N/A return skipped;
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of bytes that can be read from this input
0N/A * stream without blocking.
0N/A *
0N/A * @return the number of bytes that can be read from this input
0N/A * stream without blocking.
0N/A * @exception IOException if an I/O error occurs.
0N/A */
0N/A public synchronized int available() throws IOException {
0N/A ensureOpen();
0N/A return (count - pos) + in_available();
0N/A }
0N/A
0N/A private int in_available() throws IOException {
0N/A // XXX - overflow
0N/A return (int)((start + datalen) - (bufpos + count));
0N/A }
0N/A
0N/A /**
0N/A * See the general contract of the <code>mark</code>
0N/A * method of <code>InputStream</code>.
0N/A *
0N/A * @param readlimit the maximum limit of bytes that can be read before
0N/A * the mark position becomes invalid.
0N/A * @see #reset()
0N/A */
0N/A public synchronized void mark(int readlimit) {
0N/A marklimit = readlimit;
0N/A markpos = pos;
0N/A }
0N/A
0N/A /**
0N/A * See the general contract of the <code>reset</code>
0N/A * method of <code>InputStream</code>.
0N/A * <p>
0N/A * If <code>markpos</code> is <code>-1</code>
0N/A * (no mark has been set or the mark has been
0N/A * invalidated), an <code>IOException</code>
0N/A * is thrown. Otherwise, <code>pos</code> is
0N/A * set equal to <code>markpos</code>.
0N/A *
0N/A * @exception IOException if this stream has not been marked or
0N/A * if the mark has been invalidated.
0N/A * @see #mark(int)
0N/A */
0N/A public synchronized void reset() throws IOException {
0N/A ensureOpen();
0N/A if (markpos < 0)
0N/A throw new IOException("Resetting to invalid mark");
0N/A pos = markpos;
0N/A }
0N/A
0N/A /**
0N/A * Tests if this input stream supports the <code>mark</code>
0N/A * and <code>reset</code> methods. The <code>markSupported</code>
0N/A * method of <code>SharedFileInputStream</code> returns
0N/A * <code>true</code>.
0N/A *
0N/A * @return a <code>boolean</code> indicating if this stream type supports
0N/A * the <code>mark</code> and <code>reset</code> methods.
0N/A * @see java.io.InputStream#mark(int)
0N/A * @see java.io.InputStream#reset()
0N/A */
0N/A public boolean markSupported() {
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Closes this input stream and releases any system resources
0N/A * associated with the stream.
0N/A *
0N/A * @exception IOException if an I/O error occurs.
0N/A */
0N/A public void close() throws IOException {
0N/A if (in == null)
0N/A return;
0N/A try {
0N/A if (master)
0N/A sf.forceClose();
0N/A else
0N/A sf.close();
0N/A } finally {
0N/A sf = null;
0N/A in = null;
0N/A buf = null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return the current position in the InputStream, as an
0N/A * offset from the beginning of the InputStream.
0N/A *
0N/A * @return the current position
0N/A */
0N/A public long getPosition() {
787N/A//System.out.println("getPosition: start " + start + " pos " + pos
787N/A// + " bufpos " + bufpos + " = " + (bufpos + pos - start));
0N/A if (in == null)
0N/A throw new RuntimeException("Stream closed");
0N/A return bufpos + pos - start;
0N/A }
0N/A
0N/A /**
0N/A * Return a new InputStream representing a subset of the data
0N/A * from this InputStream, starting at <code>start</code> (inclusive)
0N/A * up to <code>end</code> (exclusive). <code>start</code> must be
0N/A * non-negative. If <code>end</code> is -1, the new stream ends
0N/A * at the same place as this stream. The returned InputStream
0N/A * will also implement the SharedInputStream interface.
0N/A *
0N/A * @param start the starting position
0N/A * @param end the ending position + 1
0N/A * @return the new stream
0N/A */
338N/A public synchronized InputStream newStream(long start, long end) {
0N/A if (in == null)
0N/A throw new RuntimeException("Stream closed");
0N/A if (start < 0)
0N/A throw new IllegalArgumentException("start < 0");
0N/A if (end == -1)
0N/A end = datalen;
0N/A return new SharedFileInputStream(sf,
678N/A this.start + start, end - start, bufsize);
0N/A }
0N/A
0N/A // for testing...
0N/A /*
0N/A public static void main(String[] argv) throws Exception {
0N/A SharedFileInputStream is = new SharedFileInputStream(argv[0]);
0N/A java.util.Random r = new java.util.Random();
0N/A int b;
0N/A while ((b = is.read()) >= 0) {
0N/A System.out.write(b);
0N/A if (r.nextDouble() < 0.3) {
0N/A InputStream is2 = is.newStream(is.getPosition(), -1);
0N/A int b2;
0N/A while ((b2 = is2.read()) >= 0)
0N/A ;
0N/A }
0N/A }
0N/A }
0N/A */
0N/A
0N/A /**
0N/A * Force this stream to close.
0N/A */
0N/A protected void finalize() throws Throwable {
0N/A super.finalize();
0N/A close();
0N/A }
0N/A}