Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/nio/cs/StreamEncoder.java
38918 views
/*1* Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*/2728package sun.nio.cs;2930import java.io.*;31import java.nio.*;32import java.nio.channels.*;33import java.nio.charset.*;3435public class StreamEncoder extends Writer36{3738private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;3940private volatile boolean isOpen = true;4142private void ensureOpen() throws IOException {43if (!isOpen)44throw new IOException("Stream closed");45}4647// Factories for java.io.OutputStreamWriter48public static StreamEncoder forOutputStreamWriter(OutputStream out,49Object lock,50String charsetName)51throws UnsupportedEncodingException52{53String csn = charsetName;54if (csn == null)55csn = Charset.defaultCharset().name();56try {57if (Charset.isSupported(csn))58return new StreamEncoder(out, lock, Charset.forName(csn));59} catch (IllegalCharsetNameException x) { }60throw new UnsupportedEncodingException (csn);61}6263public static StreamEncoder forOutputStreamWriter(OutputStream out,64Object lock,65Charset cs)66{67return new StreamEncoder(out, lock, cs);68}6970public static StreamEncoder forOutputStreamWriter(OutputStream out,71Object lock,72CharsetEncoder enc)73{74return new StreamEncoder(out, lock, enc);75}767778// Factory for java.nio.channels.Channels.newWriter7980public static StreamEncoder forEncoder(WritableByteChannel ch,81CharsetEncoder enc,82int minBufferCap)83{84return new StreamEncoder(ch, enc, minBufferCap);85}868788// -- Public methods corresponding to those in OutputStreamWriter --8990// All synchronization and state/argument checking is done in these public91// methods; the concrete stream-encoder subclasses defined below need not92// do any such checking.9394public String getEncoding() {95if (isOpen())96return encodingName();97return null;98}99100public void flushBuffer() throws IOException {101synchronized (lock) {102if (isOpen())103implFlushBuffer();104else105throw new IOException("Stream closed");106}107}108109public void write(int c) throws IOException {110char cbuf[] = new char[1];111cbuf[0] = (char) c;112write(cbuf, 0, 1);113}114115public void write(char cbuf[], int off, int len) throws IOException {116synchronized (lock) {117ensureOpen();118if ((off < 0) || (off > cbuf.length) || (len < 0) ||119((off + len) > cbuf.length) || ((off + len) < 0)) {120throw new IndexOutOfBoundsException();121} else if (len == 0) {122return;123}124implWrite(cbuf, off, len);125}126}127128public void write(String str, int off, int len) throws IOException {129/* Check the len before creating a char buffer */130if (len < 0)131throw new IndexOutOfBoundsException();132char cbuf[] = new char[len];133str.getChars(off, off + len, cbuf, 0);134write(cbuf, 0, len);135}136137public void flush() throws IOException {138synchronized (lock) {139ensureOpen();140implFlush();141}142}143144public void close() throws IOException {145synchronized (lock) {146if (!isOpen)147return;148implClose();149isOpen = false;150}151}152153private boolean isOpen() {154return isOpen;155}156157158// -- Charset-based stream encoder impl --159160private Charset cs;161private CharsetEncoder encoder;162private ByteBuffer bb;163164// Exactly one of these is non-null165private final OutputStream out;166private WritableByteChannel ch;167168// Leftover first char in a surrogate pair169private boolean haveLeftoverChar = false;170private char leftoverChar;171private CharBuffer lcb = null;172173private StreamEncoder(OutputStream out, Object lock, Charset cs) {174this(out, lock,175cs.newEncoder()176.onMalformedInput(CodingErrorAction.REPLACE)177.onUnmappableCharacter(CodingErrorAction.REPLACE));178}179180private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {181super(lock);182this.out = out;183this.ch = null;184this.cs = enc.charset();185this.encoder = enc;186187// This path disabled until direct buffers are faster188if (false && out instanceof FileOutputStream) {189ch = ((FileOutputStream)out).getChannel();190if (ch != null)191bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);192}193if (ch == null) {194bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);195}196}197198private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {199this.out = null;200this.ch = ch;201this.cs = enc.charset();202this.encoder = enc;203this.bb = ByteBuffer.allocate(mbc < 0204? DEFAULT_BYTE_BUFFER_SIZE205: mbc);206}207208private void writeBytes() throws IOException {209bb.flip();210int lim = bb.limit();211int pos = bb.position();212assert (pos <= lim);213int rem = (pos <= lim ? lim - pos : 0);214215if (rem > 0) {216if (ch != null) {217if (ch.write(bb) != rem)218assert false : rem;219} else {220out.write(bb.array(), bb.arrayOffset() + pos, rem);221}222}223bb.clear();224}225226private void flushLeftoverChar(CharBuffer cb, boolean endOfInput)227throws IOException228{229if (!haveLeftoverChar && !endOfInput)230return;231if (lcb == null)232lcb = CharBuffer.allocate(2);233else234lcb.clear();235if (haveLeftoverChar)236lcb.put(leftoverChar);237if ((cb != null) && cb.hasRemaining())238lcb.put(cb.get());239lcb.flip();240while (lcb.hasRemaining() || endOfInput) {241CoderResult cr = encoder.encode(lcb, bb, endOfInput);242if (cr.isUnderflow()) {243if (lcb.hasRemaining()) {244leftoverChar = lcb.get();245if (cb != null && cb.hasRemaining())246flushLeftoverChar(cb, endOfInput);247return;248}249break;250}251if (cr.isOverflow()) {252assert bb.position() > 0;253writeBytes();254continue;255}256cr.throwException();257}258haveLeftoverChar = false;259}260261void implWrite(char cbuf[], int off, int len)262throws IOException263{264CharBuffer cb = CharBuffer.wrap(cbuf, off, len);265266if (haveLeftoverChar)267flushLeftoverChar(cb, false);268269while (cb.hasRemaining()) {270CoderResult cr = encoder.encode(cb, bb, false);271if (cr.isUnderflow()) {272assert (cb.remaining() <= 1) : cb.remaining();273if (cb.remaining() == 1) {274haveLeftoverChar = true;275leftoverChar = cb.get();276}277break;278}279if (cr.isOverflow()) {280assert bb.position() > 0;281writeBytes();282continue;283}284cr.throwException();285}286}287288void implFlushBuffer() throws IOException {289if (bb.position() > 0)290writeBytes();291}292293void implFlush() throws IOException {294implFlushBuffer();295if (out != null)296out.flush();297}298299void implClose() throws IOException {300flushLeftoverChar(null, true);301try {302for (;;) {303CoderResult cr = encoder.flush(bb);304if (cr.isUnderflow())305break;306if (cr.isOverflow()) {307assert bb.position() > 0;308writeBytes();309continue;310}311cr.throwException();312}313314if (bb.position() > 0)315writeBytes();316if (ch != null)317ch.close();318else319out.close();320} catch (IOException x) {321encoder.reset();322throw x;323}324}325326String encodingName() {327return ((cs instanceof HistoricallyNamedCharset)328? ((HistoricallyNamedCharset)cs).historicalName()329: cs.name());330}331}332333334