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