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