Package org.modelmapper.internal

Source Code of org.modelmapper.internal.ExplicitMappingVisitor$VisitedMapping

package org.modelmapper.internal;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.modelmapper.internal.PropertyInfoImpl.FieldPropertyInfo;
import org.modelmapper.internal.util.Members;
import org.modelmapper.internal.util.Primitives;
import org.modelmapper.spi.NameableType;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;

/**
* Visits explicitly declared mappings, capturing mapping information.
*
* @author Jonathan Halterman
*/
public class ExplicitMappingVisitor extends ClassVisitor {
  private static final String SOURCE_FIELD = "source";
  private static final String DEST_FIELD = "destination";
  private static final String PROXY_FIELD_DESC = "Ljava/lang/Object;";
  private static final String CONFIGURE_METHOD = "configure";
  private static final String CONFIGURE_METHOD_DESC = "()V";
  private static final String ACCESSOR_METHOD_DESC_PREFIX = "()";

  private static final String MAP_METHOD = "map";
  private static final String SKIP_METHOD = "skip";
  private static final String MAP_EXPR_OWNER_PREFIX = "org/modelmapper/builder";
  private static final String MAP_DEST_METHOD_DESC = "()Ljava/lang/Object;";
  private static final String MAP_SOURCE_METHOD_DESC = "(Ljava/lang/Object;)Ljava/lang/Object;";
  private static final String SKIP_DEST_METHOD_DESC = "(Ljava/lang/Object;)V";
  private static final String MAP_BOTH_METHOD_DESC = "(Ljava/lang/Object;Ljava/lang/Object;)V";

  private final Errors errors;
  private final InheritingConfiguration config;
  private final String propMapClassInternalName;
  private final String destClassInternalName;
  private final ClassLoader propertyMapClassLoader;
  private final Set<String> syntheticFields = new HashSet<String>();

  /** Result mappings */
  final List<VisitedMapping> mappings = new ArrayList<VisitedMapping>();

  static class VisitedMapping {
    List<Accessor> sourceAccessors = new ArrayList<Accessor>();
    List<Accessor> destinationAccessors = new ArrayList<Accessor>();
    List<Mutator> destinationMutators = new ArrayList<Mutator>();
  }

  public ExplicitMappingVisitor(Errors errors, InheritingConfiguration config,
      String propertyMapClassName, String destinationClassName, ClassLoader propertyMapClassLoader) {
    super(Opcodes.ASM5);
    this.errors = errors;
    this.config = config;
    propMapClassInternalName = propertyMapClassName.replace('.', '/');
    destClassInternalName = destinationClassName.replace('.', '/');
    this.propertyMapClassLoader = propertyMapClassLoader;
  }

  @Override
  public FieldVisitor visitField(int access, String name, String desc, String signature,
      Object value) {
    if ((access & Opcodes.ACC_SYNTHETIC) == Opcodes.ACC_SYNTHETIC)
      syntheticFields.add(name);
    return null;
  }

  @Override
  public MethodVisitor visitMethod(int access, String name, String desc, String signature,
      String[] exceptions) {
    if (name.equals(CONFIGURE_METHOD) && desc.equals(CONFIGURE_METHOD_DESC))
      return new MappingCapturingVisitor();
    return null;
  }

  private class MappingCapturingVisitor extends MethodVisitor {
    private final List<AbstractInsnNode> instructions = new ArrayList<AbstractInsnNode>();

    /** Per mapping state */
    private VisitedMapping mapping = new VisitedMapping();
    /** The class that owns the last expected mutator method */
    private String lastMutatorOwner;
    /** 0=none, 1=source, 2=destination */
    private int subjectType;
    /** 0=none, 1=map(), 2=map(Object), 3=skip(Object) */
    private int mapType;

    private MappingCapturingVisitor() {
      super(Opcodes.ASM5);
    }

    @Override
    public void visitInsn(int opcode) {
      if (opcode == Opcodes.RETURN)
        instructions.add(new InsnNode(opcode));
    }

    @Override
    public void visitVarInsn(int opcode, int var) {
      // If ALOAD and previous instruction was a map(Object, Object) or mutator
      if (opcode == Opcodes.ALOAD) {
        if (!instructions.isEmpty()) {
          AbstractInsnNode previous = instructions.get(instructions.size() - 1);
          if (isMethodInvocation(previous)) {
            MethodInsnNode mn = (MethodInsnNode) previous;
            if (isMapBothMethod(mn) || !isAccessor(mn))
              instructions.add(new InsnNode(opcode));
          }
        }
      }
    }

