001/**
002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC
003 * and GZip files. (http://jwat.org/)
004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.jwat.common;
019
020import java.io.IOException;
021import java.io.StringReader;
022
023/**
024 * <code>StringReader</code> that keeps tracks of the number of consumed chars
025 * at any given time.
026 *
027 * @author lbihanic, selghissassi, nicl
028 */
029public class CharCountingStringReader extends StringReader {
030
031    /** Number of consumed bytes since the beginning of the stream. */
032    protected long consumed = 0;
033
034    /** Byte counter which can also be changed. */
035    protected long counter = 0;
036
037    /**
038     * Creates a new <code>StringReader</code> that keeps track of
039     * consumed characters.
040     * @param str Arbitrary string.
041     */
042    public CharCountingStringReader(String str) {
043        super(str);
044    }
045
046    @Override
047    public boolean markSupported() {
048        return false;
049    }
050
051    @Override
052    public synchronized void mark(int readlimit) {
053        throw new UnsupportedOperationException();
054    }
055
056    @Override
057    public synchronized void reset() throws IOException {
058        throw new UnsupportedOperationException();
059    }
060
061    /**
062     * Consumed length getter.
063     * @return the consumed length
064     */
065    public long getConsumed() {
066        return consumed;
067    }
068
069    /**
070     * Change the counter value.
071     * Useful for reading zero indexed relative data.
072     * @param bytes new counter value
073     */
074    public void setCounter(long bytes) {
075        counter = bytes;
076    }
077
078    /**
079     * Retrieve the current counter value.
080     * @return current counter value
081     */
082    public long getCounter() {
083        return counter;
084    }
085
086    @Override
087    public int read() throws IOException {
088        int c = super.read();
089        if (c != -1) {
090            ++consumed;
091            ++counter;
092        }
093        return c;
094    }
095
096    @Override
097    public int read(char[] cbuf, int off, int len) throws IOException {
098        int bytesRead = super.read(cbuf, off, len);
099        if (bytesRead > 0) {
100            consumed += bytesRead;
101            counter += bytesRead;
102        }
103        return bytesRead;
104    }
105
106    @Override
107    public long skip(long n) throws IOException {
108        long bytesSkipped = super.skip(n);
109        consumed += bytesSkipped;
110        counter += bytesSkipped;
111        return bytesSkipped;
112    }
113
114    /**
115     * Reads a line defined as characters read until encountering a
116     * <code>LF</code> or EOF.
117     * @return Line read from buffered <code>StringReader</code>
118     * @throws IOException if an I/O error occurs while reading line
119     */
120    public String readLine() throws IOException {
121        StringBuilder buf = new StringBuilder();
122        int c;
123        while (true) {
124            c = read();
125            if (c == -1) {
126                return null;
127            }
128            if (c == '\n') {
129                break;
130            }
131            if (c != '\r') {
132                buf.append((char) c);
133            }
134        }
135        return buf.toString();
136    }
137
138}