rlm@10
|
1 /**
|
rlm@10
|
2 * Copyright (c) Rich Hickey. All rights reserved.
|
rlm@10
|
3 * The use and distribution terms for this software are covered by the
|
rlm@10
|
4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
5 * which can be found in the file epl-v10.html at the root of this distribution.
|
rlm@10
|
6 * By using this software in any fashion, you are agreeing to be bound by
|
rlm@10
|
7 * the terms of this license.
|
rlm@10
|
8 * You must not remove this notice, or any other, from this software.
|
rlm@10
|
9 **/
|
rlm@10
|
10
|
rlm@10
|
11 /* rich Sep 13, 2007 */
|
rlm@10
|
12
|
rlm@10
|
13 package clojure.lang;
|
rlm@10
|
14
|
rlm@10
|
15 import java.util.Map;
|
rlm@10
|
16
|
rlm@10
|
17 public class MultiFn extends AFn{
|
rlm@10
|
18 final public IFn dispatchFn;
|
rlm@10
|
19 final public Object defaultDispatchVal;
|
rlm@10
|
20 final public IRef hierarchy;
|
rlm@10
|
21 final String name;
|
rlm@10
|
22 IPersistentMap methodTable;
|
rlm@10
|
23 IPersistentMap preferTable;
|
rlm@10
|
24 IPersistentMap methodCache;
|
rlm@10
|
25 Object cachedHierarchy;
|
rlm@10
|
26
|
rlm@10
|
27 static final Var assoc = RT.var("clojure.core", "assoc");
|
rlm@10
|
28 static final Var dissoc = RT.var("clojure.core", "dissoc");
|
rlm@10
|
29 static final Var isa = RT.var("clojure.core", "isa?");
|
rlm@10
|
30 static final Var parents = RT.var("clojure.core", "parents");
|
rlm@10
|
31
|
rlm@10
|
32 public MultiFn(String name, IFn dispatchFn, Object defaultDispatchVal, IRef hierarchy) throws Exception{
|
rlm@10
|
33 this.name = name;
|
rlm@10
|
34 this.dispatchFn = dispatchFn;
|
rlm@10
|
35 this.defaultDispatchVal = defaultDispatchVal;
|
rlm@10
|
36 this.methodTable = PersistentHashMap.EMPTY;
|
rlm@10
|
37 this.methodCache = getMethodTable();
|
rlm@10
|
38 this.preferTable = PersistentHashMap.EMPTY;
|
rlm@10
|
39 this.hierarchy = hierarchy;
|
rlm@10
|
40 cachedHierarchy = null;
|
rlm@10
|
41 }
|
rlm@10
|
42
|
rlm@10
|
43 synchronized public MultiFn reset(){
|
rlm@10
|
44 methodTable = methodCache = preferTable = PersistentHashMap.EMPTY;
|
rlm@10
|
45 cachedHierarchy = null;
|
rlm@10
|
46 return this;
|
rlm@10
|
47 }
|
rlm@10
|
48
|
rlm@10
|
49 synchronized public MultiFn addMethod(Object dispatchVal, IFn method) throws Exception{
|
rlm@10
|
50 methodTable = getMethodTable().assoc(dispatchVal, method);
|
rlm@10
|
51 resetCache();
|
rlm@10
|
52 return this;
|
rlm@10
|
53 }
|
rlm@10
|
54
|
rlm@10
|
55 synchronized public MultiFn removeMethod(Object dispatchVal) throws Exception{
|
rlm@10
|
56 methodTable = getMethodTable().without(dispatchVal);
|
rlm@10
|
57 resetCache();
|
rlm@10
|
58 return this;
|
rlm@10
|
59 }
|
rlm@10
|
60
|
rlm@10
|
61 synchronized public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) throws Exception{
|
rlm@10
|
62 if(prefers(dispatchValY, dispatchValX))
|
rlm@10
|
63 throw new IllegalStateException(
|
rlm@10
|
64 String.format("Preference conflict in multimethod '%s': %s is already preferred to %s",
|
rlm@10
|
65 name, dispatchValY, dispatchValX));
|
rlm@10
|
66 preferTable = getPreferTable().assoc(dispatchValX, RT.conj((IPersistentCollection) RT.get(getPreferTable(),
|
rlm@10
|
67 dispatchValX,
|
rlm@10
|
68 PersistentHashSet.EMPTY),
|
rlm@10
|
69 dispatchValY));
|
rlm@10
|
70 resetCache();
|
rlm@10
|
71 return this;
|
rlm@10
|
72 }
|
rlm@10
|
73
|
rlm@10
|
74 private boolean prefers(Object x, Object y) throws Exception{
|
rlm@10
|
75 IPersistentSet xprefs = (IPersistentSet) getPreferTable().valAt(x);
|
rlm@10
|
76 if(xprefs != null && xprefs.contains(y))
|
rlm@10
|
77 return true;
|
rlm@10
|
78 for(ISeq ps = RT.seq(parents.invoke(y)); ps != null; ps = ps.next())
|
rlm@10
|
79 {
|
rlm@10
|
80 if(prefers(x, ps.first()))
|
rlm@10
|
81 return true;
|
rlm@10
|
82 }
|
rlm@10
|
83 for(ISeq ps = RT.seq(parents.invoke(x)); ps != null; ps = ps.next())
|
rlm@10
|
84 {
|
rlm@10
|
85 if(prefers(ps.first(), y))
|
rlm@10
|
86 return true;
|
rlm@10
|
87 }
|
rlm@10
|
88 return false;
|
rlm@10
|
89 }
|
rlm@10
|
90
|
rlm@10
|
91 private boolean isA(Object x, Object y) throws Exception{
|
rlm@10
|
92 return RT.booleanCast(isa.invoke(hierarchy.deref(), x, y));
|
rlm@10
|
93 }
|
rlm@10
|
94
|
rlm@10
|
95 private boolean dominates(Object x, Object y) throws Exception{
|
rlm@10
|
96 return prefers(x, y) || isA(x, y);
|
rlm@10
|
97 }
|
rlm@10
|
98
|
rlm@10
|
99 private IPersistentMap resetCache() throws Exception{
|
rlm@10
|
100 methodCache = getMethodTable();
|
rlm@10
|
101 cachedHierarchy = hierarchy.deref();
|
rlm@10
|
102 return methodCache;
|
rlm@10
|
103 }
|
rlm@10
|
104
|
rlm@10
|
105 synchronized public IFn getMethod(Object dispatchVal) throws Exception{
|
rlm@10
|
106 if(cachedHierarchy != hierarchy.deref())
|
rlm@10
|
107 resetCache();
|
rlm@10
|
108 IFn targetFn = (IFn) methodCache.valAt(dispatchVal);
|
rlm@10
|
109 if(targetFn != null)
|
rlm@10
|
110 return targetFn;
|
rlm@10
|
111 targetFn = findAndCacheBestMethod(dispatchVal);
|
rlm@10
|
112 if(targetFn != null)
|
rlm@10
|
113 return targetFn;
|
rlm@10
|
114 targetFn = (IFn) getMethodTable().valAt(defaultDispatchVal);
|
rlm@10
|
115 return targetFn;
|
rlm@10
|
116 }
|
rlm@10
|
117
|
rlm@10
|
118 private IFn getFn(Object dispatchVal) throws Exception{
|
rlm@10
|
119 IFn targetFn = getMethod(dispatchVal);
|
rlm@10
|
120 if(targetFn == null)
|
rlm@10
|
121 throw new IllegalArgumentException(String.format("No method in multimethod '%s' for dispatch value: %s",
|
rlm@10
|
122 name, dispatchVal));
|
rlm@10
|
123 return targetFn;
|
rlm@10
|
124 }
|
rlm@10
|
125
|
rlm@10
|
126 private IFn findAndCacheBestMethod(Object dispatchVal) throws Exception{
|
rlm@10
|
127 Map.Entry bestEntry = null;
|
rlm@10
|
128 for(Object o : getMethodTable())
|
rlm@10
|
129 {
|
rlm@10
|
130 Map.Entry e = (Map.Entry) o;
|
rlm@10
|
131 if(isA(dispatchVal, e.getKey()))
|
rlm@10
|
132 {
|
rlm@10
|
133 if(bestEntry == null || dominates(e.getKey(), bestEntry.getKey()))
|
rlm@10
|
134 bestEntry = e;
|
rlm@10
|
135 if(!dominates(bestEntry.getKey(), e.getKey()))
|
rlm@10
|
136 throw new IllegalArgumentException(
|
rlm@10
|
137 String.format(
|
rlm@10
|
138 "Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred",
|
rlm@10
|
139 name, dispatchVal, e.getKey(), bestEntry.getKey()));
|
rlm@10
|
140 }
|
rlm@10
|
141 }
|
rlm@10
|
142 if(bestEntry == null)
|
rlm@10
|
143 return null;
|
rlm@10
|
144 //ensure basis has stayed stable throughout, else redo
|
rlm@10
|
145 if(cachedHierarchy == hierarchy.deref())
|
rlm@10
|
146 {
|
rlm@10
|
147 //place in cache
|
rlm@10
|
148 methodCache = methodCache.assoc(dispatchVal, bestEntry.getValue());
|
rlm@10
|
149 return (IFn) bestEntry.getValue();
|
rlm@10
|
150 }
|
rlm@10
|
151 else
|
rlm@10
|
152 {
|
rlm@10
|
153 resetCache();
|
rlm@10
|
154 return findAndCacheBestMethod(dispatchVal);
|
rlm@10
|
155 }
|
rlm@10
|
156 }
|
rlm@10
|
157
|
rlm@10
|
158 public Object invoke() throws Exception{
|
rlm@10
|
159 return getFn(dispatchFn.invoke()).invoke();
|
rlm@10
|
160 }
|
rlm@10
|
161
|
rlm@10
|
162 public Object invoke(Object arg1) throws Exception{
|
rlm@10
|
163 return getFn(dispatchFn.invoke(arg1)).invoke(arg1);
|
rlm@10
|
164 }
|
rlm@10
|
165
|
rlm@10
|
166 public Object invoke(Object arg1, Object arg2) throws Exception{
|
rlm@10
|
167 return getFn(dispatchFn.invoke(arg1, arg2)).invoke(arg1, arg2);
|
rlm@10
|
168 }
|
rlm@10
|
169
|
rlm@10
|
170 public Object invoke(Object arg1, Object arg2, Object arg3) throws Exception{
|
rlm@10
|
171 return getFn(dispatchFn.invoke(arg1, arg2, arg3)).invoke(arg1, arg2, arg3);
|
rlm@10
|
172 }
|
rlm@10
|
173
|
rlm@10
|
174 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) throws Exception{
|
rlm@10
|
175 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4)).invoke(arg1, arg2, arg3, arg4);
|
rlm@10
|
176 }
|
rlm@10
|
177
|
rlm@10
|
178 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) throws Exception{
|
rlm@10
|
179 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5)).invoke(arg1, arg2, arg3, arg4, arg5);
|
rlm@10
|
180 }
|
rlm@10
|
181
|
rlm@10
|
182 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) throws Exception{
|
rlm@10
|
183 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6)).invoke(arg1, arg2, arg3, arg4, arg5, arg6);
|
rlm@10
|
184 }
|
rlm@10
|
185
|
rlm@10
|
186 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
|
rlm@10
|
187 throws Exception{
|
rlm@10
|
188 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7))
|
rlm@10
|
189 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
rlm@10
|
190 }
|
rlm@10
|
191
|
rlm@10
|
192 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
193 Object arg8) throws Exception{
|
rlm@10
|
194 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)).
|
rlm@10
|
195 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
rlm@10
|
196 }
|
rlm@10
|
197
|
rlm@10
|
198 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
199 Object arg8, Object arg9) throws Exception{
|
rlm@10
|
200 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)).
|
rlm@10
|
201 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
|
rlm@10
|
202 }
|
rlm@10
|
203
|
rlm@10
|
204 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
205 Object arg8, Object arg9, Object arg10) throws Exception{
|
rlm@10
|
206 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)).
|
rlm@10
|
207 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
|
rlm@10
|
208 }
|
rlm@10
|
209
|
rlm@10
|
210 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
211 Object arg8, Object arg9, Object arg10, Object arg11) throws Exception{
|
rlm@10
|
212 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)).
|
rlm@10
|
213 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
|
rlm@10
|
214 }
|
rlm@10
|
215
|
rlm@10
|
216 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
217 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) throws Exception{
|
rlm@10
|
218 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)).
|
rlm@10
|
219 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
|
rlm@10
|
220 }
|
rlm@10
|
221
|
rlm@10
|
222 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
223 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13) throws Exception{
|
rlm@10
|
224 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13)).
|
rlm@10
|
225 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
|
rlm@10
|
226 }
|
rlm@10
|
227
|
rlm@10
|
228 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
229 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
|
rlm@10
|
230 throws Exception{
|
rlm@10
|
231 return getFn(
|
rlm@10
|
232 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14)).
|
rlm@10
|
233 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
|
rlm@10
|
234 }
|
rlm@10
|
235
|
rlm@10
|
236 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
237 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
|
rlm@10
|
238 Object arg15) throws Exception{
|
rlm@10
|
239 return getFn(
|
rlm@10
|
240 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
241 arg15))
|
rlm@10
|
242 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
|
rlm@10
|
243 }
|
rlm@10
|
244
|
rlm@10
|
245 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
246 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
|
rlm@10
|
247 Object arg15, Object arg16) throws Exception{
|
rlm@10
|
248 return getFn(
|
rlm@10
|
249 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
250 arg15, arg16))
|
rlm@10
|
251 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
252 arg15, arg16);
|
rlm@10
|
253 }
|
rlm@10
|
254
|
rlm@10
|
255 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
256 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
|
rlm@10
|
257 Object arg15, Object arg16, Object arg17) throws Exception{
|
rlm@10
|
258 return getFn(
|
rlm@10
|
259 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
260 arg15, arg16, arg17))
|
rlm@10
|
261 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
262 arg15, arg16, arg17);
|
rlm@10
|
263 }
|
rlm@10
|
264
|
rlm@10
|
265 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
266 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
|
rlm@10
|
267 Object arg15, Object arg16, Object arg17, Object arg18) throws Exception{
|
rlm@10
|
268 return getFn(
|
rlm@10
|
269 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
270 arg15, arg16, arg17, arg18)).
|
rlm@10
|
271 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
272 arg15, arg16, arg17, arg18);
|
rlm@10
|
273 }
|
rlm@10
|
274
|
rlm@10
|
275 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
276 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
|
rlm@10
|
277 Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) throws Exception{
|
rlm@10
|
278 return getFn(
|
rlm@10
|
279 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
280 arg15, arg16, arg17, arg18, arg19)).
|
rlm@10
|
281 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
282 arg15, arg16, arg17, arg18, arg19);
|
rlm@10
|
283 }
|
rlm@10
|
284
|
rlm@10
|
285 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
286 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
|
rlm@10
|
287 Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
|
rlm@10
|
288 throws Exception{
|
rlm@10
|
289 return getFn(
|
rlm@10
|
290 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
291 arg15, arg16, arg17, arg18, arg19, arg20)).
|
rlm@10
|
292 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
293 arg15, arg16, arg17, arg18, arg19, arg20);
|
rlm@10
|
294 }
|
rlm@10
|
295
|
rlm@10
|
296 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
|
rlm@10
|
297 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
|
rlm@10
|
298 Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20, Object... args)
|
rlm@10
|
299 throws Exception{
|
rlm@10
|
300 return getFn(
|
rlm@10
|
301 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
302 arg15, arg16, arg17, arg18, arg19, arg20, args)).
|
rlm@10
|
303 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
|
rlm@10
|
304 arg15, arg16, arg17, arg18, arg19, arg20, args);
|
rlm@10
|
305 }
|
rlm@10
|
306
|
rlm@10
|
307 public IPersistentMap getMethodTable() {
|
rlm@10
|
308 return methodTable;
|
rlm@10
|
309 }
|
rlm@10
|
310
|
rlm@10
|
311 public IPersistentMap getPreferTable() {
|
rlm@10
|
312 return preferTable;
|
rlm@10
|
313 }
|
rlm@10
|
314 }
|