001
014
015 package com.liferay.portal.kernel.io;
016
017 import com.liferay.portal.kernel.nio.charset.CharsetDecoderUtil;
018 import com.liferay.portal.kernel.nio.charset.CharsetEncoderUtil;
019 import com.liferay.portal.kernel.util.StringPool;
020
021 import java.io.IOException;
022 import java.io.OutputStream;
023 import java.io.Writer;
024
025 import java.nio.ByteBuffer;
026 import java.nio.CharBuffer;
027 import java.nio.charset.CharsetDecoder;
028 import java.nio.charset.CharsetEncoder;
029 import java.nio.charset.CoderResult;
030
031
034 public class WriterOutputStream extends OutputStream {
035
036 public WriterOutputStream(Writer writer) {
037 this(
038 writer, StringPool.DEFAULT_CHARSET_NAME,
039 _DEFAULT_OUTPUT_BUFFER_SIZE, false);
040 }
041
042 public WriterOutputStream(Writer writer, String charsetName) {
043 this(writer, charsetName, _DEFAULT_OUTPUT_BUFFER_SIZE, false);
044 }
045
046 public WriterOutputStream(
047 Writer writer, String charsetName, boolean autoFlush) {
048
049 this(writer, charsetName, _DEFAULT_OUTPUT_BUFFER_SIZE, autoFlush);
050 }
051
052 public WriterOutputStream(
053 Writer writer, String charsetName, int outputBufferSize) {
054
055 this(writer, charsetName, outputBufferSize, false);
056 }
057
058 public WriterOutputStream(
059 Writer writer, String charsetName, int outputBufferSize,
060 boolean autoFlush) {
061
062 if (outputBufferSize <= 0) {
063 if (autoFlush) {
064 outputBufferSize = _DEFAULT_OUTPUT_BUFFER_SIZE;
065 }
066 else {
067 throw new IllegalArgumentException(
068 "Output buffer size " + outputBufferSize +
069 " must be a positive number");
070 }
071 }
072
073 if (charsetName == null) {
074 charsetName = StringPool.DEFAULT_CHARSET_NAME;
075 }
076
077 _writer = writer;
078 _charsetName = charsetName;
079 _charsetDecoder = CharsetDecoderUtil.getCharsetDecoder(charsetName);
080
081 CharsetEncoder charsetEncoder = CharsetEncoderUtil.getCharsetEncoder(
082 charsetName);
083
084 _inputByteBuffer = ByteBuffer.allocate(
085 (int)Math.ceil(charsetEncoder.maxBytesPerChar()));
086
087 _inputByteBuffer.limit(0);
088
089 _outputCharBuffer = CharBuffer.allocate(outputBufferSize);
090 _autoFlush = autoFlush;
091 }
092
093 @Override
094 public void close() throws IOException {
095 _doDecode(_inputByteBuffer, true);
096
097 _flushBuffer();
098
099 _writer.close();
100 }
101
102 @Override
103 public void flush() throws IOException {
104 _flushBuffer();
105
106 _writer.flush();
107 }
108
109 public String getEncoding() {
110 return _charsetName;
111 }
112
113 @Override
114 public void write(byte[] bytes) throws IOException {
115 write(bytes, 0, bytes.length);
116 }
117
118 @Override
119 public void write(byte[] bytes, int offset, int length) throws IOException {
120 while (_inputByteBuffer.hasRemaining()) {
121 write(bytes[offset++]);
122
123 length--;
124 }
125
126 ByteBuffer inputByteBuffer = ByteBuffer.wrap(bytes, offset, length);
127
128 _doDecode(inputByteBuffer, false);
129
130 if (inputByteBuffer.hasRemaining()) {
131 _inputByteBuffer.limit(inputByteBuffer.remaining());
132
133 _inputByteBuffer.put(inputByteBuffer);
134
135 _inputByteBuffer.flip();
136 }
137 }
138
139 @Override
140 public void write(int b) throws IOException {
141 int limit = _inputByteBuffer.limit();
142
143 _inputByteBuffer.limit(limit + 1);
144
145 _inputByteBuffer.put(limit, (byte)b);
146
147 _doDecode(_inputByteBuffer, false);
148
149 if (!_inputByteBuffer.hasRemaining()) {
150 _inputByteBuffer.position(0);
151
152 _inputByteBuffer.limit(0);
153 }
154 }
155
156 private void _doDecode(ByteBuffer inputByteBuffer, boolean endOfInput)
157 throws IOException {
158
159 while (true) {
160 CoderResult coderResult = _charsetDecoder.decode(
161 inputByteBuffer, _outputCharBuffer, endOfInput);
162
163 if (coderResult.isOverflow()) {
164 _flushBuffer();
165 }
166 else if (coderResult.isUnderflow()) {
167 if (_autoFlush) {
168 _flushBuffer();
169 }
170
171 break;
172 }
173 else {
174 throw new IOException("Unexcepted coder result " + coderResult);
175 }
176 }
177 }
178
179 private void _flushBuffer() throws IOException {
180 if (_outputCharBuffer.position() > 0) {
181 _writer.write(
182 _outputCharBuffer.array(), 0, _outputCharBuffer.position());
183
184 _outputCharBuffer.rewind();
185 }
186 }
187
188 private static final int _DEFAULT_OUTPUT_BUFFER_SIZE = 8192;
189
190 private final boolean _autoFlush;
191 private final CharsetDecoder _charsetDecoder;
192 private final String _charsetName;
193 private final ByteBuffer _inputByteBuffer;
194 private final CharBuffer _outputCharBuffer;
195 private final Writer _writer;
196
197 }