1 /* 2 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 /* 21 * $Id: BasisLibrary.java,v 1.6 2006/06/20 21:51:58 spericas Exp $ 22 */ 23 24 package com.sun.org.apache.xalan.internal.xsltc.runtime; 25 26 import com.sun.org.apache.xalan.internal.xsltc.DOM; 27 import com.sun.org.apache.xalan.internal.xsltc.Translet; 28 import com.sun.org.apache.xalan.internal.xsltc.dom.AbsoluteIterator; 29 import com.sun.org.apache.xalan.internal.xsltc.dom.ArrayNodeListIterator; 30 import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter; 31 import com.sun.org.apache.xalan.internal.xsltc.dom.MultiDOM; 32 import com.sun.org.apache.xalan.internal.xsltc.dom.SingletonIterator; 33 import com.sun.org.apache.xalan.internal.xsltc.dom.StepIterator; 34 import com.sun.org.apache.xml.internal.dtm.Axis; 35 import com.sun.org.apache.xml.internal.dtm.DTM; 36 import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; 37 import com.sun.org.apache.xml.internal.dtm.DTMManager; 38 import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase; 39 import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeProxy; 40 import com.sun.org.apache.xml.internal.serializer.NamespaceMappings; 41 import com.sun.org.apache.xml.internal.serializer.SerializationHandler; 42 import com.sun.org.apache.xml.internal.utils.XML11Char; 43 import java.text.DecimalFormat; 44 import java.text.DecimalFormatSymbols; 45 import java.text.FieldPosition; 46 import java.text.MessageFormat; 47 import java.text.NumberFormat; 48 import java.util.Locale; 49 import java.util.ResourceBundle; 50 import java.util.concurrent.atomic.AtomicInteger; 51 import javax.xml.transform.dom.DOMSource; 52 import jdk.xml.internal.SecuritySupport; 53 import org.w3c.dom.Attr; 54 import org.w3c.dom.DOMException; 55 import org.w3c.dom.Document; 56 import org.w3c.dom.Element; 57 import org.w3c.dom.NodeList; 58 import org.xml.sax.SAXException; 59 60 /** 61 * Standard XSLT functions. All standard functions expect the current node 62 * and the DOM as their last two arguments. 63 * 64 * @LastModified: Sep 2017 65 */ 66 public final class BasisLibrary { 67 68 private final static String EMPTYSTRING = ""; 69 70 /** 71 * Re-use a single instance of StringBuffer (per thread) in the basis library. 72 * StringBuilder is better, however, DecimalFormat only accept StringBuffer 73 */ 74 private static final ThreadLocal<StringBuilder> threadLocalStringBuilder = 75 new ThreadLocal<StringBuilder> () { 76 @Override protected StringBuilder initialValue() { 77 return new StringBuilder(); 78 } 79 }; 80 81 /** 82 * ThreadLocal for StringBuffer used 83 */ 84 private static final ThreadLocal<StringBuffer> threadLocalStringBuffer = 85 new ThreadLocal<StringBuffer> () { 86 @Override protected StringBuffer initialValue() { 87 return new StringBuffer(); 88 } 89 }; 90 91 /** 92 * Standard function count(node-set) 93 */ countF(DTMAxisIterator iterator)94 public static int countF(DTMAxisIterator iterator) { 95 return(iterator.getLast()); 96 } 97 98 /** 99 * Standard function position() 100 * @deprecated This method exists only for backwards compatibility with old 101 * translets. New code should not reference it. 102 */ 103 @Deprecated positionF(DTMAxisIterator iterator)104 public static int positionF(DTMAxisIterator iterator) { 105 return iterator.isReverse() 106 ? iterator.getLast() - iterator.getPosition() + 1 107 : iterator.getPosition(); 108 } 109 110 /** 111 * XSLT Standard function sum(node-set). 112 * stringToDouble is inlined 113 */ sumF(DTMAxisIterator iterator, DOM dom)114 public static double sumF(DTMAxisIterator iterator, DOM dom) { 115 try { 116 double result = 0.0; 117 int node; 118 while ((node = iterator.next()) != DTMAxisIterator.END) { 119 result += Double.parseDouble(dom.getStringValueX(node)); 120 } 121 return result; 122 } 123 catch (NumberFormatException e) { 124 return Double.NaN; 125 } 126 } 127 128 /** 129 * XSLT Standard function string() 130 */ stringF(int node, DOM dom)131 public static String stringF(int node, DOM dom) { 132 return dom.getStringValueX(node); 133 } 134 135 /** 136 * XSLT Standard function string(value) 137 */ stringF(Object obj, DOM dom)138 public static String stringF(Object obj, DOM dom) { 139 if (obj instanceof DTMAxisIterator) { 140 return dom.getStringValueX(((DTMAxisIterator)obj).reset().next()); 141 } 142 else if (obj instanceof Node) { 143 return dom.getStringValueX(((Node)obj).node); 144 } 145 else if (obj instanceof DOM) { 146 return ((DOM)obj).getStringValue(); 147 } 148 else { 149 return obj.toString(); 150 } 151 } 152 153 /** 154 * XSLT Standard function string(value) 155 */ stringF(Object obj, int node, DOM dom)156 public static String stringF(Object obj, int node, DOM dom) { 157 if (obj instanceof DTMAxisIterator) { 158 return dom.getStringValueX(((DTMAxisIterator)obj).reset().next()); 159 } 160 else if (obj instanceof Node) { 161 return dom.getStringValueX(((Node)obj).node); 162 } 163 else if (obj instanceof DOM) { 164 // When the first argument is a DOM we want the whole 165 // DOM and not just a single node - that would not make sense. 166 //return ((DOM)obj).getStringValueX(node); 167 return ((DOM)obj).getStringValue(); 168 } 169 else if (obj instanceof Double) { 170 Double d = (Double)obj; 171 final String result = d.toString(); 172 final int length = result.length(); 173 if ((result.charAt(length-2)=='.') && 174 (result.charAt(length-1) == '0')) 175 return result.substring(0, length-2); 176 else 177 return result; 178 } 179 else { 180 return obj != null ? obj.toString() : ""; 181 } 182 } 183 184 /** 185 * XSLT Standard function number() 186 */ numberF(int node, DOM dom)187 public static double numberF(int node, DOM dom) { 188 return stringToReal(dom.getStringValueX(node)); 189 } 190 191 /** 192 * XSLT Standard function number(value) 193 */ numberF(Object obj, DOM dom)194 public static double numberF(Object obj, DOM dom) { 195 if (obj instanceof Double) { 196 return ((Double) obj).doubleValue(); 197 } 198 else if (obj instanceof Integer) { 199 return ((Integer) obj).doubleValue(); 200 } 201 else if (obj instanceof Boolean) { 202 return ((Boolean) obj).booleanValue() ? 1.0 : 0.0; 203 } 204 else if (obj instanceof String) { 205 return stringToReal((String) obj); 206 } 207 else if (obj instanceof DTMAxisIterator) { 208 DTMAxisIterator iter = (DTMAxisIterator) obj; 209 return stringToReal(dom.getStringValueX(iter.reset().next())); 210 } 211 else if (obj instanceof Node) { 212 return stringToReal(dom.getStringValueX(((Node) obj).node)); 213 } 214 else if (obj instanceof DOM) { 215 return stringToReal(((DOM) obj).getStringValue()); 216 } 217 else { 218 final String className = obj.getClass().getName(); 219 runTimeError(INVALID_ARGUMENT_ERR, className, "number()"); 220 return 0.0; 221 } 222 } 223 224 /** 225 * XSLT Standard function round() 226 */ roundF(double d)227 public static double roundF(double d) { 228 return (d<-0.5 || d>0.0)?Math.floor(d+0.5):((d==0.0)? 229 d:(Double.isNaN(d)?Double.NaN:-0.0)); 230 } 231 232 /** 233 * XSLT Standard function boolean() 234 */ booleanF(Object obj)235 public static boolean booleanF(Object obj) { 236 if (obj instanceof Double) { 237 final double temp = ((Double) obj).doubleValue(); 238 return temp != 0.0 && !Double.isNaN(temp); 239 } 240 else if (obj instanceof Integer) { 241 return ((Integer) obj).doubleValue() != 0; 242 } 243 else if (obj instanceof Boolean) { 244 return ((Boolean) obj).booleanValue(); 245 } 246 else if (obj instanceof String) { 247 return !((String) obj).equals(EMPTYSTRING); 248 } 249 else if (obj instanceof DTMAxisIterator) { 250 DTMAxisIterator iter = (DTMAxisIterator) obj; 251 return iter.reset().next() != DTMAxisIterator.END; 252 } 253 else if (obj instanceof Node) { 254 return true; 255 } 256 else if (obj instanceof DOM) { 257 String temp = ((DOM) obj).getStringValue(); 258 return !temp.equals(EMPTYSTRING); 259 } 260 else { 261 final String className = obj.getClass().getName(); 262 runTimeError(INVALID_ARGUMENT_ERR, className, "boolean()"); 263 } 264 return false; 265 } 266 267 /** 268 * XSLT Standard function substring(). Must take a double because of 269 * conversions resulting into NaNs and rounding. 270 */ substringF(String value, double start)271 public static String substringF(String value, double start) { 272 if (Double.isNaN(start)) 273 return(EMPTYSTRING); 274 275 final int strlen = getStringLength(value); 276 int istart = (int)Math.round(start) - 1; 277 278 if (istart > strlen) 279 return(EMPTYSTRING); 280 if (istart < 1) 281 istart = 0; 282 try { 283 istart = value.offsetByCodePoints(0, istart); 284 return value.substring(istart); 285 } catch (IndexOutOfBoundsException e) { 286 runTimeError(RUN_TIME_INTERNAL_ERR, "substring()"); 287 return null; 288 } 289 } 290 291 /** 292 * XSLT Standard function substring(). Must take a double because of 293 * conversions resulting into NaNs and rounding. 294 */ substringF(String value, double start, double length)295 public static String substringF(String value, double start, double length) { 296 if (Double.isInfinite(start) || 297 Double.isNaN(start) || 298 Double.isNaN(length) || 299 length < 0) 300 return(EMPTYSTRING); 301 302 int istart = (int)Math.round(start) - 1; 303 int ilength = (int)Math.round(length); 304 final int isum; 305 if (Double.isInfinite(length)) 306 isum = Integer.MAX_VALUE; 307 else 308 isum = istart + ilength; 309 310 final int strlen = getStringLength(value); 311 if (isum < 0 || istart > strlen) 312 return(EMPTYSTRING); 313 314 if (istart < 0) { 315 ilength += istart; 316 istart = 0; 317 } 318 319 try { 320 istart = value.offsetByCodePoints(0, istart); 321 if (isum > strlen) { 322 return value.substring(istart); 323 } else { 324 int offset = value.offsetByCodePoints(istart, ilength); 325 return value.substring(istart, offset); 326 } 327 } catch (IndexOutOfBoundsException e) { 328 runTimeError(RUN_TIME_INTERNAL_ERR, "substring()"); 329 return null; 330 } 331 } 332 333 /** 334 * XSLT Standard function substring-after(). 335 */ substring_afterF(String value, String substring)336 public static String substring_afterF(String value, String substring) { 337 final int index = value.indexOf(substring); 338 if (index >= 0) 339 return value.substring(index + substring.length()); 340 else 341 return EMPTYSTRING; 342 } 343 344 /** 345 * XSLT Standard function substring-before(). 346 */ substring_beforeF(String value, String substring)347 public static String substring_beforeF(String value, String substring) { 348 final int index = value.indexOf(substring); 349 if (index >= 0) 350 return value.substring(0, index); 351 else 352 return EMPTYSTRING; 353 } 354 355 /** 356 * XSLT Standard function translate(). 357 */ translateF(String value, String from, String to)358 public static String translateF(String value, String from, String to) { 359 final int tol = to.length(); 360 final int froml = from.length(); 361 final int valuel = value.length(); 362 363 final StringBuilder result = threadLocalStringBuilder.get(); 364 result.setLength(0); 365 for (int j, i = 0; i < valuel; i++) { 366 final char ch = value.charAt(i); 367 for (j = 0; j < froml; j++) { 368 if (ch == from.charAt(j)) { 369 if (j < tol) 370 result.append(to.charAt(j)); 371 break; 372 } 373 } 374 if (j == froml) 375 result.append(ch); 376 } 377 return result.toString(); 378 } 379 380 /** 381 * XSLT Standard function normalize-space(). 382 */ normalize_spaceF(int node, DOM dom)383 public static String normalize_spaceF(int node, DOM dom) { 384 return normalize_spaceF(dom.getStringValueX(node)); 385 } 386 387 /** 388 * XSLT Standard function normalize-space(string). 389 */ normalize_spaceF(String value)390 public static String normalize_spaceF(String value) { 391 int i = 0, n = value.length(); 392 StringBuilder result = threadLocalStringBuilder.get(); 393 result.setLength(0); 394 395 while (i < n && isWhiteSpace(value.charAt(i))) 396 i++; 397 398 while (true) { 399 while (i < n && !isWhiteSpace(value.charAt(i))) { 400 result.append(value.charAt(i++)); 401 } 402 if (i == n) 403 break; 404 while (i < n && isWhiteSpace(value.charAt(i))) { 405 i++; 406 } 407 if (i < n) 408 result.append(' '); 409 } 410 return result.toString(); 411 } 412 413 /** 414 * XSLT Standard function generate-id(). 415 */ generate_idF(int node)416 public static String generate_idF(int node) { 417 if (node > 0) 418 // Only generate ID if node exists 419 return "N" + node; 420 else 421 // Otherwise return an empty string 422 return EMPTYSTRING; 423 } 424 425 /** 426 * utility function for calls to local-name(). 427 */ getLocalName(String value)428 public static String getLocalName(String value) { 429 int idx = value.lastIndexOf(':'); 430 if (idx >= 0) value = value.substring(idx + 1); 431 idx = value.lastIndexOf('@'); 432 if (idx >= 0) value = value.substring(idx + 1); 433 return(value); 434 } 435 436 /** 437 * External functions that cannot be resolved are replaced with a call 438 * to this method. This method will generate a runtime errors. A good 439 * stylesheet checks whether the function exists using conditional 440 * constructs, and never really tries to call it if it doesn't exist. 441 * But simple stylesheets may result in a call to this method. 442 * The compiler should generate a warning if it encounters a call to 443 * an unresolved external function. 444 */ unresolved_externalF(String name)445 public static void unresolved_externalF(String name) { 446 runTimeError(EXTERNAL_FUNC_ERR, name); 447 } 448 449 /** 450 * Utility function to throw a runtime error on the use of an extension 451 * function when the secure processing feature is set to true. 452 */ unallowed_extension_functionF(String name)453 public static void unallowed_extension_functionF(String name) { 454 runTimeError(UNALLOWED_EXTENSION_FUNCTION_ERR, name); 455 } 456 457 /** 458 * Utility function to throw a runtime error on the use of an extension 459 * element when the secure processing feature is set to true. 460 */ unallowed_extension_elementF(String name)461 public static void unallowed_extension_elementF(String name) { 462 runTimeError(UNALLOWED_EXTENSION_ELEMENT_ERR, name); 463 } 464 465 /** 466 * Utility function to throw a runtime error for an unsupported element. 467 * 468 * This is only used in forward-compatibility mode, when the control flow 469 * cannot be determined. In 1.0 mode, the error message is emitted at 470 * compile time. 471 */ unsupported_ElementF(String qname, boolean isExtension)472 public static void unsupported_ElementF(String qname, boolean isExtension) { 473 if (isExtension) 474 runTimeError(UNSUPPORTED_EXT_ERR, qname); 475 else 476 runTimeError(UNSUPPORTED_XSL_ERR, qname); 477 } 478 479 /** 480 * XSLT Standard function namespace-uri(node-set). 481 */ namespace_uriF(DTMAxisIterator iter, DOM dom)482 public static String namespace_uriF(DTMAxisIterator iter, DOM dom) { 483 return namespace_uriF(iter.next(), dom); 484 } 485 486 /** 487 * XSLT Standard function system-property(name) 488 */ system_propertyF(String name)489 public static String system_propertyF(String name) { 490 if (name.equals("xsl:version")) 491 return("1.0"); 492 if (name.equals("xsl:vendor")) 493 return("Apache Software Foundation (Xalan XSLTC)"); 494 if (name.equals("xsl:vendor-url")) 495 return("http://xml.apache.org/xalan-j"); 496 497 runTimeError(INVALID_ARGUMENT_ERR, name, "system-property()"); 498 return(EMPTYSTRING); 499 } 500 501 /** 502 * XSLT Standard function namespace-uri(). 503 */ namespace_uriF(int node, DOM dom)504 public static String namespace_uriF(int node, DOM dom) { 505 final String value = dom.getNodeName(node); 506 final int colon = value.lastIndexOf(':'); 507 if (colon >= 0) 508 return value.substring(0, colon); 509 else 510 return EMPTYSTRING; 511 } 512 513 /** 514 * Implements the object-type() extension function. 515 * 516 * @see <a href="http://www.exslt.org/">EXSLT</a> 517 */ objectTypeF(Object obj)518 public static String objectTypeF(Object obj) 519 { 520 if (obj instanceof String) 521 return "string"; 522 else if (obj instanceof Boolean) 523 return "boolean"; 524 else if (obj instanceof Number) 525 return "number"; 526 else if (obj instanceof DOM) 527 return "RTF"; 528 else if (obj instanceof DTMAxisIterator) 529 return "node-set"; 530 else 531 return "unknown"; 532 } 533 534 /** 535 * Implements the nodeset() extension function. 536 */ nodesetF(Object obj)537 public static DTMAxisIterator nodesetF(Object obj) { 538 if (obj instanceof DOM) { 539 //final DOMAdapter adapter = (DOMAdapter) obj; 540 final DOM dom = (DOM)obj; 541 return new SingletonIterator(dom.getDocument(), true); 542 } 543 else if (obj instanceof DTMAxisIterator) { 544 return (DTMAxisIterator) obj; 545 } 546 else { 547 final String className = obj.getClass().getName(); 548 runTimeError(DATA_CONVERSION_ERR, "node-set", className); 549 return null; 550 } 551 } 552 553 //-- Begin utility functions 554 isWhiteSpace(char ch)555 private static boolean isWhiteSpace(char ch) { 556 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; 557 } 558 compareStrings(String lstring, String rstring, int op, DOM dom)559 private static boolean compareStrings(String lstring, String rstring, 560 int op, DOM dom) { 561 switch (op) { 562 case Operators.EQ: 563 return lstring.equals(rstring); 564 565 case Operators.NE: 566 return !lstring.equals(rstring); 567 568 case Operators.GT: 569 return numberF(lstring, dom) > numberF(rstring, dom); 570 571 case Operators.LT: 572 return numberF(lstring, dom) < numberF(rstring, dom); 573 574 case Operators.GE: 575 return numberF(lstring, dom) >= numberF(rstring, dom); 576 577 case Operators.LE: 578 return numberF(lstring, dom) <= numberF(rstring, dom); 579 580 default: 581 runTimeError(RUN_TIME_INTERNAL_ERR, "compare()"); 582 return false; 583 } 584 } 585 586 /** 587 * Utility function: node-set/node-set compare. 588 */ compare(DTMAxisIterator left, DTMAxisIterator right, int op, DOM dom)589 public static boolean compare(DTMAxisIterator left, DTMAxisIterator right, 590 int op, DOM dom) { 591 int lnode; 592 left.reset(); 593 594 while ((lnode = left.next()) != DTMAxisIterator.END) { 595 final String lvalue = dom.getStringValueX(lnode); 596 597 int rnode; 598 right.reset(); 599 while ((rnode = right.next()) != DTMAxisIterator.END) { 600 // String value must be the same if both nodes are the same 601 if (lnode == rnode) { 602 if (op == Operators.EQ) { 603 return true; 604 } else if (op == Operators.NE) { 605 continue; 606 } 607 } 608 if (compareStrings(lvalue, dom.getStringValueX(rnode), op, 609 dom)) { 610 return true; 611 } 612 } 613 } 614 return false; 615 } 616 compare(int node, DTMAxisIterator iterator, int op, DOM dom)617 public static boolean compare(int node, DTMAxisIterator iterator, 618 int op, DOM dom) { 619 //iterator.reset(); 620 621 int rnode; 622 String value; 623 624 switch(op) { 625 case Operators.EQ: 626 rnode = iterator.next(); 627 if (rnode != DTMAxisIterator.END) { 628 value = dom.getStringValueX(node); 629 do { 630 if (node == rnode 631 || value.equals(dom.getStringValueX(rnode))) { 632 return true; 633 } 634 } while ((rnode = iterator.next()) != DTMAxisIterator.END); 635 } 636 break; 637 case Operators.NE: 638 rnode = iterator.next(); 639 if (rnode != DTMAxisIterator.END) { 640 value = dom.getStringValueX(node); 641 do { 642 if (node != rnode 643 && !value.equals(dom.getStringValueX(rnode))) { 644 return true; 645 } 646 } while ((rnode = iterator.next()) != DTMAxisIterator.END); 647 } 648 break; 649 case Operators.LT: 650 // Assume we're comparing document order here 651 while ((rnode = iterator.next()) != DTMAxisIterator.END) { 652 if (rnode > node) return true; 653 } 654 break; 655 case Operators.GT: 656 // Assume we're comparing document order here 657 while ((rnode = iterator.next()) != DTMAxisIterator.END) { 658 if (rnode < node) return true; 659 } 660 break; 661 } 662 return(false); 663 } 664 665 /** 666 * Utility function: node-set/number compare. 667 */ compare(DTMAxisIterator left, final double rnumber, final int op, DOM dom)668 public static boolean compare(DTMAxisIterator left, final double rnumber, 669 final int op, DOM dom) { 670 int node; 671 //left.reset(); 672 673 switch (op) { 674 case Operators.EQ: 675 while ((node = left.next()) != DTMAxisIterator.END) { 676 if (numberF(dom.getStringValueX(node), dom) == rnumber) 677 return true; 678 } 679 break; 680 681 case Operators.NE: 682 while ((node = left.next()) != DTMAxisIterator.END) { 683 if (numberF(dom.getStringValueX(node), dom) != rnumber) 684 return true; 685 } 686 break; 687 688 case Operators.GT: 689 while ((node = left.next()) != DTMAxisIterator.END) { 690 if (numberF(dom.getStringValueX(node), dom) > rnumber) 691 return true; 692 } 693 break; 694 695 case Operators.LT: 696 while ((node = left.next()) != DTMAxisIterator.END) { 697 if (numberF(dom.getStringValueX(node), dom) < rnumber) 698 return true; 699 } 700 break; 701 702 case Operators.GE: 703 while ((node = left.next()) != DTMAxisIterator.END) { 704 if (numberF(dom.getStringValueX(node), dom) >= rnumber) 705 return true; 706 } 707 break; 708 709 case Operators.LE: 710 while ((node = left.next()) != DTMAxisIterator.END) { 711 if (numberF(dom.getStringValueX(node), dom) <= rnumber) 712 return true; 713 } 714 break; 715 716 default: 717 runTimeError(RUN_TIME_INTERNAL_ERR, "compare()"); 718 } 719 720 return false; 721 } 722 723 /** 724 * Utility function: node-set/string comparison. 725 */ compare(DTMAxisIterator left, final String rstring, int op, DOM dom)726 public static boolean compare(DTMAxisIterator left, final String rstring, 727 int op, DOM dom) { 728 int node; 729 //left.reset(); 730 while ((node = left.next()) != DTMAxisIterator.END) { 731 if (compareStrings(dom.getStringValueX(node), rstring, op, dom)) { 732 return true; 733 } 734 } 735 return false; 736 } 737 738 compare(Object left, Object right, int op, DOM dom)739 public static boolean compare(Object left, Object right, 740 int op, DOM dom) 741 { 742 boolean result = false; 743 boolean hasSimpleArgs = hasSimpleType(left) && hasSimpleType(right); 744 745 if (op != Operators.EQ && op != Operators.NE) { 746 // If node-boolean comparison -> convert node to boolean 747 if (left instanceof Node || right instanceof Node) { 748 if (left instanceof Boolean) { 749 right = booleanF(right); 750 hasSimpleArgs = true; 751 } 752 if (right instanceof Boolean) { 753 left = booleanF(left); 754 hasSimpleArgs = true; 755 } 756 } 757 758 if (hasSimpleArgs) { 759 switch (op) { 760 case Operators.GT: 761 return numberF(left, dom) > numberF(right, dom); 762 763 case Operators.LT: 764 return numberF(left, dom) < numberF(right, dom); 765 766 case Operators.GE: 767 return numberF(left, dom) >= numberF(right, dom); 768 769 case Operators.LE: 770 return numberF(left, dom) <= numberF(right, dom); 771 772 default: 773 runTimeError(RUN_TIME_INTERNAL_ERR, "compare()"); 774 } 775 } 776 // falls through 777 } 778 779 if (hasSimpleArgs) { 780 if (left instanceof Boolean || right instanceof Boolean) { 781 result = booleanF(left) == booleanF(right); 782 } 783 else if (left instanceof Double || right instanceof Double || 784 left instanceof Integer || right instanceof Integer) { 785 result = numberF(left, dom) == numberF(right, dom); 786 } 787 else { // compare them as strings 788 result = stringF(left, dom).equals(stringF(right, dom)); 789 } 790 791 if (op == Operators.NE) { 792 result = !result; 793 } 794 } 795 else { 796 if (left instanceof Node) { 797 left = new SingletonIterator(((Node)left).node); 798 } 799 if (right instanceof Node) { 800 right = new SingletonIterator(((Node)right).node); 801 } 802 803 if (hasSimpleType(left) || 804 left instanceof DOM && right instanceof DTMAxisIterator) { 805 // swap operands and operator 806 final Object temp = right; right = left; left = temp; 807 op = Operators.swapOp(op); 808 } 809 810 if (left instanceof DOM) { 811 if (right instanceof Boolean) { 812 result = ((Boolean)right).booleanValue(); 813 return result == (op == Operators.EQ); 814 } 815 816 final String sleft = ((DOM)left).getStringValue(); 817 818 if (right instanceof Number) { 819 result = ((Number)right).doubleValue() == 820 stringToReal(sleft); 821 } 822 else if (right instanceof String) { 823 result = sleft.equals((String)right); 824 } 825 else if (right instanceof DOM) { 826 result = sleft.equals(((DOM)right).getStringValue()); 827 } 828 829 if (op == Operators.NE) { 830 result = !result; 831 } 832 return result; 833 } 834 835 // Next, node-set/t for t in {real, string, node-set, result-tree} 836 837 DTMAxisIterator iter = ((DTMAxisIterator)left).reset(); 838 839 if (right instanceof DTMAxisIterator) { 840 result = compare(iter, (DTMAxisIterator)right, op, dom); 841 } 842 else if (right instanceof String) { 843 result = compare(iter, (String)right, op, dom); 844 } 845 else if (right instanceof Number) { 846 final double temp = ((Number)right).doubleValue(); 847 result = compare(iter, temp, op, dom); 848 } 849 else if (right instanceof Boolean) { 850 boolean temp = ((Boolean)right).booleanValue(); 851 result = (iter.reset().next() != DTMAxisIterator.END) == temp; 852 } 853 else if (right instanceof DOM) { 854 result = compare(iter, ((DOM)right).getStringValue(), 855 op, dom); 856 } 857 else if (right == null) { 858 return(false); 859 } 860 else { 861 final String className = right.getClass().getName(); 862 runTimeError(INVALID_ARGUMENT_ERR, className, "compare()"); 863 } 864 } 865 return result; 866 } 867 868 /** 869 * Utility function: used to test context node's language 870 */ testLanguage(String testLang, DOM dom, int node)871 public static boolean testLanguage(String testLang, DOM dom, int node) { 872 // language for context node (if any) 873 String nodeLang = dom.getLanguage(node); 874 if (nodeLang == null) 875 return(false); 876 else 877 nodeLang = nodeLang.toLowerCase(); 878 879 // compare context node's language agains test language 880 testLang = testLang.toLowerCase(); 881 if (testLang.length() == 2) { 882 return(nodeLang.startsWith(testLang)); 883 } 884 else { 885 return(nodeLang.equals(testLang)); 886 } 887 } 888 hasSimpleType(Object obj)889 private static boolean hasSimpleType(Object obj) { 890 return obj instanceof Boolean || obj instanceof Double || 891 obj instanceof Integer || obj instanceof String || 892 obj instanceof Node || obj instanceof DOM; 893 } 894 895 /** 896 * Utility function: used in StringType to convert a string to a real. 897 */ stringToReal(String s)898 public static double stringToReal(String s) { 899 try { 900 return Double.valueOf(s).doubleValue(); 901 } 902 catch (NumberFormatException e) { 903 return Double.NaN; 904 } 905 } 906 907 /** 908 * Utility function: used in StringType to convert a string to an int. 909 */ stringToInt(String s)910 public static int stringToInt(String s) { 911 try { 912 return Integer.parseInt(s); 913 } 914 catch (NumberFormatException e) { 915 return(-1); // ??? 916 } 917 } 918 919 private static final int DOUBLE_FRACTION_DIGITS = 340; 920 private static final double lowerBounds = 0.001; 921 private static final double upperBounds = 10000000; 922 private static DecimalFormat defaultFormatter, xpathFormatter; 923 private static String defaultPattern = ""; 924 925 static { 926 NumberFormat f = NumberFormat.getInstance(Locale.getDefault()); 927 defaultFormatter = (f instanceof DecimalFormat) ? 928 (DecimalFormat) f : new DecimalFormat(); 929 // Set max fraction digits so that truncation does not occur. Setting 930 // the max to Integer.MAX_VALUE may cause problems with some JDK's. 931 defaultFormatter.setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); 932 defaultFormatter.setMinimumFractionDigits(0); 933 defaultFormatter.setMinimumIntegerDigits(1); 934 defaultFormatter.setGroupingUsed(false); 935 936 // This formatter is used to convert numbers according to the XPath 937 // 1.0 syntax which ignores locales (http://www.w3.org/TR/xpath#NT-Number) 938 xpathFormatter = new DecimalFormat("", 939 new DecimalFormatSymbols(Locale.US)); 940 xpathFormatter.setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); 941 xpathFormatter.setMinimumFractionDigits(0); 942 xpathFormatter.setMinimumIntegerDigits(1); 943 xpathFormatter.setGroupingUsed(false); 944 } 945 946 /** 947 * Utility function: used in RealType to convert a real to a string. 948 * Removes the decimal if null. Uses a specialized formatter object 949 * for very large and very small numbers that ignores locales, thus 950 * using always using "." as a decimal separator. 951 */ realToString(double d)952 public static String realToString(double d) { 953 final double m = Math.abs(d); 954 if ((m >= lowerBounds) && (m < upperBounds)) { 955 final String result = Double.toString(d); 956 final int length = result.length(); 957 // Remove leading zeros. 958 if ((result.charAt(length-2) == '.') && 959 (result.charAt(length-1) == '0')) 960 return result.substring(0, length-2); 961 else 962 return result; 963 } 964 else { 965 if (Double.isNaN(d) || Double.isInfinite(d)) 966 return(Double.toString(d)); 967 968 //Convert -0.0 to +0.0 other values remains the same 969 d = d + 0.0; 970 971 // Use the XPath formatter to ignore locales 972 StringBuffer result = threadLocalStringBuffer.get(); 973 result.setLength(0); 974 xpathFormatter.format(d, result, _fieldPosition); 975 return result.toString(); 976 } 977 } 978 979 /** 980 * Utility function: used in RealType to convert a real to an integer 981 */ realToInt(double d)982 public static int realToInt(double d) { 983 return (int)d; 984 } 985 986 /** 987 * Utility function: used to format/adjust a double to a string. The 988 * DecimalFormat object comes from the 'formatSymbols' map in 989 * AbstractTranslet. 990 */ 991 private static FieldPosition _fieldPosition = new FieldPosition(0); 992 formatNumber(double number, String pattern, DecimalFormat formatter)993 public static String formatNumber(double number, String pattern, 994 DecimalFormat formatter) { 995 // bugzilla fix 12813 996 if (formatter == null) { 997 formatter = defaultFormatter; 998 } 999 try { 1000 StringBuffer result = threadLocalStringBuffer.get(); 1001 result.setLength(0); 1002 if (pattern != defaultPattern) { 1003 formatter.applyLocalizedPattern(pattern); 1004 } 1005 formatter.format(number, result, _fieldPosition); 1006 return result.toString(); 1007 } 1008 catch (IllegalArgumentException e) { 1009 runTimeError(FORMAT_NUMBER_ERR, Double.toString(number), pattern); 1010 return(EMPTYSTRING); 1011 } 1012 } 1013 1014 /** 1015 * Utility function: used to convert references to node-sets. If the 1016 * obj is an instanceof Node then create a singleton iterator. 1017 */ referenceToNodeSet(Object obj)1018 public static DTMAxisIterator referenceToNodeSet(Object obj) { 1019 // Convert var/param -> node 1020 if (obj instanceof Node) { 1021 return(new SingletonIterator(((Node)obj).node)); 1022 } 1023 // Convert var/param -> node-set 1024 else if (obj instanceof DTMAxisIterator) { 1025 return(((DTMAxisIterator)obj).cloneIterator().reset()); 1026 } 1027 else { 1028 final String className = obj.getClass().getName(); 1029 runTimeError(DATA_CONVERSION_ERR, className, "node-set"); 1030 return null; 1031 } 1032 } 1033 1034 /** 1035 * Utility function: used to convert reference to org.w3c.dom.NodeList. 1036 */ referenceToNodeList(Object obj, DOM dom)1037 public static NodeList referenceToNodeList(Object obj, DOM dom) { 1038 if (obj instanceof Node || obj instanceof DTMAxisIterator) { 1039 DTMAxisIterator iter = referenceToNodeSet(obj); 1040 return dom.makeNodeList(iter); 1041 } 1042 else if (obj instanceof DOM) { 1043 dom = (DOM)obj; 1044 return dom.makeNodeList(DTMDefaultBase.ROOTNODE); 1045 } 1046 else { 1047 final String className = obj.getClass().getName(); 1048 runTimeError(DATA_CONVERSION_ERR, className, 1049 "org.w3c.dom.NodeList"); 1050 return null; 1051 } 1052 } 1053 1054 /** 1055 * Utility function: used to convert reference to org.w3c.dom.Node. 1056 */ referenceToNode(Object obj, DOM dom)1057 public static org.w3c.dom.Node referenceToNode(Object obj, DOM dom) { 1058 if (obj instanceof Node || obj instanceof DTMAxisIterator) { 1059 DTMAxisIterator iter = referenceToNodeSet(obj); 1060 return dom.makeNode(iter); 1061 } 1062 else if (obj instanceof DOM) { 1063 dom = (DOM)obj; 1064 DTMAxisIterator iter = dom.getChildren(DTMDefaultBase.ROOTNODE); 1065 return dom.makeNode(iter); 1066 } 1067 else { 1068 final String className = obj.getClass().getName(); 1069 runTimeError(DATA_CONVERSION_ERR, className, "org.w3c.dom.Node"); 1070 return null; 1071 } 1072 } 1073 1074 /** 1075 * Utility function: used to convert reference to long. 1076 */ referenceToLong(Object obj)1077 public static long referenceToLong(Object obj) { 1078 if (obj instanceof Number) { 1079 return ((Number) obj).longValue(); // handles Integer and Double 1080 } 1081 else { 1082 final String className = obj.getClass().getName(); 1083 runTimeError(DATA_CONVERSION_ERR, className, Long.TYPE); 1084 return 0; 1085 } 1086 } 1087 1088 /** 1089 * Utility function: used to convert reference to double. 1090 */ referenceToDouble(Object obj)1091 public static double referenceToDouble(Object obj) { 1092 if (obj instanceof Number) { 1093 return ((Number) obj).doubleValue(); // handles Integer and Double 1094 } 1095 else { 1096 final String className = obj.getClass().getName(); 1097 runTimeError(DATA_CONVERSION_ERR, className, Double.TYPE); 1098 return 0; 1099 } 1100 } 1101 1102 /** 1103 * Utility function: used to convert reference to boolean. 1104 */ referenceToBoolean(Object obj)1105 public static boolean referenceToBoolean(Object obj) { 1106 if (obj instanceof Boolean) { 1107 return ((Boolean) obj).booleanValue(); 1108 } 1109 else { 1110 final String className = obj.getClass().getName(); 1111 runTimeError(DATA_CONVERSION_ERR, className, Boolean.TYPE); 1112 return false; 1113 } 1114 } 1115 1116 /** 1117 * Utility function: used to convert reference to String. 1118 */ referenceToString(Object obj, DOM dom)1119 public static String referenceToString(Object obj, DOM dom) { 1120 if (obj instanceof String) { 1121 return (String) obj; 1122 } 1123 else if (obj instanceof DTMAxisIterator) { 1124 return dom.getStringValueX(((DTMAxisIterator)obj).reset().next()); 1125 } 1126 else if (obj instanceof Node) { 1127 return dom.getStringValueX(((Node)obj).node); 1128 } 1129 else if (obj instanceof DOM) { 1130 return ((DOM) obj).getStringValue(); 1131 } 1132 else { 1133 final String className = obj.getClass().getName(); 1134 runTimeError(DATA_CONVERSION_ERR, className, String.class); 1135 return null; 1136 } 1137 } 1138 1139 /** 1140 * Utility function used to convert a w3c Node into an internal DOM iterator. 1141 */ node2Iterator(org.w3c.dom.Node node, Translet translet, DOM dom)1142 public static DTMAxisIterator node2Iterator(org.w3c.dom.Node node, 1143 Translet translet, DOM dom) 1144 { 1145 final org.w3c.dom.Node inNode = node; 1146 // Create a dummy NodeList which only contains the given node to make 1147 // use of the nodeList2Iterator() interface. 1148 org.w3c.dom.NodeList nodelist = new org.w3c.dom.NodeList() { 1149 public int getLength() { 1150 return 1; 1151 } 1152 1153 public org.w3c.dom.Node item(int index) { 1154 if (index == 0) 1155 return inNode; 1156 else 1157 return null; 1158 } 1159 }; 1160 1161 return nodeList2Iterator(nodelist, translet, dom); 1162 } 1163 1164 /** 1165 * In a perfect world, this would be the implementation for 1166 * nodeList2Iterator. In reality, though, this causes a 1167 * ClassCastException in getDTMHandleFromNode because SAXImpl is 1168 * not an instance of DOM2DTM. So we use the more lengthy 1169 * implementation below until this issue has been addressed. 1170 * 1171 * @see org.apache.xml.dtm.ref.DTMManagerDefault#getDTMHandleFromNode 1172 */ nodeList2IteratorUsingHandleFromNode( org.w3c.dom.NodeList nodeList, Translet translet, DOM dom)1173 private static DTMAxisIterator nodeList2IteratorUsingHandleFromNode( 1174 org.w3c.dom.NodeList nodeList, 1175 Translet translet, DOM dom) 1176 { 1177 final int n = nodeList.getLength(); 1178 final int[] dtmHandles = new int[n]; 1179 DTMManager dtmManager = null; 1180 if (dom instanceof MultiDOM) 1181 dtmManager = ((MultiDOM) dom).getDTMManager(); 1182 for (int i = 0; i < n; ++i) { 1183 org.w3c.dom.Node node = nodeList.item(i); 1184 int handle; 1185 if (dtmManager != null) { 1186 handle = dtmManager.getDTMHandleFromNode(node); 1187 } 1188 else if (node instanceof DTMNodeProxy 1189 && ((DTMNodeProxy) node).getDTM() == dom) { 1190 handle = ((DTMNodeProxy) node).getDTMNodeNumber(); 1191 } 1192 else { 1193 runTimeError(RUN_TIME_INTERNAL_ERR, "need MultiDOM"); 1194 return null; 1195 } 1196 dtmHandles[i] = handle; 1197 System.out.println("Node " + i + " has handle 0x" + 1198 Integer.toString(handle, 16)); 1199 } 1200 return new ArrayNodeListIterator(dtmHandles); 1201 } 1202 1203 /** 1204 * Utility function used to convert a w3c NodeList into a internal 1205 * DOM iterator. 1206 */ nodeList2Iterator( org.w3c.dom.NodeList nodeList, Translet translet, DOM dom)1207 public static DTMAxisIterator nodeList2Iterator( 1208 org.w3c.dom.NodeList nodeList, 1209 Translet translet, DOM dom) 1210 { 1211 // First pass: build w3c DOM for all nodes not proxied from our DOM. 1212 // 1213 // Notice: this looses some (esp. parent) context for these nodes, 1214 // so some way to wrap the original nodes inside a DTMAxisIterator 1215 // might be preferable in the long run. 1216 int n = 0; // allow for change in list length, just in case. 1217 Document doc = null; 1218 DTMManager dtmManager = null; 1219 int[] proxyNodes = new int[nodeList.getLength()]; 1220 if (dom instanceof MultiDOM) 1221 dtmManager = ((MultiDOM) dom).getDTMManager(); 1222 for (int i = 0; i < nodeList.getLength(); ++i) { 1223 org.w3c.dom.Node node = nodeList.item(i); 1224 if (node instanceof DTMNodeProxy) { 1225 DTMNodeProxy proxy = (DTMNodeProxy)node; 1226 DTM nodeDTM = proxy.getDTM(); 1227 int handle = proxy.getDTMNodeNumber(); 1228 boolean isOurDOM = (nodeDTM == dom); 1229 if (!isOurDOM && dtmManager != null) { 1230 try { 1231 isOurDOM = (nodeDTM == dtmManager.getDTM(handle)); 1232 } 1233 catch (ArrayIndexOutOfBoundsException e) { 1234 // invalid node handle, so definitely not our doc 1235 } 1236 } 1237 if (isOurDOM) { 1238 proxyNodes[i] = handle; 1239 ++n; 1240 continue; 1241 } 1242 } 1243 proxyNodes[i] = DTM.NULL; 1244 int nodeType = node.getNodeType(); 1245 if (doc == null) { 1246 if (dom instanceof MultiDOM == false) { 1247 runTimeError(RUN_TIME_INTERNAL_ERR, "need MultiDOM"); 1248 return null; 1249 } 1250 try { 1251 AbstractTranslet at = (AbstractTranslet) translet; 1252 doc = at.newDocument("", "__top__"); 1253 } 1254 catch (javax.xml.parsers.ParserConfigurationException e) { 1255 runTimeError(RUN_TIME_INTERNAL_ERR, e.getMessage()); 1256 return null; 1257 } 1258 } 1259 // Use one dummy element as container for each node of the 1260 // list. That way, it is easier to detect resp. avoid 1261 // funny things which change the number of nodes, 1262 // e.g. auto-concatenation of text nodes. 1263 Element mid; 1264 switch (nodeType) { 1265 case org.w3c.dom.Node.ELEMENT_NODE: 1266 case org.w3c.dom.Node.TEXT_NODE: 1267 case org.w3c.dom.Node.CDATA_SECTION_NODE: 1268 case org.w3c.dom.Node.COMMENT_NODE: 1269 case org.w3c.dom.Node.ENTITY_REFERENCE_NODE: 1270 case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE: 1271 mid = doc.createElementNS(null, "__dummy__"); 1272 mid.appendChild(doc.importNode(node, true)); 1273 doc.getDocumentElement().appendChild(mid); 1274 ++n; 1275 break; 1276 case org.w3c.dom.Node.ATTRIBUTE_NODE: 1277 // The mid element also serves as a container for 1278 // attributes, avoiding problems with conflicting 1279 // attributes or node order. 1280 mid = doc.createElementNS(null, "__dummy__"); 1281 mid.setAttributeNodeNS((Attr)doc.importNode(node, true)); 1282 doc.getDocumentElement().appendChild(mid); 1283 ++n; 1284 break; 1285 default: 1286 // Better play it safe for all types we aren't sure we know 1287 // how to deal with. 1288 runTimeError(RUN_TIME_INTERNAL_ERR, 1289 "Don't know how to convert node type " 1290 + nodeType); 1291 } 1292 } 1293 1294 // w3cDOM -> DTM -> DOMImpl 1295 DTMAxisIterator iter = null, childIter = null, attrIter = null; 1296 if (doc != null) { 1297 final MultiDOM multiDOM = (MultiDOM) dom; 1298 DOM idom = (DOM)dtmManager.getDTM(new DOMSource(doc), false, 1299 null, true, false); 1300 // Create DOMAdapter and register with MultiDOM 1301 DOMAdapter domAdapter = new DOMAdapter(idom, 1302 translet.getNamesArray(), 1303 translet.getUrisArray(), 1304 translet.getTypesArray(), 1305 translet.getNamespaceArray()); 1306 multiDOM.addDOMAdapter(domAdapter); 1307 1308 DTMAxisIterator iter1 = idom.getAxisIterator(Axis.CHILD); 1309 DTMAxisIterator iter2 = idom.getAxisIterator(Axis.CHILD); 1310 iter = new AbsoluteIterator( 1311 new StepIterator(iter1, iter2)); 1312 1313 iter.setStartNode(DTMDefaultBase.ROOTNODE); 1314 1315 childIter = idom.getAxisIterator(Axis.CHILD); 1316 attrIter = idom.getAxisIterator(Axis.ATTRIBUTE); 1317 } 1318 1319 // Second pass: find DTM handles for every node in the list. 1320 int[] dtmHandles = new int[n]; 1321 n = 0; 1322 for (int i = 0; i < nodeList.getLength(); ++i) { 1323 if (proxyNodes[i] != DTM.NULL) { 1324 dtmHandles[n++] = proxyNodes[i]; 1325 continue; 1326 } 1327 org.w3c.dom.Node node = nodeList.item(i); 1328 DTMAxisIterator iter3 = null; 1329 int nodeType = node.getNodeType(); 1330 switch (nodeType) { 1331 case org.w3c.dom.Node.ELEMENT_NODE: 1332 case org.w3c.dom.Node.TEXT_NODE: 1333 case org.w3c.dom.Node.CDATA_SECTION_NODE: 1334 case org.w3c.dom.Node.COMMENT_NODE: 1335 case org.w3c.dom.Node.ENTITY_REFERENCE_NODE: 1336 case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE: 1337 iter3 = childIter; 1338 break; 1339 case org.w3c.dom.Node.ATTRIBUTE_NODE: 1340 iter3 = attrIter; 1341 break; 1342 default: 1343 // Should not happen, as first run should have got all these 1344 throw new InternalRuntimeError("Mismatched cases"); 1345 } 1346 if (iter3 != null) { 1347 iter3.setStartNode(iter.next()); 1348 dtmHandles[n] = iter3.next(); 1349 // For now, play it self and perform extra checks: 1350 if (dtmHandles[n] == DTMAxisIterator.END) 1351 throw new InternalRuntimeError("Expected element missing at " + i); 1352 if (iter3.next() != DTMAxisIterator.END) 1353 throw new InternalRuntimeError("Too many elements at " + i); 1354 ++n; 1355 } 1356 } 1357 if (n != dtmHandles.length) 1358 throw new InternalRuntimeError("Nodes lost in second pass"); 1359 1360 return new ArrayNodeListIterator(dtmHandles); 1361 } 1362 1363 /** 1364 * Utility function used to convert references to DOMs. 1365 */ referenceToResultTree(Object obj)1366 public static DOM referenceToResultTree(Object obj) { 1367 try { 1368 return ((DOM) obj); 1369 } 1370 catch (IllegalArgumentException e) { 1371 final String className = obj.getClass().getName(); 1372 runTimeError(DATA_CONVERSION_ERR, "reference", className); 1373 return null; 1374 } 1375 } 1376 1377 /** 1378 * Utility function: used with nth position filters to convert a sequence 1379 * of nodes to just one single node (the one at position n). 1380 */ getSingleNode(DTMAxisIterator iterator)1381 public static DTMAxisIterator getSingleNode(DTMAxisIterator iterator) { 1382 int node = iterator.next(); 1383 return(new SingletonIterator(node)); 1384 } 1385 1386 /** 1387 * Utility function: used in xsl:copy. 1388 */ 1389 private static char[] _characterArray = new char[32]; 1390 copy(Object obj, SerializationHandler handler, int node, DOM dom)1391 public static void copy(Object obj, 1392 SerializationHandler handler, 1393 int node, 1394 DOM dom) { 1395 try { 1396 if (obj instanceof DTMAxisIterator) 1397 { 1398 DTMAxisIterator iter = (DTMAxisIterator) obj; 1399 dom.copy(iter.reset(), handler); 1400 } 1401 else if (obj instanceof Node) { 1402 dom.copy(((Node) obj).node, handler); 1403 } 1404 else if (obj instanceof DOM) { 1405 //((DOM)obj).copy(((com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase)((DOMAdapter)obj).getDOMImpl()).getDocument(), handler); 1406 DOM newDom = (DOM)obj; 1407 newDom.copy(newDom.getDocument(), handler); 1408 } 1409 else { 1410 String string = obj.toString(); // or call stringF() 1411 final int length = string.length(); 1412 if (length > _characterArray.length) 1413 _characterArray = new char[length]; 1414 string.getChars(0, length, _characterArray, 0); 1415 handler.characters(_characterArray, 0, length); 1416 } 1417 } 1418 catch (SAXException e) { 1419 runTimeError(RUN_TIME_COPY_ERR); 1420 } 1421 } 1422 1423 /** 1424 * Utility function to check if xsl:attribute has a valid qname 1425 * This method should only be invoked if the name attribute is an AVT 1426 */ checkAttribQName(String name)1427 public static void checkAttribQName(String name) { 1428 final int firstOccur = name.indexOf(':'); 1429 final int lastOccur = name.lastIndexOf(':'); 1430 final String localName = name.substring(lastOccur + 1); 1431 1432 if (firstOccur > 0) { 1433 final String newPrefix = name.substring(0, firstOccur); 1434 1435 if (firstOccur != lastOccur) { 1436 final String oriPrefix = name.substring(firstOccur+1, lastOccur); 1437 if (!XML11Char.isXML11ValidNCName(oriPrefix)) { 1438 // even though the orignal prefix is ignored, it should still get checked for valid NCName 1439 runTimeError(INVALID_QNAME_ERR,oriPrefix+":"+localName); 1440 } 1441 } 1442 1443 // prefix must be a valid NCName 1444 if (!XML11Char.isXML11ValidNCName(newPrefix)) { 1445 runTimeError(INVALID_QNAME_ERR,newPrefix+":"+localName); 1446 } 1447 } 1448 1449 // local name must be a valid NCName and must not be XMLNS 1450 if ((!XML11Char.isXML11ValidNCName(localName))||(localName.equals(Constants.XMLNS_PREFIX))) { 1451 runTimeError(INVALID_QNAME_ERR,localName); 1452 } 1453 } 1454 1455 /** 1456 * Utility function to check if a name is a valid ncname 1457 * This method should only be invoked if the attribute value is an AVT 1458 */ checkNCName(String name)1459 public static void checkNCName(String name) { 1460 if (!XML11Char.isXML11ValidNCName(name)) { 1461 runTimeError(INVALID_NCNAME_ERR,name); 1462 } 1463 } 1464 1465 /** 1466 * Utility function to check if a name is a valid qname 1467 * This method should only be invoked if the attribute value is an AVT 1468 */ checkQName(String name)1469 public static void checkQName(String name) { 1470 if (!XML11Char.isXML11ValidQName(name)) { 1471 runTimeError(INVALID_QNAME_ERR,name); 1472 } 1473 } 1474 1475 /** 1476 * Utility function for the implementation of xsl:element. 1477 */ startXslElement(String qname, String namespace, SerializationHandler handler, DOM dom, int node)1478 public static String startXslElement(String qname, String namespace, 1479 SerializationHandler handler, DOM dom, int node) 1480 { 1481 try { 1482 // Get prefix from qname 1483 String prefix; 1484 final int index = qname.indexOf(':'); 1485 1486 if (index > 0) { 1487 prefix = qname.substring(0, index); 1488 1489 // Handle case when prefix is not known at compile time 1490 if (namespace == null || namespace.length() == 0) { 1491 try { 1492 // not sure if this line of code ever works 1493 namespace = dom.lookupNamespace(node, prefix); 1494 } 1495 catch(RuntimeException e) { 1496 handler.flushPending(); // need to flush or else can't get namespacemappings 1497 NamespaceMappings nm = handler.getNamespaceMappings(); 1498 namespace = nm.lookupNamespace(prefix); 1499 if (namespace == null) { 1500 runTimeError(NAMESPACE_PREFIX_ERR,prefix); 1501 } 1502 } 1503 } 1504 1505 handler.startElement(namespace, qname.substring(index+1), 1506 qname); 1507 handler.namespaceAfterStartElement(prefix, namespace); 1508 } 1509 else { 1510 // Need to generate a prefix? 1511 if (namespace != null && namespace.length() > 0) { 1512 prefix = generatePrefix(); 1513 qname = prefix + ':' + qname; 1514 handler.startElement(namespace, qname, qname); 1515 handler.namespaceAfterStartElement(prefix, namespace); 1516 } 1517 else { 1518 handler.startElement(null, null, qname); 1519 } 1520 } 1521 } 1522 catch (SAXException e) { 1523 throw new RuntimeException(e.getMessage()); 1524 } 1525 1526 return qname; 1527 } 1528 1529 /** 1530 * This function is used in the execution of xsl:element 1531 */ getPrefix(String qname)1532 public static String getPrefix(String qname) { 1533 final int index = qname.indexOf(':'); 1534 return (index > 0) ? qname.substring(0, index) : null; 1535 } 1536 1537 /** 1538 * These functions are used in the execution of xsl:element to generate 1539 * and reset namespace prefix index local to current transformation process 1540 */ generatePrefix()1541 public static String generatePrefix() { 1542 return ("ns" + threadLocalPrefixIndex.get().getAndIncrement()); 1543 } 1544 resetPrefixIndex()1545 public static void resetPrefixIndex() { 1546 threadLocalPrefixIndex.get().set(0); 1547 } 1548 1549 private static final ThreadLocal<AtomicInteger> threadLocalPrefixIndex = 1550 new ThreadLocal<AtomicInteger>() { 1551 @Override 1552 protected AtomicInteger initialValue() { 1553 return new AtomicInteger(); 1554 } 1555 }; 1556 1557 public static final String RUN_TIME_INTERNAL_ERR = 1558 "RUN_TIME_INTERNAL_ERR"; 1559 public static final String RUN_TIME_COPY_ERR = 1560 "RUN_TIME_COPY_ERR"; 1561 public static final String DATA_CONVERSION_ERR = 1562 "DATA_CONVERSION_ERR"; 1563 public static final String EXTERNAL_FUNC_ERR = 1564 "EXTERNAL_FUNC_ERR"; 1565 public static final String EQUALITY_EXPR_ERR = 1566 "EQUALITY_EXPR_ERR"; 1567 public static final String INVALID_ARGUMENT_ERR = 1568 "INVALID_ARGUMENT_ERR"; 1569 public static final String FORMAT_NUMBER_ERR = 1570 "FORMAT_NUMBER_ERR"; 1571 public static final String ITERATOR_CLONE_ERR = 1572 "ITERATOR_CLONE_ERR"; 1573 public static final String AXIS_SUPPORT_ERR = 1574 "AXIS_SUPPORT_ERR"; 1575 public static final String TYPED_AXIS_SUPPORT_ERR = 1576 "TYPED_AXIS_SUPPORT_ERR"; 1577 public static final String STRAY_ATTRIBUTE_ERR = 1578 "STRAY_ATTRIBUTE_ERR"; 1579 public static final String STRAY_NAMESPACE_ERR = 1580 "STRAY_NAMESPACE_ERR"; 1581 public static final String NAMESPACE_PREFIX_ERR = 1582 "NAMESPACE_PREFIX_ERR"; 1583 public static final String DOM_ADAPTER_INIT_ERR = 1584 "DOM_ADAPTER_INIT_ERR"; 1585 public static final String PARSER_DTD_SUPPORT_ERR = 1586 "PARSER_DTD_SUPPORT_ERR"; 1587 public static final String NAMESPACES_SUPPORT_ERR = 1588 "NAMESPACES_SUPPORT_ERR"; 1589 public static final String CANT_RESOLVE_RELATIVE_URI_ERR = 1590 "CANT_RESOLVE_RELATIVE_URI_ERR"; 1591 public static final String UNSUPPORTED_XSL_ERR = 1592 "UNSUPPORTED_XSL_ERR"; 1593 public static final String UNSUPPORTED_EXT_ERR = 1594 "UNSUPPORTED_EXT_ERR"; 1595 public static final String UNKNOWN_TRANSLET_VERSION_ERR = 1596 "UNKNOWN_TRANSLET_VERSION_ERR"; 1597 public static final String INVALID_QNAME_ERR = "INVALID_QNAME_ERR"; 1598 public static final String INVALID_NCNAME_ERR = "INVALID_NCNAME_ERR"; 1599 public static final String UNALLOWED_EXTENSION_FUNCTION_ERR = "UNALLOWED_EXTENSION_FUNCTION_ERR"; 1600 public static final String UNALLOWED_EXTENSION_ELEMENT_ERR = "UNALLOWED_EXTENSION_ELEMENT_ERR"; 1601 1602 // All error messages are localized and are stored in resource bundles. 1603 private static ResourceBundle m_bundle; 1604 1605 public final static String ERROR_MESSAGES_KEY = "error-messages"; 1606 1607 static { 1608 String resource = "com.sun.org.apache.xalan.internal.xsltc.runtime.ErrorMessages"; 1609 m_bundle = SecuritySupport.getResourceBundle(resource); 1610 } 1611 1612 /** 1613 * Print a run-time error message. 1614 */ runTimeError(String code)1615 public static void runTimeError(String code) { 1616 throw new RuntimeException(m_bundle.getString(code)); 1617 } 1618 runTimeError(String code, Object[] args)1619 public static void runTimeError(String code, Object[] args) { 1620 final String message = MessageFormat.format(m_bundle.getString(code), 1621 args); 1622 throw new RuntimeException(message); 1623 } 1624 runTimeError(String code, Object arg0)1625 public static void runTimeError(String code, Object arg0) { 1626 runTimeError(code, new Object[]{ arg0 } ); 1627 } 1628 runTimeError(String code, Object arg0, Object arg1)1629 public static void runTimeError(String code, Object arg0, Object arg1) { 1630 runTimeError(code, new Object[]{ arg0, arg1 } ); 1631 } 1632 consoleOutput(String msg)1633 public static void consoleOutput(String msg) { 1634 System.out.println(msg); 1635 } 1636 1637 /** 1638 * Replace a certain character in a string with a new substring. 1639 */ replace(String base, char ch, String str)1640 public static String replace(String base, char ch, String str) { 1641 return (base.indexOf(ch) < 0) ? base : 1642 replace(base, String.valueOf(ch), new String[] { str }); 1643 } 1644 replace(String base, String delim, String[] str)1645 public static String replace(String base, String delim, String[] str) { 1646 final int len = base.length(); 1647 final StringBuilder result = threadLocalStringBuilder.get(); 1648 result.setLength(0); 1649 1650 for (int i = 0; i < len; i++) { 1651 final char ch = base.charAt(i); 1652 final int k = delim.indexOf(ch); 1653 1654 if (k >= 0) { 1655 result.append(str[k]); 1656 } 1657 else { 1658 result.append(ch); 1659 } 1660 } 1661 return result.toString(); 1662 } 1663 1664 1665 /** 1666 * Utility method to allow setting parameters of the form 1667 * {namespaceuri}localName 1668 * which get mapped to an instance variable in the class 1669 * Hence a parameter of the form "{http://foo.bar}xyz" 1670 * will be replaced with the corresponding values 1671 * by the BasisLibrary's utility method mapQNametoJavaName 1672 * and thus get mapped to legal java variable names 1673 */ mapQNameToJavaName(String base )1674 public static String mapQNameToJavaName (String base ) { 1675 return replace(base, ".-:/{}?#%*", 1676 new String[] { "$dot$", "$dash$" ,"$colon$", "$slash$", 1677 "","$colon$","$ques$","$hash$","$per$", 1678 "$aster$"}); 1679 1680 } 1681 1682 /** 1683 * Utility method to calculate string-length as a number of code points, 1684 * to avoid possible errors with string that contains 1685 * complementary characters 1686 */ getStringLength(String str)1687 public static int getStringLength(String str) { 1688 return str.codePointCount(0,str.length()); 1689 } 1690 1691 //-- End utility functions 1692 } 1693