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