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