mkwan       2002/09/06 09:50:56

  Added:       java/src/org/apache/xalan/lib ExsltDynamic.java
  Log:
  Add the ExsltDynamic class which implements the EXSLT dynamic
  extension functions (max, min, sum, map, evaluate and closure).
  
  The implementation of evaluate is taken from Extensions.java.
  The Javadoc is copied from the relevant EXSLT page.
  
  Revision  Changes    Path
  1.1                  xml-xalan/java/src/org/apache/xalan/lib/ExsltDynamic.java
  
  Index: ExsltDynamic.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.xalan.lib;
  
  import org.w3c.dom.*;
  
  import org.apache.xpath.objects.XObject;
  import org.apache.xpath.objects.XBoolean;
  import org.apache.xpath.objects.XNumber;
  import org.apache.xpath.objects.XNodeSet;
  
  import org.apache.xpath.XPath;
  import org.apache.xpath.XPathContext;
  import org.apache.xpath.NodeSet;
  import org.apache.xpath.NodeSetDTM;
  
  import org.xml.sax.SAXNotSupportedException;
  
  import org.apache.xalan.extensions.ExpressionContext;
  import org.apache.xalan.res.XSLMessages;
  import org.apache.xalan.res.XSLTErrorResources;
  
  import javax.xml.transform.*;
  import javax.xml.parsers.*;
  
  /**
   * <meta name="usage" content="general"/>
   * This class contains EXSLT dynamic extension functions.
   *
   * It is accessed by specifying a namespace URI as follows:
   * <pre>
   *    xmlns:math="http://exslt.org/dynamic";
   * </pre>
   * 
   * The documentation for each function has been copied from the relevant
   * EXSLT Implementer page.
   * 
   * @see <a href="http://www.exslt.org/";>EXSLT</a>
  
   */
  public class ExsltDynamic
  {
  
     public static final String EXSL_URI = "http://exslt.org/common";;
     
    /**
     * The dyn:max function calculates the maximum value for the nodes passed 
as 
     * the first argument, where the value of each node is calculated 
dynamically 
     * using an XPath expression passed as a string as the second argument. 
     *
     * The expressions are evaluated relative to the nodes passed as the first 
argument.
     * In other words, the value for each node is calculated by evaluating the 
XPath 
     * expression with all context information being the same as that for the 
call to 
     * the dyn:max function itself, except for the following: 
     *
     * - the context node is the node whose value is being calculated
     * - the context position is the position of the node within the node set 
passed as 
     *   the first argument to the dyn:max function, arranged in document order
     * - the context size is the number of nodes passed as the first argument 
to the 
     *   dyn:max function
     *
     * The dyn:max function returns the maximum of these values, calculated in 
exactly 
     * the same way as for math:max. 
     *
     * If the expression string passed as the second argument is an invalid 
XPath 
     * expression (including an empty string), this function returns NaN. 
     *
     * This function must take a second argument. To calculate the maximum of a 
set of 
     * nodes based on their string values, you should use the math:max function.
     */
    public static double max(ExpressionContext myContext, NodeList nl, String 
expr)
      throws SAXNotSupportedException
    {
  
      XPathContext xctxt = null;
      if (myContext instanceof XPathContext.XPathExpressionContext)
        xctxt = ((XPathContext.XPathExpressionContext) 
myContext).getXPathContext();
      else
        throw new 
SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED,
 new Object[]{myContext }));
  
      if (expr == null || expr.length() == 0)
        return Double.NaN;
        
      NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
      xctxt.pushContextNodeList(contextNodes);
      
      double maxValue = Double.MIN_VALUE;
      for (int i = 0; i < contextNodes.getLength(); i++)
      {
        int contextNode = contextNodes.item(i);
        xctxt.pushCurrentNode(contextNode);
        
        double result = 0;
        try
        {
          XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
                                         xctxt.getNamespaceContext(),
                                         XPath.SELECT);
          result = dynamicXPath.execute(xctxt, contextNode, 
xctxt.getNamespaceContext()).num();
        }
        catch (TransformerException e)
        {
          xctxt.popCurrentNode();
          xctxt.popContextNodeList();
          return Double.NaN;
        }
        
        xctxt.popCurrentNode();
                
        if (result > maxValue)
            maxValue = result;
      }      
        
      xctxt.popContextNodeList();
      return maxValue;
          
    }
    
    /**
     * The dyn:min function calculates the minimum value for the nodes passed 
as the 
     * first argument, where the value of each node is calculated dynamically 
using 
     * an XPath expression passed as a string as the second argument. 
     *
     * The expressions are evaluated relative to the nodes passed as the first 
argument. 
     * In other words, the value for each node is calculated by evaluating the 
XPath 
     * expression with all context information being the same as that for the 
call to 
     * the dyn:min function itself, except for the following: 
     *
     *  - the context node is the node whose value is being calculated
     *  - the context position is the position of the node within the node set 
passed 
     *    as the first argument to the dyn:min function, arranged in document 
order
     *  - the context size is the number of nodes passed as the first argument 
to the 
     *    dyn:min function
     *
     * The dyn:min function returns the minimum of these values, calculated in 
exactly 
     * the same way as for math:min. 
     *
     * If the expression string passed as the second argument is an invalid 
XPath expression 
     * (including an empty string), this function returns NaN. 
     *
     * This function must take a second argument. To calculate the minimum of a 
set of 
     * nodes based on their string values, you should use the math:min function.
     */
    public static double min(ExpressionContext myContext, NodeList nl, String 
expr)
      throws SAXNotSupportedException
    {
      
      XPathContext xctxt = null;
      if (myContext instanceof XPathContext.XPathExpressionContext)
        xctxt = ((XPathContext.XPathExpressionContext) 
myContext).getXPathContext();
      else
        throw new 
SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED,
 new Object[]{myContext }));
  
      if (expr == null || expr.length() == 0)
        return Double.NaN;
        
      NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
      xctxt.pushContextNodeList(contextNodes);
      
      double minValue = Double.MAX_VALUE;
      for (int i = 0; i < nl.getLength(); i++)
      {
        int contextNode = contextNodes.item(i);
        xctxt.pushCurrentNode(contextNode);
        
        double result = 0;
        try
        {
          XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
                                         xctxt.getNamespaceContext(),
                                         XPath.SELECT);
          result = dynamicXPath.execute(xctxt, contextNode, 
xctxt.getNamespaceContext()).num();
        }
        catch (TransformerException e)
        {
          xctxt.popCurrentNode();
          xctxt.popContextNodeList();
          return Double.NaN;
        }
        
        xctxt.popCurrentNode();
                
        if (result < minValue)
            minValue = result;
      }      
        
      xctxt.popContextNodeList();
      return minValue;
    
    }
  
    /**
     * The dyn:sum function calculates the sum for the nodes passed as the 
first argument, 
     * where the value of each node is calculated dynamically using an XPath 
expression 
     * passed as a string as the second argument. 
     *
     * The expressions are evaluated relative to the nodes passed as the first 
argument. 
     * In other words, the value for each node is calculated by evaluating the 
XPath 
     * expression with all context information being the same as that for the 
call to 
     * the dyn:sum function itself, except for the following: 
     *
     *  - the context node is the node whose value is being calculated
     *  - the context position is the position of the node within the node set 
passed as 
     *    the first argument to the dyn:sum function, arranged in document order
     *  - the context size is the number of nodes passed as the first argument 
to the 
     *    dyn:sum function
     *
     * The dyn:sum function returns the sumimum of these values, calculated in 
exactly 
     * the same way as for sum. 
     *
     * If the expression string passed as the second argument is an invalid 
XPath 
     * expression (including an empty string), this function returns NaN. 
     *
     * This function must take a second argument. To calculate the sumimum of a 
set of 
     * nodes based on their string values, you should use the sum function.
     */
    public static double sum(ExpressionContext myContext, NodeList nl, String 
expr)
      throws SAXNotSupportedException
    {
      XPathContext xctxt = null;
      if (myContext instanceof XPathContext.XPathExpressionContext)
        xctxt = ((XPathContext.XPathExpressionContext) 
myContext).getXPathContext();
      else
        throw new 
SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED,
 new Object[]{myContext }));
  
      if (expr == null || expr.length() == 0)
        return Double.NaN;
        
      NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
      xctxt.pushContextNodeList(contextNodes);
      
      double sum = 0;
      for (int i = 0; i < nl.getLength(); i++)
      {
        int contextNode = contextNodes.item(i);
        xctxt.pushCurrentNode(contextNode);
        
        double result = 0;
        try
        {
          XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
                                         xctxt.getNamespaceContext(),
                                         XPath.SELECT);
          result = dynamicXPath.execute(xctxt, contextNode, 
xctxt.getNamespaceContext()).num();
        }
        catch (TransformerException e)
        {
          xctxt.popCurrentNode();
          xctxt.popContextNodeList();
          return Double.NaN;
        }
        
        xctxt.popCurrentNode();
        
        sum = sum + result;
                
      }      
        
      xctxt.popContextNodeList();
      return sum;
    }
  
    /**
     * The dyn:map function evaluates the expression passed as the second 
argument for 
     * each of the nodes passed as the first argument, and returns a node set 
of those values. 
     *
     * The expressions are evaluated relative to the nodes passed as the first 
argument. 
     * In other words, the value for each node is calculated by evaluating the 
XPath 
     * expression with all context information being the same as that for the 
call to 
     * the dyn:map function itself, except for the following: 
     *
     *  - the context node is the node whose value is being calculated
     *  - the context position is the position of the node within the node set 
passed 
     *    as the first argument to the dyn:map function, arranged in document 
order
     *  - the context size is the number of nodes passed as the first argument 
to the 
     *    dyn:map function
     * 
     * If the expression string passed as the second argument is an invalid 
XPath 
     * expression (including an empty string), this function returns an empty 
node set. 
     *
     * If the XPath expression evaluates as a node set, the dyn:map function 
returns 
     * the union of the node sets returned by evaluating the expression for 
each of the 
     * nodes in the first argument. Note that this may mean that the node set 
resulting 
     * from the call to the dyn:map function contains a different number of 
nodes from 
     * the number in the node set passed as the first argument to the function. 
     *
     * If the XPath expression evaluates as a number, the dyn:map function 
returns a 
     * node set containing one exsl:number element (namespace 
http://exslt.org/common) 
     * for each node in the node set passed as the first argument to the 
dyn:map function, 
     * in document order. The string value of each exsl:number element is the 
same as 
     * the result of converting the number resulting from evaluating the 
expression to 
     * a string as with the number function, with the exception that Infinity 
results 
     * in an exsl:number holding the highest number the implementation can 
store, and 
     * -Infinity results in an exsl:number holding the lowest number the 
implementation 
     * can store. 
     *
     * If the XPath expression evaluates as a boolean, the dyn:map function 
returns a 
     * node set containing one exsl:boolean element (namespace 
http://exslt.org/common) 
     * for each node in the node set passed as the first argument to the 
dyn:map function, 
     * in document order. The string value of each exsl:boolean element is 
'true' if the 
     * expression evaluates as true for the node, and '' if the expression 
evaluates as 
     * false. 
     *
     * Otherwise, the dyn:map function returns a node set containing one 
exsl:string 
     * element (namespace http://exslt.org/common) for each node in the node 
set passed 
     * as the first argument to the dyn:map function, in document order. The 
string 
     * value of each exsl:string element is the same as the result of 
converting the 
     * result of evaluating the expression for the relevant node to a string as 
with 
     * the string function. 
     */
    public static NodeList map(ExpressionContext myContext, NodeList nl, String 
expr)
      throws SAXNotSupportedException
    {
      XPathContext xctxt = null;
      if (myContext instanceof XPathContext.XPathExpressionContext)
        xctxt = ((XPathContext.XPathExpressionContext) 
myContext).getXPathContext();
      else
        throw new 
SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED,
 new Object[]{myContext }));
  
      if (expr == null || expr.length() == 0)
        return new NodeSet();
        
      NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
      xctxt.pushContextNodeList(contextNodes);
      
      NodeSet resultSet = new NodeSet();
      resultSet.setShouldCacheNodes(true);
      
      for (int i = 0; i < nl.getLength(); i++)
      {
        int contextNode = contextNodes.item(i);
        xctxt.pushCurrentNode(contextNode);
        
        XObject object = null;
        try
        {
          XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
                                         xctxt.getNamespaceContext(),
                                         XPath.SELECT);
          object = dynamicXPath.execute(xctxt, contextNode, 
xctxt.getNamespaceContext());
          
          if (object instanceof XNodeSet)
          {
            NodeList nodelist = null;
            nodelist = ((XNodeSet)object).nodelist();
          
            for (int k = 0; k < nodelist.getLength(); k++)
            {
              Node n = nodelist.item(k);
              if (!resultSet.contains(n))
                resultSet.addNode(n);
            }
          }
          else
          {
            Document lDoc = null;
  
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            lDoc = db.newDocument();
          
            Element element = null;
            if (object instanceof XNumber)
              element = lDoc.createElementNS(EXSL_URI, "exsl:number");
            else if (object instanceof XBoolean)
              element = lDoc.createElementNS(EXSL_URI, "exsl:boolean");
            else
              element = lDoc.createElementNS(EXSL_URI, "exsl:string");
            
            Text textNode = lDoc.createTextNode(object.str());
            element.appendChild(textNode);
            resultSet.addNode(element);
          }
        }
        catch (Exception e)
        {
          xctxt.popCurrentNode();
          xctxt.popContextNodeList();
          return new NodeSet();
        }
        
        xctxt.popCurrentNode();
        
      }      
        
      xctxt.popContextNodeList();
      return resultSet;
    }
  
    /**
     * The dyn:evaluate function evaluates a string as an XPath expression and 
returns 
     * the resulting value, which might be a boolean, number, string, node set, 
result 
     * tree fragment or external object. The sole argument is the string to be 
evaluated.
     * 
     * If the expression string passed as the second argument is an invalid 
XPath 
     * expression (including an empty string), this function returns an empty 
node set. 
     *
     * You should only use this function if the expression must be constructed 
dynamically 
     * - otherwise it is much more efficient to use the expression literally. 
     */
    public static XObject evaluate(ExpressionContext myContext, String 
xpathExpr)
      throws SAXNotSupportedException
    {
      if (myContext instanceof XPathContext.XPathExpressionContext)
      {
        XPathContext xctxt = null;
        try
        {
          xctxt = ((XPathContext.XPathExpressionContext) 
myContext).getXPathContext();
          XPath dynamicXPath = new XPath(xpathExpr, xctxt.getSAXLocator(),
                                         xctxt.getNamespaceContext(),
                                         XPath.SELECT);
  
          return dynamicXPath.execute(xctxt, myContext.getContextNode(),
                                      xctxt.getNamespaceContext());
        }
        catch (TransformerException e)
        {
          return new XNodeSet(xctxt.getDTMManager());
        }
      }
      else
        throw new 
SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED,
 new Object[]{myContext })); //"Invalid context passed to evaluate "
    }
  
    /**
     * The dyn:closure function creates a node set resulting from transitive 
closure of 
     * evaluating the expression passed as the second argument on each of the 
nodes passed 
     * as the first argument, then on the node set resulting from that and so 
on until no 
     * more nodes are found. For example: 
     *
     *  dyn:closure(., '*')
     *
     * returns all the descendant elements of the node (its element children, 
their 
     * children, their children's children and so on). 
     *
     * The expression is thus evaluated several times, each with a different 
node set 
     * acting as the context of the expression. The first time the expression 
is 
     * evaluated, the context node set is the first argument passed to the 
dyn:closure 
     * function. In other words, the node set for each node is calculated by 
evaluating 
     * the XPath expression with all context information being the same as that 
for 
     * the call to the dyn:closure function itself, except for the following: 
     *
     *  - the context node is the node whose value is being calculated
     *  - the context position is the position of the node within the node set 
passed 
     *    as the first argument to the dyn:closure function, arranged in 
document order
     *  - the context size is the number of nodes passed as the first argument 
to the 
     *    dyn:closure function
     *  - the current node is the node whose value is being calculated
     *
     * The result for a particular iteration is the union of the node sets 
resulting 
     * from evaluting the expression for each of the nodes in the source node 
set for 
     * that iteration. This result is then used as the source node set for the 
next 
     * iteration, and so on. The result of the function as a whole is the union 
of 
     * the node sets generated by each iteration. 
     *
     * If the expression string passed as the second argument is an invalid 
XPath 
     * expression (including an empty string) or an expression that does not 
return a 
     * node set, this function returns an empty node set. 
     */
    public static NodeList closure(ExpressionContext myContext, NodeList nl, 
String expr)
      throws SAXNotSupportedException
    {
      XPathContext xctxt = null;
      if (myContext instanceof XPathContext.XPathExpressionContext)
        xctxt = ((XPathContext.XPathExpressionContext) 
myContext).getXPathContext();
      else
        throw new 
SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED,
 new Object[]{myContext }));
  
      if (expr == null || expr.length() == 0)
        return new NodeSet();
            
      NodeSet closureSet = new NodeSet();
      closureSet.setShouldCacheNodes(true);
          
      NodeList iterationList = nl;
      do
      {
      
        NodeSet iterationSet = new NodeSet();
  
        NodeSetDTM contextNodes = new NodeSetDTM(iterationList, xctxt);
        xctxt.pushContextNodeList(contextNodes);
        
        for (int i = 0; i < iterationList.getLength(); i++)
        {
          int contextNode = contextNodes.item(i);
          xctxt.pushCurrentNode(contextNode);
  
          XObject object = null;
          try
          {
            XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
                                           xctxt.getNamespaceContext(),
                                           XPath.SELECT);
            object = dynamicXPath.execute(xctxt, contextNode, 
xctxt.getNamespaceContext());
            
            if (object instanceof XNodeSet)
            {
              NodeList nodelist = null;
              nodelist = ((XNodeSet)object).nodelist();
          
              for (int k = 0; k < nodelist.getLength(); k++)
              {
                Node n = nodelist.item(k);
                if (!iterationSet.contains(n))
                  iterationSet.addNode(n);
              }        
            }
            else
            {
              xctxt.popCurrentNode();
              xctxt.popContextNodeList();
              return new NodeSet();
            }          
          }
          catch (TransformerException e)
          {
            xctxt.popCurrentNode();
            xctxt.popContextNodeList();
            return new NodeSet();
          }
        
          xctxt.popCurrentNode();
              
        }
        
        xctxt.popContextNodeList();
        
        iterationList = iterationSet;
        
        for (int i = 0; i < iterationList.getLength(); i++)
        {
          Node n = iterationList.item(i);
          if (!closureSet.contains(n))
            closureSet.addNode(n);
        }
        
      } while(iterationList.getLength() > 0);
      
      return closureSet;
                
    }
    
  }
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to