package com.googlecode.totallylazy; import com.googlecode.totallylazy.annotations.multimethod; import com.googlecode.totallylazy.predicates.Predicate; import com.googlecode.totallylazy.predicates.Predicates; import com.googlecode.totallylazy.reflection.Methods; import java.lang.reflect.Method; import static com.googlecode.totallylazy.Dispatcher.dispatcher; import static com.googlecode.totallylazy.reflection.Methods.methodName; import static com.googlecode.totallylazy.predicates.Predicates.and; import static com.googlecode.totallylazy.predicates.Predicates.is; import static com.googlecode.totallylazy.predicates.Predicates.not; import static com.googlecode.totallylazy.predicates.Predicates.notNullValue; import static com.googlecode.totallylazy.predicates.Predicates.where; import static com.googlecode.totallylazy.reflection.Reflection.enclosingInstance; import static java.lang.reflect.Modifier.isStatic; public abstract class multi { private final Dispatcher dispatcher; protected multi(Predicate<? super Method> predicate) { Method enclosing = enclosing(); Object instance = instance(enclosing); Class<?> aClass = declaringClass(enclosing, instance); String name = enclosing.getName(); this.dispatcher = Dispatcher.dispatcher(aClass, instance, and(where(methodName(), is(name)), not(enclosing), predicate)); } protected multi() { this(Predicates.<Method, multimethod>where(Methods.annotation(multimethod.class), notNullValue())); } public <T> T method(Object... args) { return this.<T>methodOption(args).get(); } public <T> Option<T> methodOption(Object... args) { return dispatcher.invokeOption(args); } private Method enclosing() {return getClass().getEnclosingMethod();} private Class<?> declaringClass(Method method, Object instance) { return isStatic(method.getModifiers()) ? method.getDeclaringClass() : instance.getClass(); } private Object instance(Method method) {return isStatic(method.getModifiers()) ? null : enclosingInstance(this);} }