KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > xml > XMLUtil


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.xml;
21
22 import java.io.CharConversionException JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.OutputStream JavaDoc;
25 import java.io.StringReader JavaDoc;
26 import javax.xml.parsers.DocumentBuilder JavaDoc;
27 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
28 import javax.xml.parsers.ParserConfigurationException JavaDoc;
29 import javax.xml.parsers.SAXParserFactory JavaDoc;
30 import javax.xml.transform.OutputKeys JavaDoc;
31 import javax.xml.transform.Result JavaDoc;
32 import javax.xml.transform.Source JavaDoc;
33 import javax.xml.transform.Transformer JavaDoc;
34 import javax.xml.transform.TransformerFactory JavaDoc;
35 import javax.xml.transform.dom.DOMSource JavaDoc;
36 import javax.xml.transform.stream.StreamResult JavaDoc;
37 import javax.xml.transform.stream.StreamSource JavaDoc;
38 import org.openide.util.Lookup;
39 import org.w3c.dom.DOMException JavaDoc;
40 import org.w3c.dom.DOMImplementation JavaDoc;
41 import org.w3c.dom.Document JavaDoc;
42 import org.w3c.dom.DocumentType JavaDoc;
43 import org.w3c.dom.Element JavaDoc;
44 import org.w3c.dom.Node JavaDoc;
45 import org.w3c.dom.NodeList JavaDoc;
46 import org.w3c.dom.Text JavaDoc;
47 import org.xml.sax.EntityResolver JavaDoc;
48 import org.xml.sax.ErrorHandler JavaDoc;
49 import org.xml.sax.InputSource JavaDoc;
50 import org.xml.sax.SAXException JavaDoc;
51 import org.xml.sax.XMLReader JavaDoc;
52
53 /**
54  * Utility class collecting library methods related to XML processing.
55  *
56  * <div class="nonnormative">
57  *
58  * <p>Remember that when parsing XML files you often want to set an explicit
59  * entity resolver. For example, consider a file such as this:</p>
60  *
61  * <pre>
62  * &lt;?<font class="keyword">xml</font> <font class="variable-name">version</font>=<font class="string">"1.0"</font> <font class="variable-name">encoding</font>=<font class="string">"UTF-8"</font>?&gt;
63  * &lt;!<font class="keyword">DOCTYPE</font> <font class="type">root</font> <font class="keyword">PUBLIC</font> <font class="string">"-//NetBeans//DTD Foo 1.0//EN"</font> <font class="string">"http://www.netbeans.org/dtds/foo-1_0.dtd"</font>&gt;
64  * &lt;<font class="function-name">root</font>/&gt;
65  * </pre>
66  *
67  * <p>If you parse this with a null entity resolver, or you use the
68  * default resolver ({@link EntityCatalog#getDefault}) but do not do
69  * anything special with this DTD, you will probably find the parse
70  * blocking to make a network connection <em>even when you are not
71  * validating</em>. That is because DTDs can be used to define
72  * entities and other XML oddities, and are not a pure constraint
73  * language like Schema or RELAX-NG.</p>
74  *
75  * <p>There are three basic ways to avoid the network connection.</p>
76  *
77  * <ol>
78  *
79  * <li><p>Register the DTD. This is generally the best thing to do. See
80  * {@link EntityCatalog}'s documentation for details, but for example
81  * in your layer use:</p>
82  *
83  * <pre>
84  * &lt;<font class="function-name">filesystem</font>&gt;
85  * &lt;<font class="function-name">folder</font> <font class="variable-name">name</font>=<font class="string">"xml"</font>&gt;
86  * &lt;<font class="function-name">folder</font> <font class="variable-name">name</font>=<font class="string">"entities"</font>&gt;
87  * &lt;<font class="function-name">folder</font> <font class="variable-name">name</font>=<font class="string">"NetBeans"</font>&gt;
88  * &lt;<font class="function-name">file</font> <font class="variable-name">name</font>=<font class="string">"DTD_Foo_1_0"</font>
89  * <font class="variable-name">url</font>=<font class="string">"nbres:/org/netbeans/modules/mymod/resources/foo-1_0.dtd"</font>&gt;
90  * &lt;<font class="function-name">attr</font> <font class="variable-name">name</font>=<font class="string">"hint.originalPublicID"</font>
91  * <font class="variable-name">stringvalue</font>=<font class="string">"-//NetBeans//DTD Foo 1.0//EN"</font>/&gt;
92  * &lt;/<font class="function-name">file</font>&gt;
93  * &lt;/<font class="function-name">folder</font>&gt;
94  * &lt;/<font class="function-name">folder</font>&gt;
95  * &lt;/<font class="function-name">folder</font>&gt;
96  * &lt;/<font class="function-name">filesystem</font>&gt;
97  * </pre>
98  *
99  * <p>Now the default system entity catalog will resolve the public ID
100  * to the local copy in your module, not the network copy.
101  * Additionally, anyone who mounts the "NetBeans Catalog" in the XML
102  * Entity Catalogs node in the Runtime tab will be able to use your
103  * local copy of the DTD automatically, for validation, code
104  * completion, etc. (The network URL should really exist, though, for
105  * the benefit of other tools!)</p></li>
106  *
107  * <li><p>You can also set an explicit entity resolver which maps that
108  * particular public ID to some local copy of the DTD, if you do not
109  * want to register it globally in the system for some reason. If
110  * handed other public IDs, just return null to indicate that the
111  * system ID should be loaded.</p></li>
112  *
113  * <li><p>In some cases where XML parsing is very
114  * performance-sensitive, and you know that you do not need validation
115  * and furthermore that the DTD defines no infoset (there are no
116  * entity or character definitions, etc.), you can speed up the parse.
117  * Turn off validation, but also supply a custom entity resolver that
118  * does not even bother to load the DTD at all:</p>
119  *
120  * <pre>
121  * <font class="keyword">public</font> <font class="type">InputSource</font> <font class="function-name">resolveEntity</font>(<font class="type">String</font> <font class="variable-name">pubid</font>, <font class="type">String</font> <font class="variable-name">sysid</font>)
122  * <font class="keyword">throws</font> <font class="type">SAXException</font>, <font class="type">IOException</font> {
123  * <font class="keyword">if</font> (pubid.equals(<font class="string">"-//NetBeans//DTD Foo 1.0//EN"</font>)) {
124  * <font class="keyword">return</font> <font class="keyword">new</font> <font class="type">InputSource</font>(<font class="keyword">new</font> <font class="type">ByteArrayInputStream</font>(<font class="keyword">new</font> <font class="type">byte</font>[0]));
125  * } <font class="keyword">else</font> {
126  * <font class="keyword">return</font> EntityCatalog.getDefault().resolveEntity(pubid, sysid);
127  * }
128  * }
129  * </pre></li>
130  *
131  * </ol>
132  *
133  * </div>
134  *
135  * @author Petr Kuzel
136  * @since release 3.2 */

