    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.util.ArrayList;
    021import java.util.HashMap;
    022import java.util.List;
    023import java.util.Map;
    024import java.util.Set;
    025
    026/**
    027 * An object that aggregates {@link Functions} objects into a group Functions object. Since {@link JXPathContext} can only register a single Functions object,
    028 * FunctionLibrary should always be used to group all Functions objects that need to be registered.
    029 */
    030public class FunctionLibrary implements Functions {
    031
    032    private final List<Functions> allFunctions = new ArrayList<>();
    033    private Map<String, Object> byNamespace;
    034
    035    /**
    036     * Constructs a new instance.
    037     */
    038    public FunctionLibrary() {
    039        // empty
    040    }
    041
    042    /**
    043     * Add functions to the library
    044     *
    045     * @param functions to add
    046     */
    047    public void addFunctions(final Functions functions) {
    048        allFunctions.add(functions);
    049        synchronized (this) {
    050            byNamespace = null;
    051        }
    052    }
    053
    054    /**
    055     * Prepare the cache.
    056     *
    057     * @return cache map keyed by namespace
    058     */
    059    private synchronized Map<String, Object> functionCache() {
    060        if (byNamespace == null) {
    061            byNamespace = new HashMap<>();
    062            final int count = allFunctions.size();
    063            for (int i = 0; i < count; i++) {
    064                final Functions funcs = allFunctions.get(i);
    065                final Set<String> namespaces = funcs.getUsedNamespaces();
    066                for (final String ns : namespaces) {
    067                    final Object candidates = byNamespace.get(ns);
    068                    if (candidates == null) {
    069                        byNamespace.put(ns, funcs);
    070                    } else if (candidates instanceof Functions) {
    071                        final List<Object> lst = new ArrayList<>();
    072                        lst.add(candidates);
    073                        lst.add(funcs);
    074                        byNamespace.put(ns, lst);
    075                    } else {
    076                        ((List) candidates).add(funcs);
    077                    }
    078                }
    079            }
    080        }
    081        return byNamespace;
    082    }
    083
    084    /**
    085     * Gets a Function, if any, for the specified namespace, name and parameter types.
    086     *
    087     * @param namespace  function namespace
    088     * @param name       function name
    089     * @param parameters parameters
    090     * @return Function found
    091     */
    092    @Override
    093    public Function getFunction(final String namespace, final String name, final Object[] parameters) {
    094        final Object candidates = functionCache().get(namespace);
    095        if (candidates instanceof Functions) {
    096            return ((Functions) candidates).getFunction(namespace, name, parameters);
    097        }
    098        if (candidates instanceof List) {
    099            final List<Functions> list = (List<Functions>) candidates;
    100            final int count = list.size();
    101            for (int i = 0; i < count; i++) {
    102                final Function function = list.get(i).getFunction(namespace, name, parameters);
    103                if (function != null) {
    104                    return function;
    105                }
    106            }
    107        }
    108        return null;
    109    }
    110
    111    /**
    112     * Gets a set containing all namespaces used by the aggregated Functions.
    113     *
    114     * @return a set containing all namespaces used by the aggregated Functions.
    115     */
    116    @Override
    117    public Set<String> getUsedNamespaces() {
    118        return functionCache().keySet();
    119    }
    120
    121    /**
    122     * Removes functions from the library.
    123     *
    124     * @param functions to remove.
    125     */
    126    public void removeFunctions(final Functions functions) {
    127        allFunctions.remove(functions);
    128        synchronized (this) {
    129            byNamespace = null;
    130        }
    131    }
    132}