--- /dev/null
+package org.simantics.scl.compiler.constants.generic;
+
+import gnu.trove.map.hash.THashMap;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.cojen.classfile.TypeDesc;
+import org.simantics.scl.compiler.constants.generic.MethodRef.ConstructorRef;
+import org.simantics.scl.compiler.constants.generic.MethodRef.FieldRef;
+import org.simantics.scl.compiler.constants.generic.MethodRef.ObjectMethodRef;
+import org.simantics.scl.compiler.constants.generic.MethodRef.SetFieldRef;
+import org.simantics.scl.compiler.constants.generic.MethodRef.SetStaticFieldRef;
+import org.simantics.scl.compiler.constants.generic.MethodRef.StaticFieldRef;
+import org.simantics.scl.compiler.constants.generic.MethodRef.StaticMethodRef;
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;
+
+/**
+ * This class is a reference to a Java class that contains a map of MethodRef for each
+ * method declared by the referenced class.
+ *
+ * Use {@link #getMethodRefs(String)} to get a list of overloaded methods for a given name.
+ */
+public class ClassRef {
+ /**
+ * A zero length array used as the value of argument lists of zero-arity methods.
+ */
+ public static final TypeDesc[] NO_PARAMS = new TypeDesc[0];
+
+ /**
+ * A map from method names to lists of identically named methods.
+ */
+ private THashMap<String, ArrayList<MethodRef>> methods =
+ new THashMap<String, ArrayList<MethodRef>>();
+
+ /**
+ * Get a list of methods with a given name.
+ * @param methodName A method name
+ * @return A list of {@link MethodRef} instances that share the name {@code methodName}.
+ */
+ public List<MethodRef> getMethodRefs(String methodName) {
+ List<MethodRef> refs = methods.get(methodName);
+ if(refs == null)
+ return Collections.emptyList();
+ return refs;
+ }
+
+ /**
+ * Construct a {@link ClassRef} with class loaders and a class name.
+ * @throws ClassNotFoundException if the class is not found.
+ */
+ public ClassRef(ClassLoader classLoader, String className) throws ClassNotFoundException {
+ analyzeClass(classLoader.loadClass(className), className);
+ }
+
+ /**
+ * Construct a {@link ClassRef} for a Java class object.
+ */
+ public ClassRef(Class<?> clazz) {
+ analyzeClass(clazz, MethodBuilderBase.getClassName(clazz));
+ }
+
+ /**
+ * Analyse the Java class and store {@link MethodRef} entries for all methods implemented by the class.
+ */
+ private void analyzeClass(Class<?> clazz, String className) {
+ boolean isInterface = clazz.isInterface();
+ for(Constructor<?> constructor : clazz.getConstructors()) {
+ addMethodRef("<init>", new ConstructorRef(
+ className,
+ toTypeDescs(constructor.getParameterTypes())
+ ));
+ }
+ for(Method method : clazz.getMethods()) {
+ if(method.isSynthetic())
+ continue;
+ String methodName = method.getName();
+ TypeDesc returnType = TypeDesc.forClass(method.getReturnType());
+ TypeDesc[] parameters = toTypeDescs(method.getParameterTypes());
+
+ int modifiers = method.getModifiers();
+ if(Modifier.isStatic(modifiers))
+ addMethodRef(methodName, new StaticMethodRef(
+ className, methodName,
+ returnType,
+ parameters
+ ));
+ else
+ addMethodRef(methodName, new ObjectMethodRef(
+ isInterface,
+ className, methodName,
+ returnType,
+ parameters
+ ));
+ }
+ for(Field field : clazz.getFields()) {
+ String fieldName = field.getName();
+ TypeDesc type = TypeDesc.forClass(field.getType());
+
+ int modifiers = field.getModifiers();
+ if(Modifier.isStatic(modifiers)) {
+ addMethodRef(fieldName, new StaticFieldRef(
+ className,
+ fieldName,
+ type
+ ));
+ addMethodRef("<set>" + fieldName, new SetStaticFieldRef(
+ className,
+ fieldName,
+ type
+ ));
+ }
+ else {
+ addMethodRef(fieldName, new FieldRef(
+ className,
+ fieldName,
+ type
+ ));
+ addMethodRef("<set>" + fieldName, new SetFieldRef(
+ className,
+ fieldName,
+ type
+ ));
+ }
+ }
+ }
+
+ /**
+ * Create an array of {@link TypeDesc} instances for an array of Class instances.
+ * For an empty array of classes the result will always be the empty array constant {@link #NO_PARAMS}.
+ */
+ private static TypeDesc[] toTypeDescs(Class<?>[] classes) {
+ if(classes.length == 0)
+ return NO_PARAMS;
+ TypeDesc[] result = new TypeDesc[classes.length];
+ for(int i=0;i<result.length;++i)
+ result[i] = TypeDesc.forClass(classes[i]);
+ return result;
+ }
+
+ /**
+ * Add a {@code MethodRef} to the {@link #methods} map.
+ */
+ private void addMethodRef(String name, MethodRef ref) {
+ ArrayList<MethodRef> l = methods.get(name);
+ if(l == null) {
+ l = new ArrayList<MethodRef>(2);
+ methods.put(name, l);
+ }
+ l.add(ref);
+ }
+}