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 }