blob: 98835f72f103f39d9aecf3ef41f6a10c70b517bd [file] [log] [blame]
/*
* 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.harmony.luni.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* The class contains static {@link java.io.InputStream} utilities.
*/
public class InputStreamHelper {
/**
* Provides access to a protected underlying buffer of
* <code>ByteArrayInputStream</code>.
*/
private static final Field BAIS_BUF;
/**
* Provides access to a protected position in the underlying buffer of
* <code>ByteArrayInputStream</code>.
*/
private static final Field BAIS_POS;
static {
final Field[] f = new Field[2];
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
f[0] = ByteArrayInputStream.class.getDeclaredField("buf"); //$NON-NLS-1$
f[0].setAccessible(true);
f[1] = ByteArrayInputStream.class.getDeclaredField("pos"); //$NON-NLS-1$
f[1].setAccessible(true);
} catch (NoSuchFieldException nsfe) {
throw new InternalError(nsfe.getLocalizedMessage());
}
return null;
}
});
BAIS_BUF = f[0];
BAIS_POS = f[1];
}
/**
* The extension of <code>ByteArrayInputStream</code> which exposes an
* underlying buffer.
*/
static class ExposedByteArrayInputStream extends ByteArrayInputStream {
/**
* @see java.io.ByteArrayInputStream(byte[])
*/
public ExposedByteArrayInputStream(byte buf[]) {
super(buf);
}
/**
* @see java.io.ByteArrayInputStream(byte[], int, int)
*/
public ExposedByteArrayInputStream(byte buf[], int offset, int length) {
super(buf, offset, length);
}
/**
* Reads the whole stream and returns the stream snapshot.
*/
public synchronized byte[] expose() {
if (pos == 0 && count == buf.length) {
skip(count);
return buf;
}
final int available = available();
final byte[] buffer = new byte[available];
System.arraycopy(buf, pos, buffer, 0, available);
skip(available);
return buffer;
}
}
/**
* Reads all bytes from {@link java.io.ByteArrayInputStream} using its
* underlying buffer directly.
*
* @return an underlying buffer, if a current position is at the buffer
* beginning, and an end position is at the buffer end, or a copy of
* the underlying buffer part.
*/
private static byte[] expose(ByteArrayInputStream bais) {
byte[] buffer, buf;
int pos;
synchronized (bais) {
int available = bais.available();
try {
buf = (byte[]) BAIS_BUF.get(bais);
pos = BAIS_POS.getInt(bais);
} catch (IllegalAccessException iae) {
throw new InternalError(iae.getLocalizedMessage());
}
if (pos == 0 && available == buf.length) {
buffer = buf;
} else {
buffer = new byte[available];
System.arraycopy(buf, pos, buffer, 0, available);
}
bais.skip(available);
}
return buffer;
}
/**
* The utility method for reading the whole input stream into a snapshot
* buffer. To speed up the access it works with an underlying buffer for a
* given {@link java.io.ByteArrayInputStream}.
*
* @param is
* the stream to be read.
* @return the snapshot wrapping the buffer where the bytes are read to.
* @throws UnsupportedOperationException
* if the input stream data cannot be exposed
*/
public static byte[] expose(InputStream is) throws IOException,
UnsupportedOperationException {
if (is instanceof ExposedByteArrayInputStream) {
return ((ExposedByteArrayInputStream) is).expose();
}
if (is.getClass().equals(ByteArrayInputStream.class)) {
return expose((ByteArrayInputStream) is);
}
// We don't know how to do this
throw new UnsupportedOperationException();
}
/**
* Reads all the bytes from the given input stream.
*
* Calls read multiple times on the given input stream until it receives an
* end of file marker. Returns the combined results as a byte array. Note
* that this method may block if the underlying stream read blocks.
*
* @param is
* the input stream to be read.
* @return the content of the stream as a byte array.
* @throws IOException
* if a read error occurs.
*/
public static byte[] readFullyAndClose(InputStream is) throws IOException {
try {
// Initial read
byte[] buffer = new byte[1024];
int count = is.read(buffer);
int nextByte = is.read();
// Did we get it all in one read?
if (nextByte == -1) {
byte[] dest = new byte[count];
System.arraycopy(buffer, 0, dest, 0, count);
return dest;
}
// Requires additional reads
ByteArrayOutputStream baos = new ByteArrayOutputStream(count * 2);
baos.write(buffer, 0, count);
baos.write(nextByte);
while (true) {
count = is.read(buffer);
if (count == -1) {
return baos.toByteArray();
}
baos.write(buffer, 0, count);
}
} finally {
is.close();
}
}
}