OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [tools/] [gnu/] [classpath/] [tools/] [NotifyingInputStreamReader.java] - Blame information for rev 779

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 779 jeremybenn
/* gnu.classpath.tools.NotifyingInputStreamReader
2
   Copyright (C) 2004 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19
02111-1307 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
package gnu.classpath.tools;
39
 
40
import java.io.IOException;
41
import java.io.InputStream;
42
import java.io.Reader;
43
 
44
import java.nio.ByteBuffer;
45
import java.nio.CharBuffer;
46
 
47
import java.nio.charset.Charset;
48
import java.nio.charset.CharsetDecoder;
49
import java.nio.charset.CoderResult;
50
import java.nio.charset.IllegalCharsetNameException;
51
import java.nio.charset.UnsupportedCharsetException;
52
 
53
import java.util.Iterator;
54
import java.util.LinkedHashSet;
55
import java.util.Set;
56
 
57
/**
58
 *  Similar to {@link java.io.InputStreamReader}, but can give
59
 *  notification when malformed input is encountered.
60
 *
61
 *  <p> Users of this class can register interest in the event of
62
 *  malformed input by calling {@link
63
 *  #addMalformedInputListener}. Each time a run of malformed input
64
 *  data is encountered, all listener objects are notified. For
65
 *  instance, this allows the calling code to inform the user about
66
 *  problems in the input stream. </p>
67
 *
68
 *  <p> <strong>Background:</strong> The default
69
 *  <code>InputStreamReader</code> implementation will ignore
70
 *  malformed input, silently replacing it with the default
71
 *  replacement string (usually the question mark). Alternatively, you
72
 *  can configure a <code>CharsetDecoder</code> for the default
73
 *  <char>InputStreamReader</code> implementation, instructing it to
74
 *  ignore malformed input without replacing it; to replace it with a
75
 *  different string; or to throw an exception when malformed input is
76
 *  encountered. However, you cannot configure an
77
 *  <code>InputStreamReader</code> to gracefully handle malformed data
78
 *  but notify the client code about such
79
 *  problems. <code>NotifyingInputStreamReader</code> fills this
80
 *  gap. </p>
81
 *
82
 *  @author Julian Scheid
83
 */
84
public class NotifyingInputStreamReader
85
   extends Reader
