< prev index next >

src/java.base/share/classes/sun/nio/cs/StreamEncoder.java

Print this page

 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package sun.nio.cs;
 27 
 28 import java.io.IOException;
 29 import java.io.OutputStream;
 30 import java.io.UnsupportedEncodingException;
 31 import java.io.Writer;
 32 import java.nio.ByteBuffer;
 33 import java.nio.CharBuffer;
 34 import java.nio.channels.WritableByteChannel;
 35 import java.nio.charset.Charset;
 36 import java.nio.charset.CharsetEncoder;
 37 import java.nio.charset.CoderResult;
 38 import java.nio.charset.CodingErrorAction;
 39 import java.nio.charset.IllegalCharsetNameException;
 40 import java.nio.charset.UnsupportedCharsetException;

 41 
 42 public class StreamEncoder extends Writer
 43 {
 44 
 45     private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;

 46 
 47     private volatile boolean closed;
 48 
 49     private void ensureOpen() throws IOException {
 50         if (closed)
 51             throw new IOException("Stream closed");
 52     }
 53 
 54     // Factories for java.io.OutputStreamWriter
 55     public static StreamEncoder forOutputStreamWriter(OutputStream out,
 56                                                       Object lock,
 57                                                       String charsetName)
 58         throws UnsupportedEncodingException
 59     {
 60         String csn = charsetName;
 61         if (csn == null) {
 62             csn = Charset.defaultCharset().name();
 63         }
 64         try {
 65             return new StreamEncoder(out, lock, Charset.forName(csn));

 89                                            CharsetEncoder enc,
 90                                            int minBufferCap)
 91     {
 92         return new StreamEncoder(ch, enc, minBufferCap);
 93     }
 94 
 95 
 96     // -- Public methods corresponding to those in OutputStreamWriter --
 97 
 98     // All synchronization and state/argument checking is done in these public
 99     // methods; the concrete stream-encoder subclasses defined below need not
100     // do any such checking.
101 
102     public String getEncoding() {
103         if (isOpen())
104             return encodingName();
105         return null;
106     }
107 
108     public void flushBuffer() throws IOException {
109         synchronized (lock) {
110             if (isOpen())
111                 implFlushBuffer();
112             else
113                 throw new IOException("Stream closed");






114         }
115     }
116 







117     public void write(int c) throws IOException {
118         char[] cbuf = new char[1];
119         cbuf[0] = (char) c;
120         write(cbuf, 0, 1);
121     }
122 
123     public void write(char[] cbuf, int off, int len) throws IOException {
124         synchronized (lock) {
125             ensureOpen();
126             if ((off < 0) || (off > cbuf.length) || (len < 0) ||
127                 ((off + len) > cbuf.length) || ((off + len) < 0)) {
128                 throw new IndexOutOfBoundsException();
129             } else if (len == 0) {
130                 return;
131             }
132             implWrite(cbuf, off, len);













133         }

134     }
135 
136     public void write(String str, int off, int len) throws IOException {
137         /* Check the len before creating a char buffer */
138         if (len < 0)
139             throw new IndexOutOfBoundsException();
140         char[] cbuf = new char[len];
141         str.getChars(off, off + len, cbuf, 0);
142         write(cbuf, 0, len);
143     }
144 
145     public void write(CharBuffer cb) throws IOException {
146         int position = cb.position();
147         try {
148             synchronized (lock) {
149                 ensureOpen();
150                 implWrite(cb);








151             }
152         } finally {
153             cb.position(position);
154         }
155     }
156 





157     public void flush() throws IOException {
158         synchronized (lock) {
159             ensureOpen();
160             implFlush();








161         }
162     }
163 





164     public void close() throws IOException {
165         synchronized (lock) {
166             if (closed)
167                 return;
168             try {
169                 implClose();
170             } finally {
171                 closed = true;




172             }
173         }
174     }
175 










176     private boolean isOpen() {
177         return !closed;
178     }
179 
180 
181     // -- Charset-based stream encoder impl --
182 
183     private final Charset cs;
184     private final CharsetEncoder encoder;
185     private final ByteBuffer bb;

186 
187     // Exactly one of these is non-null
188     private final OutputStream out;
189     private final WritableByteChannel ch;
190 
191     // Leftover first char in a surrogate pair
192     private boolean haveLeftoverChar = false;
193     private char leftoverChar;
194     private CharBuffer lcb = null;
195 
196     private StreamEncoder(OutputStream out, Object lock, Charset cs) {
197         this(out, lock,
198             cs.newEncoder()
199                 .onMalformedInput(CodingErrorAction.REPLACE)
200                 .onUnmappableCharacter(CodingErrorAction.REPLACE));
201     }
202 
203     private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {
204         super(lock);
205         this.out = out;
206         this.ch = null;
207         this.cs = enc.charset();
208         this.encoder = enc;
209         this.bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);


210     }
211 
212     private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {
213         this.out = null;
214         this.ch = ch;
215         this.cs = enc.charset();
216         this.encoder = enc;
217         this.bb = ByteBuffer.allocate(mbc < 0
218                                   ? DEFAULT_BYTE_BUFFER_SIZE
219                                   : mbc);





220     }
221 
222     private void writeBytes() throws IOException {
223         bb.flip();
224         int lim = bb.limit();
225         int pos = bb.position();
226         assert (pos <= lim);
227         int rem = (pos <= lim ? lim - pos : 0);
228 
229         if (rem > 0) {
230             if (ch != null) {
231                 int wc = ch.write(bb);
232                 assert wc == rem : rem;
233             } else {
234                 out.write(bb.array(), bb.arrayOffset() + pos, rem);
235             }
236         }
237         bb.clear();
238     }
239 

272             }
273             cr.throwException();
274         }
275         haveLeftoverChar = false;
276     }
277 
278     void implWrite(char[] cbuf, int off, int len)
279         throws IOException
280     {
281         CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
282         implWrite(cb);
283     }
284 
285     void implWrite(CharBuffer cb)
286         throws IOException
287     {
288         if (haveLeftoverChar) {
289             flushLeftoverChar(cb, false);
290         }
291 


292         while (cb.hasRemaining()) {
293             CoderResult cr = encoder.encode(cb, bb, false);
294             if (cr.isUnderflow()) {
295                 assert (cb.remaining() <= 1) : cb.remaining();
296                 if (cb.remaining() == 1) {
297                     haveLeftoverChar = true;
298                     leftoverChar = cb.get();
299                 }
300                 break;
301             }
302             if (cr.isOverflow()) {
303                 assert bb.position() > 0;
304                 writeBytes();
305                 continue;
306             }
307             cr.throwException();
308         }
309     }
310 















