]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/classfactory/AsmTypeClassFactory.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / classfactory / AsmTypeClassFactory.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.databoard.binding.classfactory;\r
13 \r
14 import java.lang.annotation.Annotation;\r
15 import java.util.HashMap;\r
16 import java.util.Map;\r
17 import java.util.regex.Matcher;\r
18 import java.util.regex.Pattern;\r
19 \r
20 import org.objectweb.asm.AnnotationVisitor;\r
21 import org.objectweb.asm.ClassWriter;\r
22 import org.objectweb.asm.FieldVisitor;\r
23 import org.objectweb.asm.Label;\r
24 import org.objectweb.asm.MethodVisitor;\r
25 import org.objectweb.asm.Opcodes;\r
26 import org.objectweb.asm.Type;\r
27 import org.simantics.databoard.annotations.Arguments;\r
28 import org.simantics.databoard.annotations.Identifier;\r
29 import org.simantics.databoard.annotations.Length;\r
30 import org.simantics.databoard.annotations.MIMEType;\r
31 import org.simantics.databoard.annotations.Name;\r
32 import org.simantics.databoard.annotations.Optional;\r
33 import org.simantics.databoard.annotations.Range;\r
34 import org.simantics.databoard.annotations.Referable;\r
35 import org.simantics.databoard.annotations.Union;\r
36 import org.simantics.databoard.annotations.Unit;\r
37 import org.simantics.databoard.binding.error.BindingConstructionException;\r
38 import org.simantics.databoard.binding.reflection.AsmBindingClassLoader;\r
39 import org.simantics.databoard.binding.reflection.BindingRequest;\r
40 import org.simantics.databoard.type.Component;\r
41 import org.simantics.databoard.type.Datatype;\r
42 import org.simantics.databoard.type.OptionalType;\r
43 import org.simantics.databoard.type.RecordType;\r
44 import org.simantics.databoard.type.UnionType;\r
45 import org.simantics.databoard.util.Bean;\r
46 \r
47 /**\r
48  * This class loader constructs record-like classes of RecordTypes, and\r
49  * Enums / other classes of UnionTypes.\r
50  */\r
51 public class AsmTypeClassFactory extends ClassLoader implements Opcodes, TypeClassSubFactory {\r
52         \r
53         // if true, the record classes are Beans.\r
54         boolean makeBean = true;\r
55         TypeClassFactory classFactory;\r
56         \r
57         Map<String, Datatype> signatureCache = new HashMap<String, Datatype>();\r
58         \r
59         public AsmTypeClassFactory(TypeClassFactory classFactory) {\r
60                 super( AsmTypeClassFactory.class.getClassLoader() );\r
61                 this.classFactory = classFactory;\r
62         }\r
63 \r
64         /**\r
65          * If true, the manufactured classes inherit Bean.\r
66          * @param makeBean\r
67          */\r
68         public void setBeanMaker(boolean makeBean) {\r
69                 this.makeBean = makeBean;\r
70         }\r
71 \r
72         @Override\r
73         public BindingRequest construct(TypeClassFactory mainFactory, Datatype type) \r
74         throws BindingConstructionException\r
75         {                                               \r
76                 if ( type instanceof RecordType ) {\r
77                         RecordType rt = (RecordType) type;\r
78                         \r
79                         SignatureVisitor sv = new SignatureVisitor();\r
80                         type.accept( sv, null );\r
81                         String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode );\r
82                         \r
83                         String className = "org.simantics.databoard.RecordType_"+sig;\r
84                         String classSig = "Lorg/simantics/databoard/RecordType_"+sig+";";\r
85                         BindingRequest br = new BindingRequest(this, className, classSig, classSig);\r
86                         signatureCache.put(className, rt);\r
87                         return br;\r
88                 }\r
89                 \r
90                 if ( type instanceof UnionType ) {\r
91                         UnionType ut = (UnionType) type;\r
92                         SignatureVisitor sv = new SignatureVisitor();\r
93                         type.accept( sv, null );\r
94                         String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode );\r
95                         if (ut.isEnumeration()) {\r
96                                 String className = "org.simantics.databoard.EnumType_"+sig;\r
97                                 String classSig = "Lorg/simantics/databoard/EnumType_"+sig+";";\r
98                                 BindingRequest br = new BindingRequest(this, className, classSig, classSig);\r
99                                 signatureCache.put(className, ut);\r
100                                 return br;\r
101                         } else {\r
102                                 String className = "org.simantics.databoard.UnionType_"+sig;\r
103                                 String classSig = "Lorg/simantics/databoard/UnionType_"+sig+";";\r
104                                 BindingRequest br = new BindingRequest(this, className, classSig, classSig);\r
105                                 signatureCache.put(className, ut);\r
106                                 return br;\r
107                         }               \r
108                 }\r
109                 \r
110                 return null;\r
111         }\r
112 \r
113         @Override\r
114         protected synchronized Class<?> findClass(String className)\r
115         throws ClassNotFoundException \r
116         {\r
117                 Datatype type = signatureCache.get(className);\r
118                 if ( type == null ) {\r
119                         BindingRequest br = classFactory.getRepository().getRequest(className);\r
120                         if (br!=null && br.getClazz()!=null) return br.getClazz();\r
121                         return Class.forName(className);\r
122                 }\r
123 \r
124                 try {\r
125                         if ( type instanceof RecordType ) {\r
126                                 RecordType rt = (RecordType) type;\r
127                                 return createRecordClass( rt );\r
128                         }\r
129                         \r
130                         if ( type instanceof UnionType ) {\r
131                                 UnionType ut = (UnionType) type;\r
132                                 if (ut.isEnumeration()) {\r
133                                         return createEnumClass( ut );\r
134                                 } else {\r
135                                         return createUnionClass( ut );\r
136                                 }               \r
137                         }\r
138                         throw new ClassNotFoundException(className);\r
139                         \r
140                 } catch ( BindingConstructionException bce ) {\r
141                         throw new ClassNotFoundException(className, bce);\r
142                 }\r
143         }\r
144         \r
145         \r
146         public Class<?> createRecordClass( RecordType type ) throws BindingConstructionException\r
147         {\r
148                 ClassWriter cw = new ClassWriter(0);\r
149                 AnnotationVisitor av0;\r
150 \r
151                 // Create class name\r
152                 SignatureVisitor sv = new SignatureVisitor();\r
153                 type.accept( sv, null );\r
154                 String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode );\r
155                 \r
156                 String hash = Integer.toHexString( type.hashCode() );\r
157                 String className = "org.simantics.databoard.RecordType_"+sig;\r
158                 String classSig = "org/simantics/databoard/RecordType_"+sig;\r
159                 Class<?> superClass = makeBean ? Bean.class : Object.class;\r
160                 String superType = AsmBindingClassLoader.toClassCanonicalName( superClass );\r
161                 \r
162                 cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, classSig, null, superType, null);\r
163                 \r
164                 if ( type.isReferable() ) {\r
165                         av0 = cw.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Referable.class) , true);\r
166                         av0.visitEnd();                 \r
167                 }\r
168 \r
169                 for (int i=0; i<type.getComponentCount(); i++)\r
170                 {\r
171                         Component c = type.getComponent(i);\r
172                         Datatype fieldType = c.type;\r
173                         boolean isOptional = fieldType instanceof OptionalType;\r
174                         boolean isIdentifier = type.isIdentifier(i);\r
175                         if ( isOptional ) {\r
176                                 fieldType = ((OptionalType) fieldType).componentType;\r
177                         }\r
178 //                      boolean isArray = fieldType instanceof ArrayType;\r
179 //                      if ( isArray ) {\r
180 //                              fieldType = ((ArrayType) fieldType).componentType;\r
181 //                      }\r
182                         String fieldName = toJavaFieldName( c.name );\r
183                         BindingRequest br = classFactory.getClass( fieldType );\r
184                         \r
185                         \r
186                         FieldVisitor fv = cw.visitField(\r
187                                         ACC_PUBLIC, \r
188                                         fieldName, \r
189                                         br.signature,\r
190                                         br.descriptor.equals(br.signature)?null:br.descriptor, \r
191                                         null);\r
192                         \r
193                         // Visit annotations\r
194                         if ( br.annotations!=null && br.annotations.length>0 ) {\r
195                                 for (Annotation a : br.annotations) {\r
196                                         \r
197                                         if ( a instanceof Arguments ) {\r
198                                                 Arguments arg = (Arguments) a;\r
199                                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Arguments.class) , true);\r
200                                                 AnnotationVisitor av1 = av0.visitArray("value");\r
201                                                 for ( Class<?> clzz : arg.value() ) {\r
202                                                         av1.visit(null, Type.getType( AsmBindingClassLoader.toTypeDescriptor( clzz ) ));\r
203                                                 }\r
204                                                 av1.visitEnd();\r
205                                                 av0.visitEnd();\r
206                                         }\r
207                                         \r
208                                         isIdentifier |= a instanceof Identifier;\r
209                                         \r
210                                         if ( a instanceof Length ) {\r
211                                                 Length arg = (Length) a;\r
212                                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Length.class) , true);\r
213                                                 AnnotationVisitor av1 = av0.visitArray("value");\r
214                                                 for ( String s : arg.value()) av1.visit(null, s);\r
215                                                 av1.visitEnd();\r
216                                                 av0.visitEnd();\r
217                                         }\r
218                                         \r
219                                         if ( a instanceof MIMEType ) {\r
220                                                 MIMEType arg = (MIMEType) a;\r
221                                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(MIMEType.class), true);\r
222                                                 av0.visit("value", arg.value());\r
223                                                 av0.visitEnd();\r
224                                         }\r
225                                         \r
226                                         if ( a instanceof Name ) {\r
227                                                 Name arg = (Name) a;\r
228                                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Name.class), true);\r
229                                                 av0.visit("value", arg.value());\r
230                                                 av0.visitEnd();\r
231                                         }\r
232                                         \r
233                                         isOptional |= a instanceof Optional;\r
234 \r
235                                         if ( a instanceof org.simantics.databoard.annotations.Pattern ) {\r
236                                                 org.simantics.databoard.annotations.Pattern arg = (org.simantics.databoard.annotations.Pattern) a;\r
237                                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(org.simantics.databoard.annotations.Pattern.class), true);\r
238                                                 av0.visit("value", arg.value());\r
239                                                 av0.visitEnd();\r
240                                         }\r
241                                         \r
242                                         if ( a instanceof Range ) {\r
243                                                 Range arg = (Range) a;\r
244                                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Range.class), true);\r
245                                                 av0.visit("value", arg.value());\r
246                                                 av0.visitEnd();\r
247                                         }\r
248                                         \r
249                                         if ( a instanceof Unit ) {\r
250                                                 Unit arg = (Unit) a;\r
251                                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Unit.class), true);                                            \r
252                                                 av0.visit("value", arg.value());\r
253                                                 av0.visitEnd();\r
254                                         }\r
255                                 }\r
256                         }\r
257                         \r
258                         if ( isIdentifier ) {\r
259                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Identifier.class), true);\r
260                                 av0.visitEnd();\r
261                         } \r
262                         if ( isOptional ) {\r
263                                 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Optional.class), true);\r
264                                 av0.visitEnd();\r
265                         } \r
266                         \r
267                         fv.visitEnd();                  \r
268                 }\r
269                 \r
270                 // Constructor\r
271                 {\r
272                         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);\r
273                         mv.visitCode();\r
274                         Label l0 = new Label();\r
275                         \r
276                         // super()\r
277                         if (!makeBean) \r
278                         {\r
279                                 mv.visitLabel(l0);\r
280                                 mv.visitLineNumber(20, l0);\r
281                                 mv.visitVarInsn(ALOAD, 0);\r
282                                 mv.visitMethodInsn(INVOKESPECIAL, superType, "<init>", "()V");\r
283                         } else {                                        \r
284                                 // super( getStaticBinding() )\r
285                                 mv.visitLabel(l0);\r
286                                 mv.visitVarInsn(ALOAD, 0);\r
287                                 mv.visitMethodInsn(INVOKESTATIC, classSig, "getStaticBinding", "()Lorg/simantics/databoard/binding/Binding;");\r
288                                 mv.visitMethodInsn(INVOKESPECIAL, "org/simantics/databoard/util/Bean", "<init>", "(Lorg/simantics/databoard/binding/Binding;)V");\r
289                         }\r
290                         \r
291                         mv.visitInsn(RETURN);                                           \r
292                         Label l1 = new Label();\r
293                         mv.visitLabel(l1);\r
294                         mv.visitLocalVariable("this", "L"+classSig+";", null, l0, l1, 0);\r
295                         if (!makeBean) {\r
296                                 mv.visitMaxs(1, 1);\r
297                         } else {\r
298                                 mv.visitMaxs(2, 1);\r
299                         }\r
300                         mv.visitEnd();\r
301                 }\r
302 \r
303                 // BINDING\r
304                 {\r
305                         FieldVisitor fv = cw.visitField(ACC_STATIC, "BINDING", "Lorg/simantics/databoard/binding/Binding;", null, null);\r
306                         fv.visitEnd();\r
307                         \r
308                         MethodVisitor mv = cw.visitMethod(ACC_STATIC, "getStaticBinding", "()Lorg/simantics/databoard/binding/Binding;", null, null);\r
309                         mv.visitCode();\r
310                         Label l0 = new Label();\r
311                         mv.visitLabel(l0);\r
312                         mv.visitFieldInsn(GETSTATIC, classSig, "BINDING", "Lorg/simantics/databoard/binding/Binding;");\r
313                         Label l1 = new Label();\r
314                         mv.visitJumpInsn(IFNONNULL, l1);\r
315                         mv.visitLdcInsn(Type.getType("L"+classSig+";"));\r
316                         mv.visitMethodInsn(INVOKESTATIC, "org/simantics/databoard/Bindings", "getBindingUnchecked", "(Ljava/lang/Class;)Lorg/simantics/databoard/binding/Binding;");\r
317                         mv.visitFieldInsn(PUTSTATIC, classSig, "BINDING", "Lorg/simantics/databoard/binding/Binding;");\r
318                         mv.visitLabel(l1);\r
319                         mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);\r
320                         mv.visitFieldInsn(GETSTATIC, classSig, "BINDING", "Lorg/simantics/databoard/binding/Binding;");\r
321                         mv.visitInsn(ARETURN);\r
322                         mv.visitMaxs(1, 0);\r
323                         mv.visitEnd();                                          \r
324                 }\r
325                 \r
326                 cw.visitEnd();\r
327                 byte[] data = cw.toByteArray();\r
328                 Class<?> clazz = defineClass( className, data, 0, data.length );\r
329                 BindingRequest br = new BindingRequest(clazz);          \r
330                 return clazz;\r
331         }\r
332         \r
333         private static final Pattern validJavaName = Pattern.compile("[a-zA-Z][\\w]*");\r
334         \r
335         private static String toJavaFieldName( String name )\r
336         {\r
337                 if ( name.equals("") ) return "_";\r
338                 Matcher m = validJavaName.matcher(name);\r
339                 if ( m.matches() ) return name;\r
340                 StringBuilder sb = new StringBuilder(name.length());\r
341                 \r
342                 char fc = name.charAt( 0 );\r
343                 for (int i=0; i<name.length(); i++) {\r
344                         char c = name.charAt(i);\r
345                         boolean vc;\r
346                         if ( i == 0 ) {\r
347                                 vc = (fc>='a'&&fc<'z')||(fc>='A'&&fc<'Z');\r
348                         } else {\r
349                                 vc = (c>='a'&&c<'z')||(c>='A'&&c<'Z')||(c=='_')||(c>='0'&&fc<='9');\r
350                         }\r
351                         if ( vc ) sb.append('_'); else sb.append(c);\r
352                 }               \r
353                 return sb.toString();\r
354         }\r
355         \r
356         public Class<?> createEnumClass( UnionType ut )\r
357         {\r
358                 return null;\r
359         }\r
360         \r
361         public Class<?> createUnionClass( UnionType ut ) throws BindingConstructionException\r
362         {\r
363                 ClassWriter cw = new ClassWriter(0);\r
364 \r
365                 // Create class name\r
366                 SignatureVisitor sv = new SignatureVisitor();\r
367                 ut.accept( sv, null );\r
368                 String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode );\r
369                 \r
370                 String hash = Integer.toHexString( ut.hashCode() );\r
371                 String className = "org.simantics.databoard.UnionType_"+sig;\r
372                 String classSig = "org/simantics/databoard/UnionType_"+sig;\r
373                 Class<?> superClass = Object.class;\r
374                 String superType = AsmBindingClassLoader.toClassCanonicalName( superClass );\r
375                 \r
376                 cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, classSig, null, superType, null);\r
377                 \r
378                 AnnotationVisitor av0 = cw.visitAnnotation(AsmBindingClassLoader.toTypeDescriptor(Union.class), true);\r
379                 AnnotationVisitor av1 = av0.visitArray("value");\r
380                 for (Component c : ut.components) {\r
381                         BindingRequest br = classFactory.getClass(c.type);\r
382                         av1.visit(null, Type.getType(br.descriptor));\r
383                 }\r
384                 av1.visitEnd();\r
385                 av0.visitEnd();\r
386                 \r
387                 // Constructor\r
388                 {\r
389                         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);\r
390                         mv.visitCode();\r
391                         Label l0 = new Label();\r
392                         mv.visitLabel(l0);\r
393                         mv.visitLineNumber(20, l0);\r
394                         mv.visitVarInsn(ALOAD, 0);\r
395                         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");\r
396                         mv.visitInsn(RETURN);\r
397                         Label l1 = new Label();\r
398                         mv.visitLabel(l1);\r
399                         mv.visitLocalVariable("this", "L"+classSig+";", null, l0, l1, 0);\r
400                         mv.visitMaxs(1, 1);\r
401                         mv.visitEnd();\r
402                 }               \r
403                 \r
404                 cw.visitEnd();\r
405                 byte[] data = cw.toByteArray();\r
406                 return defineClass( className, data, 0, data.length );\r
407         }       \r
408         \r
409 }\r