package com.googlecode.totallylazy;
import com.googlecode.totallylazy.annotations.multimethod;
import com.googlecode.totallylazy.functions.Function0;
import com.googlecode.totallylazy.matchers.NumberMatcher;
import org.junit.Ignore;
import org.junit.Test;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import static com.googlecode.totallylazy.Dispatcher.distanceBetween;
import static com.googlecode.totallylazy.predicates.Predicates.any;
import static com.googlecode.totallylazy.functions.TimeReport.time;
import static com.googlecode.totallylazy.matchers.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
public class multiTest {
public static class StaticSingle {
public static String process(Object o) { return new multi() {}.method(o); }
@multimethod static String process(String s) { return "String processed"; }
}
@Test
public void canDispatchWithSingle() throws Exception {
assertThat(StaticSingle.process((Object) "A String"), is("String processed"));
}
public static class Static2 {
public static String process(Object o) { return new multi() {}.method(o); }
@multimethod static String process(String s) { return "String processed"; }
@multimethod static String process(Integer s) { return "Integer processed"; }
}
@Test
public void canDispatchMethodBasedOnType() throws Exception {
assertThat(Static2.process((Object) "A String"), is("String processed"));
}
public static class Static3 {
public static String process(Object o) { return new multi(){}.method(o); }
@multimethod static String process(CharSequence s) { return "CharSequence processed"; }
@multimethod static String process(Integer s) { return "Integer processed"; }
}
@Test
public void willUseSuperTypesIfNoExactMatchFound() throws Exception {
assertThat(Static3.process((Object) "A String"), is("CharSequence processed"));
}
public static class Static4 {
public static String process(Object o) { return new multi(){}.method(o); }
@multimethod static String process(String s) { return "String processed"; }
@multimethod static String process(CharSequence s) { return "CharSequence processed"; }
@multimethod static String process(Integer s) { return "Integer processed"; }
}
@Test
public void willCallMostSpecific() throws Exception {
assertThat(Static4.process((Object) "A String"), is("String processed"));
}
public static class InterfaceOverSuperClass {
public static String process(Object o) { return new multi(){}.method(o); }
@multimethod static String process(Map s) { return "Map processed"; }
@multimethod static String process(AbstractMap s) { return "AbstractMap processed"; }
}
@Test
public void prefersInterfacesOverSuperClass() throws Exception {
assertThat(distanceBetween(HashMap.class, Map.class), NumberMatcher.is(1));
assertThat(distanceBetween(HashMap.class, AbstractMap.class), NumberMatcher.is(1.1));
assertThat(InterfaceOverSuperClass.process((Object) new HashMap<String, String>()), is("Map processed"));
}
@Test
public void distanceBetweenTwoClass() throws Exception {
assertThat(distanceBetween(String.class, CharSequence.class), NumberMatcher.is(1));
assertThat(distanceBetween(Integer.class, Serializable.class), NumberMatcher.is(2));
}
class Instance {
private multi multi;
public String process(Object o) {
if(multi == null) multi = new multi(){};
return multi.method(o);
}
@multimethod String process(String s) { return "String processed"; }
@multimethod String process(CharSequence s) { return "CharSequence processed"; }
@multimethod String process(Integer s) { return "Integer processed"; }
}
@Test
public void worksWithInstances() throws Exception {
assertThat(new Instance().process((Object) "A String"), is("String processed"));
}
@Test @Ignore
public void isPrettyFast() throws Exception {
final Instance instance = new Instance();
Function0<Object> multimethod = () -> instance.process((Object) "Foo");
Function0<Object> direct = () -> instance.process("Foo");
System.out.println("MultiMethod: " + time(10000, multimethod));
System.out.println("Direct: " + time(10000, direct));
System.out.println();
System.out.println("MultiMethod: " + time(10000, multimethod));
System.out.println("Direct: " + time(10000, direct));
System.out.println();
System.out.println("MultiMethod: " + time(10000, multimethod));
System.out.println("Direct: " + time(10000, direct));
System.out.println();
System.out.println("MultiMethod: " + time(10000, multimethod));
System.out.println("Direct: " + time(10000, direct));
}
@Test
public void worksWithInstancesAndSuperClasses() throws Exception {
class ExtendsInstance extends Instance{}
assertThat(new ExtendsInstance().process((Object) "A String"), is("String processed"));
}
@Test
public void supportsNotMatching() throws Exception {
class Instance {
public String process(Object o) { return new multi(){}.<String>methodOption(o).getOrElse("No match found"); }
}
assertThat(new Instance().process(10.f), is("No match found"));
}
@Test
public void doesNotCallMethodsWithMoreArguments() throws Exception {
class Instance {
public String process(Object o) { return new multi(){}.<String>methodOption(o).getOrElse("No match found"); }
@multimethod String process(Float a, Float b) {return "Float";}
}
assertThat(new Instance().process(10.f), is("No match found"));
}
@Test
public void doesNotCallMethodsWithLessArguments() throws Exception {
class Instance {
public String process(Object o) { return new multi(){}.<String>methodOption(o).getOrElse("No match found"); }
@multimethod String process() {return "no args";}
}
assertThat(new Instance().process(10.f), is("No match found"));
}
@Test
public void doesNotRecurse() throws Exception {
class Instance extends Eq {
}
assertThat(new Instance().equals(1), is(false));
}
@Test
public void canMatchNullToAVoidMethod() throws Exception {
class Instance extends Eq {
public String process(Object o) { return new multi(){}.<String>methodOption(o).getOrElse("No match found"); }
@multimethod String process(Float a) {return "Float";}
@multimethod String process(Void a) {return "Void";}
}
assertThat(new Instance().process((Object) null), is("Void"));
}
@Test
public void handlesNullWhenNoExplicitMatch() throws Exception {
class Instance extends Eq {
public String process(Object o) { return new multi(){}.<String>methodOption(o).getOrElse("No match found"); }
@multimethod String process(Float a) {return "Float";}
}
assertThat(new Instance().process((Object)null), is("No match found"));
}
@Test
public void byDefaultDoesNotInvokeMethodThatIsNotAnnotated() throws Exception {
class Instance extends Eq {
public String process(Object o) { return new multi(){}.<String>methodOption(o).getOrElse("No match found"); }
String process(Float a) {return "Float";}
}
assertThat(new Instance().process((Object)1.0f), is("No match found"));
}
@Test
public void supportsOverridingPredicateToAllowInvokingMethodThatIsNotAnnotated() throws Exception {
class Instance extends Eq {
public String process(Object o) { return new multi(any(Method.class)){}.<String>methodOption(o).getOrElse("No match found"); }
String process(Float a) {return "Float";}
}
assertThat(new Instance().process((Object)1.0f), is("Float"));
}
}