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