CRLFTerminatedReader.java Source code

Java tutorial

Introduction

Here is the source code for CRLFTerminatedReader.java

Source

/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you under the Apache License, Version 2.0 (the            *
 * "License"); you may not use this file except in compliance   *
 * with the License.  You may obtain a copy of the License at   *
 *                                                              *
 *   http://www.apache.org/licenses/LICENSE-2.0                 *
 *                                                              *
 * Unless required by applicable law or agreed to in writing,   *
 * software distributed under the License is distributed on an  *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
 * KIND, either express or implied.  See the License for the    *
 * specific language governing permissions and limitations      *
 * under the License.                                           *
 ****************************************************************/

import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.IOException;

/**
 * A Reader for use with SMTP or other protocols in which lines
 * must end with CRLF.  Extends Reader and overrides its 
 * readLine() method.  The Reader readLine() method cannot
 * serve for SMTP because it ends lines with either CR or LF alone. 
 */
public class CRLFTerminatedReader extends Reader {

    public class TerminationException extends IOException {
        private int where;

        public TerminationException(int where) {
            super();
            this.where = where;
        }

        public TerminationException(String s, int where) {
            super(s);
            this.where = where;
        }

        public int position() {
            return where;
        }
    }

    public class LineLengthExceededException extends IOException {
        public LineLengthExceededException(String s) {
            super(s);
        }
    }

    /**
     * Constructs this CRLFTerminatedReader.
     * @param in an InputStream
     * @param charsetName the String name of a supported charset.  
     * "ASCII" is common here.
     * @throws UnsupportedEncodingException if the named charset
     * is not supported
     */
    InputStream in;

    public CRLFTerminatedReader(InputStream in) {
        this.in = in;
    }

    public CRLFTerminatedReader(InputStream in, String enc) throws UnsupportedEncodingException {
        this(in);
    }

    private StringBuffer lineBuffer = new StringBuffer();
    private final int EOF = -1, CR = 13, LF = 10;

    private int tainted = -1;

    /**
     * Read a line of text which is terminated by CRLF.  The concluding
     * CRLF characters are not returned with the String, but if either CR
     * or LF appears in the text in any other sequence it is returned
     * in the String like any other character.  Some characters at the 
     * end of the stream may be lost if they are in a "line" not
     * terminated by CRLF.
     * 
     * @return either a String containing the contents of a 
     * line which must end with CRLF, or null if the end of the 
     * stream has been reached, possibly discarding some characters 
     * in a line not terminated with CRLF. 
     * @throws IOException if an I/O error occurs.
     */
    public String readLine() throws IOException {

        //start with the StringBuffer empty
        lineBuffer.delete(0, lineBuffer.length());

        /* This boolean tells which state we are in,
         * depending upon whether or not we got a CR
         * in the preceding read().
         */
        boolean cr_just_received = false;

        // Until we add support for specifying a maximum line lenth as
        // a Service Extension, limit lines to 2K, which is twice what
        // RFC 2821 4.5.3.1 requires.
        while (lineBuffer.length() <= 2048) {
            int inChar = read();

            if (!cr_just_received) {
                //the most common case, somewhere before the end of a line
                switch (inChar) {
                case CR:
                    cr_just_received = true;
                    break;
                case EOF:
                    return null; // premature EOF -- discards data(?)
                case LF: //the normal ending of a line
                    if (tainted == -1)
                        tainted = lineBuffer.length();
                    // intentional fall-through
                default:
                    lineBuffer.append((char) inChar);
                }
            } else {
                // CR has been received, we may be at end of line
                switch (inChar) {
                case LF: // LF without a preceding CR
                    if (tainted != -1) {
                        int pos = tainted;
                        tainted = -1;
                        throw new TerminationException("\"bare\" CR or LF in data stream", pos);
                    }
                    return lineBuffer.toString();
                case EOF:
                    return null; // premature EOF -- discards data(?)
                case CR: //we got two (or more) CRs in a row
                    if (tainted == -1)
                        tainted = lineBuffer.length();
                    lineBuffer.append((char) CR);
                    break;
                default: //we got some other character following a CR
                    if (tainted == -1)
                        tainted = lineBuffer.length();
                    lineBuffer.append((char) CR);
                    lineBuffer.append((char) inChar);
                    cr_just_received = false;
                }
            }
        } //while
        throw new LineLengthExceededException("Exceeded maximum line length");
    }//method readLine()

    public int read() throws IOException {
        return in.read();
    }

    public boolean ready() throws IOException {
        return in.available() > 0;
    }

    public int read(char cbuf[], int off, int len) throws IOException {
        byte[] temp = new byte[len];
        int result = in.read(temp, 0, len);
        for (int i = 0; i < result; i++)
            cbuf[i] = (char) temp[i];
        return result;
    }

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