1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xpath.internal.functions;
  22 
  23 import com.sun.org.apache.xalan.internal.res.XSLMessages;
  24 import com.sun.org.apache.xml.internal.utils.QName;
  25 import com.sun.org.apache.xpath.internal.Expression;
  26 import com.sun.org.apache.xpath.internal.ExpressionNode;
  27 import com.sun.org.apache.xpath.internal.ExpressionOwner;
  28 import com.sun.org.apache.xpath.internal.ExtensionsProvider;
  29 import com.sun.org.apache.xpath.internal.XPathContext;
  30 import com.sun.org.apache.xpath.internal.XPathVisitor;
  31 import com.sun.org.apache.xpath.internal.objects.XNull;
  32 import com.sun.org.apache.xpath.internal.objects.XObject;
  33 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  34 import com.sun.org.apache.xpath.internal.res.XPATHMessages;
  35 import java.util.ArrayList;
  36 import java.util.List;
  37 
  38 /**
  39  * An object of this class represents an extension call expression.  When
  40  * the expression executes, it calls ExtensionsTable#extFunction, and then
  41  * converts the result to the appropriate XObject.
  42  * @xsl.usage advanced
  43  * @LastModified: Oct 2017
  44  */
  45 public class FuncExtFunction extends Function
  46 {
  47     static final long serialVersionUID = 5196115554693708718L;
  48 
  49   /**
  50    * The namespace for the extension function, which should not normally
  51    *  be null or empty.
  52    *  @serial
  53    */
  54   String m_namespace;
  55 
  56   /**
  57    * The local name of the extension.
  58    *  @serial
  59    */
  60   String m_extensionName;
  61 
  62   /**
  63    * Unique method key, which is passed to ExtensionsTable#extFunction in
  64    *  order to allow caching of the method.
  65    *  @serial
  66    */
  67   Object m_methodKey;
  68 
  69   /**
  70    * Array of static expressions which represent the parameters to the
  71    *  function.
  72    *  @serial
  73    */
  74   List<Expression> m_argVec = new ArrayList<>();
  75 
  76   /**
  77    * This function is used to fixup variables from QNames to stack frame
  78    * indexes at stylesheet build time.
  79    * @param vars List of QNames that correspond to variables.  This list
  80    * should be searched backwards for the first qualified name that
  81    * corresponds to the variable reference qname.  The position of the
  82    * QName in the vector from the start of the vector will be its position
  83    * in the stack frame (but variables above the globalsTop value will need
  84    * to be offset to the current stack frame).
  85    * NEEDSDOC @param globalsSize
  86    */
  87   public void fixupVariables(List<QName> vars, int globalsSize)
  88   {
  89 
  90     if (null != m_argVec)
  91     {
  92       int nArgs = m_argVec.size();
  93 
  94       for (int i = 0; i < nArgs; i++)
  95       {
  96         Expression arg = m_argVec.get(i);
  97 
  98         arg.fixupVariables(vars, globalsSize);
  99       }
 100     }
 101   }
 102 
 103   /**
 104    * Return the namespace of the extension function.
 105    *
 106    * @return The namespace of the extension function.
 107    */
 108   public String getNamespace()
 109   {
 110     return m_namespace;
 111   }
 112 
 113   /**
 114    * Return the name of the extension function.
 115    *
 116    * @return The name of the extension function.
 117    */
 118   public String getFunctionName()
 119   {
 120     return m_extensionName;
 121   }
 122 
 123   /**
 124    * Return the method key of the extension function.
 125    *
 126    * @return The method key of the extension function.
 127    */
 128   public Object getMethodKey()
 129   {
 130     return m_methodKey;
 131   }
 132 
 133   /**
 134    * Return the nth argument passed to the extension function.
 135    *
 136    * @param n The argument number index.
 137    * @return The Expression object at the given index.
 138    */
 139   public Expression getArg(int n) {
 140     if (n >= 0 && n < m_argVec.size())
 141       return m_argVec.get(n);
 142     else
 143       return null;
 144   }
 145 
 146   /**
 147    * Return the number of arguments that were passed
 148    * into this extension function.
 149    *
 150    * @return The number of arguments.
 151    */
 152   public int getArgCount() {
 153     return m_argVec.size();
 154   }
 155 
 156   /**
 157    * Create a new FuncExtFunction based on the qualified name of the extension,
 158    * and a unique method key.
 159    *
 160    * @param namespace The namespace for the extension function, which should
 161    *                  not normally be null or empty.
 162    * @param extensionName The local name of the extension.
 163    * @param methodKey Unique method key, which is passed to
 164    *                  ExtensionsTable#extFunction in order to allow caching
 165    *                  of the method.
 166    */
 167   public FuncExtFunction(java.lang.String namespace,
 168                          java.lang.String extensionName, Object methodKey)
 169   {
 170     //try{throw new Exception("FuncExtFunction() " + namespace + " " + extensionName);} catch (Exception e){e.printStackTrace();}
 171     m_namespace = namespace;
 172     m_extensionName = extensionName;
 173     m_methodKey = methodKey;
 174   }
 175 
 176   /**
 177    * Execute the function.  The function must return
 178    * a valid object.
 179    * @param xctxt The current execution context.
 180    * @return A valid XObject.
 181    *
 182    * @throws javax.xml.transform.TransformerException
 183    */
 184   public XObject execute(XPathContext xctxt)
 185           throws javax.xml.transform.TransformerException
 186   {
 187     if (xctxt.isSecureProcessing())
 188       throw new javax.xml.transform.TransformerException(
 189         XPATHMessages.createXPATHMessage(
 190           XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
 191           new Object[] {toString()}));
 192 
 193     XObject result;
 194     List<XObject> argVec = new ArrayList<>();
 195     int nArgs = m_argVec.size();
 196 
 197     for (int i = 0; i < nArgs; i++)
 198     {
 199       Expression arg = m_argVec.get(i);
 200 
 201       XObject xobj = arg.execute(xctxt);
 202       /*
 203        * Should cache the arguments for func:function
 204        */
 205       xobj.allowDetachToRelease(false);
 206       argVec.add(xobj);
 207     }
 208     //dml
 209     ExtensionsProvider extProvider = (ExtensionsProvider)xctxt.getOwnerObject();
 210     Object val = extProvider.extFunction(this, argVec);
 211 
 212     if (null != val)
 213     {
 214       result = XObject.create(val, xctxt);
 215     }
 216     else
 217     {
 218       result = new XNull();
 219     }
 220 
 221     return result;
 222   }
 223 
 224   /**
 225    * Set an argument expression for a function.  This method is called by the
 226    * XPath compiler.
 227    *
 228    * @param arg non-null expression that represents the argument.
 229    * @param argNum The argument number index.
 230    *
 231    * @throws WrongNumberArgsException If the argNum parameter is beyond what
 232    * is specified for this function.
 233    */
 234   public void setArg(Expression arg, int argNum)
 235           throws WrongNumberArgsException
 236   {
 237     m_argVec.add(arg);
 238     arg.exprSetParent(this);
 239   }
 240 
 241   /**
 242    * Check that the number of arguments passed to this function is correct.
 243    *
 244    *
 245    * @param argNum The number of arguments that is being passed to the function.
 246    *
 247    * @throws WrongNumberArgsException
 248    */
 249   public void checkNumberArgs(int argNum) throws WrongNumberArgsException{}
 250 
 251 
 252   class ArgExtOwner implements ExpressionOwner
 253   {
 254 
 255     Expression m_exp;
 256 
 257         ArgExtOwner(Expression exp)
 258         {
 259                 m_exp = exp;
 260         }
 261 
 262     /**
 263      * @see ExpressionOwner#getExpression()
 264      */
 265     public Expression getExpression()
 266     {
 267       return m_exp;
 268     }
 269 
 270 
 271     /**
 272      * @see ExpressionOwner#setExpression(Expression)
 273      */
 274     public void setExpression(Expression exp)
 275     {
 276         exp.exprSetParent(FuncExtFunction.this);
 277         m_exp = exp;
 278     }
 279   }
 280 
 281 
 282   /**
 283    * Call the visitors for the function arguments.
 284    */
 285   public void callArgVisitors(XPathVisitor visitor)
 286   {
 287       for (int i = 0; i < m_argVec.size(); i++)
 288       {
 289          Expression exp = m_argVec.get(i);
 290          exp.callVisitors(new ArgExtOwner(exp), visitor);
 291       }
 292 
 293   }
 294 
 295   /**
 296    * Set the parent node.
 297    * For an extension function, we also need to set the parent
 298    * node for all argument expressions.
 299    *
 300    * @param n The parent node
 301    */
 302   public void exprSetParent(ExpressionNode n)
 303   {
 304 
 305     super.exprSetParent(n);
 306 
 307     int nArgs = m_argVec.size();
 308 
 309     for (int i = 0; i < nArgs; i++)
 310     {
 311       Expression arg = m_argVec.get(i);
 312 
 313       arg.exprSetParent(n);
 314     }
 315   }
 316 
 317   /**
 318    * Constructs and throws a WrongNumberArgException with the appropriate
 319    * message for this function object.  This class supports an arbitrary
 320    * number of arguments, so this method must never be called.
 321    *
 322    * @throws WrongNumberArgsException
 323    */
 324   protected void reportWrongNumberArgs() throws WrongNumberArgsException {
 325     String fMsg = XSLMessages.createXPATHMessage(
 326         XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
 327         new Object[]{ "Programmer's assertion:  the method FunctionMultiArgs.reportWrongNumberArgs() should never be called." });
 328 
 329     throw new RuntimeException(fMsg);
 330   }
 331 
 332   /**
 333    * Return the name of the extesion function in string format
 334    */
 335   public String toString()
 336   {
 337     if (m_namespace != null && m_namespace.length() > 0)
 338       return "{" + m_namespace + "}" + m_extensionName;
 339     else
 340       return m_extensionName;
 341   }
 342 }