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