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 }