0N/A/*
2362N/A * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A */
0N/A
0N/Apackage sun.nio.cs;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.nio.*;
0N/Aimport java.nio.channels.*;
0N/Aimport java.nio.charset.*;
0N/A
0N/Apublic class StreamDecoder extends Reader
0N/A{
0N/A
0N/A private static final int MIN_BYTE_BUFFER_SIZE = 32;
0N/A private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
0N/A
0N/A private volatile boolean isOpen = true;
0N/A
0N/A private void ensureOpen() throws IOException {
0N/A if (!isOpen)
0N/A throw new IOException("Stream closed");
0N/A }
0N/A
0N/A // In order to handle surrogates properly we must never try to produce
0N/A // fewer than two characters at a time. If we're only asked to return one
0N/A // character then the other is saved here to be returned later.
0N/A //
0N/A private boolean haveLeftoverChar = false;
0N/A private char leftoverChar;
0N/A
0N/A
0N/A // Factories for java.io.InputStreamReader
0N/A
0N/A public static StreamDecoder forInputStreamReader(InputStream in,
0N/A Object lock,
0N/A String charsetName)
0N/A throws UnsupportedEncodingException
0N/A {
0N/A String csn = charsetName;
0N/A if (csn == null)
0N/A csn = Charset.defaultCharset().name();
0N/A try {
0N/A if (Charset.isSupported(csn))
0N/A return new StreamDecoder(in, lock, Charset.forName(csn));
0N/A } catch (IllegalCharsetNameException x) { }
0N/A throw new UnsupportedEncodingException (csn);
0N/A }
0N/A
0N/A public static StreamDecoder forInputStreamReader(InputStream in,
0N/A Object lock,
0N/A Charset cs)
0N/A {
0N/A return new StreamDecoder(in, lock, cs);
0N/A }
0N/A
0N/A public static StreamDecoder forInputStreamReader(InputStream in,
0N/A Object lock,
0N/A CharsetDecoder dec)
0N/A {
0N/A return new StreamDecoder(in, lock, dec);
0N/A }
0N/A
0N/A
0N/A // Factory for java.nio.channels.Channels.newReader
0N/A
0N/A public static StreamDecoder forDecoder(ReadableByteChannel ch,
0N/A CharsetDecoder dec,
0N/A int minBufferCap)
0N/A {
0N/A return new StreamDecoder(ch, dec, minBufferCap);
0N/A }
0N/A
0N/A
0N/A // -- Public methods corresponding to those in InputStreamReader --
0N/A
0N/A // All synchronization and state/argument checking is done in these public
0N/A // methods; the concrete stream-decoder subclasses defined below need not
0N/A // do any such checking.
0N/A
0N/A public String getEncoding() {
0N/A if (isOpen())
0N/A return encodingName();
0N/A return null;
0N/A }
0N/A
0N/A public int read() throws IOException {
0N/A return read0();
0N/A }
0N/A
0N/A private int read0() throws IOException {
0N/A synchronized (lock) {
0N/A
0N/A // Return the leftover char, if there is one
0N/A if (haveLeftoverChar) {
0N/A haveLeftoverChar = false;
0N/A return leftoverChar;
0N/A }
0N/A
0N/A // Convert more bytes
0N/A char cb[] = new char[2];
0N/A int n = read(cb, 0, 2);
0N/A switch (n) {
0N/A case -1:
0N/A return -1;
0N/A case 2:
0N/A leftoverChar = cb[1];
0N/A haveLeftoverChar = true;
0N/A // FALL THROUGH
0N/A case 1:
0N/A return cb[0];
0N/A default:
0N/A assert false : n;
0N/A return -1;
0N/A }
0N/A }
0N/A }
0N/A
0N/A public int read(char cbuf[], int offset, int length) throws IOException {
0N/A int off = offset;
0N/A int len = length;
0N/A synchronized (lock) {
0N/A ensureOpen();
0N/A if ((off < 0) || (off > cbuf.length) || (len < 0) ||
0N/A ((off + len) > cbuf.length) || ((off + len) < 0)) {
0N/A throw new IndexOutOfBoundsException();
0N/A }
0N/A if (len == 0)
0N/A return 0;
0N/A
0N/A int n = 0;
0N/A
0N/A if (haveLeftoverChar) {
0N/A // Copy the leftover char into the buffer
0N/A cbuf[off] = leftoverChar;
0N/A off++; len--;
0N/A haveLeftoverChar = false;
0N/A n = 1;
0N/A if ((len == 0) || !implReady())
0N/A // Return now if this is all we can produce w/o blocking
0N/A return n;
0N/A }
0N/A
0N/A if (len == 1) {
0N/A // Treat single-character array reads just like read()
0N/A int c = read0();
0N/A if (c == -1)
0N/A return (n == 0) ? -1 : n;
0N/A cbuf[off] = (char)c;
0N/A return n + 1;
0N/A }
0N/A
0N/A return n + implRead(cbuf, off, off + len);
0N/A }
0N/A }
0N/A
0N/A public boolean ready() throws IOException {
0N/A synchronized (lock) {
0N/A ensureOpen();
0N/A return haveLeftoverChar || implReady();
0N/A }
0N/A }
0N/A
0N/A public void close() throws IOException {
0N/A synchronized (lock) {
0N/A if (!isOpen)
0N/A return;
0N/A implClose();
0N/A isOpen = false;
0N/A }
0N/A }
0N/A
0N/A private boolean isOpen() {
0N/A return isOpen;
0N/A }
0N/A
0N/A
0N/A // -- Charset-based stream decoder impl --
0N/A
0N/A // In the early stages of the build we haven't yet built the NIO native
0N/A // code, so guard against that by catching the first UnsatisfiedLinkError
0N/A // and setting this flag so that later attempts fail quickly.
0N/A //
0N/A private static volatile boolean channelsAvailable = true;
0N/A
0N/A private static FileChannel getChannel(FileInputStream in) {
0N/A if (!channelsAvailable)
0N/A return null;
0N/A try {
0N/A return in.getChannel();
0N/A } catch (UnsatisfiedLinkError x) {
0N/A channelsAvailable = false;
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A private Charset cs;
0N/A private CharsetDecoder decoder;
0N/A private ByteBuffer bb;
0N/A
0N/A // Exactly one of these is non-null
0N/A private InputStream in;
0N/A private ReadableByteChannel ch;
0N/A
0N/A StreamDecoder(InputStream in, Object lock, Charset cs) {
0N/A this(in, lock,
0N/A cs.newDecoder()
0N/A .onMalformedInput(CodingErrorAction.REPLACE)
0N/A .onUnmappableCharacter(CodingErrorAction.REPLACE));
0N/A }
0N/A
0N/A StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
0N/A super(lock);
0N/A this.cs = dec.charset();
0N/A this.decoder = dec;
0N/A
0N/A // This path disabled until direct buffers are faster
0N/A if (false && in instanceof FileInputStream) {
0N/A ch = getChannel((FileInputStream)in);
0N/A if (ch != null)
0N/A bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
0N/A }
0N/A if (ch == null) {
0N/A this.in = in;
0N/A this.ch = null;
0N/A bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
0N/A }
0N/A bb.flip(); // So that bb is initially empty
0N/A }
0N/A
0N/A StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) {
0N/A this.in = null;
0N/A this.ch = ch;
0N/A this.decoder = dec;
0N/A this.cs = dec.charset();
0N/A this.bb = ByteBuffer.allocate(mbc < 0
0N/A ? DEFAULT_BYTE_BUFFER_SIZE
0N/A : (mbc < MIN_BYTE_BUFFER_SIZE
0N/A ? MIN_BYTE_BUFFER_SIZE
0N/A : mbc));
0N/A bb.flip();
0N/A }
0N/A
0N/A private int readBytes() throws IOException {
0N/A bb.compact();
0N/A try {
0N/A if (ch != null) {
0N/A // Read from the channel
0N/A int n = ch.read(bb);
0N/A if (n < 0)
0N/A return n;
0N/A } else {
0N/A // Read from the input stream, and then update the buffer
0N/A int lim = bb.limit();
0N/A int pos = bb.position();
0N/A assert (pos <= lim);
0N/A int rem = (pos <= lim ? lim - pos : 0);
0N/A assert rem > 0;
0N/A int n = in.read(bb.array(), bb.arrayOffset() + pos, rem);
0N/A if (n < 0)
0N/A return n;
0N/A if (n == 0)
0N/A throw new IOException("Underlying input stream returned zero bytes");
0N/A assert (n <= rem) : "n = " + n + ", rem = " + rem;
0N/A bb.position(pos + n);
0N/A }
0N/A } finally {
0N/A // Flip even when an IOException is thrown,
0N/A // otherwise the stream will stutter
0N/A bb.flip();
0N/A }
0N/A
0N/A int rem = bb.remaining();
0N/A assert (rem != 0) : rem;
0N/A return rem;
0N/A }
0N/A
0N/A int implRead(char[] cbuf, int off, int end) throws IOException {
0N/A
0N/A // In order to handle surrogate pairs, this method requires that
0N/A // the invoker attempt to read at least two characters. Saving the
0N/A // extra character, if any, at a higher level is easier than trying
0N/A // to deal with it here.
0N/A assert (end - off > 1);
0N/A
0N/A CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);
0N/A if (cb.position() != 0)
0N/A // Ensure that cb[0] == cbuf[off]
0N/A cb = cb.slice();
0N/A
0N/A boolean eof = false;
0N/A for (;;) {
0N/A CoderResult cr = decoder.decode(bb, cb, eof);
0N/A if (cr.isUnderflow()) {
0N/A if (eof)
0N/A break;
0N/A if (!cb.hasRemaining())
0N/A break;
0N/A if ((cb.position() > 0) && !inReady())
0N/A break; // Block at most once
0N/A int n = readBytes();
0N/A if (n < 0) {
0N/A eof = true;
0N/A if ((cb.position() == 0) && (!bb.hasRemaining()))
0N/A break;
0N/A decoder.reset();
0N/A }
0N/A continue;
0N/A }
0N/A if (cr.isOverflow()) {
0N/A assert cb.position() > 0;
0N/A break;
0N/A }
0N/A cr.throwException();
0N/A }
0N/A
0N/A if (eof) {
0N/A // ## Need to flush decoder
0N/A decoder.reset();
0N/A }
0N/A
0N/A if (cb.position() == 0) {
0N/A if (eof)
0N/A return -1;
0N/A assert false;
0N/A }
0N/A return cb.position();
0N/A }
0N/A
0N/A String encodingName() {
0N/A return ((cs instanceof HistoricallyNamedCharset)
0N/A ? ((HistoricallyNamedCharset)cs).historicalName()
0N/A : cs.name());
0N/A }
0N/A
0N/A private boolean inReady() {
0N/A try {
0N/A return (((in != null) && (in.available() > 0))
0N/A || (ch instanceof FileChannel)); // ## RBC.available()?
0N/A } catch (IOException x) {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A boolean implReady() {
0N/A return bb.hasRemaining() || inReady();
0N/A }
0N/A
0N/A void implClose() throws IOException {
0N/A if (ch != null)
0N/A ch.close();
0N/A else
0N/A in.close();
0N/A }
0N/A
0N/A}