Path: blob/master/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
41159 views
/*1* Copyright (c) 2001, 2021, 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*/2425package sun.nio.cs;2627import java.io.IOException;28import java.io.OutputStream;29import java.io.UnsupportedEncodingException;30import java.io.Writer;31import java.nio.ByteBuffer;32import java.nio.CharBuffer;33import java.nio.channels.WritableByteChannel;34import java.nio.charset.Charset;35import java.nio.charset.CharsetEncoder;36import java.nio.charset.CoderResult;37import java.nio.charset.CodingErrorAction;38import java.nio.charset.IllegalCharsetNameException;39import java.nio.charset.UnsupportedCharsetException;4041public class StreamEncoder extends Writer42{4344private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;4546private volatile boolean closed;4748private void ensureOpen() throws IOException {49if (closed)50throw new IOException("Stream closed");51}5253// Factories for java.io.OutputStreamWriter54public static StreamEncoder forOutputStreamWriter(OutputStream out,55Object lock,56String charsetName)57throws UnsupportedEncodingException58{59String csn = charsetName;60if (csn == null) {61csn = Charset.defaultCharset().name();62}63try {64return new StreamEncoder(out, lock, Charset.forName(csn));65} catch (IllegalCharsetNameException | UnsupportedCharsetException x) {66throw new UnsupportedEncodingException (csn);67}68}6970public static StreamEncoder forOutputStreamWriter(OutputStream out,71Object lock,72Charset cs)73{74return new StreamEncoder(out, lock, cs);75}7677public static StreamEncoder forOutputStreamWriter(OutputStream out,78Object lock,79CharsetEncoder enc)80{81return new StreamEncoder(out, lock, enc);82}838485// Factory for java.nio.channels.Channels.newWriter8687public static StreamEncoder forEncoder(WritableByteChannel ch,88CharsetEncoder enc,89int minBufferCap)90{91return new StreamEncoder(ch, enc, minBufferCap);92}939495// -- Public methods corresponding to those in OutputStreamWriter --9697// All synchronization and state/argument checking is done in these public98// methods; the concrete stream-encoder subclasses defined below need not99// do any such checking.100101public String getEncoding() {102if (isOpen())103return encodingName();104return null;105}106107public void flushBuffer() throws IOException {108synchronized (lock) {109if (isOpen())110implFlushBuffer();111else112throw new IOException("Stream closed");113}114}115116public void write(int c) throws IOException {117char[] cbuf = new char[1];118cbuf[0] = (char) c;119write(cbuf, 0, 1);120}121122public void write(char[] cbuf, int off, int len) throws IOException {123synchronized (lock) {124ensureOpen();125if ((off < 0) || (off > cbuf.length) || (len < 0) ||126((off + len) > cbuf.length) || ((off + len) < 0)) {127throw new IndexOutOfBoundsException();128} else if (len == 0) {129return;130}131implWrite(cbuf, off, len);132}133}134135public void write(String str, int off, int len) throws IOException {136/* Check the len before creating a char buffer */137if (len < 0)138throw new IndexOutOfBoundsException();139char[] cbuf = new char[len];140str.getChars(off, off + len, cbuf, 0);141write(cbuf, 0, len);142}143144public void write(CharBuffer cb) throws IOException {145int position = cb.position();146try {147synchronized (lock) {148ensureOpen();149implWrite(cb);150}151} finally {152cb.position(position);153}154}155156public void flush() throws IOException {157synchronized (lock) {158ensureOpen();159implFlush();160}161}162163public void close() throws IOException {164synchronized (lock) {165if (closed)166return;167try {168implClose();169} finally {170closed = true;171}172}173}174175private boolean isOpen() {176return !closed;177}178179180// -- Charset-based stream encoder impl --181182private final Charset cs;183private final CharsetEncoder encoder;184private final ByteBuffer bb;185186// Exactly one of these is non-null187private final OutputStream out;188private final WritableByteChannel ch;189190// Leftover first char in a surrogate pair191private boolean haveLeftoverChar = false;192private char leftoverChar;193private CharBuffer lcb = null;194195private StreamEncoder(OutputStream out, Object lock, Charset cs) {196this(out, lock,197cs.newEncoder()198.onMalformedInput(CodingErrorAction.REPLACE)199.onUnmappableCharacter(CodingErrorAction.REPLACE));200}201202private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {203super(lock);204this.out = out;205this.ch = null;206this.cs = enc.charset();207this.encoder = enc;208this.bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);209}210211private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {212this.out = null;213this.ch = ch;214this.cs = enc.charset();215this.encoder = enc;216this.bb = ByteBuffer.allocate(mbc < 0217? DEFAULT_BYTE_BUFFER_SIZE218: mbc);219}220221private void writeBytes() throws IOException {222bb.flip();223int lim = bb.limit();224int pos = bb.position();225assert (pos <= lim);226int rem = (pos <= lim ? lim - pos : 0);227228if (rem > 0) {229if (ch != null) {230int wc = ch.write(bb);231assert wc == rem : rem;232} else {233out.write(bb.array(), bb.arrayOffset() + pos, rem);234}235}236bb.clear();237}238239private void flushLeftoverChar(CharBuffer cb, boolean endOfInput)240throws IOException241{242if (!haveLeftoverChar && !endOfInput)243return;244if (lcb == null)245lcb = CharBuffer.allocate(2);246else247lcb.clear();248if (haveLeftoverChar)249lcb.put(leftoverChar);250if ((cb != null) && cb.hasRemaining())251lcb.put(cb.get());252lcb.flip();253while (lcb.hasRemaining() || endOfInput) {254CoderResult cr = encoder.encode(lcb, bb, endOfInput);255if (cr.isUnderflow()) {256if (lcb.hasRemaining()) {257leftoverChar = lcb.get();258if (cb != null && cb.hasRemaining()) {259lcb.clear();260lcb.put(leftoverChar).put(cb.get()).flip();261continue;262}263return;264}265break;266}267if (cr.isOverflow()) {268assert bb.position() > 0;269writeBytes();270continue;271}272cr.throwException();273}274haveLeftoverChar = false;275}276277void implWrite(char[] cbuf, int off, int len)278throws IOException279{280CharBuffer cb = CharBuffer.wrap(cbuf, off, len);281implWrite(cb);282}283284void implWrite(CharBuffer cb)285throws IOException286{287if (haveLeftoverChar) {288flushLeftoverChar(cb, false);289}290291while (cb.hasRemaining()) {292CoderResult cr = encoder.encode(cb, bb, false);293if (cr.isUnderflow()) {294assert (cb.remaining() <= 1) : cb.remaining();295if (cb.remaining() == 1) {296haveLeftoverChar = true;297leftoverChar = cb.get();298}299break;300}301if (cr.isOverflow()) {302assert bb.position() > 0;303writeBytes();304continue;305}306cr.throwException();307}308}309310void implFlushBuffer() throws IOException {311if (bb.position() > 0) {312writeBytes();313}314}315316void implFlush() throws IOException {317implFlushBuffer();318if (out != null) {319out.flush();320}321}322323void implClose() throws IOException {324flushLeftoverChar(null, true);325try {326for (;;) {327CoderResult cr = encoder.flush(bb);328if (cr.isUnderflow())329break;330if (cr.isOverflow()) {331assert bb.position() > 0;332writeBytes();333continue;334}335cr.throwException();336}337338if (bb.position() > 0)339writeBytes();340if (ch != null)341ch.close();342else {343try {344out.flush();345} finally {346out.close();347}348}349} catch (IOException x) {350encoder.reset();351throw x;352}353}354355String encodingName() {356return ((cs instanceof HistoricallyNamedCharset)357? ((HistoricallyNamedCharset)cs).historicalName()358: cs.name());359}360}361362363