KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > lib > ExsltDynamic


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: ExsltDynamic.java,v 1.11 2004/02/11 17:56:36 minchau Exp $
18  */

19 package org.apache.xalan.lib;
20
21 import javax.xml.parsers.DocumentBuilder JavaDoc;
22 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
23 import javax.xml.transform.TransformerException JavaDoc;
24
25 import org.apache.xalan.extensions.ExpressionContext;
26 import org.apache.xalan.res.XSLMessages;
27 import org.apache.xalan.res.XSLTErrorResources;
28 import org.apache.xpath.NodeSet;
29 import org.apache.xpath.NodeSetDTM;
30 import org.apache.xpath.XPath;
31 import org.apache.xpath.XPathContext;
32 import org.apache.xpath.objects.XBoolean;
33 import org.apache.xpath.objects.XNodeSet;
34 import org.apache.xpath.objects.XNumber;
35 import org.apache.xpath.objects.XObject;
36
37 import org.w3c.dom.Document JavaDoc;
38 import org.w3c.dom.Element JavaDoc;
39 import org.w3c.dom.Node JavaDoc;
40 import org.w3c.dom.NodeList JavaDoc;
41 import org.w3c.dom.Text JavaDoc;
42
43 import org.xml.sax.SAXNotSupportedException JavaDoc;
44
45 /**
46  * This class contains EXSLT dynamic extension functions.
47  *
48  * It is accessed by specifying a namespace URI as follows:
49  * <pre>
50  * xmlns:dyn="http://exslt.org/dynamic"
51  * </pre>
52  * The documentation for each function has been copied from the relevant
53  * EXSLT Implementer page.
54  *
55  * @see <a HREF="http://www.exslt.org/">EXSLT</a>
56
57  * @xsl.usage general
58  */

59 public class ExsltDynamic extends ExsltBase
60 {
61
62    public static final String JavaDoc EXSL_URI = "http://exslt.org/common";
63    
64   /**
65    * The dyn:max function calculates the maximum value for the nodes passed as
66    * the first argument, where the value of each node is calculated dynamically
67    * using an XPath expression passed as a string as the second argument.
68    * <p>
69    * The expressions are evaluated relative to the nodes passed as the first argument.
70    * In other words, the value for each node is calculated by evaluating the XPath
71    * expression with all context information being the same as that for the call to
72    * the dyn:max function itself, except for the following:
73    * <p>
74    * <ul>
75    * <li>the context node is the node whose value is being calculated.</li>
76    * <li>the context position is the position of the node within the node set passed as
77    * the first argument to the dyn:max function, arranged in document order.</li>
78    * <li>the context size is the number of nodes passed as the first argument to the
79    * dyn:max function.</li>
80    * </ul>
81    * <p>
82    * The dyn:max function returns the maximum of these values, calculated in exactly
83    * the same way as for math:max.
84    * <p>
85    * If the expression string passed as the second argument is an invalid XPath
86    * expression (including an empty string), this function returns NaN.
87    * <p>
88    * This function must take a second argument. To calculate the maximum of a set of
89    * nodes based on their string values, you should use the math:max function.
90    *
91    * @param myContext The ExpressionContext passed by the extension processor
92    * @param nl The node set
93    * @param expr The expression string
94    *
95    * @return The maximum evaluation value
96    */

97   public static double max(ExpressionContext myContext, NodeList JavaDoc nl, String JavaDoc expr)
98     throws SAXNotSupportedException JavaDoc
99   {
100
101     XPathContext xctxt = null;
102     if (myContext instanceof XPathContext.XPathExpressionContext)
103       xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
104     else
105       throw new SAXNotSupportedException JavaDoc(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object JavaDoc[]{myContext }));
106
107     if (expr == null || expr.length() == 0)
108       return Double.NaN;
109       
110     NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
111     xctxt.pushContextNodeList(contextNodes);
112     
113     double maxValue = - Double.MAX_VALUE;
114     for (int i = 0; i < contextNodes.getLength(); i++)
115     {
116       int contextNode = contextNodes.item(i);
117       xctxt.pushCurrentNode(contextNode);
118       
119       double result = 0;
120       try
121       {
122         XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
123                                        xctxt.getNamespaceContext(),
124                                        XPath.SELECT);
125         result = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext()).num();
126       }
127       catch (TransformerException JavaDoc e)
128       {
129         xctxt.popCurrentNode();
130         xctxt.popContextNodeList();
131         return Double.NaN;
132       }
133       
134       xctxt.popCurrentNode();
135               
136       if (result > maxValue)
137           maxValue = result;
138     }
139       
140     xctxt.popContextNodeList();
141     return maxValue;
142         
143   }
144   
145   /**
146    * The dyn:min function calculates the minimum value for the nodes passed as the
147    * first argument, where the value of each node is calculated dynamically using
148    * an XPath expression passed as a string as the second argument.
149    * <p>
150    * The expressions are evaluated relative to the nodes passed as the first argument.
151    * In other words, the value for each node is calculated by evaluating the XPath
152    * expression with all context information being the same as that for the call to
153    * the dyn:min function itself, except for the following:
154    * <p>
155    * <ul>
156    * <li>the context node is the node whose value is being calculated.</li>
157    * <li>the context position is the position of the node within the node set passed
158    * as the first argument to the dyn:min function, arranged in document order.</li>
159    * <li>the context size is the number of nodes passed as the first argument to the
160    * dyn:min function.</li>
161    * </ul>
162    * <p>
163    * The dyn:min function returns the minimum of these values, calculated in exactly
164    * the same way as for math:min.
165    * <p>
166    * If the expression string passed as the second argument is an invalid XPath expression
167    * (including an empty string), this function returns NaN.
168    * <p>
169    * This function must take a second argument. To calculate the minimum of a set of
170    * nodes based on their string values, you should use the math:min function.
171    *
172    * @param myContext The ExpressionContext passed by the extension processor
173    * @param nl The node set
174    * @param expr The expression string
175    *
176    * @return The minimum evaluation value
177    */

