diff --git a/core-common/src/main/java/org/mapstruct/Mapper.java b/core-common/src/main/java/org/mapstruct/Mapper.java
index ba1f32611..f9540b6a5 100644
--- a/core-common/src/main/java/org/mapstruct/Mapper.java
+++ b/core-common/src/main/java/org/mapstruct/Mapper.java
@@ -112,4 +112,17 @@ public @interface Mapper {
* @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapper.
*/
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.DEFAULT;
+
+ /**
+ * The strategy to use for applying method-level configuration annotations of prototype methods in the interface
+ * specified with {@link #config()}. Annotations that can be inherited are for example {@link Mapping},
+ * {@link IterableMapping}, {@link MapMapping}, or {@link BeanMapping}.
+ *
+ * If no strategy is configured, the strategy given via {@link MapperConfig#mappingInheritanceStrategy()} will be
+ * applied, using {@link MappingInheritanceStrategy#EXPLICIT} as default.
+ *
+ * @return The strategy to use for applying {@code @Mapping} configurations of prototype methods in the interface
+ * specified with {@link #config()}.
+ */
+ MappingInheritanceStrategy mappingInheritanceStrategy() default MappingInheritanceStrategy.DEFAULT;
}
diff --git a/core-common/src/main/java/org/mapstruct/MapperConfig.java b/core-common/src/main/java/org/mapstruct/MapperConfig.java
index 6d044193f..14ced439b 100644
--- a/core-common/src/main/java/org/mapstruct/MapperConfig.java
+++ b/core-common/src/main/java/org/mapstruct/MapperConfig.java
@@ -26,10 +26,18 @@ import java.lang.annotation.Target;
import org.mapstruct.factory.Mappers;
/**
- * Marks a class-, interface-, enum declaration as (common) configuration.
- *
+ * Marks a class- or interface-declaration as (common) configuration.
+ *
* The {@link #unmappedTargetPolicy() } and {@link #componentModel() } can be overruled by a specific {@link Mapper}
* annotation. {@link #uses() } will be used in addition to what is specified in the {@link Mapper} annotation.
+ *
+ *
+ * Mapping methods defined in the annotated type can be used as prototypes from which method-level annotations
+ * such as {@code @Mapping}, {@code @IterableMapping}, etc. can be inherited. Depending on the configured
+ * {@link #mappingInheritanceStrategy()}, the configuration can be inherited either explicitly using
+ * {@link InheritConfiguration} or {@link InheritInverseConfiguration}, or automatically in case all source and target
+ * types are assignable.
+ *
*
* @author Sjaak Derksen
*/
@@ -80,7 +88,7 @@ public @interface MapperConfig {
*
* @return The strategy applied when propagating the value of collection-typed properties.
*/
- CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.DEFAULT;
+ CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.ACCESSOR_ONLY;
/**
* The strategy to be applied when {@code null} is passed as source value to mapping methods. If no strategy is
@@ -88,5 +96,18 @@ public @interface MapperConfig {
*
* @return The strategy to be applied when {@code null} is passed as source value to mapping methods.
*/
- NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.DEFAULT;
+ NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
+
+ /**
+ * The strategy to use for applying method-level configuration annotations of prototype methods in the interface
+ * annotated with this annotation. Annotations that can be inherited are for example {@link Mapping},
+ * {@link IterableMapping}, {@link MapMapping}, or {@link BeanMapping}.
+ *
+ * If no strategy is configured, {@link MappingInheritanceStrategy#EXPLICIT} will be used as default.
+ *
+ * @return The strategy to use for applying {@code @Mapping} configurations of prototype methods in the interface
+ * annotated with this annotation.
+ */
+ MappingInheritanceStrategy mappingInheritanceStrategy()
+ default MappingInheritanceStrategy.EXPLICIT;
}
diff --git a/core-common/src/main/java/org/mapstruct/MappingInheritanceStrategy.java b/core-common/src/main/java/org/mapstruct/MappingInheritanceStrategy.java
new file mode 100644
index 000000000..7f9f44d04
--- /dev/null
+++ b/core-common/src/main/java/org/mapstruct/MappingInheritanceStrategy.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2012-2015 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;
+
+/**
+ * The strategy to use for applying method-level configuration annotations of prototype methods in the interface
+ * specified with {@link Mapper#config()}.
+ *
+ * @author Andreas Gudian
+ */
+public enum MappingInheritanceStrategy {
+ /**
+ * Apply the method-level configuration annotations only if the prototype method is explicitly referenced using
+ * {@link InheritConfiguration}.
+ */
+ EXPLICIT,
+
+ /**
+ * Apply the method-level configuration annotations if source and target types of the prototype method are
+ * assignable from the types of a given mapping method.
+ */
+ AUTO_INHERIT_FROM_CONFIG,
+
+ /**
+ * When given via {@link Mapper#mappingInheritanceStrategy()}, the value specified via
+ * {@link MapperConfig#mappingInheritanceStrategy()} will be applied, if present.
+ *
+ * When given via {@link MapperConfig#mappingInheritanceStrategy()}, the strategy {@link #EXPLICIT} will be applied.
+ */
+ DEFAULT;
+}
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 9d8dfa1d5..43b7501fc 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
@@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+
import javax.lang.model.element.ExecutableElement;
import javax.tools.Diagnostic;
@@ -43,9 +44,9 @@ import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.BeanMappingPrism;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.NullValueMappingPrism;
-import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.MapperConfig;
+import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings;
/**
@@ -145,7 +146,7 @@ public class BeanMappingMethod extends MappingMethod {
Set handledTargets = new HashSet();
- for ( Map.Entry> entry : method.getMappings().entrySet() ) {
+ for ( Map.Entry> entry : method.getMappingOptions().getMappings().entrySet() ) {
for ( Mapping mapping : entry.getValue() ) {
PropertyMapping propertyMapping = null;
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 853fb186e..898edc36d 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/EnumMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/EnumMappingMethod.java
@@ -29,6 +29,8 @@ import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings;
+import static org.mapstruct.ap.util.Collections.first;
+
/**
* A {@link MappingMethod} which maps one enum type to another, optionally configured by one or more
* {@link EnumMapping}s.
@@ -63,8 +65,7 @@ public class EnumMappingMethod extends MappingMethod {
List enumMappings = new ArrayList();
- List sourceEnumConstants
- = method.getSourceParameters().iterator().next().getType().getEnumConstants();
+ List sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
for ( String enumConstant : sourceEnumConstants ) {
List mappedConstants = method.getMappingBySourcePropertyName( enumConstant );
@@ -75,7 +76,7 @@ public class EnumMappingMethod extends MappingMethod {
else if ( mappedConstants.size() == 1 ) {
enumMappings.add(
new EnumMapping(
- enumConstant, mappedConstants.iterator().next().getTargetName()
+ enumConstant, first( mappedConstants ).getTargetName()
)
);
}
@@ -96,13 +97,12 @@ public class EnumMappingMethod extends MappingMethod {
}
private boolean reportErrorIfMappedEnumConstantsDontExist(SourceMethod method) {
- List sourceEnumConstants =
- method.getSourceParameters().iterator().next().getType().getEnumConstants();
+ List sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
List targetEnumConstants = method.getReturnType().getEnumConstants();
boolean foundIncorrectMapping = false;
- for ( List mappedConstants : method.getMappings().values() ) {
+ for ( List mappedConstants : method.getMappingOptions().getMappings().values() ) {
for ( Mapping mappedConstant : mappedConstants ) {
if ( mappedConstant.getSourceName() == null ) {
@@ -118,7 +118,7 @@ public class EnumMappingMethod extends MappingMethod {
mappedConstant.getSourceAnnotationValue(),
Message.ENUMMAPPING_NON_EXISTING_CONSTANT,
mappedConstant.getSourceName(),
- method.getSourceParameters().iterator().next().getType()
+ first( method.getSourceParameters() ).getType()
);
foundIncorrectMapping = true;
}
@@ -148,8 +148,7 @@ public class EnumMappingMethod extends MappingMethod {
private boolean reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped(
SourceMethod method) {
- List sourceEnumConstants =
- method.getSourceParameters().iterator().next().getType().getEnumConstants();
+ List sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
List targetEnumConstants = method.getReturnType().getEnumConstants();
List unmappedSourceEnumConstants = new ArrayList();
@@ -182,6 +181,6 @@ public class EnumMappingMethod extends MappingMethod {
}
public Parameter getSourceParameter() {
- return getParameters().iterator().next();
+ return first( getParameters() );
}
}
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 37e40268a..c385ca66a 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
@@ -20,6 +20,7 @@ package org.mapstruct.ap.model;
import java.util.List;
import java.util.Set;
+
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
@@ -30,10 +31,12 @@ import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.prism.NullValueMappingPrism;
-import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.MapperConfig;
+import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings;
+import static org.mapstruct.ap.util.Collections.first;
+
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one iterable type to another. The collection
* elements are mapped either by a {@link TypeConversion} or another mapping method.
@@ -82,7 +85,7 @@ public class IterableMappingMethod extends MappingMethod {
}
public IterableMappingMethod build() {
- Type sourceParameterType = method.getSourceParameters().iterator().next().getType();
+ Type sourceParameterType = first( method.getSourceParameters() ).getType();
Type resultType = method.getResultType();
Type sourceElementType = sourceParameterType.isArrayType() ? sourceParameterType.getComponentType() :
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 1a676ec0e..0cac68cde 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
@@ -20,6 +20,7 @@ package org.mapstruct.ap.model;
import java.util.List;
import java.util.Set;
+
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.assignment.Assignment;
@@ -28,10 +29,12 @@ import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.prism.NullValueMappingPrism;
-import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.MapperConfig;
+import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings;
+import static org.mapstruct.ap.util.Collections.first;
+
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
* values are mapped either by a {@link TypeConversion} or another mapping method if required.
@@ -99,7 +102,7 @@ public class MapMappingMethod extends MappingMethod {
public MapMappingMethod build() {
- List sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
+ List sourceTypeParams = first( method.getSourceParameters() ).getType().getTypeParameters();
List resultTypeParams = method.getResultType().getTypeParameters();
// find mapping method or conversion for key
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java
index 89d857885..8c65463df 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java
@@ -24,7 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.mapstruct.ap.util.FormattingMessager;
+
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ElementKind;
@@ -37,6 +37,7 @@ import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.MappingPrism;
import org.mapstruct.ap.prism.MappingsPrism;
+import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.Message;
/**
@@ -61,7 +62,6 @@ public class Mapping {
private final AnnotationValue sourceAnnotationValue;
private final AnnotationValue targetAnnotationValue;
private SourceReference sourceReference;
- private SourceReference targetReference;
public static Map> fromMappingsPrism(MappingsPrism mappingsAnnotation,
ExecutableElement method,
@@ -239,10 +239,6 @@ public class Mapping {
return sourceReference;
}
- public SourceReference getTargetReference() {
- return targetReference;
- }
-
public TypeMirror getResultType() {
return resultType;
}
@@ -298,6 +294,33 @@ public class Mapping {
return reverse;
}
+ /**
+ * Creates a copy of this mapping, which is adapted to the given method
+ *
+ * @param the method to adapt the copy to
+ */
+ public Mapping copyForInheritanceTo(SourceMethod method) {
+ Mapping mapping = new Mapping(
+ sourceName,
+ constant,
+ javaExpression,
+ targetName,
+ dateFormat,
+ qualifiers,
+ isIgnored,
+ mirror,
+ sourceAnnotationValue,
+ targetAnnotationValue,
+ resultType
+ );
+
+ if ( sourceReference != null ) {
+ mapping.sourceReference = sourceReference.copyForInheritanceTo( method );
+ }
+
+ return mapping;
+ }
+
@Override
public String toString() {
return "Mapping {" +
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/model/source/MappingOptions.java
new file mode 100644
index 000000000..f0db38a9e
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/MappingOptions.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2012-2015 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.source;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.mapstruct.ap.model.common.TypeFactory;
+import org.mapstruct.ap.util.FormattingMessager;
+
+/**
+ * Encapsulates all options specifiable on a mapping method
+ *
+ * @author Andreas Gudian
+ */
+public class MappingOptions {
+ private Map> mappings;
+ private IterableMapping iterableMapping;
+ private MapMapping mapMapping;
+ private boolean fullyInitialized;
+
+ public MappingOptions(Map> mappings, IterableMapping iterableMapping, MapMapping mapMapping) {
+ this.mappings = mappings;
+ this.iterableMapping = iterableMapping;
+ this.mapMapping = mapMapping;
+ }
+
+ /**
+ * @return the {@link Mapping}s configured for this method, keyed by target property name. Only for enum mapping
+ * methods a target will be mapped by several sources.
+ */
+ public Map> getMappings() {
+ return mappings;
+ }
+
+ public IterableMapping getIterableMapping() {
+ return iterableMapping;
+ }
+
+ public MapMapping getMapMapping() {
+ return mapMapping;
+ }
+
+ public void setMappings(Map> mappings) {
+ this.mappings = mappings;
+ }
+
+ public void setIterableMapping(IterableMapping iterableMapping) {
+ this.iterableMapping = iterableMapping;
+ }
+
+ public void setMapMapping(MapMapping mapMapping) {
+ this.mapMapping = mapMapping;
+ }
+
+ /**
+ * @return the {@code true}, iff the options have been fully initialized by applying all available inheritance
+ * options
+ */
+ public boolean isFullyInitialized() {
+ return fullyInitialized;
+ }
+
+ public void markAsFullyInitialized() {
+ this.fullyInitialized = true;
+ }
+
+ /**
+ * Merges in all the mapping options configured, giving the already defined options precedence.
+ *
+ * @param inherited the options to inherit, may be {@code null}
+ * @param isInverse if {@code true}, the specified options are from an inverse method
+ * @param method the source method
+ * @param messager the messager
+ * @param typeFactory the type factory
+ */
+ public void applyInheritedOptions(MappingOptions inherited, boolean isInverse, SourceMethod method,
+ FormattingMessager messager, TypeFactory typeFactory) {
+ if ( null != inherited ) {
+ if ( getIterableMapping() == null ) {
+ if ( inherited.getIterableMapping() != null ) {
+ setIterableMapping( inherited.getIterableMapping() );
+ }
+ }
+
+ if ( getMapMapping() == null ) {
+ if ( inherited.getMapMapping() != null ) {
+ setMapMapping( inherited.getMapMapping() );
+ }
+ }
+
+ Map> newMappings = new HashMap>();
+
+ for ( List lmappings : inherited.getMappings().values() ) {
+ for ( Mapping mapping : lmappings ) {
+ if ( isInverse ) {
+ mapping = mapping.reverse( method, messager, typeFactory );
+ }
+
+ if ( mapping != null ) {
+ List mappingsOfProperty = newMappings.get( mapping.getTargetName() );
+ if ( mappingsOfProperty == null ) {
+ mappingsOfProperty = new ArrayList();
+ newMappings.put( mapping.getTargetName(), mappingsOfProperty );
+ }
+
+ mappingsOfProperty.add( mapping.copyForInheritanceTo( method ) );
+ }
+ }
+ }
+
+ // now add all of its own mappings
+ newMappings.putAll( getMappings() );
+ setMappings( newMappings );
+ }
+
+ markAsFullyInitialized();
+ }
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java
index 09a9307bb..29cf89728 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java
@@ -19,11 +19,12 @@
package org.mapstruct.ap.model.source;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
-import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.util.Types;
@@ -33,9 +34,12 @@ 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.SourceReference.PropertyEntry;
+import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.MapperConfig;
import org.mapstruct.ap.util.Strings;
+import static org.mapstruct.ap.util.Collections.first;
+
/**
* Represents a mapping method with source and target type and the mappings between the properties of source and target
* type.
@@ -50,7 +54,6 @@ public class SourceMethod implements Method {
private final Types typeUtils;
private final TypeFactory typeFactory;
- private final FormattingMessager messager;
private final Type declaringMapper;
private final ExecutableElement executable;
@@ -61,10 +64,14 @@ public class SourceMethod implements Method {
private final Accessibility accessibility;
private final List exceptionTypes;
private final MapperConfig config;
+ private final MappingOptions mappingOptions;
+ private final List prototypeMethods;
- private Map> mappings;
- private IterableMapping iterableMapping;
- private MapMapping mapMapping;
+ private List sourceParameters;
+
+ private List parameterNames;
+
+ private List applicablePrototypeMethods;
public static class Builder {
@@ -80,6 +87,7 @@ public class SourceMethod implements Method {
private TypeFactory typeFactory = null;
private FormattingMessager messager = null;
private MapperConfig mapperConfig = null;
+ private List prototypeMethods = Collections.emptyList();
public Builder() {
}
@@ -144,24 +152,27 @@ public class SourceMethod implements Method {
return this;
}
- public SourceMethod createSourceMethod() {
+ public Builder setPrototypeMethods(List prototypeMethods) {
+ this.prototypeMethods = prototypeMethods;
+ return this;
+ }
+
+ public SourceMethod buildSourceMethod() {
SourceMethod sourceMethod = new SourceMethod(
declaringMapper,
executable,
parameters,
returnType,
exceptionTypes,
- mappings,
- iterableMapping,
- mapMapping,
+ new MappingOptions( mappings, iterableMapping, mapMapping ),
typeUtils,
typeFactory,
- messager,
- mapperConfig
+ mapperConfig,
+ prototypeMethods
);
if ( mappings != null ) {
- for ( Map.Entry> entry : sourceMethod.getMappings().entrySet() ) {
+ for ( Map.Entry> entry : mappings.entrySet() ) {
for ( Mapping mapping : entry.getValue() ) {
mapping.init( sourceMethod, messager, typeFactory );
}
@@ -173,26 +184,24 @@ public class SourceMethod implements Method {
@SuppressWarnings( "checkstyle:parameternumber" )
private SourceMethod( Type declaringMapper, ExecutableElement executable, List parameters,
- Type returnType, List exceptionTypes, Map> mappings,
- IterableMapping iterableMapping, MapMapping mapMapping, Types typeUtils,
- TypeFactory typeFactory, FormattingMessager messager, MapperConfig config) {
+ Type returnType, List exceptionTypes, MappingOptions mappingOptions, Types typeUtils,
+ TypeFactory typeFactory, MapperConfig config, List prototypeMethods) {
this.declaringMapper = declaringMapper;
this.executable = executable;
this.parameters = parameters;
this.returnType = returnType;
this.exceptionTypes = exceptionTypes;
- this.mappings = mappings;
- this.iterableMapping = iterableMapping;
- this.mapMapping = mapMapping;
this.accessibility = Accessibility.fromModifiers( executable.getModifiers() );
+ this.mappingOptions = mappingOptions;
+
this.mappingTargetParameter = determineMappingTargetParameter( parameters );
this.targetTypeParameter = determineTargetTypeParameter( parameters );
this.typeUtils = typeUtils;
this.typeFactory = typeFactory;
- this.messager = messager;
this.config = config;
+ this.prototypeMethods = prototypeMethods;
}
private Parameter determineMappingTargetParameter(Iterable parameters) {
@@ -249,11 +258,13 @@ public class SourceMethod implements Method {
*/
@Override
public List getSourceParameters() {
- List sourceParameters = new ArrayList();
+ if ( sourceParameters == null ) {
+ sourceParameters = new ArrayList();
- for ( Parameter parameter : parameters ) {
- if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) {
- sourceParameters.add( parameter );
+ for ( Parameter parameter : parameters ) {
+ if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) {
+ sourceParameters.add( parameter );
+ }
}
}
@@ -262,10 +273,12 @@ public class SourceMethod implements Method {
@Override
public List getParameterNames() {
- List parameterNames = new ArrayList( parameters.size() );
+ if ( parameterNames == null ) {
+ parameterNames = new ArrayList( parameters.size() );
- for ( Parameter parameter : parameters ) {
- parameterNames.add( parameter.getName() );
+ for ( Parameter parameter : parameters ) {
+ parameterNames.add( parameter.getName() );
+ }
}
return parameterNames;
@@ -289,84 +302,31 @@ public class SourceMethod implements Method {
return accessibility;
}
- /**
- * @return the {@link Mapping}s configured for this method, keyed by target property name. Only for enum mapping
- * methods a target will be mapped by several sources.
- */
- public Map> getMappings() {
- return mappings;
- }
-
public Mapping getSingleMappingByTargetPropertyName(String targetPropertyName) {
- List all = mappings.get( targetPropertyName );
- return all != null ? all.iterator().next() : null;
- }
-
- public void setMappings(Map> mappings) {
- this.mappings = mappings;
- }
-
- public IterableMapping getIterableMapping() {
- return iterableMapping;
- }
-
- public void setIterableMapping(IterableMapping iterableMapping) {
- this.iterableMapping = iterableMapping;
- }
-
- public MapMapping getMapMapping() {
- return mapMapping;
- }
-
- public void setMapMapping(MapMapping mapMapping) {
- this.mapMapping = mapMapping;
+ List all = mappingOptions.getMappings().get( targetPropertyName );
+ return all != null ? first( all ) : null;
}
public boolean reverses(SourceMethod method) {
return getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1
- && equals( getSourceParameters().iterator().next().getType(), method.getResultType() )
- && equals( getResultType(), method.getSourceParameters().iterator().next().getType() );
+ && equals( first( getSourceParameters() ).getType(), method.getResultType() )
+ && equals( getResultType(), first( method.getSourceParameters() ).getType() );
}
public boolean isSame(SourceMethod method) {
return getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1
- && equals( getSourceParameters().iterator().next().getType(),
- method.getSourceParameters().iterator().next().getType())
+ && equals( first( getSourceParameters() ).getType(),
+ first( method.getSourceParameters() ).getType() )
&& equals( getResultType(), method.getResultType() );
}
- public boolean isSimilar(SourceMethod method) {
- Map test = new HashMap();
-
- // check how many times a type occurs
- for (Parameter sourceParam : method.getSourceParameters() ) {
- Type sourceType = sourceParam.getType();
- if ( !test.containsKey( sourceType ) ) {
- test.put( sourceType, 0 );
- }
- increase( sourceType, test );
- }
-
- // check if this method also contains the same time each parameter type.
- for (Parameter sourceParam : getSourceParameters() ) {
- Type sourceType = sourceParam.getType();
- if ( !test.containsKey( sourceType ) ) {
- // method contains a different parameter type than this
- return false;
- }
- decrease( sourceType, test );
- }
-
- // now, if they match they should have the same source parameter types each
- for ( Integer count : test.values() ) {
- if ( count != 0 ) {
- return false;
- }
- }
-
- // finally check the return type.
- return equals( getResultType(), method.getResultType() );
+ public boolean canInheritFrom(SourceMethod method) {
+ return isMapMapping() == method.isMapMapping()
+ && isIterableMapping() == method.isIterableMapping()
+ && isEnumMapping() == method.isEnumMapping()
+ && getResultType().isAssignableTo( method.getResultType() )
+ && allParametersAreAssignable( getSourceParameters(), method.getSourceParameters() );
}
@Override
@@ -380,17 +340,17 @@ public class SourceMethod implements Method {
}
public boolean isIterableMapping() {
- return getSourceParameters().size() == 1 && getSourceParameters().iterator().next().getType().isIterableType()
+ return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isIterableType()
&& getResultType().isIterableType();
}
public boolean isMapMapping() {
- return getSourceParameters().size() == 1 && getSourceParameters().iterator().next().getType().isMapType()
+ return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isMapType()
&& getResultType().isMapType();
}
public boolean isEnumMapping() {
- return getSourceParameters().size() == 1 && getSourceParameters().iterator().next().getType().isEnumType()
+ return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isEnumType()
&& getResultType().isEnumType();
}
@@ -422,7 +382,7 @@ public class SourceMethod implements Method {
public List getMappingBySourcePropertyName(String sourcePropertyName) {
List mappingsOfSourceProperty = new ArrayList();
- for ( List mappingOfProperty : mappings.values() ) {
+ for ( List mappingOfProperty : mappingOptions.getMappings().values() ) {
for ( Mapping mapping : mappingOfProperty ) {
if ( isEnumMapping() ) {
@@ -435,7 +395,7 @@ public class SourceMethod implements Method {
// there can only be a mapping if there's only one entry for a source property, so: param.property.
// There can be no mapping if there are more entries. So: param.property.property2
- if ( sourceEntries.size() == 1 && sourcePropertyName.equals( sourceEntries.get( 0 ).getName() ) ) {
+ if ( sourceEntries.size() == 1 && sourcePropertyName.equals( first( sourceEntries ).getName() ) ) {
mappingsOfSourceProperty.add( mapping );
}
}
@@ -455,6 +415,45 @@ public class SourceMethod implements Method {
return null;
}
+ public List getApplicablePrototypeMethods() {
+ if ( applicablePrototypeMethods == null ) {
+ applicablePrototypeMethods = new ArrayList();
+
+ for ( SourceMethod prototype : prototypeMethods ) {
+ if ( canInheritFrom( prototype ) ) {
+ applicablePrototypeMethods.add( prototype );
+ }
+ }
+ }
+
+ return applicablePrototypeMethods;
+ }
+
+ private static boolean allParametersAreAssignable(List fromParams, List toParams) {
+ if ( fromParams.size() == toParams.size() ) {
+ Set unaccountedToParams = new HashSet( toParams );
+
+ for ( Parameter fromParam : fromParams ) {
+ // each fromParam needs at least one match, and all toParam need to be accounted for at the end
+ boolean hasMatch = false;
+ for ( Parameter toParam : toParams ) {
+ if ( fromParam.getType().isAssignableTo( toParam.getType() ) ) {
+ unaccountedToParams.remove( toParam );
+ hasMatch = true;
+ }
+ }
+
+ if ( !hasMatch ) {
+ return false;
+ }
+ }
+
+ return unaccountedToParams.isEmpty();
+ }
+
+ return false;
+ }
+
/**
* Whether an implementation of this method must be generated or not.
*
@@ -494,57 +493,8 @@ public class SourceMethod implements Method {
return exceptionTypes;
}
- /**
- * Merges in all the mappings configured via the given inverse mapping method, giving the locally defined mappings
- * precedence.
- * @param inverseMethod
- * @param templateMethod
- */
- public void mergeWithInverseMappings(SourceMethod inverseMethod, SourceMethod templateMethod) {
- Map> newMappings = new HashMap>();
-
- if ( inverseMethod != null && !inverseMethod.getMappings().isEmpty() ) {
- for ( List lmappings : inverseMethod.getMappings().values() ) {
- for ( Mapping inverseMapping : lmappings ) {
- Mapping reversed = inverseMapping.reverse( this, messager, typeFactory );
- if ( reversed != null ) {
- List mappingsOfProperty = newMappings.get( reversed.getTargetName() );
- if ( mappingsOfProperty == null ) {
- mappingsOfProperty = new ArrayList();
- newMappings.put( reversed.getTargetName(), mappingsOfProperty );
- }
- mappingsOfProperty.add( reversed );
- }
- }
- }
- }
-
- if ( templateMethod != null && !templateMethod.getMappings().isEmpty() ) {
- for ( List lmappings : templateMethod.getMappings().values() ) {
- for ( Mapping templateMapping : lmappings ) {
- if ( templateMapping != null ) {
- List mappingsOfProperty = newMappings.get( templateMapping.getTargetName() );
- if ( mappingsOfProperty == null ) {
- mappingsOfProperty = new ArrayList();
- newMappings.put( templateMapping.getTargetName(), mappingsOfProperty );
- }
- mappingsOfProperty.add( templateMapping );
- }
- }
- }
- }
-
- if ( getMappings().isEmpty() ) {
- // the mapping method is configuredByReverseMappingMethod, see SourceMethod#setMappings()
- setMappings( newMappings );
- }
- else {
- // now add all of its own mappings
- newMappings.putAll( getMappings() );
- getMappings().clear();
- // the mapping method is NOT configuredByReverseMappingMethod,
- getMappings().putAll( newMappings );
- }
+ public MappingOptions getMappingOptions() {
+ return mappingOptions;
}
@Override
@@ -552,17 +502,6 @@ public class SourceMethod implements Method {
return executable.getModifiers().contains( Modifier.STATIC );
}
- private void increase(Type key, Map test) {
- Integer count = test.get( key );
- count++;
- test.put( key, count );
- }
-
- private void decrease(Type key, Map test) {
- Integer count = test.get( key );
- count--;
- test.put( key, count );
- }
/**
*
* @return the mapper config when this method needs to be implemented
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java
index fd91e26df..344c45f1b 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java
@@ -22,16 +22,18 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement;
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.util.Message;
import org.mapstruct.ap.util.Executables;
+import org.mapstruct.ap.util.FormattingMessager;
+import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings;
+import static org.mapstruct.ap.util.Collections.first;
+
/**
* This class describes the source side of a property mapping.
*
@@ -287,4 +289,25 @@ public class SourceReference {
}
}
+
+ /**
+ * Creates a copy of this reference, which is adapted to the given method
+ *
+ * @param the method to adapt the copy to
+ */
+ public SourceReference copyForInheritanceTo(SourceMethod method) {
+ List replacementParamCandidates = new ArrayList();
+ for ( Parameter sourceParam : method.getSourceParameters() ) {
+ if ( sourceParam.getType().isAssignableTo( parameter.getType() ) ) {
+ replacementParamCandidates.add( sourceParam );
+ }
+ }
+
+ Parameter replacement = parameter;
+ if ( replacementParamCandidates.size() == 1 ) {
+ replacement = first( replacementParamCandidates );
+ }
+
+ return new SourceReference( replacement, propertyEntries, isValid );
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java
index ea889a622..704ecddf5 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java
@@ -25,6 +25,8 @@ import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
+import static org.mapstruct.ap.util.Collections.first;
+
/**
* Selects on inheritance distance, e.g. the amount of inheritance steps from the parameter type.
*
@@ -50,7 +52,7 @@ public class InheritanceSelector implements MethodSelector {
// find the methods with the minimum distance regarding getParameter getParameter type
for ( T method : methods ) {
- Parameter singleSourceParam = method.getSourceParameters().iterator().next();
+ Parameter singleSourceParam = first( method.getSourceParameters() );
int sourceTypeDistance = sourceType.distanceTo( singleSourceParam.getType() );
bestMatchingSourceTypeDistance =
diff --git a/processor/src/main/java/org/mapstruct/ap/prism/MappingInheritanceStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/prism/MappingInheritanceStrategyPrism.java
new file mode 100644
index 000000000..9ce415662
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/prism/MappingInheritanceStrategyPrism.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2012-2015 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.prism;
+
+
+/**
+ * Prism for the enum {@link org.mapstruct.MappingInheritanceStrategy}
+ *
+ * @author Andreas Gudian
+ */
+public enum MappingInheritanceStrategyPrism {
+ EXPLICIT,
+ AUTO_INHERIT_FROM_CONFIG,
+ DEFAULT;
+}
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 3844c536e..ee5829956 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
@@ -46,6 +45,7 @@ import org.mapstruct.ap.model.MappingBuilderContext;
import org.mapstruct.ap.model.MappingMethod;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory;
+import org.mapstruct.ap.model.source.MappingOptions;
import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.option.Options;
import org.mapstruct.ap.prism.DecoratedWithPrism;
@@ -53,11 +53,16 @@ import org.mapstruct.ap.prism.InheritConfigurationPrism;
import org.mapstruct.ap.prism.InheritInverseConfigurationPrism;
import org.mapstruct.ap.prism.MapperPrism;
import org.mapstruct.ap.processor.creation.MappingResolverImpl;
-import org.mapstruct.ap.util.Message;
+import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.MapperConfig;
+import org.mapstruct.ap.util.Message;
import org.mapstruct.ap.util.Strings;
import org.mapstruct.ap.version.VersionInformation;
+import static org.mapstruct.ap.prism.MappingInheritanceStrategyPrism.AUTO_INHERIT_FROM_CONFIG;
+import static org.mapstruct.ap.util.Collections.first;
+import static org.mapstruct.ap.util.Collections.join;
+
/**
* A {@link ModelElementProcessor} which creates a {@link Mapper} from the given
* list of {@link SourceMethod}s.
@@ -83,7 +88,8 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences = initReferencedMappers( mapperTypeElement );
+ MapperConfig mapperConfig = MapperConfig.getInstanceOn( mapperTypeElement );
+ List mapperReferences = initReferencedMappers( mapperTypeElement, mapperConfig );
MappingBuilderContext ctx = new MappingBuilderContext(
typeFactory,
@@ -104,7 +110,7 @@ public class MapperCreationProcessor implements ModelElementProcessor initReferencedMappers(TypeElement element) {
+ private List initReferencedMappers(TypeElement element, MapperConfig mapperConfig) {
List result = new LinkedList();
List variableNames = new LinkedList();
- MapperConfig mapperPrism = MapperConfig.getInstanceOn( element );
-
- for ( TypeMirror usedMapper : mapperPrism.uses() ) {
+ for ( TypeMirror usedMapper : mapperConfig.uses() ) {
DefaultMapperReference mapperReference = DefaultMapperReference.getInstance(
typeFactory.getType( usedMapper ),
MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null,
@@ -133,9 +137,9 @@ public class MapperCreationProcessor implements ModelElementProcessor methods) {
+ private Mapper getMapper(TypeElement element, MapperConfig mapperConfig, List methods) {
List mapperReferences = mappingContext.getMapperReferences();
- List mappingMethods = getMappingMethods( methods );
+ List mappingMethods = getMappingMethods( mapperConfig, methods );
mappingMethods.addAll( mappingContext.getUsedVirtualMappings() );
mappingMethods.addAll( mappingContext.getMappingsToGenerate() );
@@ -194,7 +198,7 @@ public class MapperCreationProcessor implements ModelElementProcessor getMappingMethods(List methods) {
+ private List getMappingMethods(MapperConfig mapperConfig, List methods) {
List mappingMethods = new ArrayList();
for ( SourceMethod method : methods ) {
@@ -241,29 +245,23 @@ public class MapperCreationProcessor implements ModelElementProcessor() );
+
+ MappingOptions mappingOptions = method.getMappingOptions();
boolean hasFactoryMethod = false;
+
if ( method.isIterableMapping() ) {
IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder();
- if ( method.getIterableMapping() == null ) {
- if ( inverseMappingMethod != null && inverseMappingMethod.getIterableMapping() != null ) {
- method.setIterableMapping( inverseMappingMethod.getIterableMapping() );
- }
- else if ( templateMappingMethod != null && templateMappingMethod.getIterableMapping() != null ) {
- method.setIterableMapping( templateMappingMethod.getIterableMapping() );
- }
- }
String dateFormat = null;
List qualifiers = null;
TypeMirror qualifyingElementTargetType = null;
- if ( method.getIterableMapping() != null ) {
- dateFormat = method.getIterableMapping().getDateFormat();
- qualifiers = method.getIterableMapping().getQualifiers();
- qualifyingElementTargetType = method.getIterableMapping().getQualifyingElementTargetType();
+ if ( mappingOptions.getIterableMapping() != null ) {
+ dateFormat = mappingOptions.getIterableMapping().getDateFormat();
+ qualifiers = mappingOptions.getIterableMapping().getQualifiers();
+ qualifyingElementTargetType = mappingOptions.getIterableMapping().getQualifyingElementTargetType();
}
IterableMappingMethod iterableMappingMethod = builder
@@ -281,27 +279,19 @@ public class MapperCreationProcessor implements ModelElementProcessor keyQualifiers = null;
List valueQualifiers = null;
TypeMirror keyQualifyingTargetType = null;
TypeMirror valueQualifyingTargetType = null;
- if ( method.getMapMapping() != null ) {
- keyDateFormat = method.getMapMapping().getKeyFormat();
- valueDateFormat = method.getMapMapping().getValueFormat();
- keyQualifiers = method.getMapMapping().getKeyQualifiers();
- valueQualifiers = method.getMapMapping().getValueQualifiers();
- keyQualifyingTargetType = method.getMapMapping().getKeyQualifyingTargetType();
- valueQualifyingTargetType = method.getMapMapping().getValueQualifyingTargetType();
+ if ( mappingOptions.getMapMapping() != null ) {
+ keyDateFormat = mappingOptions.getMapMapping().getKeyFormat();
+ valueDateFormat = mappingOptions.getMapMapping().getValueFormat();
+ keyQualifiers = mappingOptions.getMapMapping().getKeyQualifiers();
+ valueQualifiers = mappingOptions.getMapMapping().getValueQualifiers();
+ keyQualifyingTargetType = mappingOptions.getMapMapping().getKeyQualifyingTargetType();
+ valueQualifyingTargetType = mappingOptions.getMapMapping().getValueQualifyingTargetType();
}
MapMappingMethod mapMappingMethod = builder
@@ -321,7 +311,6 @@ public class MapperCreationProcessor implements ModelElementProcessor availableMethods, List initializingMethods) {
+ if ( initializingMethods.contains( method ) ) {
+ // cycle detected
+
+ initializingMethods.add( method );
+
+ messager.printMessage(
+ method.getExecutable(),
+ Message.INHERITCONFIGURATION_CYCLE,
+ Strings.join( initializingMethods, " -> " ) );
+ return;
+ }
+
+ initializingMethods.add( method );
+
+ MappingOptions mappingOptions = method.getMappingOptions();
+ List applicablePrototypeMethods = method.getApplicablePrototypeMethods();
+
+ MappingOptions inverseMappingOptions =
+ getInverseMappingOptions( availableMethods, method, initializingMethods, mapperConfig );
+
+ MappingOptions templateMappingOptions =
+ getTemplateMappingOptions(
+ join( availableMethods, applicablePrototypeMethods ),
+ method,
+ initializingMethods,
+ mapperConfig );
+
+ if ( templateMappingOptions != null ) {
+ mappingOptions.applyInheritedOptions( templateMappingOptions, false, method, messager, typeFactory );
+ }
+ else if ( inverseMappingOptions != null ) {
+ mappingOptions.applyInheritedOptions( inverseMappingOptions, true, method, messager, typeFactory );
+ }
+ else if ( mapperConfig.getMappingInheritanceStrategy() == AUTO_INHERIT_FROM_CONFIG ) {
+ if ( applicablePrototypeMethods.size() == 1 ) {
+ mappingOptions.applyInheritedOptions(
+ first( applicablePrototypeMethods ).getMappingOptions(),
+ false,
+ method,
+ messager,
+ typeFactory );
+ }
+ else if ( applicablePrototypeMethods.size() > 1 ) {
+ messager.printMessage(
+ method.getExecutable(),
+ Message.INHERITCONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH,
+ Strings.join( applicablePrototypeMethods, ", " ) );
+ }
+ }
+
+ mappingOptions.markAsFullyInitialized();
+ }
+
private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType(SourceMethod method) {
if ( method.getReturnType().getTypeMirror().getKind() != TypeKind.VOID &&
method.getReturnType().isInterface() &&
@@ -365,12 +408,13 @@ public class MapperCreationProcessor implements ModelElementProcessor rawMethods, SourceMethod method) {
- SourceMethod result = null;
+ private MappingOptions getInverseMappingOptions(List rawMethods, SourceMethod method,
+ List initializingMethods, MapperConfig mapperConfig) {
+ SourceMethod resultMethod = null;
InheritInverseConfigurationPrism reversePrism = InheritInverseConfigurationPrism.getInstanceOn(
method.getExecutable()
);
@@ -389,10 +433,10 @@ public class MapperCreationProcessor implements ModelElementProcessor 1 ) {
reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, reversePrism );
}
- if ( result == null ) {
+ if ( resultMethod == null ) {
reportErrorWhenAmbigousReverseMapping( candidates, method, reversePrism );
}
}
-
- if ( result != null ) {
- reportErrorIfInverseMethodHasInheritAnnotation( result, method, reversePrism );
- }
-
}
- return result;
+
+ return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods );
}
- /**
- * Returns the configuring forward method in case the given method is annotated with
- * {@code @InheritConfiguration} and exactly one such configuring method can unambiguously be selected (as
- * per the source/target type and optionally the name given via {@code @InheritConfiguration}).
- *
- * The method cannot be marked forward mapping itself (hence 'ohter'). And neither can it contain an
- * {@code @InheritReverseConfiguration}
+ private MappingOptions extractInitializedOptions(SourceMethod resultMethod,
+ List rawMethods,
+ MapperConfig mapperConfig,
+ List initializingMethods) {
+ if ( resultMethod != null ) {
+ if ( !resultMethod.getMappingOptions().isFullyInitialized() ) {
+ mergeInheritedOptions( resultMethod, mapperConfig, rawMethods, initializingMethods );
+ }
+
+ return resultMethod.getMappingOptions();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the configuring forward method's options in case the given method is annotated with
+ * {@code @InheritConfiguration} and exactly one such configuring method can unambiguously be selected (as per the
+ * source/target type and optionally the name given via {@code @InheritConfiguration}). The method cannot be marked
+ * forward mapping itself (hence 'ohter'). And neither can it contain an {@code @InheritReverseConfiguration}
*/
- private SourceMethod getTemplateMappingMethod(List rawMethods, SourceMethod method) {
- SourceMethod result = null;
+ private MappingOptions getTemplateMappingOptions(List rawMethods, SourceMethod method,
+ List initializingMethods,
+ MapperConfig mapperConfig) {
+ SourceMethod resultMethod = null;
InheritConfigurationPrism forwardPrism = InheritConfigurationPrism.getInstanceOn(
method.getExecutable()
);
@@ -445,11 +500,10 @@ public class MapperCreationProcessor implements ModelElementProcessor candidates = new ArrayList();
for ( SourceMethod oneMethod : rawMethods ) {
// method must be similar but not equal
- if ( oneMethod.isSimilar( method ) && !( oneMethod.equals( method ) ) ) {
+ if ( method.canInheritFrom( oneMethod ) && !( oneMethod.equals( method ) ) ) {
candidates.add( oneMethod );
}
}
@@ -457,14 +511,15 @@ public class MapperCreationProcessor implements ModelElementProcessor 1 ) {
@@ -478,23 +533,19 @@ public class MapperCreationProcessor implements ModelElementProcessor 1 ) {
reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, forwardPrism );
}
- if ( result == null ) {
+ if ( resultMethod == null ) {
reportErrorWhenAmbigousMapping( candidates, method, forwardPrism );
}
}
-
- if ( result != null ) {
- reportErrorIfMethodHasAnnotation( result, method, forwardPrism );
- }
-
}
- return result;
+
+ return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods );
}
private void reportErrorWhenInheritForwardAlsoHasInheritReverseMapping(SourceMethod method) {
@@ -507,34 +558,6 @@ public class MapperCreationProcessor implements ModelElementProcessor candidates, SourceMethod method,
InheritInverseConfigurationPrism reversePrism) {
@@ -570,7 +593,7 @@ public class MapperCreationProcessor implements ModelElementProcessor candidates, SourceMethod method,
InheritConfigurationPrism prism) {
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
index 98ff02bc8..a29b2d0de 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
@@ -18,14 +18,12 @@
*/
package org.mapstruct.ap.processor;
-import static org.mapstruct.ap.util.Executables.getAllEnclosedExecutableElements;
-
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.mapstruct.ap.util.FormattingMessager;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
@@ -48,8 +46,11 @@ import org.mapstruct.ap.prism.MapMappingPrism;
import org.mapstruct.ap.prism.MappingPrism;
import org.mapstruct.ap.prism.MappingsPrism;
import org.mapstruct.ap.util.AnnotationProcessingException;
-import org.mapstruct.ap.util.Message;
+import org.mapstruct.ap.util.FormattingMessager;
import org.mapstruct.ap.util.MapperConfig;
+import org.mapstruct.ap.util.Message;
+
+import static org.mapstruct.ap.util.Executables.getAllEnclosedExecutableElements;
/**
* A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s
@@ -72,7 +73,19 @@ public class MethodRetrievalProcessor implements ModelElementProcessor prototypeMethods =
+ retrievePrototypeMethods( mapperConfig.getMapperConfigMirror(), mapperConfig );
+ return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperConfig, prototypeMethods );
}
@Override
@@ -80,19 +93,60 @@ public class MethodRetrievalProcessor implements ModelElementProcessor retrievePrototypeMethods(TypeMirror typeMirror, MapperConfig mapperConfig) {
+ if ( typeMirror == null || typeMirror.getKind() == TypeKind.VOID ) {
+ return Collections.emptyList();
+ }
+
+ TypeElement typeElement = asTypeElement( typeMirror );
+ List methods = new ArrayList();
+ for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, typeElement ) ) {
+
+ ExecutableType methodType = typeFactory.getMethodType( typeElement, executable );
+ List parameters = typeFactory.getParameters( methodType, executable );
+ boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters );
+
+ // prototype methods don't have prototypes themselves
+ List prototypeMethods = Collections.emptyList();
+
+ SourceMethod method =
+ getMethodRequiringImplementation(
+ methodType,
+ executable,
+ parameters,
+ containsTargetTypeParameter,
+ mapperConfig,
+ prototypeMethods );
+
+ if ( method != null ) {
+ methods.add( method );
+ }
+ }
+
+ return methods;
+ }
+
/**
* Retrieves the mapping methods declared by the given mapper type.
*
* @param usedMapper The type of interest (either the mapper to implement or a used mapper via @uses annotation)
* @param mapperToImplement the top level type (mapper) that requires implementation
- *
+ * @param mapperConfig the mapper config
+ * @param prototypeMethods prototype methods defined in mapper config type
* @return All mapping methods declared by the given type
*/
- private List retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement) {
+ private List retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement,
+ MapperConfig mapperConfig, List prototypeMethods) {
List methods = new ArrayList();
for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, usedMapper ) ) {
- SourceMethod method = getMethod( usedMapper, executable, mapperToImplement );
+ SourceMethod method = getMethod(
+ usedMapper,
+ executable,
+ mapperToImplement,
+ mapperConfig,
+ prototypeMethods );
+
if ( method != null ) {
methods.add( method );
}
@@ -100,15 +154,12 @@ public class MethodRetrievalProcessor implements ModelElementProcessor prototypeMethods) {
ExecutableType methodType = typeFactory.getMethodType( usedMapper, method );
List parameters = typeFactory.getParameters( methodType, method );
@@ -134,7 +187,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters,
boolean containsTargetTypeParameter,
- TypeElement mapperToImplement) {
+ MapperConfig mapperConfig,
+ List prototypeMethods) {
Type returnType = typeFactory.getReturnType( methodType );
List exceptionTypes = typeFactory.getThrownTypes( methodType );
List sourceParameters = extractSourceParameters( parameters );
@@ -181,8 +236,9 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters) {
diff --git a/processor/src/main/java/org/mapstruct/ap/util/Collections.java b/processor/src/main/java/org/mapstruct/ap/util/Collections.java
index bbefa270f..e41e4715b 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/Collections.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/Collections.java
@@ -72,4 +72,17 @@ public class Collections {
return set;
}
+
+ public static T first(Collection collection) {
+ return collection.iterator().next();
+ }
+
+ public static List join(List a, List b) {
+ List result = new ArrayList( a.size() + b.size() );
+
+ result.addAll( a );
+ result.addAll( b );
+
+ return result;
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java b/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java
index e09a5b19e..c6fc33e43 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/MapperConfig.java
@@ -33,11 +33,10 @@ import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.prism.MapperConfigPrism;
import org.mapstruct.ap.prism.MapperPrism;
+import org.mapstruct.ap.prism.MappingInheritanceStrategyPrism;
import org.mapstruct.ap.prism.NullValueMappingPrism;
import org.mapstruct.ap.prism.NullValueMappingStrategyPrism;
-import static org.mapstruct.ap.prism.CollectionMappingStrategyPrism.valueOf;
-
/**
* Class decorating the {@link MapperPrism} with the 'default' configuration.
*
@@ -58,10 +57,6 @@ public class MapperConfig {
return new MapperConfig( MapperPrism.getInstanceOn( e ) );
}
- public static MapperConfig getInstance(AnnotationMirror mirror) {
- return new MapperConfig( MapperPrism.getInstance( mirror ) );
- }
-
private MapperConfig(MapperPrism mapperPrism) {
this.mapperPrism = mapperPrism;
TypeMirror typeMirror = mapperPrism.config();
@@ -100,7 +95,8 @@ public class MapperConfig {
}
public CollectionMappingStrategyPrism getCollectionMappingStrategy() {
- CollectionMappingStrategyPrism mapperPolicy = valueOf( mapperPrism.collectionMappingStrategy() );
+ CollectionMappingStrategyPrism mapperPolicy =
+ CollectionMappingStrategyPrism.valueOf( mapperPrism.collectionMappingStrategy() );
if ( mapperPolicy != CollectionMappingStrategyPrism.DEFAULT ) {
// it is not the default mapper configuration, so return the mapper configured value
@@ -108,7 +104,8 @@ public class MapperConfig {
}
else if ( mapperConfigPrism != null ) {
// try the config mapper configuration
- CollectionMappingStrategyPrism configPolicy = valueOf( mapperConfigPrism.collectionMappingStrategy() );
+ CollectionMappingStrategyPrism configPolicy =
+ CollectionMappingStrategyPrism.valueOf( mapperConfigPrism.collectionMappingStrategy() );
if ( configPolicy != CollectionMappingStrategyPrism.DEFAULT ) {
// its not the default configuration, so return the mapper config configured value
return configPolicy;
@@ -118,6 +115,24 @@ public class MapperConfig {
return CollectionMappingStrategyPrism.ACCESSOR_ONLY;
}
+ public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() {
+ MappingInheritanceStrategyPrism mapperPolicy =
+ MappingInheritanceStrategyPrism.valueOf( mapperPrism.mappingInheritanceStrategy() );
+
+ if ( mapperPolicy != MappingInheritanceStrategyPrism.DEFAULT ) {
+ return mapperPolicy;
+ }
+ else if ( mapperConfigPrism != null ) {
+ MappingInheritanceStrategyPrism configPolicy =
+ MappingInheritanceStrategyPrism.valueOf( mapperConfigPrism.mappingInheritanceStrategy() );
+ if ( configPolicy != MappingInheritanceStrategyPrism.DEFAULT ) {
+ return configPolicy;
+ }
+ }
+
+ return MappingInheritanceStrategyPrism.EXPLICIT;
+ }
+
public boolean isMapToDefault(NullValueMappingPrism mapNullToDefault) {
// check on method level
@@ -165,6 +180,10 @@ public class MapperConfig {
}
}
+ public TypeMirror getMapperConfigMirror() {
+ return mapperPrism.config();
+ }
+
public boolean isValid() {
return mapperPrism.isValid;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/util/Message.java b/processor/src/main/java/org/mapstruct/ap/util/Message.java
index 6c61a03e1..8db9c5086 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/Message.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/Message.java
@@ -86,18 +86,16 @@ public enum Message {
RETRIEVAL_NON_ENUM_TO_ENUM( "Can't generate mapping method from non-enum type to enum type." ),
INHERITCONFIGURATION_BOTH( "Method cannot be annotated with both a @InheritConfiguration and @InheritInverseConfiguration." ),
- INHERITINVERSECONFIGURATION_REFERENCE_HAS_INVERSE( "Resolved inverse mapping method %s() should not carry the @InheritInverseConfiguration annotation itself." ),
- INHERITINVERSECONFIGURATION_REFERENCE_HAS_FORWARD( "Resolved inverse mapping method %s() should not carry the @InheritConfiguration annotation." ),
INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ),
INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ),
INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ),
INHERITINVERSECONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ),
- INHERITCONFIGURATION_REFERENCE_HAS_FORWARD( "Resolved mapping method %s() should not carry the @InheritConfiguration annotation itself." ),
- INHERITCONFIGURATION_REFERENCE_HAS_INVERSE( "Resolved mapping method %s() should not carry the @InheritInverseConfiguration annotation." ),
INHERITCONFIGURATION_DUPLICATES( "Several matching methods exist: %s(). Specify a name explicitly." ),
INHERITCONFIGURATION_INVALIDNAME( "None of the candidates %s() matches given name: \"%s\"." ),
INHERITCONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ),
- INHERITCONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." );
+ INHERITCONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ),
+ INHERITCONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH( "More than one configuration prototype method is applicable. Use @InheritConfiguration to select one of them explicitly: %s." ),
+ INHERITCONFIGURATION_CYCLE( "Cycle detected while evaluating inherited configurations. Inheritance path: %s" );
// CHECKSTYLE:ON
private final String description;
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/AutoInheritedConfig.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/AutoInheritedConfig.java
new file mode 100644
index 000000000..12c63ade8
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/AutoInheritedConfig.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+import org.mapstruct.MapperConfig;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingInheritanceStrategy;
+import org.mapstruct.Mappings;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * @author Andreas Gudian
+ */
+@MapperConfig(
+ mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG,
+ unmappedTargetPolicy = ReportingPolicy.ERROR
+)
+public interface AutoInheritedConfig {
+ @Mappings( {
+ @Mapping( target = "primaryKey", source = "id" ),
+ @Mapping( target = "auditTrail", ignore = true )
+ } )
+ BaseVehicleEntity baseDtoToEntity(BaseVehicleDto dto);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/AutoInheritedDriverConfig.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/AutoInheritedDriverConfig.java
new file mode 100644
index 000000000..6e2e1764e
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/AutoInheritedDriverConfig.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+import org.mapstruct.MapperConfig;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingInheritanceStrategy;
+import org.mapstruct.Mappings;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * @author Andreas Gudian
+ */
+@MapperConfig(
+ mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG,
+ unmappedTargetPolicy = ReportingPolicy.ERROR
+)
+public interface AutoInheritedDriverConfig {
+ @Mappings( {
+ @Mapping( target = "primaryKey", source = "dto.id" ),
+ @Mapping( target = "auditTrail", ignore = true ),
+ @Mapping( target = "driverName", source = "drv.name" )
+ } )
+ CarWithDriverEntity baseDtoToEntity(DriverDto drv, BaseVehicleDto dto);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/BaseVehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/BaseVehicleDto.java
new file mode 100644
index 000000000..17f725c22
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/BaseVehicleDto.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public abstract class BaseVehicleDto {
+ private long id;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/BaseVehicleEntity.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/BaseVehicleEntity.java
new file mode 100644
index 000000000..a79024afb
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/BaseVehicleEntity.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public abstract class BaseVehicleEntity {
+ private long primaryKey;
+ private String auditTrail;
+
+ public long getPrimaryKey() {
+ return primaryKey;
+ }
+
+ public void setPrimaryKey(long primaryKey) {
+ this.primaryKey = primaryKey;
+ }
+
+ public String getAuditTrail() {
+ return auditTrail;
+ }
+
+ public void setAuditTrail(String auditTrail) {
+ this.auditTrail = auditTrail;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarDto.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarDto.java
new file mode 100644
index 000000000..9928a3e00
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarDto.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class CarDto extends BaseVehicleDto {
+ private String colour;
+
+ public String getColour() {
+ return colour;
+ }
+
+ public void setColour(String colour) {
+ this.colour = colour;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarEntity.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarEntity.java
new file mode 100644
index 000000000..080d93958
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarEntity.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class CarEntity extends BaseVehicleEntity {
+ private String color;
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperErroneouslyAnnotated2.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperWithAutoInheritance.java
similarity index 52%
rename from processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperErroneouslyAnnotated2.java
rename to processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperWithAutoInheritance.java
index 5c11822ce..6f224352b 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperErroneouslyAnnotated2.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperWithAutoInheritance.java
@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.mapstruct.ap.test.template;
+package org.mapstruct.ap.test.inheritfromconfig;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
@@ -27,28 +27,30 @@ import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
- * @author Sjaak Derksen
+ * @author Andreas Gudian
+ *
*/
-@Mapper
-public interface SourceTargetMapperErroneouslyAnnotated2 {
+@Mapper(
+ config = AutoInheritedConfig.class
+)
+public interface CarMapperWithAutoInheritance {
+ CarMapperWithAutoInheritance INSTANCE = Mappers.getMapper( CarMapperWithAutoInheritance.class );
- SourceTargetMapperErroneouslyAnnotated2 INSTANCE =
- Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated2.class );
+ @Mapping( target = "color", source = "colour" )
+ CarEntity toCarEntity(CarDto carDto);
- @Mappings({
- @Mapping(target = "stringPropY", source = "stringPropX"),
- @Mapping(target = "integerPropY", source = "integerPropX"),
- @Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
- @Mapping(target = "constantProp", constant = "constant"),
- @Mapping(target = "expressionProp", expression = "java(\"expression\")")
- })
- Target forwardCreate(Source source);
+ @InheritInverseConfiguration( name = "toCarEntity" )
+ CarDto toCarDto(CarEntity entity);
- @InheritInverseConfiguration(name = "forwardCreate")
- @Mapping(target = "nestedSourceProp", ignore = true)
- Source forwardCreate(Target source);
+ @Mappings( {
+ @Mapping( target = "color", source = "colour" ),
+ @Mapping( target = "auditTrail", constant = "fixed" )
+ } )
+ CarEntity toCarEntityWithFixedAuditTrail(CarDto carDto);
- @InheritConfiguration( name = "forwardCreate" )
- void forwardUpdate(Target source, @MappingTarget Source target);
+ @Mapping( target = "color", source = "colour" )
+ void intoCarEntityOnItsOwn(CarDto carDto, @MappingTarget CarEntity entity);
+ @InheritConfiguration( name = "toCarEntity" )
+ void intoCarEntity(CarDto carDto, @MappingTarget CarEntity entity);
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperWithExplicitInheritance.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperWithExplicitInheritance.java
new file mode 100644
index 000000000..92d831627
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperWithExplicitInheritance.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+import org.mapstruct.InheritConfiguration;
+import org.mapstruct.InheritInverseConfiguration;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingInheritanceStrategy;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+@Mapper(
+ config = AutoInheritedConfig.class,
+ mappingInheritanceStrategy = MappingInheritanceStrategy.EXPLICIT
+)
+public interface CarMapperWithExplicitInheritance {
+ CarMapperWithExplicitInheritance INSTANCE = Mappers.getMapper( CarMapperWithExplicitInheritance.class );
+
+ @InheritConfiguration( name = "baseDtoToEntity" )
+ @Mapping( target = "color", source = "colour" )
+ CarEntity toCarEntity(CarDto carDto);
+
+ @InheritInverseConfiguration( name = "toCarEntity" )
+ CarDto toCarDto(CarEntity entity);
+
+ @InheritConfiguration( name = "toCarEntity" )
+ @Mapping( target = "auditTrail", constant = "fixed" )
+ CarEntity toCarEntityWithFixedAuditTrail(CarDto carDto);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarWithDriverEntity.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarWithDriverEntity.java
new file mode 100644
index 000000000..ad41ed9b8
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarWithDriverEntity.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class CarWithDriverEntity extends CarEntity {
+ private String driverName;
+
+ public String getDriverName() {
+ return driverName;
+ }
+
+ public void setDriverName(String driverName) {
+ this.driverName = driverName;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarWithDriverMapperWithAutoInheritance.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarWithDriverMapperWithAutoInheritance.java
new file mode 100644
index 000000000..09c067800
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarWithDriverMapperWithAutoInheritance.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+@Mapper(
+ config = AutoInheritedDriverConfig.class
+)
+public interface CarWithDriverMapperWithAutoInheritance {
+ CarWithDriverMapperWithAutoInheritance INSTANCE = Mappers.getMapper( CarWithDriverMapperWithAutoInheritance.class );
+
+ @Mapping( target = "color", source = "carDto.colour" )
+ CarWithDriverEntity toCarWithDriverEntity(CarDto carDto, DriverDto driverDto);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/DriverDto.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/DriverDto.java
new file mode 100644
index 000000000..eefb6ef24
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/DriverDto.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class DriverDto {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous1Config.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous1Config.java
new file mode 100644
index 000000000..4cb6b5015
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous1Config.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+import org.mapstruct.MapperConfig;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingInheritanceStrategy;
+import org.mapstruct.Mappings;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * Leads to ambiguous prototype methods error.
+ *
+ * @author Andreas Gudian
+ */
+@MapperConfig(
+ mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG,
+ unmappedTargetPolicy = ReportingPolicy.WARN
+)
+public interface Erroneous1Config {
+ @Mappings( {
+ @Mapping( target = "primaryKey", source = "id" ),
+ @Mapping( target = "auditTrail", ignore = true )
+ } )
+ BaseVehicleEntity baseDtoToEntity(BaseVehicleDto dto);
+
+ @Mapping( target = "primaryKey", ignore = true )
+ BaseVehicleEntity anythingToEntity(Object anyting);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous1Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous1Mapper.java
new file mode 100644
index 000000000..ab33131bc
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous1Mapper.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+@Mapper(
+ config = Erroneous1Config.class
+)
+public interface Erroneous1Mapper {
+ Erroneous1Mapper INSTANCE = Mappers.getMapper( Erroneous1Mapper.class );
+
+ @Mapping( target = "color", source = "colour" )
+ CarEntity toCarEntity(CarDto carDto);
+
+ @Mappings( {
+ @Mapping( target = "color", source = "colour" ),
+ @Mapping( target = "auditTrail", constant = "fixed" )
+ } )
+ CarEntity toCarEntityWithFixedAuditTrail(CarDto carDto);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapperErroneouslyAnnotated2.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous2Mapper.java
similarity index 55%
rename from processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapperErroneouslyAnnotated2.java
rename to processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous2Mapper.java
index 81d309fa7..35b990db0 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapperErroneouslyAnnotated2.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/Erroneous2Mapper.java
@@ -16,34 +16,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.mapstruct.ap.test.reverse;
+package org.mapstruct.ap.test.inheritfromconfig;
import org.mapstruct.InheritConfiguration;
-import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
- * @author Sjaak Derksen
+ * @author Andreas Gudian
+ *
*/
-@Mapper
-public interface SourceTargetMapperErroneouslyAnnotated2 {
+@Mapper(
+ config = AutoInheritedConfig.class
+)
+public interface Erroneous2Mapper {
+ Erroneous2Mapper INSTANCE = Mappers.getMapper( Erroneous2Mapper.class );
- SourceTargetMapperErroneouslyAnnotated2 INSTANCE
- = Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated2.class );
+ @InheritConfiguration( name = "toCarEntity2" )
+ CarEntity toCarEntity1(CarDto carDto);
- @Mappings({
- @Mapping(source = "stringPropX", target = "stringPropY"),
- @Mapping(source = "integerPropX", target = "integerPropY"),
- @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream")
- })
- Target forward(Source source);
+ @InheritConfiguration( name = "toCarEntity3" )
+ CarEntity toCarEntity2(CarDto carDto);
- @InheritConfiguration(name = "forward2")
- Source forward2(Target target);
-
- @InheritInverseConfiguration(name = "forward2")
- Target reverse(Source source);
+ @InheritConfiguration( name = "toCarEntity1" )
+ @Mappings( {
+ @Mapping( target = "color", ignore = true ),
+ @Mapping( target = "auditTrail", ignore = true ),
+ @Mapping( target = "primaryKey", ignore = true )
+ } )
+ void toCarEntity3(CarDto carDto, @MappingTarget CarEntity entity);
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java
new file mode 100644
index 000000000..df10a4bf0
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java
@@ -0,0 +1,216 @@
+/**
+ * Copyright 2012-2015 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.test.inheritfromconfig;
+
+import javax.tools.Diagnostic.Kind;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mapstruct.ap.testutil.IssueKey;
+import org.mapstruct.ap.testutil.WithClasses;
+import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
+import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
+import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
+import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+@RunWith( AnnotationProcessorTestRunner.class )
+@WithClasses( {
+ BaseVehicleDto.class,
+ BaseVehicleEntity.class,
+ CarDto.class,
+ CarEntity.class,
+ CarMapperWithAutoInheritance.class,
+ CarMapperWithExplicitInheritance.class,
+ AutoInheritedConfig.class } )
+@IssueKey( "168" )
+public class InheritFromConfigTest {
+
+ @Test
+ public void autoInheritedMappingIsApplied() {
+ CarDto carDto = newTestDto();
+
+ CarEntity carEntity = CarMapperWithAutoInheritance.INSTANCE.toCarEntity( carDto );
+
+ assertEntity( carEntity );
+ }
+
+ @Test
+ public void autoInheritedMappingIsAppliedForMappingTarget() {
+ CarDto carDto = newTestDto();
+ CarEntity carEntity = new CarEntity();
+
+ CarMapperWithAutoInheritance.INSTANCE.intoCarEntityOnItsOwn( carDto, carEntity );
+
+ assertEntity( carEntity );
+ }
+
+ @Test
+ public void autoInheritedMappingIsAppliedForMappingTargetWithTwoStepInheritance() {
+ CarDto carDto = newTestDto();
+ CarEntity carEntity = new CarEntity();
+
+ CarMapperWithAutoInheritance.INSTANCE.intoCarEntity( carDto, carEntity );
+
+ assertEntity( carEntity );
+ }
+
+ private void assertEntity(CarEntity carEntity) {
+ assertThat( carEntity.getColor() ).isEqualTo( "red" );
+ assertThat( carEntity.getPrimaryKey() ).isEqualTo( 42L );
+ assertThat( carEntity.getAuditTrail() ).isNull();
+ }
+
+ private CarDto newTestDto() {
+ CarDto carDto = new CarDto();
+ carDto.setColour( "red" );
+ carDto.setId( 42L );
+ return carDto;
+ }
+
+ @Test
+ public void autoInheritedMappingIsOverriddenAtMethodLevel() {
+ CarDto carDto = newTestDto();
+
+ CarEntity carEntity = CarMapperWithAutoInheritance.INSTANCE.toCarEntityWithFixedAuditTrail( carDto );
+
+ assertThat( carEntity.getColor() ).isEqualTo( "red" );
+ assertThat( carEntity.getPrimaryKey() ).isEqualTo( 42L );
+ assertThat( carEntity.getAuditTrail() ).isEqualTo( "fixed" );
+ }
+
+ @Test
+ public void autoInheritedMappingIsAppliedInReverse() {
+ CarEntity carEntity = new CarEntity();
+ carEntity.setColor( "red" );
+ carEntity.setPrimaryKey( 42L );
+
+ CarDto carDto = CarMapperWithAutoInheritance.INSTANCE.toCarDto( carEntity );
+
+ assertThat( carDto.getColour() ).isEqualTo( "red" );
+ assertThat( carDto.getId() ).isEqualTo( 42L );
+ }
+
+ @Test
+ public void explicitInheritedMappingIsAppliedInReverse() {
+ CarEntity carEntity = new CarEntity();
+ carEntity.setColor( "red" );
+ carEntity.setPrimaryKey( 42L );
+
+ CarDto carDto = CarMapperWithExplicitInheritance.INSTANCE.toCarDto( carEntity );
+
+ assertThat( carDto.getColour() ).isEqualTo( "red" );
+ assertThat( carDto.getId() ).isEqualTo( 42L );
+ }
+
+ @Test
+ public void explicitInheritedMappingWithTwoLevelsIsOverriddenAtMethodLevel() {
+ CarDto carDto = newTestDto();
+
+ CarEntity carEntity = CarMapperWithExplicitInheritance.INSTANCE.toCarEntityWithFixedAuditTrail( carDto );
+
+ assertThat( carEntity.getColor() ).isEqualTo( "red" );
+ assertThat( carEntity.getPrimaryKey() ).isEqualTo( 42L );
+ assertThat( carEntity.getAuditTrail() ).isEqualTo( "fixed" );
+ }
+
+ @Test
+ public void explicitInheritedMappingIsApplied() {
+ CarDto carDto = newTestDto();
+
+ CarEntity carEntity = CarMapperWithExplicitInheritance.INSTANCE.toCarEntity( carDto );
+
+ assertEntity( carEntity );
+ }
+
+ @Test
+ @WithClasses( {
+ DriverDto.class,
+ CarWithDriverEntity.class,
+ CarWithDriverMapperWithAutoInheritance.class,
+ AutoInheritedDriverConfig.class } )
+ public void autoInheritedFromMultipleSources() {
+ CarDto carDto = newTestDto();
+ DriverDto driverDto = new DriverDto();
+ driverDto.setName( "Malcroft" );
+
+ CarWithDriverEntity carWithDriverEntity =
+ CarWithDriverMapperWithAutoInheritance.INSTANCE.toCarWithDriverEntity( carDto, driverDto );
+
+ assertEntity( carWithDriverEntity );
+ assertThat( carWithDriverEntity.getDriverName() ).isEqualTo( "Malcroft" );
+ }
+
+ @Test
+ @WithClasses( { Erroneous1Mapper.class, Erroneous1Config.class } )
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic( type = Erroneous1Mapper.class,
+ kind = Kind.ERROR,
+ line = 37,
+ messageRegExp = "More than one configuration prototype method is applicable. Use @InheritConfiguration"
+ + " to select one of them explicitly:"
+ + " .*BaseVehicleEntity baseDtoToEntity\\(.*BaseVehicleDto dto\\),"
+ + " .*BaseVehicleEntity anythingToEntity\\(java.lang.Object anyting\\)\\." ),
+ @Diagnostic( type = Erroneous1Mapper.class,
+ kind = Kind.WARNING,
+ line = 37,
+ messageRegExp = "Unmapped target properties: \"auditTrail, primaryKey\"\\." ),
+ @Diagnostic( type = Erroneous1Mapper.class,
+ kind = Kind.ERROR,
+ line = 43,
+ messageRegExp = "More than one configuration prototype method is applicable. Use @InheritConfiguration"
+ + " to select one of them explicitly:"
+ + " .*BaseVehicleEntity baseDtoToEntity\\(.*BaseVehicleDto dto\\),"
+ + " .*BaseVehicleEntity anythingToEntity\\(java.lang.Object anyting\\)\\." ),
+ @Diagnostic( type = Erroneous1Mapper.class,
+ kind = Kind.WARNING,
+ line = 43,
+ messageRegExp = "Unmapped target property: \"primaryKey\"\\." )
+ }
+ )
+ public void erroneous1MultiplePrototypeMethodsMatch() {
+
+ }
+
+ @Test
+ @WithClasses( { Erroneous2Mapper.class } )
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic( type = Erroneous2Mapper.class,
+ kind = Kind.ERROR,
+ line = 39,
+ messageRegExp = "Cycle detected while evaluating inherited configurations. Inheritance path:"
+ + " .*CarEntity toCarEntity1\\(.*CarDto carDto\\)"
+ + " -> .*CarEntity toCarEntity2\\(.*CarDto carDto\\)"
+ + " -> void toCarEntity3\\(.*CarDto carDto, @MappingTarget .*CarEntity entity\\)"
+ + " -> .*CarEntity toCarEntity1\\(.*CarDto carDto\\)" )
+ }
+ )
+ public void erroneous2InheritanceCycle() {
+
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/prism/EnumPrismsTest.java b/processor/src/test/java/org/mapstruct/ap/test/prism/EnumPrismsTest.java
index b986aa17c..23824c1a5 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/prism/EnumPrismsTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/prism/EnumPrismsTest.java
@@ -23,11 +23,13 @@ import java.util.List;
import org.junit.Test;
import org.mapstruct.CollectionMappingStrategy;
+import org.mapstruct.MappingInheritanceStrategy;
+import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.ap.prism.CollectionMappingStrategyPrism;
+import org.mapstruct.ap.prism.MappingInheritanceStrategyPrism;
+import org.mapstruct.ap.prism.NullValueMappingStrategyPrism;
import static org.fest.assertions.Assertions.assertThat;
-import org.mapstruct.NullValueMappingStrategy;
-import org.mapstruct.ap.prism.NullValueMappingStrategyPrism;
/**
* Test for manually created prisms on enumeration types
@@ -47,6 +49,12 @@ public class EnumPrismsTest {
namesOf( NullValueMappingStrategyPrism.values() ) );
}
+ @Test
+ public void mapMappingInheritanceStrategyPrismIsCorrect() {
+ assertThat( namesOf( MappingInheritanceStrategy.values() ) ).isEqualTo(
+ namesOf( MappingInheritanceStrategyPrism.values() ) );
+ }
+
private static List namesOf(Enum>[] values) {
List names = new ArrayList( values.length );
diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java
index 5be18c1f4..1c51741ef 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java
@@ -108,8 +108,8 @@ public class InheritInverseConfigurationTest {
@Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.ERROR,
line = 50,
- messageRegExp = "Given name \"forward\" matches several candidate methods: .*forward.*\\(\\), "
- + ".*forward.*\\(\\)"),
+ messageRegExp = "Given name \"forward\" matches several candidate methods: .*forward\\(.*\\), "
+ + ".*forward\\(.*\\)"),
@Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.WARNING,
line = 55,
@@ -119,46 +119,6 @@ public class InheritInverseConfigurationTest {
public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() {
}
- @Test
- @WithClasses({ SourceTargetMapperErroneouslyAnnotated1.class })
- @ExpectedCompilationOutcome(
- value = CompilationResult.FAILED,
- diagnostics = {
- @Diagnostic(type = SourceTargetMapperErroneouslyAnnotated1.class,
- kind = Kind.ERROR,
- line = 50,
- messageRegExp = "Resolved inverse mapping method reverse\\(\\) should not carry the "
- + "@InheritInverseConfiguration annotation itself.")
- }
- )
- public void shouldUseWronglyAnnotatedError1() {
- }
-
- @Test
- @WithClasses({ SourceTargetMapperErroneouslyAnnotated2.class })
- @ExpectedCompilationOutcome(
- value = CompilationResult.FAILED,
- diagnostics = {
- @Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
- kind = Kind.WARNING,
- line = 45,
- messageRegExp = "Unmapped target properties: \"stringPropX, integerPropX, "
- + "someConstantDownstream, propertyToIgnoreDownstream\""),
- @Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
- kind = Kind.ERROR,
- line = 47,
- messageRegExp = "Resolved inverse mapping method forward2\\(\\) should not carry "
- + "the @InheritConfiguration annotation"),
- @Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
- kind = Kind.WARNING,
- line = 48,
- messageRegExp = "Unmapped target properties: \"stringPropY, integerPropY, "
- + "propertyNotToIgnoreUpstream\"")
- }
- )
- public void shouldUseWronglyAnnotatedError2() {
- }
-
@Test
@WithClasses({ SourceTargetMapperNonMatchingName.class })
@ExpectedCompilationOutcome(
diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapperErroneouslyAnnotated1.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapperErroneouslyAnnotated1.java
deleted file mode 100644
index 51b54c6d7..000000000
--- a/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapperErroneouslyAnnotated1.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright 2012-2015 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.test.reverse;
-
-import org.mapstruct.InheritInverseConfiguration;
-import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.mapstruct.Mappings;
-import org.mapstruct.factory.Mappers;
-
-/**
- * @author Sjaak Derksen
- */
-@Mapper
-public interface SourceTargetMapperErroneouslyAnnotated1 {
-
- SourceTargetMapperErroneouslyAnnotated1 INSTANCE =
- Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated1.class );
-
- @Mappings({
- @Mapping(source = "stringPropX", target = "stringPropY"),
- @Mapping(source = "integerPropX", target = "integerPropY"),
- @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream")
- })
- Target forward(Source source);
-
- @InheritInverseConfiguration(name = "forward")
- @Mappings({
- @Mapping(target = "someConstantDownstream", constant = "test"),
- @Mapping(target = "propertyToIgnoreDownstream", ignore = true)
- })
- Source reverse(Target target);
-
- @InheritInverseConfiguration(name = "reverse")
- @Mappings({
- @Mapping(source = "stringPropX", target = "stringPropY"),
- @Mapping(source = "integerPropX", target = "integerPropY"),
- @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream")
- })
- Target forward2(Source source);
-}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java b/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java
index dc67afac5..883d66115 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java
@@ -182,36 +182,6 @@ public class InheritConfigurationTest {
public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() {
}
- @Test
- @WithClasses({ SourceTargetMapperErroneouslyAnnotated1.class })
- @ExpectedCompilationOutcome(
- value = CompilationResult.FAILED,
- diagnostics = {
- @Diagnostic(type = SourceTargetMapperErroneouslyAnnotated1.class,
- kind = Kind.ERROR,
- line = 49,
- messageRegExp = "Resolved mapping method forwardCreate1\\(\\) should not carry the "
- + "@InheritConfiguration annotation itself.")
- }
- )
- public void shouldUseWronglyAnnotatedError1() {
- }
-
- @Test
- @WithClasses({ SourceTargetMapperErroneouslyAnnotated2.class })
- @ExpectedCompilationOutcome(
- value = CompilationResult.FAILED,
- diagnostics = {
- @Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
- kind = Kind.ERROR,
- line = 51,
- messageRegExp = "Resolved mapping method forwardCreate\\(\\) should not carry the "
- + "@InheritInverseConfiguration annotation.")
- }
- )
- public void shouldUseWronglyAnnotatedError2() {
- }
-
@Test
@WithClasses({ SourceTargetMapperNonMatchingName.class })
@ExpectedCompilationOutcome(
diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperErroneouslyAnnotated1.java b/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperErroneouslyAnnotated1.java
deleted file mode 100644
index 23499504b..000000000
--- a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperErroneouslyAnnotated1.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Copyright 2012-2015 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.test.template;
-
-import org.mapstruct.InheritConfiguration;
-import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.mapstruct.MappingTarget;
-import org.mapstruct.Mappings;
-import org.mapstruct.factory.Mappers;
-
-/**
- * @author Sjaak Derksen
- */
-@Mapper
-public interface SourceTargetMapperErroneouslyAnnotated1 {
-
- SourceTargetMapperErroneouslyAnnotated1 INSTANCE =
- Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated1.class );
-
- @Mappings({
- @Mapping(target = "stringPropY", source = "stringPropX"),
- @Mapping(target = "integerPropY", source = "integerPropX"),
- @Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
- @Mapping(target = "constantProp", constant = "constant"),
- @Mapping(target = "expressionProp", expression = "java(\"expression\")")
- })
- Target forwardCreate(Source source);
-
- @InheritConfiguration( name = "forwardCreate" )
- Target forwardCreate1(Source source);
-
- @InheritConfiguration( name = "forwardCreate1" )
- void forwardUpdate(Source source, @MappingTarget Target target);
-}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSeveralArgs.java b/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSeveralArgs.java
index d5dfa7d05..03e319b8e 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSeveralArgs.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSeveralArgs.java
@@ -34,11 +34,11 @@ public interface SourceTargetMapperSeveralArgs {
SourceTargetMapperSeveralArgs INSTANCE = Mappers.getMapper( SourceTargetMapperSeveralArgs.class );
@Mappings({
- @Mapping(target = "stringPropY", source = "source.stringPropX"),
- @Mapping(target = "integerPropY", source = "source.integerPropX"),
- @Mapping(target = "nestedResultProp", source = "source.nestedSourceProp.nested")
+ @Mapping( target = "stringPropY", source = "s1.stringPropX" ),
+ @Mapping( target = "integerPropY", source = "s1.integerPropX" ),
+ @Mapping( target = "nestedResultProp", source = "s1.nestedSourceProp.nested" )
})
- Target forwardCreate(Source source, String constantProp, String expressionProp);
+ Target forwardCreate(Source s1, String constantProp, String expressionProp);
@InheritConfiguration
void forwardUpdate(Source source, String constantProp, String expressionProp, @MappingTarget Target target);
diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSingle.java b/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSingle.java
index a6d70870b..8c892c8fa 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSingle.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/template/SourceTargetMapperSingle.java
@@ -40,7 +40,7 @@ public interface SourceTargetMapperSingle {
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")"),
})
- Target forwardCreate(Source source);
+ Target forwardCreate(Source s1);
@InheritConfiguration