001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.jxpath.ri.compiler;
018
019import java.util.Arrays;
020
021import org.apache.commons.jxpath.Function;
022import org.apache.commons.jxpath.JXPathFunctionNotFoundException;
023import org.apache.commons.jxpath.NodeSet;
024import org.apache.commons.jxpath.ri.EvalContext;
025import org.apache.commons.jxpath.ri.QName;
026import org.apache.commons.jxpath.ri.axes.NodeSetContext;
027
028/**
029 * Represents an element of the parse tree representing an extension function
030 * call.
031 *
032 * @author Dmitri Plotnikov
033 * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
034 */
035public class ExtensionFunction extends Operation {
036
037    private QName functionName;
038
039    /**
040     * Create a new ExtensionFunction.
041     * @param functionName name of the function
042     * @param args Expression[] of function args
043     */
044    public ExtensionFunction(QName functionName, Expression[] args) {
045        super(args);
046        this.functionName = functionName;
047    }
048
049    /**
050     * Get the function name
051     * @return QName
052     */
053    public QName getFunctionName() {
054        return functionName;
055    }
056
057    /**
058     * An extension function gets the current context, therefore it MAY be
059     * context dependent.
060     * @return true
061     */
062    public boolean computeContextDependent() {
063        return true;
064    }
065
066    public String toString() {
067        StringBuffer buffer = new StringBuffer();
068        buffer.append(functionName);
069        buffer.append('(');
070        Expression[] args = getArguments();
071        if (args != null) {
072            for (int i = 0; i < args.length; i++) {
073                if (i > 0) {
074                    buffer.append(", ");
075                }
076                buffer.append(args[i]);
077            }
078        }
079        buffer.append(')');
080        return buffer.toString();
081    }
082
083    public Object compute(EvalContext context) {
084        return computeValue(context);
085    }
086
087    public Object computeValue(EvalContext context) {
088        Object[] parameters = null;
089        if (args != null) {
090            parameters = new Object[args.length];
091            for (int i = 0; i < args.length; i++) {
092                parameters[i] = convert(args[i].compute(context));
093            }
094        }
095
096        Function function =
097            context.getRootContext().getFunction(functionName, parameters);
098        if (function == null) {
099            throw new JXPathFunctionNotFoundException("No such function: "
100                    + functionName + Arrays.asList(parameters));
101        }
102        Object result = function.invoke(context, parameters);
103        return result instanceof NodeSet ? new NodeSetContext(context,
104                (NodeSet) result) : result;
105    }
106
107    /**
108     * Convert any incoming context to a value.
109     * @param object Object to convert
110     * @return context value or <code>object</code> unscathed.
111     */
112    private Object convert(Object object) {
113        return object instanceof EvalContext ? ((EvalContext) object).getValue() : object;
114    }
115}