178   public static double min(ExpressionContext myContext, NodeList JavaDoc nl, String JavaDoc expr)
179     throws SAXNotSupportedException JavaDoc
180   {
181     
182     XPathContext xctxt = null;
183     if (myContext instanceof XPathContext.XPathExpressionContext)
184       xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
185     else
186       throw new SAXNotSupportedException JavaDoc(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object JavaDoc[]{myContext }));
187
188     if (expr == null || expr.length() == 0)
189       return Double.NaN;
190       
191     NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
192     xctxt.pushContextNodeList(contextNodes);
193     
194     double minValue = Double.MAX_VALUE;
195     for (int i = 0; i < nl.getLength(); i++)
196     {
197       int contextNode = contextNodes.item(i);
198       xctxt.pushCurrentNode(contextNode);
199       
200       double result = 0;
201       try
202       {
203         XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
204                                        xctxt.getNamespaceContext(),
205                                        XPath.SELECT);
206         result = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext()).num();
207       }
208       catch (TransformerException JavaDoc e)
209       {
210         xctxt.popCurrentNode();
211         xctxt.popContextNodeList();
212         return Double.NaN;
213       }
214       
215       xctxt.popCurrentNode();
216               
217       if (result < minValue)
218           minValue = result;
219     }
220       
221     xctxt.popContextNodeList();
222     return minValue;
223   
224   }
225
226   /**
227    * The dyn:sum function calculates the sum for the nodes passed as the first argument,
228    * where the value of each node is calculated dynamically using an XPath expression
229    * passed as a string as the second argument.
230    * <p>
231    * The expressions are evaluated relative to the nodes passed as the first argument.
232    * In other words, the value for each node is calculated by evaluating the XPath
233    * expression with all context information being the same as that for the call to
234    * the dyn:sum function itself, except for the following:
235    * <p>
236    * <ul>
237    * <li>the context node is the node whose value is being calculated.</li>
238    * <li>the context position is the position of the node within the node set passed as
239    * the first argument to the dyn:sum function, arranged in document order.</li>
240    * <li>the context size is the number of nodes passed as the first argument to the
241    * dyn:sum function.</li>
242    * </ul>
243    * <p>
244    * The dyn:sum function returns the sumimum of these values, calculated in exactly
245    * the same way as for sum.
246    * <p>
247    * If the expression string passed as the second argument is an invalid XPath
248    * expression (including an empty string), this function returns NaN.
249    * <p>
250    * This function must take a second argument. To calculate the sumimum of a set of
251    * nodes based on their string values, you should use the sum function.
252    *
253    * @param myContext The ExpressionContext passed by the extension processor
254    * @param nl The node set
255    * @param expr The expression string
256    *
257    * @return The sum of the evaluation value on each node
258    */

