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   *
31   * @since 1.5
32   * @version $Id: BaseNCodecOutputStream.java 1544347 2013-11-21 22:30:31Z ggregory $
33   */
34  public class BaseNCodecOutputStream extends FilterOutputStream {
35  
36      private final boolean doEncode;
37  
38      private final BaseNCodec baseNCodec;
39  
40      private final byte[] singleByte = new byte[1];
41  
42      private final Context context = new Context();
43  
44      // TODO should this be protected?
45      public BaseNCodecOutputStream(final OutputStream out, final BaseNCodec basedCodec, final boolean doEncode) {
46          super(out);
47          this.baseNCodec = basedCodec;
48          this.doEncode = doEncode;
49      }
50  
51      /**
52       * Writes the specified <code>byte</code> to this output stream.
53       *
54       * @param i
55       *            source byte
56       * @throws IOException
57       *             if an I/O error occurs.
58       */
59      @Override
60      public void write(final int i) throws IOException {
61          singleByte[0] = (byte) i;
62          write(singleByte, 0, 1);
63      }
64  
65      /**
66       * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this
67       * output stream.
68       *
69       * @param b
70       *            source byte array
71       * @param offset
72       *            where to start reading the bytes
73       * @param len
74       *            maximum number of bytes to write
75       *
76       * @throws IOException
77       *             if an I/O error occurs.
78       * @throws NullPointerException
79       *             if the byte array parameter is null
80       * @throws IndexOutOfBoundsException
81       *             if offset, len or buffer size are invalid
82       */
83      @Override
84      public void write(final byte b[], final int offset, final int len) throws IOException {
85          if (b == null) {
86              throw new NullPointerException();
87          } else if (offset < 0 || len < 0) {
88              throw new IndexOutOfBoundsException();
89          } else if (offset > b.length || offset + len > b.length) {
90              throw new IndexOutOfBoundsException();
91          } else if (len > 0) {
92              if (doEncode) {
93                  baseNCodec.encode(b, offset, len, context);
94              } else {
95                  baseNCodec.decode(b, offset, len, context);
96              }
97              flush(false);
98          }
99      }
100 
101     /**
102      * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propagate is
103      * true, the wrapped stream will also be flushed.
104      *
105      * @param propagate
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(final boolean propagate) throws IOException {
111         final int avail = baseNCodec.available(context);
112         if (avail > 0) {
113             final byte[] buf = new byte[avail];
114             final int c = baseNCodec.readResults(buf, 0, avail, context);
115             if (c > 0) {
116                 out.write(buf, 0, c);
117             }
118         }
119         if (propagate) {
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 }