/* * eXist Open Source Native XML Database * Copyright (C) 2001-2015 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.exist.util.io; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Vector; /** * * @author zwobit */ public abstract class AbstractFilterInputStreamCache extends FilterInputStream implements FilterInputStreamCache { protected Vector<InputStream> consumers = new Vector<InputStream>(); private int srcOffset = 0; private final InputStream src; private boolean srcClosed = false; public AbstractFilterInputStreamCache(InputStream src) { super(src); this.src = src; //register src this.register(src); //if src is CachingFilterInputStream also register there so it can keep track of stream which rely on cache if(src instanceof CachingFilterInputStream) { ((CachingFilterInputStream) src).register(src); } } public int getSrcOffset() { return this.srcOffset; } public boolean isSrcClosed() { return srcClosed; } @Override public int available() throws IOException { if (this.srcClosed) { return 0; } return src.available() + getLength(); } @Override /** * Closes the src InputStream and empties the cache */ public void close() throws IOException { if(consumers.contains(src)) { deregister(src); if(consumers.size() <= 0) { if (!srcClosed) { try { if(src instanceof CachingFilterInputStream) { ((CachingFilterInputStream) src).deregister(this); } else { src.close(); } } finally { srcClosed = true; } } this.invalidate(); //empty the cache } } } @Override public int read() throws IOException { if (srcClosed) { throw new IOException(FilterInputStreamCache.INPUTSTREAM_CLOSED); } final int data = src.read(); if( data == FilterInputStreamCache.END_OF_STREAM) { return FilterInputStreamCache.END_OF_STREAM; } this.write(data); this.srcOffset++; return data; } @Override public int read(final byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { if (srcClosed) { throw new IOException(FilterInputStreamCache.INPUTSTREAM_CLOSED); } int srcLen = src.read(b, off, len); if (srcLen == FilterInputStreamCache.END_OF_STREAM) { return FilterInputStreamCache.END_OF_STREAM; } this.write(b, off, srcLen); this.srcOffset += srcLen; return srcLen; } @Override public long skip(long n) throws IOException { if (srcClosed) { throw new IOException(FilterInputStreamCache.INPUTSTREAM_CLOSED); } else if (n < 1) { return 0; } if (srcOffset < n) { final byte skipped[] = new byte[(int) (n - srcOffset)]; int srcLen = src.read(skipped); //have we reached the end of the stream? if (srcLen == FilterInputStreamCache.END_OF_STREAM) { return srcOffset; } //increase srcOffset due to the read operation above srcOffset += srcLen; //store data in cache this.write(skipped, 0, srcLen); return srcOffset; } else { final byte skipped[] = new byte[(int) n]; //TODO could overflow int actualLen = src.read(skipped); //increase srcOffset due to read operation above srcOffset += actualLen; //store data in the cache this.write(skipped, 0, actualLen); return actualLen; } } @Override public boolean markSupported() { return false; } @Override public void mark(int readlimit) { } @Override public void reset() throws IOException { throw new IOException("reset() not supported."); } @Override public boolean srcIsFilterInputStreamCache() { return src instanceof CachingFilterInputStream; } @Override public void register(InputStream inputStream) { consumers.add(inputStream); } @Override public void deregister(InputStream inputStream) { consumers.remove(inputStream); } }