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 * @version $Id: BaseNCodecOutputStream.java 1744727 2016-05-20 12:43:52Z sebb $ 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 // TODO should this be protected? 051 public BaseNCodecOutputStream(final OutputStream out, final BaseNCodec basedCodec, final boolean doEncode) { 052 super(out); 053 this.baseNCodec = basedCodec; 054 this.doEncode = doEncode; 055 } 056 057 /** 058 * Writes the specified <code>byte</code> to this output stream. 059 * 060 * @param i 061 * source byte 062 * @throws IOException 063 * if an I/O error occurs. 064 */ 065 @Override 066 public void write(final int i) throws IOException { 067 singleByte[0] = (byte) i; 068 write(singleByte, 0, 1); 069 } 070 071 /** 072 * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this 073 * output stream. 074 * 075 * @param b 076 * source byte array 077 * @param offset 078 * where to start reading the bytes 079 * @param len 080 * maximum number of bytes to write 081 * 082 * @throws IOException 083 * if an I/O error occurs. 084 * @throws NullPointerException 085 * if the byte array parameter is null 086 * @throws IndexOutOfBoundsException 087 * if offset, len or buffer size are invalid 088 */ 089 @Override 090 public void write(final byte b[], final int offset, final int len) throws IOException { 091 if (b == null) { 092 throw new NullPointerException(); 093 } else if (offset < 0 || len < 0) { 094 throw new IndexOutOfBoundsException(); 095 } else if (offset > b.length || offset + len > b.length) { 096 throw new IndexOutOfBoundsException(); 097 } else if (len > 0) { 098 if (doEncode) { 099 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}