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