001: package net.sf.saxon.functions;
002:
003: import net.sf.saxon.Err;
004: import net.sf.saxon.TransformerFactoryImpl;
005: import net.sf.saxon.Controller;
006: import net.sf.saxon.Configuration;
007: import net.sf.saxon.style.StandardNames;
008: import net.sf.saxon.tinytree.TinyBuilder;
009: import net.sf.saxon.charcode.UnicodeCharacterSet;
010: import net.sf.saxon.expr.*;
011: import net.sf.saxon.om.*;
012: import net.sf.saxon.sort.GlobalOrderComparer;
013: import net.sf.saxon.sort.AtomicComparer;
014: import net.sf.saxon.trace.Location;
015: import net.sf.saxon.trans.DynamicError;
016: import net.sf.saxon.trans.XPathException;
017: import net.sf.saxon.type.SchemaType;
018: import net.sf.saxon.type.Type;
019: import net.sf.saxon.value.*;
020:
021: import javax.xml.transform.Templates;
022: import javax.xml.transform.TransformerConfigurationException;
023: import javax.xml.transform.Transformer;
024: import javax.xml.transform.TransformerException;
025: import java.math.BigDecimal;
026: import java.util.ArrayList;
027: import java.util.List;
028: import java.util.Collections;
029: import java.io.*;
030:
031: /**
032: * This class implements functions that are supplied as standard with SAXON,
033: * but which are not defined in the XSLT or XPath specifications. <p>
034: *
035: * To invoke these functions, use a function call of the form prefix:name() where
036: * name is the method name, and prefix maps to a URI such as
037: * http://saxon.sf.net/net.sf.saxon.functions.Extensions (only the part
038: * of the URI after the last slash is important).
039: */
040:
041: public class Extensions {
042:
043: // The class is never instantiated
044: private Extensions() {
045: }
046:
047: /**
048: * Switch tracing off. Only works if tracing is enabled.
049: */
050:
051: public static void pauseTracing(XPathContext c) {
052: c.getController().pauseTracing(true);
053: }
054:
055: /**
056: * Resume tracing. Only works if tracing was originally enabled
057: * but is currently paused.
058: */
059:
060: public static void resumeTracing(XPathContext c) {
061: c.getController().pauseTracing(false);
062: }
063:
064: /**
065: * Return the system identifier of the context node
066: */
067:
068: public static String systemId(XPathContext c) throws XPathException {
069: Item item = c.getContextItem();
070: if (item == null) {
071: DynamicError e = new DynamicError(
072: "The context item for saxon:systemId() is not set");
073: e.setXPathContext(c);
074: throw e;
075: }
076: if (item instanceof NodeInfo) {
077: return ((NodeInfo) item).getSystemId();
078: } else {
079: return "";
080: }
081: }
082:
083: /**
084: * Return the line number of the context node.
085: */
086: public static int lineNumber(XPathContext c) {
087: Item item = c.getCurrentIterator().current();
088: if (item instanceof NodeInfo) {
089: return ((NodeInfo) item).getLineNumber();
090: } else {
091: return -1;
092: }
093: }
094:
095: /**
096: * Return the line number of the specified node.
097: */
098: public static int lineNumber(NodeInfo node) {
099: if (node == null) {
100: return -1;
101: }
102: return node.getLineNumber();
103: }
104:
105: /**
106: * Remove a document from the document pool. The effect is that the document becomes eligible for
107: * garbage collection, allowing memory to be released when processing of the document has finished.
108: * The downside is that a subsequent call on document() with the same URI causes the document to be
109: * reloaded and reparsed, and the new nodes will have different node identity from the old.
110: * @param context the evaluation context (supplied implicitly by the call mechanism)
111: * @param doc the document to be released from the document pool
112: * @return the document that was released. This allows a call such as
113: * select="saxon:discard-document(document('a.xml'))"
114: */
115:
116: public static DocumentInfo discardDocument(XPathContext context,
117: DocumentInfo doc) {
118: if (doc == null) {
119: return null;
120: }
121: return context.getController().getDocumentPool().discard(doc);
122: }
123:
124: /**
125: * Determine whether two node-sets contain the same nodes
126: * @param p1 The first node-set. The iterator must be correctly ordered.
127: * @param p2 The second node-set. The iterator must be correctly ordered.
128: * @return true if p1 and p2 contain the same set of nodes
129: */
130:
131: public static boolean hasSameNodes(SequenceIterator p1,
132: SequenceIterator p2) throws XPathException {
133: SequenceIterator e1 = p1;
134: SequenceIterator e2 = p2;
135:
136: if (e1 == null) {
137: e1 = EmptyIterator.getInstance();
138: }
139:
140: if (e2 == null) {
141: e2 = EmptyIterator.getInstance();
142: }
143:
144: while (true) {
145: NodeInfo n1 = (NodeInfo) e1.next();
146: NodeInfo n2 = (NodeInfo) e2.next();
147: if (n1 == null || n2 == null) {
148: return n1 == n2;
149: }
150: if (!n1.isSameNodeInfo(n2)) {
151: return false;
152: }
153: }
154: }
155:
156: /**
157: * Total a stored expression over a set of nodes
158: */
159:
160: public static double sum(XPathContext context,
161: SequenceIterator nsv,
162: Evaluate.PreparedExpression pexpression)
163: throws XPathException {
164:
165: if (nsv == null) {
166: nsv = EmptyIterator.getInstance();
167: }
168: if (pexpression == null) {
169: return Double.NaN;
170: }
171: double total = 0.0;
172: XPathContext c = context.newMinorContext();
173: c
174: .setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION);
175: c.setCurrentIterator(nsv);
176: while (true) {
177: Item next = nsv.next();
178: if (next == null)
179: break;
180: Item val = pexpression.expression.evaluateItem(c);
181: if (val instanceof NumericValue) {
182: DoubleValue v = (DoubleValue) ((NumericValue) val)
183: .convert(Type.DOUBLE, context);
184: total += v.getDoubleValue();
185: } else {
186: DynamicError e = new DynamicError(
187: "expression in saxon:sum() must return numeric values");
188: e.setXPathContext(c);
189: throw e;
190: }
191: }
192: return total;
193: }
194:
195: /**
196: * Get the maximum numeric value of a stored expression over a set of nodes
197: */
198:
199: public static double max(XPathContext context,
200: SequenceIterator nsv,
201: Evaluate.PreparedExpression pexpression)
202: throws XPathException {
203:
204: if (nsv == null) {
205: nsv = EmptyIterator.getInstance();
206: }
207: if (pexpression == null) {
208: return Double.NaN;
209: }
210:
211: double max = Double.NEGATIVE_INFINITY;
212: XPathContext c = context.newMinorContext();
213: c
214: .setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION);
215: c.setCurrentIterator(nsv);
216: while (true) {
217: Item next = nsv.next();
218: if (next == null)
219: break;
220: Item val = pexpression.expression.evaluateItem(c);
221: if (val instanceof NumericValue) {
222: DoubleValue v = (DoubleValue) ((NumericValue) val)
223: .convert(Type.DOUBLE, context);
224: if (v.getDoubleValue() > max)
225: max = v.getDoubleValue();
226: } else {
227: DynamicError e = new DynamicError(
228: "expression in saxon:max() must return numeric values");
229: e.setXPathContext(c);
230: throw e;
231: }
232: }
233: return max;
234: }
235:
236: /**
237: * Get the minimum numeric value of a stored expression over a set of nodes
238: */
239:
240: public static double min(XPathContext context,
241: SequenceIterator nsv,
242: Evaluate.PreparedExpression pexpression)
243: throws XPathException {
244:
245: if (nsv == null) {
246: nsv = EmptyIterator.getInstance();
247: }
248: if (pexpression == null) {
249: return Double.NaN;
250: }
251:
252: double min = Double.POSITIVE_INFINITY;
253: XPathContext c = context.newMinorContext();
254: c
255: .setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION);
256: c.setCurrentIterator(nsv);
257: while (true) {
258: Item next = nsv.next();
259: if (next == null)
260: break;
261: Item val = pexpression.expression.evaluateItem(c);
262: if (val instanceof NumericValue) {
263: DoubleValue v = (DoubleValue) ((NumericValue) val)
264: .convert(Type.DOUBLE, context);
265: if (v.getDoubleValue() < min)
266: min = v.getDoubleValue();
267: } else {
268: DynamicError e = new DynamicError(
269: "expression in saxon:min() must return numeric values");
270: e.setXPathContext(context);
271: throw e;
272: }
273: }
274: return min;
275: }
276:
277: /**
278: * Get the node with maximum numeric value of the string-value of each of a set of nodes
279: */
280:
281: public static Value highest(SequenceIterator nsv)
282: throws XPathException {
283: return net.sf.saxon.exslt.Math.highest(nsv);
284: }
285:
286: /**
287: * Get the maximum numeric value of a stored expression over a set of nodes
288: */
289:
290: public static SequenceIterator highest(XPathContext context,
291: SequenceIterator nsv,
292: Evaluate.PreparedExpression pexpression)
293: throws XPathException {
294: if (nsv == null) {
295: return EmptyIterator.getInstance();
296: }
297: if (pexpression == null) {
298: return EmptyIterator.getInstance();
299: }
300:
301: double max = Double.NEGATIVE_INFINITY;
302: XPathContext c = context.newMinorContext();
303: c
304: .setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION);
305: Item highest = null;
306: c.setCurrentIterator(nsv);
307: while (true) {
308: Item next = nsv.next();
309: if (next == null)
310: break;
311: Item val = pexpression.expression.evaluateItem(c);
312: if (val instanceof NumericValue) {
313: DoubleValue v = (DoubleValue) ((NumericValue) val)
314: .convert(Type.DOUBLE, context);
315: if (v.getDoubleValue() > max) {
316: max = v.getDoubleValue();
317: highest = nsv.current();
318: }
319: } else {
320: DynamicError e = new DynamicError(
321: "expression in saxon:highest() must return numeric values");
322: e.setXPathContext(context);
323: throw e;
324: }
325: }
326: return SingletonIterator.makeIterator(highest);
327: }
328:
329: /**
330: * Get the node with minimum numeric value of the string-value of each of a set of nodes
331: */
332:
333: public static Value lowest(SequenceIterator nsv)
334: throws XPathException {
335: return net.sf.saxon.exslt.Math.lowest(nsv);
336: }
337:
338: /**
339: * Get the node with minimum numeric value of a stored expression over a set of nodes
340: */
341:
342: public static SequenceIterator lowest(XPathContext context,
343: SequenceIterator nsv,
344: Evaluate.PreparedExpression pexpression)
345: throws XPathException {
346: if (nsv == null) {
347: return EmptyIterator.getInstance();
348: }
349: if (pexpression == null) {
350: return EmptyIterator.getInstance();
351: }
352:
353: double min = Double.POSITIVE_INFINITY;
354: XPathContext c = context.newMinorContext();
355: c
356: .setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION);
357: Item lowest = null;
358: c.setCurrentIterator(nsv);
359: while (true) {
360: Item next = nsv.next();
361: if (next == null)
362: break;
363: Item val = pexpression.expression.evaluateItem(c);
364: if (val instanceof NumericValue) {
365: DoubleValue v = (DoubleValue) ((NumericValue) val)
366: .convert(Type.DOUBLE, context);
367: if (v.getDoubleValue() < min) {
368: min = v.getDoubleValue();
369: lowest = nsv.current();
370: }
371: } else {
372: DynamicError e = new DynamicError(
373: "expression in saxon:lowest() must return numeric values");
374: e.setXPathContext(context);
375: throw e;
376: }
377: }
378: return SingletonIterator.makeIterator(lowest);
379: }
380:
381: /**
382: * Get the items that satisfy the given expression, up to and excluding the first one
383: * (in sequence order) that doesn't
384: */
385:
386: public static SequenceIterator leading(XPathContext context,
387: SequenceIterator in, Evaluate.PreparedExpression pexp) {
388: if (in == null) {
389: return EmptyIterator.getInstance();
390: }
391: if (pexp == null) {
392: return EmptyIterator.getInstance();
393: }
394:
395: XPathContext c2 = context.newMinorContext();
396: c2
397: .setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION);
398: return new FilterIterator.Leading(in, pexp.expression, c2);
399: }
400:
401: /**
402: * Find all the nodes in ns1 that are after the first node in ns2.
403: * Return ns1 if ns2 is empty,
404: */
405:
406: // This function is no longer documented as a user-visible extension function.
407: // But exslt:trailing depends on it.
408: public static SequenceIterator after(XPathContext context,
409: SequenceIterator ns1, SequenceIterator ns2)
410: throws XPathException {
411:
412: NodeInfo first = null;
413:
414: // Find the first node in ns2 (in document order)
415:
416: GlobalOrderComparer comparer = GlobalOrderComparer
417: .getInstance();
418: while (true) {
419: Item item = ns2.next();
420: if (item == null) {
421: if (first == null) {
422: return ns1;
423: } else {
424: break;
425: }
426: }
427: if (item instanceof NodeInfo) {
428: NodeInfo node = (NodeInfo) item;
429: if (first == null) {
430: first = node;
431: } else {
432: if (comparer.compare(node, first) < 0) {
433: first = node;
434: }
435: }
436: } else {
437: DynamicError e = new DynamicError(
438: "Operand of after() contains an item that is not a node");
439: e.setXPathContext(context);
440: throw e;
441: }
442: }
443:
444: // Filter ns1 to select nodes that come after this one
445:
446: Expression filter = new IdentityComparison(
447: new ContextItemExpression(), Token.FOLLOWS,
448: new SingletonNode(first));
449:
450: return new FilterIterator(ns1, filter, context);
451:
452: }
453:
454: /**
455: * Return a node-set by tokenizing a supplied string. Tokens are delimited by any sequence of
456: * whitespace characters.
457: */
458:
459: // This extension is superseded by fn:tokenize(); it is no longer documented in Saxon 8.1
460: public static SequenceIterator tokenize(String s) {
461: if (s == null) {
462: // empty sequence supplied: treat as empty string
463: return EmptyIterator.getInstance();
464: }
465: return new StringTokenIterator(s);
466: }
467:
468: /**
469: * Return a sequence by tokenizing a supplied string. The argument delim is a String, any character
470: * in this string is considered to be a delimiter character, and any sequence of delimiter characters
471: * acts as a separator between tokens.
472: */
473:
474: // This extension is superseded by fn:tokenize(); it is no longer documented in Saxon 8.1
475: public static SequenceIterator tokenize(String s, String delim) {
476: if (s == null) {
477: // empty sequence supplied: treat as empty string
478: return EmptyIterator.getInstance();
479: }
480: if (delim == null) {
481: return new StringTokenIterator(s);
482: }
483: return new StringTokenIterator(s, delim);
484: }
485:
486: /**
487: * Return an XPath expression that identifies the current node
488: */
489:
490: public static String path(XPathContext c) throws XPathException {
491: Item item = c.getContextItem();
492: if (item == null) {
493: DynamicError e = new DynamicError(
494: "The context item for saxon:path() is not set");
495: e.setXPathContext(c);
496: throw e;
497: }
498: if (item instanceof NodeInfo) {
499: return Navigator.getPath((NodeInfo) item);
500: } else {
501: return "";
502: }
503: }
504:
505: /**
506: * Display the value of the type annotation of a node
507: */
508:
509: public static String typeAnnotation(XPathContext context,
510: NodeInfo node) {
511: if (node == null) {
512: return null;
513: }
514: int code = node.getTypeAnnotation();
515: if ((code & NodeInfo.IS_DTD_TYPE) != 0) {
516: code = StandardNames.XDT_UNTYPED_ATOMIC;
517: }
518: if (code == -1) {
519: int nodeKind = node.getNodeKind();
520: if (nodeKind == Type.ELEMENT || nodeKind == Type.DOCUMENT) {
521: return "untyped";
522: } else {
523: return "untypedAtomic";
524: }
525: }
526: SchemaType type = context.getConfiguration().getSchemaType(
527: code & 0xfffff);
528: if (type == null) {
529: // Anonymous types are not accessible by the namecode
530: return context.getNamePool().getDisplayName(code);
531: }
532: return "type " + type.getDescription();
533: }
534:
535: /**
536: * Return the XPathContext object
537: */
538:
539: public static XPathContext getContext(XPathContext c) {
540: return c;
541: }
542:
543: /**
544: * Return the Controller object
545: */
546:
547: public static Controller getController(XPathContext c) {
548: return c.getController();
549: }
550:
551: /**
552: * Return the Configuration object
553: */
554:
555: public static Configuration getConfiguration(XPathContext c) {
556: return c.getConfiguration();
557: }
558:
559: /**
560: * Get a pseudo-attribute of a processing instruction. Return an empty string
561: * if the pseudo-attribute is not present.
562: * Character references and built-in entity references are expanded
563: */
564:
565: public static String getPseudoAttribute(XPathContext c, String name)
566: throws XPathException {
567: if (name == null) {
568: return null;
569: }
570: Item pi = c.getContextItem();
571: if (pi == null) {
572: DynamicError e = new DynamicError(
573: "The context item for saxon:getPseudoAttribute() is not set");
574: e.setXPathContext(c);
575: throw e;
576: }
577: // we'll assume it's a PI, it doesn't matter if it isn't...
578: String val = ProcInstParser.getPseudoAttribute(pi
579: .getStringValue(), name);
580: if (val == null)
581: return "";
582: return val;
583: }
584:
585: /**
586: * Get a dayTimeDuration value corresponding to a given number of seconds
587: */
588: // no longer documented in Saxon 8.1
589: public static SecondsDurationValue dayTimeDurationFromSeconds(
590: double arg) throws XPathException {
591: return SecondsDurationValue.fromSeconds(new BigDecimal(arg));
592: }
593:
594: /**
595: * Get a yearMonthDuration value corresponding to a given number of months
596: */
597: // no longer documented in Saxon 8.1
598: public static MonthDurationValue yearMonthDurationFromMonths(
599: double arg) {
600: return MonthDurationValue.fromMonths((int) arg);
601: }
602:
603: /**
604: * Perform decimal division to a user-specified precision
605: */
606:
607: public static BigDecimal decimalDivide(BigDecimal arg1,
608: BigDecimal arg2, int scale) {
609: if (arg1 == null || arg2 == null) {
610: return null;
611: }
612: return arg1.divide(arg2, scale, BigDecimal.ROUND_DOWN);
613: }
614:
615: /**
616: * Get the UTF-8 encoding of a string
617: * @param in the supplied string
618: * @return a sequence of integers, each in the range 0-255, representing the octets of the UTF-8
619: * encoding of the given string
620: */
621:
622: public static List stringToUtf8(String in) {
623: if (in == null) {
624: return Collections.EMPTY_LIST;
625: }
626: ArrayList list = new ArrayList(in.length() * 2);
627: byte[] octets = new byte[4];
628: for (int i = 0; i < in.length(); i++) {
629: int used = UnicodeCharacterSet.getUTF8Encoding(
630: in.charAt(i), (i + 1 < in.length() ? in
631: .charAt(i + 1) : (char) 0), octets);
632: for (int j = 0; j < used; j++) {
633: list.add(new Integer(255 & (int) octets[j]));
634: }
635: }
636: return list;
637: }
638:
639: /**
640: * Convert a sequence of integers in the range 0-255, representing a sequence of octets,
641: * to a base64Binary value
642: */
643:
644: public static Base64BinaryValue octetsToBase64Binary(byte[] in) {
645: if (in == null) {
646: return null;
647: }
648: return new Base64BinaryValue(in);
649: }
650:
651: /**
652: * Convert a sequence of integers in the range 0-255, representing a sequence of octets,
653: * to a hexBinary value
654: */
655:
656: public static HexBinaryValue octetsToHexBinary(byte[] in) {
657: if (in == null) {
658: return null;
659: }
660: return new HexBinaryValue(in);
661: }
662:
663: /**
664: * Convert a base64Binary value to a sequence of integers representing the octets contained in the value
665: */
666:
667: public static byte[] base64BinaryToOctets(Base64BinaryValue in) {
668: if (in == null) {
669: return null;
670: }
671: return in.getBinaryValue();
672: }
673:
674: /**
675: * Convert a hexBinary value to a sequence of integers representing the octets contained in the value
676: */
677:
678: public static byte[] hexBinaryToOctets(HexBinaryValue in) {
679: if (in == null) {
680: return null;
681: }
682: return in.getBinaryValue();
683: }
684:
685: /**
686: * Convert a base64Binary value to a String, assuming a particular encoding
687: */
688:
689: public static String base64BinaryToString(XPathContext context,
690: Base64BinaryValue in, String encoding) throws Exception {
691: if (in == null) {
692: return null;
693: }
694: if (encoding == null) {
695: encoding = "UTF-8";
696: }
697: byte[] bytes = in.getBinaryValue();
698: ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
699: InputStreamReader reader = new InputStreamReader(stream,
700: encoding);
701: char[] array = new char[bytes.length];
702: int used = reader.read(array, 0, array.length);
703: checkBytes(array, 0, used, context.getConfiguration()
704: .getNameChecker());
705: return new String(array, 0, used);
706: }
707:
708: /**
709: * Convert a string to a base64Binary value in a given encoding
710: */
711:
712: public static Base64BinaryValue stringToBase64Binary(String in,
713: String encoding) throws UnsupportedEncodingException,
714: IOException {
715: if (in == null) {
716: return null;
717: }
718: if (encoding == null) {
719: encoding = "UTF-8";
720: }
721: ByteArrayOutputStream stream = new ByteArrayOutputStream(in
722: .length());
723: OutputStreamWriter writer = new OutputStreamWriter(stream,
724: encoding);
725: writer.write(in);
726: writer.close();
727: byte[] bytes = stream.toByteArray();
728: return octetsToBase64Binary(bytes);
729: }
730:
731: /**
732: * Convert a hexBinary value to a String, assuming a particular encoding
733: */
734:
735: public static String hexBinaryToString(XPathContext context,
736: HexBinaryValue in, String encoding) throws Exception {
737: if (in == null) {
738: return null;
739: }
740: if (encoding == null) {
741: encoding = "UTF-8";
742: }
743: byte[] bytes = in.getBinaryValue();
744: ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
745: InputStreamReader reader = new InputStreamReader(stream,
746: encoding);
747: char[] array = new char[bytes.length];
748: int used = reader.read(array, 0, array.length);
749: checkBytes(array, 0, used, context.getConfiguration()
750: .getNameChecker());
751: return new String(array, 0, used);
752: }
753:
754: /**
755: * Check that bytes are valid XML characters (UTF-16 encoded)
756: */
757:
758: private static void checkBytes(char[] array, int start, int end,
759: NameChecker checker) throws XPathException {
760: for (int c = start; c < end; c++) {
761: int ch32 = array[c];
762: if (XMLChar.isHighSurrogate(ch32)) {
763: char low = array[c++];
764: ch32 = XMLChar.supplemental((char) ch32, low);
765: }
766: if (!checker.isValidChar(ch32)) {
767: DynamicError err = new DynamicError(
768: "The byte sequence contains a character not allowed by XML (hex "
769: + Integer.toHexString(ch32) + ')');
770: err.setErrorCode("XTDE1180");
771: throw err;
772: }
773: }
774: }
775:
776: /**
777: * Convert a string to a hexBinary value in a given encoding
778: */
779:
780: public static HexBinaryValue stringToHexBinary(String in,
781: String encoding) throws Exception {
782: if (in == null) {
783: return null;
784: }
785: if (encoding == null) {
786: encoding = "UTF-8";
787: }
788: ByteArrayOutputStream stream = new ByteArrayOutputStream(in
789: .length());
790: OutputStreamWriter writer = new OutputStreamWriter(stream,
791: encoding);
792: writer.write(in);
793: writer.close();
794: byte[] bytes = stream.toByteArray();
795: return octetsToHexBinary(bytes);
796: }
797:
798: /**
799: * Test whether a given integer is the codepoint of a valid XML character
800: */
801:
802: public static boolean validCharacter(XPathContext c, int in) {
803: return c.getConfiguration().getNameChecker().isValidChar(in);
804: }
805:
806: /**
807: * Create a parentless namespace node. This function is useful in XQuery when namespaces need to be created
808: * dynamically. The effect is the same as that of the xsl:namespace instruction in XSLT.
809: */
810:
811: public static NodeInfo namespaceNode(XPathContext context,
812: String prefix, String uri) throws XPathException {
813: if (prefix == null) {
814: prefix = "";
815: }
816: if (!("".equals(prefix) || context.getConfiguration()
817: .getNameChecker().isValidNCName(prefix))) {
818: DynamicError err = new DynamicError("Namespace prefix "
819: + Err.wrap(prefix) + " is not a valid NCName");
820: throw err;
821: }
822: if (uri == null || "".equals(uri)) {
823: DynamicError err = new DynamicError(
824: "URI of namespace node must not be empty");
825: throw err;
826: }
827: final NamePool namePool = context.getNamePool();
828: Orphan node = new Orphan(context.getConfiguration());
829: node.setNodeKind(Type.NAMESPACE);
830: node.setNameCode(namePool.allocate("", "", prefix));
831: node.setStringValue(uri);
832: return node;
833: }
834:
835: /**
836: * Perform a parameterized deep-equals() test
837: * @param context The evaluation context
838: * @param arg1 The first sequence to be compared
839: * @param arg2 The second sequence to be compared
840: * @param collation The collation to be used (null if the default collation is to be used)
841: * @param flags A string whose characters select options that cause the comparison to vary from the
842: * standard fn:deep-equals() function. The flags are:
843: * <ul>
844: * <li>N - take namespace nodes into account</li>
845: * <li>C - take comments into account</li>
846: * <li>P - take processing instructions into account</li>
847: * <li>w - don't take whitespace-only text nodes into account</li>
848: * </ul>
849: * @return true if the sequences are deep equal, otherwise false
850: */
851:
852: public static boolean deepEqual(XPathContext context,
853: SequenceIterator arg1, SequenceIterator arg2,
854: String collation, String flags) throws XPathException {
855: AtomicComparer comparer;
856: if (collation == null) {
857: comparer = new AtomicComparer(
858: context.getDefaultCollation(), context);
859: } else {
860: comparer = new AtomicComparer(context
861: .getCollation(collation), context);
862: }
863: int flag = 0;
864: if (flags.indexOf("N") >= 0) {
865: flag |= DeepEqual.INCLUDE_NAMESPACES;
866: }
867: if (flags.indexOf("C") >= 0) {
868: flag |= DeepEqual.INCLUDE_COMMENTS;
869: }
870: if (flags.indexOf("P") >= 0) {
871: flag |= DeepEqual.INCLUDE_PROCESSING_INSTRUCTIONS;
872: }
873: if (flags.indexOf("S") >= 0) {
874: flag |= DeepEqual.COMPARE_STRING_VALUES;
875: }
876: if (flags.indexOf("A") >= 0) {
877: flag |= DeepEqual.COMPARE_ANNOTATIONS;
878: }
879: if (flags.indexOf("w") >= 0) {
880: flag |= DeepEqual.EXCLUDE_WHITESPACE_TEXT_NODES;
881: }
882: if (flags.indexOf("?") >= 0) {
883: flag |= DeepEqual.WARNING_IF_FALSE;
884: }
885: return DeepEqual.deepEquals(arg1, arg2, comparer, context
886: .getConfiguration(), flag);
887:
888: }
889:
890: /**
891: * Compile a document containing a stylesheet module into a stylesheet that can be used to perform
892: * transformations
893: */
894:
895: public static Templates compileStylesheet(XPathContext context,
896: DocumentInfo doc) throws XPathException {
897: if (doc == null) {
898: return null;
899: }
900: try {
901: TransformerFactoryImpl factory = new TransformerFactoryImpl(
902: context.getConfiguration());
903: return factory.newTemplates(doc);
904: } catch (TransformerConfigurationException e) {
905: throw DynamicError.makeDynamicError(e);
906: }
907: }
908:
909: /**
910: * Run a transformation to convert an input tree to an output document
911: * @param context The dynamic context
912: * @param templates The compiled stylesheet
913: * @param source The initial context node representing the document to be transformed
914: */
915:
916: public static DocumentInfo transform(XPathContext context,
917: Templates templates, NodeInfo source) throws XPathException {
918: if (templates == null) {
919: return null;
920: }
921: if (source == null) {
922: return null;
923: }
924: try {
925: Transformer transformer = templates.newTransformer();
926: TinyBuilder builder = new TinyBuilder();
927: builder.setPipelineConfiguration(context.getController()
928: .makePipelineConfiguration());
929: transformer.transform(source, builder);
930: return (DocumentInfo) builder.getCurrentRoot();
931: } catch (TransformerException e) {
932: throw DynamicError.makeDynamicError(e);
933: }
934: }
935: }
936:
937: //
938: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
939: // you may not use this file except in compliance with the License. You may obtain a copy of the
940: // License at http://www.mozilla.org/MPL/
941: //
942: // Software distributed under the License is distributed on an "AS IS" basis,
943: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
944: // See the License for the specific language governing rights and limitations under the License.
945: //
946: // The Original Code is: all this file.
947: //
948: // The Initial Developer of the Original Code is Michael H. Kay.
949: //
950: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
951: //
952: // Contributor(s): none.
953: //
|