    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;
    019
    020import java.lang.reflect.Constructor;
    021import java.lang.reflect.Method;
    022import java.util.Collections;
    023import java.util.Set;
    024
    025import org.apache.commons.jxpath.functions.ConstructorFunction;
    026import org.apache.commons.jxpath.functions.MethodFunction;
    027import org.apache.commons.jxpath.util.MethodLookupUtils;
    028
    029/**
    030 * Extension functions provided by a Java class.
    031 *
    032 * Let's say we declared a ClassFunction like this: <blockquote>
    033 *
    034 * <pre>
    035 * new ClassFunctions(Integer.class, "int")
    036 * </pre>
    037 *
    038 * </blockquote>
    039 *
    040 * We can now use XPaths like:
    041 * <dl>
    042 * <dt>{@code "int:new(3)"}</dt>
    043 * <dd>Equivalent to {@code Integer.valueOf(3)}</dd>
    044 * <dt>{@code "int:getInteger('foo')"}</dt>
    045 * <dd>Equivalent to {@code Integer.getInteger("foo")}</dd>
    046 * <dt>{@code "int:floatValue(int:new(4))"}</dt>
    047 * <dd>Equivalent to {@code Integer.valueOf(4).floatValue()}</dd>
    048 * </dl>
    049 *
    050 * <p>
    051 * If the first argument of a method is {@link ExpressionContext}, the expression context in which the function is evaluated is passed to the method.
    052 */
    053public class ClassFunctions implements Functions {
    054
    055    private static final Object[] EMPTY_ARRAY = {};
    056    private final Class functionClass;
    057    private final String namespace;
    058
    059    /**
    060     * Constructs a new ClassFunctions.
    061     *
    062     * @param functionClass Class providing the functions
    063     * @param namespace     assigned ns
    064     */
    065    public ClassFunctions(final Class functionClass, final String namespace) {
    066        this.functionClass = functionClass;
    067        this.namespace = namespace;
    068    }
    069
    070    /**
    071     * Returns a {@link Function}, if any, for the specified namespace, name and parameter types.
    072     *
    073     * @param namespace  if it is not the namespace specified in the constructor, the method returns null
    074     * @param name       is a function name or "new" for a constructor.
    075     * @param parameters Object[] of parameters
    076     * @return a MethodFunction, a ConstructorFunction or null if there is no such function.
    077     */
    078    @Override
    079    public Function getFunction(final String namespace, final String name, Object[] parameters) {
    080        if (namespace == null) {
    081            if (this.namespace != null) {
    082                return null;
    083            }
    084        } else if (!namespace.equals(this.namespace)) {
    085            return null;
    086        }
    087        if (parameters == null) {
    088            parameters = EMPTY_ARRAY;
    089        }
    090        if (name.equals("new")) {
    091            final Constructor constructor = MethodLookupUtils.lookupConstructor(functionClass, parameters);
    092            if (constructor != null) {
    093                return new ConstructorFunction(constructor);
    094            }
    095        } else {
    096            Method method = MethodLookupUtils.lookupStaticMethod(functionClass, name, parameters);
    097            if (method != null) {
    098                return new MethodFunction(method);
    099            }
    100            method = MethodLookupUtils.lookupMethod(functionClass, name, parameters);
    101            if (method != null) {
    102                return new MethodFunction(method);
    103            }
    104        }
    105        return null;
    106    }
    107
    108    /**
    109     * Returns a set of one namespace - the one specified in the constructor.
    110     *
    111     * @return a singleton
    112     */
    113    @Override
    114    public Set<String> getUsedNamespaces() {
    115        return Collections.singleton(namespace);
    116    }
    117}