Fix the thread safe problem
[IRC.git] / Robust / src / ClassLibrary / MGC / gnu / InputStreamReader.java
1 /* InputStreamReader.java -- Reader than transforms bytes to chars
2    Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11  
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39
40 package java.io;
41
42 /**
43  * This class reads characters from a byte input stream.   The characters
44  * read are converted from bytes in the underlying stream by a 
45  * decoding layer.  The decoding layer transforms bytes to chars according
46  * to an encoding standard.  There are many available encodings to choose 
47  * from.  The desired encoding can either be specified by name, or if no
48  * encoding is selected, the system default encoding will be used.  The
49  * system default encoding name is determined from the system property
50  * <code>file.encoding</code>.  The only encodings that are guaranteed to 
51  * be availalbe are "8859_1" (the Latin-1 character set) and "UTF8".
52  * Unforunately, Java does not provide a mechanism for listing the
53  * ecodings that are supported in a given implementation.
54  * <p>
55  * Here is a list of standard encoding names that may be available:
56  * <p>
57  * <ul>
58  * <li>8859_1 (ISO-8859-1/Latin-1)</li>
59  * <li>8859_2 (ISO-8859-2/Latin-2)</li>
60  * <li>8859_3 (ISO-8859-3/Latin-3)</li>
61  * <li>8859_4 (ISO-8859-4/Latin-4)</li>
62  * <li>8859_5 (ISO-8859-5/Latin-5)</li>
63  * <li>8859_6 (ISO-8859-6/Latin-6)</li>
64  * <li>8859_7 (ISO-8859-7/Latin-7)</li>
65  * <li>8859_8 (ISO-8859-8/Latin-8)</li>
66  * <li>8859_9 (ISO-8859-9/Latin-9)</li>
67  * <li>ASCII (7-bit ASCII)</li>
68  * <li>UTF8 (UCS Transformation Format-8)</li>
69  * <li>More later</li>
70  * </ul>
71  * <p>
72  * It is recommended that applications do not use 
73  * <code>InputStreamReader</code>'s
74  * directly.  Rather, for efficiency purposes, an object of this class
75  * should be wrapped by a <code>BufferedReader</code>.
76  * <p>
77  * Due to a deficiency the Java class library design, there is no standard
78  * way for an application to install its own byte-character encoding.
79  *
80  * @see BufferedReader
81  * @see InputStream
82  *
83  * @author Robert Schuster
84  * @author Aaron M. Renn (arenn@urbanophile.com)
85  * @author Per Bothner (bothner@cygnus.com)
86  * @date April 22, 1998.  
87  */
88 public class InputStreamReader extends Reader
89 {
90   /**
91    * The input stream.
92    */
93   private InputStream in;
94
95   /**
96    * The charset decoder.
97    */
98   //private CharsetDecoder decoder;
99
100   /**
101    * End of stream reached.
102    */
103   private boolean isDone = false;
104
105   /**
106    * Need this.
107    */
108   //private float maxBytesPerChar;
109
110   /**
111    * Buffer holding surplus loaded bytes (if any)
112    */
113   //private ByteBuffer byteBuffer;
114
115   /**
116    * java.io canonical name of the encoding.
117    */
118   //private String encoding;
119
120   /**
121    * We might decode to a 2-char UTF-16 surrogate, which won't fit in the
122    * output buffer. In this case we need to save the surrogate char.
123    */
124   //private char savedSurrogate;
125   //private boolean hasSavedSurrogate = false;
126
127   /**
128    * A byte array to be reused in read(byte[], int, int).
129    */
130   //private byte[] bytesCache;
131
132   /**
133    * Locks the bytesCache above in read(byte[], int, int).
134    */
135   //private Object cacheLock = new Object();
136
137   /**
138    * This method initializes a new instance of <code>InputStreamReader</code>
139    * to read from the specified stream using the default encoding.
140    *
141    * @param in The <code>InputStream</code> to read from 
142    */
143   public InputStreamReader(InputStream in)
144   {
145     if (in == null)
146       throw new /*NullPointer*/Exception("NullPointerException");
147     this.in = in;
148     /*try 
149         { 
150           encoding = SystemProperties.getProperty("file.encoding");
151           // Don't use NIO if avoidable
152           if(EncodingHelper.isISOLatin1(encoding))
153             {
154               encoding = "ISO8859_1";
155               maxBytesPerChar = 1f;
156               decoder = null;
157               return;
158             }
159           Charset cs = EncodingHelper.getCharset(encoding);
160           decoder = cs.newDecoder();
161           encoding = EncodingHelper.getOldCanonical(cs.name());
162           try {
163               maxBytesPerChar = cs.newEncoder().maxBytesPerChar();
164           } catch(UnsupportedOperationException _){
165               maxBytesPerChar = 1f;
166           } 
167           decoder.onMalformedInput(CodingErrorAction.REPLACE);
168           decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
169           decoder.reset();
170         } catch(RuntimeException e) {
171           encoding = "ISO8859_1";
172           maxBytesPerChar = 1f;
173           decoder = null;
174         } catch(UnsupportedEncodingException e) {
175           encoding = "ISO8859_1";
176           maxBytesPerChar = 1f;
177           decoder = null;
178         }*/
179   }
180
181   /**
182    * This method initializes a new instance of <code>InputStreamReader</code>
183    * to read from the specified stream using a caller supplied character
184    * encoding scheme.  Note that due to a deficiency in the Java language
185    * design, there is no way to determine which encodings are supported.
186    * 
187    * @param in The <code>InputStream</code> to read from
188    * @param encoding_name The name of the encoding scheme to use
189    *
190    * @exception UnsupportedEncodingException If the encoding scheme 
191    * requested is not available.
192    */
193   /*public InputStreamReader(InputStream in, String encoding_name)
194     throws UnsupportedEncodingException
195   {
196     if (in == null
197         || encoding_name == null)
198       throw new NullPointerException();
199     
200     this.in = in;
201     // Don't use NIO if avoidable
202     if(EncodingHelper.isISOLatin1(encoding_name))
203       {
204         encoding = "ISO8859_1";
205         maxBytesPerChar = 1f;
206         decoder = null;
207         return;
208       }
209     try {
210       Charset cs = EncodingHelper.getCharset(encoding_name);
211       try {
212         maxBytesPerChar = cs.newEncoder().maxBytesPerChar();
213       } catch(UnsupportedOperationException _){
214         maxBytesPerChar = 1f;
215       } 
216
217       decoder = cs.newDecoder();
218       decoder.onMalformedInput(CodingErrorAction.REPLACE);
219       decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
220       decoder.reset();
221
222       // The encoding should be the old name, if such exists.
223       encoding = EncodingHelper.getOldCanonical(cs.name());
224     } catch(RuntimeException e) {
225       encoding = "ISO8859_1";
226       maxBytesPerChar = 1f;
227       decoder = null;
228     }
229   }*/
230
231   /**
232    * Creates an InputStreamReader that uses a decoder of the given
233    * charset to decode the bytes in the InputStream into
234    * characters.
235    * 
236    * @since 1.4
237    */
238   /*public InputStreamReader(InputStream in, Charset charset) {
239     if (in == null)
240       throw new NullPointerException();
241     this.in = in;
242     decoder = charset.newDecoder();
243
244     try {
245       maxBytesPerChar = charset.newEncoder().maxBytesPerChar();
246     } catch(UnsupportedOperationException _){
247       maxBytesPerChar = 1f;
248     }
249
250     decoder.onMalformedInput(CodingErrorAction.REPLACE);
251     decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
252     decoder.reset();
253     encoding = EncodingHelper.getOldCanonical(charset.name());
254   }*/
255
256   /**
257    * Creates an InputStreamReader that uses the given charset decoder
258    * to decode the bytes in the InputStream into characters.
259    * 
260    * @since 1.4
261    */
262   /*public InputStreamReader(InputStream in, CharsetDecoder decoder) {
263     if (in == null)
264       throw new NullPointerException();
265     this.in = in;
266     this.decoder = decoder;
267
268     Charset charset = decoder.charset();
269     try {
270       if (charset == null)
271         maxBytesPerChar = 1f;
272       else
273         maxBytesPerChar = charset.newEncoder().maxBytesPerChar();
274     } catch(UnsupportedOperationException _){
275         maxBytesPerChar = 1f;
276     } 
277
278     decoder.onMalformedInput(CodingErrorAction.REPLACE);
279     decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
280     decoder.reset();
281     if (charset == null)
282       encoding = "US-ASCII";
283     else
284       encoding = EncodingHelper.getOldCanonical(decoder.charset().name());      
285   }*/
286   
287   /**
288    * This method closes this stream, as well as the underlying 
289    * <code>InputStream</code>.
290    *
291    * @exception IOException If an error occurs
292    */
293   /*public void close() //throws IOException
294   {
295     synchronized (lock)
296       {
297         // Makes sure all intermediate data is released by the decoder.
298         if (decoder != null)
299            decoder.reset();
300         if (in != null)
301            in.close();
302         in = null;
303         isDone = true;
304         decoder = null;
305       }
306   }*/
307
308   /**
309    * This method returns the name of the encoding that is currently in use
310    * by this object.  If the stream has been closed, this method is allowed
311    * to return <code>null</code>.
312    *
313    * @return The current encoding name
314    */
315   /*public String getEncoding()
316   {
317     return in != null ? encoding : null;
318   }*/
319
320   /**
321    * This method checks to see if the stream is ready to be read.  It
322    * will return <code>true</code> if is, or <code>false</code> if it is not.
323    * If the stream is not ready to be read, it could (although is not required
324    * to) block on the next read attempt.
325    *
326    * @return <code>true</code> if the stream is ready to be read, 
327    * <code>false</code> otherwise
328    *
329    * @exception IOException If an error occurs
330    */
331   /*public boolean ready() throws IOException
332   {
333     if (in == null)
334       throw new IOException("Reader has been closed");
335     
336     return in.available() != 0;
337   }*/
338
339   /**
340    * This method reads up to <code>length</code> characters from the stream into
341    * the specified array starting at index <code>offset</code> into the
342    * array.
343    *
344    * @param buf The character array to recieve the data read
345    * @param offset The offset into the array to start storing characters
346    * @param length The requested number of characters to read.
347    *
348    * @return The actual number of characters read, or -1 if end of stream.
349    *
350    * @exception IOException If an error occurs
351    */
352   /*public int read(char[] buf, int offset, int length) throws IOException
353   {
354     if (in == null)
355       throw new IOException("Reader has been closed");
356     if (isDone)
357       return -1;
358     if(decoder != null)
359       {
360         int totalBytes = (int)((double) length * maxBytesPerChar);
361         if (byteBuffer != null)
362           totalBytes = Math.max(totalBytes, byteBuffer.remaining());
363         byte[] bytes;
364         // Fetch cached bytes array if available and big enough.
365         synchronized(cacheLock)
366           {
367             bytes = bytesCache;
368             if (bytes == null || bytes.length < totalBytes)
369               bytes = new byte[totalBytes];
370             else
371               bytesCache = null;
372           }
373
374         int remaining = 0;
375         if(byteBuffer != null)
376         {
377             remaining = byteBuffer.remaining();
378             byteBuffer.get(bytes, 0, remaining);
379         }
380         int read;
381         if(totalBytes - remaining > 0)
382           {
383             read = in.read(bytes, remaining, totalBytes - remaining);
384             if(read == -1){
385               read = remaining;
386               isDone = true;
387             } else
388               read += remaining;
389           } else 
390             read = remaining;
391         byteBuffer = ByteBuffer.wrap(bytes, 0, read);   
392         CharBuffer cb = CharBuffer.wrap(buf, offset, length);
393         int startPos = cb.position();
394
395         if(hasSavedSurrogate){
396             hasSavedSurrogate = false;
397             cb.put(savedSurrogate);
398             read++;
399         }
400
401         CoderResult cr = decoder.decode(byteBuffer, cb, isDone);
402         decoder.reset();
403         // 1 char remains which is the first half of a surrogate pair.
404         if(cr.isOverflow() && cb.hasRemaining()){
405             CharBuffer overflowbuf = CharBuffer.allocate(2);
406             cr = decoder.decode(byteBuffer, overflowbuf, isDone);
407             overflowbuf.flip();
408             if(overflowbuf.hasRemaining())
409             {
410               cb.put(overflowbuf.get());
411               savedSurrogate = overflowbuf.get();
412               hasSavedSurrogate = true;     
413               isDone = false;
414             }
415         }
416
417         if(byteBuffer.hasRemaining()) {
418             byteBuffer.compact();
419             byteBuffer.flip();    
420             isDone = false;
421         } else
422             byteBuffer = null;
423
424         read = cb.position() - startPos;
425
426         // Put cached bytes array back if we are finished and the cache
427         // is null or smaller than the used bytes array.
428         synchronized (cacheLock)
429           {
430             if (byteBuffer == null
431                 && (bytesCache == null || bytesCache.length < bytes.length))
432               bytesCache = bytes;
433           }
434         return (read <= 0) ? -1 : read;
435       }
436     else
437       {
438         byte[] bytes;
439         // Fetch cached bytes array if available and big enough.
440         synchronized (cacheLock)
441           {
442             bytes = bytesCache;
443             if (bytes == null || length < bytes.length)
444               bytes = new byte[length];
445             else
446               bytesCache = null;
447           }
448
449         int read = in.read(bytes);
450         for(int i=0;i<read;i++)
451           buf[offset+i] = (char)(bytes[i]&0xFF);
452
453         // Put back byte array into cache if appropriate.
454         synchronized (cacheLock)
455           {
456             if (bytesCache == null || bytesCache.length < bytes.length)
457               bytesCache = bytes;
458           }
459         return read;
460     }
461   }*/
462
463   /**
464    * Reads an char from the input stream and returns it
465    * as an int in the range of 0-65535.  This method also will return -1 if
466    * the end of the stream has been reached.
467    * <p>
468    * This method will block until the char can be read.
469    *
470    * @return The char read or -1 if end of stream
471    *
472    * @exception IOException If an error occurs
473    */
474   /*public int read() throws IOException
475   {
476     char[] buf = new char[1];
477     int count = read(buf, 0, 1);
478     return count > 0 ? buf[0] : -1;
479   }*/
480
481   /**
482    * Skips the specified number of chars in the stream.  It
483    * returns the actual number of chars skipped, which may be less than the
484    * requested amount.
485    *
486    * @param count The requested number of chars to skip
487    *
488    * @return The actual number of chars skipped.
489    *
490    * @exception IOException If an error occurs
491    */
492    /*public long skip(long count) throws IOException
493    {
494      if (in == null)
495        throw new IOException("Reader has been closed");
496      
497      return super.skip(count);
498    }*/
499 }