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 }