org.apache.axiom.attachments.BoundaryPushbackInputStream Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axiom.attachments;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import org.apache.axiom.attachments.utils.ByteSearch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* An InputStream that reads bytes up to a boundary.
* The boundary is not logically part of the bytes to read.
* The wrapped PushbackInputStream is set to to the byte after
* the boundary once the bytes are read.
* The boundary is not logically returned.
*
* There are two forms that are supported, where . is a byte
*
* .......................boundary
*
* and
*
* ..................../r/nboundary
*
* In both cases, only the bytes (.) are returned.
*
*/
public class BoundaryPushbackInputStream extends InputStream {
private static Log log = LogFactory.getLog(BoundaryPushbackInputStream.class);
private static boolean isDebugEnabled = log.isDebugEnabled();
PushbackInputStream is;
boolean boundaryFound;
byte[] boundary;
int rnBoundaryLen; // '/r/nboundary' length
byte[] buffer;
int bufferSize; // BufferSize
int numBytes; // Number of bytes in the buffer
int index = -1; // Current index in buffer
int bIndex = -1; // Index of boundary or /r/nboundary
final int MIN_BUF_SIZE = 32;
protected static final int BOUNDARY_NT_FOUND = -1;
// Skip search array
private short[] skip = null;
// Working byte for read()
private byte[] read_byte = new byte[1];
/**
* @param inStream
* @param boundary
* @param pushBackSize
*/
public BoundaryPushbackInputStream(PushbackInputStream inStream, byte[] boundary, int pushBackSize) {
super();
this.is = inStream;
this.boundary = boundary;
this.rnBoundaryLen = boundary.length + 2;
// The buffer must accomodate twice the boundary length and
// the maximum that we will ever push back (which is the entire buffer except for
// the boundary)
this.bufferSize = Math.max(rnBoundaryLen * 2, pushBackSize + boundary.length);
}
/**
* Method readFromStream
*
* @param b
* @param start
* @param length
*
* @return
*
* @throws java.io.IOException
*/
private final int readFromStream(
final byte[] b, final int start, final int length)
throws java.io.IOException {
// We need to make sure to capture enough data to
// actually search for the rn + boundary
int minRead = Math.max(rnBoundaryLen * 2, length);
minRead = Math.min(minRead, length - start);
int br = 0;
int brTotal = 0;
do {
// Read data into the buffer
br = is.read(b, brTotal + start, length - brTotal);
if (br > 0) {
brTotal += br;
}
} while ((br > 0) && (brTotal < minRead));
return (brTotal != 0)
? brTotal
: br;
}
/**
* @param b
* @return
* @throws java.io.IOException
*/
private final int readFromStream(final byte[] b)
throws java.io.IOException {
return readFromStream(b, 0, b.length);
}
/* (non-Javadoc)
* @see java.io.InputStream#read(byte[])
*/
public int read(byte[] b) throws java.io.IOException {
return read(b, 0, b.length);
}
/**
* Read from the boundary delimited stream.
* Generally, this won't be called...callers will
* most likely call the read(byte[]..) methods
* @return The byte read, or -1 if endof stream.
*
* @throws java.io.IOException
*/
public int read() throws java.io.IOException {
// Short cut to avoid buffer copying
if (buffer != null && index > 0) {
if ((bIndex > 0 && (index+1) < bIndex) ||
bIndex < 0 && index < (numBytes - rnBoundaryLen)) {
index++;
return (buffer[index-1]);
}
}
int read = read(read_byte);
if (read < 0) {
return -1;
} else {
return read_byte[0];
}
}
/**
* Read from the boundary delimited stream.
* @param b is the array to read into.
* @param off is the offset
* @param len
* @return the number of bytes read. -1 if endof stream.
*
* @throws java.io.IOException
*/
public int read(byte[] b, final int off, final int len)
throws java.io.IOException {
// If already found the buffer, then we are done
if (boundaryFound) {
return -1;
}
// The first time called, read a chunk of data
if (buffer == null) { // Allocate the buffer.
buffer = new byte[bufferSize];
numBytes = readFromStream(buffer);
if (numBytes < 0) {
buffer = null;
boundaryFound = true;
}
index = 0;
// Finds the boundary pos.
bIndex = boundaryPosition(buffer, index, numBytes);
if (bIndex >=0) {
unread(); // Unread pushback inputstream
}
}
int bwritten = 0; // Number of bytes written to b
do {
// Never read to the end of the buffer because
// the boundary may span buffers.
int bcopy = Math.min((numBytes - rnBoundaryLen) - index,
len - bwritten);
// Never read past the boundary
if (bIndex >= 0) {
bcopy = Math.min(bcopy, bIndex - index);
}
// Copy the bytes
if (bcopy > 0) {
System.arraycopy(buffer, index, b, off + bwritten, bcopy);
bwritten += bcopy;
index += bcopy;
}
if (index == bIndex) {
boundaryFound = true;
} else if (bwritten < len) {
// If more data is needed,
// create a temporary buffer to span
// the straggling bytes in the current buffer
// and the new yet unread bytes
byte[] dstbuf = buffer;
// Move straggling bytes from the current buffer
int movecnt = numBytes - index;
System.arraycopy(buffer, index, dstbuf, 0, movecnt);
// Read in the new data.
int readcnt = readFromStream(dstbuf, movecnt,
dstbuf.length - movecnt);
if (readcnt < 0) {
if (log.isDebugEnabled()) {
log.debug("End of Stream, but boundary not found");
log.debug(toString());
}
buffer = null;
boundaryFound = true;
throw new java.io.IOException("End of Stream, but boundary not found");
}
numBytes = readcnt + movecnt;
buffer = dstbuf;
index = 0; // start at the begining.
// just move the boundary by what we moved
if (bIndex >=0) {
bIndex -= movecnt;
} else {
bIndex = boundaryPosition(
buffer, index,
numBytes);
if (bIndex >= 0) {
unread(); // Unread pushback inputstream
}
}
}
}
// read till we get the amount or the stream is finished.
while (!boundaryFound && (bwritten < len));
if (boundaryFound) {
buffer = null; // GC the buffer
}
return bwritten;
}
/**
* Unread the bytes past the buffer
*/
private void unread() throws IOException {
int i = bIndex;
if (buffer[i] == 13) { // If /r, must be /r/nboundary
i = i + this.rnBoundaryLen;
} else {
i = i + boundary.length;
}
if (numBytes - i > 0) {
is.unread(buffer, i, numBytes - i);
}
}
/**
* Read from the boundary delimited stream.
*
* @param searchbuf
* @param start
* @param end
* @return The position of the boundary.
*
*/
protected int boundaryPosition(byte[] searchbuf, int start, int end) throws java.io.IOException {
if (skip == null) {
skip = ByteSearch.getSkipArray(boundary, true);
}
int foundAt = ByteSearch.skipSearch(boundary, true,searchbuf, start, end, skip);
// Backup 2 if the boundary is preceeded by /r/n
// The /r/n are treated as part of the boundary
if (foundAt >=2) {
if (searchbuf[foundAt-2] == 13 &&
searchbuf[foundAt-1] == 10) {
foundAt = foundAt -2;
}
}
return foundAt;
}
public boolean getBoundaryStatus()
{
return boundaryFound;
}
/**
* toString
* dumps state information. Effective for debug trace.
*/
public String toString() {
final String newline = "\n";
StringBuffer sb = new StringBuffer();
sb.append("========================");
sb.append(newline);
sb.append("BoundaryPushbackInputStream");
sb.append(newline);
sb.append(" boundary = " + new String(this.boundary) );
sb.append(newline);
sb.append(" boundaryFound = " + boundaryFound);
sb.append(newline);
int available = 0;
try {
available = is.available();
} catch (Throwable t) {
; // Suppress...toString is called for debug
}
sb.append(" is available = " + available );
sb.append(newline);
sb.append(" bufferSize = " + bufferSize);
sb.append(newline);
sb.append(" bufferNumBytes = " + bufferSize);
sb.append(newline);
sb.append(" bufferIndex = " + index);
sb.append(newline);
sb.append(" boundaryIndex = " + bIndex);
sb.append(newline);
sb.append(" buffer[0,num] = " + new String(buffer, 0, numBytes));
sb.append(newline);
sb.append("========================");
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy