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