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    
018    package org.apache.commons.codec.binary;
019    
020    import java.io.FilterOutputStream;
021    import java.io.IOException;
022    import java.io.OutputStream;
023    
024    /**
025     * Abstract superclass for Base-N output streams.
026     * 
027     * @since 1.5
028     */
029    public class BaseNCodecOutputStream extends FilterOutputStream {
030    
031        private final boolean doEncode;
032    
033        private final BaseNCodec baseNCodec;
034    
035        private final byte[] singleByte = new byte[1];
036    
037        public BaseNCodecOutputStream(OutputStream out, BaseNCodec basedCodec, boolean doEncode) {
038            super(out);
039            this.baseNCodec = basedCodec;
040            this.doEncode = doEncode;
041        }
042    
043        /**
044         * Writes the specified <code>byte</code> to this output stream.
045         * 
046         * @param i
047         *            source byte
048         * @throws IOException
049         *             if an I/O error occurs.
050         */
051        public void write(int i) throws IOException {
052            singleByte[0] = (byte) i;
053            write(singleByte, 0, 1);
054        }
055    
056        /**
057         * Writes <code>len</code> bytes from the specified <code>b</code> array starting at <code>offset</code> to this
058         * output stream.
059         * 
060         * @param b
061         *            source byte array
062         * @param offset
063         *            where to start reading the bytes
064         * @param len
065         *            maximum number of bytes to write
066         * 
067         * @throws IOException
068         *             if an I/O error occurs.
069         * @throws NullPointerException
070         *             if the byte array parameter is null
071         * @throws IndexOutOfBoundsException
072         *             if offset, len or buffer size are invalid
073         */
074        public void write(byte b[], int offset, int len) throws IOException {
075            if (b == null) {
076                throw new NullPointerException();
077            } else if (offset < 0 || len < 0) {
078                throw new IndexOutOfBoundsException();
079            } else if (offset > b.length || offset + len > b.length) {
080                throw new IndexOutOfBoundsException();
081            } else if (len > 0) {
082                if (doEncode) {
083                    baseNCodec.encode(b, offset, len);
084                } else {
085                    baseNCodec.decode(b, offset, len);
086                }
087                flush(false);
088            }
089        }
090    
091        /**
092         * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propogate is
093         * true, the wrapped stream will also be flushed.
094         * 
095         * @param propogate
096         *            boolean flag to indicate whether the wrapped OutputStream should also be flushed.
097         * @throws IOException
098         *             if an I/O error occurs.
099         */
100        private void flush(boolean propogate) throws IOException {
101            int avail = baseNCodec.available();
102            if (avail > 0) {
103                byte[] buf = new byte[avail];
104                int c = baseNCodec.readResults(buf, 0, avail);
105                if (c > 0) {
106                    out.write(buf, 0, c);
107                }
108            }
109            if (propogate) {
110                out.flush();
111            }
112        }
113    
114        /**
115         * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
116         * 
117         * @throws IOException
118         *             if an I/O error occurs.
119         */
120        public void flush() throws IOException {
121            flush(true);
122        }
123    
124        /**
125         * Closes this output stream and releases any system resources associated with the stream.
126         * 
127         * @throws IOException
128         *             if an I/O error occurs.
129         */
130        public void close() throws IOException {
131            // Notify encoder of EOF (-1).
132            if (doEncode) {
133                baseNCodec.encode(singleByte, 0, -1);
134            } else {
135                baseNCodec.decode(singleByte, 0, -1);
136            }
137            flush();
138            out.close();
139        }
140    
141    }