1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.any23.util;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.Reader;
23
24 /**
25 * Adapts a <code>Reader</code> as an <code>InputStream</code>. Adapted from <CODE>StringInputStream</CODE>.
26 *
27 */
28 public class ReaderInputStream extends InputStream {
29
30 /** Source Reader */
31 private Reader in;
32
33 private String encoding = System.getProperty("file.encoding");
34
35 private byte[] slack;
36
37 private int begin;
38
39 /**
40 * Construct a <CODE>ReaderInputStream</CODE> for the specified <CODE>Reader</CODE>.
41 *
42 * @param reader
43 * <CODE>Reader</CODE>. Must not be <code>null</code>.
44 */
45 public ReaderInputStream(Reader reader) {
46 in = reader;
47 }
48
49 /**
50 * Construct a <CODE>ReaderInputStream</CODE> for the specified <CODE>Reader</CODE>, with the specified encoding.
51 *
52 * @param reader
53 * non-null <CODE>Reader</CODE>.
54 * @param encoding
55 * non-null <CODE>String</CODE> encoding.
56 */
57 public ReaderInputStream(Reader reader, String encoding) {
58 this(reader);
59 if (encoding == null) {
60 throw new IllegalArgumentException("encoding must not be null");
61 } else {
62 this.encoding = encoding;
63 }
64 }
65
66 /**
67 * Reads from the <CODE>Reader</CODE>, returning the same value.
68 *
69 * @return the value of the next character in the <CODE>Reader</CODE>.
70 *
71 * @exception IOException
72 * if the original <code>Reader</code> fails to be read
73 */
74 public synchronized int read() throws IOException {
75 if (in == null) {
76 throw new IOException("Stream Closed");
77 }
78
79 byte result;
80 if (slack != null && begin < slack.length) {
81 result = slack[begin];
82 if (++begin == slack.length) {
83 slack = null;
84 }
85 } else {
86 byte[] buf = new byte[1];
87 if (read(buf, 0, 1) <= 0) {
88 result = -1;
89 }
90 result = buf[0];
91 }
92
93 if (result < -1) {
94 result += 256;
95 }
96
97 return result;
98 }
99
100 /**
101 * Reads from the <code>Reader</code> into a byte array
102 *
103 * @param b
104 * the byte array to read into
105 * @param off
106 * the offset in the byte array
107 * @param len
108 * the length in the byte array to fill
109 *
110 * @return the actual number read into the byte array, -1 at the end of the stream
111 *
112 * @exception IOException
113 * if an error occurs
114 */
115 public synchronized int read(byte[] b, int off, int len) throws IOException {
116 if (in == null) {
117 throw new IOException("Stream Closed");
118 }
119
120 while (slack == null) {
121 char[] buf = new char[len]; // might read too much
122 int n = in.read(buf);
123 if (n == -1) {
124 return -1;
125 }
126 if (n > 0) {
127 slack = new String(buf, 0, n).getBytes(encoding);
128 begin = 0;
129 }
130 }
131
132 if (len > slack.length - begin) {
133 len = slack.length - begin;
134 }
135
136 System.arraycopy(slack, begin, b, off, len);
137
138 if ((begin += len) >= slack.length) {
139 slack = null;
140 }
141
142 return len;
143 }
144
145 /**
146 * Marks the read limit of the StringReader.
147 *
148 * @param limit
149 * the maximum limit of bytes that can be read before the mark position becomes invalid
150 */
151 public synchronized void mark(final int limit) {
152 try {
153 in.mark(limit);
154 } catch (IOException ioe) {
155 throw new RuntimeException(ioe.getMessage());
156 }
157 }
158
159 /**
160 * @return the current number of bytes ready for reading
161 *
162 * @exception IOException
163 * if an error occurs
164 */
165 public synchronized int available() throws IOException {
166 if (in == null) {
167 throw new IOException("Stream Closed");
168 }
169 if (slack != null) {
170 return slack.length - begin;
171 }
172 if (in.ready()) {
173 return 1;
174 } else {
175 return 0;
176 }
177 }
178
179 /**
180 * @return false - mark is not supported
181 */
182 public boolean markSupported() {
183 return false; // would be imprecise
184 }
185
186 /**
187 * Resets the StringReader.
188 *
189 * @exception IOException
190 * if the StringReader fails to be reset
191 */
192 public synchronized void reset() throws IOException {
193 if (in == null) {
194 throw new IOException("Stream Closed");
195 }
196 slack = null;
197 in.reset();
198 }
199
200 /**
201 * Closes the Stringreader.
202 *
203 * @exception IOException
204 * if the original StringReader fails to be closed
205 */
206 public synchronized void close() throws IOException {
207 if (in != null) {
208 in.close();
209 slack = null;
210 in = null;
211 }
212 }
213 }