1 29 30 package com.caucho.xsl; 31 32 import com.caucho.java.LineMap; 33 import com.caucho.log.Log; 34 import com.caucho.util.CharBuffer; 35 import com.caucho.util.IntArray; 36 import com.caucho.vfs.Path; 37 import com.caucho.xml.CauchoNode; 38 import com.caucho.xml.QAbstractNode; 39 import com.caucho.xml.QElement; 40 import com.caucho.xml.XMLWriter; 41 import com.caucho.xml.XmlChar; 42 import com.caucho.xml.XmlUtil; 43 import com.caucho.xpath.Env; 44 import com.caucho.xpath.Expr; 45 import com.caucho.xpath.StylesheetEnv; 46 import com.caucho.xpath.XPath; 47 import com.caucho.xpath.XPathException; 48 import com.caucho.xpath.XPathFun; 49 import com.caucho.xpath.pattern.AbstractPattern; 50 import com.caucho.xpath.pattern.NodeIterator; 51 import com.caucho.xsl.fun.DocumentFun; 52 import com.caucho.xsl.fun.ExtensionElementFun; 53 import com.caucho.xsl.fun.ExtensionFunctionFun; 54 import com.caucho.xsl.fun.SystemPropertyFun; 55 import com.caucho.xsl.fun.UnparsedEntityFun; 56 57 import org.w3c.dom.Document ; 58 import org.w3c.dom.Element ; 59 import org.w3c.dom.NamedNodeMap ; 60 import org.w3c.dom.Node ; 61 import org.w3c.dom.Text ; 62 import org.xml.sax.SAXException ; 63 64 import javax.xml.transform.TransformerException ; 65 import java.io.IOException ; 66 import java.util.ArrayList ; 67 import java.util.Comparator ; 68 import java.util.HashMap ; 69 import java.util.Iterator ; 70 import java.util.Locale ; 71 import java.util.logging.Logger ; 72 73 78 public class StylesheetImpl extends AbstractStylesheet { 79 static final Logger log = Log.open(StylesheetImpl.class); 80 81 public char []text; 83 protected HashMap templates; 84 HashMap <String ,XPathFun> _funs = new HashMap <String ,XPathFun>(); 85 86 private HashMap <String ,String > _preserve; 87 private HashMap <String ,String > _strip; 88 private HashMap <String ,String > _preservePrefix; 89 private HashMap <String ,String > _stripPrefix; 90 private HashMap <String ,Object > _properties = new HashMap <String ,Object >(); 91 92 boolean isCacheable = true; 93 protected boolean _defaultDisableEscaping; 94 Path cachePath; 95 long lastModified; 96 97 boolean _generateLocation; 98 99 LineMap lineMap; 100 101 protected void copy(AbstractStylesheet stylesheet) 102 { 103 super.copy(stylesheet); 104 105 StylesheetImpl stylesheetImpl = (StylesheetImpl) stylesheet; 106 107 stylesheetImpl.text = text; 108 stylesheetImpl.templates = templates; 109 stylesheetImpl._preserve = _preserve; 110 stylesheetImpl._strip = _strip; 111 stylesheetImpl._preservePrefix = _preservePrefix; 112 stylesheetImpl._stripPrefix = _stripPrefix; 113 stylesheetImpl.lineMap = lineMap; 114 stylesheetImpl._properties = _properties; 115 stylesheetImpl._defaultDisableEscaping = _defaultDisableEscaping; 116 } 117 118 public OutputFormat getOutputFormat() 119 { 120 return new OutputFormat(); 121 } 122 123 public void setOutputFormat(OutputFormat output) 124 { 125 } 126 127 protected void setSpaces(HashMap <String ,String > preserve, 128 HashMap <String ,String > preservePrefix, 129 HashMap <String ,String > strip, 130 HashMap <String ,String > stripPrefix) 131 { 132 _preserve = preserve; 133 _strip = strip; 134 _preservePrefix = preservePrefix; 135 _stripPrefix = stripPrefix; 136 } 137 138 public void setProperty(String name, Object value) 139 { 140 _properties.put(name, value); 141 } 142 143 public void setGenerateLocation(boolean generateLocation) 144 { 145 _generateLocation = generateLocation; 146 } 147 148 public boolean getGenerateLocation() 149 { 150 return _generateLocation; 151 } 152 153 public Object getProperty(String name) 154 { 155 Object value = _properties.get(name); 156 if (value != null) 157 return value; 158 159 return super.getProperty(name); 160 } 161 162 protected void addFunction(String name, XPathFun fun) 163 { 164 _funs.put(name, fun); 165 } 166 167 public void init(Path path) 168 throws Exception 169 { 170 super.init(path); 171 172 addFunction("system-property", new SystemPropertyFun()); 173 addFunction("element-available", new ExtensionElementFun()); 174 addFunction("function-available", new ExtensionFunctionFun()); 175 addFunction("unparsed-entity-uri", new UnparsedEntityFun()); 176 } 177 178 185 public void transform(Node xml, 186 XMLWriter writer, 187 TransformerImpl transformer) 188 throws SAXException , IOException , TransformerException 189 { 190 if (xml == null) 191 throw new NullPointerException ("can't transform null node"); 192 193 XslWriter out = new XslWriter(null, this, transformer); 194 out.init(writer); 195 196 if (_funs == null) 197 _funs = (HashMap ) ((StylesheetImpl) _stylesheet)._funs.clone(); 198 else 199 _funs.putAll((HashMap ) ((StylesheetImpl) _stylesheet)._funs); 200 201 addFunction("document", new DocumentFun(transformer)); 202 DocumentFun docFun = new DocumentFun(transformer); 203 docFun.setHtml(true); 204 addFunction("html_document", docFun); 205 206 Env env = XPath.createEnv(); 207 env.setFunctions(_funs); 208 StylesheetEnv ssEnv = new StylesheetEnv(); 209 ssEnv.setPath(getPath()); 210 env.setStylesheetEnv(ssEnv); 211 212 out.disableEscaping(_defaultDisableEscaping); 213 214 if (_strip != null && ! _strip.isEmpty()) { 215 stripSpaces(xml); 216 } 217 218 try { 219 _xsl_init(out, xml, env); 220 221 applyNode(out, xml, env, 0, Integer.MAX_VALUE); 222 } catch (TransformerException e) { 223 throw e; 224 } catch (IOException e) { 225 throw e; 226 } catch (SAXException e) { 227 throw e; 228 } catch (Exception e) { 229 throw new XslException(e, lineMap); 230 } 231 232 out.close(); 233 234 XPath.freeEnv(env); 235 236 } 238 239 protected void _xsl_init(XslWriter out, Node context, Env env) 240 throws Exception 241 { 242 } 243 244 protected Document ownerDocument(Node node) 245 { 246 Document owner = node.getOwnerDocument(); 247 248 if (owner != null) 249 return owner; 250 else 251 return (Document ) node; 252 } 253 254 public void applyNode(XslWriter out, Node node, Env env) 255 throws Exception 256 { 257 applyNode(out, node, env, Integer.MIN_VALUE, Integer.MAX_VALUE); 258 } 259 260 protected void applyNode(XslWriter out, Node node, Env env, int min, int max) 261 throws Exception 262 { 263 if (node == null) 264 return; 265 266 switch (node.getNodeType()) { 267 case Node.DOCUMENT_NODE: 268 case Node.DOCUMENT_FRAGMENT_NODE: 269 for (Node child = node.getFirstChild(); 270 child != null; 271 child = child.getNextSibling()) { 272 applyNode(out, child, env, 0, 2147483647); 273 } 274 break; 275 276 case Node.ELEMENT_NODE: 277 out.pushCopy(node); 278 if (node instanceof QElement) { 279 for (Node child = ((QElement) node).getFirstAttribute(); 280 child != null; 281 child = child.getNextSibling()) { 282 applyNode(out, child, env, 0, 2147483647); 283 } 284 } else { 285 NamedNodeMap attributeMap = ((Element) node).getAttributes(); 286 int size = attributeMap.getLength(); 287 for (int i = 0; i < size; i++) { 288 Node child = attributeMap.item(i); 289 290 applyNode(out, child, env, 0, 2147483647); 291 } 292 } 293 294 for (Node child = node.getFirstChild(); 295 child != null; 296 child = child.getNextSibling()) { 297 applyNode(out, child, env, 0, 2147483647); 298 } 299 out.popCopy(node); 300 break; 301 302 case Node.TEXT_NODE: 303 case Node.CDATA_SECTION_NODE: 304 String value = node.getNodeValue(); 305 out.print(value); 306 return; 307 308 case Node.ATTRIBUTE_NODE: 309 out.pushCopy(node); 310 out.popCopy(node); 311 break; 312 313 case Node.ENTITY_REFERENCE_NODE: 314 out.pushCopy(node); 315 out.popCopy(node); 316 break; 317 } 318 } 319 320 329 protected Template getTemplate(HashMap templates, 330 Node node, Env env, int min, int max) 331 throws XPathException 332 { 333 Template template = null; 334 335 Template []templateList = (Template []) templates.get(node.getNodeName()); 336 if (templateList == null) 337 templateList = (Template []) templates.get("*"); 338 339 for (int i = 0; templateList != null && i < templateList.length; i++) { 340 Template subtemplate = templateList[i]; 341 342 if (min <= subtemplate.maxImportance && 343 subtemplate.maxImportance <= max && 344 subtemplate.pattern.match(node, env)) { 345 return subtemplate; 346 } 347 } 348 349 return null; 350 } 351 352 361 protected void applyNodeDefault(XslWriter out, Node node, Env env) 362 throws Exception 363 { 364 switch (node.getNodeType()) { 365 case Node.TEXT_NODE: 366 case Node.CDATA_SECTION_NODE: 367 if (_generateLocation && node instanceof QAbstractNode) 368 out.setLocation(((QAbstractNode) node).getBaseURI(), 369 ((QAbstractNode) node).getFilename(), 370 ((QAbstractNode) node).getLine()); 371 String value = node.getNodeValue(); 372 out.print(value); 373 return; 374 375 case Node.ATTRIBUTE_NODE: 376 case Node.ENTITY_REFERENCE_NODE: 377 out.print(node.getNodeValue()); 378 break; 379 380 case Node.ELEMENT_NODE: 381 case Node.DOCUMENT_NODE: 382 throw new RuntimeException (); 383 } 384 } 385 386 public void printValue(XslWriter out, Node node) throws IOException 387 { 388 if (node != null) 389 out.print(getNodeValue(node)); 390 } 391 392 public String getNodeValue(Node node) 393 { 394 CharBuffer cb = new CharBuffer(); 395 396 nodeValue(cb, node); 397 398 return cb.toString(); 399 } 400 401 private void nodeValue(CharBuffer cb, Node node) 402 { 403 if (node == null) 404 return; 405 406 switch (node.getNodeType()) { 407 case Node.ELEMENT_NODE: 408 for (Node child = node.getFirstChild(); 409 child != null; 410 child = child.getNextSibling()) { 411 switch (child.getNodeType()) { 412 case Node.ELEMENT_NODE: 413 case Node.TEXT_NODE: 414 case Node.CDATA_SECTION_NODE: 415 case Node.ENTITY_REFERENCE_NODE: 416 nodeValue(cb, child); 417 break; 418 } 419 } 420 break; 421 422 case Node.ENTITY_REFERENCE_NODE: 423 cb.append('&'); 424 cb.append(node.getNodeName()); 425 cb.append(';'); 426 break; 427 428 case Node.DOCUMENT_NODE: 429 Document doc = (Document ) node; 430 nodeValue(cb, doc.getDocumentElement()); 431 break; 432 433 case Node.TEXT_NODE: 434 case Node.CDATA_SECTION_NODE: 435 String value = node.getNodeValue(); 436 cb.append(value); 437 break; 438 439 default: 440 cb.append(node.getNodeValue()); 441 break; 442 } 443 } 444 445 protected ArrayList xslSort(Node node, Env env, AbstractPattern pattern, 446 Sort []sortList) 447 throws Exception 448 { 449 ArrayList <Node> sortKeys = new ArrayList <Node>(); 450 451 Iterator<Node> sortIter; 452 NodeIterator iter = pattern.select(node, env); 453 454 while (iter.hasNext()) { 455 Node child = iter.next(); 456 sortKeys.add(child); 457 } 458 459 int []map = new int[sortKeys.size()]; 460 for (int i = map.length - 1; i >= 0; i--) 461 map[i] = i; 462 463 int []workMap = new int[map.length]; 464 465 Object []values = new Object [map.length * sortList.length]; 466 467 int size = map.length; 468 for (int i = 0; i < size; i++) { 469 Node child = (Node) sortKeys.get(i); 470 471 env.setPosition(i + 1); 472 474 for (int j = 0; j < sortList.length; j++) { 475 Sort sort = sortList[j]; 476 Object value = sort.sortValue(child, env); 477 478 values[i * sortList.length + j] = value; 479 } 480 } 481 482 boolean []ascendingList = new boolean[sortList.length]; 483 484 for (int i = 0; i < ascendingList.length; i++) { 485 Expr isAscending = sortList[i].getAscending(); 486 if (isAscending == null || isAscending.evalBoolean(node, env)) 487 ascendingList[i] = true; 488 } 489 490 Comparator []comparatorList = new Comparator [sortList.length]; 491 492 for (int i = 0; i < comparatorList.length; i++) { 493 Expr langExpr = sortList[i].getLang(); 494 String lang = null; 495 496 if (langExpr != null) { 497 lang = langExpr.evalString(node, env); 498 } 499 500 if (lang != null) 501 comparatorList[i] = getComparator(lang); 502 } 503 504 int []caseOrderList = new int[sortList.length]; 505 506 for (int i = 0; i < caseOrderList.length; i++) { 507 Expr caseOrder = sortList[i].getCaseOrder(); 508 if (caseOrder == null) 509 caseOrderList[i] = Sort.NO_CASE_ORDER; 510 else if (caseOrder.evalBoolean(node, env)) 511 caseOrderList[i] = Sort.UPPER_FIRST; 512 else 513 caseOrderList[i] = Sort.LOWER_FIRST; 514 } 515 516 sort(values, sortList, comparatorList, ascendingList, caseOrderList, 517 0, map.length, map, workMap); 518 519 ArrayList sortedKeys = new ArrayList (); 520 521 for (int i = 0; i < map.length; i++) 522 sortedKeys.add(sortKeys.get(map[i])); 523 524 return sortedKeys; 525 } 526 527 530 private Comparator getComparator(String lang) 531 { 532 Locale locale = getLocale(lang); 533 534 return java.text.Collator.getInstance(locale); 535 } 536 537 540 private Locale getLocale(String lang) 541 { 542 int p = lang.indexOf('-'); 543 Locale locale = null; 544 545 if (p < 0) { 546 locale = new Locale (lang, ""); 547 } 548 else { 549 String language = lang.substring(0, p); 550 551 int q = lang.indexOf(p + 1, '-'); 552 553 if (q < 0) { 554 String country = lang.substring(p + 1); 555 556 locale = new Locale (language, country); 557 } 558 else { 559 String country = lang.substring(p + 1, q); 560 String variant = lang.substring(q); 561 562 locale = new Locale (language, country, variant); 563 } 564 } 565 566 return locale; 567 } 568 569 575 private void sort(Object []values, Sort []sortList, 576 Comparator []comparatorList, 577 boolean []ascendingList, 578 int []caseOrder, 579 int head, int tail, 580 int map[], int []workMap) 581 { 582 int length = tail - head; 583 if (length <= 1) 584 return; 585 586 if (length == 2) { 588 int a = map[head]; 589 int b = map[head + 1]; 590 591 if (lessThan(values, sortList, comparatorList, 592 ascendingList, caseOrder, b, a)) { 593 map[head] = b; 594 map[head + 1] = a; 595 } 596 return; 597 } 598 else if (length == 3) { 600 int a = map[head]; 601 int b = map[head + 1]; 602 int c = map[head + 2]; 603 604 if (lessThan(values, sortList, comparatorList, 605 ascendingList, caseOrder, b, a)) { 606 map[head] = b; 607 map[head + 1] = a; 608 a = map[head]; 609 b = map[head + 1]; 610 } 611 612 if (! lessThan(values, sortList, comparatorList, 613 ascendingList, caseOrder, c, b)) { 614 } 615 else if (lessThan(values, sortList, comparatorList, 616 ascendingList, caseOrder, c, a)) { 617 map[head] = c; 618 map[head + 1] = a; 619 map[head + 2] = b; 620 } 621 else { 622 map[head + 1] = c; 623 map[head + 2] = b; 624 } 625 626 return; 627 } 628 629 int pivotIndex = (head + tail) / 2; 630 int pivot = map[pivotIndex]; 631 int top = tail; 632 633 for (int i = tail - 1; i >= head; i--) { 635 if (lessThan(values, sortList, comparatorList, 636 ascendingList, caseOrder, pivot, map[i])) { 637 workMap[--top] = map[i]; 638 map[i] = -1; 639 } 640 } 641 642 if (top == tail) { 644 for (int i = tail - 1; i >= head; i--) { 646 if (! lessThan(values, sortList, comparatorList, 647 ascendingList, caseOrder, map[i], pivot)) { 648 workMap[--top] = map[i]; 649 map[i] = -1; 650 } 651 } 652 653 if (top == head) { 655 for (int i = head; i < tail; i++) 656 map[i] = workMap[i]; 657 return; 658 } 659 } 660 661 int center = head; 663 for (int i = head; i < tail; i++) { 664 if (map[i] >= 0) 665 map[center++] = map[i]; 666 } 667 668 for (int i = center; i < tail; i++) 669 map[i] = workMap[i]; 670 671 sort(values, sortList, comparatorList, ascendingList, caseOrder, 672 head, center, map, workMap); 673 sort(values, sortList, comparatorList, ascendingList, caseOrder, 674 center, tail, map, workMap); 675 } 676 677 680 private void swap(int []map, int a, int b) 681 { 682 int ka = map[a]; 683 int kb = map[b]; 684 685 map[b] = ka; 686 map[a] = kb; 687 } 688 689 692 private boolean lessThan(Object []values, 693 Sort []sortList, 694 Comparator []comparatorList, 695 boolean []ascendingList, 696 int []caseOrder, 697 int ai, int bi) 698 { 699 int len = sortList.length; 700 701 for (int i = 0; i < len; i++) { 702 Object a = values[len * ai + i]; 703 Object b = values[len * bi + i]; 704 705 int cmp = sortList[i].cmp(a, b, comparatorList[i], 706 ascendingList[i], caseOrder[i]); 707 if (cmp < 0) 708 return true; 709 else if (cmp > 0) 710 return false; 711 } 712 713 return false; 714 } 715 716 public void singleNumber(XslWriter out, Node node, Env env, 717 AbstractPattern countPattern, 718 AbstractPattern fromPattern, 719 XslNumberFormat format) 720 throws Exception 721 { 722 if (countPattern == null) 723 countPattern = XPath.parseMatch(node.getNodeName()).getPattern(); 724 725 IntArray numbers = new IntArray(); 726 for (; node != null; node = node.getParentNode()) { 727 if (countPattern.match(node, env)) { 728 numbers.add(countPreviousSiblings(node, env, countPattern)); 729 break; 730 } 731 if (fromPattern != null && fromPattern.match(node, env)) 732 break; 733 } 734 if (fromPattern != null && ! findFromAncestor(node, env, fromPattern)) 735 numbers.clear(); 736 737 format.format(out, numbers); 738 } 739 740 public void multiNumber(XslWriter out, Node node, Env env, 741 AbstractPattern countPattern, 742 AbstractPattern fromPattern, 743 XslNumberFormat format) 744 throws Exception 745 { 746 if (countPattern == null) 747 countPattern = XPath.parseMatch(node.getNodeName()).getPattern(); 748 749 IntArray numbers = new IntArray(); 750 for (; node != null; node = node.getParentNode()) { 751 if (countPattern.match(node, env)) 752 numbers.add(countPreviousSiblings(node, env, countPattern)); 753 754 if (fromPattern != null && fromPattern.match(node, env)) 755 break; 756 } 757 if (fromPattern != null && ! findFromAncestor(node, env, fromPattern)) 758 numbers.clear(); 759 760 format.format(out, numbers); 761 } 762 763 public void anyNumber(XslWriter out, Node node, Env env, 764 AbstractPattern countPattern, 765 AbstractPattern fromPattern, 766 XslNumberFormat format) 767 throws Exception 768 { 769 if (countPattern == null) 770 countPattern = XPath.parseMatch(node.getNodeName()).getPattern(); 771 772 IntArray numbers = new IntArray(); 773 int count = 0; 774 for (; node != null; node = XmlUtil.getPrevious(node)) { 775 if (countPattern.match(node, env)) 776 count++; 777 778 if (fromPattern != null && fromPattern.match(node, env)) 779 break; 780 } 781 numbers.add(count); 782 if (fromPattern != null && ! findFromAncestor(node, env, fromPattern)) 783 numbers.clear(); 784 785 format.format(out, numbers); 786 } 787 788 public void exprNumber(XslWriter out, Node node, Env env, Expr expr, 789 XslNumberFormat format) 790 throws Exception 791 { 792 IntArray numbers = new IntArray(); 793 numbers.add((int) expr.evalNumber(node, env)); 794 795 format.format(out, numbers); 796 } 797 798 private int countPreviousSiblings(Node node, Env env, String name) 799 { 800 int count = 1; 801 for (node = node.getPreviousSibling(); 802 node != null; 803 node = node.getPreviousSibling()) { 804 if (node.getNodeType() == node.ELEMENT_NODE && 805 node.getNodeName().equals(name)) 806 count++; 807 } 808 809 return count; 810 } 811 812 private int countPreviousSiblings(Node node, Env env, AbstractPattern pattern) 813 throws XPathException 814 { 815 int count = 1; 816 for (node = node.getPreviousSibling(); 817 node != null; 818 node = node.getPreviousSibling()) { 819 if (pattern.match(node, env)) 820 count++; 821 } 822 823 return count; 824 } 825 826 private boolean findFromAncestor(Node node, Env env, AbstractPattern pattern) 827 throws XPathException 828 { 829 for (; node != null; node = node.getParentNode()) 830 if (pattern.match(node, env)) 831 return true; 832 833 return false; 834 } 835 836 839 void stripSpaces(Node node) 840 { 841 Node child = node.getFirstChild(); 842 843 while (child != null) { 844 Node next = child.getNextSibling(); 845 846 if (child instanceof Element) { 847 stripSpaces(child); 848 } 849 else if (child instanceof Text ) { 850 String data = ((Text ) child).getData(); 851 852 boolean hasContent = false; 853 for (int i = data.length() - 1; i >= 0; i--) { 854 char ch = data.charAt(i); 855 856 if (! XmlChar.isWhitespace(ch)) { 857 hasContent = true; 858 break; 859 } 860 } 861 862 if (! hasContent && isStripSpaces(node)) { 863 node.removeChild(child); 864 } 865 } 866 867 child = next; 868 } 869 } 870 873 boolean isStripSpaces(Node node) 874 { 875 if (_strip == null) 876 return false; 877 878 for (Node ptr = node; ptr != null; ptr = ptr.getParentNode()) { 879 if (ptr instanceof Element) { 880 Element elt = (Element) ptr; 881 String space = elt.getAttribute("xml:space"); 882 if (space != null && space.equals("preserve")) 883 return false; 884 else if (space != null) 885 break; 886 } 887 } 888 String name = node.getNodeName(); 889 if (_preserve.get(node.getNodeName()) != null) 890 return false; 891 else if (_strip.get(node.getNodeName()) != null) 892 return true; 893 894 CauchoNode cnode = (CauchoNode) node; 895 String nsStar = cnode.getPrefix(); 896 if (_preservePrefix.get(nsStar) != null) 897 return false; 898 else if (_stripPrefix.get(nsStar) != null) 899 return true; 900 901 return _strip.get("*") != null; 902 } 903 904 907 protected static Template []mergeTemplates(Template []star, 908 Template []templates) 909 { 910 Template []merged = new Template[star.length + templates.length]; 911 912 int i = 0; 913 int j = 0; 914 int k = 0; 915 916 while (i < star.length && j < templates.length) { 917 if (star[i].compareTo(templates[j]) > 0) 918 merged[k++] = star[i++]; 919 else 920 merged[k++] = templates[j++]; 921 } 922 923 for (; i < star.length; i++) 924 merged[k++] = star[i]; 925 926 for (; j < templates.length; j++) 927 merged[k++] = templates[j]; 928 929 return merged; 930 } 931 } 932 | Popular Tags |