View Javadoc
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 }