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