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 * 031 * @since 1.5 032 * @version $Id: BaseNCodecOutputStream.html 928559 2014-11-10 02:53:54Z ggregory $ 033 */ 034public class BaseNCodecOutputStream extends FilterOutputStream { 035 036 private final boolean doEncode; 037 038 private final BaseNCodec baseNCodec; 039 040 private final byte[] singleByte = new byte[1]; 041 042 private final Context context = new Context(); 043 044 // TODO should this be protected? 045 public BaseNCodecOutputStream(final OutputStream out, final BaseNCodec basedCodec, final boolean doEncode) { 046 super(out); 047 this.baseNCodec = basedCodec; 048 this.doEncode = doEncode; 049 } 050 051 /** 052 * Writes the specified <code>byte</code> to this output stream. 053 * 054 * @param i 055 * source byte 056 * @throws IOException 057 * if an I/O error occurs. 058 */ 059 @Override 060 public void write(final int i) throws IOException { 061 singleByte[0] = (byte) i; 062 write(singleByte, 0, 1); 063 } 064 065 /** 066 * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this 067 * output stream. 068 * 069 * @param b 070 * source byte array 071 * @param offset 072 * where to start reading the bytes 073 * @param len 074 * maximum number of bytes to write 075 * 076 * @throws IOException 077 * if an I/O error occurs. 078 * @throws NullPointerException 079 * if the byte array parameter is null 080 * @throws IndexOutOfBoundsException 081 * if offset, len or buffer size are invalid 082 */ 083 @Override 084 public void write(final byte b[], final int offset, final int len) throws IOException { 085 if (b == null) { 086 throw new NullPointerException(); 087 } else if (offset < 0 || len < 0) { 088 throw new IndexOutOfBoundsException(); 089 } else if (offset > b.length || offset + len > b.length) { 090 throw new IndexOutOfBoundsException(); 091 } else if (len > 0) { 092 if (doEncode) { 093 baseNCodec.encode(b, offset, len, context); 094 } else { 095 baseNCodec.decode(b, offset, len, context); 096 } 097 flush(false); 098 } 099 } 100 101 /** 102 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propagate is 103 * true, the wrapped stream will also be flushed. 104 * 105 * @param propagate 106 * boolean flag to indicate whether the wrapped OutputStream should also be flushed. 107 * @throws IOException 108 * if an I/O error occurs. 109 */ 110 private void flush(final boolean propagate) throws IOException { 111 final int avail = baseNCodec.available(context); 112 if (avail > 0) { 113 final byte[] buf = new byte[avail]; 114 final int c = baseNCodec.readResults(buf, 0, avail, context); 115 if (c > 0) { 116 out.write(buf, 0, c); 117 } 118 } 119 if (propagate) { 120 out.flush(); 121 } 122 } 123 124 /** 125 * Flushes this output stream and forces any buffered output bytes to be written out to the stream. 126 * 127 * @throws IOException 128 * if an I/O error occurs. 129 */ 130 @Override 131 public void flush() throws IOException { 132 flush(true); 133 } 134 135 /** 136 * Closes this output stream and releases any system resources associated with the stream. 137 * 138 * @throws IOException 139 * if an I/O error occurs. 140 */ 141 @Override 142 public void close() throws IOException { 143 // Notify encoder of EOF (-1). 144 if (doEncode) { 145 baseNCodec.encode(singleByte, 0, EOF, context); 146 } else { 147 baseNCodec.decode(singleByte, 0, EOF, context); 148 } 149 flush(); 150 out.close(); 151 } 152 153}