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

org.apache.oodt.commons.io.Base64DecodingInputStream Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
// Licensed to the Apache Software Foundation (ASF) under one or more contributor
// license agreements.  See the NOTICE.txt 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.oodt.commons.io;

import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
import org.apache.oodt.commons.util.Base64;

/** An input stream that decodes its data from the RFC-1512 base 64 format.
 *
 * Wrap this input stream around another input stream, and all the bytes will be converted
 * from their base-64 format when you read from it.
 *
 * @author Kelly
 */
public class Base64DecodingInputStream extends FilterInputStream {
	/** Construct a base-64 decoding input stream.
	 *
	 * @param inputStream The input stream to decode.
	 */
	public Base64DecodingInputStream(InputStream inputStream) {
		super(inputStream);
	}

	/** Read the next byte.
	 *
	 * Decode more base-64 data and return the next decoded byte.
	 *
	 * @return The byte, or -1 on end of stream.
	 * @throws IOException If an I/O error occurs.
	 */
	public int read() throws IOException {
		if (in == null) {
		  throw new IOException("Can't read from a closed stream");
		}

		// If we've used up the decoded data buffer, read 4 more bytes and decode 'em.
		if (buffer == null || index == buffer.length) {
			byte[] streamBuf = new byte[4];
			int toRead = 4;
			int atIndex = 0;
			int actuallyGot;
			boolean firstRead = true;
			while (toRead > 0) {
				actuallyGot = in.read(streamBuf, atIndex, toRead);
				if (actuallyGot == -1) {
					if (firstRead) {
					  return -1;
					} else {
					  break;
					}
				}
				firstRead = false;
				atIndex += actuallyGot;
				toRead -= actuallyGot;
			}
			buffer = Base64.decode(streamBuf);
			if (buffer.length == 0) {
				buffer = null;
				return -1;
			}
			index = 0;
		}
		return buffer[index++] & 0xff;
	}

	/** Read a bunch of bytes.
	 *
	 * This decodes base-64 data from the underlying stream and puts the result into
	 * the given array.
	 *
	 * @param b The buffer to fill with decoded base-64 data.
	 * @param offset Where in the buffer to start filling.
	 * @param length How many bytes to fill.
	 * @return The actual number of decoded bytes.
	 * @throws IOException If an I/O error occurs.
	 */
	public int read(byte[] b, int offset, int length) throws IOException {
		if (b == null) {
		  throw new IllegalArgumentException("Can't read data into a null array");
		}
		if (offset < 0 || offset >= b.length) {
		  throw new IndexOutOfBoundsException("Can't read data into an array with indexes 0.." + (b.length - 1)
											  + " at index " + offset);
		}
		if (length < 0) {
		  throw new IllegalArgumentException("Can't read a negative amount of data");
		}
		if (offset + length > b.length) {
		  throw new IndexOutOfBoundsException("Can't read data past the right edge of an array");
		}
		if (in == null) {
		  throw new IOException("Can't read from a closed stream");
		}

		int c = read();
		if (c == -1) {
		  return -1;
		}
		b[offset] = (byte) c;
		int i = 1;
		try {
			for (; i < length; ++i) {
				c = read();
				if (c == -1) {
				  break;
				}
				b[offset + i] = (byte) c;
			}
		} catch (IOException ignore) {}
		return i;
	}

	/** Skip bytes.
	 *
	 * This method skips and discards n decoded bytes on the input stream.
	 *
	 * @param n Number of bytes to skip.
	 * @return Actual number of bytes skipped.
	 * @throws IOException If an I/O error occurs.
	 */
	public long skip(long n) throws IOException {
		if (in == null) {
		  throw new IOException("Can't skip past data on a closed stream");
		}
		int actuallySkipped = 0;
		while (n > 0) {
			if (read() == -1) {
			  return actuallySkipped;
			}
			--n;
			++actuallySkipped;
		}
		return actuallySkipped;
	}

	/** Return bytes available for reading or skipping without blocking.
	 *
	 * @return The number of bytes that can be read from this stream or skipped over
	 * on the stream without blocking.
	 * @throws IOException If an I/O error occurs.
	 */
	public int available() throws IOException {
		if (in == null) {
		  throw new IOException("Can't see how many bytes are available on a closed stream");
		}
		if (buffer != null && index < buffer.length) {
		  return buffer.length - index;
		}
		return in.available() >= 4? 1 : 0;
	}

	/** Close this stream.
	 *
	 * @throws IOException If an I/O error occurs.
	 */
	public void close() throws IOException {
		if (in == null) {
		  throw new IOException("Can't close a closed stream");
		}
		in.close();
		in = null;
		buffer = null;
	}

	/** Buffer for decoded data.
	 */
	private byte[] buffer;

	/** Where we'll next read out of the buffer.
	 *
	 * Since we always read 4 bytes at a time (a base-64 block), we can decode that
	 * into as many as 3 bytes, so start out the index in an invalid location.
	 */
	private int index = 3;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy