1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.metricshub.wbem.sblim.cimclient.internal.cimxml;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 import java.io.BufferedWriter;
47 import java.io.IOException;
48 import java.io.OutputStream;
49 import java.io.OutputStreamWriter;
50 import java.nio.charset.Charset;
51 import org.metricshub.wbem.sblim.cimclient.internal.util.WBEMConstants;
52 import org.w3c.dom.Document;
53 import org.w3c.dom.NamedNodeMap;
54 import org.w3c.dom.Node;
55
56
57
58
59
60
61
62 public class CimXmlSerializer {
63
64
65
66
67
68
69 private static class XmlWriter {
70 private BufferedWriter iWriter;
71
72
73
74
75
76
77
78
79
80 public XmlWriter(OutputStream pOut, String pCharsetName) {
81 this.iWriter = new BufferedWriter(new OutputStreamWriter(pOut, Charset.forName(pCharsetName).newEncoder()));
82 }
83
84
85
86
87
88
89
90
91 public void write(String pText) throws IOException {
92 if (pText != null) this.iWriter.write(pText);
93 }
94
95
96
97
98
99
100 public void close() throws IOException {
101 this.iWriter.close();
102 }
103
104
105
106
107
108
109 public void flush() throws IOException {
110 this.iWriter.flush();
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165 public void writeValue(final String pText) throws IOException {
166 if (pText == null) {
167 return;
168 }
169 boolean escapeSpace = true;
170 final int oneBeforeLast = pText.length() - 2;
171 for (int i = 0; i < pText.length(); ++i) {
172 char currentChar = pText.charAt(i);
173 boolean isSpace = false;
174
175 if (isHighSurrogate(currentChar)) {
176 if (i > oneBeforeLast || !isLowSurrogate(pText.charAt(i + 1))) {
177 throw new IOException("Illegal Unicode character");
178 }
179 this.iWriter.write(pText, i++, 2);
180 } else if (currentChar < ' ') {
181 writeAsHex(currentChar);
182 } else if (currentChar > '~') {
183 this.iWriter.write(currentChar);
184 } else {
185 switch (currentChar) {
186 case ' ':
187 isSpace = true;
188 if (escapeSpace) {
189 writeAsHex(currentChar);
190 } else {
191 this.iWriter.write(currentChar);
192 }
193 break;
194 case '<':
195 this.iWriter.write("<");
196 break;
197 case '>':
198 this.iWriter.write(">");
199 break;
200 case '&':
201 this.iWriter.write("&");
202 break;
203 case '"':
204 this.iWriter.write(""");
205 break;
206 case '\'':
207 this.iWriter.write("'");
208 break;
209 default:
210 this.iWriter.write(currentChar);
211 }
212 }
213 escapeSpace = (isSpace && !escapeSpace) || (i == oneBeforeLast);
214 }
215 }
216
217 private void writeAsHex(char pChar) throws IOException {
218 this.iWriter.write("&#x" + Integer.toHexString(pChar) + ";");
219 }
220
221 private boolean isHighSurrogate(char pChar) {
222 return pChar >= WBEMConstants.UTF16_MIN_HIGH_SURROGATE && pChar <= WBEMConstants.UTF16_MAX_HIGH_SURROGATE;
223 }
224
225 private boolean isLowSurrogate(char pChar) {
226 return pChar >= WBEMConstants.UTF16_MIN_LOW_SURROGATE && pChar <= WBEMConstants.UTF16_MAX_LOW_SURROGATE;
227 }
228 }
229
230 private boolean iPretty;
231
232 private int iIndent = 0;
233
234 private boolean iLastClosed = false;
235
236 private final String CDATA_START = "<![CDATA[";
237
238 private final String CDATA_END = "]]>";
239
240 private CimXmlSerializer(boolean pPretty) {
241 this.iPretty = pPretty;
242 }
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 public static void serialize(OutputStream pOS, Document pDoc, boolean pPretty) throws IOException {
263 try {
264 XmlWriter writer = new XmlWriter(pOS, WBEMConstants.UTF8);
265 writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
266 new CimXmlSerializer(pPretty).serializeNode(writer, pDoc.getDocumentElement());
267 writer.flush();
268 } catch (IOException ioe) {
269 throw ioe;
270 } catch (Exception e) {
271 throw new IOException(e.getMessage());
272 }
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288 public static void serialize(OutputStream pOS, Node pNode, boolean pPretty) throws IOException {
289 try {
290 XmlWriter writer = new XmlWriter(pOS, WBEMConstants.UTF8);
291 new CimXmlSerializer(pPretty).serializeNode(writer, pNode);
292 writer.flush();
293 } catch (IOException ioe) {
294 throw ioe;
295 } catch (Exception e) {
296 throw new IOException(e.getMessage());
297 }
298 }
299
300 private void serializeNode(XmlWriter pWriter, Node pNode) throws IOException {
301 switch (pNode.getNodeType()) {
302 case Node.ELEMENT_NODE:
303 pWriter.write(indent());
304 pWriter.write("<");
305 pWriter.write(pNode.getNodeName());
306 NamedNodeMap attributes = pNode.getAttributes();
307 if (attributes != null) {
308 for (int i = 0; i < attributes.getLength(); ++i) {
309 pWriter.write(" ");
310 serializeNode(pWriter, attributes.item(i));
311 }
312 }
313 Node child = pNode.getFirstChild();
314 if (child == null) {
315 pWriter.write("/>");
316 this.iLastClosed = true;
317 break;
318 }
319 pWriter.write(">");
320 ++this.iIndent;
321 this.iLastClosed = false;
322 while (child != null) {
323 serializeNode(pWriter, child);
324 child = child.getNextSibling();
325 }
326 --this.iIndent;
327 if (this.iLastClosed) {
328 pWriter.write(indent());
329 }
330 pWriter.write("</");
331 pWriter.write(pNode.getNodeName());
332 pWriter.write(">");
333 this.iLastClosed = true;
334 break;
335 case Node.ATTRIBUTE_NODE:
336 pWriter.write(pNode.getNodeName());
337 pWriter.write("=\"");
338 pWriter.writeValue(pNode.getNodeValue());
339 pWriter.write("\"");
340 break;
341 case Node.TEXT_NODE:
342 String value = pNode.getNodeValue();
343 if (value != null) {
344 int idx = 0;
345 int len = value.length();
346
347 while (idx < len) {
348 int cdata = value.indexOf(this.CDATA_START, idx);
349
350
351 if (cdata == -1) {
352 pWriter.writeValue(value.substring(idx));
353 break;
354 }
355
356
357 if (idx < cdata) {
358 pWriter.writeValue(value.substring(idx, cdata));
359 idx = cdata;
360 }
361
362 int end = value.indexOf(this.CDATA_END, idx);
363
364
365 if (end == -1) {
366 throw new IOException("CDATA section not closed: " + value);
367 }
368
369
370 pWriter.write(value.substring(idx, end + this.CDATA_END.length()));
371 idx = end + this.CDATA_END.length();
372 }
373 }
374 }
375 }
376
377 private String indent() {
378 if (!this.iPretty) {
379 return "";
380 }
381 StringBuffer result = new StringBuffer();
382 result.append('\n');
383 for (int i = 0; i < this.iIndent; ++i) {
384 result.append(' ');
385 }
386 return result.toString();
387 }
388 }