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