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 */
017package org.apache.commons.io.input;
018
019import static org.apache.commons.io.IOUtils.EOF;
020
021import java.io.IOException;
022import java.io.InputStream;
023
024/**
025 * A decorating input stream that counts the number of bytes that have passed
026 * through the stream so far.
027 * <p>
028 * A typical use case would be during debugging, to ensure that data is being
029 * read as expected.
030 *
031 * @version $Id: CountingInputStream.java 1586350 2014-04-10 15:57:20Z ggregory $
032 */
033public class CountingInputStream extends ProxyInputStream {
034
035    /** The count of bytes that have passed. */
036    private long count;
037
038    /**
039     * Constructs a new CountingInputStream.
040     *
041     * @param in  the InputStream to delegate to
042     */
043    public CountingInputStream(final InputStream in) {
044        super(in);
045    }
046
047    //-----------------------------------------------------------------------
048
049    /**
050     * Skips the stream over the specified number of bytes, adding the skipped
051     * amount to the count.
052     *
053     * @param length  the number of bytes to skip
054     * @return the actual number of bytes skipped
055     * @throws IOException if an I/O error occurs
056     * @see java.io.InputStream#skip(long)
057     */
058    @Override
059    public synchronized long skip(final long length) throws IOException {
060        final long skip = super.skip(length);
061        this.count += skip;
062        return skip;
063    }
064
065    /**
066     * Adds the number of read bytes to the count.
067     *
068     * @param n number of bytes read, or -1 if no more bytes are available
069     * @since 2.0
070     */
071    @Override
072    protected synchronized void afterRead(final int n) {
073        if (n != EOF) {
074            this.count += n;
075        }
076    }
077
078    //-----------------------------------------------------------------------
079    /**
080     * The number of bytes that have passed through this stream.
081     * <p>
082     * NOTE: From v1.3 this method throws an ArithmeticException if the
083     * count is greater than can be expressed by an <code>int</code>.
084     * See {@link #getByteCount()} for a method using a <code>long</code>.
085     *
086     * @return the number of bytes accumulated
087     * @throws ArithmeticException if the byte count is too large
088     */
089    public int getCount() {
090        final long result = getByteCount();
091        if (result > Integer.MAX_VALUE) {
092            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
093        }
094        return (int) result;
095    }
096
097    /** 
098     * Set the byte count back to 0. 
099     * <p>
100     * NOTE: From v1.3 this method throws an ArithmeticException if the
101     * count is greater than can be expressed by an <code>int</code>.
102     * See {@link #resetByteCount()} for a method using a <code>long</code>.
103     *
104     * @return the count previous to resetting
105     * @throws ArithmeticException if the byte count is too large
106     */
107    public int resetCount() {
108        final long result = resetByteCount();
109        if (result > Integer.MAX_VALUE) {
110            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
111        }
112        return (int) result;
113    }
114
115    /**
116     * The number of bytes that have passed through this stream.
117     * <p>
118     * NOTE: This method is an alternative for <code>getCount()</code>
119     * and was added because that method returns an integer which will
120     * result in incorrect count for files over 2GB.
121     *
122     * @return the number of bytes accumulated
123     * @since 1.3
124     */
125    public synchronized long getByteCount() {
126        return this.count;
127    }
128
129    /** 
130     * Set the byte count back to 0. 
131     * <p>
132     * NOTE: This method is an alternative for <code>resetCount()</code>
133     * and was added because that method returns an integer which will
134     * result in incorrect count for files over 2GB.
135     *
136     * @return the count previous to resetting
137     * @since 1.3
138     */
139    public synchronized long resetByteCount() {
140        final long tmp = this.count;
141        this.count = 0;
142        return tmp;
143    }
144
145}