/* gnu.classpath.tools.NotifyingInputStreamReader Copyright (C) 2004 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ package gnu.classpath.tools; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; /** * Similar to {@link java.io.InputStreamReader}, but can give * notification when malformed input is encountered. * *
Users of this class can register interest in the event of * malformed input by calling {@link * #addMalformedInputListener}. Each time a run of malformed input * data is encountered, all listener objects are notified. For * instance, this allows the calling code to inform the user about * problems in the input stream.
* * Background: The default
* InputStreamReader
implementation will ignore
* malformed input, silently replacing it with the default
* replacement string (usually the question mark). Alternatively, you
* can configure a CharsetDecoder
for the default
* InputStreamReader
to gracefully handle malformed data
* but notify the client code about such
* problems. NotifyingInputStreamReader
fills this
* gap.
file.encoding
. If this property isn't set,
* ISO-8859-1
is used as a fallback.
*
* This method should use {@link Charset.defaultCharset()} * instead, but this isn't implemented in Classpath at the * moment.
* * @param in theInputStream
to read from.
*/
public NotifyingInputStreamReader(InputStream in)
{
this(in, System.getProperty("file.encoding", "ISO-8859-1"));
}
/**
* Initializes a new instance for reading from the given
* InputStream using the specified charset.
*
* @param in the InputStream
to read from.
* @param charsetName the canonical name or an alias of a
* Charset
.
*
* @throws IllegalCharsetNameException if there is no
* Charset
with the given canonical name or alias.
*
* @throws UnsupportedCharsetException if there is no support for
* the specified Charset
in the runtime environment.
*/
public NotifyingInputStreamReader(InputStream in, String charsetName)
throws IllegalCharsetNameException, UnsupportedCharsetException
{
this(in, Charset.forName(charsetName));
}
/**
* Initializes a new instance for reading from the given
* InputStream using the specified charset.
*
* @param in the InputStream
to read from.
* @param charset the Charset
to use for decoding
* characters.
*/
public NotifyingInputStreamReader(InputStream in, Charset charset)
{
this(in, charset.newDecoder());
}
/**
* Initializes a new instance for reading from the given
* InputStream using the specified charset decoder.
*
* Note: the
* NotifyingInputStreamReader
will not exhibit the
* advertised behaviour if you changed the action to take on
* malformed input in the specified
* CharsetDecoder
. In other words, you should not
* call {@link CharsetDecoder.onMalformedInput(CodingErrorAction)}
* on the specified decoder before or while this reader is being
* used unless you know what you're doing.
*
* @param in the InputStream
to read from.
* @param charset the CharsetDecoder
to use for
* decoding characters.
*/
public NotifyingInputStreamReader(InputStream in, CharsetDecoder decoder)
{
this.in = in;
this.decoder = decoder;
this.charBuffer = CharBuffer.wrap(new char[DEFAULT_INPUT_BUFFER_SIZE]);
this.charBuffer.position(charBuffer.limit());
this.readBuffer = new byte[DEFAULT_OUTPUT_BUFFER_SIZE];
this.byteBuffer = ByteBuffer.wrap(readBuffer);
this.byteBuffer.position(byteBuffer.limit());
}
public void close()
throws IOException
{
in.close();
}
/**
* Fill charBuffer with new character data. This method returns if
* either the charBuffer has been filled completely with decoded
* character data, or if EOF is reached in the underlying
* InputStream. When this method returns, charBuffer is flipped
* and ready to be read from.
*/
private void fillCharBuf()
throws IOException
{
charBuffer.clear();
while (!flushed) {
CoderResult coderResult;
int charBufferPositionBefore = charBuffer.position();
if (!decodingFinished) {
coderResult = decoder.decode(byteBuffer, charBuffer, allInputConsumed);
if (allInputConsumed) {
decodingFinished = true;
}
}
else {
coderResult = decoder.flush(charBuffer);
flushed = coderResult.isUnderflow();
}
/*
int charBufferPositionAfter = charBuffer.position();
for (int i=charBufferPositionBefore; iMalformedInputListener
which should be
* notified when malformed input is encountered.
*/
public void addMalformedInputListener(MalformedInputListener listener)
{
this.listeners.add(listeners);
}
/**
* Unregister a previously registered
* MalformedInputListener
if it should no longer be
* notified when malformed input is encountered.
*/
public void removeMalformedInputListener(MalformedInputListener listener)
{
this.listeners.remove(listeners);
}
}