137 public final class XMLUtil extends Object JavaDoc {
138
139     /*
140         public static String toCDATA(String val) throws IOException {
141
142         }
143     */

144     private static final char[] DEC2HEX = {
145         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
146     };
147     private static Class JavaDoc fastParserFactoryClass = null;
148
149     /** Forbids creating new XMLUtil */
150     private XMLUtil() {
151     }
152
153     // ~~~~~~~~~~~~~~~~~~~~~ SAX related ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
154

155     /** Create a simple parser.
156       * @return <code>createXMLReader(false, false)</code>
157       */

158     public static XMLReader JavaDoc createXMLReader() throws SAXException JavaDoc {
159         return createXMLReader(false, false);
160     }
161
162     /** Create a simple parser, possibly validating.
163      * @param validate if true, a validating parser is returned
164      * @return <code>createXMLReader(validate, false)</code>
165      */

166     public static XMLReader JavaDoc createXMLReader(boolean validate)
167     throws SAXException JavaDoc {
168         return createXMLReader(validate, false);
169     }
170
171     /** Create a SAX parser from the JAXP factory.
172      * The result can be used to parse XML files.
173      *
174      * <p>See class Javadoc for hints on setting an entity resolver.
175      * This parser has its entity resolver set to the system entity resolver chain.
176      *
177      * @param validate if true, a validating parser is returned
178      * @param namespaceAware if true, a namespace aware parser is returned
179      *
180      * @throws FactoryConfigurationError Application developers should never need to directly catch errors of this type.
181      * @throws SAXException if a parser fulfilling given parameters can not be created
182      *
183      * @return XMLReader configured according to passed parameters
184      */

185     public static XMLReader JavaDoc createXMLReader(boolean validate, boolean namespaceAware)
186     throws SAXException JavaDoc {
187         SAXParserFactory JavaDoc factory;
188         factory = SAXParserFactory.newInstance();
189
190         factory.setValidating(validate);
191         factory.setNamespaceAware(namespaceAware);
192
193         try {
194             return factory.newSAXParser().getXMLReader();
195         } catch (ParserConfigurationException JavaDoc ex) {
196             throw new SAXException JavaDoc("Cannot create parser satisfying configuration parameters", ex); //NOI18N
197
}
198     }
199
200     // ~~~~~~~~~~~~~~~~~~~~~ DOM related ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
201

202     /**
203      * Creates empty DOM Document using JAXP factoring. E.g.:
204      * <p><pre>
205      * Document doc = createDocument("book", null, null, null);
206      * </pre><p>
207      * creates new DOM of a well-formed document with root element named book.
208      *
209      * @param rootQName qualified name of root element. e.g. <code>myroot</code> or <code>ns:myroot</code>
210      * @param namespaceURI URI of root element namespace or <code>null</code>
211      * @param doctypePublicID public ID of DOCTYPE or <code>null</code>
212      * @param doctypeSystemID system ID of DOCTYPE or <code>null</code> if no DOCTYPE
213      * required and doctypePublicID is also <code>null</code>
214      *
215      * @throws DOMException if new DOM with passed parameters can not be created
216      * @throws FactoryConfigurationError Application developers should never need to directly catch errors of this type.
217      *
218      * @return new DOM Document
219      */

220     public static Document JavaDoc createDocument(
221         String JavaDoc rootQName, String JavaDoc namespaceURI, String JavaDoc doctypePublicID, String JavaDoc doctypeSystemID
222     ) throws DOMException JavaDoc {
223         DOMImplementation JavaDoc impl = getDOMImplementation();
224
225         if ((doctypePublicID != null) && (doctypeSystemID == null)) {
226             throw new IllegalArgumentException JavaDoc("System ID cannot be null if public ID specified. "); //NOI18N
227
}
228
229         DocumentType JavaDoc dtd = null;
230
231         if (doctypeSystemID != null) {
232             dtd = impl.createDocumentType(rootQName, doctypePublicID, doctypeSystemID);
233         }
234
235         return impl.createDocument(namespaceURI, rootQName, dtd);
236     }
237
238     /**
239      * Obtains DOMImpementaton interface providing a number of methods for performing
240      * operations that are independent of any particular DOM instance.
241      *
242      * @throw DOMException <code>NOT_SUPPORTED_ERR</code> if cannot get DOMImplementation
243      * @throw FactoryConfigurationError Application developers should never need to directly catch errors of this type.
244      *
245      * @return DOMImplementation implementation
246      */

247     private static DOMImplementation JavaDoc getDOMImplementation()
248     throws DOMException JavaDoc { //can be made public
249

250         DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
251
252         try {
253             return factory.newDocumentBuilder().getDOMImplementation();
254         } catch (ParserConfigurationException JavaDoc ex) {
255             throw new DOMException JavaDoc(
256                 DOMException.NOT_SUPPORTED_ERR, "Cannot create parser satisfying configuration parameters"
257             ); //NOI18N
258
} catch (RuntimeException JavaDoc e) {
259             // E.g. #36578, IllegalArgumentException. Try to recover gracefully.
260
throw (DOMException JavaDoc) new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, e.toString()).initCause(e);
261         }
262     }
263
264     /**
265      * Cache of DocumentBuilder instances per thread.
266      * They are relatively expensive to create, so don't do it more than necessary.
267      */

268     @SuppressWarnings JavaDoc("unchecked")
269     private static final ThreadLocal JavaDoc<DocumentBuilder JavaDoc>[] builderTL = new ThreadLocal JavaDoc[4];
270     static {
271         for (int i = 0; i < 4; i++) {
272             builderTL[i] = new ThreadLocal JavaDoc<DocumentBuilder JavaDoc>();
273         }
274     }
275     /**
276      * Create from factory a DocumentBuilder and let it create a org.w3c.dom.Document.
277      * This method takes InputSource. After successful finish the document tree is returned.
278      *
279      * @param input a parser input (for URL users use: <code>new InputSource(url.toExternalForm())</code>
280      * @param validate if true validating parser is used
281      * @param namespaceAware if true DOM is created by namespace aware parser
282      * @param errorHandler a error handler to notify about exception or <code>null</code>
283      * @param entityResolver SAX entity resolver or <code>null</code>; see class Javadoc for hints
284      *
285      * @throws IOException if an I/O problem during parsing occurs
286      * @throws SAXException is thrown if a parser error occurs
287      * @throws FactoryConfigurationError Application developers should never need to directly catch errors of this type.
288      *
289      * @return document representing given input, or null if a parsing error occurs
290      */

291     public static Document JavaDoc parse(
292         InputSource JavaDoc input, boolean validate, boolean namespaceAware, ErrorHandler JavaDoc errorHandler,
293         EntityResolver JavaDoc entityResolver
294     ) throws IOException JavaDoc, SAXException JavaDoc {
295         
296         int index = (validate ? 0 : 1) + (namespaceAware ? 0 : 2);
297         DocumentBuilder JavaDoc builder = builderTL[index].get();
298         if (builder == null) {
299             DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
300             factory.setValidating(validate);
301             factory.setNamespaceAware(namespaceAware);
302
303             try {
304                 builder = factory.newDocumentBuilder();
305             } catch (ParserConfigurationException JavaDoc ex) {
306                 throw new SAXException JavaDoc("Cannot create parser satisfying configuration parameters", ex); //NOI18N
307
}
308             builderTL[index].set(builder);
309         }
310         
311         if (errorHandler != null) {
312             builder.setErrorHandler(errorHandler);
313         }
314         
315         if (entityResolver != null) {
316             builder.setEntityResolver(entityResolver);
317         }
318         
319         return builder.parse(input);
320     }
321
322     /**
323      * Identity transformation in XSLT with indentation added.
324      * Just using the identity transform and calling
325      * t.setOutputProperty(OutputKeys.INDENT, "yes");
326      * t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
327      * does not work currently.
328      * You really have to use this bogus stylesheet.
329      * @see "JDK bug #5064280"
330      */

331     private static final String JavaDoc IDENTITY_XSLT_WITH_INDENT =
332             "<xsl:stylesheet version='1.0' " + // NOI18N
333
"xmlns:xsl='http://www.w3.org/1999/XSL/Transform' " + // NOI18N
334
"xmlns:xalan='http://xml.apache.org/xslt' " + // NOI18N
335
"exclude-result-prefixes='xalan'>" + // NOI18N
336
"<xsl:output method='xml' indent='yes' xalan:indent-amount='4'/>" + // NOI18N
337
"<xsl:template match='@*|node()'>" + // NOI18N
338
"<xsl:copy>" + // NOI18N
339
"<xsl:apply-templates select='@*|node()'/>" + // NOI18N
340
"</xsl:copy>" + // NOI18N
341
"</xsl:template>" + // NOI18N
342
"</xsl:stylesheet>"; // NOI18N
343
/**
344      * Writes a DOM document to a stream.
345      * The precise output format is not guaranteed but this method will attempt to indent it sensibly.
346      *
347      * @param doc DOM document to be written
348      * @param out data sink
349      * @param enc XML-defined encoding name (e.g. "UTF-8")
350      * @throws IOException if JAXP fails or the stream cannot be written to
351      */

352     public static void write(Document JavaDoc doc, OutputStream JavaDoc out, String JavaDoc enc) throws IOException JavaDoc {
353         if (enc == null) {
354             throw new NullPointerException JavaDoc("You must set an encoding; use \"UTF-8\" unless you have a good reason not to!"); // NOI18N
355
}
356         Document JavaDoc doc2 = normalize(doc);
357         // XXX the following DOM 3 LS implementation of the rest of this method works fine on JDK 6 but pretty-printing is broken on JDK 5:
358
/*
359         DOMImplementationLS ls = (DOMImplementationLS) doc.getImplementation().getFeature("LS", "3.0"); // NOI18N
360         assert ls != null : "No DOM 3 LS supported in " + doc.getClass().getName();
361         LSOutput output = ls.createLSOutput();
362         output.setEncoding(enc);
363         output.setByteStream(out);
364         LSSerializer ser = ls.createLSSerializer();
365         String fpp = "format-pretty-print"; // NOI18N
366         if (ser.getDomConfig().canSetParameter(fpp, true)) {
367             ser.getDomConfig().setParameter(fpp, true);
368         }
369         ser.write(doc2, output);
370          */

371         // XXX #66563 workaround
372
ClassLoader JavaDoc orig = Thread.currentThread().getContextClassLoader();
373         ClassLoader JavaDoc global = Lookup.getDefault().lookup(ClassLoader JavaDoc.class);
374         ClassLoader JavaDoc target = XMLUtil.class.getClassLoader();
375         if (global == null) {
376             global = target;
377         }
378         try {
379             Class JavaDoc clazz = global.loadClass("org.netbeans.core.startup.SAXFactoryImpl");
380             if (clazz != null) target = clazz.getClassLoader();
381         } catch (Exception JavaDoc e) {
382             //Ignore...
383
//ErrorManager.getDefault().notify(e);
384
}
385         Thread.currentThread().setContextClassLoader(target);
386         
387         try {
388             Transformer JavaDoc t = TransformerFactory.newInstance().newTransformer(
389                     new StreamSource JavaDoc(new StringReader JavaDoc(IDENTITY_XSLT_WITH_INDENT)));
390             DocumentType JavaDoc dt = doc2.getDoctype();
391             if (dt != null) {
392                 String JavaDoc pub = dt.getPublicId();
393                 if (pub != null) {
394                     t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, pub);
395                 }
396                 t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, dt.getSystemId());
397             }
398             t.setOutputProperty(OutputKeys.ENCODING, enc);
399             Source JavaDoc source = new DOMSource JavaDoc(doc2);
400             Result JavaDoc result = new StreamResult JavaDoc(out);
401             t.transform(source, result);
402         } catch (Exception JavaDoc e) {
403             throw (IOException JavaDoc) new IOException JavaDoc(e.toString()).initCause(e);
404         } finally {
405             Thread.currentThread().setContextClassLoader(orig);
406         }
407     }
408
409     /**
410      * Escape passed string as XML attibute value
411      * (<code>&lt;</code>, <code>&amp;</code>, <code>'</code> and <code>"</code>
412      * will be escaped.
413      * Note: An XML processor returns normalized value that can be different.
414      *
415      * @param val a string to be escaped
416      *
417      * @return escaped value
418      * @throws CharConversionException if val contains an improper XML character
419      *
420      * @since 1.40
421      */

422     public static String JavaDoc toAttributeValue(String JavaDoc val) throws CharConversionException JavaDoc {
423         if (val == null) {
424             throw new CharConversionException JavaDoc("null"); // NOI18N
425
}
426
427         if (checkAttributeCharacters(val)) {
428             return val;
429         }
430
431         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
432
433         for (int i = 0; i < val.length(); i++) {
434             char ch = val.charAt(i);
435
436             if ('<' == ch) {
437                 buf.append("&lt;");
438
439                 continue;
440             } else if ('&' == ch) {
441                 buf.append("&amp;");
442
443                 continue;
444             } else if ('\'' == ch) {
445                 buf.append("&apos;");
446
447                 continue;
448             } else if ('"' == ch) {
449                 buf.append("&quot;");
450
451                 continue;
452             }
453
454             buf.append(ch);
455         }
456
457         return buf.toString();
458     }
459
460     /**
461      * Escape passed string as XML element content (<code>&lt;</code>,
462      * <code>&amp;</code> and <code>><code> in <code>]]></code> sequences).
463      *
464      * @param val a string to be escaped
465      *
466      * @return escaped value
467      * @throws CharConversionException if val contains an improper XML character
468      *
469      * @since 1.40
470      */

471     public static String JavaDoc toElementContent(String JavaDoc val) throws CharConversionException JavaDoc {
472         if (val == null) {
473             throw new CharConversionException JavaDoc("null"); // NOI18N
474
}
475
476         if (checkContentCharacters(val)) {
477             return val;
478         }
479
480         StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
481
482         for (int i = 0; i < val.length(); i++) {
483             char ch = val.charAt(i);
484
485             if ('<' == ch) {
486                 buf.append("&lt;");
487
488                 continue;
489             } else if ('&' == ch) {
490                 buf.append("&amp;");
491
492                 continue;
493             } else if (('>' == ch) && (i > 1) && (val.charAt(i - 2) == ']') && (val.charAt(i - 1) == ']')) {
494                 buf.append("&gt;");
495
496                 continue;
497             }
498
499             buf.append(ch);
500         }
501
502         return buf.toString();
503     }
504
505     /**
506      * Can be used to encode values that contain invalid XML characters.
507      * At SAX parser end must be used pair method to get original value.
508      *
509      * @param val data to be converted
510      * @param start offset
511      * @param len count
512      *
513      * @since 1.29
514      */

515     public static String JavaDoc toHex(byte[] val, int start, int len) {
516         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
517
518         for (int i = 0; i < len; i++) {
519             byte b = val[start + i];
520             buf.append(DEC2HEX[(b & 0xf0) >> 4]);
521             buf.append(DEC2HEX[b & 0x0f]);
522         }
523
524         return buf.toString();
525     }
526
527     /**
528      * Decodes data encoded using {@link #toHex(byte[],int,int) toHex}.
529      *
530      * @param hex data to be converted
531      * @param start offset
532      * @param len count
533      *
534      * @throws IOException if input does not represent hex encoded value
535      *
536      * @since 1.29
537      */

538     public static byte[] fromHex(char[] hex, int start, int len)
539     throws IOException JavaDoc {
540         if (hex == null) {
541             throw new IOException JavaDoc("null");
542         }
543
544         int i = hex.length;
545
546         if ((i % 2) != 0) {
547             throw new IOException JavaDoc("odd length");
548         }
549
550         byte[] magic = new byte[i / 2];
551
552         for (; i > 0; i -= 2) {
553             String JavaDoc g = new String JavaDoc(hex, i - 2, 2);
554
555             try {
556                 magic[(i / 2) - 1] = (byte) Integer.parseInt(g, 16);
557             } catch (NumberFormatException JavaDoc ex) {
558                 throw new IOException JavaDoc(ex.getLocalizedMessage());
559             }
560         }
561
562         return magic;
563     }
564
565     /**
566      * Check if all passed characters match XML expression [2].
567      * @return true if no escaping necessary
568      * @throws CharConversionException if contains invalid chars
569      */

570     private static boolean checkAttributeCharacters(String JavaDoc chars)
571     throws CharConversionException JavaDoc {
572         boolean escape = false;
573
574         for (int i = 0; i < chars.length(); i++) {
575             char ch = chars.charAt(i);
576
577             if (((int) ch) <= 93) { // we are UNICODE ']'
578

579                 switch (ch) {
580                 case 0x9:
581                 case 0xA:
582                 case 0xD:
583
584                     continue;
585
586                 case '\'':
587                 case '"':
588                 case '<':
589                 case '&':
590                     escape = true;
591
592                     continue;
593
594                 default:
595
596                     if (((int) ch) < 0x20) {
597                         throw new CharConversionException JavaDoc("Invalid XML character &#" + ((int) ch) + ";.");
598                     }
599                 }
600             }
601         }
602
603         return escape == false;
604     }
605
606     /**
607      * Check if all passed characters match XML expression [2].
608      * @return true if no escaping necessary
609      * @throws CharConversionException if contains invalid chars
610      */

611     private static boolean checkContentCharacters(String JavaDoc chars)
612     throws CharConversionException JavaDoc {
613         boolean escape = false;
614
615         for (int i = 0; i < chars.length(); i++) {
616             char ch = chars.charAt(i);
617
618             if (((int) ch) <= 93) { // we are UNICODE ']'
619

620                 switch (ch) {
621                 case 0x9:
622                 case 0xA:
623                 case 0xD:
624
625                     continue;
626
627                 case '>': // only ]]> is dangerous
628

629                     if (escape) {
630                         continue;
631                     }
632
633                     escape = (i > 0) && (chars.charAt(i - 1) == ']');
634
635                     continue;
636
637                 case '<':
638                 case '&':
639                     escape = true;
640
641                     continue;
642
643                 default:
644
645                     if (((int) ch) < 0x20) {
646                         throw new CharConversionException JavaDoc("Invalid XML character &#" + ((int) ch) + ";.");
647                     }
648                 }
649             }
650         }
651
652         return escape == false;
653     }
654
655     /**
656      * Try to normalize a document by removing nonsignificant whitespace.
657      * @see "#62006"
658      */

659     private static Document JavaDoc normalize(Document JavaDoc orig) throws IOException JavaDoc {
660         DocumentBuilder JavaDoc builder = builderTL[0].get();
661         if (builder == null) {
662             DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
663             factory.setValidating(false);
664             factory.setNamespaceAware(false);
665             try {
666                 builder = factory.newDocumentBuilder();
667             } catch (ParserConfigurationException JavaDoc e) {
668                 throw (IOException JavaDoc) new IOException JavaDoc("Cannot create parser satisfying configuration parameters: " + e).initCause(e); //NOI18N
669
}
670             builderTL[0].set(builder);
671         }
672         DocumentType JavaDoc doctype = null;
673         NodeList JavaDoc nl = orig.getChildNodes();
674         for (int i = 0; i < nl.getLength(); i++) {
675             if (nl.item(i) instanceof DocumentType JavaDoc) {
676                 // We cannot import DocumentType's, so we need to manually copy it.
677
doctype = (DocumentType JavaDoc) nl.item(i);
678             }
679         }
680         Document JavaDoc doc;
681         if (doctype != null) {
682             doc = builder.getDOMImplementation().createDocument(
683                 orig.getDocumentElement().getNamespaceURI(),
684                 orig.getDocumentElement().getTagName(),
685                 builder.getDOMImplementation().createDocumentType(
686                     orig.getDoctype().getName(),
687                     orig.getDoctype().getPublicId(),
688                     orig.getDoctype().getSystemId()));
689             // XXX what about entity decls inside the DOCTYPE?
690
doc.removeChild(doc.getDocumentElement());
691         } else {
692             doc = builder.newDocument();
693         }
694         for (int i = 0; i < nl.getLength(); i++) {
695             if (!(nl.item(i) instanceof DocumentType JavaDoc)) {
696                 doc.appendChild(doc.importNode(nl.item(i), true));
697             }
698         }
699         doc.normalize();
700         nl = doc.getElementsByTagName("*"); // NOI18N
701
for (int i = 0; i < nl.getLength(); i++) {
702             Element JavaDoc e = (Element JavaDoc) nl.item(i);
703             NodeList JavaDoc nl2 = e.getChildNodes();
704             for (int j = 0; j < nl2.getLength(); j++) {
705                 Node JavaDoc n = nl2.item(j);
706                 if (n instanceof Text JavaDoc && ((Text JavaDoc) n).getNodeValue().trim().length() == 0) {
707                     e.removeChild(n);
708                     j--; // since list is dynamic
709
}
710             }
711         }
712         return doc;
713     }
714 }
715
Popular Tags