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