001 /*
002 * Apache License
003 * Version 2.0, January 2004
004 * http://www.apache.org/licenses/
005 *
006 * Copyright 2008 by chenillekit.org
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 */
014 package org.chenillekit.core.streams;
015
016 import java.io.IOException;
017 import java.io.InputStream;
018 import java.io.Reader;
019
020 /**
021 * Adapts a <code>Reader</code> as an <code>InputStream</code>.
022 * Adapted from <CODE>StringInputStream</CODE>.
023 * <p/>
024 * stolen from org.apache.tools.ant.util.ReaderInputStream
025 *
026 */
027 public class ReaderInputStream extends InputStream {
028
029 /** Source Reader */
030 private Reader in;
031
032 private String encoding = System.getProperty("file.encoding");
033
034 private byte[] slack;
035
036 private int begin;
037
038 /**
039 * Construct a <CODE>ReaderInputStream</CODE>
040 * for the specified <CODE>Reader</CODE>.
041 *
042 * @param reader <CODE>Reader</CODE>. Must not be <code>null</code>.
043 */
044 public ReaderInputStream(Reader reader) {
045 in = reader;
046 }
047
048 /**
049 * Construct a <CODE>ReaderInputStream</CODE>
050 * for the specified <CODE>Reader</CODE>,
051 * with the specified encoding.
052 *
053 * @param reader non-null <CODE>Reader</CODE>.
054 * @param encoding non-null <CODE>String</CODE> encoding.
055 */
056 public ReaderInputStream(Reader reader, String encoding) {
057 this(reader);
058 if (encoding == null) {
059 throw new IllegalArgumentException("encoding must not be null");
060 } else {
061 this.encoding = encoding;
062 }
063 }
064
065 /**
066 * Reads from the <CODE>Reader</CODE>, returning the same value.
067 *
068 * @return the value of the next character in the <CODE>Reader</CODE>.
069 *
070 * @exception java.io.IOException if the original <code>Reader</code> fails to be read
071 */
072 public synchronized int read() throws IOException {
073 if (in == null) {
074 throw new IOException("Stream Closed");
075 }
076
077 byte result;
078 if (slack != null && begin < slack.length) {
079 result = slack[begin];
080 if (++begin == slack.length) {
081 slack = null;
082 }
083 } else {
084 byte[] buf = new byte[1];
085 if (read(buf, 0, 1) <= 0) {
086 result = -1;
087 }
088 result = buf[0];
089 }
090
091 if (result < -1) {
092 result+= 256;
093 }
094
095 return result;
096 }
097
098 /**
099 * Reads from the <code>Reader</code> into a byte array
100 *
101 * @param b the byte array to read into
102 * @param off the offset in the byte array
103 * @param len the length in the byte array to fill
104 * @return the actual number read into the byte array, -1 at
105 * the end of the stream
106 * @exception java.io.IOException if an error occurs
107 */
108 public synchronized int read(byte[] b, int off, int len)
109 throws IOException {
110 if (in == null) {
111 throw new IOException("Stream Closed");
112 }
113
114 while (slack == null) {
115 char[] buf = new char[len]; // might read too much
116 int n = in.read(buf);
117 if (n == -1) {
118 return -1;
119 }
120 if (n > 0) {
121 slack = new String(buf, 0, n).getBytes(encoding);
122 begin = 0;
123 }
124 }
125
126 if (len > slack.length - begin) {
127 len = slack.length - begin;
128 }
129
130 System.arraycopy(slack, begin, b, off, len);
131
132 if ((begin += len) >= slack.length) {
133 slack = null;
134 }
135
136 return len;
137 }
138
139 /**
140 * Marks the read limit of the StringReader.
141 *
142 * @param limit the maximum limit of bytes that can be read before the
143 * mark position becomes invalid
144 */
145 public synchronized void mark(final int limit) {
146 try {
147 in.mark(limit);
148 } catch (IOException ioe) {
149 throw new RuntimeException(ioe.getMessage());
150 }
151 }
152
153
154 /**
155 * @return the current number of bytes ready for reading
156 * @exception java.io.IOException if an error occurs
157 */
158 public synchronized int available() throws IOException {
159 if (in == null) {
160 throw new IOException("Stream Closed");
161 }
162 if (slack != null) {
163 return slack.length - begin;
164 }
165 if (in.ready()) {
166 return 1;
167 } else {
168 return 0;
169 }
170 }
171
172 /**
173 * @return false - mark is not supported
174 */
175 public boolean markSupported () {
176 return false; // would be imprecise
177 }
178
179 /**
180 * Resets the StringReader.
181 *
182 * @exception java.io.IOException if the StringReader fails to be reset
183 */
184 public synchronized void reset() throws IOException {
185 if (in == null) {
186 throw new IOException("Stream Closed");
187 }
188 slack = null;
189 in.reset();
190 }
191
192 /**
193 * Closes the Stringreader.
194 *
195 * @exception java.io.IOException if the original StringReader fails to be closed
196 */
197 public synchronized void close() throws IOException {
198 in.close();
199 slack = null;
200 in = null;
201 }
202 }