View Javadoc
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 }