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    package org.apache.commons.io.input;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    
022    /**
023     * A decorating input stream that counts the number of bytes that have passed
024     * through the stream so far.
025     * <p>
026     * A typical use case would be during debugging, to ensure that data is being
027     * read as expected.
028     *
029     * @author Marcelo Liberato
030     * @version $Id: CountingInputStream.java 471628 2006-11-06 04:06:45Z bayard $
031     */
032    public class CountingInputStream extends ProxyInputStream {
033    
034        /** The count of bytes that have passed. */
035        private long count;
036    
037        /**
038         * Constructs a new CountingInputStream.
039         *
040         * @param in  the InputStream to delegate to
041         */
042        public CountingInputStream(InputStream in) {
043            super(in);
044        }
045    
046        //-----------------------------------------------------------------------
047        /**
048         * Reads a number of bytes into the byte array, keeping count of the
049         * number read.
050         *
051         * @param b  the buffer into which the data is read, not null
052         * @return the total number of bytes read into the buffer, -1 if end of stream
053         * @throws IOException if an I/O error occurs
054         * @see java.io.InputStream#read(byte[]) 
055         */
056        public int read(byte[] b) throws IOException {
057            int found = super.read(b);
058            this.count += (found >= 0) ? found : 0;
059            return found;
060        }
061    
062        /**
063         * Reads a number of bytes into the byte array at a specific offset,
064         * keeping count of the number read.
065         *
066         * @param b  the buffer into which the data is read, not null
067         * @param off  the start offset in the buffer
068         * @param len  the maximum number of bytes to read
069         * @return the total number of bytes read into the buffer, -1 if end of stream
070         * @throws IOException if an I/O error occurs
071         * @see java.io.InputStream#read(byte[], int, int)
072         */
073        public int read(byte[] b, int off, int len) throws IOException {
074            int found = super.read(b, off, len);
075            this.count += (found >= 0) ? found : 0;
076            return found;
077        }
078    
079        /**
080         * Reads the next byte of data adding to the count of bytes received
081         * if a byte is successfully read. 
082         *
083         * @return the byte read, -1 if end of stream
084         * @throws IOException if an I/O error occurs
085         * @see java.io.InputStream#read()
086         */
087        public int read() throws IOException {
088            int found = super.read();
089            this.count += (found >= 0) ? 1 : 0;
090            return found;
091        }
092    
093        /**
094         * Skips the stream over the specified number of bytes, adding the skipped
095         * amount to the count.
096         *
097         * @param length  the number of bytes to skip
098         * @return the actual number of bytes skipped
099         * @throws IOException if an I/O error occurs
100         * @see java.io.InputStream#skip(long)
101         */
102        public long skip(final long length) throws IOException {
103            final long skip = super.skip(length);
104            this.count += skip;
105            return skip;
106        }
107    
108        //-----------------------------------------------------------------------
109        /**
110         * The number of bytes that have passed through this stream.
111         * <p>
112         * NOTE: From v1.3 this method throws an ArithmeticException if the
113         * count is greater than can be expressed by an <code>int</code>.
114         * See {@link #getByteCount()} for a method using a <code>long</code>.
115         *
116         * @return the number of bytes accumulated
117         * @throws ArithmeticException if the byte count is too large
118         */
119        public synchronized int getCount() {
120            long result = getByteCount();
121            if (result > Integer.MAX_VALUE) {
122                throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
123            }
124            return (int) result;
125        }
126    
127        /** 
128         * Set the byte count back to 0. 
129         * <p>
130         * NOTE: From v1.3 this method throws an ArithmeticException if the
131         * count is greater than can be expressed by an <code>int</code>.
132         * See {@link #resetByteCount()} for a method using a <code>long</code>.
133         *
134         * @return the count previous to resetting
135         * @throws ArithmeticException if the byte count is too large
136         */
137        public synchronized int resetCount() {
138            long result = resetByteCount();
139            if (result > Integer.MAX_VALUE) {
140                throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
141            }
142            return (int) result;
143        }
144    
145        /**
146         * The number of bytes that have passed through this stream.
147         * <p>
148         * NOTE: This method is an alternative for <code>getCount()</code>
149         * and was added because that method returns an integer which will
150         * result in incorrect count for files over 2GB.
151         *
152         * @return the number of bytes accumulated
153         * @since Commons IO 1.3
154         */
155        public synchronized long getByteCount() {
156            return this.count;
157        }
158    
159        /** 
160         * Set the byte count back to 0. 
161         * <p>
162         * NOTE: This method is an alternative for <code>resetCount()</code>
163         * and was added because that method returns an integer which will
164         * result in incorrect count for files over 2GB.
165         *
166         * @return the count previous to resetting
167         * @since Commons IO 1.3
168         */
169        public synchronized long resetByteCount() {
170            long tmp = this.count;
171            this.count = 0;
172            return tmp;
173        }
174    
175    }