Java tutorial
/* * Copyright 2010-2013 the original author or authors. * * 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. */ package gemlite.maven.plugin.support.mapper; import gemlite.core.internal.asm.AsmHelper; import gemlite.core.internal.support.context.DomainMapperHelper; import gemlite.maven.plugin.support.ClassProcessor; import gemlite.maven.plugin.support.DomainMojoConstant; import gemlite.maven.plugin.support.DomainMojoHelper; import gemlite.maven.plugin.support.ProcessResult; import gemlite.maven.plugin.support.mapper.MapperToolRegistry.Item1; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.TypeInsnNode; import org.objectweb.asm.tree.VarInsnNode; /*** * * @author ynd ?domain??IMapperTool * 1.??= domain.name + "$$IMapperToolImpl" * gemlite.sample.domain.Order$$IMapperToolImpl * 2.??? */ @SuppressWarnings("unchecked") public class MapperToolProcessor implements ClassProcessor, Opcodes, DomainMojoConstant { private CreateMapperRegister mapperRegister = new CreateMapperRegister(); public final static String INTERFACE_NAME = "gemlite/core/internal/domain/IMapperTool"; @Override public ProcessResult process(File originFile, byte[] bytes, ClassNode domain) { ProcessResult result = new ProcessResult(); // ? try { ClassNode mapper = createIMapperToolClass(domain); String mapperClassName = mapper.name.replaceAll("\\/", "\\."); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Domain:" + domain.name + ", create mapper class:" + mapper.name); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); mapper.accept(cw); byte[] bt = cw.toByteArray(); writeClassFile(originFile, bt); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug(domain.name + " write file done."); result.node = mapper; result.success = true; result.newBytes = bt; // create domain mapper register mapperRegister.getIDomainRegistryClass(domain); mapperRegister.addRegistryItem(domain, mapperClassName); } catch (Exception e) { DomainMojoHelper.log().error(domain.name + " " + originFile.toString(), e); result.success = false; } return result; } private void writeClassFile(File originFile, byte[] processedBytes) throws IOException { // "D:/springsource/gemlite_prod/ynd.test.domain/target/classes/ynd/test/Ac01.class" // "D:/springsource/gemlite_prod/ynd.test.domain/target/classes/ynd/test/Ac01$$$IMapperToolImpl.class" String fname = originFile.getAbsolutePath(); fname = fname.replace(".class", DomainMapperHelper.DEFAULT_SUBFFIX + ".class"); File mapperFile = new File(fname); mapperFile.delete(); BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(mapperFile)); bo.write(processedBytes); bo.close(); } /*** * @param domain * @return * @throws IOException */ private ClassNode createIMapperToolClass(ClassNode domain) throws IOException { ClassNode mapper = new ClassNode(); mapper.version = V1_7; mapper.access = ACC_PUBLIC + ACC_SUPER; mapper.name = domain.name + DomainMapperHelper.DEFAULT_SUBFFIX; mapper.superName = "java/lang/Object"; mapper.interfaces.add(INTERFACE_NAME); addConstructFunction(domain, mapper); String keyDesc = implementInterface(domain, mapper); String name = AsmHelper.toFullName(keyDesc); mapper.signature = "Ljava/lang/Object;Lgemlite/core/internal/domain/IMapperTool<" + name + "L" + domain.name + ";>;"; return mapper; } private void addConstructFunction(ClassNode domain, ClassNode mapper) { MethodNode mn = new MethodNode(ACC_PUBLIC, "<init>", "()V", null, null); InsnList insn = mn.instructions; insn.add(new VarInsnNode(ALOAD, 0)); insn.add(AsmHelper.newMethodInsnNode(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)); insn.add(new InsnNode(RETURN)); mapper.methods.add(mn); } private ClassNode findKeyClass(ClassNode domain) throws IOException { ClassNode key = null; if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log() .debug("Traverse domain annotations to find if there exists key class, domain:" + domain.name); for (Object o : domain.visibleAnnotations) { AnnotationNode an = (AnnotationNode) o; if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Annotation:" + an.desc + " value:" + an.values); if (AN_Key.equals(an.desc)) { Type t = (Type) an.values.get(1); InputStream keyClassIn = DomainMojoHelper.getLoader() .getResourceAsStream(t.getInternalName() + ".class"); byte[] keyClassBytes = new byte[keyClassIn.available()]; keyClassIn.read(keyClassBytes); keyClassIn.close(); ClassReader cr = new ClassReader(keyClassBytes); key = new ClassNode(); cr.accept(key, 0); } } return key; } private boolean isKeyField(FieldNode fn) { if (fn.visibleAnnotations != null) { for (Object o : fn.visibleAnnotations) { AnnotationNode an = (AnnotationNode) o; if (AN_Key.equals(an.desc)) { if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Found key field:" + fn.name + "," + fn.desc); return true; } } } return false; } /** * 1.Domainannotation @Key * 2.??field,@Key??field.name->field.set/get method * 3.domain->@Key,?keyClass * * @param domain * @param mapper * @throws IOException */ private String implementInterface(ClassNode domain, ClassNode mapper) throws IOException { String keyDesc = ""; ClassNode key = findKeyClass(domain); addMapperValueMethod(domain, mapper); FieldNode fnKey = addMergeValueMethod(domain, mapper, key == null); if (key != null) { addValue2KeyComplex(domain, mapper, key); addMapperKeyComplex(domain, mapper, key); keyDesc = "L" + key.name + ";"; } else if (fnKey != null) { addValue2KeySimple(domain, mapper, fnKey); addMapperKeySimple(mapper, fnKey); keyDesc = fnKey.desc; } else throw new RuntimeException("Domain:" + domain.name + " not define key class or key property!"); addFieldNames(domain, mapper, key, fnKey); addKeyValueTypeMethod(domain, mapper, keyDesc); addBridgeMethod(domain, mapper, keyDesc); return keyDesc; } private void addMapperValueMethod(ClassNode domain, ClassNode mapper) { if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperValue(IDataSource)V method..."); MethodNode mn = new MethodNode(ACC_PUBLIC, "mapperValue", "(Lgemlite/core/internal/domain/IDataSource;)L" + domain.name + ";", null, null); InsnList insn = mn.instructions; insn.add(new TypeInsnNode(NEW, domain.name)); insn.add(new InsnNode(DUP)); insn.add(AsmHelper.newMethodInsnNode(INVOKESPECIAL, domain.name, "<init>", "()V", false)); insn.add(new VarInsnNode(ASTORE, 2)); insn.add(new VarInsnNode(ALOAD, 0)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(new VarInsnNode(ALOAD, 2)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, mapper.name, "mapperValue", "(Lgemlite/core/internal/domain/IDataSource;L" + domain.name + ";)L" + domain.name + ";", false)); insn.add(new InsnNode(POP)); insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperValue(IDataSource)V method done."); } private FieldNode addMergeValueMethod(ClassNode domain, ClassNode mapper, boolean findKey) { if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperValue(IDataSource,V)V method..."); MethodNode mn = new MethodNode(ACC_PUBLIC, "mapperValue", "(Lgemlite/core/internal/domain/IDataSource;L" + domain.name + ";)L" + domain.name + ";", null, null); InsnList insn = mn.instructions; FieldNode fnKey = null; for (Object o : domain.fields) { FieldNode fn = (FieldNode) o; if (!DomainMojoHelper.isValidField(fn)) continue; if (findKey && isKeyField(fn)) fnKey = fn; Item1 mti = MapperToolRegistry.getDataSItem(fn.desc); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("desc:" + fn.desc + " item:" + mti); String s1 = fn.name.substring(0, 1); String s2 = fn.name.substring(1); String setMethod = "set" + s1.toUpperCase() + s2; // field start insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(new LdcInsnNode(fn.name));// aab001 insn.add(AsmHelper.newMethodInsnNode(INVOKEINTERFACE, "gemlite/core/internal/domain/IDataSource", mti.getMethod, "(Ljava/lang/String;)" + mti.desc, true)); // Integer prop; if (AsmHelper.needTypeConvert(mti.desc, fn.desc)) AsmHelper.addTypeConvert(insn, mti.desc); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, domain.name, setMethod, "(" + fn.desc + ")V", false)); // field end } insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperValue(IDataSource,V)V method done."); return fnKey; } private void addBridgeMethod(ClassNode domain, ClassNode mapper, String keyClassDesc) { // MethodNode mn = new MethodNode(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "mapperValue", "(Lgemlite/core/internal/domain/IDataSource;Ljava/lang/Object;)Ljava/lang/Object;", null, null); InsnList insn = mn.instructions; insn.add(new VarInsnNode(ALOAD, 0)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new TypeInsnNode(CHECKCAST, domain.name)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, mapper.name, "mapperValue", "(Lgemlite/core/internal/domain/IDataSource;L" + domain.name + ";)L" + domain.name + ";", false)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); // mn = new MethodNode(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "mapperValue", "(Lgemlite/core/internal/domain/IDataSource;)Ljava/lang/Object;", null, null); insn = mn.instructions; insn.add(new VarInsnNode(ALOAD, 0)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, mapper.name, "mapperValue", "(Lgemlite/core/internal/domain/IDataSource;)L" + domain.name + ";", false)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); // mn = new MethodNode(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "value2Key", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); insn = mn.instructions; insn.add(new VarInsnNode(ALOAD, 0)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(new TypeInsnNode(CHECKCAST, domain.name)); keyClassDesc = AsmHelper.toFullName(keyClassDesc); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, mapper.name, "value2Key", "(L" + domain.name + ";)" + keyClassDesc + "", false)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); // mn = new MethodNode(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "mapperKey", "(Lgemlite/core/internal/domain/IDataSource;)Ljava/lang/Object;", null, null); insn = mn.instructions; insn.add(new VarInsnNode(ALOAD, 0)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, mapper.name, "mapperKey", "(Lgemlite/core/internal/domain/IDataSource;)" + keyClassDesc + "", false)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); } private void addValue2KeyComplex(ClassNode domain, ClassNode mapper, ClassNode key) { if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add value2key(V)K method, key:" + key.name); MethodNode mn = new MethodNode(ACC_PUBLIC, "value2Key", "(L" + domain.name + ";)L" + key.name + ";", null, null); InsnList insn = mn.instructions; insn.add(new TypeInsnNode(NEW, key.name)); insn.add(new InsnNode(DUP)); insn.add(AsmHelper.newMethodInsnNode(INVOKESPECIAL, key.name, "<init>", "()V", false)); insn.add(new VarInsnNode(ASTORE, 2)); for (Object o : key.fields) { FieldNode fn = (FieldNode) o; String s1 = fn.name.substring(0, 1); String s2 = fn.name.substring(1); String setMethod = "set" + s1.toUpperCase() + s2; String getMethod = "get" + s1.toUpperCase() + s2; // field start insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, domain.name, getMethod, "()" + fn.desc, false)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, key.name, setMethod, "(" + fn.desc + ")V", false)); // field end } insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add value2key(V)K method, key:" + key.name + " done."); } private void addMapperKeyComplex(ClassNode domain, ClassNode mapper, ClassNode key) { if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperKey(mapperKey)K method, key:" + key.name); MethodNode mn = new MethodNode(ACC_PUBLIC, "mapperKey", "(Lgemlite/core/internal/domain/IDataSource;)L" + key.name + ";", null, null); InsnList insn = mn.instructions; insn.add(new TypeInsnNode(NEW, key.name)); insn.add(new InsnNode(DUP)); insn.add(AsmHelper.newMethodInsnNode(INVOKESPECIAL, key.name, "<init>", "()V", false)); insn.add(new VarInsnNode(ASTORE, 2)); for (Object o : key.fields) { FieldNode fn = (FieldNode) o; if (!DomainMojoHelper.isValidField(fn)) continue; Item1 mti = MapperToolRegistry.getDataSItem(fn.desc); String s1 = fn.name.substring(0, 1); String s2 = fn.name.substring(1); String setMethod = "set" + s1.toUpperCase() + s2; // field start insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new VarInsnNode(ALOAD, 1)); insn.add(new LdcInsnNode(fn.name));// aab001 insn.add(AsmHelper.newMethodInsnNode(INVOKEINTERFACE, "gemlite/core/internal/domain/IDataSource", mti.getMethod, "(Ljava/lang/String;)" + fn.desc, true)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, key.name, setMethod, "(" + fn.desc + ")V", false)); // field end } insn.add(new VarInsnNode(ALOAD, 2)); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperKey(mapperKey)K method, key:" + key.name + " done."); } private void addValue2KeySimple(ClassNode domain, ClassNode mapper, FieldNode key) { if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add value2key(V)K method, key field:" + key.name); String s1 = key.name.substring(0, 1); String s2 = key.name.substring(1); String getMethod = "get" + s1.toUpperCase() + s2; String fullDesc = AsmHelper.toFullName(key.desc); MethodNode mn = new MethodNode(ACC_PUBLIC, "value2Key", "(L" + domain.name + ";)" + fullDesc, null, null); InsnList insn = mn.instructions; insn.add(new VarInsnNode(ALOAD, 1)); insn.add(AsmHelper.newMethodInsnNode(INVOKEVIRTUAL, domain.name, getMethod, "()" + key.desc, false)); AsmHelper.addTypeConvert(insn, key.desc); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add value2key(V)K method, key field:" + key.name + " done."); } private void addMapperKeySimple(ClassNode mapper, FieldNode key) { if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperKey(IDataSource)K method, key field:" + key.name); String fullDesc = AsmHelper.toFullName(key.desc); MethodNode mn = new MethodNode(ACC_PUBLIC, "mapperKey", "(Lgemlite/core/internal/domain/IDataSource;)" + fullDesc, null, null); InsnList insn = mn.instructions; insn.add(new VarInsnNode(ALOAD, 1)); insn.add(new LdcInsnNode(key.name)); Item1 mti = MapperToolRegistry.getDataSItem(key.desc); insn.add(AsmHelper.newMethodInsnNode(INVOKEINTERFACE, "gemlite/core/internal/domain/IDataSource", mti.getMethod, "(Ljava/lang/String;)" + key.desc, true)); AsmHelper.addTypeConvert(insn, key.desc); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); if (DomainMojoHelper.log().isDebugEnabled()) DomainMojoHelper.log().debug("Add mapperKey(IDataSource)K method, key field:" + key.name); } private void addFieldNames(ClassNode domain, ClassNode mapper, ClassNode kcn, FieldNode kfn) { // ???fieldNames MethodNode mn = new MethodNode(ACC_STATIC, "<clinit>", "()V", null, null); InsnList insn = mn.instructions; // new valueFieldNames insn.add(new TypeInsnNode(NEW, "java/util/ArrayList")); insn.add(new InsnNode(DUP)); insn.add(AsmHelper.newMethodInsnNode(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V", false)); insn.add(new FieldInsnNode(PUTSTATIC, mapper.name, "valueFieldNames", "Ljava/util/List;")); // new keyFieldNames insn.add(new TypeInsnNode(NEW, "java/util/ArrayList")); insn.add(new InsnNode(DUP)); insn.add(AsmHelper.newMethodInsnNode(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V", false)); insn.add(new FieldInsnNode(PUTSTATIC, mapper.name, "keyFieldNames", "Ljava/util/List;")); // value fields for (Object o : domain.fields) { FieldNode fn = (FieldNode) o; if (!DomainMojoHelper.isValidField(fn)) continue; insn.add(new FieldInsnNode(GETSTATIC, mapper.name, "valueFieldNames", "Ljava/util/List;")); insn.add(new LdcInsnNode(fn.name)); insn.add(AsmHelper.newMethodInsnNode(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true)); insn.add(new InsnNode(POP)); } // key fields if (kcn != null) { for (Object o : kcn.fields) { FieldNode fn = (FieldNode) o; insn.add(new FieldInsnNode(GETSTATIC, mapper.name, "keyFieldNames", "Ljava/util/List;")); insn.add(new LdcInsnNode(fn.name)); insn.add(AsmHelper.newMethodInsnNode(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true)); insn.add(new InsnNode(POP)); } } else { insn.add(new FieldInsnNode(GETSTATIC, mapper.name, "keyFieldNames", "Ljava/util/List;")); insn.add(new LdcInsnNode(kfn.name)); insn.add(AsmHelper.newMethodInsnNode(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true)); insn.add(new InsnNode(POP)); } insn.add(new InsnNode(RETURN)); mapper.methods.add(mn); // fieldNames ???? FieldNode fnFieldNames = new FieldNode(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "valueFieldNames", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", null); mapper.fields.add(fnFieldNames); fnFieldNames = new FieldNode(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "keyFieldNames", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", null); mapper.fields.add(fnFieldNames); // getFieldNames mn = new MethodNode(ACC_PUBLIC, "getValueFieldNames", "()Ljava/util/List;", "()Ljava/util/List<Ljava/lang/String;>;", null); insn = mn.instructions; insn.add(new FieldInsnNode(GETSTATIC, mapper.name, "valueFieldNames", "Ljava/util/List;")); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); // getKeyFieldNames mn = new MethodNode(ACC_PUBLIC, "getKeyFieldNames", "()Ljava/util/List;", "()Ljava/util/List<Ljava/lang/String;>;", null); insn = mn.instructions; insn.add(new FieldInsnNode(GETSTATIC, mapper.name, "keyFieldNames", "Ljava/util/List;")); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); } private void addKeyValueTypeMethod(ClassNode domain, ClassNode mapper, String keyClassDesc) { keyClassDesc = AsmHelper.toFullName(keyClassDesc); MethodNode mn = new MethodNode(ACC_PUBLIC, "getKeyClass", "()Ljava/lang/Class;", "()Ljava/lang/Class<" + keyClassDesc + ">;", null); InsnList insn = mn.instructions; insn.add(new LdcInsnNode(Type.getType(keyClassDesc))); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); String valueClassDesc = "L" + domain.name + ";"; mn = new MethodNode(ACC_PUBLIC, "getValueClass", "()Ljava/lang/Class;", "()Ljava/lang/Class<" + valueClassDesc + ">;", null); insn = mn.instructions; insn.add(new LdcInsnNode(Type.getType(valueClassDesc))); insn.add(new InsnNode(ARETURN)); mapper.methods.add(mn); } public void endProcess() { mapperRegister.endProcess(); } public CreateMapperRegister getMapperRegister() { return mapperRegister; } }