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 }