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