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