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