1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.io.output;
18
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.io.OutputStreamWriter;
25 import java.io.StringWriter;
26 import java.io.Writer;
27 import java.nio.charset.Charset;
28 import java.nio.charset.StandardCharsets;
29 import java.util.Locale;
30 import java.util.Objects;
31 import java.util.regex.Matcher;
32
33 import org.apache.commons.io.Charsets;
34 import org.apache.commons.io.IOUtils;
35 import org.apache.commons.io.build.AbstractStreamBuilder;
36 import org.apache.commons.io.input.XmlStreamReader;
37
38
39
40
41
42
43
44
45
46
47
48 public class XmlStreamWriter extends Writer {
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public static class Builder extends AbstractStreamBuilder<XmlStreamWriter, Builder> {
69
70
71
72
73 public Builder() {
74 setCharsetDefault(StandardCharsets.UTF_8);
75 setCharset(StandardCharsets.UTF_8);
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 @SuppressWarnings("resource")
98 @Override
99 public XmlStreamWriter get() throws IOException {
100 return new XmlStreamWriter(getOutputStream(), getCharset());
101 }
102
103 }
104
105 private static final int BUFFER_SIZE = IOUtils.DEFAULT_BUFFER_SIZE;
106
107
108
109
110
111
112
113 public static Builder builder() {
114 return new Builder();
115 }
116
117 private final OutputStream out;
118
119 private final Charset defaultCharset;
120
121 private StringWriter prologWriter = new StringWriter(BUFFER_SIZE);
122
123 private Writer writer;
124
125 private Charset charset;
126
127
128
129
130
131
132
133
134
135
136 @Deprecated
137 public XmlStreamWriter(final File file) throws FileNotFoundException {
138 this(file, null);
139 }
140
141
142
143
144
145
146
147
148
149
150
151 @Deprecated
152 @SuppressWarnings("resource")
153 public XmlStreamWriter(final File file, final String defaultEncoding) throws FileNotFoundException {
154 this(new FileOutputStream(file), defaultEncoding);
155 }
156
157
158
159
160
161
162
163
164 @Deprecated
165 public XmlStreamWriter(final OutputStream out) {
166 this(out, StandardCharsets.UTF_8);
167 }
168
169
170
171
172
173
174
175
176 private XmlStreamWriter(final OutputStream out, final Charset defaultEncoding) {
177 this.out = out;
178 this.defaultCharset = Objects.requireNonNull(defaultEncoding);
179 }
180
181
182
183
184
185
186
187
188
189 @Deprecated
190 public XmlStreamWriter(final OutputStream out, final String defaultEncoding) {
191 this(out, Charsets.toCharset(defaultEncoding, StandardCharsets.UTF_8));
192 }
193
194
195
196
197
198
199 @Override
200 public void close() throws IOException {
201 if (writer == null) {
202 charset = defaultCharset;
203 writer = new OutputStreamWriter(out, charset);
204 writer.write(prologWriter.toString());
205 }
206 writer.close();
207 }
208
209
210
211
212
213
214
215
216
217 private void detectEncoding(final char[] cbuf, final int off, final int len)
218 throws IOException {
219 int size = len;
220 final StringBuffer xmlProlog = prologWriter.getBuffer();
221 if (xmlProlog.length() + len > BUFFER_SIZE) {
222 size = BUFFER_SIZE - xmlProlog.length();
223 }
224 prologWriter.write(cbuf, off, size);
225
226
227 if (xmlProlog.length() >= 5) {
228 if (xmlProlog.substring(0, 5).equals("<?xml")) {
229
230 final int xmlPrologEnd = xmlProlog.indexOf("?>");
231 if (xmlPrologEnd > 0) {
232
233 final Matcher m = XmlStreamReader.ENCODING_PATTERN.matcher(xmlProlog.substring(0,
234 xmlPrologEnd));
235 if (m.find()) {
236 final String encName = m.group(1).toUpperCase(Locale.ROOT);
237 charset = Charset.forName(encName.substring(1, encName.length() - 1));
238 } else {
239
240
241 charset = defaultCharset;
242 }
243 } else if (xmlProlog.length() >= BUFFER_SIZE) {
244
245
246 charset = defaultCharset;
247 }
248 } else {
249
250 charset = defaultCharset;
251 }
252 if (charset != null) {
253
254 prologWriter = null;
255 writer = new OutputStreamWriter(out, charset);
256 writer.write(xmlProlog.toString());
257 if (len > size) {
258 writer.write(cbuf, off + size, len - size);
259 }
260 }
261 }
262 }
263
264
265
266
267
268
269 @Override
270 public void flush() throws IOException {
271 if (writer != null) {
272 writer.flush();
273 }
274 }
275
276
277
278
279
280
281 public String getDefaultEncoding() {
282 return defaultCharset.name();
283 }
284
285
286
287
288
289
290 public String getEncoding() {
291 return charset.name();
292 }
293
294
295
296
297
298
299
300
301
302 @Override
303 public void write(final char[] cbuf, final int off, final int len) throws IOException {
304 if (prologWriter != null) {
305 detectEncoding(cbuf, off, len);
306 } else {
307 writer.write(cbuf, off, len);
308 }
309 }
310 }