259   public static double sum(ExpressionContext myContext, NodeList JavaDoc nl, String JavaDoc expr)
260     throws SAXNotSupportedException JavaDoc
261   {
262     XPathContext xctxt = null;
263     if (myContext instanceof XPathContext.XPathExpressionContext)
264       xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
265     else
266       throw new SAXNotSupportedException JavaDoc(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object JavaDoc[]{myContext }));
267
268     if (expr == null || expr.length() == 0)
269       return Double.NaN;
270       
271     NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
272     xctxt.pushContextNodeList(contextNodes);
273     
274     double sum = 0;
275     for (int i = 0; i < nl.getLength(); i++)
276     {
277       int contextNode = contextNodes.item(i);
278       xctxt.pushCurrentNode(contextNode);
279       
280       double result = 0;
281       try
282       {
283         XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
284                                        xctxt.getNamespaceContext(),
285                                        XPath.SELECT);
286         result = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext()).num();
287       }
288       catch (TransformerException JavaDoc e)
289       {
290         xctxt.popCurrentNode();
291         xctxt.popContextNodeList();
292         return Double.NaN;
293       }
294       
295       xctxt.popCurrentNode();
296       
297       sum = sum + result;
298               
299     }
300       
301     xctxt.popContextNodeList();
302     return sum;
303   }
304
305   /**
306    * The dyn:map function evaluates the expression passed as the second argument for
307    * each of the nodes passed as the first argument, and returns a node set of those values.
308    * <p>
309    * The expressions are evaluated relative to the nodes passed as the first argument.
310    * In other words, the value for each node is calculated by evaluating the XPath
311    * expression with all context information being the same as that for the call to
312    * the dyn:map function itself, except for the following:
313    * <p>
314    * <ul>
315    * <li>The context node is the node whose value is being calculated.</li>
316    * <li>the context position is the position of the node within the node set passed
317    * as the first argument to the dyn:map function, arranged in document order.</li>
318    * <li>the context size is the number of nodes passed as the first argument to the
319    * dyn:map function.</li>
320    * </ul>
321    * <p>
322    * If the expression string passed as the second argument is an invalid XPath
323    * expression (including an empty string), this function returns an empty node set.
324    * <p>
325    * If the XPath expression evaluates as a node set, the dyn:map function returns
326    * the union of the node sets returned by evaluating the expression for each of the
327    * nodes in the first argument. Note that this may mean that the node set resulting
328    * from the call to the dyn:map function contains a different number of nodes from
329    * the number in the node set passed as the first argument to the function.
330    * <p>
331    * If the XPath expression evaluates as a number, the dyn:map function returns a
332    * node set containing one exsl:number element (namespace http://exslt.org/common)
333    * for each node in the node set passed as the first argument to the dyn:map function,
334    * in document order. The string value of each exsl:number element is the same as
335    * the result of converting the number resulting from evaluating the expression to
336    * a string as with the number function, with the exception that Infinity results
337    * in an exsl:number holding the highest number the implementation can store, and
338    * -Infinity results in an exsl:number holding the lowest number the implementation
339    * can store.
340    * <p>
341    * If the XPath expression evaluates as a boolean, the dyn:map function returns a
342    * node set containing one exsl:boolean element (namespace http://exslt.org/common)
343    * for each node in the node set passed as the first argument to the dyn:map function,
344    * in document order. The string value of each exsl:boolean element is 'true' if the
345    * expression evaluates as true for the node, and '' if the expression evaluates as
346    * false.
347    * <p>
348    * Otherwise, the dyn:map function returns a node set containing one exsl:string
349    * element (namespace http://exslt.org/common) for each node in the node set passed
350    * as the first argument to the dyn:map function, in document order. The string
351    * value of each exsl:string element is the same as the result of converting the
352    * result of evaluating the expression for the relevant node to a string as with
353    * the string function.
354    *
355    * @param myContext The ExpressionContext passed by the extension processor
356    * @param nl The node set
357    * @param expr The expression string
358    *
359    * @return The node set after evaluation
360    */

