package act.inject.param;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import act.asm.*;
import act.inject.param.JsonDTOClassManager.DynamicClassLoader;
import org.osgl.$;
import org.osgl.inject.BeanSpec;
import org.osgl.util.FastStr;
import org.osgl.util.S;
import java.util.List;
class JsonDTOClassGenerator implements Opcodes {
private static final String JSON_DTO_CLASS = "act/inject/param/JsonDTO";
private String className;
private List<BeanSpec> beanSpecs;
private DynamicClassLoader dynamicClassLoader;
private ClassWriter cw;
private MethodVisitor mv;
JsonDTOClassGenerator(String name, List<BeanSpec> list, DynamicClassLoader classLoader) {
this.className = name;
this.beanSpecs = list;
this.dynamicClassLoader = classLoader;
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
}
Class<? extends JsonDTO> generate() {
return $.cast(dynamicClassLoader.defineClass(className, generateByteCode()));
}
byte[] generateByteCode() {
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, className, null, JSON_DTO_CLASS, null);
generateConstructor();
generateSetters();
return cw.toByteArray();
}
private void generateConstructor() {
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "act/inject/param/JsonDTO", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
private void generateSetters() {
for (BeanSpec beanSpec : beanSpecs) {
generateSetter(beanSpec);
}
}
private void generateSetter(BeanSpec beanSpec) {
String setterName = setterName(beanSpec);
mv = cw.visitMethod(ACC_PUBLIC, setterName, setterDescriptor(beanSpec), setterSignature(beanSpec), null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(beanSpec.name());
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, className, "set", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLocalVariable("this", S.fmt("L%s;", className), null, l0, l2, 0);
mv.visitLocalVariable("v", classDesc(beanSpec.rawType()), null, l0, l2, 1);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
private static String setterDescriptor(BeanSpec spec) {
return S.fmt("(%s)V", classDesc(spec.rawType()));
}
private static String setterSignature(BeanSpec spec) {
return S.fmt("(%s)V", typeDesc(spec));
}
private static String typeDesc(BeanSpec spec) {
String root = classDesc(spec.rawType());
List<java.lang.reflect.Type> typeParams = spec.typeParams();
if (typeParams.isEmpty()) {
return root;
}
S.Buffer sb = S.newBuffer("<");
for (java.lang.reflect.Type type : typeParams) {
BeanSpec specx = BeanSpec.of(type, null, spec.injector());
sb.append(typeDesc(specx));
}
sb.append(">");
FastStr str = FastStr.of(root);
str = str.take(str.length() - 1).append(sb.toString()).append(";");
return str.toString();
}
private static String classDesc(Class c) {
if (c.isPrimitive()) {
c = $.wrapperClassOf(c);
}
return Type.getDescriptor(c);
}
private static String setterName(BeanSpec beanSpec) {
return S.concat("set", S.capFirst(beanSpec.name()));
}
}