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