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