    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 */
    017
    018package org.apache.commons.jxpath.functions;
    019
    020import java.lang.reflect.InvocationTargetException;
    021import java.lang.reflect.Method;
    022import java.lang.reflect.Modifier;
    023
    024import org.apache.commons.jxpath.ExpressionContext;
    025import org.apache.commons.jxpath.Function;
    026import org.apache.commons.jxpath.JXPathInvalidAccessException;
    027import org.apache.commons.jxpath.util.TypeUtils;
    028import org.apache.commons.jxpath.util.ValueUtils;
    029
    030/**
    031 * An XPath extension function implemented as an individual Java method.
    032 */
    033public class MethodFunction implements Function {
    034
    035    private static final Object[] EMPTY_ARRAY = {};
    036    private final Method method;
    037
    038    /**
    039     * Constructs a new MethodFunction.
    040     *
    041     * @param method implementing Method
    042     */
    043    public MethodFunction(final Method method) {
    044        this.method = ValueUtils.getAccessibleMethod(method);
    045    }
    046
    047    @Override
    048    public Object invoke(final ExpressionContext context, Object[] parameters) {
    049        try {
    050            Object target;
    051            Object[] args;
    052            if (Modifier.isStatic(method.getModifiers())) {
    053                target = null;
    054                if (parameters == null) {
    055                    parameters = EMPTY_ARRAY;
    056                }
    057                int pi = 0;
    058                final Class[] types = method.getParameterTypes();
    059                if (types.length >= 1 && ExpressionContext.class.isAssignableFrom(types[0])) {
    060                    pi = 1;
    061                }
    062                args = new Object[parameters.length + pi];
    063                if (pi == 1) {
    064                    args[0] = context;
    065                }
    066                for (int i = 0; i < parameters.length; i++) {
    067                    args[i + pi] = TypeUtils.convert(parameters[i], types[i + pi]);
    068                }
    069            } else {
    070                int pi = 0;
    071                final Class[] types = method.getParameterTypes();
    072                if (types.length >= 1 && ExpressionContext.class.isAssignableFrom(types[0])) {
    073                    pi = 1;
    074                }
    075                target = TypeUtils.convert(parameters[0], method.getDeclaringClass());
    076                args = new Object[parameters.length - 1 + pi];
    077                if (pi == 1) {
    078                    args[0] = context;
    079                }
    080                for (int i = 1; i < parameters.length; i++) {
    081                    args[pi + i - 1] = TypeUtils.convert(parameters[i], types[i + pi - 1]);
    082                }
    083            }
    084            return method.invoke(target, args);
    085        } catch (Throwable ex) {
    086            if (ex instanceof InvocationTargetException) {
    087                ex = ((InvocationTargetException) ex).getTargetException();
    088            }
    089            throw new JXPathInvalidAccessException("Cannot invoke " + method, ex);
    090        }
    091    }
    092
    093    @Override
    094    public String toString() {
    095        return method.toString();
    096    }
    097}