361   public static NodeList JavaDoc map(ExpressionContext myContext, NodeList JavaDoc nl, String JavaDoc expr)
362     throws SAXNotSupportedException JavaDoc
363   {
364     XPathContext xctxt = null;
365     Document JavaDoc lDoc = null;
366     
367     if (myContext instanceof XPathContext.XPathExpressionContext)
368       xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
369     else
370       throw new SAXNotSupportedException JavaDoc(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object JavaDoc[]{myContext }));
371
372     if (expr == null || expr.length() == 0)
373       return new NodeSet();
374       
375     NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
376     xctxt.pushContextNodeList(contextNodes);
377     
378     NodeSet resultSet = new NodeSet();
379     resultSet.setShouldCacheNodes(true);
380     
381     for (int i = 0; i < nl.getLength(); i++)
382     {
383       int contextNode = contextNodes.item(i);
384       xctxt.pushCurrentNode(contextNode);
385       
386       XObject object = null;
387       try
388       {
389         XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
390                                        xctxt.getNamespaceContext(),
391                                        XPath.SELECT);
392         object = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext());
393         
394         if (object instanceof XNodeSet)
395         {
396           NodeList JavaDoc nodelist = null;
397           nodelist = ((XNodeSet)object).nodelist();
398         
399           for (int k = 0; k < nodelist.getLength(); k++)
400           {
401             Node JavaDoc n = nodelist.item(k);
402             if (!resultSet.contains(n))
403               resultSet.addNode(n);
404           }
405         }
406         else
407         {
408       if (lDoc == null)
409       {
410             DocumentBuilderFactory JavaDoc dbf = DocumentBuilderFactory.newInstance();
411             dbf.setNamespaceAware(true);
412             DocumentBuilder JavaDoc db = dbf.newDocumentBuilder();
413             lDoc = db.newDocument();
414           }
415         
416           Element JavaDoc element = null;
417           if (object instanceof XNumber)
418             element = lDoc.createElementNS(EXSL_URI, "exsl:number");
419           else if (object instanceof XBoolean)
420             element = lDoc.createElementNS(EXSL_URI, "exsl:boolean");
421           else
422             element = lDoc.createElementNS(EXSL_URI, "exsl:string");
423           
424           Text JavaDoc textNode = lDoc.createTextNode(object.str());
425           element.appendChild(textNode);
426           resultSet.addNode(element);
427         }
428       }
429       catch (Exception JavaDoc e)
430       {
431         xctxt.popCurrentNode();
432         xctxt.popContextNodeList();
433         return new NodeSet();
434       }
435       
436       xctxt.popCurrentNode();
437       
438     }
439       
440     xctxt.popContextNodeList();
441     return resultSet;
442   }
443
444   /**
445    * The dyn:evaluate function evaluates a string as an XPath expression and returns
446    * the resulting value, which might be a boolean, number, string, node set, result
447    * tree fragment or external object. The sole argument is the string to be evaluated.
448    * <p>
449    * If the expression string passed as the second argument is an invalid XPath
450    * expression (including an empty string), this function returns an empty node set.
451    * <p>
452    * You should only use this function if the expression must be constructed dynamically,
453    * otherwise it is much more efficient to use the expression literally.
454    *
455    * @param myContext The ExpressionContext passed by the extension processor
456    * @param xpathExpr The XPath expression string
457    *
458    * @return The evaluation result
459    */

