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