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 StreamEncoder extends Writer
0N/A{
0N/A
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 // Factories for java.io.OutputStreamWriter
0N/A public static StreamEncoder forOutputStreamWriter(OutputStream out,
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 StreamEncoder(out, lock, Charset.forName(csn));
0N/A } catch (IllegalCharsetNameException x) { }
0N/A throw new UnsupportedEncodingException (csn);
0N/A }
0N/A
0N/A public static StreamEncoder forOutputStreamWriter(OutputStream out,
0N/A Object lock,
0N/A Charset cs)
0N/A {
0N/A return new StreamEncoder(out, lock, cs);
0N/A }
0N/A
0N/A public static StreamEncoder forOutputStreamWriter(OutputStream out,
0N/A Object lock,
0N/A CharsetEncoder enc)
0N/A {
0N/A return new StreamEncoder(out, lock, enc);
0N/A }
0N/A
0N/A
0N/A // Factory for java.nio.channels.Channels.newWriter
0N/A
0N/A public static StreamEncoder forEncoder(WritableByteChannel ch,
0N/A CharsetEncoder enc,
0N/A int minBufferCap)
0N/A {
0N/A return new StreamEncoder(ch, enc, minBufferCap);
0N/A }
0N/A
0N/A
0N/A // -- Public methods corresponding to those in OutputStreamWriter --
0N/A
0N/A // All synchronization and state/argument checking is done in these public
0N/A // methods; the concrete stream-encoder 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 void flushBuffer() throws IOException {
0N/A synchronized (lock) {
0N/A if (isOpen())
0N/A implFlushBuffer();
0N/A else
0N/A throw new IOException("Stream closed");
0N/A }
0N/A }
0N/A
0N/A public void write(int c) throws IOException {
0N/A char cbuf[] = new char[1];
0N/A cbuf[0] = (char) c;
0N/A write(cbuf, 0, 1);
0N/A }
0N/A
0N/A public void write(char cbuf[], int off, int len) throws IOException {
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 } else if (len == 0) {
0N/A return;
0N/A }
0N/A implWrite(cbuf, off, len);
0N/A }
0N/A }
0N/A
0N/A public void write(String str, int off, int len) throws IOException {
0N/A /* Check the len before creating a char buffer */
0N/A if (len < 0)
0N/A throw new IndexOutOfBoundsException();
0N/A char cbuf[] = new char[len];
0N/A str.getChars(off, off + len, cbuf, 0);
0N/A write(cbuf, 0, len);
0N/A }
0N/A
0N/A public void flush() throws IOException {
0N/A synchronized (lock) {
0N/A ensureOpen();
0N/A implFlush();
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 encoder impl --
0N/A
0N/A private Charset cs;
0N/A private CharsetEncoder encoder;
0N/A private ByteBuffer bb;
0N/A
0N/A // Exactly one of these is non-null
0N/A private final OutputStream out;
0N/A private WritableByteChannel ch;
0N/A
0N/A // Leftover first char in a surrogate pair
0N/A private boolean haveLeftoverChar = false;
0N/A private char leftoverChar;
0N/A private CharBuffer lcb = null;
0N/A
0N/A private StreamEncoder(OutputStream out, Object lock, Charset cs) {
0N/A this(out, lock,
0N/A cs.newEncoder()
0N/A .onMalformedInput(CodingErrorAction.REPLACE)
0N/A .onUnmappableCharacter(CodingErrorAction.REPLACE));
0N/A }
0N/A
0N/A private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {
0N/A super(lock);
0N/A this.out = out;
0N/A this.ch = null;
0N/A this.cs = enc.charset();
0N/A this.encoder = enc;
0N/A
0N/A // This path disabled until direct buffers are faster
0N/A if (false && out instanceof FileOutputStream) {
0N/A ch = ((FileOutputStream)out).getChannel();
0N/A if (ch != null)
0N/A bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
0N/A }
0N/A if (ch == null) {
0N/A bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
0N/A }
0N/A }
0N/A
0N/A private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {
0N/A this.out = null;
0N/A this.ch = ch;
0N/A this.cs = enc.charset();
0N/A this.encoder = enc;
0N/A this.bb = ByteBuffer.allocate(mbc < 0
0N/A ? DEFAULT_BYTE_BUFFER_SIZE
0N/A : mbc);
0N/A }
0N/A
0N/A private void writeBytes() throws IOException {
0N/A bb.flip();
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
0N/A if (rem > 0) {
0N/A if (ch != null) {
0N/A if (ch.write(bb) != rem)
0N/A assert false : rem;
0N/A } else {
0N/A out.write(bb.array(), bb.arrayOffset() + pos, rem);
0N/A }
0N/A }
0N/A bb.clear();
0N/A }
0N/A
0N/A private void flushLeftoverChar(CharBuffer cb, boolean endOfInput)
0N/A throws IOException
0N/A {
0N/A if (!haveLeftoverChar && !endOfInput)
0N/A return;
0N/A if (lcb == null)
0N/A lcb = CharBuffer.allocate(2);
0N/A else
0N/A lcb.clear();
0N/A if (haveLeftoverChar)
0N/A lcb.put(leftoverChar);
0N/A if ((cb != null) && cb.hasRemaining())
0N/A lcb.put(cb.get());
0N/A lcb.flip();
0N/A while (lcb.hasRemaining() || endOfInput) {
0N/A CoderResult cr = encoder.encode(lcb, bb, endOfInput);
0N/A if (cr.isUnderflow()) {
0N/A if (lcb.hasRemaining()) {
0N/A leftoverChar = lcb.get();
0N/A if (cb != null && cb.hasRemaining())
0N/A flushLeftoverChar(cb, endOfInput);
0N/A return;
0N/A }
0N/A break;
0N/A }
0N/A if (cr.isOverflow()) {
0N/A assert bb.position() > 0;
0N/A writeBytes();
0N/A continue;
0N/A }
0N/A cr.throwException();
0N/A }
0N/A haveLeftoverChar = false;
0N/A }
0N/A
0N/A void implWrite(char cbuf[], int off, int len)
0N/A throws IOException
0N/A {
0N/A CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
0N/A
0N/A if (haveLeftoverChar)
0N/A flushLeftoverChar(cb, false);
0N/A
0N/A while (cb.hasRemaining()) {
0N/A CoderResult cr = encoder.encode(cb, bb, false);
0N/A if (cr.isUnderflow()) {
0N/A assert (cb.remaining() <= 1) : cb.remaining();
0N/A if (cb.remaining() == 1) {
0N/A haveLeftoverChar = true;
0N/A leftoverChar = cb.get();
0N/A }
0N/A break;
0N/A }
0N/A if (cr.isOverflow()) {
0N/A assert bb.position() > 0;
0N/A writeBytes();
0N/A continue;
0N/A }
0N/A cr.throwException();
0N/A }
0N/A }
0N/A
0N/A void implFlushBuffer() throws IOException {
0N/A if (bb.position() > 0)
0N/A writeBytes();
0N/A }
0N/A
0N/A void implFlush() throws IOException {
0N/A implFlushBuffer();
0N/A if (out != null)
0N/A out.flush();
0N/A }
0N/A
0N/A void implClose() throws IOException {
0N/A flushLeftoverChar(null, true);
0N/A try {
0N/A for (;;) {
0N/A CoderResult cr = encoder.flush(bb);
0N/A if (cr.isUnderflow())
0N/A break;
0N/A if (cr.isOverflow()) {
0N/A assert bb.position() > 0;
0N/A writeBytes();
0N/A continue;
0N/A }
0N/A cr.throwException();
0N/A }
0N/A
0N/A if (bb.position() > 0)
0N/A writeBytes();
0N/A if (ch != null)
0N/A ch.close();
0N/A else
0N/A out.close();
0N/A } catch (IOException x) {
0N/A encoder.reset();
0N/A throw x;
0N/A }
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}