View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jxpath;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  
26  /**
27   * An object that aggregates {@link Functions} objects into a group Functions object. Since {@link JXPathContext} can only register a single Functions object,
28   * FunctionLibrary should always be used to group all Functions objects that need to be registered.
29   */
30  public class FunctionLibrary implements Functions {
31  
32      private final List<Functions> allFunctions = new ArrayList<>();
33      private Map<String, Object> byNamespace;
34  
35      /**
36       * Constructs a new instance.
37       */
38      public FunctionLibrary() {
39          // empty
40      }
41  
42      /**
43       * Add functions to the library
44       *
45       * @param functions to add
46       */
47      public void addFunctions(final Functions functions) {
48          allFunctions.add(functions);
49          synchronized (this) {
50              byNamespace = null;
51          }
52      }
53  
54      /**
55       * Prepare the cache.
56       *
57       * @return cache map keyed by namespace
58       */
59      private synchronized Map<String, Object> functionCache() {
60          if (byNamespace == null) {
61              byNamespace = new HashMap<>();
62              final int count = allFunctions.size();
63              for (int i = 0; i < count; i++) {
64                  final Functions funcs = allFunctions.get(i);
65                  final Set<String> namespaces = funcs.getUsedNamespaces();
66                  for (final String ns : namespaces) {
67                      final Object candidates = byNamespace.get(ns);
68                      if (candidates == null) {
69                          byNamespace.put(ns, funcs);
70                      } else if (candidates instanceof Functions) {
71                          final List<Object> lst = new ArrayList<>();
72                          lst.add(candidates);
73                          lst.add(funcs);
74                          byNamespace.put(ns, lst);
75                      } else {
76                          ((List) candidates).add(funcs);
77                      }
78                  }
79              }
80          }
81          return byNamespace;
82      }
83  
84      /**
85       * Gets a Function, if any, for the specified namespace, name and parameter types.
86       *
87       * @param namespace  function namespace
88       * @param name       function name
89       * @param parameters parameters
90       * @return Function found
91       */
92      @Override
93      public Function getFunction(final String namespace, final String name, final Object[] parameters) {
94          final Object candidates = functionCache().get(namespace);
95          if (candidates instanceof Functions) {
96              return ((Functions) candidates).getFunction(namespace, name, parameters);
97          }
98          if (candidates instanceof List) {
99              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 }