source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/util/DOMElementWriter.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 10.8 KB
Line 
1/*
2 * Copyright 2000-2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.util;
19
20import java.io.IOException;
21import java.io.OutputStream;
22import java.io.OutputStreamWriter;
23import java.io.Writer;
24import org.w3c.dom.Attr;
25import org.w3c.dom.Element;
26import org.w3c.dom.NamedNodeMap;
27import org.w3c.dom.Node;
28import org.w3c.dom.NodeList;
29import org.w3c.dom.Text;
30
31/**
32 * Writes a DOM tree to a given Writer.
33 *
34 * <p>Utility class used by {@link org.apache.tools.ant.XmlLogger
35 * XmlLogger} and
36 * org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter
37 * XMLJUnitResultFormatter}.</p>
38 *
39 */
40public class DOMElementWriter {
41
42 private static String lSep = System.getProperty("line.separator");
43
44 /**
45 * Don't try to be too smart but at least recognize the predefined
46 * entities.
47 */
48 protected String[] knownEntities = {"gt", "amp", "lt", "apos", "quot"};
49
50
51 /**
52 * Writes a DOM tree to a stream in UTF8 encoding. Note that
53 * it prepends the &lt;?xml version='1.0' encoding='UTF-8'?&gt;.
54 * The indent number is set to 0 and a 2-space indent.
55 * @param root the root element of the DOM tree.
56 * @param out the outputstream to write to.
57 * @throws IOException if an error happens while writing to the stream.
58 */
59 public void write(Element root, OutputStream out) throws IOException {
60 Writer wri = new OutputStreamWriter(out, "UTF8");
61 wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
62 write(root, wri, 0, " ");
63 wri.flush();
64 }
65
66 /**
67 * Writes a DOM tree to a stream.
68 *
69 * @param element the Root DOM element of the tree
70 * @param out where to send the output
71 * @param indent number of
72 * @param indentWith string that should be used to indent the corresponding tag.
73 * @throws IOException if an error happens while writing to the stream.
74 */
75 public void write(Element element, Writer out, int indent,
76 String indentWith)
77 throws IOException {
78
79 openElement(element, out, indent, indentWith);
80
81 // Write child elements and text
82 boolean hasChildren = false;
83 NodeList children = element.getChildNodes();
84 for (int i = 0; i < children.getLength(); i++) {
85 Node child = children.item(i);
86
87 switch (child.getNodeType()) {
88
89 case Node.ELEMENT_NODE:
90 if (!hasChildren) {
91 out.write(lSep);
92 hasChildren = true;
93 }
94 write((Element) child, out, indent + 1, indentWith);
95 break;
96
97 case Node.TEXT_NODE:
98 out.write(encode(child.getNodeValue()));
99 break;
100
101 case Node.COMMENT_NODE:
102 out.write("<!--");
103 out.write(encode(child.getNodeValue()));
104 out.write("-->");
105 break;
106
107 case Node.CDATA_SECTION_NODE:
108 out.write("<![CDATA[");
109 out.write(encodedata(((Text) child).getData()));
110 out.write("]]>");
111 break;
112
113 case Node.ENTITY_REFERENCE_NODE:
114 out.write('&');
115 out.write(child.getNodeName());
116 out.write(';');
117 break;
118
119 case Node.PROCESSING_INSTRUCTION_NODE:
120 out.write("<?");
121 out.write(child.getNodeName());
122 String data = child.getNodeValue();
123 if (data != null && data.length() > 0) {
124 out.write(' ');
125 out.write(data);
126 }
127 out.write("?>");
128 break;
129 }
130 }
131
132 closeElement(element, out, indent, indentWith, hasChildren);
133 }
134
135 /**
136 * Writes the opening tag - including all attributes -
137 * correspondong to a DOM element.
138 *
139 * @param element the DOM element to write
140 * @param out where to send the output
141 * @param indent number of
142 * @param indentWith string that should be used to indent the
143 * corresponding tag.
144 * @throws IOException if an error happens while writing to the stream.
145 */
146 public void openElement(Element element, Writer out, int indent,
147 String indentWith)
148 throws IOException {
149 // Write indent characters
150 for (int i = 0; i < indent; i++) {
151 out.write(indentWith);
152 }
153
154 // Write element
155 out.write("<");
156 out.write(element.getTagName());
157
158 // Write attributes
159 NamedNodeMap attrs = element.getAttributes();
160 for (int i = 0; i < attrs.getLength(); i++) {
161 Attr attr = (Attr) attrs.item(i);
162 out.write(" ");
163 out.write(attr.getName());
164 out.write("=\"");
165 out.write(encode(attr.getValue()));
166 out.write("\"");
167 }
168 out.write(">");
169 }
170
171 /**
172 * Writes a DOM tree to a stream.
173 *
174 * @param element the Root DOM element of the tree
175 * @param out where to send the output
176 * @param indent number of
177 * @param indentWith string that should be used to indent the
178 * corresponding tag.
179 * @throws IOException if an error happens while writing to the stream.
180 */
181 public void closeElement(Element element, Writer out, int indent,
182 String indentWith, boolean hasChildren)
183 throws IOException {
184 // If we had child elements, we need to indent before we close
185 // the element, otherwise we're on the same line and don't need
186 // to indent
187 if (hasChildren) {
188 for (int i = 0; i < indent; i++) {
189 out.write(indentWith);
190 }
191 }
192
193 // Write element close
194 out.write("</");
195 out.write(element.getTagName());
196 out.write(">");
197 out.write(lSep);
198 out.flush();
199 }
200
201 /**
202 * Escape &lt;, &gt; &amp; &apos;, &quot; as their entities and
203 * drop characters that are illegal in XML documents.
204 */
205 public String encode(String value) {
206 StringBuffer sb = new StringBuffer();
207 int len = value.length();
208 for (int i = 0; i < len; i++) {
209 char c = value.charAt(i);
210 switch (c) {
211 case '<':
212 sb.append("&lt;");
213 break;
214 case '>':
215 sb.append("&gt;");
216 break;
217 case '\'':
218 sb.append("&apos;");
219 break;
220 case '\"':
221 sb.append("&quot;");
222 break;
223 case '&':
224 int nextSemi = value.indexOf(";", i);
225 if (nextSemi < 0
226 || !isReference(value.substring(i, nextSemi + 1))) {
227 sb.append("&amp;");
228 } else {
229 sb.append('&');
230 }
231 break;
232 default:
233 if (isLegalCharacter(c)) {
234 sb.append(c);
235 }
236 break;
237 }
238 }
239 return sb.substring(0);
240 }
241
242 /**
243 * Drop characters that are illegal in XML documents.
244 *
245 * <p>Also ensure that we are not including an <code>]]&gt;</code>
246 * marker by replacing that sequence with
247 * <code>&amp;#x5d;&amp;#x5d;&amp;gt;</code>.</p>
248 *
249 * <p>See XML 1.0 2.2 <a
250 * href="http://www.w3.org/TR/1998/REC-xml-19980210#charsets">http://www.w3.org/TR/1998/REC-xml-19980210#charsets</a> and
251 * 2.7 <a
252 * href="http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect">http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect</a>.</p>
253
254 */
255 public String encodedata(final String value) {
256 StringBuffer sb = new StringBuffer();
257 int len = value.length();
258 for (int i = 0; i < len; ++i) {
259 char c = value.charAt(i);
260 if (isLegalCharacter(c)) {
261 sb.append(c);
262 }
263 }
264
265 String result = sb.substring(0);
266 int cdEnd = result.indexOf("]]>");
267 while (cdEnd != -1) {
268 sb.setLength(cdEnd);
269 sb.append("&#x5d;&#x5d;&gt;")
270 .append(result.substring(cdEnd + 3));
271 result = sb.substring(0);
272 cdEnd = result.indexOf("]]>");
273 }
274
275 return result;
276 }
277
278 /**
279 * Is the given argument a character or entity reference?
280 */
281 public boolean isReference(String ent) {
282 if (!(ent.charAt(0) == '&') || !ent.endsWith(";")) {
283 return false;
284 }
285
286 if (ent.charAt(1) == '#') {
287 if (ent.charAt(2) == 'x') {
288 try {
289 Integer.parseInt(ent.substring(3, ent.length() - 1), 16);
290 return true;
291 } catch (NumberFormatException nfe) {
292 return false;
293 }
294 } else {
295 try {
296 Integer.parseInt(ent.substring(2, ent.length() - 1));
297 return true;
298 } catch (NumberFormatException nfe) {
299 return false;
300 }
301 }
302 }
303
304 String name = ent.substring(1, ent.length() - 1);
305 for (int i = 0; i < knownEntities.length; i++) {
306 if (name.equals(knownEntities[i])) {
307 return true;
308 }
309 }
310 return false;
311 }
312
313 /**
314 * Is the given character allowed inside an XML document?
315 *
316 * <p>See XML 1.0 2.2 <a
317 * href="http://www.w3.org/TR/1998/REC-xml-19980210#charsets">
318 * http://www.w3.org/TR/1998/REC-xml-19980210#charsets</a>.</p>
319 *
320 * @since 1.10, Ant 1.5
321 */
322 public boolean isLegalCharacter(char c) {
323 if (c == 0x9 || c == 0xA || c == 0xD) {
324 return true;
325 } else if (c < 0x20) {
326 return false;
327 } else if (c <= 0xD7FF) {
328 return true;
329 } else if (c < 0xE000) {
330 return false;
331 } else if (c <= 0xFFFD) {
332 return true;
333 }
334 return false;
335 }
336}
Note: See TracBrowser for help on using the repository browser.