Added toString
[mime4j.git] / src / main / java / org / apache / james / mime4j / MimeBoundaryInputStream.java
blobabc113a89be1aca06c5f506c5db8b28e8c3960f6
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 ****************************************************************/
20 package org.apache.james.mime4j;
22 import java.io.IOException;
24 /**
25 * Stream that constrains itself to a single MIME body part.
26 * After the stream ends (i.e. read() returns -1) {@link #isLastPart()}
27 * can be used to determine if a final boundary has been seen or not.
29 * @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $
31 public class MimeBoundaryInputStream extends BufferingInputStream {
33 private final InputBuffer buffer;
34 private final byte[] boundary;
36 private boolean eof;
37 private int limit;
38 private boolean atBoundary;
39 private int boundaryLen;
40 private boolean lastPart;
41 private boolean completed;
43 /**
44 * Creates a new MimeBoundaryInputStream.
45 * @param s The underlying stream.
46 * @param boundary Boundary string (not including leading hyphens).
48 public MimeBoundaryInputStream(InputBuffer inbuffer, String boundary)
49 throws IOException {
50 this.buffer = inbuffer;
51 this.eof = false;
52 this.limit = 0;
53 this.atBoundary = false;
54 this.boundaryLen = 0;
55 this.lastPart = false;
56 this.completed = false;
58 this.boundary = new byte[boundary.length() + 2];
59 this.boundary[0] = (byte) '-';
60 this.boundary[1] = (byte) '-';
61 for (int i = 0; i < boundary.length(); i++) {
62 byte ch = (byte) boundary.charAt(i);
63 if (ch == '\r' || ch == '\n') {
64 throw new IllegalArgumentException("Boundary may not contain CR or LF");
66 this.boundary[i + 2] = ch;
68 fillBuffer();
71 /**
72 * Closes the underlying stream.
74 * @throws IOException on I/O errors.
76 public void close() throws IOException {
79 /**
80 * @see java.io.InputStream#markSupported()
82 public boolean markSupported() {
83 return false;
86 /**
87 * @see java.io.InputStream#read()
89 public int read() throws IOException {
90 if (completed) {
91 return -1;
93 if (endOfStream() && !hasData()) {
94 skipBoundary();
95 return -1;
97 for (;;) {
98 if (hasData()) {
99 return buffer.read();
100 } else if (endOfStream()) {
101 skipBoundary();
102 return -1;
104 fillBuffer();
108 public int read(byte[] b, int off, int len) throws IOException {
109 if (completed) {
110 return -1;
112 if (endOfStream() && !hasData()) {
113 skipBoundary();
114 return -1;
116 fillBuffer();
117 if (!hasData()) {
118 return 0;
120 int chunk = Math.min(len, limit - buffer.pos());
121 return buffer.read(b, off, chunk);
124 public int readLine(final ByteArrayBuffer dst) throws IOException {
125 if (dst == null) {
126 throw new IllegalArgumentException("Destination buffer may not be null");
128 if (completed) {
129 return -1;
131 if (endOfStream() && !hasData()) {
132 skipBoundary();
133 return -1;
136 int total = 0;
137 boolean found = false;
138 int bytesRead = 0;
139 while (!found) {
140 if (!hasData()) {
141 bytesRead = fillBuffer();
142 if (!hasData() && endOfStream()) {
143 skipBoundary();
144 bytesRead = -1;
145 break;
148 int len = this.limit - this.buffer.pos();
149 int i = this.buffer.indexOf((byte)'\n', this.buffer.pos(), len);
150 int chunk;
151 if (i != -1) {
152 found = true;
153 chunk = i + 1 - this.buffer.pos();
154 } else {
155 chunk = len;
157 if (chunk > 0) {
158 dst.append(this.buffer.buf(), this.buffer.pos(), chunk);
159 this.buffer.skip(chunk);
160 total += chunk;
163 if (total == 0 && bytesRead == -1) {
164 return -1;
165 } else {
166 return total;
170 private boolean endOfStream() {
171 return eof || atBoundary;
174 private boolean hasData() {
175 return limit > buffer.pos() && limit < buffer.limit();
178 private int fillBuffer() throws IOException {
179 if (eof) {
180 return -1;
182 int bytesRead;
183 if (!hasData()) {
184 bytesRead = buffer.fillBuffer();
185 } else {
186 bytesRead = 0;
188 eof = bytesRead == -1;
190 int i = buffer.indexOf(boundary);
191 if (i != -1) {
192 limit = i;
193 atBoundary = true;
194 calculateBoundaryLen();
195 } else {
196 if (eof) {
197 limit = buffer.limit();
198 } else {
199 limit = buffer.limit() - (boundary.length + 1);
200 // \r\n + (boundary - one char)
203 return bytesRead;
206 private void calculateBoundaryLen() throws IOException {
207 boundaryLen = boundary.length;
208 int len = limit - buffer.pos();
209 if (len > 0) {
210 if (buffer.charAt(limit - 1) == '\n') {
211 boundaryLen++;
212 limit--;
215 if (len > 1) {
216 if (buffer.charAt(limit - 1) == '\r') {
217 boundaryLen++;
218 limit--;
223 private void skipBoundary() throws IOException {
224 if (!completed) {
225 completed = true;
226 buffer.skip(boundaryLen);
227 for (;;) {
228 if (buffer.length() > 1) {
229 int ch1 = buffer.charAt(buffer.pos());
230 int ch2 = buffer.charAt(buffer.pos() + 1);
231 if (ch1 == '-' && ch2 == '-') {
232 this.lastPart = true;
233 buffer.skip(2);
235 skipLineDelimiter();
236 break;
237 } else {
238 fillBuffer();
240 if (eof) {
241 break;
247 private void skipLineDelimiter() {
248 int ch1 = 0;
249 int ch2 = 0;
250 int len = buffer.length();
251 if (len > 0) {
252 ch1 = buffer.charAt(buffer.pos());
254 if (len > 1) {
255 ch2 = buffer.charAt(buffer.pos() + 1);
257 if (ch1 == '\r' && ch2 == '\n') {
258 buffer.skip(2);
259 } else if (ch1 == '\n') {
260 buffer.skip(1);
264 public boolean isLastPart() {
265 return lastPart;
268 public boolean eof() {
269 return eof && !buffer.hasBufferedData();
272 public String toString() {
273 final StringBuffer buffer = new StringBuffer("MimeBoundaryInputStream, boundary ");
274 for (int i = 0; i < boundary.length; i++) {
275 buffer.append((char) boundary[i]);
277 return buffer.toString();