From d42216ee4f1de112c651735946324409dbf36fc5 Mon Sep 17 00:00:00 2001
From: sjaakd
Date: Mon, 29 Sep 2014 20:53:13 +0200
Subject: [PATCH] #302 refactoring of MappingCreationProcessor: using Builders
to address smaller concerns
---
.../ap/conversion/ConversionProvider.java | 4 +-
.../ap/conversion/DateToStringConversion.java | 4 +-
.../ap/conversion/ReverseConversion.java | 2 +-
.../ap/conversion/SimpleConversion.java | 6 +-
.../mapstruct/ap/model/AssignmentFactory.java | 103 ++
.../mapstruct/ap/model/BeanMappingMethod.java | 341 +++++-
.../ap/model/{assignment => }/Direct.java | 4 +-
.../mapstruct/ap/model/EnumMappingMethod.java | 167 ++-
.../ap/model/IterableMappingMethod.java | 72 +-
.../mapstruct/ap/model/MapMappingMethod.java | 102 +-
.../mapstruct/ap/model/MappingContext.java | 171 +++
.../mapstruct/ap/model/MappingResolver.java | 63 +
.../{assignment => }/MethodReference.java | 7 +-
.../mapstruct/ap/model/PropertyMapping.java | 426 ++++++-
.../{assignment => }/TypeConversion.java | 4 +-
.../ap/model/assignment/AdderWrapper.java | 1 -
.../ap/model/{ => assignment}/Assignment.java | 2 +-
.../model/assignment/AssignmentFactory.java | 63 -
.../model/assignment/AssignmentWrapper.java | 1 -
.../GetterCollectionOrMapWrapper.java | 2 -
.../ap/model/assignment/LocalVarWrapper.java | 1 -
.../assignment/NewCollectionOrMapWrapper.java | 1 -
.../ap/model/assignment/NullCheckWrapper.java | 2 -
.../SetterCollectionOrMapWrapper.java | 1 -
.../ap/model/assignment/SetterWrapper.java | 1 -
.../ap/processor/MapperCreationProcessor.java | 1068 +----------------
...Resolver.java => MappingResolverImpl.java} | 320 ++---
....ftl => org.mapstruct.ap.model.Direct.ftl} | 0
...rg.mapstruct.ap.model.MethodReference.ftl} | 0
...org.mapstruct.ap.model.TypeConversion.ftl} | 0
30 files changed, 1669 insertions(+), 1270 deletions(-)
create mode 100644 processor/src/main/java/org/mapstruct/ap/model/AssignmentFactory.java
rename processor/src/main/java/org/mapstruct/ap/model/{assignment => }/Direct.java (95%)
create mode 100644 processor/src/main/java/org/mapstruct/ap/model/MappingContext.java
create mode 100644 processor/src/main/java/org/mapstruct/ap/model/MappingResolver.java
rename processor/src/main/java/org/mapstruct/ap/model/{assignment => }/MethodReference.java (96%)
rename processor/src/main/java/org/mapstruct/ap/model/{assignment => }/TypeConversion.java (97%)
rename processor/src/main/java/org/mapstruct/ap/model/{ => assignment}/Assignment.java (98%)
delete mode 100644 processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentFactory.java
rename processor/src/main/java/org/mapstruct/ap/processor/creation/{MappingResolver.java => MappingResolverImpl.java} (65%)
rename processor/src/main/resources/{org.mapstruct.ap.model.assignment.Direct.ftl => org.mapstruct.ap.model.Direct.ftl} (100%)
rename processor/src/main/resources/{org.mapstruct.ap.model.assignment.MethodReference.ftl => org.mapstruct.ap.model.MethodReference.ftl} (100%)
rename processor/src/main/resources/{org.mapstruct.ap.model.assignment.TypeConversion.ftl => org.mapstruct.ap.model.TypeConversion.ftl} (100%)
diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/ConversionProvider.java b/processor/src/main/java/org/mapstruct/ap/conversion/ConversionProvider.java
index 3d8ba9fc8..4efc14c36 100644
--- a/processor/src/main/java/org/mapstruct/ap/conversion/ConversionProvider.java
+++ b/processor/src/main/java/org/mapstruct/ap/conversion/ConversionProvider.java
@@ -18,8 +18,8 @@
*/
package org.mapstruct.ap.conversion;
-import org.mapstruct.ap.model.Assignment;
-import org.mapstruct.ap.model.assignment.TypeConversion;
+import org.mapstruct.ap.model.assignment.Assignment;
+import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.common.ConversionContext;
/**
diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/DateToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/conversion/DateToStringConversion.java
index 614ca3957..21741f1c4 100644
--- a/processor/src/main/java/org/mapstruct/ap/conversion/DateToStringConversion.java
+++ b/processor/src/main/java/org/mapstruct/ap/conversion/DateToStringConversion.java
@@ -23,8 +23,8 @@ import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
-import org.mapstruct.ap.model.Assignment;
-import org.mapstruct.ap.model.assignment.AssignmentFactory;
+import org.mapstruct.ap.model.assignment.Assignment;
+import org.mapstruct.ap.model.AssignmentFactory;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.Type;
diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/ReverseConversion.java b/processor/src/main/java/org/mapstruct/ap/conversion/ReverseConversion.java
index 99bd24fd8..2ba0adc7b 100644
--- a/processor/src/main/java/org/mapstruct/ap/conversion/ReverseConversion.java
+++ b/processor/src/main/java/org/mapstruct/ap/conversion/ReverseConversion.java
@@ -18,7 +18,7 @@
*/
package org.mapstruct.ap.conversion;
-import org.mapstruct.ap.model.Assignment;
+import org.mapstruct.ap.model.assignment.Assignment;
import org.mapstruct.ap.model.common.ConversionContext;
/**
diff --git a/processor/src/main/java/org/mapstruct/ap/conversion/SimpleConversion.java b/processor/src/main/java/org/mapstruct/ap/conversion/SimpleConversion.java
index febec383b..a1ceb02bc 100644
--- a/processor/src/main/java/org/mapstruct/ap/conversion/SimpleConversion.java
+++ b/processor/src/main/java/org/mapstruct/ap/conversion/SimpleConversion.java
@@ -21,9 +21,9 @@ package org.mapstruct.ap.conversion;
import java.util.Collections;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
-import org.mapstruct.ap.model.assignment.AssignmentFactory;
-import org.mapstruct.ap.model.assignment.TypeConversion;
+import org.mapstruct.ap.model.assignment.Assignment;
+import org.mapstruct.ap.model.AssignmentFactory;
+import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.Type;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/AssignmentFactory.java b/processor/src/main/java/org/mapstruct/ap/model/AssignmentFactory.java
new file mode 100644
index 000000000..8c6e5ef74
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/model/AssignmentFactory.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
+ * and/or other contributors as indicated by the @authors tag. See the
+ * copyright.txt file in the distribution for a full listing of all
+ * contributors.
+ *
+ * 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 org.mapstruct.ap.model;
+
+import org.mapstruct.ap.model.assignment.Assignment;
+import java.util.List;
+import java.util.Set;
+import javax.tools.Diagnostic;
+import org.mapstruct.ap.model.common.ConversionContext;
+import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.source.Method;
+import org.mapstruct.ap.model.source.SourceMethod;
+import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
+import org.mapstruct.ap.model.source.selector.MethodSelectors;
+
+/**
+ * Factory class for creating all types of assignments
+ *
+ * @author Sjaak Derksen
+ */
+public class AssignmentFactory {
+
+ private AssignmentFactory() {
+ }
+
+ public static Assignment createTypeConversion(Set importTypes, List exceptionTypes, String expression) {
+ return new TypeConversion( importTypes, exceptionTypes, expression );
+ }
+
+ public static Assignment createMethodReference(Method method, MapperReference declaringMapper,
+ Type targetType) {
+ return new MethodReference( method, declaringMapper, targetType );
+ }
+
+ public static Assignment createMethodReference(BuiltInMethod method, ConversionContext contextParam) {
+ return new MethodReference( method, contextParam );
+ }
+
+ public static Direct createSimple(String sourceRef) {
+ return new Direct( sourceRef );
+ }
+
+ public static FactoryMethod createFactoryMethod( Type returnType, MappingContext ctx ) {
+ FactoryMethod result = null;
+ for ( SourceMethod method : ctx.getSourceModel() ) {
+ if ( !method.overridesMethod() && !method.isIterableMapping() && !method.isMapMapping()
+ && method.getSourceParameters().isEmpty() ) {
+
+ List parameterTypes = MethodSelectors.getParameterTypes(
+ ctx.getTypeFactory(),
+ method.getParameters(),
+ null,
+ returnType
+ );
+
+ if ( method.matches( parameterTypes, returnType ) ) {
+ if ( result == null ) {
+ MapperReference mapperReference = findMapperReference( ctx.getMapperReferences(), method );
+ result = new MethodReference( method, mapperReference, null );
+ }
+ else {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Ambiguous factory methods: \"%s\" conflicts with \"%s\".",
+ result,
+ method
+ ),
+ method.getExecutable()
+ );
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static MapperReference findMapperReference( List mapperReferences, SourceMethod method ) {
+ for ( MapperReference ref : mapperReferences ) {
+ if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
+ return ref;
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
index 83f61f2f3..88968f500 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
@@ -18,15 +18,25 @@
*/
package org.mapstruct.ap.model;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.lang.model.element.ExecutableElement;
+import javax.tools.Diagnostic;
+import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.SourceMethod;
+import org.mapstruct.ap.option.ReportingPolicy;
+import org.mapstruct.ap.util.Executables;
+import org.mapstruct.ap.util.MapperConfig;
+import org.mapstruct.ap.util.Strings;
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one
@@ -44,7 +54,336 @@ public class BeanMappingMethod extends MappingMethod {
private final FactoryMethod factoryMethod;
- public BeanMappingMethod(SourceMethod method,
+ public static class Builder {
+
+ private MappingContext ctx;
+ private SourceMethod method;
+
+ public Builder mappingContext(MappingContext mappingContext) {
+ this.ctx = mappingContext;
+ return this;
+ }
+
+ public Builder souceMethod( SourceMethod sourceMethod ) {
+ this.method = sourceMethod;
+ return this;
+ }
+
+ public BeanMappingMethod build() {
+
+ // fetch settings from element to implement
+ ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy();
+ CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy();
+
+ List propertyMappings = new ArrayList();
+ Set mappedTargetProperties = new HashSet();
+ Set ignoredTargetProperties = new HashSet();
+
+ if ( !reportErrorIfMappedPropertiesDontExist() ) {
+ return null;
+ }
+
+ // collect all target accessors
+ List targetAccessors = new ArrayList();
+ targetAccessors.addAll( method.getResultType().getSetters() );
+ targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() );
+
+ for ( ExecutableElement targetAccessor : targetAccessors ) {
+
+ String targetPropertyName = Executables.getPropertyName( targetAccessor );
+
+ Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
+
+ if ( mapping != null && mapping.isIgnored() ) {
+ ignoredTargetProperties.add( targetPropertyName );
+ continue;
+ }
+
+ // A target access is in general a setter method on the target object. However, in case of collections,
+ // the current target accessor can also be a getter method.
+ // The following if block, checks if the target accessor should be overruled by an add method.
+ if ( cmStrategy.equals( CollectionMappingStrategy.SETTER_PREFERRED )
+ || cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
+
+ // first check if there's a setter method.
+ ExecutableElement adderMethod = null;
+ if ( Executables.isSetterMethod( targetAccessor ) ) {
+ Type targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
+ // ok, the current accessor is a setter. So now the strategy determines what to use
+ if ( cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
+ adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
+ }
+ }
+ else if ( Executables.isGetterMethod( targetAccessor ) ) {
+ // the current accessor is a getter (no setter available). But still, an add method is according
+ // to the above strategy (SETTER_PREFERRED || ADDER_PREFERRED) preferred over the getter.
+ Type targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
+ adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
+ }
+ if ( adderMethod != null ) {
+ // an adder has been found (according strategy) so overrule current choice.
+ targetAccessor = adderMethod;
+ }
+ }
+
+ PropertyMapping propertyMapping = null;
+ if ( mapping != null ) {
+
+ if ( mapping.getSourceParameterName() != null ) {
+ // this is a parameterized property, so sourceParameter.property
+ Parameter parameter = method.getSourceParameter( mapping.getSourceParameterName() );
+
+ PropertyMapping.PropertyMappingBuilder builder = new PropertyMapping.PropertyMappingBuilder();
+ propertyMapping = builder
+ .mappingContext( ctx )
+ .souceMethod( method )
+ .targetAccessor( targetAccessor )
+ .targetPropertyName( targetPropertyName )
+ .parameter( parameter )
+ .build();
+
+ }
+ else if ( Executables.isSetterMethod( targetAccessor )
+ || Executables.isGetterMethod( targetAccessor ) ) {
+
+ if ( !mapping.getConstant().isEmpty() ) {
+ // its a constant
+ PropertyMapping.ConstantMappingBuilder builder =
+ new PropertyMapping.ConstantMappingBuilder();
+ propertyMapping = builder
+ .mappingContext( ctx )
+ .sourceMethod( method )
+ .constantExpression( "\"" + mapping.getConstant() + "\"" )
+ .targetAccessor( targetAccessor )
+ .dateFormat( mapping.getDateFormat() )
+ .qualifiers( mapping.getQualifiers() )
+ .build();
+ }
+
+ else if ( !mapping.getJavaExpression().isEmpty() ) {
+ // its an expression
+ PropertyMapping.JavaExpressionMappingBuilder builder =
+ new PropertyMapping.JavaExpressionMappingBuilder();
+ propertyMapping = builder
+ .mappingContext( ctx )
+ .souceMethod( method )
+ .javaExpression( mapping.getJavaExpression() )
+ .targetAccessor( targetAccessor )
+ .build();
+ }
+ }
+ }
+
+ if ( propertyMapping == null ) {
+ for ( Parameter sourceParameter : method.getSourceParameters() ) {
+ PropertyMapping.PropertyMappingBuilder builder = new PropertyMapping.PropertyMappingBuilder();
+ PropertyMapping newPropertyMapping = builder
+ .mappingContext( ctx )
+ .souceMethod( method )
+ .targetAccessor( targetAccessor )
+ .targetPropertyName( targetPropertyName )
+ .parameter( sourceParameter )
+ .build();
+
+ if ( propertyMapping != null && newPropertyMapping != null ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ "Several possible source properties for target property \"" + targetPropertyName +
+ "\".",
+ method.getExecutable()
+ );
+ break;
+ }
+ else if ( newPropertyMapping != null ) {
+ propertyMapping = newPropertyMapping;
+ }
+ }
+ }
+
+ if ( propertyMapping != null ) {
+ propertyMappings.add( propertyMapping );
+ mappedTargetProperties.add( targetPropertyName );
+ }
+ }
+
+ Set targetProperties = Executables.getPropertyNames( targetAccessors );
+
+ reportErrorForUnmappedTargetPropertiesIfRequired(
+ method,
+ unmappedTargetPolicy,
+ targetProperties,
+ mappedTargetProperties,
+ ignoredTargetProperties
+ );
+ FactoryMethod factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
+ return new BeanMappingMethod( method, propertyMappings, factoryMethod );
+ }
+
+ /**
+ * Returns the effective policy for reporting unmapped getReturnType properties. If explicitly set via
+ * {@code Mapper}, this value will be returned. Otherwise the value from the corresponding processor option will
+ * be returned. If that is not set either, the default value from {@code Mapper#unmappedTargetPolicy()} will be
+ * returned.
+ *
+ * @param element The type declaring the generated mapper type
+ *
+ * @return The effective policy for reporting unmapped getReturnType properties.
+ */
+ private ReportingPolicy getEffectiveUnmappedTargetPolicy() {
+ MapperConfig mapperSettings = MapperConfig.getInstanceOn( ctx.getMapperTypeElement() );
+ boolean setViaAnnotation = mapperSettings.isSetUnmappedTargetPolicy();
+ ReportingPolicy annotationValue = ReportingPolicy.valueOf( mapperSettings.unmappedTargetPolicy() );
+
+ if ( setViaAnnotation
+ || ctx.getOptions().getUnmappedTargetPolicy() == null ) {
+ return annotationValue;
+ }
+ else {
+ return ctx.getOptions().getUnmappedTargetPolicy();
+ }
+ }
+
+ private CollectionMappingStrategy getEffectiveCollectionMappingStrategy() {
+ MapperConfig mapperSettings = MapperConfig.getInstanceOn( ctx.getMapperTypeElement() );
+ return mapperSettings.getCollectionMappingStrategy();
+ }
+
+ private boolean reportErrorIfMappedPropertiesDontExist() {
+ // only report errors if this method itself is configured
+ if ( method.isConfiguredByReverseMappingMethod() ) {
+ return true;
+ }
+
+ // collect all target accessors
+ List targetAccessors = new ArrayList();
+ targetAccessors.addAll( method.getResultType().getSetters() );
+ targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() );
+
+ Set targetProperties = Executables.getPropertyNames( targetAccessors );
+
+ boolean foundUnmappedProperty = false;
+
+ for ( List mappedProperties : method.getMappings().values() ) {
+ for ( Mapping mappedProperty : mappedProperties ) {
+ if ( mappedProperty.isIgnored() ) {
+ continue;
+ }
+
+ if ( mappedProperty.getSourceParameterName() != null ) {
+ Parameter sourceParameter = method.getSourceParameter(
+ mappedProperty.getSourceParameterName()
+ );
+
+ if ( sourceParameter == null ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Method has no parameter named \"%s\".",
+ mappedProperty.getSourceParameterName()
+ ),
+ method.getExecutable(),
+ mappedProperty.getMirror(),
+ mappedProperty.getSourceAnnotationValue()
+ );
+ foundUnmappedProperty = true;
+ }
+ else {
+ if ( !hasSourceProperty( sourceParameter, mappedProperty.getSourcePropertyName() ) ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "The type of parameter \"%s\" has no property named \"%s\".",
+ mappedProperty.getSourceParameterName(),
+ mappedProperty.getSourcePropertyName()
+ ),
+ method.getExecutable(),
+ mappedProperty.getMirror(),
+ mappedProperty.getSourceAnnotationValue()
+ );
+ foundUnmappedProperty = true;
+ }
+ }
+
+ }
+ else if ( mappedProperty.getConstant().isEmpty()
+ && mappedProperty.getJavaExpression().isEmpty()
+ && !hasSourceProperty( mappedProperty.getSourcePropertyName() ) ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "No property named \"%s\" exists in source parameter(s).",
+ mappedProperty.getSourceName()
+ ),
+ method.getExecutable(),
+ mappedProperty.getMirror(),
+ mappedProperty.getSourceAnnotationValue()
+ );
+ foundUnmappedProperty = true;
+ }
+ if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Unknown property \"%s\" in return type %s.",
+ mappedProperty.getTargetName(),
+ method.getResultType()
+ ),
+ method.getExecutable(),
+ mappedProperty.getMirror(),
+ mappedProperty.getTargetAnnotationValue()
+ );
+ foundUnmappedProperty = true;
+ }
+ }
+ }
+ return !foundUnmappedProperty;
+ }
+
+ private void reportErrorForUnmappedTargetPropertiesIfRequired( SourceMethod method,
+ ReportingPolicy unmappedTargetPolicy,
+ Set targetProperties,
+ Set mappedTargetProperties,
+ Set ignoredTargetProperties ) {
+
+ Set unmappedTargetProperties = new HashSet();
+
+ for ( String property : targetProperties ) {
+ if ( !mappedTargetProperties.contains( property ) && !ignoredTargetProperties.contains( property ) ) {
+ unmappedTargetProperties.add( property );
+ }
+ }
+
+ if ( !unmappedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
+ ctx.getMessager().printMessage(
+ unmappedTargetPolicy.getDiagnosticKind(),
+ MessageFormat.format(
+ "Unmapped target {0,choice,1#property|1 getters = parameter.getType().getGetters();
+ return Executables.getPropertyNames( getters ).contains( propertyName );
+ }
+
+ }
+
+ private BeanMappingMethod(SourceMethod method,
List propertyMappings,
FactoryMethod factoryMethod) {
super( method );
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/Direct.java b/processor/src/main/java/org/mapstruct/ap/model/Direct.java
similarity index 95%
rename from processor/src/main/java/org/mapstruct/ap/model/assignment/Direct.java
rename to processor/src/main/java/org/mapstruct/ap/model/Direct.java
index 2815c1f26..232c827f3 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/Direct.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/Direct.java
@@ -16,12 +16,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.mapstruct.ap.model.assignment;
+package org.mapstruct.ap.model;
+import org.mapstruct.ap.model.assignment.Assignment;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/EnumMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/EnumMappingMethod.java
index ca5afee69..3fb0886d3 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/EnumMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/EnumMappingMethod.java
@@ -18,11 +18,18 @@
*/
package org.mapstruct.ap.model;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.tools.Diagnostic;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.source.EnumMapping;
+import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.Method;
+import org.mapstruct.ap.model.source.SourceMethod;
+import org.mapstruct.ap.util.Strings;
/**
* A {@link MappingMethod} which maps one enum type to another, optionally configured by one or more
@@ -34,7 +41,165 @@ public class EnumMappingMethod extends MappingMethod {
private final List enumMappings;
- public EnumMappingMethod(Method method, List enumMappings) {
+ public static class Builder {
+
+ private SourceMethod method;
+ private MappingContext ctx;
+
+ public Builder mappingContext( MappingContext mappingContext ) {
+ this.ctx = mappingContext;
+ return this;
+ }
+
+ public Builder souceMethod( SourceMethod sourceMethod ) {
+ this.method = sourceMethod;
+ return this;
+ }
+
+ public EnumMappingMethod build() {
+
+ if ( !reportErrorIfMappedEnumConstantsDontExist( method )
+ || !reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped( method ) ) {
+ return null;
+ }
+
+ List enumMappings = new ArrayList();
+
+ List sourceEnumConstants
+ = method.getSourceParameters().iterator().next().getType().getEnumConstants();
+ Map> mappings = method.getMappings();
+
+ for ( String enumConstant : sourceEnumConstants ) {
+ List mappedConstants = mappings.get( enumConstant );
+
+ if ( mappedConstants == null ) {
+ enumMappings.add( new EnumMapping( enumConstant, enumConstant ) );
+ }
+ else if ( mappedConstants.size() == 1 ) {
+ enumMappings.add( new EnumMapping(
+ enumConstant, mappedConstants.iterator().next().getTargetName() ) );
+ }
+ else {
+ List targetConstants = new ArrayList( mappedConstants.size() );
+ for ( Mapping mapping : mappedConstants ) {
+ targetConstants.add( mapping.getTargetName() );
+ }
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "One enum constant must not be mapped to more than one target constant, "
+ + "but constant %s is mapped to %s.",
+ enumConstant,
+ Strings.join( targetConstants, ", " )
+ ),
+ method.getExecutable()
+ );
+ }
+ }
+
+ return new EnumMappingMethod( method, enumMappings );
+ }
+
+ private boolean reportErrorIfMappedEnumConstantsDontExist( SourceMethod method ) {
+ // only report errors if this method itself is configured
+ if ( method.isConfiguredByReverseMappingMethod() ) {
+ return true;
+ }
+
+ List sourceEnumConstants =
+ method.getSourceParameters().iterator().next().getType().getEnumConstants();
+ List targetEnumConstants = method.getReturnType().getEnumConstants();
+
+ boolean foundIncorrectMapping = false;
+
+ for ( List mappedConstants : method.getMappings().values() ) {
+ for ( Mapping mappedConstant : mappedConstants ) {
+ if ( mappedConstant.getSourceName() == null ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ "A source constant must be specified for mappings of an enum mapping method.",
+ method.getExecutable(),
+ mappedConstant.getMirror()
+ );
+ foundIncorrectMapping = true;
+ }
+ else if ( !sourceEnumConstants.contains( mappedConstant.getSourceName() ) ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Constant %s doesn't exist in enum type %s.",
+ mappedConstant.getSourceName(),
+ method.getSourceParameters().iterator().next().getType()
+ ),
+ method.getExecutable(),
+ mappedConstant.getMirror(),
+ mappedConstant.getSourceAnnotationValue()
+ );
+ foundIncorrectMapping = true;
+ }
+ if ( mappedConstant.getTargetName() == null ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ "A target constant must be specified for mappings of an enum mapping method.",
+ method.getExecutable(),
+ mappedConstant.getMirror()
+ );
+ foundIncorrectMapping = true;
+ }
+ else if ( !targetEnumConstants.contains( mappedConstant.getTargetName() ) ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Constant %s doesn't exist in enum type %s.",
+ mappedConstant.getTargetName(),
+ method.getReturnType()
+ ),
+ method.getExecutable(),
+ mappedConstant.getMirror(),
+ mappedConstant.getTargetAnnotationValue()
+ );
+ foundIncorrectMapping = true;
+ }
+ }
+ }
+
+ return !foundIncorrectMapping;
+ }
+
+ private boolean reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped(
+ SourceMethod method ) {
+
+ List sourceEnumConstants =
+ method.getSourceParameters().iterator().next().getType().getEnumConstants();
+ List targetEnumConstants = method.getReturnType().getEnumConstants();
+ Set mappedSourceEnumConstants = method.getMappings().keySet();
+ List unmappedSourceEnumConstants = new ArrayList();
+
+ for ( String sourceEnumConstant : sourceEnumConstants ) {
+ if ( !targetEnumConstants.contains( sourceEnumConstant )
+ && !mappedSourceEnumConstants.contains( sourceEnumConstant ) ) {
+ unmappedSourceEnumConstants.add( sourceEnumConstant );
+ }
+ }
+
+ if ( !unmappedSourceEnumConstants.isEmpty() ) {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "The following constants from the source enum have no corresponding constant in the "
+ + "target enum and must be be mapped via @Mapping: %s",
+ Strings.join( unmappedSourceEnumConstants, ", " )
+ ),
+ method.getExecutable()
+ );
+ }
+
+ return unmappedSourceEnumConstants.isEmpty();
+ }
+
+ }
+
+ private EnumMappingMethod( Method method, List enumMappings ) {
super( method );
this.enumMappings = enumMappings;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
index bd9dfd94e..eb5b573de 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
@@ -18,8 +18,12 @@
*/
package org.mapstruct.ap.model;
+import org.mapstruct.ap.model.assignment.Assignment;
+import java.util.List;
import java.util.Set;
-import org.mapstruct.ap.model.assignment.TypeConversion;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import org.mapstruct.ap.model.assignment.SetterWrapper;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
@@ -37,7 +41,71 @@ public class IterableMappingMethod extends MappingMethod {
private final FactoryMethod factoryMethod;
private final boolean overridden;
- public IterableMappingMethod(Method method, Assignment parameterAssignment, FactoryMethod factoryMethod) {
+ public static class Builder {
+
+ private Method method;
+ private MappingContext ctx;
+ private String dateFormat;
+ private List qualifiers;
+
+ public Builder mappingContext( MappingContext mappingContext ) {
+ this.ctx = mappingContext;
+ return this;
+ }
+
+ public Builder method( Method sourceMethod ) {
+ this.method = sourceMethod;
+ return this;
+ }
+
+ public Builder dateFormat( String dateFormat ) {
+ this.dateFormat = dateFormat;
+ return this;
+ }
+
+ public Builder qualifiers( List qualifiers ) {
+ this.qualifiers = qualifiers;
+ return this;
+ }
+
+ public IterableMappingMethod build( ) {
+ Type sourceElementType =
+ method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 );
+ Type targetElementType =
+ method.getResultType().getTypeParameters().get( 0 );
+ String conversionStr =
+ Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() );
+
+
+ Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method,
+ "collection element",
+ sourceElementType,
+ targetElementType,
+ null, // there is no targetPropertyName
+ dateFormat,
+ qualifiers,
+ conversionStr
+ );
+
+ if ( assignment == null ) {
+ String message = String.format(
+ "Can't create implementation of method %s. Found no method nor built-in conversion for mapping "
+ + "source element type into target element type.",
+ method
+ );
+ method.printMessage( ctx.getMessager(), Diagnostic.Kind.ERROR, message );
+ }
+
+ // target accessor is setter, so decorate assignment as setter
+ assignment = new SetterWrapper( assignment, method.getThrownTypes() );
+
+ FactoryMethod factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
+ return new IterableMappingMethod( method, assignment, factoryMethod );
+ }
+ }
+
+
+ private IterableMappingMethod(Method method, Assignment parameterAssignment, FactoryMethod factoryMethod) {
super( method );
this.elementAssignment = parameterAssignment;
this.factoryMethod = factoryMethod;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
index 390ed064a..91b6f3bd1 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
@@ -18,8 +18,12 @@
*/
package org.mapstruct.ap.model;
+import org.mapstruct.ap.model.assignment.Assignment;
+import java.util.List;
import java.util.Set;
-import org.mapstruct.ap.model.assignment.TypeConversion;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import org.mapstruct.ap.model.assignment.LocalVarWrapper;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
@@ -38,7 +42,101 @@ public class MapMappingMethod extends MappingMethod {
private final FactoryMethod factoryMethod;
private final boolean overridden;
- public MapMappingMethod(Method method, Assignment keyAssignment, Assignment valueAssignment,
+ public static class Builder {
+
+ private String keyDateFormat;
+ private String valueDateFormat;
+ private List keyQualifiers;
+ private List valueQualifiers;
+ private Method method;
+ private MappingContext ctx;
+
+ public Builder mappingContext( MappingContext mappingContext ) {
+ this.ctx = mappingContext;
+ return this;
+ }
+
+ public Builder method( Method sourceMethod ) {
+ this.method = sourceMethod;
+ return this;
+ }
+
+ public Builder keyDateFormat( String keyDateFormat ) {
+ this.keyDateFormat = keyDateFormat;
+ return this;
+ }
+
+ public Builder valueDateFormat( String valueDateFormat ) {
+ this.valueDateFormat = valueDateFormat;
+ return this;
+ }
+
+ public Builder keyQualifiers( List keyQualifiers ) {
+ this.keyQualifiers = keyQualifiers;
+ return this;
+ }
+
+ public Builder valueQualifiers( List valueQualifiers ) {
+ this.valueQualifiers = valueQualifiers;
+ return this;
+ }
+
+ public MapMappingMethod build() {
+
+ List sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
+ List resultTypeParams = method.getResultType().getTypeParameters();
+
+ // find mapping method or conversion for key
+ Type keySourceType = sourceTypeParams.get( 0 );
+ Type keyTargetType = resultTypeParams.get( 0 );
+
+ Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment( method,
+ "map key",
+ keySourceType,
+ keyTargetType,
+ null, // there is no targetPropertyName
+ keyDateFormat,
+ keyQualifiers,
+ "entry.getKey()"
+ );
+
+ if ( keyAssignment == null ) {
+ String message = String.format( "Can't create implementation of method %s. Found no method nor "
+ + "built-in conversion for mapping source key type to target key type.", method );
+ method.printMessage( ctx.getMessager(), Diagnostic.Kind.ERROR, message );
+ }
+
+ // find mapping method or conversion for value
+ Type valueSourceType = sourceTypeParams.get( 1 );
+ Type valueTargetType = resultTypeParams.get( 1 );
+
+ Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment( method,
+ "map value",
+ valueSourceType,
+ valueTargetType,
+ null, // there is no targetPropertyName
+ valueDateFormat,
+ valueQualifiers,
+ "entry.getValue()"
+ );
+
+ if ( valueAssignment == null ) {
+ String message = String.format( "Can't create implementation of method %s. Found no method nor "
+ + "built-in conversion for mapping source value type to target value type.", method );
+ method.printMessage( ctx.getMessager(), Diagnostic.Kind.ERROR, message );
+ }
+
+ FactoryMethod factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx );
+
+ keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes() );
+ valueAssignment = new LocalVarWrapper( valueAssignment, method.getThrownTypes() );
+
+ return new MapMappingMethod( method, keyAssignment, valueAssignment, factoryMethod );
+ }
+
+ }
+
+ private MapMappingMethod(Method method, Assignment keyAssignment, Assignment valueAssignment,
FactoryMethod factoryMethod) {
super( method );
diff --git a/processor/src/main/java/org/mapstruct/ap/model/MappingContext.java b/processor/src/main/java/org/mapstruct/ap/model/MappingContext.java
new file mode 100644
index 000000000..5fa9427cc
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/model/MappingContext.java
@@ -0,0 +1,171 @@
+/**
+ * Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
+ * and/or other contributors as indicated by the @authors tag. See the
+ * copyright.txt file in the distribution for a full listing of all
+ * contributors.
+ *
+ * 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 org.mapstruct.ap.model;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import org.mapstruct.ap.model.common.TypeFactory;
+import org.mapstruct.ap.model.source.SourceMethod;
+import org.mapstruct.ap.option.Options;
+import org.mapstruct.ap.prism.MapperPrism;
+import org.mapstruct.ap.util.MapperConfig;
+
+/**
+ * This class provides the context for the builders.
+ *
+ *
+ * The following mappers make use of this context:
+ *
+ * - {@link BeanMappingMethod.Builder}
+ * - {@link PropertyMappingMethod.Builder}
+ * - {@link IterableMappingMethod.Builder}
+ * - {@link MapMappingMethod.Builder}
+ * - {@link EnumMappingMethod.Builder}
+ *
+ *
+ *
+ * The context provides:
+ *
+ * - Input for the building process, such as the source model (mapping methods found) and mapper references.
+ * - Required factory, utility, reporting methods for building the mappings.
+ * - Means to harbor results produced by the builders, such as forged- and virtual mapping methods that should be
+ * generated in a later stage.
+ *
+ *
+ *
+ * @author Sjaak Derksen
+ */
+public class MappingContext {
+
+ private final TypeFactory typeFactory;
+ private final Elements elementUtils;
+ private final Types typeUtils;
+ private final Messager messager;
+ private final Options options;
+ private final TypeElement mapperTypeElement;
+ private final List sourceModel;
+ private final List mapperReferences;
+ private MappingResolver mappingResolver;
+ private final List mappingsToGenerate = new ArrayList();
+
+ /**
+ * Private methods which are not present in the original mapper interface and are added to map certain property
+ * types.
+ */
+ private final Set usedVirtualMappings = new HashSet();
+
+
+
+ public MappingContext( TypeFactory typeFactory,
+ Elements elementUtils,
+ Types typeUtils,
+ Messager messager,
+ Options options,
+ TypeElement mapper,
+ List sourceModel ) {
+ this.typeFactory = typeFactory;
+ this.elementUtils = elementUtils;
+ this.typeUtils = typeUtils;
+ this.messager = messager;
+ this.options = options;
+ this.mapperTypeElement = mapper;
+ this.sourceModel = sourceModel;
+ this.mapperReferences = initReferencedMappers( mapper );
+ }
+
+ public TypeElement getMapperTypeElement() {
+ return mapperTypeElement;
+ }
+
+ public List getSourceModel() {
+ return sourceModel;
+ }
+
+ public List getMapperReferences() {
+ return mapperReferences;
+ }
+
+ public TypeFactory getTypeFactory() {
+ return typeFactory;
+ }
+
+ public Elements getElementUtils() {
+ return elementUtils;
+ }
+
+ public Types getTypeUtils() {
+ return typeUtils;
+ }
+
+ public Messager getMessager() {
+ return messager;
+ }
+
+ public Options getOptions() {
+ return options;
+ }
+
+ public MappingResolver getMappingResolver() {
+ return mappingResolver;
+ }
+
+ public void setMappingResolver(MappingResolver mappingResolver) {
+ this.mappingResolver = mappingResolver;
+ }
+
+ public List getMappingsToGenerate() {
+ return mappingsToGenerate;
+ }
+
+ public Set getUsedVirtualMappings() {
+ return usedVirtualMappings;
+ }
+
+ private List initReferencedMappers(TypeElement element) {
+ List result = new LinkedList();
+ List variableNames = new LinkedList();
+
+ MapperConfig mapperPrism = MapperConfig.getInstanceOn( element );
+
+ for ( TypeMirror usedMapper : mapperPrism.uses() ) {
+ DefaultMapperReference mapperReference = DefaultMapperReference.getInstance(
+ typeFactory.getType( usedMapper ),
+ MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null,
+ typeFactory,
+ variableNames
+ );
+
+ result.add( mapperReference );
+ variableNames.add( mapperReference.getVariableName() );
+ }
+
+ return result;
+ }
+
+
+
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/MappingResolver.java b/processor/src/main/java/org/mapstruct/ap/model/MappingResolver.java
new file mode 100644
index 000000000..3a7873763
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/model/MappingResolver.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
+ * and/or other contributors as indicated by the @authors tag. See the
+ * copyright.txt file in the distribution for a full listing of all
+ * contributors.
+ *
+ * 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 org.mapstruct.ap.model;
+
+import org.mapstruct.ap.model.assignment.Assignment;
+import java.util.List;
+import javax.lang.model.type.TypeMirror;
+import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.source.Method;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public interface MappingResolver {
+
+ /**
+ * returns a parameter assignment
+ *
+ * @param mappingMethod target mapping method
+ * @param mappedElement used for error messages
+ * @param sourceType parameter to match
+ * @param targetType return type to match
+ * @param targetPropertyName name of the target property
+ * @param dateFormat used for formatting dates in build in methods that need context information
+ * @param qualifiers used for further select the appropriate mapping method based on class and name
+ * @param sourceReference call to source type as string
+ *
+ * @return an assignment to a method parameter, which can either be:
+ *
+ * - MethodReference
+ * - TypeConversion
+ * - Direct Assignment (empty TargetAssignment)
+ * - null, no assignment found
+ *
+ */
+ Assignment getTargetAssignment(
+ Method mappingMethod,
+ String mappedElement,
+ Type sourceType,
+ Type targetType,
+ String targetPropertyName,
+ String dateFormat,
+ List qualifiers,
+ String sourceReference );
+
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java
similarity index 96%
rename from processor/src/main/java/org/mapstruct/ap/model/assignment/MethodReference.java
rename to processor/src/main/java/org/mapstruct/ap/model/MethodReference.java
index db03d823e..179ca5e0f 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/MethodReference.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java
@@ -16,17 +16,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.mapstruct.ap.model.assignment;
+package org.mapstruct.ap.model;
+import org.mapstruct.ap.model.assignment.Assignment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
-import org.mapstruct.ap.model.FactoryMethod;
-import org.mapstruct.ap.model.MapperReference;
-import org.mapstruct.ap.model.MappingMethod;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/model/PropertyMapping.java
index 10c151b55..bdb9066be 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/PropertyMapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/PropertyMapping.java
@@ -18,10 +18,30 @@
*/
package org.mapstruct.ap.model;
+import org.mapstruct.ap.model.assignment.Assignment;
+import java.util.List;
import java.util.Set;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import static org.mapstruct.ap.model.assignment.Assignment.AssignmentType.DIRECT;
+import static org.mapstruct.ap.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED;
+import static org.mapstruct.ap.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED_MAPPED;
+import org.mapstruct.ap.model.assignment.AdderWrapper;
+import org.mapstruct.ap.model.assignment.GetterCollectionOrMapWrapper;
+import org.mapstruct.ap.model.assignment.NewCollectionOrMapWrapper;
+import org.mapstruct.ap.model.assignment.NullCheckWrapper;
+import org.mapstruct.ap.model.assignment.SetterCollectionOrMapWrapper;
+import org.mapstruct.ap.model.assignment.SetterWrapper;
import org.mapstruct.ap.model.common.ModelElement;
+import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.source.ForgedMethod;
+import org.mapstruct.ap.model.source.Mapping;
+import org.mapstruct.ap.model.source.SourceMethod;
+import org.mapstruct.ap.util.Executables;
/**
@@ -41,12 +61,414 @@ public class PropertyMapping extends ModelElement {
private final Assignment assignment;
+ public static class PropertyMappingBuilder {
+
+ private MappingContext ctx;
+ private SourceMethod method;
+ private ExecutableElement targetAccessor;
+ private String targetPropertyName;
+ private Parameter parameter;
+
+ public PropertyMappingBuilder mappingContext(MappingContext mappingContext) {
+ this.ctx = mappingContext;
+ return this;
+ }
+
+ public PropertyMappingBuilder souceMethod( SourceMethod sourceMethod ) {
+ this.method = sourceMethod;
+ return this;
+ }
+
+ public PropertyMappingBuilder targetAccessor( ExecutableElement targetAccessor ) {
+ this.targetAccessor = targetAccessor;
+ return this;
+ }
+
+ public PropertyMappingBuilder targetPropertyName( String targetPropertyName ) {
+ this.targetPropertyName = targetPropertyName;
+ return this;
+ }
+
+ public PropertyMappingBuilder parameter( Parameter parameter ) {
+ this.parameter = parameter;
+ return this;
+ }
+
+ private enum TargetAccessorType {
+
+ GETTER,
+ SETTER,
+ ADDER
+ }
+
+ public PropertyMapping build() {
+
+ // check if there's a mapping defined
+ Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
+ String dateFormat = null;
+ List qualifiers = null;
+ String sourcePropertyName;
+ if ( mapping != null ) {
+ dateFormat = mapping.getDateFormat();
+ qualifiers = mapping.getQualifiers();
+ sourcePropertyName = mapping.getSourcePropertyName();
+ }
+ else {
+ sourcePropertyName = targetPropertyName;
+ }
+
+ List sourceGetters = parameter.getType().getGetters();
+
+ // then iterate over source accessors (assuming the source is a bean)
+ for ( ExecutableElement sourceAccessor : sourceGetters ) {
+
+ List sourceMappings = method.getMappings().get( sourcePropertyName );
+ if ( method.getMappings().containsKey( sourcePropertyName ) ) {
+ for ( Mapping sourceMapping : sourceMappings ) {
+ boolean mapsToOtherTarget = !sourceMapping.getTargetName().equals( targetPropertyName );
+ if ( Executables.getPropertyName( sourceAccessor ).equals( sourcePropertyName )
+ && !mapsToOtherTarget ) {
+ return getPropertyMapping( sourceAccessor, dateFormat, qualifiers );
+ }
+ }
+ }
+ else if ( Executables.getPropertyName( sourceAccessor ).equals( sourcePropertyName ) ) {
+ return getPropertyMapping( sourceAccessor, dateFormat, qualifiers );
+ }
+ }
+ return null;
+ }
+
+ private PropertyMapping getPropertyMapping( ExecutableElement sourceAccessor,
+ String dateFormat,
+ List qualifiers ) {
+
+ Type sourceType;
+ Type targetType;
+ TargetAccessorType targetAccessorType;
+ String sourceReference = parameter.getName() + "." + sourceAccessor.getSimpleName().toString() + "()";
+ String iteratorReference = null;
+ boolean sourceIsCollection = false;
+ if ( Executables.isSetterMethod( targetAccessor ) ) {
+ sourceType = ctx.getTypeFactory().getReturnType( sourceAccessor );
+ targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
+ targetAccessorType = TargetAccessorType.SETTER;
+ }
+ else if ( Executables.isAdderMethod( targetAccessor ) ) {
+ sourceType = ctx.getTypeFactory().getReturnType( sourceAccessor );
+ if ( sourceType.isCollectionType() ) {
+ sourceIsCollection = true;
+ sourceType = sourceType.getTypeParameters().get( 0 );
+ iteratorReference = Executables.getElementNameForAdder( targetAccessor );
+ }
+ targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
+ targetAccessorType = TargetAccessorType.ADDER;
+ }
+ else {
+ sourceType = ctx.getTypeFactory().getReturnType( sourceAccessor );
+ targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
+ targetAccessorType = TargetAccessorType.GETTER;
+ }
+ String sourcePropertyName = Executables.getPropertyName( sourceAccessor );
+ String mappedElement = "property '" + sourcePropertyName + "'";
+
+ Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method,
+ mappedElement,
+ sourceType,
+ targetType,
+ targetPropertyName,
+ dateFormat,
+ qualifiers,
+ iteratorReference != null ? iteratorReference : sourceReference
+ );
+
+ if ( assignment == null ) {
+ assignment = forgeMapping( sourceType, targetType, sourceReference, method.getExecutable() );
+ }
+
+ if ( assignment != null ) {
+
+ if ( targetType.isCollectionOrMapType() ) {
+
+ // wrap the setter in the collection / map initializers
+ if ( targetAccessorType == TargetAccessorType.SETTER ) {
+
+ // wrap the assignment in a new Map or Collection implementation if this is not done in a
+ // mapping method. Note, typeconversons do not apply to collections or maps
+ Assignment newCollectionOrMap = null;
+ if ( assignment.getType() == DIRECT ) {
+ newCollectionOrMap =
+ new NewCollectionOrMapWrapper( assignment, targetType.getImportTypes() );
+ newCollectionOrMap = new SetterWrapper( newCollectionOrMap, method.getThrownTypes() );
+ }
+
+ // wrap the assignment in the setter method
+ assignment = new SetterWrapper( assignment, method.getThrownTypes() );
+
+ // target accessor is setter, so wrap the setter in setter map/ collection handling
+ assignment = new SetterCollectionOrMapWrapper(
+ assignment,
+ targetAccessor.getSimpleName().toString(),
+ newCollectionOrMap
+ );
+ }
+ else {
+ // wrap the assignment in the setter method
+ assignment = new SetterWrapper( assignment, method.getThrownTypes() );
+
+ // target accessor is getter, so wrap the setter in getter map/ collection handling
+ assignment = new GetterCollectionOrMapWrapper( assignment );
+ }
+
+ // For collections and maps include a null check, when the assignment type is DIRECT.
+ // for mapping methods (builtin / custom), the mapping method is responsible for the
+ // null check. Typeconversions do not apply to collections and maps.
+ if ( assignment.getType() == DIRECT ) {
+ assignment = new NullCheckWrapper( assignment );
+ }
+ }
+ else {
+ if ( targetAccessorType == TargetAccessorType.SETTER ) {
+ assignment = new SetterWrapper( assignment, method.getThrownTypes() );
+ if ( !sourceType.isPrimitive()
+ && ( assignment.getType() == TYPE_CONVERTED
+ || assignment.getType() == TYPE_CONVERTED_MAPPED
+ || assignment.getType() == DIRECT && targetType.isPrimitive() ) ) {
+ // for primitive types null check is not possible at all, but a conversion needs
+ // a null check.
+ assignment = new NullCheckWrapper( assignment );
+ }
+ }
+ else {
+ // TargetAccessorType must be ADDER
+ if ( sourceIsCollection ) {
+ assignment = new AdderWrapper(
+ assignment,
+ method.getThrownTypes(),
+ sourceReference,
+ sourceType
+ );
+ }
+ else {
+ // Possibly adding null to a target collection. So should be surrounded by an null check.
+ assignment = new SetterWrapper( assignment, method.getThrownTypes() );
+ assignment = new NullCheckWrapper( assignment );
+ }
+ }
+ }
+ }
+ else {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Can't map property \"%s %s\" to \"%s %s\".",
+ sourceType,
+ sourcePropertyName,
+ targetType,
+ targetPropertyName
+ ),
+ method.getExecutable()
+ );
+ }
+ return new PropertyMapping(
+ parameter.getName(),
+ targetAccessor.getSimpleName().toString(),
+ targetType,
+ assignment
+ );
+ }
+
+ private Assignment forgeMapping( Type sourceType, Type targetType, String sourceReference, Element element ) {
+
+ Assignment assignment = null;
+ if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
+
+ ForgedMethod methodToGenerate = new ForgedMethod( sourceType, targetType, element );
+ IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder( );
+
+
+ IterableMappingMethod iterableMappingMethod = builder
+ .mappingContext( ctx )
+ .method( methodToGenerate )
+ .build();
+
+ if ( !ctx.getMappingsToGenerate().contains( iterableMappingMethod ) ) {
+ ctx.getMappingsToGenerate().add( iterableMappingMethod );
+ }
+ assignment = AssignmentFactory.createMethodReference( methodToGenerate, null, targetType );
+ assignment.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
+
+ }
+ else if ( sourceType.isMapType() && targetType.isMapType() ) {
+
+ ForgedMethod methodToGenerate = new ForgedMethod( sourceType, targetType, element );
+
+ MapMappingMethod.Builder builder = new MapMappingMethod.Builder( );
+ MapMappingMethod mapMappingMethod = builder
+ .mappingContext( ctx )
+ .method( methodToGenerate )
+ .build();
+
+ if ( !ctx.getMappingsToGenerate().contains( mapMappingMethod ) ) {
+ ctx.getMappingsToGenerate().add( mapMappingMethod );
+ }
+ assignment = AssignmentFactory.createMethodReference( methodToGenerate, null, targetType );
+ assignment.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
+ }
+ return assignment;
+ }
+ }
+
+ public static class ConstantMappingBuilder {
+
+ private MappingContext ctx;
+ private SourceMethod method;
+ private String constantExpression;
+ private ExecutableElement targetAccessor;
+ private String dateFormat;
+ private List qualifiers;
+
+ public ConstantMappingBuilder mappingContext(MappingContext mappingContext) {
+ this.ctx = mappingContext;
+ return this;
+ }
+
+ public ConstantMappingBuilder sourceMethod( SourceMethod sourceMethod ) {
+ this.method = sourceMethod;
+ return this;
+ }
+
+ public ConstantMappingBuilder constantExpression( String constantExpression ) {
+ this.constantExpression = constantExpression;
+ return this;
+ }
+
+ public ConstantMappingBuilder targetAccessor( ExecutableElement targetAccessor ) {
+ this.targetAccessor = targetAccessor;
+ return this;
+ }
+
+ public ConstantMappingBuilder dateFormat( String dateFormat ) {
+ this.dateFormat = dateFormat;
+ return this;
+ }
+
+ public ConstantMappingBuilder qualifiers( List qualifiers ) {
+ this.qualifiers = qualifiers;
+ return this;
+ }
+
+ public PropertyMapping build() {
+
+ // source
+ String mappedElement = "constant '" + constantExpression + "'";
+ Type sourceType = ctx.getTypeFactory().getType( String.class );
+
+ // target
+ Type targetType;
+ if ( Executables.isSetterMethod( targetAccessor ) ) {
+ targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
+ }
+ else {
+ targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
+ }
+
+ String targetPropertyName = Executables.getPropertyName( targetAccessor );
+
+ Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method,
+ mappedElement,
+ sourceType,
+ targetType,
+ targetPropertyName,
+ dateFormat,
+ qualifiers,
+ constantExpression
+ );
+
+ if ( assignment != null ) {
+
+ // target accessor is setter, so decorate assignment as setter
+ assignment = new SetterWrapper( assignment, method.getThrownTypes() );
+
+ // wrap when dealing with getter only on target
+ if ( Executables.isGetterMethod( targetAccessor ) ) {
+ assignment = new GetterCollectionOrMapWrapper( assignment );
+ }
+ }
+ else {
+ ctx.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Can't map \"%s %s\" to \"%s %s\".",
+ sourceType,
+ constantExpression,
+ targetType,
+ targetPropertyName
+ ),
+ method.getExecutable()
+ );
+ }
+
+ return new PropertyMapping( targetAccessor.getSimpleName().toString(), targetType, assignment );
+ }
+ }
+
+ public static class JavaExpressionMappingBuilder {
+
+ private MappingContext ctx;
+ private SourceMethod method;
+ private String javaExpression;
+ private ExecutableElement targetAccessor;
+
+ public JavaExpressionMappingBuilder mappingContext(MappingContext mappingContext) {
+ this.ctx = mappingContext;
+ return this;
+ }
+
+ public JavaExpressionMappingBuilder souceMethod( SourceMethod sourceMethod ) {
+ this.method = sourceMethod;
+ return this;
+ }
+
+ public JavaExpressionMappingBuilder javaExpression( String javaExpression ) {
+ this.javaExpression = javaExpression;
+ return this;
+ }
+
+ public JavaExpressionMappingBuilder targetAccessor( ExecutableElement targetAccessor ) {
+ this.targetAccessor = targetAccessor;
+ return this;
+ }
+
+ public PropertyMapping build() {
+
+ Assignment assignment = AssignmentFactory.createSimple( javaExpression );
+ assignment = new SetterWrapper( assignment, method.getThrownTypes() );
+
+ Type targetType;
+ if ( Executables.isSetterMethod( targetAccessor ) ) {
+ targetType = ctx.getTypeFactory().getSingleParameter( targetAccessor ).getType();
+ }
+ else {
+ targetType = ctx.getTypeFactory().getReturnType( targetAccessor );
+
+ // target accessor is getter, so wrap the setter in getter map/ collection handling
+ assignment = new GetterCollectionOrMapWrapper( assignment );
+ }
+
+ return new PropertyMapping( targetAccessor.getSimpleName().toString(), targetType, assignment );
+ }
+
+
+ }
+
+
// Constructor for creating mappings of constant expressions.
- public PropertyMapping(String targetAccessorName, Type targetType, Assignment propertyAssignment) {
+ private PropertyMapping(String targetAccessorName, Type targetType, Assignment propertyAssignment) {
this( null, targetAccessorName, targetType, propertyAssignment );
}
- public PropertyMapping(String sourceBeanName, String targetAccessorName, Type targetType, Assignment assignment) {
+ private PropertyMapping(String sourceBeanName, String targetAccessorName, Type targetType, Assignment assignment) {
this.sourceBeanName = sourceBeanName;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/TypeConversion.java b/processor/src/main/java/org/mapstruct/ap/model/TypeConversion.java
similarity index 97%
rename from processor/src/main/java/org/mapstruct/ap/model/assignment/TypeConversion.java
rename to processor/src/main/java/org/mapstruct/ap/model/TypeConversion.java
index 829aa6ad6..a77b23d9e 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/TypeConversion.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/TypeConversion.java
@@ -16,13 +16,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.mapstruct.ap.model.assignment;
+package org.mapstruct.ap.model;
+import org.mapstruct.ap.model.assignment.Assignment;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/AdderWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/AdderWrapper.java
index 49cf519b8..5480d9dc5 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/AdderWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/AdderWrapper.java
@@ -23,7 +23,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.Type;
/**
diff --git a/processor/src/main/java/org/mapstruct/ap/model/Assignment.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/Assignment.java
similarity index 98%
rename from processor/src/main/java/org/mapstruct/ap/model/Assignment.java
rename to processor/src/main/java/org/mapstruct/ap/model/assignment/Assignment.java
index a14ed8092..4085ff7e0 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/Assignment.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/Assignment.java
@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.mapstruct.ap.model;
+package org.mapstruct.ap.model.assignment;
import java.util.List;
import java.util.Set;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentFactory.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentFactory.java
deleted file mode 100644
index e307d376a..000000000
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentFactory.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
- * and/or other contributors as indicated by the @authors tag. See the
- * copyright.txt file in the distribution for a full listing of all
- * contributors.
- *
- * 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 org.mapstruct.ap.model.assignment;
-
-import java.util.List;
-import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
-import org.mapstruct.ap.model.FactoryMethod;
-import org.mapstruct.ap.model.MapperReference;
-import org.mapstruct.ap.model.common.ConversionContext;
-import org.mapstruct.ap.model.common.Type;
-import org.mapstruct.ap.model.source.Method;
-import org.mapstruct.ap.model.source.SourceMethod;
-import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
-
-/**
- * Factory class for creating all types of assignments
- *
- * @author Sjaak Derksen
- */
-public class AssignmentFactory {
-
- private AssignmentFactory() {
- }
-
- public static Assignment createTypeConversion(Set importTypes, List exceptionTypes, String expression) {
- return new TypeConversion( importTypes, exceptionTypes, expression );
- }
-
- public static FactoryMethod createFactory(SourceMethod method, MapperReference declaringMapper) {
- return new MethodReference( method, declaringMapper, null );
- }
-
- public static Assignment createMethodReference(Method method, MapperReference declaringMapper,
- Type targetType) {
- return new MethodReference( method, declaringMapper, targetType );
- }
-
- public static Assignment createMethodReference(BuiltInMethod method, ConversionContext contextParam) {
- return new MethodReference( method, contextParam );
- }
-
- public static Direct createSimple(String sourceRef) {
- return new Direct( sourceRef );
- }
-
-}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentWrapper.java
index 1e5901c84..5c6d20599 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/AssignmentWrapper.java
@@ -20,7 +20,6 @@ package org.mapstruct.ap.model.assignment;
import java.util.List;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/GetterCollectionOrMapWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/GetterCollectionOrMapWrapper.java
index 2bcf383d2..a5ce9f64c 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/GetterCollectionOrMapWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/GetterCollectionOrMapWrapper.java
@@ -18,8 +18,6 @@
*/
package org.mapstruct.ap.model.assignment;
-import org.mapstruct.ap.model.Assignment;
-
/**
* This wrapper handles the situation were an assignment must be done via a target getter method because there
* is no setter available.
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/LocalVarWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/LocalVarWrapper.java
index cf3fb7693..e89bc1108 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/LocalVarWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/LocalVarWrapper.java
@@ -20,7 +20,6 @@ package org.mapstruct.ap.model.assignment;
import java.util.ArrayList;
import java.util.List;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.Type;
/**
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/NewCollectionOrMapWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/NewCollectionOrMapWrapper.java
index 935e8fb5b..07e85917a 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/NewCollectionOrMapWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/NewCollectionOrMapWrapper.java
@@ -21,7 +21,6 @@ package org.mapstruct.ap.model.assignment;
import java.util.HashSet;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.Type;
/**
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/NullCheckWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/NullCheckWrapper.java
index 162dbd208..a01bed66c 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/NullCheckWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/NullCheckWrapper.java
@@ -18,8 +18,6 @@
*/
package org.mapstruct.ap.model.assignment;
-import org.mapstruct.ap.model.Assignment;
-
/**
* Wraps the assignment in a null check.
*
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterCollectionOrMapWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterCollectionOrMapWrapper.java
index a62313abd..4f33e9175 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterCollectionOrMapWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterCollectionOrMapWrapper.java
@@ -21,7 +21,6 @@ package org.mapstruct.ap.model.assignment;
import java.util.HashSet;
import java.util.Set;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.Type;
/**
diff --git a/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterWrapper.java b/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterWrapper.java
index 110f9bd15..152eb113f 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterWrapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/assignment/SetterWrapper.java
@@ -20,7 +20,6 @@ package org.mapstruct.ap.model.assignment;
import java.util.ArrayList;
import java.util.List;
-import org.mapstruct.ap.model.Assignment;
import org.mapstruct.ap.model.common.Type;
/**
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
index 0615045d6..98ea0eea1 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
@@ -18,18 +18,13 @@
*/
package org.mapstruct.ap.processor;
-import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
@@ -38,48 +33,24 @@ import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
-import org.mapstruct.CollectionMappingStrategy;
-import org.mapstruct.ap.model.Assignment;
-import static org.mapstruct.ap.model.Assignment.AssignmentType.DIRECT;
-import static org.mapstruct.ap.model.Assignment.AssignmentType.TYPE_CONVERTED;
-import static org.mapstruct.ap.model.Assignment.AssignmentType.TYPE_CONVERTED_MAPPED;
import org.mapstruct.ap.model.BeanMappingMethod;
import org.mapstruct.ap.model.Decorator;
-import org.mapstruct.ap.model.DefaultMapperReference;
import org.mapstruct.ap.model.DelegatingMethod;
import org.mapstruct.ap.model.EnumMappingMethod;
-import org.mapstruct.ap.model.FactoryMethod;
import org.mapstruct.ap.model.IterableMappingMethod;
import org.mapstruct.ap.model.MapMappingMethod;
import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.model.MappingMethod;
-import org.mapstruct.ap.model.PropertyMapping;
-import org.mapstruct.ap.model.assignment.AdderWrapper;
-import org.mapstruct.ap.model.assignment.AssignmentFactory;
-import org.mapstruct.ap.model.assignment.GetterCollectionOrMapWrapper;
-import org.mapstruct.ap.model.assignment.LocalVarWrapper;
-import org.mapstruct.ap.model.assignment.NewCollectionOrMapWrapper;
-import org.mapstruct.ap.model.assignment.NullCheckWrapper;
-import org.mapstruct.ap.model.assignment.SetterCollectionOrMapWrapper;
-import org.mapstruct.ap.model.assignment.SetterWrapper;
-import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory;
-import org.mapstruct.ap.model.source.EnumMapping;
-import org.mapstruct.ap.model.source.ForgedMethod;
import org.mapstruct.ap.model.source.Mapping;
-import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.SourceMethod;
-import org.mapstruct.ap.model.source.selector.MethodSelectors;
import org.mapstruct.ap.option.Options;
-import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.DecoratedWithPrism;
-import org.mapstruct.ap.prism.MapperPrism;
-import org.mapstruct.ap.processor.creation.MappingResolver;
-import org.mapstruct.ap.util.Executables;
+import org.mapstruct.ap.model.MappingContext;
+import org.mapstruct.ap.processor.creation.MappingResolverImpl;
import org.mapstruct.ap.util.MapperConfig;
-import org.mapstruct.ap.util.Strings;
/**
* A {@link ModelElementProcessor} which creates a {@link Mapper} from the given
@@ -89,19 +60,13 @@ import org.mapstruct.ap.util.Strings;
*/
public class MapperCreationProcessor implements ModelElementProcessor, Mapper> {
- private enum TargetAccessorType {
- GETTER,
- SETTER,
- ADDER
- }
private Elements elementUtils;
private Types typeUtils;
private Messager messager;
private Options options;
private TypeFactory typeFactory;
- private MappingResolver mappingResolver;
- private final List mappingsToGenerate = new ArrayList();
+ private MappingContext mappingContext;
@Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List sourceModel) {
@@ -110,8 +75,17 @@ public class MapperCreationProcessor implements ModelElementProcessor methods) {
- List mapperReferences = getReferencedMappers( element );
- List mappingMethods = getMappingMethods( mapperReferences, methods, element );
- mappingMethods.addAll( mappingResolver.getVirtualMethodsToGenerate() );
- mappingMethods.addAll( mappingsToGenerate );
+ List mapperReferences = mappingContext.getMapperReferences();
+ List mappingMethods = getMappingMethods( methods );
+ mappingMethods.addAll( mappingContext.getUsedVirtualMappings() );
+ mappingMethods.addAll( mappingContext.getMappingsToGenerate() );
Mapper mapper = new Mapper.Builder()
.element( element )
@@ -140,36 +114,6 @@ public class MapperCreationProcessor implements ModelElementProcessor methods) {
DecoratedWithPrism decoratorPrism = DecoratedWithPrism.getInstanceOn( element );
@@ -245,27 +189,6 @@ public class MapperCreationProcessor implements ModelElementProcessor getReferencedMappers(TypeElement element) {
- List mapperReferences = new LinkedList();
- List variableNames = new LinkedList();
-
- MapperConfig mapperPrism = MapperConfig.getInstanceOn( element );
-
- for ( TypeMirror usedMapper : mapperPrism.uses() ) {
- DefaultMapperReference mapperReference = DefaultMapperReference.getInstance(
- typeFactory.getType( usedMapper ),
- MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null,
- typeFactory,
- variableNames
- );
-
- mapperReferences.add( mapperReference );
- variableNames.add( mapperReference.getVariableName() );
- }
-
- return mapperReferences;
- }
-
private SortedSet getExtraImports(TypeElement element) {
SortedSet extraImports = new TreeSet();
@@ -280,8 +203,7 @@ public class MapperCreationProcessor implements ModelElementProcessor getMappingMethods(List mapperReferences, List methods,
- TypeElement element) {
+ private List getMappingMethods(List methods ) {
List mappingMethods = new ArrayList();
for ( SourceMethod method : methods ) {
@@ -293,6 +215,8 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences, List methods,
- Type returnType) {
- FactoryMethod result = null;
- for ( SourceMethod method : methods ) {
- if ( !method.overridesMethod() && !method.isIterableMapping() && !method.isMapMapping()
- && method.getSourceParameters().isEmpty() ) {
-
- List parameterTypes =
- MethodSelectors.getParameterTypes( typeFactory, method.getParameters(), null, returnType );
-
- if ( method.matches( parameterTypes, returnType ) ) {
- if ( result == null ) {
- MapperReference mapperReference = findMapperReference( mapperReferences, method );
- result = AssignmentFactory.createFactory( method, mapperReference );
- }
- else {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "Ambiguous factory methods: \"%s\" conflicts with \"%s\".",
- result,
- method
- ),
- method.getExecutable()
- );
- }
- }
- }
- }
- return result;
- }
-
private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType(SourceMethod method) {
if ( method.getReturnType().getTypeMirror().getKind() != TypeKind.VOID &&
method.getReturnType().isInterface() &&
@@ -449,239 +353,6 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences,
- List methods,
- SourceMethod method,
- ExecutableElement targetAccessor,
- String targetPropertyName,
- Parameter parameter) {
-
- // check if there's a mapping defined
- Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
- String dateFormat = null;
- List qualifiers = null;
- String sourcePropertyName;
- if ( mapping != null ) {
- dateFormat = mapping.getDateFormat();
- qualifiers = mapping.getQualifiers();
- sourcePropertyName = mapping.getSourcePropertyName();
- }
- else {
- sourcePropertyName = targetPropertyName;
- }
-
- List sourceGetters = parameter.getType().getGetters();
-
- // then iterate over source accessors (assuming the source is a bean)
- for ( ExecutableElement sourceAccessor : sourceGetters ) {
-
- List sourceMappings = method.getMappings().get( sourcePropertyName );
- if ( method.getMappings().containsKey( sourcePropertyName ) ) {
- for ( Mapping sourceMapping : sourceMappings ) {
- boolean mapsToOtherTarget = !sourceMapping.getTargetName().equals( targetPropertyName );
- if ( Executables.getPropertyName( sourceAccessor ).equals( sourcePropertyName ) &&
- !mapsToOtherTarget ) {
- return getPropertyMapping(
- mapperReferences,
- methods,
- method,
- parameter,
- sourceAccessor,
- targetAccessor,
- targetPropertyName,
- dateFormat,
- qualifiers
- );
- }
- }
- }
- else if ( Executables.getPropertyName( sourceAccessor ).equals( sourcePropertyName ) ) {
- return getPropertyMapping(
- mapperReferences,
- methods,
- method,
- parameter,
- sourceAccessor,
- targetAccessor,
- targetPropertyName,
- dateFormat,
- qualifiers
- );
- }
- }
- return null;
- }
-
- private BeanMappingMethod getBeanMappingMethod(List mapperReferences, List methods,
- SourceMethod method, TypeElement element) {
-
- // fetch settings from element to implement
- ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
- CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy( element );
-
- List propertyMappings = new ArrayList();
- Set mappedTargetProperties = new HashSet();
- Set ignoredTargetProperties = new HashSet();
-
- if ( !reportErrorIfMappedPropertiesDontExist( method ) ) {
- return null;
- }
-
- // collect all target accessors
- List targetAccessors = new ArrayList();
- targetAccessors.addAll( method.getResultType().getSetters() );
- targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() );
-
- for ( ExecutableElement targetAccessor : targetAccessors ) {
- String targetPropertyName = Executables.getPropertyName( targetAccessor );
-
- Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
-
- if ( mapping != null && mapping.isIgnored() ) {
- ignoredTargetProperties.add( targetPropertyName );
- continue;
- }
-
- // A target access is in general a setter method on the target object. However, in case of collections,
- // the current target accessor can also be a getter method.
- //
- // The following if block, checks if the target accessor should be overruled by an add method.
- if ( cmStrategy.equals( CollectionMappingStrategy.SETTER_PREFERRED ) ||
- cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
-
- // first check if there's a setter method.
- ExecutableElement adderMethod = null;
- if ( Executables.isSetterMethod( targetAccessor ) ) {
- Type targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
- // ok, the current accessor is a setter. So now the strategy determines what to use
- if ( cmStrategy.equals( CollectionMappingStrategy.ADDER_PREFERRED ) ) {
- adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
- }
- }
- else if ( Executables.isGetterMethod( targetAccessor ) ) {
- // the current accessor is a getter (no setter available). But still, an add method is according
- // to the above strategy (SETTER_PREFERRED || ADDER_PREFERRED) preferred over the getter.
- Type targetType = typeFactory.getReturnType( targetAccessor );
- adderMethod = method.getResultType().getAdderForType( targetType, targetPropertyName );
- }
- if ( adderMethod != null ) {
- // an adder has been found (according strategy) so overrule current choice.
- targetAccessor = adderMethod;
- }
- }
-
- PropertyMapping propertyMapping = null;
- if ( mapping != null ) {
- if ( mapping.getSourceParameterName() != null ) {
- // this is a parameterized property, so sourceParameter.property
- Parameter parameter = method.getSourceParameter( mapping.getSourceParameterName() );
- propertyMapping = getPropertyMapping(
- mapperReferences,
- methods,
- method,
- targetAccessor,
- targetPropertyName,
- parameter
- );
- }
- else if ( Executables.isSetterMethod( targetAccessor ) ||
- Executables.isGetterMethod( targetAccessor ) ) {
-
- if ( !mapping.getConstant().isEmpty() ) {
- // its a constant
- propertyMapping = getConstantMapping(
- mapperReferences,
- methods,
- method,
- "\"" + mapping.getConstant() + "\"",
- targetAccessor,
- mapping.getDateFormat(),
- mapping.getQualifiers()
- );
- }
-
- else if ( !mapping.getJavaExpression().isEmpty() ) {
- // its an expression
- propertyMapping = getJavaExpressionMapping(
- method,
- mapping.getJavaExpression(),
- targetAccessor
- );
- }
- }
- }
-
- if ( propertyMapping == null ) {
- for ( Parameter sourceParameter : method.getSourceParameters() ) {
- PropertyMapping newPropertyMapping = getPropertyMapping(
- mapperReferences,
- methods,
- method,
- targetAccessor,
- targetPropertyName,
- sourceParameter
- );
- if ( propertyMapping != null && newPropertyMapping != null ) {
- messager.printMessage(
- Kind.ERROR,
- "Several possible source properties for target property \"" + targetPropertyName + "\".",
- method.getExecutable()
- );
- break;
- }
- else if ( newPropertyMapping != null ) {
- propertyMapping = newPropertyMapping;
- }
- }
- }
-
- if ( propertyMapping != null ) {
- propertyMappings.add( propertyMapping );
- mappedTargetProperties.add( targetPropertyName );
- }
- }
-
- Set targetProperties = Executables.getPropertyNames( targetAccessors );
-
- reportErrorForUnmappedTargetPropertiesIfRequired(
- method,
- unmappedTargetPolicy,
- targetProperties,
- mappedTargetProperties,
- ignoredTargetProperties
- );
-
- FactoryMethod factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
- return new BeanMappingMethod( method, propertyMappings, factoryMethod );
- }
-
- private void reportErrorForUnmappedTargetPropertiesIfRequired(SourceMethod method,
- ReportingPolicy unmappedTargetPolicy,
- Set targetProperties,
- Set mappedTargetProperties,
- Set ignoredTargetProperties) {
-
- Set unmappedTargetProperties = new HashSet();
-
- for ( String property : targetProperties ) {
- if ( !mappedTargetProperties.contains( property ) && !ignoredTargetProperties.contains( property ) ) {
- unmappedTargetProperties.add( property );
- }
- }
-
- if ( !unmappedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
- messager.printMessage(
- unmappedTargetPolicy.getDiagnosticKind(),
- MessageFormat.format(
- "Unmapped target {0,choice,1#property|1 rawMethods, SourceMethod method) {
for ( SourceMethod oneMethod : rawMethods ) {
if ( oneMethod.reverses( method ) ) {
@@ -690,631 +361,4 @@ public class MapperCreationProcessor implements ModelElementProcessor getters = parameter.getType().getGetters();
- return Executables.getPropertyNames( getters ).contains( propertyName );
- }
-
- private boolean reportErrorIfMappedPropertiesDontExist(SourceMethod method) {
- // only report errors if this method itself is configured
- if ( method.isConfiguredByReverseMappingMethod() ) {
- return true;
- }
-
- // collect all target accessors
- List targetAccessors = new ArrayList();
- targetAccessors.addAll( method.getResultType().getSetters() );
- targetAccessors.addAll( method.getResultType().getAlternativeTargetAccessors() );
-
- Set targetProperties = Executables.getPropertyNames( targetAccessors );
-
- boolean foundUnmappedProperty = false;
-
- for ( List mappedProperties : method.getMappings().values() ) {
- for ( Mapping mappedProperty : mappedProperties ) {
- if ( mappedProperty.isIgnored() ) {
- continue;
- }
-
- if ( mappedProperty.getSourceParameterName() != null ) {
- Parameter sourceParameter = method.getSourceParameter( mappedProperty.getSourceParameterName() );
-
- if ( sourceParameter == null ) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "Method has no parameter named \"%s\".",
- mappedProperty.getSourceParameterName()
- ),
- method.getExecutable(),
- mappedProperty.getMirror(),
- mappedProperty.getSourceAnnotationValue()
- );
- foundUnmappedProperty = true;
- }
- else {
- if ( !hasSourceProperty( sourceParameter, mappedProperty.getSourcePropertyName() ) ) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "The type of parameter \"%s\" has no property named \"%s\".",
- mappedProperty.getSourceParameterName(),
- mappedProperty.getSourcePropertyName()
- ),
- method.getExecutable(),
- mappedProperty.getMirror(),
- mappedProperty.getSourceAnnotationValue()
- );
- foundUnmappedProperty = true;
- }
- }
-
- }
- else if ( mappedProperty.getConstant().isEmpty() &&
- mappedProperty.getJavaExpression().isEmpty() &&
- !hasSourceProperty( method, mappedProperty.getSourcePropertyName() ) ) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "No property named \"%s\" exists in source parameter(s).",
- mappedProperty.getSourceName()
- ),
- method.getExecutable(),
- mappedProperty.getMirror(),
- mappedProperty.getSourceAnnotationValue()
- );
- foundUnmappedProperty = true;
- }
- if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "Unknown property \"%s\" in return type %s.",
- mappedProperty.getTargetName(),
- method.getResultType()
- ),
- method.getExecutable(),
- mappedProperty.getMirror(),
- mappedProperty.getTargetAnnotationValue()
- );
- foundUnmappedProperty = true;
- }
- }
- }
- return !foundUnmappedProperty;
- }
-
- private PropertyMapping getPropertyMapping(List mapperReferences,
- List methods,
- SourceMethod method,
- Parameter parameter,
- ExecutableElement sourceAccessor,
- ExecutableElement targetAccessor,
- String targetPropertyName,
- String dateFormat,
- List qualifiers) {
-
- Type sourceType;
- Type targetType;
- TargetAccessorType targetAccessorType;
- String sourceReference = parameter.getName() + "." + sourceAccessor.getSimpleName().toString() + "()";
- String iteratorReference = null;
- boolean sourceIsCollection = false;
- if ( Executables.isSetterMethod( targetAccessor ) ) {
- sourceType = typeFactory.getReturnType( sourceAccessor );
- targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
- targetAccessorType = TargetAccessorType.SETTER;
- }
- else if ( Executables.isAdderMethod( targetAccessor ) ) {
- sourceType = typeFactory.getReturnType( sourceAccessor );
- if ( sourceType.isCollectionType() ) {
- sourceIsCollection = true;
- sourceType = sourceType.getTypeParameters().get( 0 );
- iteratorReference = Executables.getElementNameForAdder( targetAccessor );
- }
- targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
- targetAccessorType = TargetAccessorType.ADDER;
- }
- else {
- sourceType = typeFactory.getReturnType( sourceAccessor );
- targetType = typeFactory.getReturnType( targetAccessor );
- targetAccessorType = TargetAccessorType.GETTER;
- }
- String sourcePropertyName = Executables.getPropertyName( sourceAccessor );
- String mappedElement = "property '" + sourcePropertyName + "'";
-
- Assignment assignment = mappingResolver.getTargetAssignment(
- method,
- mappedElement,
- mapperReferences,
- methods,
- sourceType,
- targetType,
- targetPropertyName,
- dateFormat,
- qualifiers,
- iteratorReference != null ? iteratorReference : sourceReference
- );
-
- if ( assignment == null ) {
- assignment = forgeMapping(
- mapperReferences,
- methods,
- sourceType,
- targetType,
- sourceReference,
- method.getExecutable()
- );
- }
-
- if ( assignment != null ) {
-
- if ( targetType.isCollectionOrMapType() ) {
-
- // wrap the setter in the collection / map initializers
- if ( targetAccessorType == TargetAccessorType.SETTER ) {
-
- // wrap the assignment in a new Map or Collection implementation if this is not done in a mapping
- // method. Note, typeconversons do not apply to collections or maps
- Assignment newCollectionOrMap = null;
- if ( assignment.getType() == DIRECT ) {
- newCollectionOrMap = new NewCollectionOrMapWrapper( assignment, targetType.getImportTypes() );
- newCollectionOrMap = new SetterWrapper( newCollectionOrMap, method.getThrownTypes() );
- }
-
- // wrap the assignment in the setter method
- assignment = new SetterWrapper( assignment, method.getThrownTypes() );
-
- // target accessor is setter, so wrap the setter in setter map/ collection handling
- assignment = new SetterCollectionOrMapWrapper(
- assignment,
- targetAccessor.getSimpleName().toString(),
- newCollectionOrMap
- );
- }
- else {
- // wrap the assignment in the setter method
- assignment = new SetterWrapper( assignment, method.getThrownTypes() );
-
- // target accessor is getter, so wrap the setter in getter map/ collection handling
- assignment = new GetterCollectionOrMapWrapper( assignment );
- }
-
- // For collections and maps include a null check, when the assignment type is DIRECT.
- // for mapping methods (builtin / custom), the mapping method is responsible for the
- // null check. Typeconversions do not apply to collections and maps.
- if ( assignment.getType() == DIRECT ) {
- assignment = new NullCheckWrapper( assignment );
- }
- }
- else {
- if ( targetAccessorType == TargetAccessorType.SETTER ) {
- assignment = new SetterWrapper( assignment, method.getThrownTypes() );
- if ( !sourceType.isPrimitive() &&
- ( assignment.getType() == TYPE_CONVERTED ||
- assignment.getType() == TYPE_CONVERTED_MAPPED ||
- assignment.getType() == DIRECT && targetType.isPrimitive() ) ) {
- // for primitive types null check is not possible at all, but a conversion needs
- // a null check.
- assignment = new NullCheckWrapper( assignment );
- }
- }
- else {
- // TargetAccessorType must be ADDER
- if ( sourceIsCollection ) {
- assignment =
- new AdderWrapper( assignment, method.getThrownTypes(), sourceReference, sourceType );
- }
- else {
- // Possibly adding null to a target collection. So should be surrounded by an null check.
- assignment = new SetterWrapper( assignment, method.getThrownTypes() );
- assignment = new NullCheckWrapper( assignment );
- }
- }
- }
- }
- else {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "Can't map property \"%s %s\" to \"%s %s\".",
- sourceType,
- sourcePropertyName,
- targetType,
- targetPropertyName
- ),
- method.getExecutable()
- );
- }
- return new PropertyMapping(
- parameter.getName(),
- targetAccessor.getSimpleName().toString(),
- targetType,
- assignment
- );
- }
-
- /**
- * Creates a {@link PropertyMapping} representing the mapping of the given constant expression into the target
- * property.
- */
- private PropertyMapping getConstantMapping(List mapperReferences,
- List methods,
- SourceMethod method,
- String constantExpression,
- ExecutableElement targetAccessor,
- String dateFormat,
- List qualifiers) {
-
- // source
- String mappedElement = "constant '" + constantExpression + "'";
- Type sourceType = typeFactory.getType( String.class );
-
- // target
- Type targetType;
- if ( Executables.isSetterMethod( targetAccessor ) ) {
- targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
- }
- else {
- targetType = typeFactory.getReturnType( targetAccessor );
- }
- String targetPropertyName = Executables.getPropertyName( targetAccessor );
-
- Assignment assignment = mappingResolver.getTargetAssignment(
- method,
- mappedElement,
- mapperReferences,
- methods,
- sourceType,
- targetType,
- targetPropertyName,
- dateFormat,
- qualifiers,
- constantExpression
- );
-
- if ( assignment != null ) {
-
- // target accessor is setter, so decorate assignment as setter
- assignment = new SetterWrapper( assignment, method.getThrownTypes() );
-
- // wrap when dealing with getter only on target
- if ( Executables.isGetterMethod( targetAccessor ) ) {
- assignment = new GetterCollectionOrMapWrapper( assignment );
- }
- }
- else {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "Can't map \"%s %s\" to \"%s %s\".",
- sourceType,
- constantExpression,
- targetType,
- targetPropertyName
- ),
- method.getExecutable()
- );
- }
-
- return new PropertyMapping( targetAccessor.getSimpleName().toString(), targetType, assignment );
- }
-
- /**
- * Creates a {@link PropertyMapping} representing the mapping of the given java expression into the target
- * property.
- */
- private PropertyMapping getJavaExpressionMapping(SourceMethod method,
- String javaExpression,
- ExecutableElement targetAccessor) {
-
- Assignment assignment = AssignmentFactory.createSimple( javaExpression );
- assignment = new SetterWrapper( assignment, method.getThrownTypes() );
-
- Type targetType;
- if ( Executables.isSetterMethod( targetAccessor ) ) {
- targetType = typeFactory.getSingleParameter( targetAccessor ).getType();
- }
- else {
- targetType = typeFactory.getReturnType( targetAccessor );
-
- // target accessor is getter, so wrap the setter in getter map/ collection handling
- assignment = new GetterCollectionOrMapWrapper( assignment );
- }
-
- return new PropertyMapping( targetAccessor.getSimpleName().toString(), targetType, assignment );
- }
-
- private IterableMappingMethod getIterableMappingMethod(List mapperReferences,
- List methods,
- Method method,
- String dateFormat,
- List qualifiers) {
- Type sourceElementType = method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 );
- Type targetElementType = method.getResultType().getTypeParameters().get( 0 );
- String conversionStr = Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() );
-
- Assignment assignment = mappingResolver.getTargetAssignment(
- method,
- "collection element",
- mapperReferences,
- methods,
- sourceElementType,
- targetElementType,
- null, // there is no targetPropertyName
- dateFormat,
- qualifiers,
- conversionStr
- );
-
- if ( assignment == null ) {
- String message = String.format(
- "Can't create implementation of method %s. Found no method nor built-in conversion for mapping "
- + "source element type into target element type.",
- method
- );
- method.printMessage( messager, Kind.ERROR, message );
- }
-
- // target accessor is setter, so decorate assignment as setter
- assignment = new SetterWrapper( assignment, method.getThrownTypes() );
-
- FactoryMethod factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
- return new IterableMappingMethod( method, assignment, factoryMethod );
- }
-
- private MapMappingMethod getMapMappingMethod(List mapperReferences,
- List methods,
- Method method,
- String keyDateFormat,
- String valueDateFormat,
- List keyQualifiers,
- List valueQualifiers ) {
- List sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
- List resultTypeParams = method.getResultType().getTypeParameters();
-
- // find mapping method or conversion for key
- Type keySourceType = sourceTypeParams.get( 0 );
- Type keyTargetType = resultTypeParams.get( 0 );
-
- Assignment keyAssignment = mappingResolver.getTargetAssignment(
- method,
- "map key",
- mapperReferences,
- methods,
- keySourceType,
- keyTargetType,
- null, // there is no targetPropertyName
- keyDateFormat,
- keyQualifiers,
- "entry.getKey()"
- );
-
- if ( keyAssignment == null ) {
- String message = String.format( "Can't create implementation of method %s. Found no method nor built-in "
- + "conversion for mapping source key type to target key type.", method );
- method.printMessage( messager, Kind.ERROR, message );
- }
-
- // find mapping method or conversion for value
- Type valueSourceType = sourceTypeParams.get( 1 );
- Type valueTargetType = resultTypeParams.get( 1 );
-
- Assignment valueAssignment = mappingResolver.getTargetAssignment(
- method,
- "map value",
- mapperReferences,
- methods,
- valueSourceType,
- valueTargetType,
- null, // there is no targetPropertyName
- valueDateFormat,
- valueQualifiers,
- "entry.getValue()"
- );
-
- if ( valueAssignment == null ) {
- String message = String.format( "Can't create implementation of method %s. Found no method nor built-in "
- + "conversion for mapping source value type to target value type.", method );
- method.printMessage( messager, Kind.ERROR, message );
- }
-
- FactoryMethod factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
-
- keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes() );
- valueAssignment = new LocalVarWrapper( valueAssignment, method.getThrownTypes() );
-
- return new MapMappingMethod( method, keyAssignment, valueAssignment, factoryMethod );
- }
-
- private EnumMappingMethod getEnumMappingMethod(SourceMethod method) {
- if ( !reportErrorIfMappedEnumConstantsDontExist( method ) ||
- !reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped( method ) ) {
- return null;
- }
-
- List enumMappings = new ArrayList();
-
- List sourceEnumConstants = method.getSourceParameters().iterator().next().getType().getEnumConstants();
- Map> mappings = method.getMappings();
-
- for ( String enumConstant : sourceEnumConstants ) {
- List mappedConstants = mappings.get( enumConstant );
-
- if ( mappedConstants == null ) {
- enumMappings.add( new EnumMapping( enumConstant, enumConstant ) );
- }
- else if ( mappedConstants.size() == 1 ) {
- enumMappings.add( new EnumMapping( enumConstant, mappedConstants.iterator().next().getTargetName() ) );
- }
- else {
- List targetConstants = new ArrayList( mappedConstants.size() );
- for ( Mapping mapping : mappedConstants ) {
- targetConstants.add( mapping.getTargetName() );
- }
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "One enum constant must not be mapped to more than one target constant, but constant %s is " +
- "mapped to %s.",
- enumConstant,
- Strings.join( targetConstants, ", " )
- ),
- method.getExecutable()
- );
- }
- }
-
- return new EnumMappingMethod( method, enumMappings );
- }
-
- private boolean reportErrorIfMappedEnumConstantsDontExist(SourceMethod method) {
- // only report errors if this method itself is configured
- if ( method.isConfiguredByReverseMappingMethod() ) {
- return true;
- }
-
- List sourceEnumConstants = method.getSourceParameters().iterator().next().getType().getEnumConstants();
- List targetEnumConstants = method.getReturnType().getEnumConstants();
-
- boolean foundIncorrectMapping = false;
-
- for ( List mappedConstants : method.getMappings().values() ) {
- for ( Mapping mappedConstant : mappedConstants ) {
- if ( mappedConstant.getSourceName() == null ) {
- messager.printMessage(
- Kind.ERROR,
- "A source constant must be specified for mappings of an enum mapping method.",
- method.getExecutable(),
- mappedConstant.getMirror()
- );
- foundIncorrectMapping = true;
- }
- else if ( !sourceEnumConstants.contains( mappedConstant.getSourceName() ) ) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "Constant %s doesn't exist in enum type %s.",
- mappedConstant.getSourceName(),
- method.getSourceParameters().iterator().next().getType()
- ),
- method.getExecutable(),
- mappedConstant.getMirror(),
- mappedConstant.getSourceAnnotationValue()
- );
- foundIncorrectMapping = true;
- }
- if ( mappedConstant.getTargetName() == null ) {
- messager.printMessage(
- Kind.ERROR,
- "A target constant must be specified for mappings of an enum mapping method.",
- method.getExecutable(),
- mappedConstant.getMirror()
- );
- foundIncorrectMapping = true;
- }
- else if ( !targetEnumConstants.contains( mappedConstant.getTargetName() ) ) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "Constant %s doesn't exist in enum type %s.",
- mappedConstant.getTargetName(),
- method.getReturnType()
- ),
- method.getExecutable(),
- mappedConstant.getMirror(),
- mappedConstant.getTargetAnnotationValue()
- );
- foundIncorrectMapping = true;
- }
- }
- }
-
- return !foundIncorrectMapping;
- }
-
- private boolean reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped(
- SourceMethod method) {
-
- List sourceEnumConstants = method.getSourceParameters().iterator().next().getType().getEnumConstants();
- List targetEnumConstants = method.getReturnType().getEnumConstants();
- Set mappedSourceEnumConstants = method.getMappings().keySet();
- List unmappedSourceEnumConstants = new ArrayList();
-
- for ( String sourceEnumConstant : sourceEnumConstants ) {
- if ( !targetEnumConstants.contains( sourceEnumConstant ) &&
- !mappedSourceEnumConstants.contains( sourceEnumConstant ) ) {
- unmappedSourceEnumConstants.add( sourceEnumConstant );
- }
- }
-
- if ( !unmappedSourceEnumConstants.isEmpty() ) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "The following constants from the source enum have no corresponding constant in the target enum " +
- "and must be be mapped via @Mapping: %s",
- Strings.join( unmappedSourceEnumConstants, ", " )
- ),
- method.getExecutable()
- );
- }
-
- return unmappedSourceEnumConstants.isEmpty();
- }
-
- private MapperReference findMapperReference(List mapperReferences, SourceMethod method) {
- for ( MapperReference ref : mapperReferences ) {
- if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
- return ref;
- }
- }
- return null;
- }
-
- public Assignment forgeMapping( List mapperReferences,
- List methods,
- Type sourceType,
- Type targetType,
- String sourceReference,
- Element element) {
- Assignment assignment = null;
- if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
-
- ForgedMethod methodToGenerate
- = new ForgedMethod( sourceType, targetType, element );
- IterableMappingMethod iterableMappingMethod
- = getIterableMappingMethod( mapperReferences, methods, methodToGenerate, null, null );
- if ( !mappingsToGenerate.contains( iterableMappingMethod ) ) {
- mappingsToGenerate.add( iterableMappingMethod );
- }
- assignment = AssignmentFactory.createMethodReference( methodToGenerate, null, targetType );
- assignment.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
-
- }
- else if ( sourceType.isMapType() && targetType.isMapType() ) {
-
- ForgedMethod methodToGenerate
- = new ForgedMethod( sourceType, targetType, element );
- MapMappingMethod mapMappingMethod
- = getMapMappingMethod( mapperReferences, methods, methodToGenerate, null, null, null, null );
- if ( !mappingsToGenerate.contains( mapMappingMethod ) ) {
- mappingsToGenerate.add( mapMappingMethod );
- }
- assignment = AssignmentFactory.createMethodReference( methodToGenerate, null, targetType );
- assignment.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
- }
- return assignment;
- }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolver.java b/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java
similarity index 65%
rename from processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolver.java
rename to processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java
index a015d2106..04b9d9b08 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolver.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/creation/MappingResolverImpl.java
@@ -18,28 +18,26 @@
*/
package org.mapstruct.ap.processor.creation;
+import org.mapstruct.ap.model.MappingResolver;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import javax.annotation.processing.Messager;
import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.conversion.ConversionProvider;
import org.mapstruct.ap.conversion.Conversions;
-import org.mapstruct.ap.model.Assignment;
+import org.mapstruct.ap.model.assignment.Assignment;
import org.mapstruct.ap.model.MapperReference;
+import org.mapstruct.ap.model.MappingContext;
import org.mapstruct.ap.model.VirtualMappingMethod;
-import org.mapstruct.ap.model.assignment.AssignmentFactory;
-import org.mapstruct.ap.model.assignment.Direct;
+import org.mapstruct.ap.model.AssignmentFactory;
+import org.mapstruct.ap.model.Direct;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.DefaultConversionContext;
import org.mapstruct.ap.model.common.Type;
-import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.model.source.builtin.BuiltInMappingMethods;
@@ -67,30 +65,29 @@ import org.mapstruct.ap.util.Strings;
*
* @author Sjaak Derksen
*/
-public class MappingResolver {
+public class MappingResolverImpl implements MappingResolver {
- private final Messager messager;
- private final TypeFactory typeFactory;
private final Conversions conversions;
private final BuiltInMappingMethods builtInMethods;
- private final Types typeUtils;
-
private final MethodSelectors methodSelectors;
+ private final MappingContext mappingContext;
+
+
+
/**
* Private methods which are not present in the original mapper interface and are added to map certain property
- * types.
+ types.
+ * @param mappingContext
*/
- private final Set virtualMethods;
-
-
- public MappingResolver(Messager messager, TypeFactory typeFactory, Elements elementUtils, Types typeUtils) {
- this.messager = messager;
- this.typeFactory = typeFactory;
- this.conversions = new Conversions( elementUtils, typeFactory );
- this.builtInMethods = new BuiltInMappingMethods( typeFactory );
- this.virtualMethods = new HashSet();
- this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory );
- this.typeUtils = typeUtils;
+ public MappingResolverImpl( MappingContext mappingContext ) {
+ this.conversions = new Conversions( mappingContext.getElementUtils(), mappingContext.getTypeFactory() );
+ this.builtInMethods = new BuiltInMappingMethods( mappingContext.getTypeFactory() );
+ this.methodSelectors = new MethodSelectors(
+ mappingContext.getTypeUtils(),
+ mappingContext.getElementUtils(),
+ mappingContext.getTypeFactory()
+ );
+ this.mappingContext = mappingContext;
}
@@ -99,8 +96,6 @@ public class MappingResolver {
*
* @param mappingMethod target mapping method
* @param mappedElement used for error messages
- * @param mapperReferences list of references to mapper
- * @param methods list of candidate methods
* @param sourceType parameter to match
* @param targetType return type to match
* @param targetPropertyName name of the target property
@@ -116,10 +111,10 @@ public class MappingResolver {
* null, no assignment found
*
*/
- public Assignment getTargetAssignment( Method mappingMethod,
+ @Override
+ public Assignment getTargetAssignment(
+ Method mappingMethod,
String mappedElement,
- List mapperReferences,
- List methods,
Type sourceType,
Type targetType,
String targetPropertyName,
@@ -129,22 +124,19 @@ public class MappingResolver {
ResolvingAttempt attempt = new ResolvingAttempt( mappingMethod,
mappedElement,
- mapperReferences,
- methods,
targetPropertyName,
dateFormat,
qualifiers,
sourceReference,
- this
+ conversions,
+ builtInMethods,
+ methodSelectors,
+ mappingContext
);
return attempt.getTargetAssignment( sourceType, targetType );
}
- public Set getVirtualMethodsToGenerate() {
- return virtualMethods;
- }
-
private static class ResolvingAttempt {
@@ -156,7 +148,10 @@ public class MappingResolver {
private final String dateFormat;
private final List qualifiers;
private final String sourceReference;
- private final MappingResolver context;
+ private final Conversions conversions;
+ private final BuiltInMappingMethods builtInMethods;
+ private final MethodSelectors methodSelectors;
+ private final MappingContext mappingContext;
// resolving via 2 steps creates the possibillity of wrong matches, first builtin method matches,
// second doesn't. In that case, the first builtin method should not lead to a virtual method
@@ -165,22 +160,26 @@ public class MappingResolver {
private ResolvingAttempt( Method mappingMethod,
String mappedElement,
- List mapperReferences,
- List methods,
String targetPropertyName,
String dateFormat,
List qualifiers,
String sourceReference,
- MappingResolver context ) {
+ Conversions conversions,
+ BuiltInMappingMethods builtInMethods,
+ MethodSelectors methodSelectors,
+ MappingContext mappingContext ) {
this.mappingMethod = mappingMethod;
this.mappedElement = mappedElement;
- this.mapperReferences = mapperReferences;
- this.methods = methods;
+ this.mapperReferences = mappingContext.getMapperReferences();
+ this.methods = mappingContext.getSourceModel();
this.targetPropertyName = targetPropertyName;
this.dateFormat = dateFormat;
this.qualifiers = qualifiers;
this.sourceReference = sourceReference;
- this.context = context;
+ this.conversions = conversions;
+ this.builtInMethods = builtInMethods;
+ this.methodSelectors = methodSelectors;
+ this.mappingContext = mappingContext;
this.virtualMethodCandidates = new HashSet();
}
@@ -190,12 +189,12 @@ public class MappingResolver {
Assignment referencedMethod = resolveViaMethod( sourceType, targetType );
if ( referencedMethod != null ) {
referencedMethod.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
- context.virtualMethods.addAll( virtualMethodCandidates );
+ mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
return referencedMethod;
}
// then direct assignable
- if ( sourceType.isAssignableTo( targetType ) || context.isPropertyMappable( sourceType, targetType ) ) {
+ if ( sourceType.isAssignableTo( targetType ) || isPropertyMappable( sourceType, targetType ) ) {
Assignment simpleAssignment = AssignmentFactory.createSimple( sourceReference );
return simpleAssignment;
}
@@ -210,21 +209,21 @@ public class MappingResolver {
// 2 step method, first: method(method(souurce))
referencedMethod = resolveViaMethodAndMethod( sourceType, targetType );
if ( referencedMethod != null ) {
- context.virtualMethods.addAll( virtualMethodCandidates );
+ mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
return referencedMethod;
}
// 2 step method, then: method(conversion(souurce))
referencedMethod = resolveViaConversionAndMethod( sourceType, targetType );
if ( referencedMethod != null ) {
- context.virtualMethods.addAll( virtualMethodCandidates );
+ mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
return referencedMethod;
}
// 2 step method, finally: conversion(method(souurce))
conversion = resolveViaMethodAndConversion( sourceType, targetType );
if ( conversion != null ) {
- context.virtualMethods.addAll( virtualMethodCandidates );
+ mappingContext.getUsedVirtualMappings().addAll( virtualMethodCandidates );
return conversion;
}
@@ -233,13 +232,14 @@ public class MappingResolver {
}
private Assignment resolveViaConversion( Type sourceType, Type targetType ) {
- ConversionProvider conversionProvider = context.conversions.getConversion( sourceType, targetType );
+ ConversionProvider conversionProvider = conversions.getConversion( sourceType, targetType );
if ( conversionProvider == null ) {
return null;
}
- ConversionContext ctx = new DefaultConversionContext( context.typeFactory, targetType, dateFormat );
+ ConversionContext ctx =
+ new DefaultConversionContext( mappingContext.getTypeFactory(), targetType, dateFormat );
return conversionProvider.to( ctx );
}
@@ -259,11 +259,12 @@ public class MappingResolver {
// then a matching built-in method
BuiltInMethod matchingBuiltInMethod =
- getBestMatch( context.builtInMethods.getBuiltInMethods(), sourceType, targetType );
+ getBestMatch( builtInMethods.getBuiltInMethods(), sourceType, targetType );
if ( matchingBuiltInMethod != null ) {
virtualMethodCandidates.add( new VirtualMappingMethod( matchingBuiltInMethod ) );
- ConversionContext ctx = new DefaultConversionContext( context.typeFactory, targetType, dateFormat );
+ ConversionContext ctx =
+ new DefaultConversionContext( mappingContext.getTypeFactory(), targetType, dateFormat );
Assignment methodReference = AssignmentFactory.createMethodReference( matchingBuiltInMethod, ctx );
methodReference.setAssignment( AssignmentFactory.createSimple( sourceReference ) );
return methodReference;
@@ -285,7 +286,7 @@ public class MappingResolver {
private Assignment resolveViaMethodAndMethod( Type sourceType, Type targetType ) {
List methodYCandidates = new ArrayList( methods );
- methodYCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
+ methodYCandidates.addAll( builtInMethods.getBuiltInMethods() );
Assignment methodRefY = null;
@@ -331,7 +332,7 @@ public class MappingResolver {
private Assignment resolveViaConversionAndMethod( Type sourceType, Type targetType ) {
List methodYCandidates = new ArrayList( methods );
- methodYCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
+ methodYCandidates.addAll( builtInMethods.getBuiltInMethods() );
Assignment methodRefY = null;
@@ -373,7 +374,7 @@ public class MappingResolver {
private Assignment resolveViaMethodAndConversion( Type sourceType, Type targetType ) {
List methodXCandidates = new ArrayList( methods );
- methodXCandidates.addAll( context.builtInMethods.getBuiltInMethods() );
+ methodXCandidates.addAll( builtInMethods.getBuiltInMethods() );
Assignment conversionYRef = null;
@@ -404,7 +405,7 @@ public class MappingResolver {
private T getBestMatch( List methods, Type sourceType, Type returnType ) {
- List candidates = context.methodSelectors.getMatchingMethods(
+ List candidates = methodSelectors.getMatchingMethods(
mappingMethod,
methods,
sourceType,
@@ -423,7 +424,7 @@ public class MappingResolver {
returnType,
Strings.join( candidates, ", " ) );
- mappingMethod.printMessage( context.messager, Kind.ERROR, errorMsg );
+ mappingMethod.printMessage( mappingContext.getMessager(), Kind.ERROR, errorMsg );
}
if ( !candidates.isEmpty() ) {
@@ -453,114 +454,115 @@ public class MappingResolver {
}
return null;
}
- }
- /**
- * Whether the specified property can be mapped from source to target or not. A mapping if possible if one of
- * the following conditions is true:
- *
- * - the source type is assignable to the target type
- * - a mapping method exists
- * - a built-in conversion exists
- * - the property is of a collection or map type and the constructor of the target type (either itself or its
- * implementation type) accepts the source type.
- *
- *
- * @param property The property mapping to check.
- *
- * @return {@code true} if the specified property can be mapped, {@code false} otherwise.
- */
- private boolean isPropertyMappable(Type sourceType, Type targetType) {
- boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
+ /**
+ * Whether the specified property can be mapped from source to target or not. A mapping if possible if one of
+ * the following conditions is true:
+ *
+ * - the source type is assignable to the target type
+ * - a mapping method exists
+ * - a built-in conversion exists
+ * - the property is of a collection or map type and the constructor of the target type (either itself or its
+ * implementation type) accepts the source type.
+ *
+ *
+ * @param property The property mapping to check.
+ *
+ * @return {@code true} if the specified property can be mapped, {@code false} otherwise.
+ */
+ private boolean isPropertyMappable( Type sourceType, Type targetType ) {
+ boolean collectionOrMapTargetTypeHasCompatibleConstructor = false;
- if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
- collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor(
- sourceType,
- targetType.getImplementationType() != null ?
- targetType.getImplementationType() : targetType
- );
+ if ( sourceType.isCollectionType() && targetType.isCollectionType() ) {
+ collectionOrMapTargetTypeHasCompatibleConstructor = collectionTypeHasCompatibleConstructor(
+ sourceType,
+ targetType.getImplementationType() != null
+ ? targetType.getImplementationType() : targetType
+ );
+ }
+
+ if ( sourceType.isMapType() && targetType.isMapType() ) {
+ collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
+ sourceType,
+ targetType.getImplementationType() != null
+ ? targetType.getImplementationType() : targetType
+ );
+ }
+
+ if ( ( ( targetType.isCollectionType() || targetType.isMapType() )
+ && collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
+ return true;
+ }
+
+ return false;
}
- if ( sourceType.isMapType() && targetType.isMapType() ) {
- collectionOrMapTargetTypeHasCompatibleConstructor = mapTypeHasCompatibleConstructor(
- sourceType,
- targetType.getImplementationType() != null ?
- targetType.getImplementationType() : targetType
- );
+ /**
+ * Whether the given target type has a single-argument constructor which accepts the given source type.
+ *
+ * @param sourceType the source type
+ * @param targetType the target type
+ *
+ * @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
+ * otherwise.
+ */
+ private boolean collectionTypeHasCompatibleConstructor( Type sourceType, Type targetType ) {
+ // note (issue #127): actually this should check for the presence of a matching constructor, with help of
+ // Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead
+ // we just check whether the target type is parameterized in a way that it implicitly should have a
+ // constructor which accepts the source type
+
+ TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty()
+ ? mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror()
+ : sourceType.getTypeParameters().get( 0 ).getTypeMirror();
+
+ TypeMirror targetElementType = targetType.getTypeParameters().isEmpty()
+ ? mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror()
+ : targetType.getTypeParameters().get( 0 ).getTypeMirror();
+
+ return mappingContext.getTypeUtils().isAssignable( sourceElementType, targetElementType );
}
- if ( ( ( targetType.isCollectionType() || targetType.isMapType() ) &&
- collectionOrMapTargetTypeHasCompatibleConstructor ) ) {
- return true;
+ /**
+ * Whether the given target type has a single-argument constructor which accepts the given source type.
+ *
+ * @param sourceType the source type
+ * @param targetType the target type
+ *
+ * @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
+ * otherwise.
+ */
+ private boolean mapTypeHasCompatibleConstructor( Type sourceType, Type targetType ) {
+ // note (issue #127): actually this should check for the presence of a matching constructor, with help of
+ // Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead
+ // we just check whether the target type is parameterized in a way that it implicitly should have a
+ // constructor which accepts the source type
+
+ TypeMirror sourceKeyType;
+ TypeMirror targetKeyType;
+ TypeMirror sourceValueType;
+ TypeMirror targetValueType;
+
+ if ( sourceType.getTypeParameters().isEmpty() ) {
+ sourceKeyType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
+ sourceValueType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
+ }
+ else {
+ sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror();
+ sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror();
+ }
+
+ if ( targetType.getTypeParameters().isEmpty() ) {
+ targetKeyType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
+ targetValueType = mappingContext.getTypeFactory().getType( Object.class ).getTypeMirror();
+ }
+ else {
+ targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror();
+ targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror();
+ }
+
+ return mappingContext.getTypeUtils().isAssignable( sourceKeyType, targetKeyType )
+ && mappingContext.getTypeUtils().isAssignable( sourceValueType, targetValueType );
}
-
- return false;
- }
- /**
- * Whether the given target type has a single-argument constructor which accepts the given source type.
- *
- * @param sourceType the source type
- * @param targetType the target type
- *
- * @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
- * otherwise.
- */
- private boolean collectionTypeHasCompatibleConstructor(Type sourceType, Type targetType) {
- // note (issue #127): actually this should check for the presence of a matching constructor, with help of
- // Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead we
- // just check whether the target type is parameterized in a way that it implicitly should have a constructor
- // which accepts the source type
-
- TypeMirror sourceElementType = sourceType.getTypeParameters().isEmpty() ?
- typeFactory.getType( Object.class ).getTypeMirror() :
- sourceType.getTypeParameters().get( 0 ).getTypeMirror();
-
- TypeMirror targetElementType = targetType.getTypeParameters().isEmpty() ?
- typeFactory.getType( Object.class ).getTypeMirror() :
- targetType.getTypeParameters().get( 0 ).getTypeMirror();
-
- return typeUtils.isAssignable( sourceElementType, targetElementType );
- }
-
- /**
- * Whether the given target type has a single-argument constructor which accepts the given source type.
- *
- * @param sourceType the source type
- * @param targetType the target type
- *
- * @return {@code true} if the target type has a constructor accepting the given source type, {@code false}
- * otherwise.
- */
- private boolean mapTypeHasCompatibleConstructor(Type sourceType, Type targetType) {
- // note (issue #127): actually this should check for the presence of a matching constructor, with help of
- // Types#asMemberOf(); but this method seems to not work correctly in the Eclipse implementation, so instead we
- // just check whether the target type is parameterized in a way that it implicitly should have a constructor
- // which accepts the source type
-
- TypeMirror sourceKeyType = null;
- TypeMirror targetKeyType = null;
- TypeMirror sourceValueType = null;
- TypeMirror targetValueType = null;
-
- if ( sourceType.getTypeParameters().isEmpty() ) {
- sourceKeyType = typeFactory.getType( Object.class ).getTypeMirror();
- sourceValueType = typeFactory.getType( Object.class ).getTypeMirror();
- }
- else {
- sourceKeyType = sourceType.getTypeParameters().get( 0 ).getTypeMirror();
- sourceValueType = sourceType.getTypeParameters().get( 1 ).getTypeMirror();
- }
-
- if ( targetType.getTypeParameters().isEmpty() ) {
- targetKeyType = typeFactory.getType( Object.class ).getTypeMirror();
- targetValueType = typeFactory.getType( Object.class ).getTypeMirror();
- }
- else {
- targetKeyType = targetType.getTypeParameters().get( 0 ).getTypeMirror();
- targetValueType = targetType.getTypeParameters().get( 1 ).getTypeMirror();
- }
-
- return typeUtils.isAssignable( sourceKeyType, targetKeyType ) &&
- typeUtils.isAssignable( sourceValueType, targetValueType );
}
}
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.assignment.Direct.ftl b/processor/src/main/resources/org.mapstruct.ap.model.Direct.ftl
similarity index 100%
rename from processor/src/main/resources/org.mapstruct.ap.model.assignment.Direct.ftl
rename to processor/src/main/resources/org.mapstruct.ap.model.Direct.ftl
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.assignment.MethodReference.ftl b/processor/src/main/resources/org.mapstruct.ap.model.MethodReference.ftl
similarity index 100%
rename from processor/src/main/resources/org.mapstruct.ap.model.assignment.MethodReference.ftl
rename to processor/src/main/resources/org.mapstruct.ap.model.MethodReference.ftl
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.assignment.TypeConversion.ftl b/processor/src/main/resources/org.mapstruct.ap.model.TypeConversion.ftl
similarity index 100%
rename from processor/src/main/resources/org.mapstruct.ap.model.assignment.TypeConversion.ftl
rename to processor/src/main/resources/org.mapstruct.ap.model.TypeConversion.ftl