311     void implFlushBuffer() throws IOException {
312         if (bb.position() > 0) {
313             writeBytes();
314         }
315     }
316 
317     void implFlush() throws IOException {
318         implFlushBuffer();
319         if (out != null) {
320             out.flush();
321         }
322     }
323 
324     void implClose() throws IOException {
325         flushLeftoverChar(null, true);
326         try {
327             for (;;) {
328                 CoderResult cr = encoder.flush(bb);
329                 if (cr.isUnderflow())
330                     break;

 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package sun.nio.cs;
 27 
 28 import java.io.IOException;
 29 import java.io.OutputStream;
 30 import java.io.UnsupportedEncodingException;
 31 import java.io.Writer;
 32 import java.nio.ByteBuffer;
 33 import java.nio.CharBuffer;
 34 import java.nio.channels.WritableByteChannel;
 35 import java.nio.charset.Charset;
 36 import java.nio.charset.CharsetEncoder;
 37 import java.nio.charset.CoderResult;
 38 import java.nio.charset.CodingErrorAction;
 39 import java.nio.charset.IllegalCharsetNameException;
 40 import java.nio.charset.UnsupportedCharsetException;
 41 import jdk.internal.misc.InternalLock;
 42 
 43 public final class StreamEncoder extends Writer {

 44 
 45     private static final int INITIAL_BYTE_BUFFER_CAPACITY = 512;
 46     private static final int MAX_BYTE_BUFFER_CAPACITY = 8192;
 47 
 48     private volatile boolean closed;
 49 
 50     private void ensureOpen() throws IOException {
 51         if (closed)
 52             throw new IOException("Stream closed");
 53     }
 54 
 55     // Factories for java.io.OutputStreamWriter
 56     public static StreamEncoder forOutputStreamWriter(OutputStream out,
 57                                                       Object lock,
 58                                                       String charsetName)
 59         throws UnsupportedEncodingException
 60     {
 61         String csn = charsetName;
 62         if (csn == null) {
 63             csn = Charset.defaultCharset().name();
 64         }
 65         try {
 66             return new StreamEncoder(out, lock, Charset.forName(csn));

 90                                            CharsetEncoder enc,
 91                                            int minBufferCap)
 92     {
 93         return new StreamEncoder(ch, enc, minBufferCap);
 94     }
 95 
 96 
 97     // -- Public methods corresponding to those in OutputStreamWriter --
 98 
 99     // All synchronization and state/argument checking is done in these public
100     // methods; the concrete stream-encoder subclasses defined below need not
101     // do any such checking.
102 
103     public String getEncoding() {
104         if (isOpen())
105             return encodingName();
106         return null;
107     }
108 
109     public void flushBuffer() throws IOException {
110         if (lock instanceof InternalLock locker) {
111             locker.lock();
112             try {
113                 lockedFlushBuffer();
114             } finally {
115                 locker.unlock();
116             }
117         } else {
118             synchronized (super.lock) {
119                 lockedFlushBuffer();
120             }
121         }
122     }
123 
124     private void lockedFlushBuffer() throws IOException {
125         if (isOpen())
126             implFlushBuffer();
127         else
128             throw new IOException("Stream closed");
129     }
130 
131     public void write(int c) throws IOException {
132         char[] cbuf = new char[1];
133         cbuf[0] = (char) c;
134         write(cbuf, 0, 1);
135     }
136 
137     public void write(char[] cbuf, int off, int len) throws IOException {
138         if (lock instanceof InternalLock locker) {
139             locker.lock();
140             try {
141                 lockedWrite(cbuf, off, len);
142             } finally {
143                 locker.unlock();

144             }
145         } else {
146             synchronized (super.lock) {
147                 lockedWrite(cbuf, off, len);
148             }
149         }
150     }
151 
152     private void lockedWrite(char[] cbuf, int off, int len) throws IOException {
153         ensureOpen();
154         if ((off < 0) || (off > cbuf.length) || (len < 0) ||
155                 ((off + len) > cbuf.length) || ((off + len) < 0)) {
156             throw new IndexOutOfBoundsException();
157         } else if (len == 0) {
158             return;
159         }
160         implWrite(cbuf, off, len);
161     }
162 
163     public void write(String str, int off, int len) throws IOException {
164         /* Check the len before creating a char buffer */
165         if (len < 0)
166             throw new IndexOutOfBoundsException();
167         char[] cbuf = new char[len];
168         str.getChars(off, off + len, cbuf, 0);
169         write(cbuf, 0, len);
170     }
171 
172     public void write(CharBuffer cb) throws IOException {
173         int position = cb.position();
174         try {
175             if (lock instanceof InternalLock locker) {
176                 locker.lock();
177                 try {
178                     lockedWrite(cb);
179                 } finally {
180                     locker.unlock();
181                 }
182             } else {
183                 synchronized (super.lock) {
184                     lockedWrite(cb);
185                 }
186             }
187         } finally {
188             cb.position(position);
189         }
190     }
191 
192     private void lockedWrite(CharBuffer cb) throws IOException {
193         ensureOpen();
194         implWrite(cb);
195     }
196 
197     public void flush() throws IOException {
198         if (lock instanceof InternalLock locker) {
199             locker.lock();
200             try {
201                 lockedFlush();
202             } finally {
203                 locker.unlock();
204             }
205         } else {
206             synchronized (lock) {
207                 lockedFlush();
208             }
209         }
210     }
211 
212     private void lockedFlush() throws IOException {
213         ensureOpen();
214         implFlush();
215     }
216 
217     public void close() throws IOException {
218         if (lock instanceof InternalLock locker) {
219             locker.lock();

220             try {
221                 lockedClose();
222             } finally {
223                 locker.unlock();
224             }
225         } else {
226             synchronized (lock) {
227                 lockedClose();
228             }
229         }
230     }
231 
232     private void lockedClose() throws IOException {
233         if (closed)
234             return;
235         try {
236             implClose();
237         } finally {
238             closed = true;
239         }
240     }
241 
242     private boolean isOpen() {
243         return !closed;
244     }
245 
246 
247     // -- Charset-based stream encoder impl --
248 
249     private final Charset cs;
250     private final CharsetEncoder encoder;
251     private ByteBuffer bb;
252     private final int maxBufferCapacity;
253 
254     // Exactly one of these is non-null
255     private final OutputStream out;
256     private final WritableByteChannel ch;
257 
258     // Leftover first char in a surrogate pair
259     private boolean haveLeftoverChar = false;
260     private char leftoverChar;
261     private CharBuffer lcb = null;
262 
263     private StreamEncoder(OutputStream out, Object lock, Charset cs) {
264         this(out, lock,
265             cs.newEncoder()
266                 .onMalformedInput(CodingErrorAction.REPLACE)
267                 .onUnmappableCharacter(CodingErrorAction.REPLACE));
268     }
269 
270     private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {
271         super(lock);
272         this.out = out;
273         this.ch = null;
274         this.cs = enc.charset();
275         this.encoder = enc;
276 
277         this.bb = ByteBuffer.allocate(INITIAL_BYTE_BUFFER_CAPACITY);
278         this.maxBufferCapacity = MAX_BYTE_BUFFER_CAPACITY;
279     }
280 
281     private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {
282         this.out = null;
283         this.ch = ch;
284         this.cs = enc.charset();
285         this.encoder = enc;
286 
287         if (mbc > 0) {
288             this.bb = ByteBuffer.allocate(mbc);
289             this.maxBufferCapacity = mbc;
290         } else {
291             this.bb = ByteBuffer.allocate(INITIAL_BYTE_BUFFER_CAPACITY);
292             this.maxBufferCapacity = MAX_BYTE_BUFFER_CAPACITY;
293         }
294     }
295 
296     private void writeBytes() throws IOException {
297         bb.flip();
298         int lim = bb.limit();
299         int pos = bb.position();
300         assert (pos <= lim);
301         int rem = (pos <= lim ? lim - pos : 0);
302 
303         if (rem > 0) {
304             if (ch != null) {
305                 int wc = ch.write(bb);
306                 assert wc == rem : rem;
307             } else {
308                 out.write(bb.array(), bb.arrayOffset() + pos, rem);
309             }
310         }
311         bb.clear();
312     }
313 

346             }
347             cr.throwException();
348         }
349         haveLeftoverChar = false;
350     }
351 
352     void implWrite(char[] cbuf, int off, int len)
353         throws IOException
354     {
355         CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
356         implWrite(cb);
357     }
358 
359     void implWrite(CharBuffer cb)
360         throws IOException
361     {
362         if (haveLeftoverChar) {
363             flushLeftoverChar(cb, false);
364         }
365 
366         growByteBufferIfNeeded(cb.remaining());
367 
368         while (cb.hasRemaining()) {
369             CoderResult cr = encoder.encode(cb, bb, false);
370             if (cr.isUnderflow()) {
371                 assert (cb.remaining() <= 1) : cb.remaining();
372                 if (cb.remaining() == 1) {
373                     haveLeftoverChar = true;
374                     leftoverChar = cb.get();
375                 }
376                 break;
377             }
378             if (cr.isOverflow()) {
379                 assert bb.position() > 0;
380                 writeBytes();
381                 continue;
382             }
383             cr.throwException();
384         }
385     }
386 
387     /**
388      * Grows bb to a capacity to allow len characters be encoded.
389      */
390     void growByteBufferIfNeeded(int len) throws IOException {
391         int cap = bb.capacity();
392         if (cap < maxBufferCapacity) {
393             int maxBytes = len * Math.round(encoder.maxBytesPerChar());
394             int newCap = Math.min(maxBytes, maxBufferCapacity);
395             if (newCap > cap) {
396                 implFlushBuffer();
397                 bb = ByteBuffer.allocate(newCap);
398             }
399         }
400     }
401 
402     void implFlushBuffer() throws IOException {
403         if (bb.position() > 0) {
404             writeBytes();
405         }
406     }
407 
408     void implFlush() throws IOException {
409         implFlushBuffer();
410         if (out != null) {
411             out.flush();
412         }
413     }
414 
415     void implClose() throws IOException {
416         flushLeftoverChar(null, true);
417         try {
418             for (;;) {
419                 CoderResult cr = encoder.flush(bb);
420                 if (cr.isUnderflow())
421                     break;
< prev index next >