Package com.ensifera.animosity.craftirc.libs.org.jibble.pircbot

Source Code of com.ensifera.animosity.craftirc.libs.org.jibble.pircbot.FallbackInputStreamReader

package com.ensifera.animosity.craftirc.libs.org.jibble.pircbot;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;

public class FallbackInputStreamReader extends Reader {

    /**
     * Turn this on for low level debugging output
     */
    private static final boolean debug = false;

    private final PircBotLogger logger;
    private final InputStream in;
    private final Charset primaryCharset;
    private final Charset fallbackCharset;

    private boolean gotEof; // if true, fillBuffer() will not try to read more
    private ByteBuffer buf = ByteBuffer.allocate(1024); // if null after call to fillBuffer(), no more data available
    private int bufFillLevel; // how much of buf is filled (position in buf) - always kept up to date, even inside method calls
    private byte[] tmp = new byte[1024];

    private CharBuffer decBuf; // if null after call to fillDecodeBuffer(), no more data available
    private Charset lastCharset;

    public FallbackInputStreamReader(PircBotLogger logger, InputStream in, String primaryCharsetName, String fallbackCharsetName) throws UnsupportedEncodingException {
        this.logger = logger;
        this.in = in;
        this.primaryCharset = Charset.forName(primaryCharsetName);
        this.fallbackCharset = Charset.forName(fallbackCharsetName);
        buf.limit(0);
    }

    @Override
    public void close() throws IOException {
        in.close();
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        fillDecodeBuffer();
        if(decBuf == null) {
            return -1;
        }
        int rem = decBuf.remaining();
        if(rem < len) {
            len = rem;
        }
        decBuf.get(cbuf, off, len);
        if(debug) logger.log(lastCharset + " READ return '" + new String(cbuf, off, len) + "'");
        return len;
    }

    private void fillDecodeBuffer() throws IOException {
        if (decBuf != null && decBuf.hasRemaining()) {
            return;
        }
        fillBuffer();
        if (buf == null) {
            decBuf = null;
            return;
        }
        CharsetDecoder d = primaryCharset.newDecoder();
        d.onMalformedInput(CodingErrorAction.REPORT);
        d.onUnmappableCharacter(CodingErrorAction.REPORT);
        int origPos = buf.position();
        try {
            decBuf = d.decode(buf);
            lastCharset = primaryCharset;
        } catch(IOException e) {
            d = fallbackCharset.newDecoder();
            d.onMalformedInput(CodingErrorAction.REPLACE);
            d.onUnmappableCharacter(CodingErrorAction.REPLACE);
            try {
                // rewind
                buf.position(origPos);
                decBuf = d.decode(buf);
                lastCharset = fallbackCharset;
            } catch (CharacterCodingException e1) {
                throw new RuntimeException(e1);
            }
        }
    }

    private void fillBuffer() throws IOException {
        if(debug) logger.log(buf == null ? "FISR bp null bl null fl n/a" : ("bp " + buf.position() + " bl " + buf.limit() + " fl " + bufFillLevel));

        // if we got EOF, just give everything we got left, if any
        if(gotEof) {
            if(buf != null && !buf.hasRemaining()) {
                buf = null;
            }
            if(debug) logger.log("FISR  -> EOF");
            return;
        }

        // sanity check
        if(buf != null && buf.hasRemaining()) {
            // this should never happen
            logger.log("### BUG in FISR: pos<lim: " + buf.position() + " < " + buf.limit());
            // discard remaining
            buf.position(buf.limit());
        }

        // compact remaining data
        buf.limit(bufFillLevel);
        buf.compact();
        bufFillLevel = buf.position();

        // see if we got a line feed already last round
        if(checkLF(0)) {
            return;
        }

        try {
            while(true) {
                // read some more data
                int r = in.read(tmp);

                // if we got EOF, just toss the remaining data back and remember EOF happened
                if(r == -1) {
                    gotEof = true;
                    tmp = null;
                    buf.flip();
                    if(debug) logger.log("FISR  -> 2 bp " + buf.position() + " bl " + buf.limit() + " fl " + bufFillLevel);
                    if(buf.remaining() == 0) {
                        buf = null;
                    }
                    return;
                }

                // make sure we have space to receive the data
                if(buf.remaining() < r) {
                    if(debug) logger.log("FISR bp Doubling buffer");
                    buf.flip();
                    ByteBuffer bb = ByteBuffer.allocate(buf.capacity() * 2);
                    bb.put(buf);
                    buf = bb;
                }

                // copy the newly received data to the buffer
                int start = buf.position();
                buf.put(tmp, 0, r);
                bufFillLevel = buf.position();

                // see if we got a line feed
                if(checkLF(start)) {
                    return;
                }
            }
        } catch (IOException e) {
            // this might happen non-fatally, for example SocketTimeoutException, so just update state so we can continue later.
           
            // set limit to 0 since nothing of what may be in the buffer will get consumed this round. This way next fillBuffer() call will continue where we left
            buf.limit(0);
            if(debug) logger.log("FISR  -> " + e + " bp " + buf.position() + " bl " + buf.limit() + " fl " + bufFillLevel);
            throw e;
        }
    }

    private boolean checkLF(int start) {
        for(int i=start; i<buf.position(); ++i) {
            if(buf.get(i) == '\n') {
                // do a flip, but only give away up until and including LF
                buf.position(0);
                buf.limit(i+1);
                if(debug) logger.log("FISR  -> 1 / " + start + " bp " + buf.position() + " bl " + buf.limit() + " fl " + bufFillLevel);
                return true;
            }
        }
        return false;
    }
}
TOP

Related Classes of com.ensifera.animosity.craftirc.libs.org.jibble.pircbot.FallbackInputStreamReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.