86
{
87
   /**
88
    *  The default size (in bytes) for byteBuf, i.e. the size of data
89
    *  chunks read from the underlying input stream.
90
    */
91
   private static final int DEFAULT_INPUT_BUFFER_SIZE = 64;
92
 
93
   /**
94
    *  The default size (in chars) for charBuf. This should be large
95
    *  enough to hold a decoded chunk of input data for the Charset
96
    *  with the minimum number of bytes per character. Since the
97
    *  minimum number of bytes per character for usual Charsets is
98
    *  one, this number should be identical to
99
    *  DEFAULT_INPUT_BUFFER_SIZE.
100
    */
101
   private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 64;
102
 
103
   /**
104
    *  The underlying InputStream.
105
    */
106
   private InputStream in;
107
 
108
   /**
109
    *  The CharsetDecoder used to decode the underlying stream.
110
    */
111
   private CharsetDecoder decoder;
112
 
113
   /**
114
    *  Holds bytes already read from the underlying stream, but not
115
    *  yet decoded.
116
    */
117
   private ByteBuffer byteBuffer;
118
 
119
   /**
120
    *  Holds characters already decoded, but not yet fetched via
121
    *  read().
122
    */
123
   private CharBuffer charBuffer;
124
 
125
   /**
126
    *  This is the primitive byte array wrapped in byteBuffer for
127
    *  passing to to InputStream.read().
128
    */
129
   private byte[] readBuffer;
130
 
131
   /**
132
    *  Keeps track of the current line number (1-based).
133
    */
134
   private int lineNumber = 1;
135
 
136
   /**
137
    *  Keeps track of the current column number (0-based).
138
    */
139
   private int columnNumber = 0;
140
 
141
   /**
142
    *  Becomes true as soon as EOF has been reached in the underlying
143
    *  InputStream. At this point, byteBuffer contains the last chunk
144
    *  of data from the underlying InputStream.
145
    */
146
   private boolean allInputConsumed = false;
147
 
148
   /**
149
    *  Becomes true as soon as the decoder has been supplied with the
150
    *  last chunk of data from the InputStream after EOF has been
151
    *  reached. At this point, the last chunk of data has been drained
152
    *  from the byteBuffer.
153
    */
154
   private boolean decodingFinished = false;
155
 
156
   /**
157
    *  Becomes true as soon as the decoder has been flushed. At this
158
    *  point, the last chunk of character data has been written to the
159
    *  charBuffer.
160
    */
161
   private boolean flushed = false;
162
 
163
   /**
164
    *  Stores all registered MalformedInputListeners.
165
    */
166
   private Set listeners = new LinkedHashSet();
167
 
168
   /**
169
    *  Initializes a new instance for reading from the given
170
    *  InputStream using the default encoding. The default encoding is
171
    *  currently determined by looking at the system property
172
    *  <code>file.encoding</code>. If this property isn't set,
173
    *  <code>ISO-8859-1</code> is used as a fallback.
174
    *
175
    *  <p>This method should use {@link Charset.defaultCharset()}
176
    *  instead, but this isn't implemented in Classpath at the
177
    *  moment.</p>
178
    *
179
    *  @param in the <code>InputStream</code> to read from.
180
    */
181
   public NotifyingInputStreamReader(InputStream in)
182
   {
183
      this(in, System.getProperty("file.encoding", "ISO-8859-1"));
184
   }
185
 
186
   /**
187
    *  Initializes a new instance for reading from the given
188
    *  InputStream using the specified charset.
189
    *
190
    *  @param in the <code>InputStream</code> to read from.
191
    *  @param charsetName the canonical name or an alias of a
192
    *  <code>Charset</code>.
193
    *
194
    *  @throws IllegalCharsetNameException if there is no
195
    *  <code>Charset</code> with the given canonical name or alias.
196
    *
197
    *  @throws UnsupportedCharsetException if there is no support for
198
    *  the specified <code>Charset</code> in the runtime environment.
199
    */
200
   public NotifyingInputStreamReader(InputStream in, String charsetName)
201
      throws IllegalCharsetNameException, UnsupportedCharsetException
202
   {
203
      this(in, Charset.forName(charsetName));
204
   }
205
 
206
   /**
207
    *  Initializes a new instance for reading from the given
208
    *  InputStream using the specified charset.
209
    *
210
    *  @param in the <code>InputStream</code> to read from.
211
    *  @param charset the <code>Charset</code> to use for decoding
212
    *  characters.
213
    */
214
   public NotifyingInputStreamReader(InputStream in, Charset charset)
215
   {
216
      this(in, charset.newDecoder());
217
   }
218
 
219
   /**
220
    *  Initializes a new instance for reading from the given
221
    *  InputStream using the specified charset decoder.
222
    *
223
    *  <strong>Note:</strong> the
224
    *  <code>NotifyingInputStreamReader</code> will not exhibit the
225
    *  advertised behaviour if you changed the action to take on
226
    *  malformed input in the specified
227
    *  <code>CharsetDecoder</code>. In other words, you should not
228
    *  call {@link CharsetDecoder.onMalformedInput(CodingErrorAction)}
229
    *  on the specified decoder before or while this reader is being
230
    *  used unless you know what you're doing.
231
    *
232
    *  @param in the <code>InputStream</code> to read from.
233
    *  @param charset the <code>CharsetDecoder</code> to use for
234
    *  decoding characters.
235
    */
236
   public NotifyingInputStreamReader(InputStream in, CharsetDecoder decoder)
237
   {
238
      this.in = in;
239
      this.decoder = decoder;
240
      this.charBuffer = CharBuffer.wrap(new char[DEFAULT_INPUT_BUFFER_SIZE]);
241
      this.charBuffer.position(charBuffer.limit());
242
      this.readBuffer = new byte[DEFAULT_OUTPUT_BUFFER_SIZE];
243
      this.byteBuffer = ByteBuffer.wrap(readBuffer);
244
      this.byteBuffer.position(byteBuffer.limit());
245
   }
246
 
247
   public void close()
248
      throws IOException
249
   {
250
      in.close();
251
   }
252
 
253
   /**
254
    *  Fill charBuffer with new character data. This method returns if
255
    *  either the charBuffer has been filled completely with decoded
256
    *  character data, or if EOF is reached in the underlying
257
    *  InputStream. When this method returns, charBuffer is flipped
258
    *  and ready to be read from.
259
    */
260
   private void fillCharBuf()
261
      throws IOException
262
   {
263
      charBuffer.clear();
264
   outer:
265
      while (!flushed) {
266
         CoderResult coderResult;
267
         int charBufferPositionBefore = charBuffer.position();
268
         if (!decodingFinished) {
269
            coderResult = decoder.decode(byteBuffer, charBuffer, allInputConsumed);
270
            if (allInputConsumed) {
271
               decodingFinished = true;
272
            }
273
         }
274
         else {
275
            coderResult = decoder.flush(charBuffer);
276
            flushed = coderResult.isUnderflow();
277
         }
278
 
279
         int charBufferPositionAfter = charBuffer.position();
280
         for (int i=charBufferPositionBefore; i<charBufferPositionAfter; ++i) {
281
            if (10 == charBuffer.get(i)) {
282
               ++ lineNumber;
283
               columnNumber = 0;
284
            }
285
            else {
286
               ++ columnNumber;
287
            }
288
         }
289
         if (coderResult.isOverflow()) {
290
            break;
291
         }
292
         else if (coderResult.isUnderflow()) {
293
            if (!allInputConsumed) {
294
               int nRemainingBytes = 0;
295
               if (byteBuffer.position() > 0) {
296
                  nRemainingBytes = Math.max(0, byteBuffer.limit() - byteBuffer.position());
297
               }
298
               if (nRemainingBytes > 0) {
299
                  byteBuffer.get(readBuffer, 0, nRemainingBytes);
300
               }
301
               byteBuffer.rewind();
302
               int nread = in.read(readBuffer, nRemainingBytes,
303
                                   readBuffer.length - nRemainingBytes);
304
               if (nread < 0) {
305
                  allInputConsumed = true;
306
               }
307
               byteBuffer.limit(nRemainingBytes + Math.max(0, nread));
308
            }
309
            else {
310
               break;
311
            }
312
         }
313
         else if (coderResult.isMalformed()) {
314
            fireMalformedInputEncountered(coderResult.length());
315
            String replacement = decoder.replacement();
316
            for (int i=0; i<coderResult.length(); ++i) {
317
               if (charBuffer.remaining() > replacement.length()) {
318
                  charBuffer.put(replacement);
319
                  byteBuffer.position(byteBuffer.position() + 1);
320
                  columnNumber ++;
321
               }
322
               else {
323
                  break outer;
324
               }
325
            }
326
         }
327
         else if (coderResult.isUnmappable()) {
328
            // This should not happen, since unmappable input bytes
329
            // trigger a "malformed" event instead.
330
            coderResult.throwException();
331
         }
332
         else {
333
            // This should only happen if run in a future environment
334
            // where additional events apart from underflow, overflow,
335
            // malformed and unmappable can be generated.
336
            coderResult.throwException();
337
         }
338
      }
339
      charBuffer.flip();
340
   }
341
 
342
   /**
343
    *  Fire a MalformedInputEvent, notifying all registered listeners.
344
    */
345
   private void fireMalformedInputEncountered(int length)
346
   {
347
      MalformedInputEvent event
348
         = new MalformedInputEvent(this, lineNumber, columnNumber, length);
349
      Iterator it = listeners.iterator();
350
      while (it.hasNext()) {
351
         MalformedInputListener listener
352
            = (MalformedInputListener)it.next();
353
         listener.malformedInputEncountered(event);
354
      }
355
   }
356
 
357
   public int read(char[] cbuf, int offset, int length)
358
      throws IOException
359
   {
360
      if (flushed) {
361
         return -1;
362
      }
363
      else {
364
         int nread = 0;
365
         while (nread < length && !flushed) {
366
            while (charBuffer.hasRemaining() && nread < length) {
367
               int copyLen = Math.min(length - nread,
368
                                      charBuffer.remaining());
369
               charBuffer.get(cbuf, offset + nread, copyLen);
370
               nread += copyLen;
371
            }
372
            if (nread < length) {
373
               fillCharBuf();
374
            }
375
         }
376
         return nread;
377
      }
378
   }
379
 
380
   public int read()
381
      throws IOException
382
   {
383
      while (!flushed) {
384
         if (charBuffer.hasRemaining()) {
385
            return charBuffer.get();
386
         }
387
         else {
388
            fillCharBuf();
389
         }
390
      }
391
      return -1;
392
   }
393
 
394
   /**
395
    *  Returns whether this reader is ready. The reader is ready if
396
    *  there is data in the internal buffer, or if additional data can
397
    *  be read from the underlying InputStream.
398
    */
399
   public boolean ready()
400
   {
401
      return charBuffer.hasRemaining() || !flushed;
402
   }
403
 
404
   /**
405
    *  Register a <code>MalformedInputListener</code> which should be
406
    *  notified when malformed input is encountered.
407
    */
408
   public void addMalformedInputListener(MalformedInputListener listener)
409
   {
410
      this.listeners.add(listener);
411
   }
412
 
413
   /**
414
    *  Unregister a previously registered
415
    *  <code>MalformedInputListener</code> if it should no longer be
416
    *  notified when malformed input is encountered.
417
    */
418
   public void removeMalformedInputListener(MalformedInputListener listener)
419
   {
420
      this.listeners.remove(listener);
421
   }
422
 
423
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.