1/****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one   *
3 * or more contributor license agreements.  See the NOTICE file *
4 * distributed with this work for additional information        *
5 * regarding copyright ownership.  The ASF licenses this file   *
6 * to you under the Apache License, Version 2.0 (the            *
7 * "License"); you may not use this file except in compliance   *
8 * with the License.  You may obtain a copy of the License at   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
12 * Unless required by applicable law or agreed to in writing,   *
13 * software distributed under the License is distributed on an  *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15 * KIND, either express or implied.  See the License for the    *
16 * specific language governing permissions and limitations      *
17 * under the License.                                           *
18 ****************************************************************/
19
20package org.apache.james.mime4j;
21
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.PushbackInputStream;
25
26/**
27 * InputStream which converts <code>\r</code>
28 * bytes not followed by <code>\n</code> and <code>\n</code> not
29 * preceded by <code>\r</code> to <code>\r\n</code>.
30 *
31 *
32 * @version $Id: EOLConvertingInputStream.java,v 1.4 2004/11/29 13:15:42 ntherning Exp $
33 */
34public class EOLConvertingInputStream extends InputStream {
35    /** Converts single '\r' to '\r\n' */
36    public static final int CONVERT_CR   = 1;
37    /** Converts single '\n' to '\r\n' */
38    public static final int CONVERT_LF   = 2;
39    /** Converts single '\r' and '\n' to '\r\n' */
40    public static final int CONVERT_BOTH = 3;
41
42    private PushbackInputStream in = null;
43    private int previous = 0;
44    private int flags = CONVERT_BOTH;
45
46    /**
47     * Creates a new <code>EOLConvertingInputStream</code>
48     * instance converting bytes in the given <code>InputStream</code>.
49     * The flag <code>CONVERT_BOTH</code> is the default.
50     *
51     * @param in the <code>InputStream</code> to read from.
52     */
53    public EOLConvertingInputStream(InputStream in) {
54        this(in, CONVERT_BOTH);
55    }
56    /**
57     * Creates a new <code>EOLConvertingInputStream</code>
58     * instance converting bytes in the given <code>InputStream</code>.
59     *
60     * @param in the <code>InputStream</code> to read from.
61     * @param flags one of <code>CONVERT_CR</code>, <code>CONVERT_LF</code> or
62     *        <code>CONVERT_BOTH</code>.
63     */
64    public EOLConvertingInputStream(InputStream in, int flags) {
65        super();
66
67        this.in = new PushbackInputStream(in, 2);
68        this.flags = flags;
69    }
70
71    /**
72     * Closes the underlying stream.
73     *
74     * @throws IOException on I/O errors.
75     */
76    public void close() throws IOException {
77        in.close();
78    }
79
80    /**
81     * @see java.io.InputStream#read()
82     */
83    public int read() throws IOException {
84        int b = in.read();
85
86        if (b == -1) {
87            return -1;
88        }
89
90        if ((flags & CONVERT_CR) != 0 && b == '\r') {
91            int c = in.read();
92            if (c != -1) {
93                in.unread(c);
94            }
95            if (c != '\n') {
96                in.unread('\n');
97            }
98        } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') {
99            b = '\r';
100            in.unread('\n');
101        }
102
103        previous = b;
104
105        return b;
106    }
107
108}
109