annotate src/clojure/lang/MultiFn.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
rev   line source
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 }