package org.simantics.scl.reflection.internal.registry; import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TObjectObjectProcedure; 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 org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; import org.simantics.scl.compiler.types.exceptions.SCLTypeParseException; import org.simantics.scl.compiler.types.util.ITypeEnvironment; import org.simantics.scl.reflection.MinimalTypeBindingScheme; import org.simantics.scl.reflection.ReflectionUtils; import org.simantics.scl.reflection.TypeBindingScheme; import org.simantics.scl.reflection.TypeNotFoundException; import org.simantics.scl.reflection.TypedValue; import org.simantics.scl.reflection.ValueNotFoundException; import org.simantics.scl.reflection.annotations.SCLType; import org.simantics.scl.reflection.annotations.SCLValue; import org.simantics.scl.reflection.functions.ClassMethodFunction; import org.simantics.scl.reflection.functions.ClassMethodFunction3; import org.simantics.scl.reflection.functions.ConstructorFunction; import org.simantics.scl.reflection.functions.FieldAccessorFunction; import org.simantics.scl.reflection.functions.InstanceMethodFunction; import org.simantics.scl.reflection.internal.Activator; import org.simantics.scl.reflection.internal.typeRegistry.TypeRegistry; public class Namespace { String namespace; ImportSeq importSeq; ArrayList classes = new ArrayList(); ArrayList externalClasses = new ArrayList(); ArrayList externalMethods = new ArrayList(); volatile THashMap> types; volatile THashMap values; public Namespace(String namespace, ImportSeq importSeq) { this.namespace = namespace; this.importSeq = importSeq; } public void addClass(Entry e) { classes.add(e); } public void addExternalMethod(ExternalMethod e) { externalMethods.add(e); } public void addExternalClass(ExternalClass e) { externalClasses.add(e); } public Class getClass(String name) throws TypeNotFoundException { if(types == null) { try { initializeTypes(); } catch (Exception e) { throw new TypeNotFoundException(e); } } Class type = types.get(name); if(type == null) throw new TypeNotFoundException("Didn't find type " + name + "."); return type; } public TypedValue getValue(String name) throws ValueNotFoundException { if(values == null) { try { initializeValues(); } catch (Exception e) { e.printStackTrace(); throw new ValueNotFoundException(e); } } TypedValue value = values.get(name); if(value == null) throw new ValueNotFoundException("Didn't find value " + name + "."); return value; } ITypeEnvironment typeEnvironment = new ITypeEnvironment() { @Override public Type resolve(String namespace, String name) { if(namespace == null) { if(TypeRegistry.isBuiltin(name)) namespace = Types.BUILTIN; else if(types.contains(name)) namespace = Namespace.this.namespace; else namespace = ""; } else { for(ImportSeq cur = importSeq;cur != null;cur = cur.parent) { if(namespace.equals(cur.localName)) { namespace = cur.path; break; } } } return Types.con(namespace, name); } }; private Type parseType(String typeText) throws SCLTypeParseException { return Types.closure(Types.parseType(typeEnvironment, typeText)); } private synchronized void initializeTypes() { if(types == null) { types = new THashMap>(); for(Entry entry : classes) { Class clazz = entry.loadClass(); if(clazz == null) { Activator.logError("Didn't find class " + entry.name + "."); continue; } SCLType sclType = clazz.getAnnotation(SCLType.class); if(sclType != null) { String name = sclType.name(); if(name.isEmpty()) name = clazz.getSimpleName(); types.put(name, clazz); } } for(ExternalClass entry : externalClasses) { Class clazz = entry.loadClass(); if(clazz == null) { Activator.logError("Didn't find class " + entry.className + "."); continue; } String name = entry.alternativeName; if(name == null) name = clazz.getSimpleName(); types.put(name, clazz); } } } private void handleMethod(TypeBindingScheme scheme, Class clazz, Method method) { SCLValue sclValue = method.getAnnotation(SCLValue.class); if(sclValue != null) { String name = sclValue.name(); if(name.isEmpty()) name = method.getName(); Type type; try { type = parseType(sclValue.type()); } catch (SCLTypeParseException e) { Activator.logError("Method " + method.getName() + " in class " + clazz.getCanonicalName() + " has invalid type declaration.", e ); return; } try { if(ReflectionUtils.isCompatible(scheme, type, method)) { Object value; if(Modifier.isStatic(method.getModifiers())) { int arity = method.getParameterTypes().length; if(arity == 3) value = new ClassMethodFunction3(method); else value = new ClassMethodFunction(method); } else value = new InstanceMethodFunction(method); values.put(name, new TypedValue(type, value)); } else { Activator.logError("Method " + method.getName() + " in class " + clazz.getCanonicalName() + " has incompatible SCL type in the SCLValue annotation." ); ReflectionUtils.isCompatible(scheme, type, method); } } catch (TypeNotFoundException e) { Activator.logError("Couldn't find all types in the type declaration of method " + method.getName() + " in class " + clazz.getCanonicalName() + "." ); } } } private void handleConstructor(TypeBindingScheme scheme, Class clazz, Constructor constr) { SCLValue sclValue = constr.getAnnotation(SCLValue.class); if(sclValue != null) { String name = sclValue.name(); if(name.isEmpty()) name = constr.getDeclaringClass().getSimpleName(); Type type; try { type = parseType(sclValue.type()); } catch (SCLTypeParseException e) { Activator.logError("Constructor in " + clazz.getCanonicalName() + " has invalid type declaration.", e ); return; } try { if(ReflectionUtils.isCompatible(scheme, type, constr)) { Object value = new ConstructorFunction(constr); values.put(name, new TypedValue(type, value)); } else { Activator.logError("Constructor of " + clazz.getCanonicalName() + " has incompatible SCL type in the SCLValue annotation." ); } } catch (TypeNotFoundException e) { Activator.logError("Couldn't find all types in the type declaration of constructor in " + clazz.getCanonicalName() + "." ); } } } private void handleField(TypeBindingScheme scheme, Class clazz, Field field) { SCLValue sclValue = field.getAnnotation(SCLValue.class); if(sclValue != null) { String name = sclValue.name(); if(name.isEmpty()) name = field.getName(); Type type; try { type = parseType(sclValue.type()); } catch (SCLTypeParseException e) { Activator.logError("Field " + field.getName() + " in class " + clazz.getCanonicalName() + " has invalid type declaration.", e ); return; } try { if(ReflectionUtils.isCompatible(scheme, type, field)) { Object value; if(Modifier.isStatic(field.getModifiers())) try { value = field.get(null); } catch (IllegalArgumentException e) { Activator.logError("Cannot read field " + field.getName() + " in class " + clazz.getCanonicalName() + ".", e ); return; } catch (IllegalAccessException e) { Activator.logError("Cannot read field " + field.getName() + " in class " + clazz.getCanonicalName() + ".", e ); return; } else value = new FieldAccessorFunction(field); values.put(name, new TypedValue(type, value)); } else { Activator.logError("Field " + field.getName() + " in class " + clazz.getCanonicalName() + " has incompatible SCL type in the SCLValue annotation." ); } } catch (TypeNotFoundException e) { Activator.logError("Couldn't find all types in the type declaration of field " + field.getName() + " in class " + clazz.getCanonicalName() + "." ); } } } private synchronized void initializeValues() { if(values == null) { initializeTypes(); TypeBindingScheme scheme = MinimalTypeBindingScheme.INSTANCE; values = new THashMap(); for(Entry entry : classes) { Class clazz = entry.loadClass(); if(clazz == null) { Activator.logError("Didn't find class " + entry.name + "."); continue; } for(Method method : clazz.getMethods()) { handleMethod(scheme, clazz, method); } for(Constructor constr : clazz.getConstructors()) { handleConstructor(scheme, clazz, constr); } for(Field field : clazz.getFields()) { handleField(scheme, clazz, field); } } for(ExternalMethod entry : externalMethods) { Class clazz = entry.loadClass(); if(clazz == null) { Activator.logError("Didn't find class " + entry.className + "."); continue; } Method method = entry.getMethod(clazz); if(method == null) { Activator.logError("Didn't find method " + entry.methodName + " in class " + entry.className + "."); continue; } handleMethod(scheme, clazz, method); } } } public void print() { for(Entry entry : classes) { System.out.println(" " + entry.name + " (" + entry.bundle + ")"); } try { initializeTypes(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } types.forEachEntry(new TObjectObjectProcedure>() { @Override public boolean execute(String name, Class clazz) { System.out.println(" type " + name + " = " + clazz.getCanonicalName()); return true; } }); try { initializeValues(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } values.forEachEntry(new TObjectObjectProcedure() { @Override public boolean execute(String name, TypedValue value) { System.out.println(" " + name + " :: " + value.getType()); return true; } }); } }