1 52 53 package freemarker.ext.xml; 54 55 import java.io.StringWriter ; 56 import java.util.ArrayList ; 57 import java.util.Collection ; 58 import java.util.HashSet ; 59 import java.util.Iterator ; 60 import java.util.List ; 61 import java.util.Set ; 62 63 import freemarker.log.Logger; 64 import freemarker.template.TemplateHashModel; 65 import freemarker.template.TemplateMethodModel; 66 import freemarker.template.TemplateModel; 67 import freemarker.template.TemplateModelException; 68 import freemarker.template.TemplateNodeModel; 69 import freemarker.template.TemplateScalarModel; 70 import freemarker.template.TemplateSequenceModel; 71 import freemarker.template.utility.ClassUtil; 72 import freemarker.template.utility.Collections12; 73 74 101 public class NodeListModel 102 implements 103 TemplateHashModel, 104 TemplateMethodModel, 105 TemplateScalarModel, 106 TemplateSequenceModel, 107 TemplateNodeModel 108 { 109 private static final Logger logger = Logger.getLogger("freemarker.xml"); 110 111 private static final Class DOM_NODE_CLASS = getClass("org.w3c.dom.Node"); 112 private static final Class DOM4J_NODE_CLASS = getClass("org.dom4j.Node"); 113 private static final Navigator DOM_NAVIGATOR = getNavigator("Dom"); 114 private static final Navigator DOM4J_NAVIGATOR = getNavigator("Dom4j"); 115 private static final Navigator JDOM_NAVIGATOR = getNavigator("Jdom"); 116 private static final Namespaces.Factory NS_FACTORY = getNamespacesFactory(); 117 118 private final Navigator navigator; 120 private final List nodes; 122 private Namespaces namespaces; 124 125 137 public NodeListModel(Object nodes) { 138 Object node = nodes; 139 if(nodes instanceof Collection ) { 140 this.nodes = new ArrayList ((Collection )nodes); 141 node = this.nodes.isEmpty() ? null : this.nodes.get(0); 142 } 143 else if(nodes != null) { 144 this.nodes = Collections12.singletonList(nodes); 145 } 146 else { 147 throw new IllegalArgumentException ("nodes == null"); 148 } 149 if(DOM_NODE_CLASS != null && DOM_NODE_CLASS.isInstance(node)) { 150 navigator = DOM_NAVIGATOR; 151 } 152 else if(DOM4J_NODE_CLASS != null && DOM4J_NODE_CLASS.isInstance(node)) { 153 navigator = DOM4J_NAVIGATOR; 154 } 155 else { 156 navigator = JDOM_NAVIGATOR; 158 } 159 namespaces = NS_FACTORY.create(); 160 } 161 162 private NodeListModel(Navigator navigator, List nodes, Namespaces namespaces) { 163 this.navigator = navigator; 164 this.nodes = nodes; 165 this.namespaces = namespaces; 166 } 167 168 private NodeListModel deriveModel(List derivedNodes) { 169 namespaces.markShared(); 170 return new NodeListModel(navigator, derivedNodes, namespaces); 171 } 172 173 177 public int size() { 178 return nodes.size(); 179 } 180 181 189 public Object exec(List arguments) throws TemplateModelException { 190 if(arguments.size() != 1) { 191 throw new TemplateModelException( 192 "Expecting exactly one argument - an XPath expression"); 193 } 194 return deriveModel(navigator.applyXPath(nodes, (String )arguments.get(0), namespaces)); 195 } 196 197 208 public String getAsString() throws TemplateModelException { 209 StringWriter sw = new StringWriter (size() * 128); 210 for (Iterator iter = nodes.iterator(); iter.hasNext();) { 211 Object o = iter.next(); 212 if(o instanceof String ) { 213 sw.write((String )o); 214 } 215 else { 216 navigator.getAsString(o, sw); 217 } 218 } 219 return sw.toString(); 220 } 221 222 228 public TemplateModel get(int index) { 229 return deriveModel(Collections12.singletonList(nodes.get(index))); 230 } 231 232 410 public TemplateModel get(String key) throws TemplateModelException { 411 NodeOperator op = navigator.getOperator(key); 413 String localName = null; 414 String namespaceUri = ""; 415 if(op == null && key.length() > 0 && key.charAt(0) == '_') { 417 if(key.equals("_unique")) { 418 return deriveModel(removeDuplicates(nodes)); 419 } 420 else if(key.equals("_filterType") || key.equals("_ftype")) { 421 return new FilterByType(); 422 } 423 else if(key.equals("_registerNamespace")) { 424 if(namespaces.isShared()) { 425 namespaces = (Namespaces)namespaces.clone(); 426 } 427 } 428 } 429 if(op == null) { 431 int colon = key.indexOf(':'); 432 if(colon == -1) { 433 localName = key; 435 } 436 else { 437 localName = key.substring(colon + 1); 439 String prefix = key.substring(0, colon); 440 namespaceUri = namespaces.translateNamespacePrefixToUri(prefix); 441 if(namespaceUri == null) { 442 throw new TemplateModelException("Namespace prefix " + prefix + " is not registered."); 443 } 444 } 445 if(localName.charAt(0) == '@') { 446 op = navigator.getAttributeOperator(); 447 localName = localName.substring(1); 448 } 449 else { 450 op = navigator.getChildrenOperator(); 451 } 452 } 453 List result = new ArrayList (); 454 for (Iterator iter = nodes.iterator(); iter.hasNext();) { 455 try { 456 op.process(iter.next(), localName, namespaceUri, result); 457 } 458 catch(RuntimeException e) { 459 throw new TemplateModelException(e); 460 } 461 } 462 return deriveModel(result); 463 } 464 465 469 public boolean isEmpty() { 470 return nodes.isEmpty(); 471 } 472 473 479 public void registerNamespace(String prefix, String uri) { 480 if(namespaces.isShared()) { 481 namespaces = (Namespaces)namespaces.clone(); 482 } 483 namespaces.registerNamespace(prefix, uri); 484 } 485 486 private class FilterByType 487 implements 488 TemplateMethodModel 489 { 490 public Object exec(List arguments) 491 { 492 List filteredNodes = new ArrayList (); 493 for (Iterator iter = arguments.iterator(); iter.hasNext();) 494 { 495 Object node = iter.next(); 496 if(arguments.contains(navigator.getType(node))) { 497 filteredNodes.add(node); 498 } 499 } 500 return deriveModel(filteredNodes); 501 } 502 } 503 504 private static final List removeDuplicates(List list) 505 { 506 int s = list.size(); 507 ArrayList ulist = new ArrayList (s); 508 Set set = new HashSet (s * 4 / 3, .75f); 509 Iterator it = list.iterator(); 510 while (it.hasNext()) { 511 Object o = it.next(); 512 if (set.add(o)) { 513 ulist.add(o); 514 } 515 } 516 return ulist; 517 } 518 519 private static Class getClass(String className) { 520 try { 521 return ClassUtil.forName(className); 522 } 523 catch(Exception e) { 524 if(logger.isDebugEnabled()) { 525 logger.debug("Couldn't load class " + className, e); 526 } 527 return null; 528 } 529 } 530 531 private static Namespaces.Factory getNamespacesFactory() { 532 Namespaces.Factory factory = getNamespacesFactory("JaxenNamespaces"); 533 if(factory == null) { 534 factory = getNamespacesFactory("Namespaces"); 535 } 536 return factory; 537 } 538 539 private static Namespaces.Factory getNamespacesFactory(String clazz) { 540 try { 541 return (Namespaces.Factory) 542 ClassUtil.forName("freemarker.ext.xml." + clazz) 543 .getDeclaredField("FACTORY").get(null); 544 } 545 catch(Throwable t) { 546 if(logger.isDebugEnabled()) { 547 logger.debug("Could not load " + clazz, t); 548 } 549 return null; 550 } 551 } 552 553 private static Navigator getNavigator(String navType) { 554 try { 555 Navigator nav = 556 (Navigator) ClassUtil.forName("freemarker.ext.xml." + navType + "Navigator") 557 .getDeclaredConstructor(new Class [] {}).newInstance(new Object [] {}); 558 return nav; 559 } 560 catch(Throwable t) { 561 if(logger.isDebugEnabled()) { 562 logger.debug("Could not load navigator for " + navType, t); 563 } 564 return null; 565 } 566 } 567 568 public TemplateSequenceModel getChildNodes() throws TemplateModelException 569 { 570 return (TemplateSequenceModel)get("_content"); 571 } 572 573 public String getNodeName() throws TemplateModelException 574 { 575 return getUniqueText((NodeListModel)get("_name"), "name"); 576 } 577 578 public String getNodeNamespace() throws TemplateModelException 579 { 580 return getUniqueText((NodeListModel)get("_nsuri"), "namespace"); 581 } 582 583 public String getNodeType() throws TemplateModelException 584 { 585 return getUniqueText((NodeListModel)get("_type"), "type"); 586 } 587 public TemplateNodeModel getParentNode() throws TemplateModelException 588 { 589 return (TemplateNodeModel)get("_parent"); 590 } 591 592 private String getUniqueText(NodeListModel model, String property) throws TemplateModelException { 593 String s1 = null; 594 Set s = null; 595 for(Iterator it = model.nodes.iterator(); it.hasNext();) { 596 String s2 = (String )it.next(); 597 if(s2 != null) { 598 if(s1 == null) { 600 s1 = s2; 601 } 602 else if(!s1.equals(s2)) { 605 if(s == null) { 606 s = new HashSet (); 607 s.add(s1); 608 } 609 s.add(s2); 610 } 611 } 612 } 613 if(s == null) { 615 return s1; 616 } 617 throw new TemplateModelException( 619 "Value for node " + property + " is ambiguos: " + s); 620 } 621 } 622 | Popular Tags |