001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  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,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019
020package org.apache.james.mime4j.io;
021
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.PushbackInputStream;
025
026/**
027 * InputStream which converts <code>\r</code>
028 * bytes not followed by <code>\n</code> and <code>\n</code> not
029 * preceded by <code>\r</code> to <code>\r\n</code>.
030 */
031public class EOLConvertingInputStream extends InputStream {
032    /** Converts single '\r' to '\r\n' */
033    public static final int CONVERT_CR   = 1;
034    /** Converts single '\n' to '\r\n' */
035    public static final int CONVERT_LF   = 2;
036    /** Converts single '\r' and '\n' to '\r\n' */
037    public static final int CONVERT_BOTH = 3;
038
039    private PushbackInputStream in = null;
040    private int previous = 0;
041    private int flags = CONVERT_BOTH;
042
043    /**
044     * Creates a new <code>EOLConvertingInputStream</code>
045     * instance converting bytes in the given <code>InputStream</code>.
046     * The flag <code>CONVERT_BOTH</code> is the default.
047     *
048     * @param in the <code>InputStream</code> to read from.
049     */
050    public EOLConvertingInputStream(InputStream in) {
051        this(in, CONVERT_BOTH);
052    }
053    /**
054     * Creates a new <code>EOLConvertingInputStream</code>
055     * instance converting bytes in the given <code>InputStream</code>.
056     *
057     * @param in the <code>InputStream</code> to read from.
058     * @param flags one of <code>CONVERT_CR</code>, <code>CONVERT_LF</code> or
059     *        <code>CONVERT_BOTH</code>.
060     */
061    public EOLConvertingInputStream(InputStream in, int flags) {
062        super();
063
064        this.in = new PushbackInputStream(in, 2);
065        this.flags = flags;
066    }
067
068    /**
069     * Closes the underlying stream.
070     *
071     * @throws IOException on I/O errors.
072     */
073    @Override
074    public void close() throws IOException {
075        in.close();
076    }
077
078    /**
079     * @see java.io.InputStream#read()
080     */
081    @Override
082    public int read() throws IOException {
083        int b = in.read();
084
085        if (b == -1) {
086            return -1;
087        }
088
089        if ((flags & CONVERT_CR) != 0 && b == '\r') {
090            int c = in.read();
091            if (c != -1) {
092                in.unread(c);
093            }
094            if (c != '\n') {
095                in.unread('\n');
096            }
097        } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') {
098            b = '\r';
099            in.unread('\n');
100        }
101
102        previous = b;
103
104        return b;
105    }
106
107}