mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#168 Allow to inherit mapping method configurations from the @MapperConfig-annotated type, either automatically when all method types match, or explicitly using @InheritConfiguration.
This commit is contained in:
parent
2c845480a0
commit
952ee8526d
@ -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}.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* Mapping methods defined in the annotated type can be used as <em>prototypes</em> 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.
|
||||
* </p>
|
||||
*
|
||||
* @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}.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* When given via {@link MapperConfig#mappingInheritanceStrategy()}, the strategy {@link #EXPLICIT} will be applied.
|
||||
*/
|
||||
DEFAULT;
|
||||
}
|
@ -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<String> handledTargets = new HashSet<String>();
|
||||
|
||||
for ( Map.Entry<String, List<Mapping>> entry : method.getMappings().entrySet() ) {
|
||||
for ( Map.Entry<String, List<Mapping>> entry : method.getMappingOptions().getMappings().entrySet() ) {
|
||||
for ( Mapping mapping : entry.getValue() ) {
|
||||
|
||||
PropertyMapping propertyMapping = null;
|
||||
|
@ -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<EnumMapping> enumMappings = new ArrayList<EnumMapping>();
|
||||
|
||||
List<String> sourceEnumConstants
|
||||
= method.getSourceParameters().iterator().next().getType().getEnumConstants();
|
||||
List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
|
||||
|
||||
for ( String enumConstant : sourceEnumConstants ) {
|
||||
List<Mapping> 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<String> sourceEnumConstants =
|
||||
method.getSourceParameters().iterator().next().getType().getEnumConstants();
|
||||
List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
|
||||
List<String> targetEnumConstants = method.getReturnType().getEnumConstants();
|
||||
|
||||
boolean foundIncorrectMapping = false;
|
||||
|
||||
for ( List<Mapping> mappedConstants : method.getMappings().values() ) {
|
||||
for ( List<Mapping> 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<String> sourceEnumConstants =
|
||||
method.getSourceParameters().iterator().next().getType().getEnumConstants();
|
||||
List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants();
|
||||
List<String> targetEnumConstants = method.getReturnType().getEnumConstants();
|
||||
List<String> unmappedSourceEnumConstants = new ArrayList<String>();
|
||||
|
||||
@ -182,6 +181,6 @@ public class EnumMappingMethod extends MappingMethod {
|
||||
}
|
||||
|
||||
public Parameter getSourceParameter() {
|
||||
return getParameters().iterator().next();
|
||||
return first( getParameters() );
|
||||
}
|
||||
}
|
||||
|
@ -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() :
|
||||
|
@ -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<Type> sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
|
||||
List<Type> sourceTypeParams = first( method.getSourceParameters() ).getType().getTypeParameters();
|
||||
List<Type> resultTypeParams = method.getResultType().getTypeParameters();
|
||||
|
||||
// find mapping method or conversion for key
|
||||
|
@ -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<String, List<Mapping>> 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 {" +
|
||||
|
@ -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<String, List<Mapping>> mappings;
|
||||
private IterableMapping iterableMapping;
|
||||
private MapMapping mapMapping;
|
||||
private boolean fullyInitialized;
|
||||
|
||||
public MappingOptions(Map<String, List<Mapping>> 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<String, List<Mapping>> getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public IterableMapping getIterableMapping() {
|
||||
return iterableMapping;
|
||||
}
|
||||
|
||||
public MapMapping getMapMapping() {
|
||||
return mapMapping;
|
||||
}
|
||||
|
||||
public void setMappings(Map<String, List<Mapping>> 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<String, List<Mapping>> newMappings = new HashMap<String, List<Mapping>>();
|
||||
|
||||
for ( List<Mapping> lmappings : inherited.getMappings().values() ) {
|
||||
for ( Mapping mapping : lmappings ) {
|
||||
if ( isInverse ) {
|
||||
mapping = mapping.reverse( method, messager, typeFactory );
|
||||
}
|
||||
|
||||
if ( mapping != null ) {
|
||||
List<Mapping> mappingsOfProperty = newMappings.get( mapping.getTargetName() );
|
||||
if ( mappingsOfProperty == null ) {
|
||||
mappingsOfProperty = new ArrayList<Mapping>();
|
||||
newMappings.put( mapping.getTargetName(), mappingsOfProperty );
|
||||
}
|
||||
|
||||
mappingsOfProperty.add( mapping.copyForInheritanceTo( method ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now add all of its own mappings
|
||||
newMappings.putAll( getMappings() );
|
||||
setMappings( newMappings );
|
||||
}
|
||||
|
||||
markAsFullyInitialized();
|
||||
}
|
||||
}
|
@ -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<Type> exceptionTypes;
|
||||
private final MapperConfig config;
|
||||
private final MappingOptions mappingOptions;
|
||||
private final List<SourceMethod> prototypeMethods;
|
||||
|
||||
private Map<String, List<Mapping>> mappings;
|
||||
private IterableMapping iterableMapping;
|
||||
private MapMapping mapMapping;
|
||||
private List<Parameter> sourceParameters;
|
||||
|
||||
private List<String> parameterNames;
|
||||
|
||||
private List<SourceMethod> 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<SourceMethod> prototypeMethods = Collections.emptyList();
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
@ -144,24 +152,27 @@ public class SourceMethod implements Method {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SourceMethod createSourceMethod() {
|
||||
public Builder setPrototypeMethods(List<SourceMethod> 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<String, List<Mapping>> entry : sourceMethod.getMappings().entrySet() ) {
|
||||
for ( Map.Entry<String, List<Mapping>> 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<Parameter> parameters,
|
||||
Type returnType, List<Type> exceptionTypes, Map<String, List<Mapping>> mappings,
|
||||
IterableMapping iterableMapping, MapMapping mapMapping, Types typeUtils,
|
||||
TypeFactory typeFactory, FormattingMessager messager, MapperConfig config) {
|
||||
Type returnType, List<Type> exceptionTypes, MappingOptions mappingOptions, Types typeUtils,
|
||||
TypeFactory typeFactory, MapperConfig config, List<SourceMethod> 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<Parameter> parameters) {
|
||||
@ -249,11 +258,13 @@ public class SourceMethod implements Method {
|
||||
*/
|
||||
@Override
|
||||
public List<Parameter> getSourceParameters() {
|
||||
List<Parameter> sourceParameters = new ArrayList<Parameter>();
|
||||
if ( sourceParameters == null ) {
|
||||
sourceParameters = new ArrayList<Parameter>();
|
||||
|
||||
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<String> getParameterNames() {
|
||||
List<String> parameterNames = new ArrayList<String>( parameters.size() );
|
||||
if ( parameterNames == null ) {
|
||||
parameterNames = new ArrayList<String>( 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<String, List<Mapping>> getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public Mapping getSingleMappingByTargetPropertyName(String targetPropertyName) {
|
||||
List<Mapping> all = mappings.get( targetPropertyName );
|
||||
return all != null ? all.iterator().next() : null;
|
||||
}
|
||||
|
||||
public void setMappings(Map<String, List<Mapping>> 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<Mapping> 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<Type, Integer> test = new HashMap<Type, Integer>();
|
||||
|
||||
// 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<Mapping> getMappingBySourcePropertyName(String sourcePropertyName) {
|
||||
List<Mapping> mappingsOfSourceProperty = new ArrayList<Mapping>();
|
||||
|
||||
for ( List<Mapping> mappingOfProperty : mappings.values() ) {
|
||||
for ( List<Mapping> 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<SourceMethod> getApplicablePrototypeMethods() {
|
||||
if ( applicablePrototypeMethods == null ) {
|
||||
applicablePrototypeMethods = new ArrayList<SourceMethod>();
|
||||
|
||||
for ( SourceMethod prototype : prototypeMethods ) {
|
||||
if ( canInheritFrom( prototype ) ) {
|
||||
applicablePrototypeMethods.add( prototype );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return applicablePrototypeMethods;
|
||||
}
|
||||
|
||||
private static boolean allParametersAreAssignable(List<Parameter> fromParams, List<Parameter> toParams) {
|
||||
if ( fromParams.size() == toParams.size() ) {
|
||||
Set<Parameter> unaccountedToParams = new HashSet<Parameter>( 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<String, List<Mapping>> newMappings = new HashMap<String, List<Mapping>>();
|
||||
|
||||
if ( inverseMethod != null && !inverseMethod.getMappings().isEmpty() ) {
|
||||
for ( List<Mapping> lmappings : inverseMethod.getMappings().values() ) {
|
||||
for ( Mapping inverseMapping : lmappings ) {
|
||||
Mapping reversed = inverseMapping.reverse( this, messager, typeFactory );
|
||||
if ( reversed != null ) {
|
||||
List<Mapping> mappingsOfProperty = newMappings.get( reversed.getTargetName() );
|
||||
if ( mappingsOfProperty == null ) {
|
||||
mappingsOfProperty = new ArrayList<Mapping>();
|
||||
newMappings.put( reversed.getTargetName(), mappingsOfProperty );
|
||||
}
|
||||
mappingsOfProperty.add( reversed );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( templateMethod != null && !templateMethod.getMappings().isEmpty() ) {
|
||||
for ( List<Mapping> lmappings : templateMethod.getMappings().values() ) {
|
||||
for ( Mapping templateMapping : lmappings ) {
|
||||
if ( templateMapping != null ) {
|
||||
List<Mapping> mappingsOfProperty = newMappings.get( templateMapping.getTargetName() );
|
||||
if ( mappingsOfProperty == null ) {
|
||||
mappingsOfProperty = new ArrayList<Mapping>();
|
||||
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<Type, Integer> test) {
|
||||
Integer count = test.get( key );
|
||||
count++;
|
||||
test.put( key, count );
|
||||
}
|
||||
|
||||
private void decrease(Type key, Map<Type, Integer> test) {
|
||||
Integer count = test.get( key );
|
||||
count--;
|
||||
test.put( key, count );
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return the mapper config when this method needs to be implemented
|
||||
|
@ -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.
|
||||
* <p>
|
||||
@ -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<Parameter> replacementParamCandidates = new ArrayList<Parameter>();
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
}
|
@ -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<List<Sourc
|
||||
this.versionInformation = context.getVersionInformation();
|
||||
this.typeFactory = context.getTypeFactory();
|
||||
|
||||
List<MapperReference> mapperReferences = initReferencedMappers( mapperTypeElement );
|
||||
MapperConfig mapperConfig = MapperConfig.getInstanceOn( mapperTypeElement );
|
||||
List<MapperReference> mapperReferences = initReferencedMappers( mapperTypeElement, mapperConfig );
|
||||
|
||||
MappingBuilderContext ctx = new MappingBuilderContext(
|
||||
typeFactory,
|
||||
@ -104,7 +110,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
mapperReferences
|
||||
);
|
||||
this.mappingContext = ctx;
|
||||
return getMapper( mapperTypeElement, sourceModel );
|
||||
return getMapper( mapperTypeElement, mapperConfig, sourceModel );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -112,13 +118,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
return 1000;
|
||||
}
|
||||
|
||||
private List<MapperReference> initReferencedMappers(TypeElement element) {
|
||||
private List<MapperReference> initReferencedMappers(TypeElement element, MapperConfig mapperConfig) {
|
||||
List<MapperReference> result = new LinkedList<MapperReference>();
|
||||
List<String> variableNames = new LinkedList<String>();
|
||||
|
||||
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<List<Sourc
|
||||
return result;
|
||||
}
|
||||
|
||||
private Mapper getMapper(TypeElement element, List<SourceMethod> methods) {
|
||||
private Mapper getMapper(TypeElement element, MapperConfig mapperConfig, List<SourceMethod> methods) {
|
||||
List<MapperReference> mapperReferences = mappingContext.getMapperReferences();
|
||||
List<MappingMethod> mappingMethods = getMappingMethods( methods );
|
||||
List<MappingMethod> mappingMethods = getMappingMethods( mapperConfig, methods );
|
||||
mappingMethods.addAll( mappingContext.getUsedVirtualMappings() );
|
||||
mappingMethods.addAll( mappingContext.getMappingsToGenerate() );
|
||||
|
||||
@ -194,7 +198,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
else if ( constructor.getParameters().size() == 1 ) {
|
||||
if ( typeUtils.isAssignable(
|
||||
element.asType(),
|
||||
constructor.getParameters().iterator().next().asType()
|
||||
first( constructor.getParameters() ).asType()
|
||||
) ) {
|
||||
hasDelegateConstructor = true;
|
||||
}
|
||||
@ -233,7 +237,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
return extraImports;
|
||||
}
|
||||
|
||||
private List<MappingMethod> getMappingMethods(List<SourceMethod> methods) {
|
||||
private List<MappingMethod> getMappingMethods(MapperConfig mapperConfig, List<SourceMethod> methods) {
|
||||
List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>();
|
||||
|
||||
for ( SourceMethod method : methods ) {
|
||||
@ -241,29 +245,23 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
continue;
|
||||
}
|
||||
|
||||
SourceMethod inverseMappingMethod = getInverseMappingMethod( methods, method );
|
||||
SourceMethod templateMappingMethod = getTemplateMappingMethod( methods, method );
|
||||
mergeInheritedOptions( method, mapperConfig, methods, new ArrayList<SourceMethod>() );
|
||||
|
||||
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<TypeMirror> 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<List<Sourc
|
||||
|
||||
MapMappingMethod.Builder builder = new MapMappingMethod.Builder();
|
||||
|
||||
if ( method.getMapMapping() == null ) {
|
||||
if ( inverseMappingMethod != null && inverseMappingMethod.getMapMapping() != null ) {
|
||||
method.setMapMapping( inverseMappingMethod.getMapMapping() );
|
||||
}
|
||||
else if ( templateMappingMethod != null && templateMappingMethod.getMapMapping() != null ) {
|
||||
method.setMapMapping( templateMappingMethod.getMapMapping() );
|
||||
}
|
||||
}
|
||||
String keyDateFormat = null;
|
||||
String valueDateFormat = null;
|
||||
List<TypeMirror> keyQualifiers = null;
|
||||
List<TypeMirror> 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<List<Sourc
|
||||
else if ( method.isEnumMapping() ) {
|
||||
|
||||
EnumMappingMethod.Builder builder = new EnumMappingMethod.Builder();
|
||||
method.mergeWithInverseMappings( inverseMappingMethod, templateMappingMethod );
|
||||
MappingMethod enumMappingMethod = builder
|
||||
.mappingContext( mappingContext )
|
||||
.souceMethod( method )
|
||||
@ -334,7 +323,6 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
else {
|
||||
|
||||
BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder();
|
||||
method.mergeWithInverseMappings( inverseMappingMethod, templateMappingMethod );
|
||||
BeanMappingMethod beanMappingMethod = builder
|
||||
.mappingContext( mappingContext )
|
||||
.souceMethod( method )
|
||||
@ -356,6 +344,61 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
return mappingMethods;
|
||||
}
|
||||
|
||||
private void mergeInheritedOptions(SourceMethod method, MapperConfig mapperConfig,
|
||||
List<SourceMethod> availableMethods, List<SourceMethod> 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<SourceMethod> 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<List<Sourc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuring inverse method in case the given method is annotated with
|
||||
* Returns the configuring inverse method's options in case the given method is annotated with
|
||||
* {@code @InheritInverseConfiguration} and exactly one such configuring method can unambiguously be selected (as
|
||||
* per the source/target type and optionally the name given via {@code @InheritInverseConfiguration}).
|
||||
*/
|
||||
private SourceMethod getInverseMappingMethod(List<SourceMethod> rawMethods, SourceMethod method) {
|
||||
SourceMethod result = null;
|
||||
private MappingOptions getInverseMappingOptions(List<SourceMethod> rawMethods, SourceMethod method,
|
||||
List<SourceMethod> initializingMethods, MapperConfig mapperConfig) {
|
||||
SourceMethod resultMethod = null;
|
||||
InheritInverseConfigurationPrism reversePrism = InheritInverseConfigurationPrism.getInstanceOn(
|
||||
method.getExecutable()
|
||||
);
|
||||
@ -389,10 +433,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
if ( candidates.size() == 1 ) {
|
||||
// no ambiguity: if no configuredBy is specified, or configuredBy specified and match
|
||||
if ( name.isEmpty() ) {
|
||||
result = candidates.get( 0 );
|
||||
resultMethod = candidates.get( 0 );
|
||||
}
|
||||
else if ( candidates.get( 0 ).getName().equals( name ) ) {
|
||||
result = candidates.get( 0 );
|
||||
resultMethod = candidates.get( 0 );
|
||||
}
|
||||
else {
|
||||
reportErrorWhenNonMatchingName( candidates.get( 0 ), method, reversePrism );
|
||||
@ -409,35 +453,46 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
}
|
||||
|
||||
if ( nameFilteredcandidates.size() == 1 ) {
|
||||
result = nameFilteredcandidates.get( 0 );
|
||||
resultMethod = nameFilteredcandidates.get( 0 );
|
||||
}
|
||||
else if ( nameFilteredcandidates.size() > 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<SourceMethod> rawMethods,
|
||||
MapperConfig mapperConfig,
|
||||
List<SourceMethod> 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<SourceMethod> rawMethods, SourceMethod method) {
|
||||
SourceMethod result = null;
|
||||
private MappingOptions getTemplateMappingOptions(List<SourceMethod> rawMethods, SourceMethod method,
|
||||
List<SourceMethod> initializingMethods,
|
||||
MapperConfig mapperConfig) {
|
||||
SourceMethod resultMethod = null;
|
||||
InheritConfigurationPrism forwardPrism = InheritConfigurationPrism.getInstanceOn(
|
||||
method.getExecutable()
|
||||
);
|
||||
@ -445,11 +500,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
if (forwardPrism != null) {
|
||||
reportErrorWhenInheritForwardAlsoHasInheritReverseMapping( method );
|
||||
|
||||
// method is configured as being reverse method, collect candidates
|
||||
List<SourceMethod> candidates = new ArrayList<SourceMethod>();
|
||||
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<List<Sourc
|
||||
String name = forwardPrism.name();
|
||||
if ( candidates.size() == 1 ) {
|
||||
// no ambiguity: if no configuredBy is specified, or configuredBy specified and match
|
||||
SourceMethod sourceMethod = first( candidates );
|
||||
if ( name.isEmpty() ) {
|
||||
result = candidates.get( 0 );
|
||||
resultMethod = sourceMethod;
|
||||
}
|
||||
else if ( candidates.get( 0 ).getName().equals( name ) ) {
|
||||
result = candidates.get( 0 );
|
||||
else if ( sourceMethod.getName().equals( name ) ) {
|
||||
resultMethod = sourceMethod;
|
||||
}
|
||||
else {
|
||||
reportErrorWhenNonMatchingName( candidates.get( 0 ), method, forwardPrism );
|
||||
reportErrorWhenNonMatchingName( sourceMethod, method, forwardPrism );
|
||||
}
|
||||
}
|
||||
else if ( candidates.size() > 1 ) {
|
||||
@ -478,23 +533,19 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
}
|
||||
|
||||
if ( nameFilteredcandidates.size() == 1 ) {
|
||||
result = nameFilteredcandidates.get( 0 );
|
||||
resultMethod = first( nameFilteredcandidates );
|
||||
}
|
||||
else if ( nameFilteredcandidates.size() > 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<List<Sourc
|
||||
|
||||
}
|
||||
|
||||
private void reportErrorIfInverseMethodHasInheritAnnotation(SourceMethod candidate,
|
||||
SourceMethod method,
|
||||
InheritInverseConfigurationPrism reversePrism) {
|
||||
|
||||
InheritInverseConfigurationPrism candidatePrism = InheritInverseConfigurationPrism.getInstanceOn(
|
||||
candidate.getExecutable()
|
||||
);
|
||||
|
||||
if ( candidatePrism != null ) {
|
||||
messager.printMessage( method.getExecutable(),
|
||||
reversePrism.mirror,
|
||||
Message.INHERITINVERSECONFIGURATION_REFERENCE_HAS_INVERSE,
|
||||
candidate.getName()
|
||||
);
|
||||
}
|
||||
|
||||
InheritConfigurationPrism candidateTemplatePrism = InheritConfigurationPrism.getInstanceOn(
|
||||
candidate.getExecutable()
|
||||
);
|
||||
if ( candidateTemplatePrism != null ) {
|
||||
messager.printMessage( method.getExecutable(),
|
||||
reversePrism.mirror,
|
||||
Message.INHERITINVERSECONFIGURATION_REFERENCE_HAS_FORWARD,
|
||||
candidate.getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportErrorWhenAmbigousReverseMapping(List<SourceMethod> candidates, SourceMethod method,
|
||||
InheritInverseConfigurationPrism reversePrism) {
|
||||
|
||||
@ -570,7 +593,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
reversePrism.mirror,
|
||||
Message.INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES,
|
||||
reversePrism.name(),
|
||||
Strings.join( candidates, "(), " )
|
||||
Strings.join( candidates, ", " )
|
||||
|
||||
);
|
||||
}
|
||||
@ -586,35 +609,6 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
);
|
||||
}
|
||||
|
||||
private void reportErrorIfMethodHasAnnotation(SourceMethod candidate,
|
||||
SourceMethod method,
|
||||
InheritConfigurationPrism prism) {
|
||||
|
||||
InheritConfigurationPrism candidatePrism = InheritConfigurationPrism.getInstanceOn(
|
||||
candidate.getExecutable()
|
||||
);
|
||||
|
||||
if ( candidatePrism != null ) {
|
||||
messager.printMessage( method.getExecutable(),
|
||||
prism.mirror,
|
||||
Message.INHERITCONFIGURATION_REFERENCE_HAS_FORWARD,
|
||||
candidate.getName()
|
||||
);
|
||||
}
|
||||
|
||||
InheritInverseConfigurationPrism candidateInversePrism = InheritInverseConfigurationPrism.getInstanceOn(
|
||||
candidate.getExecutable()
|
||||
);
|
||||
|
||||
if ( candidateInversePrism != null ) {
|
||||
messager.printMessage( method.getExecutable(),
|
||||
prism.mirror,
|
||||
Message.INHERITCONFIGURATION_REFERENCE_HAS_INVERSE,
|
||||
candidate.getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportErrorWhenAmbigousMapping(List<SourceMethod> candidates, SourceMethod method,
|
||||
InheritConfigurationPrism prism) {
|
||||
|
||||
|
@ -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<Void, Lis
|
||||
this.typeFactory = context.getTypeFactory();
|
||||
this.typeUtils = context.getTypeUtils();
|
||||
this.elementUtils = context.getElementUtils();
|
||||
return retrieveMethods( mapperTypeElement, mapperTypeElement );
|
||||
|
||||
MapperConfig mapperConfig = MapperConfig.getInstanceOn( mapperTypeElement );
|
||||
|
||||
if ( !mapperConfig.isValid() ) {
|
||||
throw new AnnotationProcessingException(
|
||||
"Couldn't retrieve @Mapper annotation",
|
||||
mapperTypeElement,
|
||||
mapperConfig.getAnnotationMirror() );
|
||||
}
|
||||
|
||||
List<SourceMethod> prototypeMethods =
|
||||
retrievePrototypeMethods( mapperConfig.getMapperConfigMirror(), mapperConfig );
|
||||
return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperConfig, prototypeMethods );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -80,19 +93,60 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
return 1;
|
||||
}
|
||||
|
||||
private List<SourceMethod> retrievePrototypeMethods(TypeMirror typeMirror, MapperConfig mapperConfig) {
|
||||
if ( typeMirror == null || typeMirror.getKind() == TypeKind.VOID ) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
TypeElement typeElement = asTypeElement( typeMirror );
|
||||
List<SourceMethod> methods = new ArrayList<SourceMethod>();
|
||||
for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, typeElement ) ) {
|
||||
|
||||
ExecutableType methodType = typeFactory.getMethodType( typeElement, executable );
|
||||
List<Parameter> parameters = typeFactory.getParameters( methodType, executable );
|
||||
boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters );
|
||||
|
||||
// prototype methods don't have prototypes themselves
|
||||
List<SourceMethod> 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<SourceMethod> retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement) {
|
||||
private List<SourceMethod> retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement,
|
||||
MapperConfig mapperConfig, List<SourceMethod> prototypeMethods) {
|
||||
List<SourceMethod> methods = new ArrayList<SourceMethod>();
|
||||
|
||||
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<Void, Lis
|
||||
|
||||
//Add all methods of used mappers in order to reference them in the aggregated model
|
||||
if ( usedMapper.equals( mapperToImplement ) ) {
|
||||
MapperConfig mapperSettings = MapperConfig.getInstanceOn( usedMapper );
|
||||
if ( !mapperSettings.isValid() ) {
|
||||
throw new AnnotationProcessingException(
|
||||
"Couldn't retrieve @Mapper annotation", usedMapper, mapperSettings.getAnnotationMirror()
|
||||
);
|
||||
}
|
||||
|
||||
for ( TypeMirror mapper : mapperSettings.uses() ) {
|
||||
methods.addAll( retrieveMethods( asTypeElement( mapper ), mapperToImplement ) );
|
||||
for ( TypeMirror mapper : mapperConfig.uses() ) {
|
||||
methods.addAll( retrieveMethods(
|
||||
asTypeElement( mapper ),
|
||||
mapperToImplement,
|
||||
mapperConfig,
|
||||
prototypeMethods ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +172,9 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
|
||||
private SourceMethod getMethod(TypeElement usedMapper,
|
||||
ExecutableElement method,
|
||||
TypeElement mapperToImplement) {
|
||||
TypeElement mapperToImplement,
|
||||
MapperConfig mapperConfig,
|
||||
List<SourceMethod> prototypeMethods) {
|
||||
|
||||
ExecutableType methodType = typeFactory.getMethodType( usedMapper, method );
|
||||
List<Parameter> parameters = typeFactory.getParameters( methodType, method );
|
||||
@ -134,7 +187,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
method,
|
||||
parameters,
|
||||
containsTargetTypeParameter,
|
||||
mapperToImplement );
|
||||
mapperConfig,
|
||||
prototypeMethods );
|
||||
}
|
||||
//otherwise add reference to existing mapper method
|
||||
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters ) ) {
|
||||
@ -148,7 +202,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, ExecutableElement method,
|
||||
List<Parameter> parameters,
|
||||
boolean containsTargetTypeParameter,
|
||||
TypeElement mapperToImplement) {
|
||||
MapperConfig mapperConfig,
|
||||
List<SourceMethod> prototypeMethods) {
|
||||
Type returnType = typeFactory.getReturnType( methodType );
|
||||
List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType );
|
||||
List<Parameter> sourceParameters = extractSourceParameters( parameters );
|
||||
@ -181,8 +236,9 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
.setTypeUtils( typeUtils )
|
||||
.setMessager( messager )
|
||||
.setTypeFactory( typeFactory )
|
||||
.setMapperConfig( MapperConfig.getInstanceOn( mapperToImplement ) )
|
||||
.createSourceMethod();
|
||||
.setMapperConfig( mapperConfig )
|
||||
.setPrototypeMethods( prototypeMethods )
|
||||
.buildSourceMethod();
|
||||
}
|
||||
|
||||
private SourceMethod getReferencedMethod(TypeElement usedMapper, ExecutableType methodType,
|
||||
@ -205,7 +261,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
.setExceptionTypes( exceptionTypes )
|
||||
.setTypeUtils( typeUtils )
|
||||
.setTypeFactory( typeFactory )
|
||||
.createSourceMethod();
|
||||
.buildSourceMethod();
|
||||
}
|
||||
|
||||
private boolean isValidReferencedMethod(List<Parameter> parameters) {
|
||||
|
@ -72,4 +72,17 @@ public class Collections {
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
public static <T> T first(Collection<T> collection) {
|
||||
return collection.iterator().next();
|
||||
}
|
||||
|
||||
public static <T> List<T> join(List<T> a, List<T> b) {
|
||||
List<T> result = new ArrayList<T>( a.size() + b.size() );
|
||||
|
||||
result.addAll( a );
|
||||
result.addAll( b );
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
@ -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<String> namesOf(Enum<?>[] values) {
|
||||
List<String> names = new ArrayList<String>( values.length );
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user