All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gc.iotools.stream.is.ChunkInputStream Maven / Gradle / Ivy

Go to download

EasyStream is a small set of utilities for dealing with streams (InputStreams and OutputStreams). The aim is to ease the use of pipes when they're required. Main features are: * "Convert" an OutputStream to an InputStream. * Count the number of bytes read or wrote to a given stream. * While reading the data from an InputStream copy it to a supplied OutputStream. * Read the content of an InputStream multiple times or seek to a definite position

The newest version!
package com.gc.iotools.stream.is;

/*
 * Copyright (c) 2008, 2015 Gabriele Contini. This source code is released
 * under the BSD License.
 */
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

import com.gc.iotools.stream.base.EasyStreamConstants;
import com.gc.iotools.stream.utils.ArrayTools;
import com.gc.iotools.stream.utils.StreamUtils;

/**
 * 

* This class is useful when you have an InputStream and you want * to filter some parts of it basing on its content without reading it into * memory. *

*

* Basically it strips the initial bytes of the stream until a sequence of * bytes equal to startMarker is found. When a sequence of bytes * equals to endMarker is found the stream hide the bytes until a * new startMarker is found. *

* The result is that only the bytes between startMarker and * endMarker are shown to the outer stream through the * read() methods. *

* Example: *

* *
 * InputStream is = new ByteArrayInputStream("aa start bbb stopfff".getBytes());
 * ChunckInputStream chunkIs = new ChunkInputStream("rt".getBytes(),
 * 		"stop".getBytes(), is);
 * byte[] bytes = IOUtils.toByteArray(chunkIs);
 * //here bytes contains " bbb "
 * 
*

* The class has three operational modes. They can be selected invoking the * constructor with four parameters. These two modes affect how this class * handles multiple chunks in a file. *

*
    *
  • automaticFetch=true (default). After an * endMarker is found the stream automatically moves to the next * startMarker if found. Usage pattern is shown in the example * above.
  • *
  • automaticFetch=false Each chunk need to be fetched * invoking explicitly a {@link #fetchNextChunk()} methods. The * stream is initially in an EOF state and * {@link #fetchNextChunk()} must be called at first. At this * point all the bytes of the stream are shown until an endMarker * is found. At this point the ChunkInputStream is in an EOF state until a * {@link #fetchNextChunk()} is invoked.
  • *
  • automaticFetch=false and startMarker=null It * is similar to the previous case. It can be used to the src stream on the * endMarkers
  • *
*

* Example of automaticFetch=false mode: *

* *
 * InputStream is = new ByteArrayInputStream("aa start bbb stopfff".getBytes());
 * ChunckInputStream chunkIs = new ChunkInputStream(is, "rt"
 * 		.getBytes(), "stop".getBytes(), false, false);
 * while (chunkIs.moveToNextChunk()) {
 * 	byte[] bytes = IOUtils.toByteArray(chunkIs);
 * 	//here bytes contains " bbb "
 * }
 * 
* * @author dvd.smnt * @since 1.0.8 * @version $Id: ChunkInputStream.java 576 2015-03-28 00:03:33Z gcontini $ */ public final class ChunkInputStream extends InputStream { private final boolean automaticFetch; private boolean copyToOuter = false; private final boolean showMarkers; private final byte[] start; private final byte[] stop; private final InputStream wrappedIs; /** * Constructs a ChunkInputStream. * * @param src * Source InputStream. Must not be null. * @param startMarker * When this sequence of bytes is found in the src * stream the bytes of the inner stream are shown until an * endMarker is found. If this parameter is set to * null the stream is initially in an EOF state * until a fetchNextChunk() is performed. * @param stopMarker * when this sequence of bytes is found in the src * stream the bytes of the inner stream are hidden until a * startMarker is found. If this parameter is set * to null the stream is made available until the * inner stream reaches an EOF. */ public ChunkInputStream(final InputStream src, final byte[] startMarker, final byte[] stopMarker) { this(src, startMarker, stopMarker, false, ((startMarker != null) && (startMarker.length > 0))); } /** * Gets an instance of the ChunkInputStream. If * startMarker!=null the operating mode is set to * automaticFetch=true * * @param src * Source stream. Must not be null. * @param startMarker * When this sequence of bytes is found in the src * stream the bytes of the inner stream are shown until an * endMarker is found. If this parameter is set to * null the stream is initially in an EOF state * until a fetchNextChunk() is performed. * @param showMarkers * if set to true start and end markers are shown * in the outer stream. * @param automaticFetch * enables automatic fetching of startMarkers. If * false startMarkers must be fetched * manually invoking {@link #fetchNextChunk()} * @param stopMarker * when this sequence of bytes is found in the src * stream the bytes of the inner stream are hidden until a * startMarker is found. If this parameter is set * to null the stream is made available until the * inner stream reaches an EOF. * @see fetchNextChunk() */ public ChunkInputStream(final InputStream src, final byte[] startMarker, final byte[] stopMarker, final boolean showMarkers, final boolean automaticFetch) { if (src == null) { throw new IllegalArgumentException( "Wrapped InputStrem can't be null"); } this.start = (startMarker == null ? new byte[0] : startMarker); this.stop = (stopMarker == null ? new byte[0] : stopMarker); this.wrappedIs = new BufferedInputStream(src); this.automaticFetch = automaticFetch; if ((this.start.length == 0) && automaticFetch) { throw new IllegalArgumentException( "It's not possible to specify " + "a startMarker" + (startMarker == null ? "=null" : ".size=0") + " and" + " automaticFetch=[" + automaticFetch + "]"); } this.showMarkers = showMarkers; } /** * {@inheritDoc}. */ @Override public int available() throws IOException { findStartMarker(); return this.wrappedIs.available(); } /** * {@inheritDoc}. */ @Override public void close() throws IOException { this.wrappedIs.close(); } /** * This method must be called if automaticFetch=false before * the stream can be used and each time an endMarker has been found to * proceed to next startMarker. * * @return true if another chunk is available, * false otherwise. * @throws java.io.IOException * exception thrown if it is impossible to read from the inner * stream for some unknown reason. */ public boolean fetchNextChunk() throws IOException { if (this.automaticFetch) { throw new IllegalStateException( "this method shouldn't be called when automaticFetch [" + this.automaticFetch + "]"); } this.copyToOuter = moveToNextStartMarker(); return this.copyToOuter; } private void findStartMarker() throws IOException { if (!this.copyToOuter && this.automaticFetch) { // if no start marker set copy this.copyToOuter = moveToNextStartMarker(); } } /** * {@inheritDoc}. */ @Override public synchronized void mark(final int readlimit) { this.wrappedIs.mark(readlimit); } /** * {@inheritDoc}. */ @Override public boolean markSupported() { return this.wrappedIs.markSupported(); } private boolean moveToNextStartMarker() throws IOException { boolean found; if (this.start.length == 0) { this.wrappedIs.mark(2); // if EOF stop. found = (this.wrappedIs.read() >= 0); this.wrappedIs.reset(); } else { int n; found = false; final byte[] buffer = new byte[EasyStreamConstants.SKIP_BUFFER_SIZE + this.start.length]; do { this.wrappedIs.mark(EasyStreamConstants.SKIP_BUFFER_SIZE + this.start.length); n = StreamUtils.tryReadFully(this.wrappedIs, buffer, 0, EasyStreamConstants.SKIP_BUFFER_SIZE + this.start.length); if (n > 0) { final int pos = ArrayTools.indexOf( ArrayTools.subarray(buffer, 0, n), this.start); if (pos >= 0) { // found found = true; this.wrappedIs.reset(); final int skip = pos + (this.showMarkers ? 0 : this.start.length); this.wrappedIs.skip(skip); } else { // not found if (n - this.start.length > 0) { this.wrappedIs.reset(); this.wrappedIs.skip(n - this.start.length); } } } } while (!found && (n >= 0)); } return found; } /** * {@inheritDoc}. */ @Override public int read() throws IOException { final byte[] buf = new byte[1]; final int rd = read(buf); int result = buf[0]; if (rd < 0) { result = rd; } return result; } /** * {@inheritDoc}. */ @Override public int read(final byte[] b) throws IOException { return this.read(b, 0, b.length); } /** * {@inheritDoc}. */ @Override public int read(final byte[] b, final int off, final int len) throws IOException { if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException("b.length[" + b.length + "] offset[" + off + "] length[" + len + ""); } else if (len == 0) { return 0; } findStartMarker(); int ret; if (this.copyToOuter) { if (this.stop.length > 0) { final int readSize = len - off + this.stop.length; final byte[] tmpBuffer = new byte[readSize]; this.wrappedIs.mark(readSize); ret = StreamUtils.tryReadFully(this.wrappedIs, tmpBuffer, 0, readSize); this.wrappedIs.reset(); if (ret != -1) { final int position = ArrayTools.indexOf(tmpBuffer, this.stop); if (position == -1) { // stop marker not found ret = Math.min(ret, len - off); this.wrappedIs.skip(ret); System.arraycopy(tmpBuffer, 0, b, off, ret); } else if (position == 0) { this.wrappedIs.skip(this.stop.length); this.copyToOuter = false; ret = this.read(b, off, len); } else { // position >0 ret = position; final int bytesToSkip = position + this.stop.length; this.wrappedIs.skip(bytesToSkip); System.arraycopy(tmpBuffer, 0, b, off, position); this.copyToOuter = false; } } } else { ret = this.wrappedIs.read(b, off, len); } } else { ret = -1; } return ret; } /** * {@inheritDoc}. */ @Override public synchronized void reset() throws IOException { this.wrappedIs.reset(); } /** * {@inheritDoc}. */ @Override public long skip(final long n) throws IOException { findStartMarker(); return super.skip(n); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy