1 19 20 package org.openide.xml; 21 22 import java.io.CharConversionException ; 23 import java.io.IOException ; 24 import java.io.OutputStream ; 25 import java.io.StringReader ; 26 import javax.xml.parsers.DocumentBuilder ; 27 import javax.xml.parsers.DocumentBuilderFactory ; 28 import javax.xml.parsers.ParserConfigurationException ; 29 import javax.xml.parsers.SAXParserFactory ; 30 import javax.xml.transform.OutputKeys ; 31 import javax.xml.transform.Result ; 32 import javax.xml.transform.Source ; 33 import javax.xml.transform.Transformer ; 34 import javax.xml.transform.TransformerFactory ; 35 import javax.xml.transform.dom.DOMSource ; 36 import javax.xml.transform.stream.StreamResult ; 37 import javax.xml.transform.stream.StreamSource ; 38 import org.openide.util.Lookup; 39 import org.w3c.dom.DOMException ; 40 import org.w3c.dom.DOMImplementation ; 41 import org.w3c.dom.Document ; 42 import org.w3c.dom.DocumentType ; 43 import org.w3c.dom.Element ; 44 import org.w3c.dom.Node ; 45 import org.w3c.dom.NodeList ; 46 import org.w3c.dom.Text ; 47 import org.xml.sax.EntityResolver ; 48 import org.xml.sax.ErrorHandler ; 49 import org.xml.sax.InputSource ; 50 import org.xml.sax.SAXException ; 51 import org.xml.sax.XMLReader ; 52 53 137 public final class XMLUtil extends Object { 138 139 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 fastParserFactoryClass = null; 148 149 150 private XMLUtil() { 151 } 152 153 155 158 public static XMLReader createXMLReader() throws SAXException { 159 return createXMLReader(false, false); 160 } 161 162 166 public static XMLReader createXMLReader(boolean validate) 167 throws SAXException { 168 return createXMLReader(validate, false); 169 } 170 171 185 public static XMLReader createXMLReader(boolean validate, boolean namespaceAware) 186 throws SAXException { 187 SAXParserFactory 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 ex) { 196 throw new SAXException ("Cannot create parser satisfying configuration parameters", ex); } 198 } 199 200 202 220 public static Document createDocument( 221 String rootQName, String namespaceURI, String doctypePublicID, String doctypeSystemID 222 ) throws DOMException { 223 DOMImplementation impl = getDOMImplementation(); 224 225 if ((doctypePublicID != null) && (doctypeSystemID == null)) { 226 throw new IllegalArgumentException ("System ID cannot be null if public ID specified. "); } 228 229 DocumentType 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 247 private static DOMImplementation getDOMImplementation() 248 throws DOMException { 250 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 251 252 try { 253 return factory.newDocumentBuilder().getDOMImplementation(); 254 } catch (ParserConfigurationException ex) { 255 throw new DOMException ( 256 DOMException.NOT_SUPPORTED_ERR, "Cannot create parser satisfying configuration parameters" 257 ); } catch (RuntimeException e) { 259 throw (DOMException ) new DOMException (DOMException.NOT_SUPPORTED_ERR, e.toString()).initCause(e); 261 } 262 } 263 264 268 @SuppressWarnings ("unchecked") 269 private static final ThreadLocal <DocumentBuilder >[] builderTL = new ThreadLocal [4]; 270 static { 271 for (int i = 0; i < 4; i++) { 272 builderTL[i] = new ThreadLocal <DocumentBuilder >(); 273 } 274 } 275 291 public static Document parse( 292 InputSource input, boolean validate, boolean namespaceAware, ErrorHandler errorHandler, 293 EntityResolver entityResolver 294 ) throws IOException , SAXException { 295 296 int index = (validate ? 0 : 1) + (namespaceAware ? 0 : 2); 297 DocumentBuilder builder = builderTL[index].get(); 298 if (builder == null) { 299 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 300 factory.setValidating(validate); 301 factory.setNamespaceAware(namespaceAware); 302 303 try { 304 builder = factory.newDocumentBuilder(); 305 } catch (ParserConfigurationException ex) { 306 throw new SAXException ("Cannot create parser satisfying configuration parameters", ex); } 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 331 private static final String IDENTITY_XSLT_WITH_INDENT = 332 "<xsl:stylesheet version='1.0' " + "xmlns:xsl='http://www.w3.org/1999/XSL/Transform' " + "xmlns:xalan='http://xml.apache.org/xslt' " + "exclude-result-prefixes='xalan'>" + "<xsl:output method='xml' indent='yes' xalan:indent-amount='4'/>" + "<xsl:template match='@*|node()'>" + "<xsl:copy>" + "<xsl:apply-templates select='@*|node()'/>" + "</xsl:copy>" + "</xsl:template>" + "</xsl:stylesheet>"; 352 public static void write(Document doc, OutputStream out, String enc) throws IOException { 353 if (enc == null) { 354 throw new NullPointerException ("You must set an encoding; use \"UTF-8\" unless you have a good reason not to!"); } 356 Document doc2 = normalize(doc); 357 371 ClassLoader orig = Thread.currentThread().getContextClassLoader(); 373 ClassLoader global = Lookup.getDefault().lookup(ClassLoader .class); 374 ClassLoader target = XMLUtil.class.getClassLoader(); 375 if (global == null) { 376 global = target; 377 } 378 try { 379 Class clazz = global.loadClass("org.netbeans.core.startup.SAXFactoryImpl"); 380 if (clazz != null) target = clazz.getClassLoader(); 381 } catch (Exception e) { 382 } 385 Thread.currentThread().setContextClassLoader(target); 386 387 try { 388 Transformer t = TransformerFactory.newInstance().newTransformer( 389 new StreamSource (new StringReader (IDENTITY_XSLT_WITH_INDENT))); 390 DocumentType dt = doc2.getDoctype(); 391 if (dt != null) { 392 String 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 source = new DOMSource (doc2); 400 Result result = new StreamResult (out); 401 t.transform(source, result); 402 } catch (Exception e) { 403 throw (IOException ) new IOException (e.toString()).initCause(e); 404 } finally { 405 Thread.currentThread().setContextClassLoader(orig); 406 } 407 } 408 409 422 public static String toAttributeValue(String val) throws CharConversionException { 423 if (val == null) { 424 throw new CharConversionException ("null"); } 426 427 if (checkAttributeCharacters(val)) { 428 return val; 429 } 430 431 StringBuffer buf = new StringBuffer (); 432 433 for (int i = 0; i < val.length(); i++) { 434 char ch = val.charAt(i); 435 436 if ('<' == ch) { 437 buf.append("<"); 438 439 continue; 440 } else if ('&' == ch) { 441 buf.append("&"); 442 443 continue; 444 } else if ('\'' == ch) { 445 buf.append("'"); 446 447 continue; 448 } else if ('"' == ch) { 449 buf.append("""); 450 451 continue; 452 } 453 454 buf.append(ch); 455 } 456 457 return buf.toString(); 458 } 459 460 471 public static String toElementContent(String val) throws CharConversionException { 472 if (val == null) { 473 throw new CharConversionException ("null"); } 475 476 if (checkContentCharacters(val)) { 477 return val; 478 } 479 480 StringBuilder buf = new StringBuilder (); 481 482 for (int i = 0; i < val.length(); i++) { 483 char ch = val.charAt(i); 484 485 if ('<' == ch) { 486 buf.append("<"); 487 488 continue; 489 } else if ('&' == ch) { 490 buf.append("&"); 491 492 continue; 493 } else if (('>' == ch) && (i > 1) && (val.charAt(i - 2) == ']') && (val.charAt(i - 1) == ']')) { 494 buf.append(">"); 495 496 continue; 497 } 498 499 buf.append(ch); 500 } 501 502 return buf.toString(); 503 } 504 505 515 public static String toHex(byte[] val, int start, int len) { 516 StringBuffer buf = new StringBuffer (); 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 538 public static byte[] fromHex(char[] hex, int start, int len) 539 throws IOException { 540 if (hex == null) { 541 throw new IOException ("null"); 542 } 543 544 int i = hex.length; 545 546 if ((i % 2) != 0) { 547 throw new IOException ("odd length"); 548 } 549 550 byte[] magic = new byte[i / 2]; 551 552 for (; i > 0; i -= 2) { 553 String g = new String (hex, i - 2, 2); 554 555 try { 556 magic[(i / 2) - 1] = (byte) Integer.parseInt(g, 16); 557 } catch (NumberFormatException ex) { 558 throw new IOException (ex.getLocalizedMessage()); 559 } 560 } 561 562 return magic; 563 } 564 565 570 private static boolean checkAttributeCharacters(String chars) 571 throws CharConversionException { 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) { 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 ("Invalid XML character &#" + ((int) ch) + ";."); 598 } 599 } 600 } 601 } 602 603 return escape == false; 604 } 605 606 611 private static boolean checkContentCharacters(String chars) 612 throws CharConversionException { 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) { 620 switch (ch) { 621 case 0x9: 622 case 0xA: 623 case 0xD: 624 625 continue; 626 627 case '>': 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 ("Invalid XML character &#" + ((int) ch) + ";."); 647 } 648 } 649 } 650 } 651 652 return escape == false; 653 } 654 655 659 private static Document normalize(Document orig) throws IOException { 660 DocumentBuilder builder = builderTL[0].get(); 661 if (builder == null) { 662 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 663 factory.setValidating(false); 664 factory.setNamespaceAware(false); 665 try { 666 builder = factory.newDocumentBuilder(); 667 } catch (ParserConfigurationException e) { 668 throw (IOException ) new IOException ("Cannot create parser satisfying configuration parameters: " + e).initCause(e); } 670 builderTL[0].set(builder); 671 } 672 DocumentType doctype = null; 673 NodeList nl = orig.getChildNodes(); 674 for (int i = 0; i < nl.getLength(); i++) { 675 if (nl.item(i) instanceof DocumentType ) { 676 doctype = (DocumentType ) nl.item(i); 678 } 679 } 680 Document 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 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 )) { 696 doc.appendChild(doc.importNode(nl.item(i), true)); 697 } 698 } 699 doc.normalize(); 700 nl = doc.getElementsByTagName("*"); for (int i = 0; i < nl.getLength(); i++) { 702 Element e = (Element ) nl.item(i); 703 NodeList nl2 = e.getChildNodes(); 704 for (int j = 0; j < nl2.getLength(); j++) { 705 Node n = nl2.item(j); 706 if (n instanceof Text && ((Text ) n).getNodeValue().trim().length() == 0) { 707 e.removeChild(n); 708 j--; } 710 } 711 } 712 return doc; 713 } 714 } 715 | Popular Tags |