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 { 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 */ 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 */ 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 */ 88 public void close() throws IOException { 89 in.close(); 90 } 91 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 105 private void unreadByte(int c) throws IOException { 106 in.unread(c); 107 pos--; 108 } 109 110 /** 111 * @see java.io.InputStream#read() 112 */ 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