All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.jcharm.common.Reproduce Maven / Gradle / Ivy

There is a newer version: 1.2
Show newest version
/**
 * Copyright (c) 2016,Daniel Wang ([email protected]) All rights reserved。
 */
package io.github.jcharm.common;

import java.lang.reflect.Modifier;
import java.util.function.Predicate;

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;

/**
 * The Interface Reproduce.
 *
 * @param  目标对象的数据类型
 * @param  源对象的数据类型
 */
public interface Reproduce {

	/**
	 * Creates the.
	 *
	 * @param  the generic type
	 * @param  the generic type
	 * @param destClass the dest class
	 * @param srcClass the src class
	 * @return the Reproduce
	 */
	public static  Reproduce create(final Class destClass, final Class srcClass) {
		return Reproduce.create(destClass, srcClass, null);
	}

	/**
	 * Creates the.
	 *
	 * @param  the generic type
	 * @param  the generic type
	 * @param destClass the dest class
	 * @param srcClass the src class
	 * @param columnPredicate the column predicate
	 * @return the Reproduce
	 */
	public static  Reproduce create(final Class destClass, final Class srcClass, final Predicate columnPredicate) {
		final String supDynName = Reproduce.class.getName().replace('.', '/');
		final String destName = destClass.getName().replace('.', '/');
		final String srcName = srcClass.getName().replace('.', '/');
		final String destDesc = Type.getDescriptor(destClass);
		final String srcDesc = Type.getDescriptor(srcClass);
		String newDynName = supDynName + "Dyn_" + destClass.getSimpleName() + "_" + srcClass.getSimpleName();
		ClassLoader loader = Reproduce.class.getClassLoader();
		if (String.class.getClassLoader() != destClass.getClassLoader()) {
			loader = destClass.getClassLoader();
			newDynName = destName + "_Dyn" + Reproduce.class.getSimpleName() + "_" + srcClass.getSimpleName();
		}
		try {
			return (Reproduce) Class.forName(newDynName.replace('/', '.')).newInstance();
		} catch (final Exception ex) {
		}
		final ClassWriter cw = new ClassWriter(0);
		MethodVisitor mv;
		cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + destDesc + srcDesc + ">;", "java/lang/Object", new String[] { supDynName });
		{ // 构造函数
			mv = (cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null));
			// mv.setDebug(true);
			mv.visitVarInsn(Opcodes.ALOAD, 0);
			mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
			mv.visitInsn(Opcodes.RETURN);
			mv.visitMaxs(1, 1);
			mv.visitEnd();
		}
		{
			mv = (cw.visitMethod(Opcodes.ACC_PUBLIC, "copy", "(" + destDesc + srcDesc + ")" + destDesc, null, null));
			for (final java.lang.reflect.Field field : srcClass.getFields()) {
				if (Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				if (Modifier.isFinal(field.getModifiers())) {
					continue;
				}
				if (!Modifier.isPublic(field.getModifiers())) {
					continue;
				}
				final String fname = field.getName();
				try {
					if (!field.getType().equals(destClass.getField(fname).getType())) {
						continue;
					}
					if (!columnPredicate.test(fname)) {
						continue;
					}
				} catch (final Exception e) {
					continue;
				}
				mv.visitVarInsn(Opcodes.ALOAD, 1);
				mv.visitVarInsn(Opcodes.ALOAD, 2);
				final String td = Type.getDescriptor(field.getType());
				mv.visitFieldInsn(Opcodes.GETFIELD, srcName, fname, td);
				mv.visitFieldInsn(Opcodes.PUTFIELD, destName, fname, td);
			}

			for (final java.lang.reflect.Method getter : srcClass.getMethods()) {
				if (Modifier.isStatic(getter.getModifiers())) {
					continue;
				}
				if (getter.getParameterTypes().length > 0) {
					continue; // 为了兼容android 而不使用 getParameterCount()
				}
				if ("getClass".equals(getter.getName())) {
					continue;
				}
				if (!getter.getName().startsWith("get") && !getter.getName().startsWith("is")) {
					continue;
				}
				java.lang.reflect.Method setter;
				final boolean is = getter.getName().startsWith("is");
				try {
					setter = destClass.getMethod(getter.getName().replaceFirst(is ? "is" : "get", "set"), getter.getReturnType());
					if (columnPredicate != null) {
						String col = setter.getName().substring(3);
						if ((col.length() < 2) || Character.isLowerCase(col.charAt(1))) {
							final char[] cs = col.toCharArray();
							cs[0] = Character.toLowerCase(cs[0]);
							col = new String(cs);
						}
						if (!columnPredicate.test(col)) {
							continue;
						}
					}
				} catch (final Exception e) {
					continue;
				}
				mv.visitVarInsn(Opcodes.ALOAD, 1);
				mv.visitVarInsn(Opcodes.ALOAD, 2);
				mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, srcName, getter.getName(), Type.getMethodDescriptor(getter), false);
				mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, destName, setter.getName(), Type.getMethodDescriptor(setter), false);
			}
			mv.visitVarInsn(Opcodes.ALOAD, 1);
			mv.visitInsn(Opcodes.ARETURN);
			mv.visitMaxs(3, 3);
			mv.visitEnd();
		}
		{
			mv = (cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_BRIDGE + Opcodes.ACC_SYNTHETIC, "copy", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, null));
			mv.visitVarInsn(Opcodes.ALOAD, 0);
			mv.visitVarInsn(Opcodes.ALOAD, 1);
			mv.visitTypeInsn(Opcodes.CHECKCAST, destName);
			mv.visitVarInsn(Opcodes.ALOAD, 2);
			mv.visitTypeInsn(Opcodes.CHECKCAST, srcName);
			mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, newDynName, "copy", "(" + destDesc + srcDesc + ")" + destDesc, false);
			mv.visitInsn(Opcodes.ARETURN);
			mv.visitMaxs(3, 3);
			mv.visitEnd();
		}
		cw.visitEnd();
		final byte[] bytes = cw.toByteArray();
		final Class creatorClazz = new ClassLoader(loader) {

			public final Class loadClass(final String name, final byte[] b) {
				return this.defineClass(name, b, 0, b.length);
			}
		}.loadClass(newDynName.replace('/', '.'), bytes);
		try {
			return (Reproduce) creatorClazz.newInstance();
		} catch (final Exception ex) {
			throw new RuntimeException(ex);
		}
	}

	/**
	 * Copy.
	 *
	 * @param dest the dest
	 * @param src the src
	 * @return the D
	 */
	public D copy(D dest, S src);

}