001 package railo.runtime.text.xml; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.File; 005 import java.io.IOException; 006 import java.io.InputStream; 007 import java.io.Reader; 008 import java.io.StringReader; 009 import java.io.StringWriter; 010 import java.util.ArrayList; 011 import java.util.Iterator; 012 import java.util.List; 013 import java.util.Map; 014 015 import javax.xml.parsers.DocumentBuilder; 016 import javax.xml.parsers.DocumentBuilderFactory; 017 import javax.xml.parsers.FactoryConfigurationError; 018 import javax.xml.parsers.ParserConfigurationException; 019 import javax.xml.transform.Transformer; 020 import javax.xml.transform.TransformerException; 021 import javax.xml.transform.TransformerFactory; 022 import javax.xml.transform.dom.DOMResult; 023 import javax.xml.transform.dom.DOMSource; 024 import javax.xml.transform.sax.SAXSource; 025 import javax.xml.transform.stream.StreamResult; 026 import javax.xml.transform.stream.StreamSource; 027 028 import org.apache.xalan.processor.TransformerFactoryImpl; 029 import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl; 030 import org.ccil.cowan.tagsoup.Parser; 031 import org.w3c.dom.Attr; 032 import org.w3c.dom.CDATASection; 033 import org.w3c.dom.CharacterData; 034 import org.w3c.dom.Comment; 035 import org.w3c.dom.Document; 036 import org.w3c.dom.Element; 037 import org.w3c.dom.NamedNodeMap; 038 import org.w3c.dom.Node; 039 import org.w3c.dom.NodeList; 040 import org.w3c.dom.Text; 041 import org.xml.sax.InputSource; 042 import org.xml.sax.SAXException; 043 import org.xml.sax.XMLReader; 044 import org.xml.sax.helpers.XMLReaderFactory; 045 046 import railo.commons.io.IOUtil; 047 import railo.commons.io.res.Resource; 048 import railo.commons.io.res.util.ResourceUtil; 049 import railo.commons.lang.StringUtil; 050 import railo.runtime.PageContext; 051 import railo.runtime.engine.ThreadLocalPageContext; 052 import railo.runtime.exp.ExpressionException; 053 import railo.runtime.exp.PageException; 054 import railo.runtime.exp.XMLException; 055 import railo.runtime.op.Caster; 056 import railo.runtime.op.Decision; 057 import railo.runtime.text.xml.struct.XMLMultiElementStruct; 058 import railo.runtime.text.xml.struct.XMLStruct; 059 import railo.runtime.text.xml.struct.XMLStructFactory; 060 import railo.runtime.type.Array; 061 import railo.runtime.type.ArrayImpl; 062 import railo.runtime.type.Collection; 063 import railo.runtime.type.Collection.Key; 064 import railo.runtime.type.KeyImpl; 065 import railo.runtime.type.Struct; 066 067 /** 068 * 069 */ 070 public final class XMLUtil { 071 public static final Collection.Key XMLCOMMENT = KeyImpl.intern("xmlcomment"); 072 public static final Collection.Key XMLTEXT = KeyImpl.intern("xmltext"); 073 public static final Collection.Key XMLCDATA = KeyImpl.intern("xmlcdata"); 074 public static final Collection.Key XMLCHILDREN = KeyImpl.intern("xmlchildren"); 075 public static final Collection.Key XMLNODES = KeyImpl.intern("xmlnodes"); 076 public static final Collection.Key XMLNSURI = KeyImpl.intern("xmlnsuri"); 077 public static final Collection.Key XMLNSPREFIX = KeyImpl.intern("xmlnsprefix"); 078 public static final Collection.Key XMLROOT = KeyImpl.intern("xmlroot"); 079 public static final Collection.Key XMLPARENT = KeyImpl.intern("xmlparent"); 080 public static final Collection.Key XMLNAME = KeyImpl.intern("xmlname"); 081 public static final Collection.Key XMLTYPE = KeyImpl.intern("xmltype"); 082 public static final Collection.Key XMLVALUE = KeyImpl.intern("xmlvalue"); 083 public static final Collection.Key XMLATTRIBUTES = KeyImpl.intern("xmlattributes"); 084 /* 085 private static final Collection.Key = KeyImpl.getInstance(); 086 private static final Collection.Key = KeyImpl.getInstance(); 087 private static final Collection.Key = KeyImpl.getInstance(); 088 private static final Collection.Key = KeyImpl.getInstance(); 089 private static final Collection.Key = KeyImpl.getInstance(); 090 private static final Collection.Key = KeyImpl.getInstance(); 091 */ 092 093 094 095 //static DOMParser parser = new DOMParser(); 096 private static DocumentBuilder docBuilder; 097 //private static DocumentBuilderFactory factory; 098 private static TransformerFactory transformerFactory; 099 100 101 public static String unescapeXMLString(String str) { 102 103 StringBuffer rtn=new StringBuffer(); 104 int posStart=-1; 105 int posFinish=-1; 106 while((posStart=str.indexOf('&',posStart))!=-1) { 107 int last=posFinish+1; 108 109 posFinish=str.indexOf(';',posStart); 110 if(posFinish==-1)break; 111 rtn.append(str.substring(last,posStart)); 112 if(posStart+1<posFinish) { 113 rtn.append(unescapeXMLEntity(str.substring(posStart+1,posFinish))); 114 } 115 else { 116 rtn.append("&;"); 117 } 118 119 posStart=posFinish+1; 120 } 121 rtn.append(str.substring(posFinish+1)); 122 return rtn.toString(); 123 } 124 125 public static String unescapeXMLString2(String str) { 126 127 StringBuffer sb=new StringBuffer(); 128 int index,last=0,indexSemi; 129 while((index=str.indexOf('&',last))!=-1) { 130 sb.append(str.substring(last,index)); 131 indexSemi=str.indexOf(';',index+1); 132 133 if(indexSemi==-1) { 134 sb.append('&'); 135 last=index+1; 136 } 137 else if(index+1==indexSemi) { 138 sb.append("&;"); 139 last=index+2; 140 } 141 else { 142 sb.append(unescapeXMLEntity(str.substring(index+1,indexSemi))); 143 last=indexSemi+1; 144 } 145 } 146 sb.append(str.substring(last)); 147 return sb.toString(); 148 } 149 150 151 152 153 private static String unescapeXMLEntity(String str) { 154 if("lt".equals(str)) return "<"; 155 if("gt".equals(str)) return ">"; 156 if("amp".equals(str)) return "&"; 157 if("apos".equals(str)) return "'"; 158 if("quot".equals(str)) return "\""; 159 return "&"+str+";"; 160 } 161 162 public static String escapeXMLString(String xmlStr) { 163 char c; 164 StringBuffer sb=new StringBuffer(); 165 int len=xmlStr.length(); 166 for(int i=0;i<len;i++) { 167 c=xmlStr.charAt(i); 168 if(c=='<') sb.append("<"); 169 else if(c=='>') sb.append(">"); 170 else if(c=='&') sb.append("&"); 171 //else if(c=='\'') sb.append("&"); 172 else if(c=='"') sb.append("""); 173 //else if(c>127) sb.append("&#"+((int)c)+";"); 174 else sb.append(c); 175 } 176 return sb.toString(); 177 } 178 179 180 /** 181 * @return returns a singelton TransformerFactory 182 */ 183 public static TransformerFactory getTransformerFactory() { 184 // Saxon 185 //if(transformerFactory==null)transformerFactory=new com.icl.saxon.TransformerFactoryImpl(); 186 // Xalan 187 if(transformerFactory==null)transformerFactory=new TransformerFactoryImpl(); 188 // Trax 189 //if(transformerFactory==null)transformerFactory=new com.jclark.xsl.trax.TransformerFactoryImpl(); 190 // Trax 191 //if(transformerFactory==null)transformerFactory=new jd.xml.xslt.trax.TransformerFactoryImpl(); 192 // Caucho 193 //if(transformerFactory==null)transformerFactory=new Xsl(); 194 // xsltc 195 //if(transformerFactory==null)transformerFactory=new org.apache.xalan.xsltc.trax.TransformerFactoryImpl(); 196 197 198 return transformerFactory; 199 } 200 201 /** 202 * parse XML/HTML String to a XML DOM representation 203 * @param xml XML InputSource 204 * @param isHtml is a HTML or XML Object 205 * @return parsed Document 206 * @throws SAXException 207 * @throws IOException 208 * @throws ParserConfigurationException 209 */ 210 public static final Document parse(InputSource xml,InputSource validator, boolean isHtml) 211 throws SAXException, IOException { 212 213 if(!isHtml) { 214 // try to load org.apache.xerces.jaxp.DocumentBuilderFactoryImpl, oracle impl sucks 215 DocumentBuilderFactory factory = null; 216 try{ 217 factory = new DocumentBuilderFactoryImpl(); 218 } 219 catch(Throwable t) { 220 factory = DocumentBuilderFactory.newInstance(); 221 } 222 223 //print.o(factory); 224 if(validator==null) { 225 XMLUtil.setAttributeEL(factory,XMLConstants.NON_VALIDATING_DTD_EXTERNAL, Boolean.FALSE); 226 XMLUtil.setAttributeEL(factory,XMLConstants.NON_VALIDATING_DTD_GRAMMAR, Boolean.FALSE); 227 } 228 else { 229 XMLUtil.setAttributeEL(factory,XMLConstants.VALIDATION_SCHEMA, Boolean.TRUE); 230 XMLUtil.setAttributeEL(factory,XMLConstants.VALIDATION_SCHEMA_FULL_CHECKING, Boolean.TRUE); 231 232 233 } 234 235 236 factory.setNamespaceAware(true); 237 factory.setValidating(validator!=null); 238 239 try { 240 DocumentBuilder builder = factory.newDocumentBuilder(); 241 builder.setEntityResolver(new XMLEntityResolverDefaultHandler(validator)); 242 builder.setErrorHandler(new ThrowingErrorHandler(true,true,false)); 243 return builder.parse(xml); 244 } 245 catch (ParserConfigurationException e) { 246 throw new SAXException(e); 247 } 248 249 /*DOMParser parser = new DOMParser(); 250 print.out("parse"); 251 parser.setEntityResolver(new XMLEntityResolverDefaultHandler(validator)); 252 parser.parse(xml); 253 return parser.getDocument();*/ 254 } 255 256 XMLReader reader = new Parser(); 257 reader.setFeature(Parser.namespacesFeature, true); 258 reader.setFeature(Parser.namespacePrefixesFeature, true); 259 260 try { 261 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 262 263 DOMResult result = new DOMResult(); 264 transformer.transform(new SAXSource(reader, xml), result); 265 return XMLUtil.getDocument(result.getNode()); 266 } 267 catch (Exception e) { 268 throw new SAXException(e); 269 } 270 } 271 272 private static void setAttributeEL(DocumentBuilderFactory factory,String name, Object value) { 273 try{ 274 factory.setAttribute(name, value); 275 } 276 catch(Throwable t){ 277 //SystemOut.printDate("attribute ["+name+"] is not allowed for ["+factory.getClass().getName()+"]"); 278 } 279 } 280 281 /** 282 * sets a node to a node (Expression Less) 283 * @param node 284 * @param key 285 * @param value 286 * @return Object set 287 */ 288 public static Object setPropertyEL(Node node, Collection.Key key, Object value) { 289 try { 290 return setProperty(node,key,value); 291 } catch (PageException e) { 292 return null; 293 } 294 } 295 public static Object setProperty(Node node, Collection.Key key, Object value,boolean caseSensitive, Object defaultValue) { 296 try { 297 return setProperty(node,key,value,caseSensitive); 298 } catch (PageException e) { 299 return defaultValue; 300 } 301 } 302 303 /** 304 * sets a node to a node 305 * @param node 306 * @param key 307 * @param value 308 * @return Object set 309 * @throws PageException 310 */ 311 312 public static Object setProperty(Node node, Collection.Key k, Object value) throws PageException { 313 return setProperty(node, k, value, isCaseSensitve(node)); 314 } 315 316 public static Object setProperty(Node node, Collection.Key k, Object value,boolean caseSensitive) throws PageException { 317 Document doc=(node instanceof Document)?(Document)node:node.getOwnerDocument(); 318 // Comment 319 if(k.equals(XMLCOMMENT)) { 320 removeChildren(XMLCaster.toRawNode(node),Node.COMMENT_NODE,false); 321 node.appendChild(XMLCaster.toRawNode(XMLCaster.toComment(doc,value))); 322 } 323 // NS URI 324 else if(k.equals(XMLNSURI)) { 325 // TODO impl 326 throw new ExpressionException("XML NS URI can't be set","not implemented"); 327 } 328 // Prefix 329 else if(k.equals(XMLNSPREFIX)) { 330 // TODO impl 331 throw new ExpressionException("XML NS Prefix can't be set","not implemented"); 332 //node.setPrefix(Caster.toString(value)); 333 } 334 // Root 335 else if(k.equals(XMLROOT)) { 336 doc.appendChild(XMLCaster.toNode(doc,value)); 337 } 338 // Parent 339 else if(k.equals(XMLPARENT)) { 340 Node parent = getParentNode(node,caseSensitive); 341 Key name = KeyImpl.init(parent.getNodeName()); 342 parent = getParentNode(parent,caseSensitive); 343 344 if(parent==null) 345 throw new ExpressionException("there is no parent element, you are already on the root element"); 346 347 return setProperty(parent, name, value, caseSensitive); 348 } 349 // Name 350 else if(k.equals(XMLNAME)) { 351 throw new XMLException("You can't assign a new value for the property [xmlname]"); 352 } 353 // Type 354 else if(k.equals(XMLTYPE)) { 355 throw new XMLException("You can't change type of a xml node [xmltype]"); 356 } 357 // value 358 else if(k.equals(XMLVALUE)) { 359 node.setNodeValue(Caster.toString(value)); 360 } 361 // Attributes 362 else if(k.equals(XMLATTRIBUTES)) { 363 Element parent=XMLCaster.toElement(doc,node); 364 Attr[] attres=XMLCaster.toAttrArray(doc,value); 365 //print.ln("=>"+value); 366 for(int i=0;i<attres.length;i++) { 367 if(attres[i]!=null) { 368 parent.setAttributeNode(attres[i]); 369 //print.ln(attres[i].getName()+"=="+attres[i].getValue()); 370 } 371 } 372 } 373 // Text 374 else if(k.equals(XMLTEXT)) { 375 removeChildCharacterData(XMLCaster.toRawNode(node),false); 376 node.appendChild(XMLCaster.toRawNode(XMLCaster.toText(doc,value))); 377 } 378 // CData 379 else if(k.equals(XMLCDATA)) { 380 removeChildCharacterData(XMLCaster.toRawNode(node),false); 381 node.appendChild(XMLCaster.toRawNode(XMLCaster.toCDATASection(doc,value))); 382 } 383 // Children 384 else if(k.equals(XMLCHILDREN) || k.equals(XMLNODES)) { 385 Node[] nodes=XMLCaster.toNodeArray(doc,value); 386 removeChildren(XMLCaster.toRawNode(node),Node.ELEMENT_NODE,false); 387 for(int i=0;i<nodes.length;i++) { 388 if(nodes[i]==node) throw new XMLException("can't assign a XML Node to himself"); 389 if(nodes[i]!=null)node.appendChild(XMLCaster.toRawNode(nodes[i])); 390 } 391 } 392 else { 393 boolean isIndex=false; 394 Node child = XMLCaster.toNode(doc,value); 395 if(!k.getString().equalsIgnoreCase(child.getNodeName()) && !(isIndex=Decision.isInteger(k))) { 396 throw new XMLException("if you assign a XML Element to a XMLStruct , assignment property must have same name like XML Node Name", "Property Name is "+k.getString()+" and XML Element Name is "+child.getNodeName()); 397 } 398 Node n; 399 400 // by index 401 if(isIndex) { 402 NodeList list = XMLUtil.getChildNodes(node.getParentNode(), Node.ELEMENT_NODE,true,node.getNodeName()); 403 int len = list.getLength(); 404 405 406 int index=Caster.toIntValue(k); 407 if(index>len || index<1){ 408 String detail=len>1? 409 "your index is "+index+", but there are only "+len+" child elements": 410 "your index is "+index+", but there is only "+len+" child element"; 411 412 413 throw new XMLException("index is out of range", detail); 414 } 415 n=list.item(index-1); 416 XMLUtil.replaceChild(child, n); 417 return value; 418 } 419 420 421 422 423 NodeList list = XMLUtil.getChildNodes(node, Node.ELEMENT_NODE); 424 int len = list.getLength(); 425 426 // by name 427 for(int i=0;i<len;i++) { 428 n=list.item(i); 429 if(nameEqual(n, k.getString(), caseSensitive)) { 430 XMLUtil.replaceChild(child, n); 431 return value; 432 } 433 } 434 node.appendChild(XMLCaster.toRawNode(child)); 435 } 436 437 return value; 438 } 439 440 441 public static void replaceChild(Node newChild, Node oldChild) { 442 443 444 Node nc = XMLCaster.toRawNode(newChild); 445 Node oc = XMLCaster.toRawNode(oldChild); 446 Node p = oc.getParentNode(); 447 448 if(nc!=oc)p.replaceChild(nc, oc); 449 } 450 451 public static Object getProperty(Node node, Collection.Key key, Object defaultValue) { 452 return getProperty(node, key,isCaseSensitve(node),defaultValue); 453 } 454 455 456 /** 457 * returns a property from a XMl Node (Expression Less) 458 * @param node 459 * @param key 460 * @param caseSensitive 461 * @return Object matching key 462 */ 463 public static Object getProperty(Node node, Collection.Key k,boolean caseSensitive, Object defaultValue) { 464 try { 465 return getProperty(node, k,caseSensitive); 466 } catch (SAXException e) { 467 return defaultValue; 468 } 469 } 470 471 public static Object getProperty(Node node, Collection.Key key) throws SAXException { 472 return getProperty(node, key,isCaseSensitve(node)); 473 } 474 475 /** 476 * returns a property from a XMl Node 477 * @param node 478 * @param key 479 * @param caseSensitive 480 * @return Object matching key 481 * @throws SAXException 482 */ 483 public static Object getProperty(Node node, Collection.Key k,boolean caseSensitive) throws SAXException { 484 //String lcKey=StringUtil.toLowerCase(key); 485 if(k.getLowerString().startsWith("xml")) { 486 // Comment 487 if(k.equals(XMLCOMMENT)) { 488 StringBuffer sb=new StringBuffer(); 489 NodeList list = node.getChildNodes(); 490 int len=list.getLength(); 491 for(int i=0;i<len;i++) { 492 Node n=list.item(i); 493 if(n instanceof Comment) { 494 sb.append(((Comment)n).getData()); 495 } 496 } 497 return sb.toString(); 498 } 499 // NS URI 500 if(k.equals(XMLNSURI)) { 501 undefinedInRoot(k,node); 502 return param(node.getNamespaceURI(),""); 503 } 504 // Prefix 505 if(k.equals(XMLNSPREFIX)) { 506 undefinedInRoot(k,node); 507 return param(node.getPrefix(),""); 508 } 509 // Root 510 else if(k.equals(XMLROOT)) { 511 Element re = getRootElement(node,caseSensitive); 512 if(re==null) throw new SAXException("Attribute ["+k.getString()+"] not found in XML, XML is empty"); 513 return param(re,""); 514 } 515 // Parent 516 else if(k.equals(XMLPARENT)) { 517 518 Node parent = getParentNode(node,caseSensitive); 519 if(parent==null) { 520 if(node.getNodeType()==Node.DOCUMENT_NODE) 521 throw new SAXException("Attribute ["+k.getString()+"] not found in XML, there is no parent element, you are already at the root element"); 522 throw new SAXException("Attribute ["+k.getString()+"] not found in XML, there is no parent element"); 523 } 524 return parent; 525 } 526 // Name 527 else if(k.equals(XMLNAME)) { 528 return node.getNodeName(); 529 } 530 // Value 531 else if(k.equals(XMLVALUE)) { 532 return StringUtil.toStringEmptyIfNull(node.getNodeValue()); 533 } 534 // type 535 else if(k.equals(XMLTYPE)) { 536 return getTypeAsString(node,true); 537 } 538 // Attributes 539 else if(k.equals(XMLATTRIBUTES)) { 540 NamedNodeMap attr = node.getAttributes(); 541 542 if(attr==null)throw undefined(k,node); 543 return new XMLAttributes(node,caseSensitive); 544 } 545 // Text 546 else if(k.equals(XMLTEXT)) { 547 undefinedInRoot(k,node); 548 StringBuffer sb=new StringBuffer(); 549 NodeList list = node.getChildNodes(); 550 int len=list.getLength(); 551 for(int i=0;i<len;i++) { 552 Node n=list.item(i); 553 if(n instanceof Text || n instanceof CDATASection) { 554 sb.append(((CharacterData)n).getData()); 555 } 556 } 557 return sb.toString(); 558 } 559 else if(k.equals(XMLCDATA)) { 560 undefinedInRoot(k,node); 561 StringBuffer sb=new StringBuffer(); 562 NodeList list = node.getChildNodes(); 563 int len=list.getLength(); 564 for(int i=0;i<len;i++) { 565 Node n=list.item(i); 566 if(n instanceof Text || n instanceof CDATASection) { 567 sb.append(((CharacterData)n).getData()); 568 } 569 } 570 return sb.toString(); 571 } 572 // children 573 else if(k.equals(XMLCHILDREN) || k.equals(XMLNODES)) { 574 return new XMLNodeList(node,caseSensitive); 575 } 576 } 577 578 if(node instanceof Document) { 579 node=((Document)node).getDocumentElement(); 580 if(node==null) throw new SAXException("Attribute ["+k.getString()+"] not found in XML, XML is empty"); 581 582 //if((!caseSensitive && node.getNodeName().equalsIgnoreCase(k.getString())) || (caseSensitive && node.getNodeName().equals(k.getString()))) { 583 if(nameEqual(node, k.getString(), caseSensitive)) { 584 return XMLStructFactory.newInstance(node,caseSensitive); 585 } 586 } 587 else if(node.getNodeType()==Node.ELEMENT_NODE && Decision.isInteger(k)){ 588 int index=Caster.toIntValue(k,0); 589 int count=0; 590 Node parent = node.getParentNode(); 591 String nodeName=node.getNodeName(); 592 Element[] children = XMLUtil.getChildElementsAsArray(parent); 593 594 for(int i=0;i<children.length;i++){ 595 if(XMLUtil.nameEqual(children[i],nodeName,caseSensitive)) count++; 596 597 if(count==index) return XMLCaster.toXMLStruct(children[i],caseSensitive); 598 } 599 String detail; 600 if(count==0)detail="there are no Elements with this name"; 601 else if(count==1)detail="there is only 1 Element with this name"; 602 else detail="there are only "+count+" Elements with this name"; 603 throw new SAXException("invalid index ["+k.getString()+"] for Element with name ["+node.getNodeName()+"], "+detail); 604 } 605 else { 606 List<Node> children = XMLUtil.getChildNodesAsList(node,Node.ELEMENT_NODE,caseSensitive,null); 607 int len=children.size(); 608 Array array=null;//new ArrayImpl(); 609 Element el; 610 XMLStruct sct=null,first=null; 611 for(int i=0;i<len;i++) { 612 el=(Element) children.get(i);// XMLCaster.toXMLStruct(getChildNode(index),caseSensitive); 613 if(XMLUtil.nameEqual(el,k.getString(),caseSensitive)) { 614 sct = XMLCaster.toXMLStruct(el,caseSensitive); 615 616 if(array!=null) { 617 array.appendEL(sct); 618 } 619 else if(first!=null) { 620 array=new ArrayImpl(); 621 array.appendEL(first); 622 array.appendEL(sct); 623 } 624 else { 625 first=sct; 626 } 627 } 628 } 629 630 if(array!=null) { 631 try { 632 return new XMLMultiElementStruct(array,false); 633 } catch (PageException e) {} 634 } 635 if(first!=null) return first; 636 } 637 throw new SAXException("Attribute ["+k.getString()+"] not found"); 638 } 639 640 641 private static SAXException undefined(Key key, Node node) { 642 if(node.getNodeType()==Node.DOCUMENT_NODE) 643 return new SAXException("you cannot address ["+key+"] on the Document Object, to address ["+key+"] from the root Node use [{variable-name}.xmlRoot."+key+"]"); 644 645 return new SAXException(key+" is undefined"); 646 } 647 648 private static void undefinedInRoot(Key key, Node node) throws SAXException { 649 if(node.getNodeType()==Node.DOCUMENT_NODE) 650 throw undefined(key, node); 651 } 652 653 /** 654 * check if given name is equal to name of the element (with and without namespace) 655 * @param node 656 * @param k 657 * @param caseSensitive 658 * @return 659 */ 660 public static boolean nameEqual(Node node, String name, boolean caseSensitive) { 661 if(name==null) return false; 662 if(caseSensitive){ 663 return name.equals(node.getNodeName()) || name.equals(node.getLocalName()); 664 } 665 return name.equalsIgnoreCase(node.getNodeName()) || name.equalsIgnoreCase(node.getLocalName()); 666 } 667 668 public static boolean isCaseSensitve(Node node) { 669 if(node instanceof XMLStruct) return ((XMLStruct)node).isCaseSensitive(); 670 return true; 671 } 672 673 /** 674 * removes child from a node 675 * @param node 676 * @param key 677 * @param caseSensitive 678 * @return removed property 679 */ 680 public static Object removeProperty(Node node, Collection.Key k,boolean caseSensitive) { 681 682 //String lcKeyx=k.getLowerString(); 683 if(k.getLowerString().startsWith("xml")) { 684 // Comment 685 if(k.equals(XMLCOMMENT)) { 686 StringBuffer sb=new StringBuffer(); 687 NodeList list = node.getChildNodes(); 688 int len=list.getLength(); 689 for(int i=0;i<len;i++) { 690 Node n=list.item(i); 691 if(n instanceof Comment) { 692 sb.append(((Comment)n).getData()); 693 node.removeChild(XMLCaster.toRawNode(n)); 694 } 695 } 696 return sb.toString(); 697 } 698 // Text 699 else if(k.equals(XMLTEXT)) { 700 StringBuffer sb=new StringBuffer(); 701 NodeList list = node.getChildNodes(); 702 int len=list.getLength(); 703 for(int i=0;i<len;i++) { 704 Node n=list.item(i); 705 if(n instanceof Text || n instanceof CDATASection) { 706 sb.append(((CharacterData)n).getData()); 707 node.removeChild(XMLCaster.toRawNode(n)); 708 } 709 } 710 return sb.toString(); 711 } 712 // children 713 else if(k.equals(XMLCHILDREN) || k.equals(XMLNODES)) { 714 NodeList list=node.getChildNodes(); 715 for(int i=list.getLength()-1;i>=0;i--) { 716 node.removeChild(XMLCaster.toRawNode(list.item(i))); 717 } 718 return list; 719 } 720 } 721 722 NodeList nodes = node.getChildNodes(); 723 Array array=new ArrayImpl(); 724 for(int i=nodes.getLength()-1;i>=0;i--) { 725 Object o=nodes.item(i); 726 if(o instanceof Element) { 727 Element el=(Element) o; 728 if(nameEqual(el, k.getString(), caseSensitive)) { 729 array.appendEL(XMLCaster.toXMLStruct(el,caseSensitive)); 730 node.removeChild(XMLCaster.toRawNode(el)); 731 } 732 } 733 } 734 735 if(array.size()>0) { 736 try { 737 return new XMLMultiElementStruct(array,false); 738 } catch (PageException e) {} 739 } 740 return null; 741 } 742 743 744 private static Object param(Object o1, Object o2) { 745 if(o1==null)return o2; 746 return o1; 747 } 748 749 /** 750 * return the root Element from a node 751 * @param node node to get root element from 752 * @param caseSensitive 753 * @return Root Element 754 */ 755 public static Element getRootElement(Node node, boolean caseSensitive) { 756 Document doc=null; 757 if(node instanceof Document) doc=(Document) node; 758 else doc=node.getOwnerDocument(); 759 Element el = doc.getDocumentElement(); 760 if(el==null) return null; 761 return (Element)XMLStructFactory.newInstance(el,caseSensitive); 762 } 763 764 765 public static Node getParentNode(Node node, boolean caseSensitive) { 766 Node parent = node.getParentNode(); 767 if(parent==null) return null; 768 return XMLStructFactory.newInstance(parent,caseSensitive); 769 } 770 771 /** 772 * returns a new Empty XMl Document 773 * @return new Document 774 * @throws ParserConfigurationException 775 * @throws FactoryConfigurationError 776 */ 777 public static Document newDocument() throws ParserConfigurationException, FactoryConfigurationError { 778 if(docBuilder==null) { 779 docBuilder=DocumentBuilderFactory.newInstance().newDocumentBuilder(); 780 } 781 return docBuilder.newDocument(); 782 } 783 784 /** 785 * return the Owner Document of a Node List 786 * @param nodeList 787 * @return XML Document 788 * @throws XMLException 789 */ 790 public static Document getDocument(NodeList nodeList) throws XMLException { 791 if(nodeList instanceof Document) return (Document)nodeList; 792 int len=nodeList.getLength(); 793 for(int i=0;i<len;i++) { 794 Node node=nodeList.item(i); 795 if(node!=null) return node.getOwnerDocument(); 796 } 797 throw new XMLException("can't get Document from NodeList, in NoteList are no Nodes"); 798 } 799 800 /** 801 * return the Owner Document of a Node 802 * @param node 803 * @return XML Document 804 */ 805 public static Document getDocument(Node node) { 806 if(node instanceof Document) return (Document)node; 807 return node.getOwnerDocument(); 808 } 809 810 811 /** 812 * removes child elements from a specific type 813 * @param node node to remove elements from 814 * @param type Type Definition to remove (Constant value from class Node) 815 * @param deep remove also in sub nodes 816 */ 817 private synchronized static void removeChildren(Node node, short type, boolean deep) { 818 NodeList list = node.getChildNodes(); 819 820 for(int i=list.getLength();i>=0;i--) { 821 Node n=list.item(i); 822 if(n ==null )continue; 823 824 if(n.getNodeType()==type)node.removeChild(XMLCaster.toRawNode(n)); 825 else if(deep)removeChildren(n,type,deep); 826 } 827 } 828 829 /** 830 * remove children from type CharacterData from a node, this includes Text,Comment and CDataSection nodes 831 * @param node 832 * @param type 833 * @param deep 834 */ 835 private synchronized static void removeChildCharacterData(Node node, boolean deep) { 836 NodeList list = node.getChildNodes(); 837 838 for(int i=list.getLength();i>=0;i--) { 839 Node n=list.item(i); 840 if(n ==null )continue; 841 842 if(n instanceof CharacterData)node.removeChild(XMLCaster.toRawNode(n)); 843 else if(deep)removeChildCharacterData(n,deep); 844 } 845 } 846 847 /** 848 * return all Children of a node by a defined type as Node List 849 * @param node node to get children from 850 * @param type type of returned node 851 * @param filter 852 * @param caseSensitive 853 * @return all matching child node 854 */ 855 public synchronized static ArrayNodeList getChildNodes(Node node, short type) { 856 return getChildNodes(node, type, false, null); 857 } 858 859 860 public synchronized static int childNodesLength(Node node, short type, boolean caseSensitive, String filter) { 861 NodeList nodes=node.getChildNodes(); 862 int len=nodes.getLength(); 863 Node n; 864 int count=0; 865 for(int i=0;i<len;i++) { 866 try { 867 n=nodes.item(i); 868 if(n!=null && n.getNodeType()==type){ 869 if(filter==null || (caseSensitive?filter.equals(n.getLocalName()):filter.equalsIgnoreCase(n.getLocalName()))) 870 count++; 871 } 872 } 873 catch(Throwable t){} 874 } 875 return count; 876 } 877 878 public synchronized static ArrayNodeList getChildNodes(Node node, short type, boolean caseSensitive, String filter) { 879 ArrayNodeList rtn=new ArrayNodeList(); 880 NodeList nodes=node.getChildNodes(); 881 int len=nodes.getLength(); 882 Node n; 883 for(int i=0;i<len;i++) { 884 try { 885 n=nodes.item(i); 886 if(n!=null && n.getNodeType()==type){ 887 if(filter==null || (caseSensitive?filter.equals(n.getLocalName()):filter.equalsIgnoreCase(n.getLocalName()))) 888 rtn.add(n); 889 } 890 } 891 catch(Throwable t){} 892 } 893 return rtn; 894 } 895 896 public synchronized static List<Node> getChildNodesAsList(Node node, short type, boolean caseSensitive, String filter) { 897 List<Node> rtn=new ArrayList<Node>(); 898 NodeList nodes=node.getChildNodes(); 899 int len=nodes.getLength(); 900 Node n; 901 for(int i=0;i<len;i++) { 902 try { 903 n=nodes.item(i); 904 if(n!=null && n.getNodeType()==type){ 905 if(filter==null || (caseSensitive?filter.equals(n.getLocalName()):filter.equalsIgnoreCase(n.getLocalName()))) 906 rtn.add(n); 907 } 908 } 909 catch(Throwable t){} 910 } 911 return rtn; 912 } 913 914 915 public synchronized static Node getChildNode(Node node, short type, boolean caseSensitive, String filter, int index) { 916 NodeList nodes=node.getChildNodes(); 917 int len=nodes.getLength(); 918 Node n; 919 int count=0; 920 for(int i=0;i<len;i++) { 921 try { 922 n=nodes.item(i); 923 if(n!=null && n.getNodeType()==type){ 924 if(filter==null || (caseSensitive?filter.equals(n.getLocalName()):filter.equalsIgnoreCase(n.getLocalName()))) { 925 if(count==index) return n; 926 count++; 927 } 928 } 929 } 930 catch(Throwable t){} 931 } 932 return null; 933 } 934 935 936 937 /** 938 * return all Children of a node by a defined type as Node Array 939 * @param node node to get children from 940 * @param type type of returned node 941 * @param filter 942 * @param caseSensitive 943 * @return all matching child node 944 */ 945 public static Node[] getChildNodesAsArray(Node node, short type) { 946 ArrayNodeList nodeList=getChildNodes(node, type); 947 return nodeList.toArray(new Node[nodeList.getLength()]); 948 } 949 950 public static Node[] getChildNodesAsArray(Node node, short type, boolean caseSensitive, String filter) { 951 ArrayNodeList nodeList=getChildNodes(node, type,caseSensitive,filter); 952 return nodeList.toArray(new Node[nodeList.getLength()]); 953 } 954 955 /** 956 * return all Element Children of a node 957 * @param node node to get children from 958 * @return all matching child node 959 */ 960 public static Element[] getChildElementsAsArray(Node node) { 961 ArrayNodeList nodeList=getChildNodes(node,Node.ELEMENT_NODE); 962 return nodeList.toArray(new Element[nodeList.getLength()]); 963 } 964 965 /** 966 * transform a XML Object to a other format, with help of a XSL Stylesheet 967 * @param xml xml to convert 968 * @param xsl xsl used to convert 969 * @return resulting string 970 * @throws TransformerException 971 * @throws SAXException 972 * @throws IOException 973 */ 974 public static String transform(InputSource xml, InputSource xsl) throws TransformerException, SAXException, IOException { 975 return transform( parse( xml, null , false ), xsl, null ); 976 } 977 978 /** 979 * transform a XML Object to a other format, with help of a XSL Stylesheet 980 * @param xml xml to convert 981 * @param xsl xsl used to convert 982 * @param parameters parameters used to convert 983 * @return resulting string 984 * @throws TransformerException 985 * @throws SAXException 986 * @throws IOException 987 */ 988 public static String transform(InputSource xml, InputSource xsl, Map parameters) throws TransformerException, SAXException, IOException { 989 return transform( parse( xml, null , false ), xsl, parameters ); 990 } 991 992 /** 993 * transform a XML Document to a other format, with help of a XSL Stylesheet 994 * @param xml xml to convert 995 * @param xsl xsl used to convert 996 * @return resulting string 997 * @throws TransformerException 998 * @throws SAXException 999 * @throws IOException 1000 */ 1001 public static String transform( Document doc, InputSource xsl ) throws TransformerException { 1002 return transform( doc, xsl, null ); 1003 } 1004 1005 /** 1006 * transform a XML Document to a other format, with help of a XSL Stylesheet 1007 * @param xml xml to convert 1008 * @param xsl xsl used to convert 1009 * @param parameters parameters used to convert 1010 * @return resulting string 1011 * @throws TransformerException 1012 * @throws SAXException 1013 * @throws IOException 1014 */ 1015 public static String transform(Document doc, InputSource xsl, Map parameters) throws TransformerException { 1016 StringWriter sw = new StringWriter(); 1017 TransformerFactory factory = XMLUtil.getTransformerFactory(); 1018 factory.setErrorListener(SimpleErrorListener.THROW_FATAL); 1019 Transformer transformer = factory.newTransformer(new StreamSource(xsl.getCharacterStream())); 1020 if (parameters != null) { 1021 Iterator it = parameters.entrySet().iterator(); 1022 while ( it.hasNext() ) { 1023 Map.Entry e = (Map.Entry) it.next(); 1024 transformer.setParameter(e.getKey().toString(), e.getValue()); 1025 } 1026 } 1027 transformer.transform(new DOMSource(doc), new StreamResult(sw)); 1028 return sw.toString(); 1029 } 1030 1031 /** 1032 * returns the Node Type As String 1033 * @param node 1034 * @param cftype 1035 * @return 1036 */ 1037 public static String getTypeAsString(Node node, boolean cftype) { 1038 String suffix=cftype?"":"_NODE"; 1039 1040 switch(node.getNodeType()) { 1041 case Node.ATTRIBUTE_NODE: return "ATTRIBUTE"+suffix; 1042 case Node.CDATA_SECTION_NODE: return "CDATA_SECTION"+suffix; 1043 case Node.COMMENT_NODE: return "COMMENT"+suffix; 1044 case Node.DOCUMENT_FRAGMENT_NODE: return "DOCUMENT_FRAGMENT"+suffix; 1045 case Node.DOCUMENT_NODE: return "DOCUMENT"+suffix; 1046 case Node.DOCUMENT_TYPE_NODE: return "DOCUMENT_TYPE"+suffix; 1047 case Node.ELEMENT_NODE: return "ELEMENT"+suffix; 1048 case Node.ENTITY_NODE: return "ENTITY"+suffix; 1049 case Node.ENTITY_REFERENCE_NODE: return "ENTITY_REFERENCE"+suffix; 1050 case Node.NOTATION_NODE: return "NOTATION"+suffix; 1051 case Node.PROCESSING_INSTRUCTION_NODE: return "PROCESSING_INSTRUCTION"+suffix; 1052 case Node.TEXT_NODE: return "TEXT"+suffix; 1053 default: return "UNKNOW"+suffix; 1054 } 1055 } 1056 1057 public synchronized static Element getChildWithName(String name, Element el) { 1058 Element[] children = XMLUtil.getChildElementsAsArray(el); 1059 for(int i=0;i<children.length;i++) { 1060 if(name.equalsIgnoreCase(children[i].getNodeName())) 1061 return children[i]; 1062 } 1063 return null; 1064 } 1065 1066 public static InputSource toInputSource(Resource res) throws IOException { 1067 String str = IOUtil.toString((res), null); 1068 return new InputSource(new StringReader(str)); 1069 } 1070 1071 public static InputSource toInputSource(PageContext pc, Object value) throws IOException, ExpressionException { 1072 if(value instanceof InputSource) { 1073 return (InputSource) value; 1074 } 1075 if(value instanceof String) { 1076 return toInputSource(pc, (String)value); 1077 } 1078 if(value instanceof StringBuffer) { 1079 return toInputSource(pc, value.toString()); 1080 } 1081 if(value instanceof Resource) { 1082 String str = IOUtil.toString(((Resource)value), null); 1083 return new InputSource(new StringReader(str)); 1084 } 1085 if(value instanceof File) { 1086 String str = IOUtil.toString(ResourceUtil.toResource(((File)value)), null); 1087 return new InputSource(new StringReader(str)); 1088 } 1089 if(value instanceof InputStream) { 1090 InputStream is = (InputStream)value; 1091 try { 1092 String str = IOUtil.toString(is, null); 1093 return new InputSource(new StringReader(str)); 1094 } 1095 finally { 1096 IOUtil.closeEL(is); 1097 } 1098 } 1099 if(value instanceof Reader) { 1100 Reader reader = (Reader)value; 1101 try { 1102 String str = IOUtil.toString(reader); 1103 return new InputSource(new StringReader(str)); 1104 } 1105 finally { 1106 IOUtil.closeEL(reader); 1107 } 1108 } 1109 if(value instanceof byte[]) { 1110 return new InputSource(new ByteArrayInputStream((byte[])value)); 1111 } 1112 throw new ExpressionException("cat cast object of type ["+Caster.toClassName(value)+"] to a Input for xml parser"); 1113 1114 } 1115 1116 public static InputSource toInputSource(PageContext pc, String xml) throws IOException, ExpressionException { 1117 return toInputSource(pc, xml,true); 1118 } 1119 1120 public static InputSource toInputSource(PageContext pc, String xml, boolean canBePath) throws IOException, ExpressionException { 1121 // xml text 1122 xml=xml.trim(); 1123 if(!canBePath || xml.startsWith("<")) { 1124 return new InputSource(new StringReader(xml)); 1125 } 1126 // xml link 1127 pc=ThreadLocalPageContext.get(pc); 1128 Resource res = ResourceUtil.toResourceExisting(pc, xml); 1129 return toInputSource(pc, res); 1130 } 1131 1132 public static Struct validate(InputSource xml, InputSource schema, String strSchema) throws XMLException { 1133 return new XMLValidator(schema,strSchema).validate(xml); 1134 } 1135 1136 /** 1137 * adds a child at the first place 1138 * @param parent 1139 * @param child 1140 */ 1141 public static void prependChild(Element parent, Element child) { 1142 Node first = parent.getFirstChild(); 1143 if(first==null)parent.appendChild(child); 1144 else { 1145 parent.insertBefore(child, first); 1146 } 1147 } 1148 1149 public static void setFirst(Node parent, Node node) { 1150 Node first = parent.getFirstChild(); 1151 if(first!=null) parent.insertBefore(node, first); 1152 else parent.appendChild(node); 1153 } 1154 1155 public static XMLReader createXMLReader(String oprionalDefaultSaxParser) throws SAXException { 1156 try{ 1157 return XMLReaderFactory.createXMLReader(oprionalDefaultSaxParser); 1158 } 1159 catch(Throwable t){ 1160 return XMLReaderFactory.createXMLReader(); 1161 } 1162 } 1163 }