    @Override
    public void visitFieldInsn(final int opcode, final String owner, final String name,
        final String desc) {
      // If GETFIELD and not owned by property map or not synthetic
      if (opcode == Opcodes.GETFIELD
          && (!owner.equals(propMapClassInternalName) || !syntheticFields.contains(name)))
        instructions.add(new FieldInsnNode(opcode, owner, name, desc));
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
      boolean isSourceMethod = owner.equals(propMapClassInternalName)
          && (name.equals(SOURCE_FIELD) || name.equals(DEST_FIELD));

      // If not special and not source method invocation and owner is not a primitive wrapper
      if (opcode != Opcodes.INVOKESPECIAL && !isSourceMethod
          && !Primitives.isPrimitiveWrapperInternalName(owner))
        instructions.add(new MethodInsnNode(opcode, owner, name, desc, itf));
    }

    @Override
    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
      instructions.add(new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs));
    }

    @Override
    public void visitEnd() {
      for (int i = 0; i < instructions.size(); i++) {
        AbstractInsnNode ins = instructions.get(i);

        if (ins.getOpcode() == Opcodes.GETFIELD) {
          FieldInsnNode fn = (FieldInsnNode) ins;

          if (fn.owner.equals(propMapClassInternalName) && fn.desc.equals(PROXY_FIELD_DESC)) {
            if (fn.name.equals(SOURCE_FIELD))
              subjectType = 1;
            else if (fn.name.equals(DEST_FIELD))
              subjectType = 2;
            continue;
          }

          Class<?> ownerType = classFor(fn.owner.replace('/', '.'));
          if (subjectType == 1)
            recordSourceField(ownerType, fieldFor(ownerType, fn));
          else if (subjectType == 2) {
            setLastMutatorOwner(Type.getType(fn.desc));
            recordDestinationField(ownerType, fieldFor(ownerType, fn));
          }
        } else if (isMethodInvocation(ins)) {
          MethodInsnNode mn = (MethodInsnNode) ins;

          if (isMapMethod(mn) || isSkipMethod(mn)) {
            lastMutatorOwner = destClassInternalName;
            if (MAP_DEST_METHOD_DESC.equals(mn.desc)) {
              mapType = 1;
              subjectType = 2;
            } else if (MAP_SOURCE_METHOD_DESC.equals(mn.desc)) {
              // If already recorded destination field
              if (subjectType == 2) {
                recordProperties();
              } else {
                mapType = 2;
                subjectType = 2;
              }
            } else if (MAP_BOTH_METHOD_DESC.equals(mn.desc)) {
              recordProperties();
            } else if (SKIP_DEST_METHOD_DESC.equals(mn.desc)) {
              mapType = 3;
            }
          } else if (subjectType != 0) {
            Class<?> ownerType = classFor(mn.owner.replace('/', '.'));
            Type methodType = Type.getMethodType(mn.desc);

            // If last destination mutator
            if (mapType != 0 && !isAccessor(mn) && mn.owner.equals(lastMutatorOwner)) {
              recordDestinationMethod(ownerType, methodFor(ownerType, methodType, mn));
              recordProperties();
            } else {
              if (subjectType == 1)
                recordSourceMethod(ownerType, methodFor(ownerType, methodType, mn));
              else if (subjectType == 2) {
                setLastMutatorOwner(methodType.getReturnType());
                recordDestinationMethod(ownerType, methodFor(ownerType, methodType, mn));
              }
            }
          }
        } else if (ins.getOpcode() == Opcodes.ALOAD || ins.getOpcode() == Opcodes.RETURN) {
          // If skip(Object)
          if (mapType == 3 && subjectType != 0)
            recordProperties();
          else if (mapType != 0)
            errors.missingDestination();
        }
      }
    }

    private void setLastMutatorOwner(Type type) {
      int sort = type.getSort();
      if (sort == Type.ARRAY || sort == Type.OBJECT)
        lastMutatorOwner = type.getInternalName();
    }

    private boolean isMethodInvocation(AbstractInsnNode ins) {
      return ins.getOpcode() == Opcodes.INVOKEVIRTUAL || ins.getOpcode() == Opcodes.INVOKEINTERFACE;
    }

    private boolean isMapMethod(MethodInsnNode mn) {
      return mn.name.equals(MAP_METHOD)
          && (mn.owner.equals(propMapClassInternalName) || mn.owner.startsWith(MAP_EXPR_OWNER_PREFIX));
    }

    private boolean isSkipMethod(MethodInsnNode mn) {
      return mn.name.equals(SKIP_METHOD)
          && (mn.owner.equals(propMapClassInternalName) || mn.owner.startsWith(MAP_EXPR_OWNER_PREFIX));
    }

    private boolean isMapBothMethod(MethodInsnNode mn) {
      return mn.name.equals(MAP_METHOD)
          && (mn.owner.equals(propMapClassInternalName) || mn.owner.startsWith(MAP_EXPR_OWNER_PREFIX))
          && mn.desc.equals(MAP_BOTH_METHOD_DESC);
    }

    private boolean isAccessor(MethodInsnNode mn) {
      return mn.desc.startsWith(ACCESSOR_METHOD_DESC_PREFIX);
    }

    private void recordProperties() {
      mappings.add(mapping);
      mapping = new VisitedMapping();
      lastMutatorOwner = null;
      subjectType = 0;
      mapType = 0;
    }

    private void recordSourceMethod(Class<?> type, Method method) {
      assertNotFinal(method);
      if (PropertyInfoResolver.ACCESSORS.isValid(method)) {
        String propertyName = config.getSourceNameTransformer().transform(method.getName(),
            NameableType.METHOD);
        mapping.sourceAccessors.add(PropertyInfoRegistry.accessorFor(type, method, config,
            propertyName));
      } else
        errors.invalidSourceMethod(method);
    }

    private void recordSourceField(Class<?> type, Field field) {
      assertNotFinal(field);
      if (PropertyInfoResolver.FIELDS.isValid(field)) {
        String propertyName = config.getSourceNameTransformer().transform(field.getName(),
            NameableType.FIELD);
        mapping.sourceAccessors.add(PropertyInfoRegistry.fieldPropertyFor(type, field, config,
            propertyName));
      } else
        errors.invalidSourceField(field);
    }

    private void recordDestinationMethod(Class<?> type, Method method) {
      assertNotFinal(method);
      if (PropertyInfoResolver.MUTATORS.isValid(method)) {
        String propertyName = config.getDestinationNameTransformer().transform(method.getName(),
            NameableType.METHOD);
        mapping.destinationMutators.add(PropertyInfoRegistry.mutatorFor(type, method, config,
            propertyName));
      } else if (PropertyInfoResolver.ACCESSORS.isValid(method)) {
        String propertyName = config.getSourceNameTransformer().transform(method.getName(),
            NameableType.METHOD);
        mapping.destinationAccessors.add(PropertyInfoRegistry.accessorFor(type, method, config,
            propertyName));

        // Find mutator corresponding to accessor
        Mutator mutator = TypeInfoRegistry.typeInfoFor(type, config).mutatorForAccessorMethod(
            method.getName());
        if (mutator != null)
          mapping.destinationMutators.add(mutator);
        else
          errors.missingMutatorForAccessor(method);
      } else
        errors.invalidDestinationMethod(method);
    }

    private void recordDestinationField(Class<?> type, Field field) {
      assertNotFinal(field);
      if (PropertyInfoResolver.FIELDS.isValid(field)) {
        String propertyName = config.getDestinationNameTransformer().transform(field.getName(),
            NameableType.FIELD);
        FieldPropertyInfo propertyInfo = PropertyInfoRegistry.fieldPropertyFor(type, field, config,
            propertyName);
        mapping.destinationAccessors.add(propertyInfo);
        mapping.destinationMutators.add(propertyInfo);
      } else
        errors.invalidDestinationField(field);
    }

    private void assertNotFinal(Member member) {
      if (Modifier.isFinal(member.getDeclaringClass().getModifiers()))
        errors.invocationAgainstFinalClass(member.getDeclaringClass());
      if (member instanceof Method && Modifier.isFinal(member.getModifiers()))
        errors.invocationAgainstFinalMethod(member);
    }
  }

  private Field fieldFor(Class<?> type, FieldInsnNode fn) {
    return Members.fieldFor(type, fn.name);
  }

  private Method methodFor(Class<?> type, Type methodType, MethodInsnNode mn) {
    Type[] argumentTypes = methodType.getArgumentTypes();
    Class<?>[] paramTypes = new Class<?>[argumentTypes.length];
    for (int i = 0; i < paramTypes.length; i++) {
      Class<?> paramType = Primitives.primitiveFor(argumentTypes[i]);
      paramTypes[i] = paramType == null ? classFor(argumentTypes[i].getClassName()) : paramType;
    }
    return Members.methodFor(type, mn.name, paramTypes);
  }

  private Class<?> classFor(String className) {
    try {
      return Class.forName(className, true, propertyMapClassLoader);
    } catch (ClassNotFoundException e) {
      throw errors.errorResolvingClass(e, className).toException();
    }
  }
}
TOP

Related Classes of org.modelmapper.internal.ExplicitMappingVisitor$VisitedMapping

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
reate', 'UA-20639858-1', 'auto'); ga('send', 'pageview');