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      */
countF(DTMAxisIterator iterator)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
positionF(DTMAxisIterator iterator)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      */
sumF(DTMAxisIterator iterator, DOM dom)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      */
stringF(int node, DOM dom)131     public static String stringF(int node, DOM dom) {
132         return dom.getStringValueX(node);
133     }
134 
135     /**
136      * XSLT Standard function string(value)
137      */
stringF(Object obj, DOM dom)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      */
stringF(Object obj, int node, DOM dom)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      */
numberF(int node, DOM dom)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      */
numberF(Object obj, DOM dom)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      */
roundF(double d)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      */
booleanF(Object obj)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      */
substringF(String value, double start)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      */
substringF(String value, double start, double length)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      */
substring_afterF(String value, String substring)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      */
substring_beforeF(String value, String substring)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      */
translateF(String value, String from, String to)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      */
normalize_spaceF(int node, DOM dom)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      */
normalize_spaceF(String value)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      */
generate_idF(int node)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      */
getLocalName(String value)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      */
unresolved_externalF(String name)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      */
unallowed_extension_functionF(String name)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      */
unallowed_extension_elementF(String name)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      */
unsupported_ElementF(String qname, boolean isExtension)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      */
namespace_uriF(DTMAxisIterator iter, DOM dom)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      */
system_propertyF(String name)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      */
namespace_uriF(int node, DOM dom)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      */
objectTypeF(Object obj)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      */
nodesetF(Object obj)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 
isWhiteSpace(char ch)555     private static boolean isWhiteSpace(char ch) {
556         return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
557     }
558 
compareStrings(String lstring, String rstring, int op, DOM dom)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      */
compare(DTMAxisIterator left, DTMAxisIterator right, int op, DOM dom)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 
compare(int node, DTMAxisIterator iterator, int op, DOM dom)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      */
compare(DTMAxisIterator left, final double rnumber, final int op, DOM dom)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      */
compare(DTMAxisIterator left, final String rstring, int op, DOM dom)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 
compare(Object left, Object right, int op, DOM dom)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      */
testLanguage(String testLang, DOM dom, int node)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 
hasSimpleType(Object obj)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      */
stringToReal(String s)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      */
stringToInt(String s)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      */
realToString(double d)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      */
realToInt(double d)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 
formatNumber(double number, String pattern, DecimalFormat formatter)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      */
referenceToNodeSet(Object obj)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      */
referenceToNodeList(Object obj, DOM dom)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      */
referenceToNode(Object obj, DOM dom)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      */
referenceToLong(Object obj)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      */
referenceToDouble(Object obj)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      */
referenceToBoolean(Object obj)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      */
referenceToString(Object obj, DOM dom)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      */
node2Iterator(org.w3c.dom.Node node, Translet translet, DOM dom)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      */
nodeList2IteratorUsingHandleFromNode( org.w3c.dom.NodeList nodeList, Translet translet, DOM dom)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      */
nodeList2Iterator( org.w3c.dom.NodeList nodeList, Translet translet, DOM dom)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      */
referenceToResultTree(Object obj)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      */
getSingleNode(DTMAxisIterator iterator)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 
copy(Object obj, SerializationHandler handler, int node, DOM dom)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      */
checkAttribQName(String name)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      */
checkNCName(String name)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      */
checkQName(String name)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      */
startXslElement(String qname, String namespace, SerializationHandler handler, DOM dom, int node)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      */
getPrefix(String qname)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      */
generatePrefix()1541     public static String generatePrefix() {
1542         return ("ns" + threadLocalPrefixIndex.get().getAndIncrement());
1543     }
1544 
resetPrefixIndex()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      */
runTimeError(String code)1615     public static void runTimeError(String code) {
1616         throw new RuntimeException(m_bundle.getString(code));
1617     }
1618 
runTimeError(String code, Object[] args)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 
runTimeError(String code, Object arg0)1625     public static void runTimeError(String code, Object arg0) {
1626         runTimeError(code, new Object[]{ arg0 } );
1627     }
1628 
runTimeError(String code, Object arg0, Object arg1)1629     public static void runTimeError(String code, Object arg0, Object arg1) {
1630         runTimeError(code, new Object[]{ arg0, arg1 } );
1631     }
1632 
consoleOutput(String msg)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      */
replace(String base, char ch, String str)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 
replace(String base, String delim, String[] str)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      */
mapQNameToJavaName(String base )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      */
getStringLength(String str)1687     public static int getStringLength(String str) {
1688         return str.codePointCount(0,str.length());
1689     }
1690 
1691     //-- End utility functions
1692 }
1693