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