@Test
  public void shouldCustomizeClassMapBuilder() {
    // given
    MapperFactory mapperFactory =
        new DefaultMapperFactory.Builder()
            .classMapBuilderFactory(new ScoringClassMapBuilder.Factory())
            .build();

    // when
    ClassMap<Source, Destination> map =
        mapperFactory.classMap(Source.class, Destination.class).byDefault().toClassMap();

    Map<String, String> mapping = new HashMap<String, String>();
    for (FieldMap f : map.getFieldsMapping()) {
      mapping.put(f.getSource().getExpression(), f.getDestination().getExpression());
    }

    // then
    assertThat("name.first").isEqualTo(mapping.get("firstName"));
    assertThat("name.last").isEqualTo(mapping.get("lastName"));
    assertThat("streetAddress").isEqualTo(mapping.get("postalAddress.street"));
    assertThat("countryCode").isEqualTo(mapping.get("postalAddress.country.alphaCode"));
    assertThat("currentAge").isEqualTo(mapping.get("age"));
    assertThat("birthState").isEqualTo(mapping.get("stateOfBirth"));

    assertThat(mapping.containsKey("driversLicenseNumber")).isFalse();
    assertThat(mapping.containsKey("eyeColor")).isFalse();
  }
예제 #2
0
  private boolean isAlreadyExistsInUsedMappers(FieldMap fieldMap, ClassMap<?, ?> classMap) {

    Set<ClassMap<Object, Object>> usedClassMapSet =
        mapperFactory.lookupUsedClassMap(new MapperKey(classMap.getAType(), classMap.getBType()));

    if (!fieldMap.isByDefault()) {
      return false;
    }

    for (ClassMap<Object, Object> usedClassMap : usedClassMapSet) {
      for (FieldMap usedFieldMap : usedClassMap.getFieldsMapping()) {
        if (usedFieldMap.getSource().equals(fieldMap.getSource())
            && usedFieldMap.getDestination().equals(fieldMap.getDestination())) {
          return true;
        }
      }
    }

    return false;
  }
