001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.codec.binary;
019
020 import java.io.FilterOutputStream;
021 import java.io.IOException;
022 import java.io.OutputStream;
023
024 /**
025 * Abstract superclass for Base-N output streams.
026 *
027 * @since 1.5
028 */
029 public class BaseNCodecOutputStream extends FilterOutputStream {
030
031 private final boolean doEncode;
032
033 private final BaseNCodec baseNCodec;
034
035 private final byte[] singleByte = new byte[1];
036
037 public BaseNCodecOutputStream(OutputStream out, BaseNCodec basedCodec, boolean doEncode) {
038 super(out);
039 this.baseNCodec = basedCodec;
040 this.doEncode = doEncode;
041 }
042
043 /**
044 * Writes the specified <code>byte</code> to this output stream.
045 *
046 * @param i
047 * source byte
048 * @throws IOException
049 * if an I/O error occurs.
050 */
051 public void write(int i) throws IOException {
052 singleByte[0] = (byte) i;
053 write(singleByte, 0, 1);
054 }
055
056 /**
057 * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this
058 * output stream.
059 *
060 * @param b
061 * source byte array
062 * @param offset
063 * where to start reading the bytes
064 * @param len
065 * maximum number of bytes to write
066 *
067 * @throws IOException
068 * if an I/O error occurs.
069 * @throws NullPointerException
070 * if the byte array parameter is null
071 * @throws IndexOutOfBoundsException
072 * if offset, len or buffer size are invalid
073 */
074 public void write(byte b[], int offset, int len) throws IOException {
075 if (b == null) {
076 throw new NullPointerException();
077 } else if (offset < 0 || len < 0) {
078 throw new IndexOutOfBoundsException();
079 } else if (offset > b.length || offset + len > b.length) {
080 throw new IndexOutOfBoundsException();
081 } else if (len > 0) {
082 if (doEncode) {
083 baseNCodec.encode(b, offset, len);
084 } else {
085 baseNCodec.decode(b, offset, len);
086 }
087 flush(false);
088 }
089 }
090
091 /**
092 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propogate is
093 * true, the wrapped stream will also be flushed.
094 *
095 * @param propogate
096 * boolean flag to indicate whether the wrapped OutputStream should also be flushed.
097 * @throws IOException
098 * if an I/O error occurs.
099 */
100 private void flush(boolean propogate) throws IOException {
101 int avail = baseNCodec.available();
102 if (avail > 0) {
103 byte[] buf = new byte[avail];
104 int c = baseNCodec.readResults(buf, 0, avail);
105 if (c > 0) {
106 out.write(buf, 0, c);
107 }
108 }
109 if (propogate) {
110 out.flush();
111 }
112 }
113
114 /**
115 * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
116 *
117 * @throws IOException
118 * if an I/O error occurs.
119 */
120 public void flush() throws IOException {
121 flush(true);
122 }
123
124 /**
125 * Closes this output stream and releases any system resources associated with the stream.
126 *
127 * @throws IOException
128 * if an I/O error occurs.
129 */
130 public void close() throws IOException {
131 // Notify encoder of EOF (-1).
132 if (doEncode) {
133 baseNCodec.encode(singleByte, 0, -1);
134 } else {
135 baseNCodec.decode(singleByte, 0, -1);
136 }
137 flush();
138 out.close();
139 }
140
141 }