001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.codec.binary; 019 020import static org.apache.commons.codec.binary.BaseNCodec.EOF; 021 022import java.io.FilterOutputStream; 023import java.io.IOException; 024import java.io.OutputStream; 025import java.util.Objects; 026 027import org.apache.commons.codec.binary.BaseNCodec.Context; 028 029/** 030 * Abstract superclass for Base-N output streams. 031 * <p> 032 * To write the EOF marker without closing the stream, call {@link #eof()} or use an <a 033 * href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href= 034 * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html" 035 * >CloseShieldOutputStream</a>. 036 * </p> 037 * 038 * @since 1.5 039 */ 040public class BaseNCodecOutputStream extends FilterOutputStream { 041 042 private final boolean doEncode; 043 044 private final BaseNCodec baseNCodec; 045 046 private final byte[] singleByte = new byte[1]; 047 048 private final Context context = new Context(); 049 050 /** 051 * Constructs a new instance. 052 * 053 * TODO should this be protected? 054 * 055 * @param outputStream the underlying output or null. 056 * @param basedCodec a BaseNCodec. 057 * @param doEncode true to encode, false to decode, TODO should be an enum? 058 */ 059 public BaseNCodecOutputStream(final OutputStream outputStream, final BaseNCodec basedCodec, final boolean doEncode) { 060 super(outputStream); 061 this.baseNCodec = basedCodec; 062 this.doEncode = doEncode; 063 } 064 065 /** 066 * Closes this output stream and releases any system resources associated with the stream. 067 * <p> 068 * To write the EOF marker without closing the stream, call {@link #eof()} or use an 069 * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href= 070 * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html" 071 * >CloseShieldOutputStream</a>. 072 * </p> 073 * 074 * @throws IOException 075 * if an I/O error occurs. 076 */ 077 @Override 078 public void close() throws IOException { 079 eof(); 080 flush(); 081 out.close(); 082 } 083 084 /** 085 * Writes EOF. 086 * 087 * @since 1.11 088 */ 089 public void eof() { 090 // Notify encoder of EOF (-1). 091 if (doEncode) { 092 baseNCodec.encode(singleByte, 0, EOF, context); 093 } else { 094 baseNCodec.decode(singleByte, 0, EOF, context); 095 } 096 } 097 098 /** 099 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. 100 * 101 * @throws IOException 102 * if an I/O error occurs. 103 */ 104 @Override 105 public void flush() throws IOException { 106 flush(true); 107 } 108 109 /** 110 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propagate is 111 * true, the wrapped stream will also be flushed. 112 * 113 * @param propagate 114 * boolean flag to indicate whether the wrapped OutputStream should also be flushed. 115 * @throws IOException 116 * if an I/O error occurs. 117 */ 118 private void flush(final boolean propagate) throws IOException { 119 final int avail = baseNCodec.available(context); 120 if (avail > 0) { 121 final byte[] buf = new byte[avail]; 122 final int c = baseNCodec.readResults(buf, 0, avail, context); 123 if (c > 0) { 124 out.write(buf, 0, c); 125 } 126 } 127 if (propagate) { 128 out.flush(); 129 } 130 } 131 132 /** 133 * Returns true if decoding behavior is strict. Decoding will raise an 134 * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding. 135 * 136 * <p> 137 * The default is false for lenient encoding. Decoding will compose trailing bits 138 * into 8-bit bytes and discard the remainder. 139 * </p> 140 * 141 * @return true if using strict decoding 142 * @since 1.15 143 */ 144 public boolean isStrictDecoding() { 145 return baseNCodec.isStrictDecoding(); 146 } 147 148 /** 149 * Writes {@code len} bytes from the specified {@code b} array starting at {@code offset} to this 150 * output stream. 151 * 152 * @param array 153 * source byte array 154 * @param offset 155 * where to start reading the bytes 156 * @param len 157 * maximum number of bytes to write 158 * 159 * @throws IOException 160 * if an I/O error occurs. 161 * @throws NullPointerException 162 * if the byte array parameter is null 163 * @throws IndexOutOfBoundsException 164 * if offset, len or buffer size are invalid 165 */ 166 @Override 167 public void write(final byte[] array, final int offset, final int len) throws IOException { 168 Objects.requireNonNull(array, "array"); 169 if (offset < 0 || len < 0) { 170 throw new IndexOutOfBoundsException(); 171 } 172 if (offset > array.length || offset + len > array.length) { 173 throw new IndexOutOfBoundsException(); 174 } 175 if (len > 0) { 176 if (doEncode) { 177 baseNCodec.encode(array, offset, len, context); 178 } else { 179 baseNCodec.decode(array, offset, len, context); 180 } 181 flush(false); 182 } 183 } 184 185 /** 186 * Writes the specified {@code byte} to this output stream. 187 * 188 * @param i 189 * source byte 190 * @throws IOException 191 * if an I/O error occurs. 192 */ 193 @Override 194 public void write(final int i) throws IOException { 195 singleByte[0] = (byte) i; 196 write(singleByte, 0, 1); 197 } 198 199}