예제 #3
0
  private Set<FieldMap> addMapMethod(
      SourceCodeContext code, boolean aToB, ClassMap<?, ?> classMap, StringBuilder logDetails) {

    Set<FieldMap> mappedFields = new LinkedHashSet<FieldMap>();
    if (logDetails != null) {
      if (aToB) {
        logDetails.append(
            "\n\t"
                + code.getClassSimpleName()
                + ".mapAToB("
                + classMap.getAType()
                + ", "
                + classMap.getBTypeName()
                + ") {");
      } else {
        logDetails.append(
            "\n\t"
                + code.getClassSimpleName()
                + ".mapBToA("
                + classMap.getBType()
                + ", "
                + classMap.getATypeName()
                + ") {");
      }
    }

    final StringBuilder out = new StringBuilder();
    final String mapMethod = "map" + (aToB ? "AtoB" : "BtoA");
    out.append("\tpublic void ");
    out.append(mapMethod);
    out.append(
        format(
            "(java.lang.Object a, java.lang.Object b, %s mappingContext) {\n\n",
            MappingContext.class.getCanonicalName()));

    VariableRef source;
    VariableRef destination;
    if (aToB) {
      source = new VariableRef(classMap.getAType(), "source");
      destination = new VariableRef(classMap.getBType(), "destination");
    } else {
      source = new VariableRef(classMap.getBType(), "source");
      destination = new VariableRef(classMap.getAType(), "destination");
    }

    append(
        out,
        format("super.%s(a, b, mappingContext);", mapMethod),
        "\n\n",
        "// sourceType: " + source.type() + source.declare("a"),
        "// destinationType: " + destination.type() + destination.declare("b"),
        "\n\n");

    for (FieldMap currentFieldMap : classMap.getFieldsMapping()) {

      if (currentFieldMap.isExcluded()) {
        if (logDetails != null) {
          code.debugField(currentFieldMap, "excuding (explicitly)");
        }
        continue;
      }

      if (isAlreadyExistsInUsedMappers(currentFieldMap, classMap)) {
        if (logDetails != null) {
          code.debugField(
              currentFieldMap,
              "excluding because it is already handled by another mapper in this hierarchy");
        }
        continue;
      }

      FieldMap fieldMap = currentFieldMap;
      if (!aToB) {
        fieldMap = fieldMap.flip();
      }

      if (!fieldMap.isIgnored()) {
        if (code.aggregateSpecsApply(fieldMap)) {
          continue;
        }
        try {
          mappedFields.add(currentFieldMap);
          String sourceCode =
              generateFieldMapCode(code, fieldMap, classMap, destination, logDetails);
          out.append(sourceCode);
        } catch (final Exception e) {
          MappingException me = new MappingException(e);
          me.setSourceProperty(fieldMap.getSource());
          me.setDestinationProperty(fieldMap.getDestination());
          me.setSourceType(source.type());
          me.setDestinationType(destination.type());
          throw me;
        }
      } else if (logDetails != null) {
        code.debugField(fieldMap, "ignored for this mapping direction");
      }
    }

    out.append(code.mapAggregateFields());

    out.append("\n\t\tif(customMapper != null) { \n\t\t\t customMapper.")
        .append(mapMethod)
        .append("(source, destination, mappingContext);\n\t\t}");

    out.append("\n\t}");

    if (logDetails != null) {
      logDetails.append("\n\t}");
    }

    code.addMethod(out.toString());

    return mappedFields;
  }
  @SuppressWarnings({"unchecked"})
  public <T, A, B> ConstructorMapping<T> resolve(ClassMap<A, B> classMap, Type<T> sourceType) {
    boolean aToB = classMap.getBType().equals(sourceType);

    Type<?> targetClass = aToB ? classMap.getBType() : classMap.getAType();

    String[] declaredParameterNames =
        aToB ? classMap.getConstructorB() : classMap.getConstructorA();

    Map<String, FieldMap> targetParameters = new LinkedHashMap<String, FieldMap>();
    if (declaredParameterNames != null) {
      /*
       * An override to the property names was provided
       */
      Set<FieldMap> fields = new HashSet<FieldMap>(classMap.getFieldsMapping());
      for (String arg : declaredParameterNames) {
        Iterator<FieldMap> iter = fields.iterator();
        while (iter.hasNext()) {
          FieldMap fieldMap = iter.next();
          if (!fieldMap.is(aMappingOfTheRequiredClassProperty())) {
            if (!aToB) {
              fieldMap = fieldMap.flip();
            }
            if (fieldMap.getSource().getName().equals(arg)) {
              targetParameters.put(arg, fieldMap);
              iter.remove();
            }
          }
        }
      }
    } else {
      /*
       * Determine the set of constructor argument names
       * from the field mapping
       */
      for (FieldMap fieldMap : classMap.getFieldsMapping()) {
        if (!fieldMap.is(aMappingOfTheRequiredClassProperty())) {
          if (!aToB) {
            fieldMap = fieldMap.flip();
          }
          targetParameters.put(fieldMap.getDestination().getName(), fieldMap);
        }
      }
    }

    Constructor<T>[] constructors = (Constructor<T>[]) targetClass.getRawType().getConstructors();
    TreeMap<Integer, ConstructorMapping<T>> constructorsByMatchedParams =
        new TreeMap<Integer, ConstructorMapping<T>>();
    for (Constructor<T> constructor : constructors) {
      ConstructorMapping<T> constructorMapping = new ConstructorMapping<T>();
      constructorMapping.setDeclaredParameters(declaredParameterNames);
      boolean byDefault = declaredParameterNames == null;

      try {
        /*
         * 1) A constructor's parameters are all matched by known parameter names
         * 2) ...
         */
        String[] parameterNames = paranamer.lookupParameterNames(constructor);
        java.lang.reflect.Type[] genericParameterTypes = constructor.getGenericParameterTypes();
        Type<?>[] parameterTypes = new Type[genericParameterTypes.length];
        constructorMapping.setParameterNameInfoAvailable(true);
        if (targetParameters.keySet().containsAll(Arrays.asList(parameterNames))) {
          constructorMapping.setConstructor(constructor);
          for (int i = 0; i < parameterNames.length; ++i) {
            String parameterName = parameterNames[i];
            parameterTypes[i] = TypeFactory.valueOf(genericParameterTypes[i]);
            FieldMap existingField = targetParameters.get(parameterName);
            FieldMap argumentMap =
                mapConstructorArgument(existingField, parameterTypes[i], byDefault);
            constructorMapping.getMappedFields().add(argumentMap);
          }
          constructorMapping.setParameterTypes(parameterTypes);
          constructorsByMatchedParams.put(parameterNames.length * 1000, constructorMapping);
        }
      } catch (ParameterNamesNotFoundException e) {
        /*
         * Could not find parameter names of the constructors; attempt to match constructors
         * based on the types of the destination properties
         */
        List<FieldMap> targetTypes = new ArrayList<FieldMap>(targetParameters.values());
        int matchScore = 0;
        int exactMatches = 0;
        java.lang.reflect.Type[] params = constructor.getGenericParameterTypes();
        Type<?>[] parameterTypes = new Type[params.length];

        if (targetTypes.size() >= parameterTypes.length) {
          for (int i = 0; i < params.length; ++i) {
            java.lang.reflect.Type param = params[i];

            parameterTypes[i] = TypeFactory.valueOf(param);
            for (Iterator<FieldMap> iter = targetTypes.iterator(); iter.hasNext(); ) {
              FieldMap fieldMap = iter.next();
              Type<?> targetType = fieldMap.getDestination().getType();
              if ((parameterTypes[i].equals(targetType) && ++exactMatches != 0)
                  || parameterTypes[i].isAssignableFrom(targetType)) {
                ++matchScore;

                String parameterName = fieldMap.getDestination().getName();
                FieldMap existingField = targetParameters.get(parameterName);
                FieldMap argumentMap =
                    mapConstructorArgument(existingField, parameterTypes[i], byDefault);
                constructorMapping.getMappedFields().add(argumentMap);

                iter.remove();
                break;
              }
            }
          }
          constructorMapping.setParameterTypes(parameterTypes);
          constructorMapping.setConstructor(constructor);
          constructorMapping.setDeclaredParameters(declaredParameterNames);
          constructorsByMatchedParams.put((matchScore * 1000 + exactMatches), constructorMapping);
        }
      }
    }

    if (constructorsByMatchedParams.size() > 0) {
      return constructorsByMatchedParams.get(constructorsByMatchedParams.lastKey());
    } else if (declaredParameterNames != null) {
      throw new IllegalArgumentException(
          "No constructors found for "
              + targetClass
              + " matching the specified constructor parameters "
              + Arrays.toString(declaredParameterNames)
              + (declaredParameterNames.length == 0 ? " (no-arg constructor)" : ""));
    } else {

      /*
       * User didn't specify any constructor, and we couldn't find any that seem compatible;
       * TODO: can we really do anything in this case? maybe we should just throw an error
       * describing some alternative options like creating a Converter or declaring their own
       * custom ObjectFactory...
       * */

      ConstructorMapping<T> defaultMapping = new ConstructorMapping<T>();
      defaultMapping.setConstructor(constructors.length == 0 ? null : constructors[0]);
      return defaultMapping;
    }
  }