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