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