Revert "Prime SCL BindingRegistry to shave ~0.5s from startup"
[simantics/platform.git] / bundles / org.simantics.scl.reflection / src / org / simantics / scl / reflection / internal / registry / Namespace.java
1 package org.simantics.scl.reflection.internal.registry;
2
3 import gnu.trove.map.hash.THashMap;
4 import gnu.trove.procedure.TObjectObjectProcedure;
5
6 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Field;
8 import java.lang.reflect.Method;
9 import java.lang.reflect.Modifier;
10 import java.util.ArrayList;
11
12 import org.simantics.scl.compiler.types.Type;
13 import org.simantics.scl.compiler.types.Types;
14 import org.simantics.scl.compiler.types.exceptions.SCLTypeParseException;
15 import org.simantics.scl.compiler.types.util.ITypeEnvironment;
16 import org.simantics.scl.reflection.MinimalTypeBindingScheme;
17 import org.simantics.scl.reflection.ReflectionUtils;
18 import org.simantics.scl.reflection.TypeBindingScheme;
19 import org.simantics.scl.reflection.TypeNotFoundException;
20 import org.simantics.scl.reflection.TypedValue;
21 import org.simantics.scl.reflection.ValueNotFoundException;
22 import org.simantics.scl.reflection.annotations.SCLType;
23 import org.simantics.scl.reflection.annotations.SCLValue;
24 import org.simantics.scl.reflection.functions.ClassMethodFunction;
25 import org.simantics.scl.reflection.functions.ClassMethodFunction3;
26 import org.simantics.scl.reflection.functions.ConstructorFunction;
27 import org.simantics.scl.reflection.functions.FieldAccessorFunction;
28 import org.simantics.scl.reflection.functions.InstanceMethodFunction;
29 import org.simantics.scl.reflection.internal.Activator;
30 import org.simantics.scl.reflection.internal.typeRegistry.TypeRegistry;
31
32 public class Namespace {
33     String namespace;
34     ImportSeq importSeq;
35     ArrayList<Entry> classes = new ArrayList<Entry>();
36     ArrayList<ExternalClass> externalClasses = 
37             new ArrayList<ExternalClass>();
38     ArrayList<ExternalMethod> externalMethods = 
39             new ArrayList<ExternalMethod>();
40     volatile THashMap<String, Class<?>> types;
41     volatile THashMap<String, TypedValue> values;    
42     
43     public Namespace(String namespace, ImportSeq importSeq) {
44         this.namespace = namespace;
45         this.importSeq = importSeq;
46     }
47
48     public void addClass(Entry e) {
49         classes.add(e);
50     }
51     
52     public void addExternalMethod(ExternalMethod e) {
53         externalMethods.add(e);
54     }
55     
56     public void addExternalClass(ExternalClass e) {
57         externalClasses.add(e);
58     }
59     
60     public Class<?> getClass(String name) throws TypeNotFoundException {
61         if(types == null) {
62             try {
63                 initializeTypes();
64             } catch (Exception e) {
65                 throw new TypeNotFoundException(e);
66             } 
67         }
68         Class<?> type = types.get(name);
69         if(type == null)
70             throw new TypeNotFoundException("Didn't find type " + name + ".");
71         return type;
72     }
73     
74     public TypedValue getValue(String name) throws ValueNotFoundException {
75         if(values == null) {
76             try {
77                 initializeValues();
78             } catch (Exception e) {
79                 e.printStackTrace();
80                 throw new ValueNotFoundException(e);
81             } 
82         }
83         TypedValue value = values.get(name);
84         if(value == null)
85             throw new ValueNotFoundException("Didn't find value " + name + ".");
86         return value;
87     }
88     
89     ITypeEnvironment typeEnvironment = new ITypeEnvironment() {
90         
91         @Override
92         public Type resolve(String namespace, String name) {
93             if(namespace == null) {
94                 if(TypeRegistry.isBuiltin(name))
95                     namespace = Types.BUILTIN;
96                 else if(types.contains(name))
97                     namespace = Namespace.this.namespace;
98                 else
99                     namespace = "";
100             }
101             else {
102                 for(ImportSeq cur = importSeq;cur != null;cur = cur.parent) {
103                     if(namespace.equals(cur.localName)) {
104                         namespace = cur.path;
105                         break;
106                     }
107                 }
108             }
109             return Types.con(namespace, name);
110         }
111         
112     };
113     
114     private Type parseType(String typeText) throws SCLTypeParseException {
115         return Types.closure(Types.parseType(typeEnvironment, typeText));
116     }
117
118     private synchronized void initializeTypes() {
119         if(types == null) {
120             types = new THashMap<String, Class<?>>();
121             
122             for(Entry entry : classes) {
123                 Class<?> clazz = entry.loadClass();
124                 if(clazz == null) {
125                     Activator.logError("Didn't find class " + entry.name + ".");
126                     continue;
127                 }
128
129                 SCLType sclType = clazz.getAnnotation(SCLType.class);                    
130                 if(sclType != null) {
131                     String name = sclType.name();
132                     if(name.isEmpty())
133                         name = clazz.getSimpleName();
134                     types.put(name, clazz);
135                 }
136             }
137             
138             for(ExternalClass entry : externalClasses) {
139                 Class<?> clazz = entry.loadClass();
140                 if(clazz == null) {
141                     Activator.logError("Didn't find class " + entry.className + ".");
142                     continue;
143                 }
144                 
145                 String name = entry.alternativeName;
146                 if(name == null)
147                     name = clazz.getSimpleName();
148                 types.put(name, clazz);
149             }
150         }        
151     }
152     
153     private void handleMethod(TypeBindingScheme scheme, Class<?> clazz, Method method) {
154         SCLValue sclValue = method.getAnnotation(SCLValue.class);
155         if(sclValue != null) {
156             String name = sclValue.name();
157             if(name.isEmpty())
158                 name = method.getName();        
159             Type type;
160             try {
161                 type = parseType(sclValue.type());
162             } catch (SCLTypeParseException e) {
163                 Activator.logError("Method " + method.getName() + " in class " + 
164                         clazz.getCanonicalName() + " has invalid type declaration.", e
165                         );
166                 return;
167             }
168             try {
169                 if(ReflectionUtils.isCompatible(scheme, type, method)) {
170                     Object value;
171                     if(Modifier.isStatic(method.getModifiers())) {
172                         int arity = method.getParameterTypes().length;
173                         if(arity == 3)
174                             value = new ClassMethodFunction3(method);
175                         else
176                             value = new ClassMethodFunction(method);
177                     }
178                     else
179                         value = new InstanceMethodFunction(method);
180                     values.put(name, new TypedValue(type, value));
181                 }
182                 else {
183                     Activator.logError("Method " + method.getName() + " in class " + 
184                             clazz.getCanonicalName() + " has incompatible SCL type in the SCLValue annotation."
185                             );
186                     ReflectionUtils.isCompatible(scheme, type, method);
187                 }
188             } catch (TypeNotFoundException e) {
189                 Activator.logError("Couldn't find all types in the type declaration of method " + method.getName() + " in class " + 
190                         clazz.getCanonicalName() + "."
191                         );
192             }
193         }
194     }
195     
196     private void handleConstructor(TypeBindingScheme scheme, Class<?> clazz, Constructor<?> constr) {
197         SCLValue sclValue = constr.getAnnotation(SCLValue.class);
198         if(sclValue != null) {
199             String name = sclValue.name();
200             if(name.isEmpty())
201                 name = constr.getDeclaringClass().getSimpleName();
202             Type type;
203             try {
204                 type = parseType(sclValue.type());
205             } catch (SCLTypeParseException e) {
206                 Activator.logError("Constructor in " + 
207                         clazz.getCanonicalName() + " has invalid type declaration.", e
208                         );
209                 return;
210             }
211             try {
212                 if(ReflectionUtils.isCompatible(scheme, type, constr)) {
213                     Object value = new ConstructorFunction(constr);
214                     values.put(name, new TypedValue(type, value));
215                 }
216                 else {
217                     Activator.logError("Constructor of " + 
218                             clazz.getCanonicalName() + " has incompatible SCL type in the SCLValue annotation."
219                             );
220                 }
221             } catch (TypeNotFoundException e) {
222                 Activator.logError("Couldn't find all types in the type declaration of constructor in " + 
223                         clazz.getCanonicalName() + "."
224                         );
225             }
226         }
227     }
228     
229     private void handleField(TypeBindingScheme scheme, Class<?> clazz, Field field) {
230         SCLValue sclValue = field.getAnnotation(SCLValue.class);
231         if(sclValue != null) {
232             String name = sclValue.name();
233             if(name.isEmpty())
234                 name = field.getName();
235             Type type;
236             try {
237                 type = parseType(sclValue.type());
238             } catch (SCLTypeParseException e) {
239                 Activator.logError("Field " + field.getName() + " in class " + 
240                         clazz.getCanonicalName() + " has invalid type declaration.", e
241                         );
242                 return;
243             }
244             try {
245                 if(ReflectionUtils.isCompatible(scheme, type, field)) {
246                     Object value;
247                     if(Modifier.isStatic(field.getModifiers()))
248                         try {
249                             value = field.get(null);
250                         } catch (IllegalArgumentException e) {
251                             Activator.logError("Cannot read field " + field.getName() + " in class " + 
252                                     clazz.getCanonicalName() + ".", e
253                                     );
254                             return;
255                         } catch (IllegalAccessException e) {
256                             Activator.logError("Cannot read field " + field.getName() + " in class " + 
257                                     clazz.getCanonicalName() + ".", e
258                                     );
259                             return;
260                         }
261                     else
262                         value = new FieldAccessorFunction(field);
263                     values.put(name, new TypedValue(type, value));
264                 }
265                 else {
266                     Activator.logError("Field " + field.getName() + " in class " + 
267                             clazz.getCanonicalName() + " has incompatible SCL type in the SCLValue annotation."
268                             );
269                 }
270             } catch (TypeNotFoundException e) {
271                 Activator.logError("Couldn't find all types in the type declaration of field " + field.getName() + " in class " + 
272                         clazz.getCanonicalName() + "."
273                         );
274             }
275         }
276     }        
277         
278     private synchronized void initializeValues() {
279         if(values == null) {
280             initializeTypes();
281             TypeBindingScheme scheme = MinimalTypeBindingScheme.INSTANCE;
282             
283             values = new THashMap<String, TypedValue>();
284             
285             for(Entry entry : classes) {
286                 Class<?> clazz = entry.loadClass();
287                 
288                 if(clazz == null) {
289                     Activator.logError("Didn't find class " + entry.name + ".");
290                     continue;
291                 }
292                 
293                 for(Method method : clazz.getMethods()) {
294                     handleMethod(scheme, clazz, method);
295                 }
296                 
297                 for(Constructor<?> constr : clazz.getConstructors()) {
298                     handleConstructor(scheme, clazz, constr);
299                 }
300                 
301                 for(Field field : clazz.getFields()) {
302                     handleField(scheme, clazz, field);
303                 }
304             }
305             
306             for(ExternalMethod entry : externalMethods) {
307                 Class<?> clazz = entry.loadClass();
308                 
309                 if(clazz == null) {
310                     Activator.logError("Didn't find class " + entry.className + ".");
311                     continue;
312                 }
313                 
314                 Method method = entry.getMethod(clazz);
315                 
316                 if(method == null) {
317                     Activator.logError("Didn't find method " + entry.methodName + 
318                             " in class " + entry.className + ".");
319                     continue;
320                 }
321                 
322                 handleMethod(scheme, clazz, method);
323             }
324         }        
325     }
326     
327     public void print() {        
328         for(Entry entry : classes) {
329             System.out.println("    " + entry.name + " (" + entry.bundle + ")");
330         }
331         try {
332             initializeTypes();
333         } catch (Exception e) {
334             e.printStackTrace();
335             throw new RuntimeException(e);
336         } 
337         types.forEachEntry(new TObjectObjectProcedure<String, Class<?>>() {            
338             @Override
339             public boolean execute(String name, Class<?> clazz) {
340                 System.out.println("    type " + name + " = " + clazz.getCanonicalName());
341                 return true;
342             }
343         });
344         try {
345             initializeValues();
346         } catch (Exception e) {
347             e.printStackTrace();
348             throw new RuntimeException(e);
349         } 
350         values.forEachEntry(new TObjectObjectProcedure<String, TypedValue>() {            
351             @Override
352             public boolean execute(String name, TypedValue value) {
353                 System.out.println("    " + name + " :: " + value.getType());
354                 return true;
355             }
356         });
357     }
358     
359 }