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.util;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24
25 /***
26 * An InputStream class that terminates the stream when it encounters a
27 * particular byte sequence.
28 *
29 * @version 1.0.0, 24/04/1999
30 */
31 public class CharTerminatedInputStream
32 extends InputStream {
33
34 /***
35 * The wrapped input stream
36 */
37 private InputStream in;
38
39 /***
40 * The terminating character array
41 */
42 private int match[];
43
44 /***
45 * An array containing the last N characters read from the stream, where
46 * N is the length of the terminating character array
47 */
48 private int buffer[];
49
50 /***
51 * The number of bytes that have been read that have not been placed
52 * in the internal buffer.
53 */
54 private int pos = 0;
55
56 /***
57 * Whether the terminating sequence has been read from the stream
58 */
59 private boolean endFound = false;
60
61 /***
62 * A constructor for this object that takes a stream to be wrapped
63 * and a terminating character sequence.
64 *
65 * @param in the <code>InputStream</code> to be wrapped
66 * @param terminator the array of characters that will terminate the stream.
67 *
68 * @throws IllegalArgumentException if the terminator array is null or empty
69 */
70 public CharTerminatedInputStream(InputStream in, char[] terminator) {
71 if (terminator == null) {
72 throw new IllegalArgumentException("The terminating character array cannot be null.");
73 }
74 if (terminator.length == 0) {
75 throw new IllegalArgumentException("The terminating character array cannot be of zero length.");
76 }
77 match = new int[terminator.length];
78 buffer = new int[terminator.length];
79 for (int i = 0; i < terminator.length; i++) {
80 match[i] = (int)terminator[i];
81 buffer[i] = (int)terminator[i];
82 }
83 this.in = in;
84 }
85
86 /***
87 * Read a byte off this stream.
88 *
89 * @return the byte read off the stream
90 * @throws IOException if an IOException is encountered while reading off the stream
91 * @throws ProtocolException if the underlying stream returns -1 before the terminator is seen.
92 */
93 public int read() throws IOException {
94 if (endFound) {
95
96 return -1;
97 }
98 if (pos == 0) {
99
100 int b = in.read();
101 if (b == -1) {
102
103 throw new java.net.ProtocolException("pre-mature end of data");
104 }
105 if (b != match[0]) {
106
107 return b;
108 }
109
110
111 buffer[0] = b;
112 pos++;
113 } else {
114 if (buffer[0] != match[0]) {
115
116
117
118 return topChar();
119 }
120
121 }
122
123
124
125
126 for (int i = 0; i < match.length; i++) {
127 if (i >= pos) {
128 int b = in.read();
129 if (b == -1) {
130
131
132
133 return topChar();
134 }
135
136 buffer[pos] = b;
137 pos++;
138 }
139 if (buffer[i] != match[i]) {
140
141 return topChar();
142 }
143 }
144
145 endFound = true;
146 return -1;
147 }
148
149 /***
150 * Private helper method to update the internal buffer of last read characters
151 *
152 * @return the byte that was previously at the front of the internal buffer
153 */
154 private int topChar() {
155 int b = buffer[0];
156 if (pos > 1) {
157
158 System.arraycopy(buffer, 1, buffer, 0, pos - 1);
159 }
160 pos--;
161 return b;
162 }
163 }
164