001/** 002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC 003 * and GZip files. (http://jwat.org/) 004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/) 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.jwat.common; 019 020import java.io.FilterInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023 024/** 025 * <code>InputStream</code> with a fixed amount of bytes available to read. 026 * When the stream is closed the remaining bytes that have not been read are 027 * read or skipped. 028 * 029 * @author lbihanic, selghissassi, nicl 030 */ 031public final class FixedLengthInputStream extends FilterInputStream { 032 033 /** Remaining bytes available. */ 034 protected long remaining; 035 036 /** 037 * Create a new input stream with a fixed number of bytes available from 038 * the underlying stream. 039 * @param in the input stream to wrap 040 * @param length fixed number of bytes available through this stream 041 */ 042 public FixedLengthInputStream(InputStream in, long length) { 043 super(in); 044 this.remaining = length; 045 } 046 047 /** 048 * Closing will only skip to the end of this fixed length input stream and 049 * not call the parent's close method. 050 * @throws IOException if an I/O error occurs while closing stream 051 */ 052 @Override 053 public void close() throws IOException { 054 long skippedLast = 0; 055 if (remaining > 0) { 056 skippedLast = skip(remaining); 057 while (remaining > 0 && skippedLast > 0) { 058 skippedLast = skip(remaining); 059 } 060 } 061 } 062 063 @Override 064 public int available() throws IOException { 065 return (remaining > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) (remaining); 066 } 067 068 @Override 069 public boolean markSupported() { 070 return false; 071 } 072 073 @Override 074 public synchronized void mark(int readlimit) { 075 } 076 077 @Override 078 public synchronized void reset() throws IOException { 079 throw new UnsupportedOperationException(); 080 } 081 082 @Override 083 public int read() throws IOException { 084 int b = -1; 085 if (remaining > 0) { 086 b = in.read(); 087 if (b != -1) { 088 --remaining; 089 } 090 } 091 return b; 092 } 093 094 @Override 095 public int read(byte[] b) throws IOException { 096 return read(b, 0, b.length); 097 } 098 099 @Override 100 public int read(byte[] b, int off, int len) throws IOException { 101 int bytesRead = -1; 102 if (remaining > 0) { 103 bytesRead = in.read(b, off, (int) Math.min(len, remaining)); 104 if (bytesRead > 0){ 105 remaining -= bytesRead; 106 } 107 } 108 return bytesRead; 109 } 110 111 @Override 112 public long skip(long n) throws IOException { 113 long bytesSkipped = 0; 114 if (remaining > 0) { 115 bytesSkipped = in.skip(Math.min(n, remaining)); 116 remaining -= bytesSkipped; 117 } 118 return bytesSkipped; 119 } 120 121}