1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.codec.binary;
19
20 import static org.apache.commons.codec.binary.BaseNCodec.EOF;
21
22 import java.io.FilterOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25
26 import org.apache.commons.codec.binary.BaseNCodec.Context;
27
28 /**
29 * Abstract superclass for Base-N output streams.
30 * <p>
31 * To write the EOF marker without closing the stream, call {@link #eof()} or use an <a
32 * href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
33 * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
34 * >CloseShieldOutputStream</a>.
35 * </p>
36 *
37 * @since 1.5
38 * @version $Id: BaseNCodecOutputStream.java 1744727 2016-05-20 12:43:52Z sebb $
39 */
40 public class BaseNCodecOutputStream extends FilterOutputStream {
41
42 private final boolean doEncode;
43
44 private final BaseNCodec baseNCodec;
45
46 private final byte[] singleByte = new byte[1];
47
48 private final Context context = new Context();
49
50 // TODO should this be protected?
51 public BaseNCodecOutputStream(final OutputStream out, final BaseNCodec basedCodec, final boolean doEncode) {
52 super(out);
53 this.baseNCodec = basedCodec;
54 this.doEncode = doEncode;
55 }
56
57 /**
58 * Writes the specified <code>byte</code> to this output stream.
59 *
60 * @param i
61 * source byte
62 * @throws IOException
63 * if an I/O error occurs.
64 */
65 @Override
66 public void write(final int i) throws IOException {
67 singleByte[0] = (byte) i;
68 write(singleByte, 0, 1);
69 }
70
71 /**
72 * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this
73 * output stream.
74 *
75 * @param b
76 * source byte array
77 * @param offset
78 * where to start reading the bytes
79 * @param len
80 * maximum number of bytes to write
81 *
82 * @throws IOException
83 * if an I/O error occurs.
84 * @throws NullPointerException
85 * if the byte array parameter is null
86 * @throws IndexOutOfBoundsException
87 * if offset, len or buffer size are invalid
88 */
89 @Override
90 public void write(final byte b[], final int offset, final int len) throws IOException {
91 if (b == null) {
92 throw new NullPointerException();
93 } else if (offset < 0 || len < 0) {
94 throw new IndexOutOfBoundsException();
95 } else if (offset > b.length || offset + len > b.length) {
96 throw new IndexOutOfBoundsException();
97 } else if (len > 0) {
98 if (doEncode) {
99 baseNCodec.encode(b, offset, len, context);
100 } else {
101 baseNCodec.decode(b, offset, len, context);
102 }
103 flush(false);
104 }
105 }
106
107 /**
108 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propagate is
109 * true, the wrapped stream will also be flushed.
110 *
111 * @param propagate
112 * boolean flag to indicate whether the wrapped OutputStream should also be flushed.
113 * @throws IOException
114 * if an I/O error occurs.
115 */
116 private void flush(final boolean propagate) throws IOException {
117 final int avail = baseNCodec.available(context);
118 if (avail > 0) {
119 final byte[] buf = new byte[avail];
120 final int c = baseNCodec.readResults(buf, 0, avail, context);
121 if (c > 0) {
122 out.write(buf, 0, c);
123 }
124 }
125 if (propagate) {
126 out.flush();
127 }
128 }
129
130 /**
131 * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
132 *
133 * @throws IOException
134 * if an I/O error occurs.
135 */
136 @Override
137 public void flush() throws IOException {
138 flush(true);
139 }
140
141 /**
142 * Closes this output stream and releases any system resources associated with the stream.
143 * <p>
144 * To write the EOF marker without closing the stream, call {@link #eof()} or use an
145 * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
146 * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
147 * >CloseShieldOutputStream</a>.
148 * </p>
149 *
150 * @throws IOException
151 * if an I/O error occurs.
152 */
153 @Override
154 public void close() throws IOException {
155 eof();
156 flush();
157 out.close();
158 }
159
160 /**
161 * Writes EOF.
162 *
163 * @throws IOException
164 * if an I/O error occurs.
165 * @since 1.11
166 */
167 public void eof() throws IOException {
168 // Notify encoder of EOF (-1).
169 if (doEncode) {
170 baseNCodec.encode(singleByte, 0, EOF, context);
171 } else {
172 baseNCodec.decode(singleByte, 0, EOF, context);
173 }
174 }
175
176 }