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
98 @Override
99 public XmlStreamWriter get() throws IOException {
100 return new XmlStreamWriter(this);
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 @SuppressWarnings("resource")
128 private XmlStreamWriter(final Builder builder) throws IOException {
129 this(builder.getOutputStream(), builder.getCharset());
130 }
131
132
133
134
135
136
137
138
139
140
141 @Deprecated
142 public XmlStreamWriter(final File file) throws FileNotFoundException {
143 this(file, null);
144 }
145
146
147
148
149
150
151
152
153
154
155
156 @Deprecated
157 @SuppressWarnings("resource")
158 public XmlStreamWriter(final File file, final String defaultEncoding) throws FileNotFoundException {
159 this(new FileOutputStream(file), defaultEncoding);
160 }
161
162
163
164
165
166
167
168
169 @Deprecated
170 public XmlStreamWriter(final OutputStream out) {
171 this(out, StandardCharsets.UTF_8);
172 }
173
174
175
176
177
178
179
180
181 private XmlStreamWriter(final OutputStream out, final Charset defaultEncoding) {
182 this.out = out;
183 this.defaultCharset = Objects.requireNonNull(defaultEncoding);
184 }
185
186
187
188
189
190
191
192
193
194 @Deprecated
195 public XmlStreamWriter(final OutputStream out, final String defaultEncoding) {
196 this(out, Charsets.toCharset(defaultEncoding, StandardCharsets.UTF_8));
197 }
198
199
200
201
202
203
204 @Override
205 public void close() throws IOException {
206 if (writer == null) {
207 charset = defaultCharset;
208 writer = new OutputStreamWriter(out, charset);
209 writer.write(prologWriter.toString());
210 }
211 writer.close();
212 }
213
214
215
216
217
218
219
220
221
222 private void detectEncoding(final char[] cbuf, final int off, final int len)
223 throws IOException {
224 int size = len;
225 final StringBuffer xmlProlog = prologWriter.getBuffer();
226 if (xmlProlog.length() + len > BUFFER_SIZE) {
227 size = BUFFER_SIZE - xmlProlog.length();
228 }
229 prologWriter.write(cbuf, off, size);
230
231
232 if (xmlProlog.length() >= 5) {
233 if (xmlProlog.substring(0, 5).equals("<?xml")) {
234
235 final int xmlPrologEnd = xmlProlog.indexOf("?>");
236 if (xmlPrologEnd > 0) {
237
238 final Matcher m = XmlStreamReader.ENCODING_PATTERN.matcher(xmlProlog.substring(0,
239 xmlPrologEnd));
240 if (m.find()) {
241 final String encName = m.group(1).toUpperCase(Locale.ROOT);
242 charset = Charset.forName(encName.substring(1, encName.length() - 1));
243 } else {
244
245
246 charset = defaultCharset;
247 }
248 } else if (xmlProlog.length() >= BUFFER_SIZE) {
249
250
251 charset = defaultCharset;
252 }
253 } else {
254
255 charset = defaultCharset;
256 }
257 if (charset != null) {
258
259 prologWriter = null;
260 writer = new OutputStreamWriter(out, charset);
261 writer.write(xmlProlog.toString());
262 if (len > size) {
263 writer.write(cbuf, off + size, len - size);
264 }
265 }
266 }
267 }
268
269
270
271
272
273
274 @Override
275 public void flush() throws IOException {
276 if (writer != null) {
277 writer.flush();
278 }
279 }
280
281
282
283
284
285
286 public String getDefaultEncoding() {
287 return defaultCharset.name();
288 }
289
290
291
292
293
294
295 public String getEncoding() {
296 return charset.name();
297 }
298
299
300
301
302
303
304
305
306
307 @Override
308 public void write(final char[] cbuf, final int off, final int len) throws IOException {
309 if (prologWriter != null) {
310 detectEncoding(cbuf, off, len);
311 } else {
312 writer.write(cbuf, off, len);
313 }
314 }
315 }