1 package org.pojomatic.internal.factory; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.InputStream; 5 import java.lang.annotation.Annotation; 6 import java.util.HashMap; 7 import java.util.HashSet; 8 import java.util.Map; 9 import java.util.Set; 10 11 import org.objectweb.asm.AnnotationVisitor; 12 import org.objectweb.asm.ClassWriter; 13 import org.objectweb.asm.FieldVisitor; 14 import org.objectweb.asm.Label; 15 import org.objectweb.asm.MethodVisitor; 16 import org.objectweb.asm.Type; 17 import org.pojomatic.annotations.AutoDetectPolicy; 18 import org.pojomatic.annotations.AutoProperty; 19 import org.pojomatic.annotations.Property; 20 21 import static org.objectweb.asm.Opcodes.*; 22 23 public class PojoClassFactory { 24 25 private static final class DynamicClassLoader extends ClassLoader { 26 private final static Map<String, byte[]> classes= new HashMap<>(); 27 private DynamicClassLoader(ClassLoader parent) { 28 super(parent); 29 } 30 31 Class<?> loadClass(String name, byte[] classBytes) { 32 classes.put(name.replace('.', '/') + ".class", classBytes.clone()); 33 return defineClass(name, classBytes, 0, classBytes.length); 34 } 35 36 @Override 37 public InputStream getResourceAsStream(String name) { 38 if (classes.containsKey(name)) { 39 return new ByteArrayInputStream(classes.get(name)); 40 } 41 return super.getResourceAsStream(name); 42 } 43 44 void definePackage(String packageName) { 45 definePackage(packageName, null, null, null, null, null, null, null); 46 } 47 } 48 49 private final DynamicClassLoader classLoader = new DynamicClassLoader( 50 PojoClassFactory.class.getClassLoader()); 51 52 private final Set<String> definedPackageNames = new HashSet<>(); 53 54 public Class<?> generateClass(PojoDescriptor pojoDescriptor) { 55 if (definedPackageNames.add(pojoDescriptor.packageName)) { 56 classLoader.definePackage(pojoDescriptor.packageName); 57 } 58 return classLoader.loadClass(pojoDescriptor.qualifiedName(), generateClassBytes(pojoDescriptor)); 59 } 60 61 private byte[] generateClassBytes(PojoDescriptor pojoDescriptor) { 62 ClassWriter cw = new ClassWriter(0); 63 64 cw.visit( 65 V1_7, 66 pojoDescriptor.access.getCode() | ACC_SUPER, 67 pojoDescriptor.internalName(), 68 null, 69 pojoDescriptor.parentInternalName(), 70 null); 71 72 if (pojoDescriptor.autoDetectPolicy != null) { 73 AnnotationVisitor annotationVisitor = cw.visitAnnotation(Type.getDescriptor(AutoProperty.class), true); 74 annotationVisitor.visitEnum( 75 "autoDetect", Type.getDescriptor(AutoDetectPolicy.class), pojoDescriptor.autoDetectPolicy.name()); 76 } 77 generateConstructor(cw, pojoDescriptor); 78 for (PropertyDescriptor property : pojoDescriptor.properties) { 79 generateProperty(cw, property); 80 } 81 return cw.toByteArray(); 82 } 83 84 private void generateConstructor(ClassWriter cw, PojoDescriptor pojoDescriptor) { 85 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 86 mv.visitCode(); 87 Label l0 = new Label(); 88 mv.visitLabel(l0); 89 mv.visitVarInsn(ALOAD, 0); 90 mv.visitMethodInsn(INVOKESPECIAL, pojoDescriptor.parentInternalName(), "<init>", "()V", false); 91 mv.visitInsn(RETURN); 92 Label l1 = new Label(); 93 mv.visitLabel(l1); 94 mv.visitLocalVariable( 95 "this", 96 "L" + pojoDescriptor.internalName() + ";", 97 null, 98 l0, 99 l1, 100 0); 101 mv.visitMaxs(1, 1); 102 mv.visitEnd(); 103 } 104 105 private void generateProperty(ClassWriter cw, PropertyDescriptor property) { 106 if (property.isMethod) { 107 generatePropertyMethod(cw, property); 108 } 109 else { 110 generatePropertyField(cw, property); 111 } 112 } 113 114 private void generatePropertyField(ClassWriter cw, PropertyDescriptor property) { 115 FieldVisitor fv = cw.visitField( 116 property.getFlags(), 117 property.name, 118 Type.getDescriptor(property.type), 119 null, 120 null); 121 fv.visitAnnotation(Type.getDescriptor(Property.class), true).visitEnd(); 122 for (Class<? extends Annotation> annotationClass: property.annotations) { 123 fv.visitAnnotation(Type.getDescriptor(annotationClass), true).visitEnd(); 124 } 125 fv.visitEnd(); 126 } 127 128 public Object getX() { 129 return null; 130 } 131 132 private void generatePropertyMethod(ClassWriter cw, PropertyDescriptor property) { 133 if (property.type.isPrimitive()) { 134 throw new UnsupportedOperationException("Cannot generate methods returning primitives"); 135 } 136 MethodVisitor mv = cw.visitMethod( 137 property.getFlags(), 138 property.name, 139 Type.getMethodDescriptor(Type.getType(property.type)), 140 null, 141 null); 142 mv.visitAnnotation(Type.getDescriptor(Property.class), true).visitEnd(); 143 for (Class<? extends Annotation> annotationClass: property.annotations) { 144 mv.visitAnnotation(Type.getDescriptor(annotationClass), true).visitEnd(); 145 } 146 mv.visitCode(); 147 Label l0 = new Label(); 148 mv.visitLabel(l0); 149 mv.visitInsn(ACONST_NULL); 150 mv.visitInsn(ARETURN); 151 Label l1 = new Label(); 152 mv.visitLabel(l1); 153 mv.visitLocalVariable("this", "Lorg/pojomatic/internal/factory/PojoClassFactory;", null, l0, l1, 0); 154 mv.visitMaxs(1, 1); 155 mv.visitEnd(); 156 } 157 }