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 static org.apache.commons.codec.binary.BaseNCodec.EOF;
021
022 import java.io.FilterOutputStream;
023 import java.io.IOException;
024 import java.io.OutputStream;
025
026 import org.apache.commons.codec.binary.BaseNCodec.Context;
027
028 /**
029 * Abstract superclass for Base-N output streams.
030 *
031 * @since 1.5
032 * @version $Id: BaseNCodecOutputStream.html 889935 2013-12-11 05:05:13Z ggregory $
033 */
034 public class BaseNCodecOutputStream extends FilterOutputStream {
035
036 private final boolean doEncode;
037
038 private final BaseNCodec baseNCodec;
039
040 private final byte[] singleByte = new byte[1];
041
042 private final Context context = new Context();
043
044 // TODO should this be protected?
045 public BaseNCodecOutputStream(OutputStream out, BaseNCodec basedCodec, boolean doEncode) {
046 super(out);
047 this.baseNCodec = basedCodec;
048 this.doEncode = doEncode;
049 }
050
051 /**
052 * Writes the specified <code>byte</code> to this output stream.
053 *
054 * @param i
055 * source byte
056 * @throws IOException
057 * if an I/O error occurs.
058 */
059 @Override
060 public void write(int i) throws IOException {
061 singleByte[0] = (byte) i;
062 write(singleByte, 0, 1);
063 }
064
065 /**
066 * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this
067 * output stream.
068 *
069 * @param b
070 * source byte array
071 * @param offset
072 * where to start reading the bytes
073 * @param len
074 * maximum number of bytes to write
075 *
076 * @throws IOException
077 * if an I/O error occurs.
078 * @throws NullPointerException
079 * if the byte array parameter is null
080 * @throws IndexOutOfBoundsException
081 * if offset, len or buffer size are invalid
082 */
083 @Override
084 public void write(byte b[], int offset, int len) throws IOException {
085 if (b == null) {
086 throw new NullPointerException();
087 } else if (offset < 0 || len < 0) {
088 throw new IndexOutOfBoundsException();
089 } else if (offset > b.length || offset + len > b.length) {
090 throw new IndexOutOfBoundsException();
091 } else if (len > 0) {
092 if (doEncode) {
093 baseNCodec.encode(b, offset, len, context);
094 } else {
095 baseNCodec.decode(b, offset, len, context);
096 }
097 flush(false);
098 }
099 }
100
101 /**
102 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propogate is
103 * true, the wrapped stream will also be flushed.
104 *
105 * @param propogate
106 * boolean flag to indicate whether the wrapped OutputStream should also be flushed.
107 * @throws IOException
108 * if an I/O error occurs.
109 */
110 private void flush(boolean propogate) throws IOException {
111 int avail = baseNCodec.available(context);
112 if (avail > 0) {
113 byte[] buf = new byte[avail];
114 int c = baseNCodec.readResults(buf, 0, avail, context);
115 if (c > 0) {
116 out.write(buf, 0, c);
117 }
118 }
119 if (propogate) {
120 out.flush();
121 }
122 }
123
124 /**
125 * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
126 *
127 * @throws IOException
128 * if an I/O error occurs.
129 */
130 @Override
131 public void flush() throws IOException {
132 flush(true);
133 }
134
135 /**
136 * Closes this output stream and releases any system resources associated with the stream.
137 *
138 * @throws IOException
139 * if an I/O error occurs.
140 */
141 @Override
142 public void close() throws IOException {
143 // Notify encoder of EOF (-1).
144 if (doEncode) {
145 baseNCodec.encode(singleByte, 0, EOF, context);
146 } else {
147 baseNCodec.decode(singleByte, 0, EOF, context);
148 }
149 flush();
150 out.close();
151 }
152
153 }