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 
20 package org.apache.james.mime4j;
21 
22 import java.io.IOException;
23 import java.io.InputStream;
24 import 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  */
34 public 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     private int size = 0;
46     private int pos = 0;
47     private int nextTenPctPos;
48     private int tenPctSize;
49     private Callback callback;
50 
51     public interface Callback {
report(int bytesRead)52         public void report(int bytesRead);
53     }
54 
55     /**
56      * Creates a new <code>EOLConvertingInputStream</code>
57      * instance converting bytes in the given <code>InputStream</code>.
58      * The flag <code>CONVERT_BOTH</code> is the default.
59      *
60      * @param in the <code>InputStream</code> to read from.
61      */
EOLConvertingInputStream(InputStream _in)62     public EOLConvertingInputStream(InputStream _in) {
63         super();
64         in = new PushbackInputStream(_in, 2);
65     }
66 
67     /**
68      * Creates a new <code>EOLConvertingInputStream</code>
69      * instance converting bytes in the given <code>InputStream</code>.
70      *
71      * @param _in the <code>InputStream</code> to read from.
72      * @param _size the size of the input stream (need not be exact)
73      * @param _callback a callback reporting when each 10% of stream's size is reached
74      */
EOLConvertingInputStream(InputStream _in, int _size, Callback _callback)75     public EOLConvertingInputStream(InputStream _in, int _size, Callback _callback) {
76         this(_in);
77         size = _size;
78         tenPctSize = size / 10;
79         nextTenPctPos = tenPctSize;
80         callback = _callback;
81     }
82 
83     /**
84      * Closes the underlying stream.
85      *
86      * @throws IOException on I/O errors.
87      */
close()88     public void close() throws IOException {
89         in.close();
90     }
91 
readByte()92     private int readByte() throws IOException {
93         int b = in.read();
94         if (b != -1) {
95             if (callback != null && pos++ == nextTenPctPos) {
96                 nextTenPctPos += tenPctSize;
97                 if (callback != null) {
98                     callback.report(pos);
99                 }
100             }
101         }
102         return b;
103     }
104 
unreadByte(int c)105     private void unreadByte(int c) throws IOException {
106         in.unread(c);
107         pos--;
108     }
109 
110     /**
111      * @see java.io.InputStream#read()
112      */
read()113     public int read() throws IOException {
114         int b = readByte();
115 
116         if (b == -1) {
117             pos = size;
118             return -1;
119         }
120 
121         if ((flags & CONVERT_CR) != 0 && b == '\r') {
122             int c = readByte();
123             if (c != -1) {
124                 unreadByte(c);
125             }
126             if (c != '\n') {
127                 unreadByte('\n');
128             }
129         } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') {
130             b = '\r';
131             unreadByte('\n');
132         }
133 
134         previous = b;
135 
136         return b;
137     }
138 
139 }
140