1 package com.atlassian.plugin.webresource.transformer;
2
3 import java.io.IOException;
4 import java.io.OutputStream;
5 import java.io.Writer;
6 import java.nio.ByteBuffer;
7 import java.nio.CharBuffer;
8 import java.nio.charset.Charset;
9 import java.nio.charset.CharsetDecoder;
10 import java.nio.charset.CoderResult;
11 import java.nio.charset.CodingErrorAction;
12
13
14
15
16 class WriterOutputStream extends OutputStream
17 {
18 private final Writer writer;
19 private final CharsetDecoder decoder;
20 private final boolean writeImmediately;
21 private final ByteBuffer decoderIn;
22 private final CharBuffer decoderOut;
23
24 WriterOutputStream(final Writer writer, final Charset charset, final int bufferSize, final boolean writeImmediately)
25 {
26 decoderIn = ByteBuffer.allocate(128);
27
28 this.writer = writer;
29 decoder = charset.newDecoder();
30 decoder.onMalformedInput(CodingErrorAction.REPLACE);
31 decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
32 decoder.replaceWith("?");
33 this.writeImmediately = writeImmediately;
34 decoderOut = CharBuffer.allocate(bufferSize);
35 }
36
37 WriterOutputStream(final Writer writer, final Charset charset)
38 {
39 this(writer, charset, 1024, false);
40 }
41
42 WriterOutputStream(final Writer writer, final String charsetName, final int bufferSize, final boolean writeImmediately)
43 {
44 this(writer, Charset.forName(charsetName), bufferSize, writeImmediately);
45 }
46
47 WriterOutputStream(final Writer writer, final String charsetName)
48 {
49 this(writer, charsetName, 1024, false);
50 }
51
52 WriterOutputStream(final Writer writer)
53 {
54 this(writer, Charset.defaultCharset(), 1024, false);
55 }
56
57 @Override
58 public void write(final byte[] b, int off, int len) throws IOException
59 {
60 while (len > 0)
61 {
62 final int c = Math.min(len, decoderIn.remaining());
63 decoderIn.put(b, off, c);
64 processInput(false);
65 len -= c;
66 off += c;
67 }
68 if (writeImmediately)
69 {
70 flushOutput();
71 }
72 }
73
74 @Override
75 public void write(final byte[] b) throws IOException
76 {
77 write(b, 0, b.length);
78 }
79
80 @Override
81 public void write(final int b) throws IOException
82 {
83 write(new byte[] { (byte) b }, 0, 1);
84 }
85
86 @Override
87 public void flush() throws IOException
88 {
89 flushOutput();
90 writer.flush();
91 }
92
93 @Override
94 public void close() throws IOException
95 {
96 processInput(true);
97 flushOutput();
98 writer.close();
99 }
100
101 private void processInput(final boolean endOfInput) throws IOException
102 {
103 decoderIn.flip();
104 CoderResult coderResult;
105 while (true)
106 {
107 coderResult = decoder.decode(decoderIn, decoderOut, endOfInput);
108 if (!(coderResult.isOverflow()))
109 {
110 break;
111 }
112 flushOutput();
113 }
114 if (!(coderResult.isUnderflow()))
115 {
116 throw new IOException("Unexpected coder result");
117 }
118
119 decoderIn.compact();
120 }
121
122 private void flushOutput() throws IOException
123 {
124 if (decoderOut.position() > 0)
125 {
126 writer.write(decoderOut.array(), 0, decoderOut.position());
127 decoderOut.rewind();
128 }
129 }
130 }