/******************************************************************************* * Copyright (c) 2007, 2011 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.binding.classfactory; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.simantics.databoard.annotations.Arguments; import org.simantics.databoard.annotations.Identifier; import org.simantics.databoard.annotations.Length; import org.simantics.databoard.annotations.MIMEType; import org.simantics.databoard.annotations.Name; import org.simantics.databoard.annotations.Optional; import org.simantics.databoard.annotations.Range; import org.simantics.databoard.annotations.Referable; import org.simantics.databoard.annotations.Union; import org.simantics.databoard.annotations.Unit; import org.simantics.databoard.binding.error.BindingConstructionException; import org.simantics.databoard.binding.reflection.AsmBindingClassLoader; import org.simantics.databoard.binding.reflection.BindingRequest; import org.simantics.databoard.type.Component; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.OptionalType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.util.Bean; /** * This class loader constructs record-like classes of RecordTypes, and * Enums / other classes of UnionTypes. */ public class AsmTypeClassFactory extends ClassLoader implements Opcodes, TypeClassSubFactory { // if true, the record classes are Beans. boolean makeBean = true; TypeClassFactory classFactory; Map signatureCache = new HashMap(); public AsmTypeClassFactory(TypeClassFactory classFactory) { super( AsmTypeClassFactory.class.getClassLoader() ); this.classFactory = classFactory; } /** * If true, the manufactured classes inherit Bean. * @param makeBean */ public void setBeanMaker(boolean makeBean) { this.makeBean = makeBean; } @Override public BindingRequest construct(TypeClassFactory mainFactory, Datatype type) throws BindingConstructionException { if ( type instanceof RecordType ) { RecordType rt = (RecordType) type; SignatureVisitor sv = new SignatureVisitor(); type.accept( sv, null ); String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode ); String className = "org.simantics.databoard.RecordType_"+sig; String classSig = "Lorg/simantics/databoard/RecordType_"+sig+";"; BindingRequest br = new BindingRequest(this, className, classSig, classSig); signatureCache.put(className, rt); return br; } if ( type instanceof UnionType ) { UnionType ut = (UnionType) type; SignatureVisitor sv = new SignatureVisitor(); type.accept( sv, null ); String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode ); if (ut.isEnumeration()) { String className = "org.simantics.databoard.EnumType_"+sig; String classSig = "Lorg/simantics/databoard/EnumType_"+sig+";"; BindingRequest br = new BindingRequest(this, className, classSig, classSig); signatureCache.put(className, ut); return br; } else { String className = "org.simantics.databoard.UnionType_"+sig; String classSig = "Lorg/simantics/databoard/UnionType_"+sig+";"; BindingRequest br = new BindingRequest(this, className, classSig, classSig); signatureCache.put(className, ut); return br; } } return null; } @Override protected synchronized Class findClass(String className) throws ClassNotFoundException { Datatype type = signatureCache.get(className); if ( type == null ) { BindingRequest br = classFactory.getRepository().getRequest(className); if (br!=null && br.getClazz()!=null) return br.getClazz(); return Class.forName(className); } try { if ( type instanceof RecordType ) { RecordType rt = (RecordType) type; return createRecordClass( rt ); } if ( type instanceof UnionType ) { UnionType ut = (UnionType) type; if (ut.isEnumeration()) { return createEnumClass( ut ); } else { return createUnionClass( ut ); } } throw new ClassNotFoundException(className); } catch ( BindingConstructionException bce ) { throw new ClassNotFoundException(className, bce); } } public Class createRecordClass( RecordType type ) throws BindingConstructionException { ClassWriter cw = new ClassWriter(0); AnnotationVisitor av0; // Create class name SignatureVisitor sv = new SignatureVisitor(); type.accept( sv, null ); String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode ); String hash = Integer.toHexString( type.hashCode() ); String className = "org.simantics.databoard.RecordType_"+sig; String classSig = "org/simantics/databoard/RecordType_"+sig; Class superClass = makeBean ? Bean.class : Object.class; String superType = AsmBindingClassLoader.toClassCanonicalName( superClass ); cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, classSig, null, superType, null); if ( type.isReferable() ) { av0 = cw.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Referable.class) , true); av0.visitEnd(); } for (int i=0; i0 ) { for (Annotation a : br.annotations) { if ( a instanceof Arguments ) { Arguments arg = (Arguments) a; av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Arguments.class) , true); AnnotationVisitor av1 = av0.visitArray("value"); for ( Class clzz : arg.value() ) { av1.visit(null, Type.getType( AsmBindingClassLoader.toTypeDescriptor( clzz ) )); } av1.visitEnd(); av0.visitEnd(); } isIdentifier |= a instanceof Identifier; if ( a instanceof Length ) { Length arg = (Length) a; av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Length.class) , true); AnnotationVisitor av1 = av0.visitArray("value"); for ( String s : arg.value()) av1.visit(null, s); av1.visitEnd(); av0.visitEnd(); } if ( a instanceof MIMEType ) { MIMEType arg = (MIMEType) a; av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(MIMEType.class), true); av0.visit("value", arg.value()); av0.visitEnd(); } if ( a instanceof Name ) { Name arg = (Name) a; av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Name.class), true); av0.visit("value", arg.value()); av0.visitEnd(); } isOptional |= a instanceof Optional; if ( a instanceof org.simantics.databoard.annotations.Pattern ) { org.simantics.databoard.annotations.Pattern arg = (org.simantics.databoard.annotations.Pattern) a; av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(org.simantics.databoard.annotations.Pattern.class), true); av0.visit("value", arg.value()); av0.visitEnd(); } if ( a instanceof Range ) { Range arg = (Range) a; av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Range.class), true); av0.visit("value", arg.value()); av0.visitEnd(); } if ( a instanceof Unit ) { Unit arg = (Unit) a; av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Unit.class), true); av0.visit("value", arg.value()); av0.visitEnd(); } } } if ( isIdentifier ) { av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Identifier.class), true); av0.visitEnd(); } if ( isOptional ) { av0 = fv.visitAnnotation( AsmBindingClassLoader.toTypeDescriptor(Optional.class), true); av0.visitEnd(); } fv.visitEnd(); } // Constructor { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); Label l0 = new Label(); // super() if (!makeBean) { mv.visitLabel(l0); mv.visitLineNumber(20, l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, superType, "", "()V"); } else { // super( getStaticBinding() ) mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, classSig, "getStaticBinding", "()Lorg/simantics/databoard/binding/Binding;"); mv.visitMethodInsn(INVOKESPECIAL, "org/simantics/databoard/util/Bean", "", "(Lorg/simantics/databoard/binding/Binding;)V"); } mv.visitInsn(RETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", "L"+classSig+";", null, l0, l1, 0); if (!makeBean) { mv.visitMaxs(1, 1); } else { mv.visitMaxs(2, 1); } mv.visitEnd(); } // BINDING { FieldVisitor fv = cw.visitField(ACC_STATIC, "BINDING", "Lorg/simantics/databoard/binding/Binding;", null, null); fv.visitEnd(); MethodVisitor mv = cw.visitMethod(ACC_STATIC, "getStaticBinding", "()Lorg/simantics/databoard/binding/Binding;", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitFieldInsn(GETSTATIC, classSig, "BINDING", "Lorg/simantics/databoard/binding/Binding;"); Label l1 = new Label(); mv.visitJumpInsn(IFNONNULL, l1); mv.visitLdcInsn(Type.getType("L"+classSig+";")); mv.visitMethodInsn(INVOKESTATIC, "org/simantics/databoard/Bindings", "getBindingUnchecked", "(Ljava/lang/Class;)Lorg/simantics/databoard/binding/Binding;"); mv.visitFieldInsn(PUTSTATIC, classSig, "BINDING", "Lorg/simantics/databoard/binding/Binding;"); mv.visitLabel(l1); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitFieldInsn(GETSTATIC, classSig, "BINDING", "Lorg/simantics/databoard/binding/Binding;"); mv.visitInsn(ARETURN); mv.visitMaxs(1, 0); mv.visitEnd(); } cw.visitEnd(); byte[] data = cw.toByteArray(); Class clazz = defineClass( className, data, 0, data.length ); BindingRequest br = new BindingRequest(clazz); return clazz; } private static final Pattern validJavaName = Pattern.compile("[a-zA-Z][\\w]*"); private static String toJavaFieldName( String name ) { if ( name.equals("") ) return "_"; Matcher m = validJavaName.matcher(name); if ( m.matches() ) return name; StringBuilder sb = new StringBuilder(name.length()); char fc = name.charAt( 0 ); for (int i=0; i='a'&&fc<'z')||(fc>='A'&&fc<'Z'); } else { vc = (c>='a'&&c<'z')||(c>='A'&&c<'Z')||(c=='_')||(c>='0'&&fc<='9'); } if ( vc ) sb.append('_'); else sb.append(c); } return sb.toString(); } public Class createEnumClass( UnionType ut ) { return null; } public Class createUnionClass( UnionType ut ) throws BindingConstructionException { ClassWriter cw = new ClassWriter(0); // Create class name SignatureVisitor sv = new SignatureVisitor(); ut.accept( sv, null ); String sig = sv.sb.toString()+"_"+Integer.toHexString( sv.hashcode ); String hash = Integer.toHexString( ut.hashCode() ); String className = "org.simantics.databoard.UnionType_"+sig; String classSig = "org/simantics/databoard/UnionType_"+sig; Class superClass = Object.class; String superType = AsmBindingClassLoader.toClassCanonicalName( superClass ); cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, classSig, null, superType, null); AnnotationVisitor av0 = cw.visitAnnotation(AsmBindingClassLoader.toTypeDescriptor(Union.class), true); AnnotationVisitor av1 = av0.visitArray("value"); for (Component c : ut.components) { BindingRequest br = classFactory.getClass(c.type); av1.visit(null, Type.getType(br.descriptor)); } av1.visitEnd(); av0.visitEnd(); // Constructor { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(20, l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V"); mv.visitInsn(RETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", "L"+classSig+";", null, l0, l1, 0); mv.visitMaxs(1, 1); mv.visitEnd(); } cw.visitEnd(); byte[] data = cw.toByteArray(); return defineClass( className, data, 0, data.length ); } }