/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.engine.io; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import org.restlet.data.Range; import org.restlet.representation.Representation; // [excludes gwt] /** * Filters an input stream to expose only a given range. * * @author Jerome Louvel */ public class RangeInputStream extends FilterInputStream { /** The current position. */ private volatile long position; /** The range to satisfy. */ private volatile Range range; /** The total size of the source stream. */ private volatile long totalSize; /** The start index inside the source stream. */ private final long startIndex; /** The end index inside the source stream. */ private final long endIndex; /** The range size available. */ private volatile int availableSize; /** * Constructs a stream exposing only a range of a given source stream. * * @param in * The source input stream. * @param totalSize * The total size of the source stream. * @param range * The range to satisfy. */ public RangeInputStream(InputStream in, long totalSize, Range range) { super(in); this.range = range; this.position = 0; this.totalSize = totalSize; this.availableSize = (int) range.getSize(); if (totalSize == Representation.UNKNOWN_SIZE) { if (range.getIndex() == Range.INDEX_LAST) { if (range.getSize() == Range.SIZE_MAX) { // Read the whole stream this.startIndex = -1; this.endIndex = -1; } else { throw new IllegalArgumentException( "Can't determine the start and end index."); } } else { if (range.getSize() == Range.SIZE_MAX) { this.startIndex = range.getIndex(); this.endIndex = -1; } else { this.startIndex = range.getIndex(); this.endIndex = range.getIndex() + range.getSize() - 1; } } } else { if (range.getIndex() == Range.INDEX_LAST) { if (range.getSize() == Range.SIZE_MAX) { this.startIndex = -1; this.endIndex = -1; } else { this.startIndex = totalSize - range.getSize(); this.endIndex = -1; } } else { if (range.getSize() == Range.SIZE_MAX) { this.startIndex = range.getIndex(); this.endIndex = -1; } else { this.startIndex = range.getIndex(); this.endIndex = range.getIndex() + range.getSize() - 1; } } } } @Override public int available() throws IOException { return this.availableSize; } @Override public synchronized void mark(int readlimit) { if (range.getIndex() == Range.INDEX_LAST) { super.mark(readlimit + (int) (totalSize - range.getSize())); } else { super.mark(readlimit + (int) range.getIndex()); } } @Override public int read() throws IOException { int result = super.read(); while ((result != -1) && !this.range.isIncluded(position++, totalSize)) { result = super.read(); } if ((result != -1) && (this.availableSize > 0)) { this.availableSize--; } return result; } @Override public int read(byte[] b, int off, int len) throws IOException { // Reach the start index. while (!(position >= startIndex)) { long skipped = skip(startIndex - position); if (skipped <= 0) { throw new IOException("Cannot skip ahead in FilterInputStream"); } position += skipped; } int result = -1; if (endIndex != -1) { // Read up until the end index if (position > endIndex) { // The end index is reached. result = -1; } else { // Take care to read the right number of bytes according to the // end index and the buffer size. result = super.read(b, off, ((position + len) > endIndex) ? (int) (endIndex - position + 1) : len); } } else { // Read normally up until the end of the stream. result = super.read(b, off, len); } if (result > 0) { // Move the cursor. position += result; } if ((result != -1) && (this.availableSize > 0)) { this.availableSize -= result; } return result; } }