Index: gnu/java/nio/charset/EncodingHelper.java =================================================================== RCS file: /cvsroot/classpath/classpath/gnu/java/nio/charset/EncodingHelper.java,v retrieving revision 1.1 diff -u -r1.1 EncodingHelper.java --- gnu/java/nio/charset/EncodingHelper.java 8 Apr 2005 21:46:05 -0000 1.1 +++ gnu/java/nio/charset/EncodingHelper.java 15 Apr 2005 15:54:44 -0000 @@ -116,6 +116,18 @@ return (oldCanonical != null)?oldCanonical : newCanonical; } + public static boolean isISOLatin1(String s) + { + if(s.equals("ISO-8859-1") || + s.equals("8859_1") || + s.equals("ISO_8859-1") || + s.equals("latin1") || + s.equals("ISO8859_1") || + s.equals("ISO_8859_1")) + return true; + return false; + } + /** * Gets a charset, throwing the java.io exception and not * the java.nio exception if an error occurs. Index: java/io/InputStreamReader.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/io/InputStreamReader.java,v retrieving revision 1.21 diff -u -r1.21 InputStreamReader.java --- java/io/InputStreamReader.java 10 Mar 2005 19:35:51 -0000 1.21 +++ java/io/InputStreamReader.java 15 Apr 2005 15:54:46 -0000 @@ -38,12 +38,16 @@ package java.io; -import java.nio.channels.Channels; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; - -import gnu.java.io.EncodingManager; -import gnu.java.io.decode.Decoder; +import java.nio.CharBuffer; +import java.nio.ByteBuffer; +import gnu.java.nio.charset.EncodingHelper; /** * This class reads characters from a byte input stream. The characters @@ -86,20 +90,50 @@ * @see BufferedReader * @see InputStream * + * @author Robert Schuster * @author Aaron M. Renn (address@hidden) * @author Per Bothner (address@hidden) * @date April 22, 1998. */ public class InputStreamReader extends Reader { - /* - * This is the byte-character decoder class that does the reading and - * translation of bytes from the underlying stream. + /** + * The input stream. + */ + private InputStream in; + + /** + * The charset decoder. + */ + private CharsetDecoder decoder; + + /** + * End of stream reached. + */ + private boolean isDone = false; + + /** + * Need this. */ - private Reader in; + private float maxBytesPerChar; + /** + * Buffer holding surplus loaded bytes (if any) + */ + private ByteBuffer byteBuffer; + + /** + * java.io canonical name of the encoding. + */ private String encoding; - + + /** + * We might decode to a 2-char UTF-16 surrogate, which won't fit in the + * output buffer. In this case we need to save the surrogate char. + */ + private char savedSurrogate; + private boolean hasSavedSurrogate = false; + /** * This method initializes a new instance of InputStreamReader * to read from the specified stream using the default encoding. @@ -110,11 +144,38 @@ { if (in == null) throw new NullPointerException(); - - Decoder decoder = EncodingManager.getDecoder(in); - encoding = decoder.getSchemeName(); - - this.in = decoder; + this.in = in; + try + { + encoding = System.getProperty("file.encoding"); + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + Charset cs = EncodingHelper.getCharset(encoding); + decoder = cs.newDecoder(); + encoding = EncodingHelper.getOldCanonical(cs.name()); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } catch(UnsupportedEncodingException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } } /** @@ -136,41 +197,72 @@ || encoding_name == null) throw new NullPointerException(); - Decoder decoder = EncodingManager.getDecoder(in, encoding_name); - encoding = decoder.getSchemeName(); - - this.in = decoder; - + this.in = in; + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_name)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + try { + Charset cs = EncodingHelper.getCharset(encoding_name); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder = cs.newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + + // The encoding should be the old name, if such exists. + encoding = EncodingHelper.getOldCanonical(cs.name()); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } } /** * Creates an InputStreamReader that uses a decoder of the given * charset to decode the bytes in the InputStream into * characters. - * @since 1.4 */ - public InputStreamReader(InputStream in, Charset charset) - { - /* FIXME: InputStream is wrapped in Channel which is read by a - * Reader-implementation for channels. However to fix this we - * need to completely move to NIO-style character - * encoding/decoding. - */ - this.in = Channels.newReader(Channels.newChannel(in), charset.newDecoder(), - -1); - encoding = charset.name(); + public InputStreamReader(InputStream in, Charset charset) { + this.in = in; + decoder = charset.newDecoder(); + + // JDK reports errors, so we do the same. + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + decoder.reset(); + encoding = EncodingHelper.getOldCanonical(charset.name()); } /** * Creates an InputStreamReader that uses the given charset decoder * to decode the bytes in the InputStream into characters. - * @since 1.4 */ - public InputStreamReader(InputStream in, CharsetDecoder decoder) - { - // FIXME: see address@hidden InputStreamReader(InputStream, Charset) - this.in = Channels.newReader(Channels.newChannel(in), decoder, -1); - encoding = decoder.charset().name(); + public InputStreamReader(InputStream in, CharsetDecoder decoder) { + this.in = in; + this.decoder = decoder; + + try { + maxBytesPerChar = decoder.charset().newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + // JDK reports errors, so we do the same. + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + decoder.reset(); + encoding = EncodingHelper.getOldCanonical(decoder.charset().name()); } /** @@ -183,9 +275,14 @@ { synchronized (lock) { + // Makes sure all intermediate data is released by the decoder. + if (decoder != null) + decoder.reset(); if (in != null) - in.close(); + in.close(); in = null; + isDone = true; + decoder = null; } } @@ -202,7 +299,7 @@ } /** - * This method checks to see if the stream is read to be read. It + * This method checks to see if the stream is ready to be read. It * will return true if is, or false if it is not. * If the stream is not ready to be read, it could (although is not required * to) block on the next read attempt. @@ -217,7 +314,7 @@ if (in == null) throw new IOException("Reader has been closed"); - return in.ready(); + return in.available() != 0; } /** @@ -233,45 +330,108 @@ * * @exception IOException If an error occurs */ - public int read (char[] buf, int offset, int length) throws IOException + public int read(char[] buf, int offset, int length) throws IOException { if (in == null) throw new IOException("Reader has been closed"); - - return in.read(buf, offset, length); + if (isDone) + return -1; + + if(decoder != null){ + int totalBytes = (int)((double)length * maxBytesPerChar); + byte[] bytes = new byte[totalBytes]; + + int remaining = 0; + if(byteBuffer != null) + { + remaining = byteBuffer.remaining(); + byteBuffer.get(bytes, 0, remaining); + } + int read; + if(totalBytes - remaining > 0) + { + read = in.read(bytes, remaining, totalBytes - remaining); + if(read == -1){ + read = remaining; + isDone = true; + } else + read += remaining; + } else + read = remaining; + byteBuffer = ByteBuffer.wrap(bytes, 0, read); + CharBuffer cb = CharBuffer.wrap(buf, offset, length); + + if(hasSavedSurrogate){ + hasSavedSurrogate = false; + cb.put(savedSurrogate); + read++; + } + + CoderResult cr = decoder.decode(byteBuffer, cb, isDone); + decoder.reset(); + + // 1 char remains which is the first half of a surrogate pair. + if(cr.isOverflow() && cb.hasRemaining()){ + CharBuffer overflowbuf = CharBuffer.allocate(2); + cr = decoder.decode(byteBuffer, overflowbuf, isDone); + overflowbuf.flip(); + cb.put(overflowbuf.get()); + savedSurrogate = overflowbuf.get(); + hasSavedSurrogate = true; + isDone = false; + } + + if(byteBuffer.hasRemaining()) { + byteBuffer.compact(); + byteBuffer.flip(); + isDone = false; + } else + byteBuffer = null; + + return (read == 0)?-1:cb.position(); + } else { + byte[] bytes = new byte[length]; + int read = in.read(bytes); + for(int i=0;i + * This method will block until the char can be read. * - * @return The char read, as an int, or -1 if end of stream. + * @return The char read or -1 if end of stream * * @exception IOException If an error occurs */ public int read() throws IOException { - if (in == null) - throw new IOException("Reader has been closed"); - - return in.read(); + char[] buf = new char[1]; + int count = read(buf, 0, 1); + return count > 0 ? buf[0] : -1; } - /** - * Skips the specified number of chars in the stream. It - * returns the actual number of chars skipped, which may be less than the - * requested amount. - * - * @param count The requested number of chars to skip - * - * @return The actual number of chars skipped. - * - * @exception IOException If an error occurs - */ + /** + * Skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ public long skip(long count) throws IOException { if (in == null) throw new IOException("Reader has been closed"); return super.skip(count); - } + } } Index: java/io/OutputStreamWriter.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/io/OutputStreamWriter.java,v retrieving revision 1.14 diff -u -r1.14 OutputStreamWriter.java --- java/io/OutputStreamWriter.java 16 Feb 2005 11:18:37 -0000 1.14 +++ java/io/OutputStreamWriter.java 15 Apr 2005 15:54:46 -0000 @@ -38,8 +38,17 @@ package java.io; -import gnu.java.io.EncodingManager; -import gnu.java.io.encode.Encoder; +import gnu.java.nio.charset.EncodingHelper; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; /** * This class writes characters to an output stream that is byte oriented @@ -76,13 +85,26 @@ */ public class OutputStreamWriter extends Writer { + /** + * The output stream. + */ + private OutputStream out; /** - * This is the byte-character encoder class that does the writing and - * translation of characters to bytes before writing to the underlying - * class. + * The charset encoder. */ - private Encoder out; + private CharsetEncoder encoder; + + /** + * java.io canonical name of the encoding. + */ + private String encodingName; + + /** + * Buffer output before character conversion as it has costly overhead. + */ + private CharBuffer outputBuffer; + private final static int BUFFER_SIZE = 1024; /** * This method initializes a new instance of OutputStreamWriter @@ -100,7 +122,54 @@ public OutputStreamWriter (OutputStream out, String encoding_scheme) throws UnsupportedEncodingException { - this.out = EncodingManager.getEncoder (out, encoding_scheme); + this.out = out; + try + { + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_scheme)) + { + encodingName = "ISO8859_1"; + encoder = null; + return; + } + + /* + * Workraround for encodings with a byte-order-mark. + * We only want to write it once per stream. + */ + try { + if(encoding_scheme.equalsIgnoreCase("UnicodeBig") || + encoding_scheme.equalsIgnoreCase("UTF-16") || + encoding_scheme.equalsIgnoreCase("UTF16")) + { + encoding_scheme = "UTF-16BE"; + out.write((byte)0xFE); + out.write((byte)0xFF); + } else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle")){ + encoding_scheme = "UTF-16LE"; + out.write((byte)0xFF); + out.write((byte)0xFE); + } + } catch(IOException ioe){ + } + + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + + Charset cs = EncodingHelper.getCharset(encoding_scheme); + if(cs == null) + throw new UnsupportedEncodingException("Encoding "+encoding_scheme+ + " unknown"); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + } catch(RuntimeException e) { + // Default to ISO Latin-1, will happen if this is called, for instance, + // before the NIO provider is loadable. + encoder = null; + encodingName = "ISO8859_1"; + } } /** @@ -111,7 +180,24 @@ */ public OutputStreamWriter (OutputStream out) { - this.out = EncodingManager.getEncoder (out); + this.out = out; + outputBuffer = null; + try + { + String encoding = System.getProperty("file.encoding"); + Charset cs = Charset.forName(encoding); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + } catch(RuntimeException e) { + encoder = null; + encodingName = "ISO8859_1"; + } + if(encoder != null) + { + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + } } /** @@ -122,7 +208,11 @@ */ public void close () throws IOException { + if(out == null) + throw new IOException("Stream is closed."); + flush(); out.close (); + out = null; } /** @@ -134,7 +224,7 @@ */ public String getEncoding () { - return out != null ? out.getSchemeName () : null; + return out != null ? encodingName : null; } /** @@ -144,7 +234,18 @@ */ public void flush () throws IOException { - out.flush (); + if(out != null){ + if(outputBuffer != null){ + char[] buf = new char[outputBuffer.position()]; + if(buf.length > 0){ + outputBuffer.flip(); + outputBuffer.get(buf); + writeConvert(buf, 0, buf.length); + outputBuffer.clear(); + } + } + out.flush (); + } } /** @@ -160,7 +261,64 @@ */ public void write (char[] buf, int offset, int count) throws IOException { - out.write (buf, offset, count); + if(out == null) + throw new IOException("Stream is closed."); + if(buf == null) + throw new IOException("Buffer is null."); + + if(outputBuffer != null) + { + if(count >= outputBuffer.remaining()) + { + int r = outputBuffer.remaining(); + outputBuffer.put(buf, offset, r); + writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); + outputBuffer.clear(); + offset += r; + count -= r; + // if the remaining bytes is larger than the whole buffer, + // just don't buffer. + if(count >= outputBuffer.remaining()){ + writeConvert(buf, offset, count); + return; + } + } + outputBuffer.put(buf, offset, count); + } else writeConvert(buf, offset, count); + } + + /** + * Converts and writes characters. + */ + private void writeConvert (char[] buf, int offset, int count) + throws IOException + { + if(encoder == null) + { + byte[] b = new byte[count]; + for(int i=0;iPrintStream object to write * to the specified output sink. * @@ -146,10 +111,15 @@ { super (out); - // FIXME Instead of using PrintWriter and ForwardStream we - // should inline the character conversion (see libgcj's version - // of this class) - pw = new PrintWriter (new ForwardStream (), auto_flush); + try { + this.encoding = System.getProperty("file.encoding"); + } catch (SecurityException e){ + this.encoding = "ISO8859_1"; + } catch (IllegalArgumentException e){ + this.encoding = "ISO8859_1"; + } catch (NullPointerException e){ + this.encoding = "ISO8859_1"; + } this.auto_flush = auto_flush; } @@ -173,12 +143,8 @@ { super (out); - // FIXME Instead of using PrintWriter and ForwardStream we - // should inline the character conversion (see libgcj's version - // of this class) - pw = new PrintWriter ( - new OutputStreamWriter ( - new ForwardStream (), encoding), auto_flush); + new String(new byte[]{0}, encoding); // check if encoding is supported + this.encoding = encoding; this.auto_flush = auto_flush; } @@ -193,10 +159,8 @@ */ public boolean checkError () { - if (!closed) - flush (); - - return error_occurred | pw.checkError (); + flush (); + return error_occurred; } /** @@ -213,8 +177,19 @@ */ public void close () { - pw.close (); - closed = true; + try + { + flush(); + out.close(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } } /** @@ -223,7 +198,73 @@ */ public void flush () { - pw.flush(); + try + { + out.flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (String str, boolean println) + { + try + { + writeChars(str, 0, str.length()); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (char[] chars, int pos, int len, + boolean println) + { + try + { + writeChars(chars, pos, len); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private void writeChars(char[] buf, int offset, int count) + throws IOException + { + byte[] bytes = (new String(buf, offset, count)).getBytes(encoding); + out.write(bytes, 0, bytes.length); + } + + private void writeChars(String str, int offset, int count) + throws IOException + { + byte[] bytes = str.substring(offset, offset+count).getBytes(encoding); + out.write(bytes, 0, bytes.length); } /** @@ -235,7 +276,7 @@ */ public void print (boolean bool) { - print (String.valueOf (bool)); + print(String.valueOf(bool), false); } /** @@ -246,7 +287,7 @@ */ public void print (int inum) { - print (String.valueOf (inum)); + print(String.valueOf(inum), false); } /** @@ -257,7 +298,7 @@ */ public void print (long lnum) { - print (String.valueOf (lnum)); + print(String.valueOf(lnum), false); } /** @@ -268,7 +309,7 @@ */ public void print (float fnum) { - print (String.valueOf (fnum)); + print(String.valueOf(fnum), false); } /** @@ -279,7 +320,7 @@ */ public void print (double dnum) { - print (String.valueOf (dnum)); + print(String.valueOf(dnum), false); } /** @@ -291,9 +332,7 @@ */ public void print (Object obj) { - // Don't call pw directly. Convert to String so we scan for newline - // characters on auto-flush; - print (String.valueOf (obj)); + print(obj == null ? "null" : obj.toString(), false); } /** @@ -304,10 +343,7 @@ */ public void print (String str) { - pw.print (str); - - if (auto_flush) - flush (); + print(str == null ? "null" : str, false); } /** @@ -316,9 +352,9 @@ * * @param ch The char value to be printed */ - public void print (char ch) + public synchronized void print (char ch) { - print (String.valueOf (ch)); + print(new char[]{ch}, 0, 1, false); } /** @@ -329,7 +365,7 @@ */ public void print (char[] charArray) { - pw.print (charArray); + print(charArray, 0, charArray.length, false); } /** @@ -339,7 +375,7 @@ */ public void println () { - pw.println(); + print(line_separator, 0, line_separator.length, false); } /** @@ -353,7 +389,7 @@ */ public void println (boolean bool) { - println (String.valueOf (bool)); + print(String.valueOf(bool), true); } /** @@ -366,7 +402,7 @@ */ public void println (int inum) { - println (String.valueOf (inum)); + print(String.valueOf(inum), true); } /** @@ -379,7 +415,7 @@ */ public void println (long lnum) { - println (String.valueOf (lnum)); + print(String.valueOf(lnum), true); } /** @@ -392,7 +428,7 @@ */ public void println (float fnum) { - println (String.valueOf (fnum)); + print(String.valueOf(fnum), true); } /** @@ -405,7 +441,7 @@ */ public void println (double dnum) { - println (String.valueOf (dnum)); + print(String.valueOf(dnum), true); } /** @@ -419,7 +455,7 @@ */ public void println (Object obj) { - println (String.valueOf (obj)); + print(obj == null ? "null" : obj.toString(), true); } /** @@ -432,7 +468,7 @@ */ public void println (String str) { - pw.println (str); + print (str == null ? "null" : str, true); } /** @@ -443,9 +479,9 @@ * * @param ch The char value to be printed */ - public void println (char ch) + public synchronized void println (char ch) { - println (String.valueOf (ch)); + print(new char[]{ch}, 0, 1, true); } /** @@ -458,7 +494,7 @@ */ public void println (char[] charArray) { - pw.println (charArray); + print(charArray, 0, charArray.length, true); } /** @@ -470,10 +506,6 @@ */ public void write (int oneByte) { - // We actually have to implement this method. Flush first so that - // things get written in the right order. - flush(); - try { out.write (oneByte & 0xff); @@ -501,10 +533,6 @@ */ public void write (byte[] buffer, int offset, int len) { - // We actually have to implement this method too. Flush first so that - // things get written in the right order. - flush(); - try { out.write (buffer, offset, len); @@ -522,3 +550,4 @@ } } } // class PrintStream + Index: java/lang/String.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/lang/String.java,v retrieving revision 1.61 diff -u -r1.61 String.java --- java/lang/String.java 19 Jan 2005 19:26:54 -0000 1.61 +++ java/lang/String.java 15 Apr 2005 15:54:46 -0000 @@ -39,9 +39,16 @@ package java.lang; -import gnu.java.io.EncodingManager; import gnu.java.lang.CharData; - +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.Comparator; @@ -105,14 +112,14 @@ * Characters which make up the String. * Package access is granted for use by StringBuffer. */ - final char[] value; + public final char[] value; /** * Holds the number of characters in value. This number is generally * the same as value.length, but can be smaller because substrings and * StringBuffers can share arrays. Package visible for use by trusted code. */ - final int count; + public final int count; /** * Caches the result of hashCode(). If this value is zero, the hashcode @@ -125,7 +132,7 @@ * substring()'s are common, the use of offset allows the operation * to perform in O(1). Package access is granted for use by StringBuffer. */ - final int offset; + public final int offset; /** * An implementation for address@hidden CASE_INSENSITIVE_ORDER}. @@ -304,7 +311,7 @@ * * @param data byte array to copy * @param offset the offset to start at - * @param count the number of characters in the array to use + * @param count the number of bytes in the array to use * @param encoding the name of the encoding to use * @throws NullPointerException if data or encoding is null * @throws IndexOutOfBoundsException if offset or count is incorrect @@ -318,11 +325,34 @@ { if (offset < 0 || count < 0 || offset + count > data.length) throw new StringIndexOutOfBoundsException(); - // XXX Consider using java.nio here. - value = EncodingManager.getDecoder(encoding) - .convertToChars(data, offset, count); - this.offset = 0; - this.count = value.length; + try + { + CharsetDecoder csd = Charset.forName(encoding).newDecoder(); + csd.onMalformedInput(CodingErrorAction.REPLACE); + csd.onUnmappableCharacter(CodingErrorAction.REPLACE); + CharBuffer cbuf = csd.decode(ByteBuffer.wrap(data, offset, count)); + if(cbuf.hasArray()) + { + value = cbuf.array(); + this.offset = cbuf.position(); + this.count = cbuf.remaining(); + } else { + // Doubt this will happen. But just in case. + value = new char[cbuf.remaining()]; + cbuf.get(value); + this.offset = 0; + this.count = value.length; + } + } catch(CharacterCodingException e){ + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } catch(IllegalCharsetNameException e){ + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } catch(UnsupportedCharsetException e){ + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } } /** @@ -359,7 +389,7 @@ * * @param data byte array to copy * @param offset the offset to start at - * @param count the number of characters in the array to use + * @param count the number of bytes in the array to use * @throws NullPointerException if data is null * @throws IndexOutOfBoundsException if offset or count is incorrect * @throws Error if the decoding fails @@ -370,11 +400,41 @@ { if (offset < 0 || count < 0 || offset + count > data.length) throw new StringIndexOutOfBoundsException(); - // XXX Consider using java.nio here. - value = EncodingManager.getDecoder() - .convertToChars(data, offset, count); - this.offset = 0; - this.count = value.length; + int o, c; + char[] v; + String encoding; + try + { + encoding = System.getProperty("file.encoding"); + CharsetDecoder csd = Charset.forName(encoding).newDecoder(); + csd.onMalformedInput(CodingErrorAction.REPLACE); + csd.onUnmappableCharacter(CodingErrorAction.REPLACE); + CharBuffer cbuf = csd.decode(ByteBuffer.wrap(data, offset, count)); + if(cbuf.hasArray()) + { + v = cbuf.array(); + o = cbuf.position(); + c = cbuf.remaining(); + } else { + // Doubt this will happen. But just in case. + v = new char[cbuf.remaining()]; + cbuf.get(v); + o = 0; + c = v.length; + } + } catch(Exception ex){ + // If anything goes wrong (System property not set, + // NIO provider not available, etc) + // Default to the 'safe' encoding ISO8859_1 + v = new char[count]; + o = 0; + c = count; + for (int i=0;i