460   public static XObject evaluate(ExpressionContext myContext, String JavaDoc xpathExpr)
461     throws SAXNotSupportedException JavaDoc
462   {
463     if (myContext instanceof XPathContext.XPathExpressionContext)
464     {
465       XPathContext xctxt = null;
466       try
467       {
468         xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
469         XPath dynamicXPath = new XPath(xpathExpr, xctxt.getSAXLocator(),
470                                        xctxt.getNamespaceContext(),
471                                        XPath.SELECT);
472
473         return dynamicXPath.execute(xctxt, myContext.getContextNode(),
474                                     xctxt.getNamespaceContext());
475       }
476       catch (TransformerException JavaDoc e)
477       {
478         return new XNodeSet(xctxt.getDTMManager());
479       }
480     }
481     else
482       throw new SAXNotSupportedException JavaDoc(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object JavaDoc[]{myContext })); //"Invalid context passed to evaluate "
483
}
484
485   /**
486    * The dyn:closure function creates a node set resulting from transitive closure of
487    * evaluating the expression passed as the second argument on each of the nodes passed
488    * as the first argument, then on the node set resulting from that and so on until no
489    * more nodes are found. For example:
490    * <pre>
491    * dyn:closure(., '*')
492    * </pre>
493    * returns all the descendant elements of the node (its element children, their
494    * children, their children's children and so on).
495    * <p>
496    * The expression is thus evaluated several times, each with a different node set
497    * acting as the context of the expression. The first time the expression is
498    * evaluated, the context node set is the first argument passed to the dyn:closure
499    * function. In other words, the node set for each node is calculated by evaluating
500    * the XPath expression with all context information being the same as that for
501    * the call to the dyn:closure function itself, except for the following:
502    * <p>
503    * <ul>
504    * <li>the context node is the node whose value is being calculated.</li>
505    * <li>the context position is the position of the node within the node set passed
506    * as the first argument to the dyn:closure function, arranged in document order.</li>
507    * <li>the context size is the number of nodes passed as the first argument to the
508    * dyn:closure function.</li>
509    * <li>the current node is the node whose value is being calculated.</li>
510    * </ul>
511    * <p>
512    * The result for a particular iteration is the union of the node sets resulting
513    * from evaluting the expression for each of the nodes in the source node set for
514    * that iteration. This result is then used as the source node set for the next
515    * iteration, and so on. The result of the function as a whole is the union of
516    * the node sets generated by each iteration.
517    * <p>
518    * If the expression string passed as the second argument is an invalid XPath
519    * expression (including an empty string) or an expression that does not return a
520    * node set, this function returns an empty node set.
521    *
522    * @param myContext The ExpressionContext passed by the extension processor
523    * @param nl The node set
524    * @param expr The expression string
525    *
526    * @return The node set after evaluation
527    */

528   public static NodeList JavaDoc closure(ExpressionContext myContext, NodeList JavaDoc nl, String JavaDoc expr)
529     throws SAXNotSupportedException JavaDoc
530   {
531     XPathContext xctxt = null;
532     if (myContext instanceof XPathContext.XPathExpressionContext)
533       xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
534     else
535       throw new SAXNotSupportedException JavaDoc(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object JavaDoc[]{myContext }));
536
537     if (expr == null || expr.length() == 0)
538       return new NodeSet();
539           
540     NodeSet closureSet = new NodeSet();
541     closureSet.setShouldCacheNodes(true);
542         
543     NodeList JavaDoc iterationList = nl;
544     do
545     {
546     
547       NodeSet iterationSet = new NodeSet();
548
549       NodeSetDTM contextNodes = new NodeSetDTM(iterationList, xctxt);
550       xctxt.pushContextNodeList(contextNodes);
551       
552       for (int i = 0; i < iterationList.getLength(); i++)
553       {
554         int contextNode = contextNodes.item(i);
555         xctxt.pushCurrentNode(contextNode);
556
557         XObject object = null;
558         try
559         {
560           XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
561                                          xctxt.getNamespaceContext(),
562                                          XPath.SELECT);
563           object = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext());
564           
565           if (object instanceof XNodeSet)
566           {
567             NodeList JavaDoc nodelist = null;
568             nodelist = ((XNodeSet)object).nodelist();
569         
570             for (int k = 0; k < nodelist.getLength(); k++)
571             {
572               Node JavaDoc n = nodelist.item(k);
573               if (!iterationSet.contains(n))
574                 iterationSet.addNode(n);
575             }
576           }
577           else
578           {
579             xctxt.popCurrentNode();
580             xctxt.popContextNodeList();
581             return new NodeSet();
582           }
583         }
584         catch (TransformerException JavaDoc e)
585         {
586           xctxt.popCurrentNode();
587           xctxt.popContextNodeList();
588           return new NodeSet();
589         }
590       
591         xctxt.popCurrentNode();
592             
593       }
594       
595       xctxt.popContextNodeList();
596       
597       iterationList = iterationSet;
598       
599       for (int i = 0; i < iterationList.getLength(); i++)
600       {
601         Node JavaDoc n = iterationList.item(i);
602         if (!closureSet.contains(n))
603           closureSet.addNode(n);
604       }
605       
606     } while(iterationList.getLength() > 0);
607     
608     return closureSet;
609               
610   }
611   
612 }
613
Popular Tags