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}