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; 025 026import org.apache.commons.codec.binary.BaseNCodec.Context; 027 028/** 029 * Abstract superclass for Base-N output streams. 030 * <p> 031 * To write the EOF marker without closing the stream, call {@link #eof()} or use an <a 032 * href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href= 033 * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html" 034 * >CloseShieldOutputStream</a>. 035 * </p> 036 * 037 * @since 1.5 038 */ 039public class BaseNCodecOutputStream extends FilterOutputStream { 040 041 private final boolean doEncode; 042 043 private final BaseNCodec baseNCodec; 044 045 private final byte[] singleByte = new byte[1]; 046 047 private final Context context = new Context(); 048 049 // TODO should this be protected? 050 public BaseNCodecOutputStream(final OutputStream out, final BaseNCodec basedCodec, final boolean doEncode) { 051 super(out); 052 this.baseNCodec = basedCodec; 053 this.doEncode = doEncode; 054 } 055 056 /** 057 * Writes the specified <code>byte</code> to this output stream. 058 * 059 * @param i 060 * source byte 061 * @throws IOException 062 * if an I/O error occurs. 063 */ 064 @Override 065 public void write(final int i) throws IOException { 066 singleByte[0] = (byte) i; 067 write(singleByte, 0, 1); 068 } 069 070 /** 071 * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this 072 * output stream. 073 * 074 * @param b 075 * source byte array 076 * @param offset 077 * where to start reading the bytes 078 * @param len 079 * maximum number of bytes to write 080 * 081 * @throws IOException 082 * if an I/O error occurs. 083 * @throws NullPointerException 084 * if the byte array parameter is null 085 * @throws IndexOutOfBoundsException 086 * if offset, len or buffer size are invalid 087 */ 088 @Override 089 public void write(final byte b[], final int offset, final int len) throws IOException { 090 if (b == null) { 091 throw new NullPointerException(); 092 } else if (offset < 0 || len < 0) { 093 throw new IndexOutOfBoundsException(); 094 } else if (offset > b.length || offset + len > b.length) { 095 throw new IndexOutOfBoundsException(); 096 } else if (len > 0) { 097 if (doEncode) { 098 baseNCodec.encode(b, offset, len, context); 099 } else { 100 baseNCodec.decode(b, offset, len, context); 101 } 102 flush(false); 103 } 104 } 105 106 /** 107 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propagate is 108 * true, the wrapped stream will also be flushed. 109 * 110 * @param propagate 111 * boolean flag to indicate whether the wrapped OutputStream should also be flushed. 112 * @throws IOException 113 * if an I/O error occurs. 114 */ 115 private void flush(final boolean propagate) throws IOException { 116 final int avail = baseNCodec.available(context); 117 if (avail > 0) { 118 final byte[] buf = new byte[avail]; 119 final int c = baseNCodec.readResults(buf, 0, avail, context); 120 if (c > 0) { 121 out.write(buf, 0, c); 122 } 123 } 124 if (propagate) { 125 out.flush(); 126 } 127 } 128 129 /** 130 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. 131 * 132 * @throws IOException 133 * if an I/O error occurs. 134 */ 135 @Override 136 public void flush() throws IOException { 137 flush(true); 138 } 139 140 /** 141 * Closes this output stream and releases any system resources associated with the stream. 142 * <p> 143 * To write the EOF marker without closing the stream, call {@link #eof()} or use an 144 * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href= 145 * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html" 146 * >CloseShieldOutputStream</a>. 147 * </p> 148 * 149 * @throws IOException 150 * if an I/O error occurs. 151 */ 152 @Override 153 public void close() throws IOException { 154 eof(); 155 flush(); 156 out.close(); 157 } 158 159 /** 160 * Writes EOF. 161 * 162 * @throws IOException 163 * if an I/O error occurs. 164 * @since 1.11 165 */ 166 public void eof() throws IOException { 167 // Notify encoder of EOF (-1). 168 if (doEncode) { 169 baseNCodec.encode(singleByte, 0, EOF, context); 170 } else { 171 baseNCodec.decode(singleByte, 0, EOF, context); 172 } 173 } 174 175}