001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: FuncExtFunction.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath.functions;
022    
023    import java.util.Vector;
024    
025    import org.apache.xalan.res.XSLMessages;
026    import org.apache.xpath.Expression;
027    import org.apache.xpath.ExpressionNode;
028    import org.apache.xpath.ExpressionOwner;
029    import org.apache.xpath.ExtensionsProvider;
030    import org.apache.xpath.XPathContext;
031    import org.apache.xpath.XPathVisitor;
032    import org.apache.xpath.objects.XNull;
033    import org.apache.xpath.objects.XObject;
034    import org.apache.xpath.res.XPATHErrorResources;
035    import org.apache.xpath.res.XPATHMessages;
036    
037    /**
038     * An object of this class represents an extension call expression.  When
039     * the expression executes, it calls ExtensionsTable#extFunction, and then
040     * converts the result to the appropriate XObject.
041     * @xsl.usage advanced
042     */
043    public class FuncExtFunction extends Function
044    {
045        static final long serialVersionUID = 5196115554693708718L;
046    
047      /**
048       * The namespace for the extension function, which should not normally
049       *  be null or empty.
050       *  @serial    
051       */
052      String m_namespace;
053    
054      /**
055       * The local name of the extension.
056       *  @serial   
057       */
058      String m_extensionName;
059    
060      /**
061       * Unique method key, which is passed to ExtensionsTable#extFunction in
062       *  order to allow caching of the method.
063       *  @serial 
064       */
065      Object m_methodKey;
066    
067      /**
068       * Array of static expressions which represent the parameters to the
069       *  function.
070       *  @serial   
071       */
072      Vector m_argVec = new Vector();
073    
074      /**
075       * This function is used to fixup variables from QNames to stack frame
076       * indexes at stylesheet build time.
077       * @param vars List of QNames that correspond to variables.  This list
078       * should be searched backwards for the first qualified name that
079       * corresponds to the variable reference qname.  The position of the
080       * QName in the vector from the start of the vector will be its position
081       * in the stack frame (but variables above the globalsTop value will need
082       * to be offset to the current stack frame).
083       * NEEDSDOC @param globalsSize
084       */
085      public void fixupVariables(java.util.Vector vars, int globalsSize)
086      {
087    
088        if (null != m_argVec)
089        {
090          int nArgs = m_argVec.size();
091    
092          for (int i = 0; i < nArgs; i++)
093          {
094            Expression arg = (Expression) m_argVec.elementAt(i);
095    
096            arg.fixupVariables(vars, globalsSize);
097          }
098        }
099      }
100      
101      /**
102       * Return the namespace of the extension function.
103       *
104       * @return The namespace of the extension function.
105       */
106      public String getNamespace()
107      {
108        return m_namespace;
109      }
110      
111      /**
112       * Return the name of the extension function.
113       *
114       * @return The name of the extension function.
115       */
116      public String getFunctionName()
117      {
118        return m_extensionName;
119      }
120      
121      /**
122       * Return the method key of the extension function.
123       *
124       * @return The method key of the extension function.
125       */
126      public Object getMethodKey()
127      {
128        return m_methodKey;
129      }
130    
131      /** 
132       * Return the nth argument passed to the extension function.
133       * 
134       * @param n The argument number index.
135       * @return The Expression object at the given index.
136       */    
137      public Expression getArg(int n) {
138        if (n >= 0 && n < m_argVec.size())
139          return (Expression) m_argVec.elementAt(n);
140        else
141          return null;
142      }
143    
144      /**
145       * Return the number of arguments that were passed
146       * into this extension function.
147       *
148       * @return The number of arguments.
149       */    
150      public int getArgCount() {
151        return m_argVec.size();
152      }
153    
154      /**
155       * Create a new FuncExtFunction based on the qualified name of the extension,
156       * and a unique method key.
157       *
158       * @param namespace The namespace for the extension function, which should
159       *                  not normally be null or empty.
160       * @param extensionName The local name of the extension.
161       * @param methodKey Unique method key, which is passed to
162       *                  ExtensionsTable#extFunction in order to allow caching
163       *                  of the method.
164       */
165      public FuncExtFunction(java.lang.String namespace,
166                             java.lang.String extensionName, Object methodKey)
167      {
168        //try{throw new Exception("FuncExtFunction() " + namespace + " " + extensionName);} catch (Exception e){e.printStackTrace();}
169        m_namespace = namespace;
170        m_extensionName = extensionName;
171        m_methodKey = methodKey;
172      }
173    
174      /**
175       * Execute the function.  The function must return
176       * a valid object.
177       * @param xctxt The current execution context.
178       * @return A valid XObject.
179       *
180       * @throws javax.xml.transform.TransformerException
181       */
182      public XObject execute(XPathContext xctxt)
183              throws javax.xml.transform.TransformerException
184      {
185        if (xctxt.isSecureProcessing())
186          throw new javax.xml.transform.TransformerException(
187            XPATHMessages.createXPATHMessage(
188              XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
189              new Object[] {toString()}));
190          
191        XObject result;
192        Vector argVec = new Vector();
193        int nArgs = m_argVec.size();
194    
195        for (int i = 0; i < nArgs; i++)
196        {
197          Expression arg = (Expression) m_argVec.elementAt(i);
198          
199          XObject xobj = arg.execute(xctxt);
200          /*
201           * Should cache the arguments for func:function
202           */
203          xobj.allowDetachToRelease(false); 
204          argVec.addElement(xobj);
205        }
206        //dml
207        ExtensionsProvider extProvider = (ExtensionsProvider)xctxt.getOwnerObject();
208        Object val = extProvider.extFunction(this, argVec);
209    
210        if (null != val)
211        {
212          result = XObject.create(val, xctxt);
213        }
214        else
215        {
216          result = new XNull();
217        }
218    
219        return result;
220      }
221    
222      /**
223       * Set an argument expression for a function.  This method is called by the
224       * XPath compiler.
225       *
226       * @param arg non-null expression that represents the argument.
227       * @param argNum The argument number index.
228       *
229       * @throws WrongNumberArgsException If the argNum parameter is beyond what
230       * is specified for this function.
231       */
232      public void setArg(Expression arg, int argNum)
233              throws WrongNumberArgsException
234      {
235        m_argVec.addElement(arg);
236        arg.exprSetParent(this);
237      }
238    
239      /**
240       * Check that the number of arguments passed to this function is correct.
241       *
242       *
243       * @param argNum The number of arguments that is being passed to the function.
244       *
245       * @throws WrongNumberArgsException
246       */
247      public void checkNumberArgs(int argNum) throws WrongNumberArgsException{}
248    
249    
250      class ArgExtOwner implements ExpressionOwner
251      {
252      
253        Expression m_exp;
254            
255            ArgExtOwner(Expression exp)
256            {
257                    m_exp = exp;
258            }
259            
260        /**
261         * @see ExpressionOwner#getExpression()
262         */
263        public Expression getExpression()
264        {
265          return m_exp;
266        }
267    
268    
269        /**
270         * @see ExpressionOwner#setExpression(Expression)
271         */
272        public void setExpression(Expression exp)
273        {
274            exp.exprSetParent(FuncExtFunction.this);
275            m_exp = exp;
276        }
277      }
278      
279      
280      /**
281       * Call the visitors for the function arguments.
282       */
283      public void callArgVisitors(XPathVisitor visitor)
284      {
285          for (int i = 0; i < m_argVec.size(); i++)
286          {
287             Expression exp = (Expression)m_argVec.elementAt(i);
288             exp.callVisitors(new ArgExtOwner(exp), visitor);
289          }
290        
291      }
292    
293      /**
294       * Set the parent node.
295       * For an extension function, we also need to set the parent
296       * node for all argument expressions.
297       * 
298       * @param n The parent node
299       */
300      public void exprSetParent(ExpressionNode n) 
301      {
302            
303        super.exprSetParent(n);
304          
305        int nArgs = m_argVec.size();
306    
307        for (int i = 0; i < nArgs; i++)
308        {
309          Expression arg = (Expression) m_argVec.elementAt(i);
310    
311          arg.exprSetParent(n);
312        }           
313      }
314    
315      /**
316       * Constructs and throws a WrongNumberArgException with the appropriate
317       * message for this function object.  This class supports an arbitrary
318       * number of arguments, so this method must never be called.
319       *
320       * @throws WrongNumberArgsException
321       */
322      protected void reportWrongNumberArgs() throws WrongNumberArgsException {
323        String fMsg = XSLMessages.createXPATHMessage(
324            XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
325            new Object[]{ "Programmer's assertion:  the method FunctionMultiArgs.reportWrongNumberArgs() should never be called." });
326    
327        throw new RuntimeException(fMsg);
328      }
329      
330      /**
331       * Return the name of the extesion function in string format
332       */
333      public String toString()
334      {
335        if (m_namespace != null && m_namespace.length() > 0)
336          return "{" + m_namespace + "}" + m_extensionName;
337        else
338          return m_extensionName;
339      }  
340    }