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