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>.
26   * Adapted from <CODE>StringInputStream</CODE>.
27   *
28   */
29  public class ReaderInputStream extends InputStream {
30  
31      /** Source Reader */
32      private Reader in;
33  
34      private String encoding = System.getProperty("file.encoding");
35  
36      private byte[] slack;
37  
38      private int begin;
39  
40      /**
41       * Construct a <CODE>ReaderInputStream</CODE>
42       * for the specified <CODE>Reader</CODE>.
43       *
44       * @param reader   <CODE>Reader</CODE>.  Must not be <code>null</code>.
45       */
46      public ReaderInputStream(Reader reader) {
47          in = reader;
48      }
49  
50      /**
51       * Construct a <CODE>ReaderInputStream</CODE>
52       * for the specified <CODE>Reader</CODE>,
53       * with the specified encoding.
54       *
55       * @param reader     non-null <CODE>Reader</CODE>.
56       * @param encoding   non-null <CODE>String</CODE> encoding.
57       */
58      public ReaderInputStream(Reader reader, String encoding) {
59          this(reader);
60          if (encoding == null) {
61              throw new IllegalArgumentException("encoding must not be null");
62          } else {
63              this.encoding = encoding;
64          }
65      }
66  
67      /**
68       * Reads from the <CODE>Reader</CODE>, returning the same value.
69       *
70       * @return the value of the next character in the <CODE>Reader</CODE>.
71       *
72       * @exception IOException 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  the byte array to read into
104      * @param off the offset in the byte array
105      * @param len the length in the byte array to fill
106      * @return the actual number read into the byte array, -1 at
107      *         the end of the stream
108      * @exception IOException if an error occurs
109      */
110     public synchronized int read(byte[] b, int off, int len)
111         throws IOException {
112         if (in == null) {
113             throw new IOException("Stream Closed");
114         }
115 
116         while (slack == null) {
117             char[] buf = new char[len]; // might read too much
118             int n = in.read(buf);
119             if (n == -1) {
120                 return -1;
121             }
122             if (n > 0) {
123                 slack = new String(buf, 0, n).getBytes(encoding);
124                 begin = 0;
125             }
126         }
127 
128         if (len > slack.length - begin) {
129             len = slack.length - begin;
130         }
131 
132         System.arraycopy(slack, begin, b, off, len);
133 
134         if ((begin += len) >= slack.length) {
135             slack = null;
136         }
137 
138         return len;
139     }
140 
141     /**
142      * Marks the read limit of the StringReader.
143      *
144      * @param limit the maximum limit of bytes that can be read before the
145      *              mark position becomes invalid
146      */
147     public synchronized void mark(final int limit) {
148         try {
149             in.mark(limit);
150         } catch (IOException ioe) {
151             throw new RuntimeException(ioe.getMessage());
152         }
153     }
154 
155 
156     /**
157      * @return   the current number of bytes ready for reading
158      * @exception IOException if an error occurs
159      */
160     public synchronized int available() throws IOException {
161         if (in == null) {
162             throw new IOException("Stream Closed");
163         }
164         if (slack != null) {
165             return slack.length - begin;
166         }
167         if (in.ready()) {
168             return 1;
169         } else {
170             return 0;
171         }
172     }
173 
174     /**
175      * @return false - mark is not supported
176      */
177     public boolean markSupported () {
178         return false;   // would be imprecise
179     }
180 
181     /**
182      * Resets the StringReader.
183      *
184      * @exception IOException if the StringReader fails to be reset
185      */
186     public synchronized void reset() throws IOException {
187         if (in == null) {
188             throw new IOException("Stream Closed");
189         }
190         slack = null;
191         in.reset();
192     }
193 
194     /**
195      * Closes the Stringreader.
196      *
197      * @exception IOException if the original StringReader fails to be closed
198      */
199     public synchronized void close() throws IOException {
200         if (in != null) {
201             in.close();
202             slack = null;
203             in = null;
204         }
205     }
206 }