1 /*******************************************************************************
2 * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.binding.classfactory;
14 import java.lang.annotation.Annotation;
15 import java.util.HashMap;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
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;
48 * This class loader constructs record-like classes of RecordTypes, and
49 * Enums / other classes of UnionTypes.
51 public class AsmTypeClassFactory extends ClassLoader implements Opcodes, TypeClassSubFactory {
53 // if true, the record classes are Beans.
54 boolean makeBean = true;
55 TypeClassFactory classFactory;
57 Map<String, Datatype> signatureCache = new HashMap<String, Datatype>();
59 public AsmTypeClassFactory(TypeClassFactory classFactory) {
60 super( AsmTypeClassFactory.class.getClassLoader() );
61 this.classFactory = classFactory;
65 * If true, the manufactured classes inherit Bean.
68 public void setBeanMaker(boolean makeBean) {
69 this.makeBean = makeBean;
73 public BindingRequest construct(TypeClassFactory mainFactory, Datatype type)
74 throws BindingConstructionException
76 if ( type instanceof RecordType ) {
77 RecordType rt = (RecordType) type;
79 SignatureVisitor sv = new SignatureVisitor();
80 type.accept( sv, null );
81 String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode );
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);
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);
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);
114 protected synchronized Class<?> findClass(String className)
115 throws ClassNotFoundException
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);
125 if ( type instanceof RecordType ) {
126 RecordType rt = (RecordType) type;
127 return createRecordClass( rt );
130 if ( type instanceof UnionType ) {
131 UnionType ut = (UnionType) type;
132 if (ut.isEnumeration()) {
133 return createEnumClass( ut );
135 return createUnionClass( ut );
138 throw new ClassNotFoundException(className);
140 } catch ( BindingConstructionException bce ) {
141 throw new ClassNotFoundException(className, bce);
146 public Class<?> createRecordClass( RecordType type ) throws BindingConstructionException
148 ClassWriter cw = new ClassWriter(0);
149 AnnotationVisitor av0;
152 SignatureVisitor sv = new SignatureVisitor();
153 type.accept( sv, null );
154 String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode );
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 );
162 cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, classSig, null, superType, null);
164 if ( type.isReferable() ) {
165 av0 = cw.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Referable.class) , true);
169 for (int i=0; i<type.getComponentCount(); i++)
171 Component c = type.getComponent(i);
172 Datatype fieldType = c.type;
173 boolean isOptional = fieldType instanceof OptionalType;
174 boolean isIdentifier = type.isIdentifier(i);
176 fieldType = ((OptionalType) fieldType).componentType;
178 // boolean isArray = fieldType instanceof ArrayType;
180 // fieldType = ((ArrayType) fieldType).componentType;
182 String fieldName = toJavaFieldName( c.name );
183 BindingRequest br = classFactory.getClass( fieldType );
186 FieldVisitor fv = cw.visitField(
190 br.descriptor.equals(br.signature)?null:br.descriptor,
194 if ( br.annotations!=null && br.annotations.length>0 ) {
195 for (Annotation a : br.annotations) {
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 ) ));
208 isIdentifier |= a instanceof Identifier;
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);
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());
226 if ( a instanceof Name ) {
228 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Name.class), true);
229 av0.visit("value", arg.value());
233 isOptional |= a instanceof Optional;
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());
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());
249 if ( a instanceof Unit ) {
251 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Unit.class), true);
252 av0.visit("value", arg.value());
258 if ( isIdentifier ) {
259 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Identifier.class), true);
263 av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Optional.class), true);
272 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
274 Label l0 = new Label();
280 mv.visitLineNumber(20, l0);
281 mv.visitVarInsn(ALOAD, 0);
282 mv.visitMethodInsn(INVOKESPECIAL, superType, "<init>", "()V");
284 // super( getStaticBinding() )
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");
291 mv.visitInsn(RETURN);
292 Label l1 = new Label();
294 mv.visitLocalVariable("this", "L"+classSig+";", null, l0, l1, 0);
305 FieldVisitor fv = cw.visitField(ACC_STATIC, "BINDING", "Lorg/simantics/databoard/binding/Binding;", null, null);
308 MethodVisitor mv = cw.visitMethod(ACC_STATIC, "getStaticBinding", "()Lorg/simantics/databoard/binding/Binding;", null, null);
310 Label l0 = new Label();
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;");
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);
327 byte[] data = cw.toByteArray();
328 Class<?> clazz = defineClass( className, data, 0, data.length );
329 BindingRequest br = new BindingRequest(clazz);
333 private static final Pattern validJavaName = Pattern.compile("[a-zA-Z][\\w]*");
335 private static String toJavaFieldName( String name )
337 if ( name.equals("") ) return "_";
338 Matcher m = validJavaName.matcher(name);
339 if ( m.matches() ) return name;
340 StringBuilder sb = new StringBuilder(name.length());
342 char fc = name.charAt( 0 );
343 for (int i=0; i<name.length(); i++) {
344 char c = name.charAt(i);
347 vc = (fc>='a'&&fc<'z')||(fc>='A'&&fc<'Z');
349 vc = (c>='a'&&c<'z')||(c>='A'&&c<'Z')||(c=='_')||(c>='0'&&fc<='9');
351 if ( vc ) sb.append('_'); else sb.append(c);
353 return sb.toString();
356 public Class<?> createEnumClass( UnionType ut )
361 public Class<?> createUnionClass( UnionType ut ) throws BindingConstructionException
363 ClassWriter cw = new ClassWriter(0);
366 SignatureVisitor sv = new SignatureVisitor();
367 ut.accept( sv, null );
368 String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode );
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 );
376 cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, classSig, null, superType, null);
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));
389 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
391 Label l0 = new Label();
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();
399 mv.visitLocalVariable("this", "L"+classSig+";", null, l0, l1, 0);
405 byte[] data = cw.toByteArray();
406 return defineClass( className, data, 0, data.length );