View Javadoc
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 }