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    
020    package org.apache.james.mime4j.io;
021    
022    import java.io.IOException;
023    import java.io.InputStream;
024    import 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     */
031    public 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    }