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