Skip to content

Instantly share code, notes, and snippets.

@maxymania
Last active September 28, 2018 08:25
Show Gist options
  • Save maxymania/4280f94381c2a9878e0fd26697247c0b to your computer and use it in GitHub Desktop.
Save maxymania/4280f94381c2a9878e0fd26697247c0b to your computer and use it in GitHub Desktop.
Reflection, Method selector like the java compiler
// TODO: insert your package name here!
package your.packag.e.name.here;
/*
* The ZLIB license (with two restrictions removed).
*
* Copyright 2018 Simon Schmidt.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. This notice may not be removed or altered from any source distribution.
*/
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
*
* @author Simon Schmidt
*/
public class MethodSelector {
public static Class<?> box(Class<?> c){
if(c==void.class)c = Void.class;
if(c==boolean.class)c = Boolean.class;
if(c==byte.class)c = Byte.class;
if(c==short.class)c = Short.class;
if(c==char.class)c = Character.class;
if(c==int.class)c = Integer.class;
if(c==long.class)c = Long.class;
if(c==float.class)c = Float.class;
if(c==double.class)c = Double.class;
return c;
}
public static class CachedMethod{
public final Method method;
public final Class<?>[] parameters,parametersBoxed;
public CachedMethod(Method method) {
this.method = method;
parameters = method.getParameterTypes();
parametersBoxed = parameters.clone();
for(int i=0,n=parametersBoxed.length;i<n;++i) parametersBoxed[i] = box(parametersBoxed[i]);
}
public CachedMethod(Class<?> ...args) {
this.method = null;
parameters = args;
parametersBoxed = parameters.clone();
for(int i=0,n=parametersBoxed.length;i<n;++i) parametersBoxed[i] = box(parametersBoxed[i]);
}
public Class<?>[] getParameterTypes(boolean box){ return box?parametersBoxed:parameters; }
public boolean isAssignableFrom(CachedMethod other,boolean box){
final Class<?>[] a = getParameterTypes(box),b = other.getParameterTypes(box);
if(a.length!=b.length) return false;
for(int i=0,n=a.length;i<n;++i)
if(!a[i].isAssignableFrom(b[i])) return false;
return true;
}
/*
bit & 1 if this := other
bit & 2 if other := this
*/
public int compareBit(CachedMethod other,boolean box){
final Class<?>[] a = getParameterTypes(box),b = other.getParameterTypes(box);
if(a.length!=b.length) return 0;
boolean afromb = true,bfroma = true;
for(int i=0,n=a.length;i<n;++i){
if(!a[i].isAssignableFrom(b[i])) afromb = false;
if(!b[i].isAssignableFrom(a[i])) bfroma = false;
}
int bit = 0;
if(afromb) bit|=1;
if(bfroma) bit|=2;
return bit;
}
}
public static void insert(List<CachedMethod> meth,CachedMethod inst,boolean box){
ListIterator<CachedMethod> iter = meth.listIterator();
while(iter.hasNext()){
CachedMethod cm = iter.next();
switch(cm.compareBit(inst, box)){
case 0: /* fully incompatible */ continue;
case 1: /* inst SC of cm. */ iter.previous(); break;
case 2: /* cm SC of inst. */
case 3: /* fully equivalent */ continue;
}
break;
}
iter.add(inst);
}
public static String methodId(Method m){
return m.getName()+"/"+m.getParameterCount();
}
private static List<CachedMethod> idealList(String unused){ return new LinkedList(); }
private static final CachedMethod[] C = {};
private final boolean useBoxing;
private final Map<String,CachedMethod[]> dyn = new HashMap(),stat = new HashMap();
private MethodSelector(Iterable<Method> meths,boolean box){
useBoxing = box;
Map<String,List<CachedMethod>> ldyn = new HashMap<>(),lstat = new HashMap<>();
for(Method meth:meths) {
int m = meth.getModifiers();
if(!Modifier.isPublic(m)) continue;
String id = methodId(meth);
CachedMethod cm = new CachedMethod(meth);
insert((Modifier.isStatic(m)?lstat:ldyn).computeIfAbsent(id, MethodSelector::idealList),cm,box);
}
ldyn.forEach((k,v)->dyn.put(k, v.toArray(C)));
lstat.forEach((k,v)->stat.put(k, v.toArray(C)));
}
public static MethodSelector getSelectorForClassEx(Class<?> itself,boolean declared,boolean useBoxing){
return new MethodSelector(Arrays.asList(declared?itself.getDeclaredMethods():itself.getMethods()),useBoxing);
}
public Method lookupEx(boolean dynamic,String name,Class<?> ...args) throws NoSuchMethodException{
String id = name+"/"+(args.length);
CachedMethod cm = new CachedMethod(args);
for(CachedMethod other:(dynamic?dyn:stat).getOrDefault(id, C)){
if(other.isAssignableFrom(cm, useBoxing))
return other.method;
}
throw new NoSuchMethodException(""+id+" with the given types "+Arrays.toString(args));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment