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