1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.core.metapath.function;
28
29 import gov.nist.secauto.metaschema.core.metapath.IExpression;
30 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
31
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.stream.Stream;
36
37 import javax.xml.namespace.QName;
38
39 import edu.umd.cs.findbugs.annotations.NonNull;
40 import edu.umd.cs.findbugs.annotations.Nullable;
41
42 public class FunctionLibrary implements IFunctionLibrary {
43
44 @NonNull
45 private final Map<QName, NamedFunctionSet> libraryByQName = new HashMap<>();
46 @NonNull
47 private final Map<String, NamedFunctionSet> libraryByName = new HashMap<>();
48
49
50
51
52
53
54
55
56
57
58 public void registerFunction(@NonNull IFunction function) {
59 registerFunctionByQName(function);
60 registerFunctionByName(function);
61 }
62
63 protected void registerFunctionByQName(@NonNull IFunction function) {
64 QName qname = function.getQName();
65 IFunction duplicate;
66 synchronized (this) {
67 NamedFunctionSet functions = libraryByQName.get(qname);
68 if (functions == null) {
69 functions = new NamedFunctionSet();
70 libraryByQName.put(qname, functions);
71 }
72 duplicate = functions.addFunction(function);
73 }
74 if (duplicate != null) {
75 throw new IllegalArgumentException(String.format("Duplicate functions with same arity: %s shadows %s",
76 duplicate.toSignature(), function.toSignature()));
77 }
78 }
79
80 protected void registerFunctionByName(@NonNull IFunction function) {
81 String name = function.getName();
82 synchronized (this) {
83 NamedFunctionSet functions = libraryByName.get(name);
84 if (functions == null) {
85 functions = new NamedFunctionSet();
86 libraryByName.put(name, functions);
87 }
88
89 functions.addFunction(function);
90 }
91 }
92
93 @Override
94 public Stream<IFunction> getFunctionsAsStream() {
95 synchronized (this) {
96 return ObjectUtils.notNull(
97 libraryByQName.values().stream().flatMap(set -> {
98 return set.getFunctionsAsStream();
99 }));
100 }
101 }
102
103 @Override
104 public IFunction getFunction(@NonNull String name, @NonNull List<IExpression> args) {
105 IFunction retval;
106 synchronized (this) {
107 NamedFunctionSet functions = libraryByName.get(name);
108 retval = functions == null ? null : functions.getFunctionWithArity(args.size());
109 }
110 return retval;
111 }
112
113 @Override
114 public IFunction getFunction(@NonNull QName name, @NonNull List<IExpression> args) {
115 IFunction retval;
116 synchronized (this) {
117 NamedFunctionSet functions = libraryByQName.get(name);
118 retval = functions == null ? null : functions.getFunctionWithArity(args.size());
119 }
120 return retval;
121 }
122
123 private static class NamedFunctionSet {
124 private final Map<Integer, IFunction> arityToFunctionMap;
125
126 public NamedFunctionSet() {
127 this.arityToFunctionMap = new HashMap<>();
128 }
129
130 @SuppressWarnings("null")
131 @NonNull
132 public Stream<IFunction> getFunctionsAsStream() {
133 return arityToFunctionMap.values().stream();
134 }
135
136 @Nullable
137 public IFunction getFunctionWithArity(int arity) {
138 return arityToFunctionMap.get(arity);
139 }
140
141 @Nullable
142 public IFunction addFunction(@NonNull IFunction function) {
143 return arityToFunctionMap.put(function.arity(), function);
144 }
145 }
146 }