BaseNCodecOutputStream.java

  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. package org.apache.commons.codec.binary;

  18. import static org.apache.commons.codec.binary.BaseNCodec.EOF;

  19. import java.io.FilterOutputStream;
  20. import java.io.IOException;
  21. import java.io.OutputStream;

  22. import org.apache.commons.codec.binary.BaseNCodec.Context;

  23. /**
  24.  * Abstract superclass for Base-N output streams.
  25.  * <p>
  26.  * To write the EOF marker without closing the stream, call {@link #eof()} or use an <a
  27.  * href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
  28.  * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
  29.  * >CloseShieldOutputStream</a>.
  30.  * </p>
  31.  *
  32.  * @since 1.5
  33.  * @version $Id: BaseNCodecOutputStream.java 1744727 2016-05-20 12:43:52Z sebb $
  34.  */
  35. public class BaseNCodecOutputStream extends FilterOutputStream {

  36.     private final boolean doEncode;

  37.     private final BaseNCodec baseNCodec;

  38.     private final byte[] singleByte = new byte[1];

  39.     private final Context context = new Context();

  40.     // TODO should this be protected?
  41.     public BaseNCodecOutputStream(final OutputStream out, final BaseNCodec basedCodec, final boolean doEncode) {
  42.         super(out);
  43.         this.baseNCodec = basedCodec;
  44.         this.doEncode = doEncode;
  45.     }

  46.     /**
  47.      * Writes the specified <code>byte</code> to this output stream.
  48.      *
  49.      * @param i
  50.      *            source byte
  51.      * @throws IOException
  52.      *             if an I/O error occurs.
  53.      */
  54.     @Override
  55.     public void write(final int i) throws IOException {
  56.         singleByte[0] = (byte) i;
  57.         write(singleByte, 0, 1);
  58.     }

  59.     /**
  60.      * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this
  61.      * output stream.
  62.      *
  63.      * @param b
  64.      *            source byte array
  65.      * @param offset
  66.      *            where to start reading the bytes
  67.      * @param len
  68.      *            maximum number of bytes to write
  69.      *
  70.      * @throws IOException
  71.      *             if an I/O error occurs.
  72.      * @throws NullPointerException
  73.      *             if the byte array parameter is null
  74.      * @throws IndexOutOfBoundsException
  75.      *             if offset, len or buffer size are invalid
  76.      */
  77.     @Override
  78.     public void write(final byte b[], final int offset, final int len) throws IOException {
  79.         if (b == null) {
  80.             throw new NullPointerException();
  81.         } else if (offset < 0 || len < 0) {
  82.             throw new IndexOutOfBoundsException();
  83.         } else if (offset > b.length || offset + len > b.length) {
  84.             throw new IndexOutOfBoundsException();
  85.         } else if (len > 0) {
  86.             if (doEncode) {
  87.                 baseNCodec.encode(b, offset, len, context);
  88.             } else {
  89.                 baseNCodec.decode(b, offset, len, context);
  90.             }
  91.             flush(false);
  92.         }
  93.     }

  94.     /**
  95.      * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propagate is
  96.      * true, the wrapped stream will also be flushed.
  97.      *
  98.      * @param propagate
  99.      *            boolean flag to indicate whether the wrapped OutputStream should also be flushed.
  100.      * @throws IOException
  101.      *             if an I/O error occurs.
  102.      */
  103.     private void flush(final boolean propagate) throws IOException {
  104.         final int avail = baseNCodec.available(context);
  105.         if (avail > 0) {
  106.             final byte[] buf = new byte[avail];
  107.             final int c = baseNCodec.readResults(buf, 0, avail, context);
  108.             if (c > 0) {
  109.                 out.write(buf, 0, c);
  110.             }
  111.         }
  112.         if (propagate) {
  113.             out.flush();
  114.         }
  115.     }

  116.     /**
  117.      * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
  118.      *
  119.      * @throws IOException
  120.      *             if an I/O error occurs.
  121.      */
  122.     @Override
  123.     public void flush() throws IOException {
  124.         flush(true);
  125.     }

  126.     /**
  127.      * Closes this output stream and releases any system resources associated with the stream.
  128.      * <p>
  129.      * To write the EOF marker without closing the stream, call {@link #eof()} or use an
  130.      * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
  131.      * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
  132.      * >CloseShieldOutputStream</a>.
  133.      * </p>
  134.      *
  135.      * @throws IOException
  136.      *             if an I/O error occurs.
  137.      */
  138.     @Override
  139.     public void close() throws IOException {
  140.         eof();
  141.         flush();
  142.         out.close();
  143.     }

  144.     /**
  145.      * Writes EOF.
  146.      *
  147.      * @throws IOException
  148.      *             if an I/O error occurs.
  149.      * @since 1.11
  150.      */
  151.     public void eof() throws IOException {
  152.         // Notify encoder of EOF (-1).
  153.         if (doEncode) {
  154.             baseNCodec.encode(singleByte, 0, EOF, context);
  155.         } else {
  156.             baseNCodec.decode(singleByte, 0, EOF, context);
  157.         }
  158.     }

  159. }