mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#31 Providing support for bean mapping methods with several source parameters
This commit is contained in:
parent
b61f0ce915
commit
86a8e72692
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright 2012-2013 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;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
/**
|
||||
* Indicates an error during annotation processing.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AnnotationProcessingException extends RuntimeException {
|
||||
|
||||
private final Element element;
|
||||
private final AnnotationMirror annotationMirror;
|
||||
private final AnnotationValue annotationValue;
|
||||
|
||||
public AnnotationProcessingException(String message, Element element) {
|
||||
this( message, element, null, null );
|
||||
}
|
||||
|
||||
public AnnotationProcessingException(String message, Element element, AnnotationMirror annotationMirror) {
|
||||
this( message, element, annotationMirror, null );
|
||||
}
|
||||
|
||||
public AnnotationProcessingException(String message, Element element, AnnotationMirror annotationMirror,
|
||||
AnnotationValue annotationValue) {
|
||||
super( message );
|
||||
this.element = element;
|
||||
this.annotationMirror = annotationMirror;
|
||||
this.annotationValue = annotationValue;
|
||||
}
|
||||
|
||||
public Element getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
public AnnotationMirror getAnnotationMirror() {
|
||||
return annotationMirror;
|
||||
}
|
||||
|
||||
public AnnotationValue getAnnotationValue() {
|
||||
return annotationValue;
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.util.ElementKindVisitor6;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
|
||||
import net.java.dev.hickory.prism.GeneratePrism;
|
||||
import net.java.dev.hickory.prism.GeneratePrisms;
|
||||
@ -152,8 +153,21 @@ public class MappingProcessor extends AbstractProcessor {
|
||||
Object model = null;
|
||||
|
||||
for ( ModelElementProcessor<?, ?> processor : getProcessors() ) {
|
||||
try {
|
||||
model = process( context, processor, mapperTypeElement, model );
|
||||
}
|
||||
catch ( AnnotationProcessingException e ) {
|
||||
processingEnv.getMessager()
|
||||
.printMessage(
|
||||
Kind.ERROR,
|
||||
e.getMessage(),
|
||||
e.getElement(),
|
||||
e.getAnnotationMirror(),
|
||||
e.getAnnotationValue()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <P, R> R process(ProcessorContext context, ModelElementProcessor<P, R> processor,
|
||||
|
@ -18,7 +18,10 @@
|
||||
*/
|
||||
package org.mapstruct.ap.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.model.source.Method;
|
||||
@ -43,6 +46,22 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
return propertyMappings;
|
||||
}
|
||||
|
||||
public Map<String, List<PropertyMapping>> getPropertyMappingsByParameter() {
|
||||
Map<String, List<PropertyMapping>> mappingsByParameter = new HashMap<String, List<PropertyMapping>>();
|
||||
|
||||
for ( Parameter sourceParameter : getSourceParameters() ) {
|
||||
ArrayList<PropertyMapping> mappingsOfParameter = new ArrayList<PropertyMapping>();
|
||||
mappingsByParameter.put( sourceParameter.getName(), mappingsOfParameter );
|
||||
for ( PropertyMapping mapping : propertyMappings ) {
|
||||
if ( mapping.getSourceBeanName().equals( sourceParameter.getName() ) ) {
|
||||
mappingsOfParameter.add( mapping );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mappingsByParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> getImportTypes() {
|
||||
Set<Type> types = super.getImportTypes();
|
||||
|
@ -42,6 +42,16 @@ public class IterableMappingMethod extends MappingMethod {
|
||||
this.conversion = conversion;
|
||||
}
|
||||
|
||||
public Parameter getSourceParameter() {
|
||||
for ( Parameter parameter : getParameters() ) {
|
||||
if ( !parameter.isMappingTarget() ) {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException( "Method " + this + " has no source parameter." );
|
||||
}
|
||||
|
||||
public MappingMethodReference getElementMappingMethod() {
|
||||
return elementMappingMethod;
|
||||
}
|
||||
@ -64,7 +74,7 @@ public class IterableMappingMethod extends MappingMethod {
|
||||
public String getLoopVariableName() {
|
||||
return Strings.getSaveVariableName(
|
||||
Introspector.decapitalize(
|
||||
getSingleSourceParameter().getType()
|
||||
getSourceParameter().getType()
|
||||
.getTypeParameters()
|
||||
.get( 0 )
|
||||
.getName()
|
||||
|
@ -46,6 +46,16 @@ public class MapMappingMethod extends MappingMethod {
|
||||
this.valueConversion = valueConversion;
|
||||
}
|
||||
|
||||
public Parameter getSourceParameter() {
|
||||
for ( Parameter parameter : getParameters() ) {
|
||||
if ( !parameter.isMappingTarget() ) {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException( "Method " + this + " has no source parameter." );
|
||||
}
|
||||
|
||||
public MappingMethodReference getKeyMappingMethod() {
|
||||
return keyMappingMethod;
|
||||
}
|
||||
|
@ -37,14 +37,12 @@ public abstract class MappingMethod extends AbstractModelElement {
|
||||
private final String name;
|
||||
private final List<Parameter> parameters;
|
||||
private final Type returnType;
|
||||
private final Parameter singleSourceParameter;
|
||||
private final Parameter targetParameter;
|
||||
|
||||
public MappingMethod(Method method) {
|
||||
this.name = method.getName();
|
||||
this.parameters = method.getParameters();
|
||||
this.returnType = method.getReturnType();
|
||||
this.singleSourceParameter = method.getSingleSourceParameter();
|
||||
this.targetParameter = method.getTargetParameter();
|
||||
}
|
||||
|
||||
@ -56,8 +54,16 @@ public abstract class MappingMethod extends AbstractModelElement {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public Parameter getSingleSourceParameter() {
|
||||
return singleSourceParameter;
|
||||
public List<Parameter> getSourceParameters() {
|
||||
List<Parameter> sourceParameters = new ArrayList<Parameter>();
|
||||
|
||||
for ( Parameter parameter : parameters ) {
|
||||
if ( !parameter.isMappingTarget() ) {
|
||||
sourceParameters.add( parameter );
|
||||
}
|
||||
}
|
||||
|
||||
return sourceParameters;
|
||||
}
|
||||
|
||||
public Type getResultType() {
|
||||
|
@ -31,6 +31,7 @@ import java.util.Set;
|
||||
*/
|
||||
public class PropertyMapping extends AbstractModelElement {
|
||||
|
||||
private final String sourceBeanName;
|
||||
private final String sourceName;
|
||||
private final String sourceAccessorName;
|
||||
private final Type sourceType;
|
||||
@ -42,10 +43,11 @@ public class PropertyMapping extends AbstractModelElement {
|
||||
private final MappingMethodReference mappingMethod;
|
||||
private final TypeConversion conversion;
|
||||
|
||||
public PropertyMapping(String sourceName, String sourceAccessorName, Type sourceType, String targetName,
|
||||
String targetAccessorName, Type targetType, MappingMethodReference mappingMethod,
|
||||
TypeConversion conversion) {
|
||||
public PropertyMapping(String sourceBeanName, String sourceName, String sourceAccessorName, Type sourceType,
|
||||
String targetName, String targetAccessorName, Type targetType,
|
||||
MappingMethodReference mappingMethod, TypeConversion conversion) {
|
||||
|
||||
this.sourceBeanName = sourceBeanName;
|
||||
this.sourceName = sourceName;
|
||||
this.sourceAccessorName = sourceAccessorName;
|
||||
this.sourceType = sourceType;
|
||||
@ -58,6 +60,10 @@ public class PropertyMapping extends AbstractModelElement {
|
||||
this.conversion = conversion;
|
||||
}
|
||||
|
||||
public String getSourceBeanName() {
|
||||
return sourceBeanName;
|
||||
}
|
||||
|
||||
public String getSourceName() {
|
||||
return sourceName;
|
||||
}
|
||||
|
@ -22,7 +22,9 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import org.mapstruct.ap.AnnotationProcessingException;
|
||||
import org.mapstruct.ap.MappingPrism;
|
||||
import org.mapstruct.ap.MappingsPrism;
|
||||
|
||||
@ -34,25 +36,36 @@ import org.mapstruct.ap.MappingsPrism;
|
||||
public class Mapping {
|
||||
|
||||
private final String sourceName;
|
||||
private final String sourceParameterName;
|
||||
private final String sourcePropertyName;
|
||||
private final String targetName;
|
||||
private final String dateFormat;
|
||||
private final AnnotationMirror mirror;
|
||||
private final AnnotationValue sourceAnnotationValue;
|
||||
private final AnnotationValue targetAnnotationValue;
|
||||
|
||||
public static Map<String, Mapping> fromMappingsPrism(MappingsPrism mappingsAnnotation) {
|
||||
public static Map<String, Mapping> fromMappingsPrism(MappingsPrism mappingsAnnotation, Element element) {
|
||||
Map<String, Mapping> mappings = new HashMap<String, Mapping>();
|
||||
|
||||
for ( MappingPrism mapping : mappingsAnnotation.value() ) {
|
||||
mappings.put( mapping.source(), fromMappingPrism( mapping ) );
|
||||
mappings.put( mapping.source(), fromMappingPrism( mapping, element ) );
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public static Mapping fromMappingPrism(MappingPrism mapping) {
|
||||
public static Mapping fromMappingPrism(MappingPrism mapping, Element element) {
|
||||
String[] sourceNameParts = getSourceNameParts(
|
||||
mapping.source(),
|
||||
element,
|
||||
mapping.mirror,
|
||||
mapping.values.source()
|
||||
);
|
||||
|
||||
return new Mapping(
|
||||
mapping.source(),
|
||||
sourceNameParts != null ? sourceNameParts[0] : null,
|
||||
sourceNameParts != null ? sourceNameParts[1] : mapping.source(),
|
||||
mapping.target(),
|
||||
mapping.dateFormat(),
|
||||
mapping.mirror,
|
||||
@ -61,9 +74,31 @@ public class Mapping {
|
||||
);
|
||||
}
|
||||
|
||||
private Mapping(String sourceName, String targetName, String dateFormat, AnnotationMirror mirror,
|
||||
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) {
|
||||
private static String[] getSourceNameParts(String sourceName, Element element, AnnotationMirror annotationMirror,
|
||||
AnnotationValue annotationValue) {
|
||||
if ( !sourceName.contains( "." ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] parts = sourceName.split( "\\." );
|
||||
if ( parts.length != 2 ) {
|
||||
throw new AnnotationProcessingException(
|
||||
"Mapping of nested attributes not supported yet.",
|
||||
element,
|
||||
annotationMirror,
|
||||
annotationValue
|
||||
);
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
private Mapping(String sourceName, String sourceParameterName, String sourcePropertyName, String targetName,
|
||||
String dateFormat, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue,
|
||||
AnnotationValue targetAnnotationValue) {
|
||||
this.sourceName = sourceName;
|
||||
this.sourceParameterName = sourceParameterName;
|
||||
this.sourcePropertyName = sourcePropertyName;
|
||||
this.targetName = targetName.equals( "" ) ? sourceName : targetName;
|
||||
this.dateFormat = dateFormat;
|
||||
this.mirror = mirror;
|
||||
@ -71,10 +106,34 @@ public class Mapping {
|
||||
this.targetAnnotationValue = targetAnnotationValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete source name of this mapping, either a qualified (e.g. {@code parameter1.foo}) or
|
||||
* unqualified (e.g. {@code foo}) property reference.
|
||||
*
|
||||
* @return The complete source name of this mapping.
|
||||
*/
|
||||
public String getSourceName() {
|
||||
return sourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unqualified name of the source property (i.e. without the parameter name if given) of this mapping.
|
||||
*
|
||||
* @return The unqualified name of the source property of this mapping.
|
||||
*/
|
||||
public String getSourcePropertyName() {
|
||||
return sourcePropertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the source parameter of this mapping if the source name is qualified.
|
||||
*
|
||||
* @return The name of the source parameter of this mapping if given, {@code null} otherwise.
|
||||
*/
|
||||
public String getSourceParameterName() {
|
||||
return sourceParameterName;
|
||||
}
|
||||
|
||||
public String getTargetName() {
|
||||
return targetName;
|
||||
}
|
||||
@ -96,7 +155,16 @@ public class Mapping {
|
||||
}
|
||||
|
||||
public Mapping reverse() {
|
||||
return new Mapping( targetName, sourceName, dateFormat, mirror, sourceAnnotationValue, targetAnnotationValue );
|
||||
return new Mapping(
|
||||
targetName,
|
||||
null,
|
||||
targetName,
|
||||
sourceName,
|
||||
dateFormat,
|
||||
mirror,
|
||||
sourceAnnotationValue,
|
||||
targetAnnotationValue
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,7 +45,6 @@ public class Method {
|
||||
private IterableMapping iterableMapping;
|
||||
private MapMapping mapMapping;
|
||||
|
||||
private final Parameter singleSourceParameter;
|
||||
private final Parameter targetParameter;
|
||||
|
||||
public static Method forMethodRequiringImplementation(ExecutableElement executable, List<Parameter> parameters,
|
||||
@ -83,12 +82,10 @@ public class Method {
|
||||
this.executable = executable;
|
||||
this.parameters = parameters;
|
||||
this.returnType = returnType;
|
||||
|
||||
this.mappings = mappings;
|
||||
this.iterableMapping = iterableMapping;
|
||||
this.mapMapping = mapMapping;
|
||||
|
||||
this.singleSourceParameter = determineSingleSourceParameter();
|
||||
this.targetParameter = determineTargetParameter( parameters );
|
||||
}
|
||||
|
||||
@ -102,16 +99,6 @@ public class Method {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Parameter determineSingleSourceParameter() {
|
||||
for ( Parameter parameter : parameters ) {
|
||||
if ( !parameter.isMappingTarget() ) {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException( "Method " + this + " has no source parameter." );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapper type declaring this method if it is not declared by
|
||||
* the mapper interface currently processed but by another mapper imported
|
||||
@ -135,6 +122,18 @@ public class Method {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public List<Parameter> getSourceParameters() {
|
||||
List<Parameter> sourceParameters = new ArrayList<Parameter>();
|
||||
|
||||
for ( Parameter parameter : parameters ) {
|
||||
if ( !parameter.isMappingTarget() ) {
|
||||
sourceParameters.add( parameter );
|
||||
}
|
||||
}
|
||||
|
||||
return sourceParameters;
|
||||
}
|
||||
|
||||
public List<String> getParameterNames() {
|
||||
List<String> parameterNames = new ArrayList<String>( parameters.size() );
|
||||
|
||||
@ -179,12 +178,10 @@ public class Method {
|
||||
|
||||
public boolean reverses(Method method) {
|
||||
return
|
||||
equals( getSingleSourceParameter().getType(), method.getResultType() )
|
||||
&& equals( getResultType(), method.getSingleSourceParameter().getType() );
|
||||
}
|
||||
|
||||
public Parameter getSingleSourceParameter() {
|
||||
return singleSourceParameter;
|
||||
getSourceParameters().size() == 1 &&
|
||||
method.getSourceParameters().size() == 1 &&
|
||||
equals( getSourceParameters().iterator().next().getType(), method.getResultType() ) &&
|
||||
equals( getResultType(), method.getSourceParameters().iterator().next().getType() );
|
||||
}
|
||||
|
||||
public Parameter getTargetParameter() {
|
||||
@ -192,11 +189,13 @@ public class Method {
|
||||
}
|
||||
|
||||
public boolean isIterableMapping() {
|
||||
return getSingleSourceParameter().getType().isIterableType() && getResultType().isIterableType();
|
||||
return getSourceParameters().size() == 1 &&
|
||||
getSourceParameters().iterator().next().getType().isIterableType() && getResultType().isIterableType();
|
||||
}
|
||||
|
||||
public boolean isMapMapping() {
|
||||
return getSingleSourceParameter().getType().isMapType() && getResultType().isMapType();
|
||||
return getSourceParameters().size() == 1 && getSourceParameters().iterator().next().getType().isMapType() &&
|
||||
getResultType().isMapType();
|
||||
}
|
||||
|
||||
private boolean equals(Object o1, Object o2) {
|
||||
@ -207,4 +206,24 @@ public class Method {
|
||||
public String toString() {
|
||||
return returnType + " " + getName() + "(" + Strings.join( parameters, ", " ) + ")";
|
||||
}
|
||||
|
||||
public Mapping getMapping(String targetPropertyName) {
|
||||
for ( Mapping mapping : mappings.values() ) {
|
||||
if ( mapping.getTargetName().equals( targetPropertyName ) ) {
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Parameter getSourceParameter(String sourceParameterName) {
|
||||
for ( Parameter parameter : getSourceParameters() ) {
|
||||
if ( parameter.getName().equals( sourceParameterName ) ) {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
method.setMappings( reverse( reverseMappingMethod.getMappings() ) );
|
||||
}
|
||||
}
|
||||
mappingMethods.add( getBeanMappingMethod( methods, method, unmappedTargetPolicy ) );
|
||||
MappingMethod beanMappingMethod = getBeanMappingMethod( methods, method, unmappedTargetPolicy );
|
||||
if ( beanMappingMethod != null ) {
|
||||
mappingMethods.add( beanMappingMethod );
|
||||
}
|
||||
}
|
||||
}
|
||||
return mappingMethods;
|
||||
@ -210,59 +213,90 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
return reversed;
|
||||
}
|
||||
|
||||
private PropertyMapping getPropertyMapping(List<Method> methods, Method method, ExecutableElement setterMethod,
|
||||
Parameter parameter) {
|
||||
String targetPropertyName = executables.getPropertyName( setterMethod );
|
||||
Mapping mapping = method.getMapping( targetPropertyName );
|
||||
String dateFormat = mapping != null ? mapping.getDateFormat() : null;
|
||||
String sourcePropertyName = mapping != null ? mapping.getSourcePropertyName() : targetPropertyName;
|
||||
TypeElement parameterElement = elementUtils.getTypeElement( parameter.getType().getCanonicalName() );
|
||||
List<ExecutableElement> sourceGetters = Filters.getterMethodsIn(
|
||||
elementUtils.getAllMembers( parameterElement )
|
||||
);
|
||||
|
||||
for ( ExecutableElement getter : sourceGetters ) {
|
||||
Mapping sourceMapping = method.getMappings().get( sourcePropertyName );
|
||||
boolean mapsToOtherTarget =
|
||||
sourceMapping != null && !sourceMapping.getTargetName().equals( targetPropertyName );
|
||||
if ( executables.getPropertyName( getter ).equals( sourcePropertyName ) && !mapsToOtherTarget ) {
|
||||
return getPropertyMapping(
|
||||
methods,
|
||||
method,
|
||||
parameter,
|
||||
getter,
|
||||
setterMethod,
|
||||
dateFormat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MappingMethod getBeanMappingMethod(List<Method> methods, Method method,
|
||||
ReportingPolicy unmappedTargetPolicy) {
|
||||
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
||||
Set<String> mappedTargetProperties = new HashSet<String>();
|
||||
|
||||
Map<String, Mapping> mappings = method.getMappings();
|
||||
if ( !reportErrorIfMappedPropertiesDontExist( method ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeElement resultTypeElement = elementUtils.getTypeElement( method.getResultType().getCanonicalName() );
|
||||
TypeElement parameterElement = elementUtils.getTypeElement(
|
||||
method.getSingleSourceParameter()
|
||||
.getType()
|
||||
.getCanonicalName()
|
||||
);
|
||||
|
||||
List<ExecutableElement> sourceGetters = Filters.getterMethodsIn(
|
||||
elementUtils.getAllMembers( parameterElement )
|
||||
);
|
||||
List<ExecutableElement> targetSetters = Filters.setterMethodsIn(
|
||||
elementUtils.getAllMembers( resultTypeElement )
|
||||
);
|
||||
|
||||
Set<String> sourceProperties = executables.getPropertyNames(
|
||||
Filters.getterMethodsIn( sourceGetters )
|
||||
);
|
||||
Set<String> targetProperties = executables.getPropertyNames(
|
||||
Filters.setterMethodsIn( targetSetters )
|
||||
);
|
||||
|
||||
reportErrorIfMappedPropertiesDontExist( method, sourceProperties, targetProperties );
|
||||
|
||||
for ( ExecutableElement getterMethod : sourceGetters ) {
|
||||
String sourcePropertyName = executables.getPropertyName( getterMethod );
|
||||
Mapping mapping = mappings.get( sourcePropertyName );
|
||||
String dateFormat = mapping != null ? mapping.getDateFormat() : null;
|
||||
|
||||
for ( ExecutableElement setterMethod : targetSetters ) {
|
||||
String targetPropertyName = executables.getPropertyName( setterMethod );
|
||||
|
||||
if ( targetPropertyName.equals( mapping != null ? mapping.getTargetName() : sourcePropertyName ) ) {
|
||||
PropertyMapping property = getPropertyMapping(
|
||||
Mapping mapping = method.getMapping( targetPropertyName );
|
||||
|
||||
PropertyMapping propertyMapping = null;
|
||||
if ( mapping != null && mapping.getSourceParameterName() != null ) {
|
||||
Parameter parameter = method.getSourceParameter( mapping.getSourceParameterName() );
|
||||
propertyMapping = getPropertyMapping( methods, method, setterMethod, parameter );
|
||||
}
|
||||
|
||||
if ( propertyMapping == null ) {
|
||||
for ( Parameter sourceParameter : method.getSourceParameters() ) {
|
||||
PropertyMapping newPropertyMapping = getPropertyMapping(
|
||||
methods,
|
||||
method,
|
||||
getterMethod,
|
||||
setterMethod,
|
||||
dateFormat
|
||||
sourceParameter
|
||||
);
|
||||
if ( propertyMapping != null && newPropertyMapping != null ) {
|
||||
messager.printMessage(
|
||||
Kind.ERROR,
|
||||
"Several possible source properties for target property \"" + targetPropertyName + "\".",
|
||||
method.getExecutable()
|
||||
);
|
||||
break;
|
||||
}
|
||||
else if ( newPropertyMapping != null ) {
|
||||
propertyMapping = newPropertyMapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
propertyMappings.add( property );
|
||||
|
||||
if ( propertyMapping != null ) {
|
||||
propertyMappings.add( propertyMapping );
|
||||
mappedTargetProperties.add( targetPropertyName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> targetProperties = executables.getPropertyNames( targetSetters );
|
||||
|
||||
reportErrorForUnmappedTargetPropertiesIfRequired(
|
||||
method,
|
||||
@ -303,21 +337,85 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
return null;
|
||||
}
|
||||
|
||||
private void reportErrorIfMappedPropertiesDontExist(Method method, Set<String> sourceProperties,
|
||||
Set<String> targetProperties) {
|
||||
private boolean hasSourceProperty(Method method, String propertyName) {
|
||||
for ( Parameter parameter : method.getSourceParameters() ) {
|
||||
if ( hasProperty( parameter, propertyName ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasProperty(Parameter parameter, String propertyName) {
|
||||
TypeElement parameterTypeElement = elementUtils.getTypeElement( parameter.getType().getCanonicalName() );
|
||||
List<ExecutableElement> getters = Filters.setterMethodsIn(
|
||||
elementUtils.getAllMembers( parameterTypeElement )
|
||||
);
|
||||
|
||||
return executables.getPropertyNames( getters ).contains( propertyName );
|
||||
}
|
||||
|
||||
private boolean reportErrorIfMappedPropertiesDontExist(Method method) {
|
||||
TypeElement resultTypeElement = elementUtils.getTypeElement( method.getResultType().getCanonicalName() );
|
||||
List<ExecutableElement> targetSetters = Filters.setterMethodsIn(
|
||||
elementUtils.getAllMembers( resultTypeElement )
|
||||
);
|
||||
|
||||
Set<String> targetProperties = executables.getPropertyNames( targetSetters );
|
||||
|
||||
boolean foundUnmappedProperty = false;
|
||||
|
||||
for ( Mapping mappedProperty : method.getMappings().values() ) {
|
||||
if ( !sourceProperties.contains( mappedProperty.getSourceName() ) ) {
|
||||
if ( mappedProperty.getSourceParameterName() != null ) {
|
||||
Parameter sourceParameter = method.getSourceParameter( mappedProperty.getSourceParameterName() );
|
||||
|
||||
if ( sourceParameter == null ) {
|
||||
messager.printMessage(
|
||||
Kind.ERROR,
|
||||
String.format(
|
||||
"Unknown property \"%s\" in parameter type %s.",
|
||||
mappedProperty.getSourceName(),
|
||||
method.getSingleSourceParameter().getType()
|
||||
"Method has no parameter named \"%s\".",
|
||||
mappedProperty.getSourceParameterName()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getSourceAnnotationValue()
|
||||
);
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
else {
|
||||
if ( !hasProperty( sourceParameter, mappedProperty.getSourcePropertyName() ) ) {
|
||||
messager.printMessage(
|
||||
Kind.ERROR,
|
||||
String.format(
|
||||
"The type of parameter \"%s\" has no property named \"%s\".",
|
||||
mappedProperty.getSourceParameterName(),
|
||||
mappedProperty.getSourcePropertyName()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getSourceAnnotationValue()
|
||||
);
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if ( !hasSourceProperty(
|
||||
method,
|
||||
mappedProperty.getSourcePropertyName()
|
||||
) ) {
|
||||
messager.printMessage(
|
||||
Kind.ERROR,
|
||||
String.format(
|
||||
"No property named \"%s\" exists in source parameter(s).",
|
||||
mappedProperty.getSourceName()
|
||||
),
|
||||
method.getExecutable(),
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getSourceAnnotationValue()
|
||||
);
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
|
||||
messager.printMessage(
|
||||
@ -331,12 +429,16 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
mappedProperty.getMirror(),
|
||||
mappedProperty.getTargetAnnotationValue()
|
||||
);
|
||||
}
|
||||
foundUnmappedProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private PropertyMapping getPropertyMapping(List<Method> methods, Method method, ExecutableElement getterMethod,
|
||||
ExecutableElement setterMethod, String dateFormat) {
|
||||
return !foundUnmappedProperty;
|
||||
}
|
||||
|
||||
private PropertyMapping getPropertyMapping(List<Method> methods, Method method, Parameter parameter,
|
||||
ExecutableElement getterMethod, ExecutableElement setterMethod,
|
||||
String dateFormat) {
|
||||
Type sourceType = executables.retrieveReturnType( getterMethod );
|
||||
Type targetType = executables.retrieveSingleParameter( setterMethod ).getType();
|
||||
|
||||
@ -345,11 +447,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
sourceType,
|
||||
targetType,
|
||||
dateFormat,
|
||||
method.getSingleSourceParameter().getName() + "."
|
||||
+ getterMethod.getSimpleName().toString() + "()"
|
||||
parameter.getName() + "." + getterMethod.getSimpleName().toString() + "()"
|
||||
);
|
||||
|
||||
PropertyMapping property = new PropertyMapping(
|
||||
parameter.getName(),
|
||||
executables.getPropertyName( getterMethod ),
|
||||
getterMethod.getSimpleName().toString(),
|
||||
sourceType,
|
||||
@ -369,7 +471,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
}
|
||||
|
||||
private MappingMethod getIterableMappingMethod(List<Method> methods, Method method) {
|
||||
Type sourceElementType = method.getSingleSourceParameter().getType().getTypeParameters().get( 0 );
|
||||
Type sourceElementType = method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 );
|
||||
Type targetElementType = method.getResultType().getTypeParameters().get( 0 );
|
||||
|
||||
TypeConversion conversion = getConversion(
|
||||
@ -390,7 +492,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
}
|
||||
|
||||
private MappingMethod getMapMappingMethod(List<Method> methods, Method method) {
|
||||
List<Type> sourceTypeParams = method.getSingleSourceParameter().getType().getTypeParameters();
|
||||
List<Type> sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
|
||||
Type sourceKeyType = sourceTypeParams.get( 0 );
|
||||
Type sourceValueType = sourceTypeParams.get( 1 );
|
||||
|
||||
@ -431,12 +533,16 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
|
||||
|
||||
private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType,
|
||||
Type returnType) {
|
||||
for ( Method oneMethod : methods ) {
|
||||
Parameter singleSourceParam = oneMethod.getSingleSourceParameter();
|
||||
for ( Method method : methods ) {
|
||||
if ( method.getSourceParameters().size() > 1 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Parameter singleSourceParam = method.getSourceParameters().iterator().next();
|
||||
|
||||
if ( singleSourceParam.getType().equals( parameterType ) &&
|
||||
oneMethod.getResultType().equals( returnType ) ) {
|
||||
return new MappingMethodReference( oneMethod );
|
||||
method.getResultType().equals( returnType ) ) {
|
||||
return new MappingMethodReference( method );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,15 +194,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( sourceParameters.size() > 1 ) {
|
||||
messager.printMessage(
|
||||
Kind.ERROR,
|
||||
"Mappings from more than one source objects are not yet supported.",
|
||||
method
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( targetParameter != null && ( sourceParameters.size() + 1 != method.getParameters().size() ) ) {
|
||||
messager.printMessage(
|
||||
Kind.ERROR,
|
||||
@ -274,11 +265,11 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( method );
|
||||
|
||||
if ( mappingAnnotation != null ) {
|
||||
mappings.put( mappingAnnotation.source(), Mapping.fromMappingPrism( mappingAnnotation ) );
|
||||
mappings.put( mappingAnnotation.source(), Mapping.fromMappingPrism( mappingAnnotation, method ) );
|
||||
}
|
||||
|
||||
if ( mappingsAnnotation != null ) {
|
||||
mappings.putAll( Mapping.fromMappingsPrism( mappingsAnnotation ) );
|
||||
mappings.putAll( Mapping.fromMappingsPrism( mappingsAnnotation, method ) );
|
||||
}
|
||||
|
||||
return mappings;
|
||||
|
@ -20,17 +20,29 @@
|
||||
-->
|
||||
@Override
|
||||
public ${returnType.name} ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) {
|
||||
if ( ${singleSourceParameter.name} == null ) {
|
||||
if ( <#list sourceParameters as sourceParam>${sourceParam.name} == null<#if sourceParam_has_next> && </#if></#list> ) {
|
||||
return<#if returnType.name != "void"> null</#if>;
|
||||
}
|
||||
|
||||
<#if !existingInstanceMapping>
|
||||
${resultType.name} ${resultName} = new ${resultType.name}();
|
||||
|
||||
</#if>
|
||||
|
||||
<#list propertyMappings as propertyMapping>
|
||||
<@includeModel object=propertyMapping sourceBeanName=singleSourceParameter.name targetBeanName=resultName/>
|
||||
<#if (sourceParameters?size > 1)>
|
||||
<#list sourceParameters as sourceParam>
|
||||
if ( ${sourceParam.name} != null ) {
|
||||
<#list propertyMappingsByParameter[sourceParam.name] as propertyMapping>
|
||||
<@includeModel object=propertyMapping targetBeanName=resultName/>
|
||||
</#list>
|
||||
}
|
||||
</#list>
|
||||
<#else>
|
||||
<#list propertyMappingsByParameter[sourceParameters[0].name] as propertyMapping>
|
||||
<@includeModel object=propertyMapping targetBeanName=resultName/>
|
||||
</#list>
|
||||
</#if>
|
||||
|
||||
<#if returnType.name != "void">
|
||||
|
||||
return ${resultName};
|
||||
|
@ -20,7 +20,7 @@
|
||||
-->
|
||||
@Override
|
||||
public <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) {
|
||||
if ( ${singleSourceParameter.name} == null ) {
|
||||
if ( ${sourceParameter.name} == null ) {
|
||||
return<#if returnType.name != "void"> null</#if>;
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
<#if resultType.name == "Iterable" && resultType.packageName == "java.lang">${resultType.iterableImplementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>> ${resultName} = new <#if resultType.iterableImplementationType??>${resultType.iterableImplementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>>();
|
||||
</#if>
|
||||
|
||||
for ( <@includeModel object=singleSourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${singleSourceParameter.name} ) {
|
||||
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
|
||||
<#if elementMappingMethod??>
|
||||
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
|
||||
<#else>
|
||||
|
@ -20,7 +20,7 @@
|
||||
-->
|
||||
@Override
|
||||
public <@includeModel object=returnType /> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) {
|
||||
if ( ${singleSourceParameter.name} == null ) {
|
||||
if ( ${sourceParameter.name} == null ) {
|
||||
return<#if returnType.name != "void"> null</#if>;
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
<@includeModel object=resultType /> ${resultName} = new <#if resultType.mapImplementationType??><@includeModel object=resultType.mapImplementationType /><#else><@includeModel object=resultType /></#if>();
|
||||
</#if>
|
||||
|
||||
for ( Map.Entry<<#list singleSourceParameter.type.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> ${entryVariableName} : ${singleSourceParameter.name}.entrySet() ) {
|
||||
for ( Map.Entry<<#list sourceParameter.type.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> ${entryVariableName} : ${sourceParameter.name}.entrySet() ) {
|
||||
|
||||
<#-- key -->
|
||||
<#if keyMappingMethod??>
|
||||
|
@ -20,11 +20,11 @@
|
||||
-->
|
||||
<#-- a) invoke mapping method -->
|
||||
<#if mappingMethod??>
|
||||
${ext.targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${ext.sourceBeanName}.${sourceAccessorName}()"/> );
|
||||
${ext.targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()"/> );
|
||||
<#-- b) simple conversion -->
|
||||
<#elseif conversion??>
|
||||
<#if sourceType.primitive == false>
|
||||
if ( ${ext.sourceBeanName}.${sourceAccessorName}() != null ) {
|
||||
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
|
||||
<@applyConversion targetBeanName=ext.targetBeanName targetAccessorName=targetAccessorName conversion=conversion/>
|
||||
}
|
||||
<#else>
|
||||
@ -33,11 +33,11 @@
|
||||
<#-- c) simply set -->
|
||||
<#else>
|
||||
<#if targetType.collectionType == true>
|
||||
if ( ${ext.sourceBeanName}.${sourceAccessorName}() != null ) {
|
||||
${ext.targetBeanName}.${targetAccessorName}( new <#if targetType.collectionImplementationType??>${targetType.collectionImplementationType.name}<#else>${targetType.name}</#if><#if targetType.elementType??><${targetType.elementType.name}></#if>( ${ext.sourceBeanName}.${sourceAccessorName}() ) );
|
||||
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
|
||||
${ext.targetBeanName}.${targetAccessorName}( new <#if targetType.collectionImplementationType??>${targetType.collectionImplementationType.name}<#else>${targetType.name}</#if><#if targetType.elementType??><${targetType.elementType.name}></#if>( ${sourceBeanName}.${sourceAccessorName}() ) );
|
||||
}
|
||||
<#else>
|
||||
${ext.targetBeanName}.${targetAccessorName}( ${ext.sourceBeanName}.${sourceAccessorName}() );
|
||||
${ext.targetBeanName}.${targetAccessorName}( ${sourceBeanName}.${sourceAccessorName}() );
|
||||
</#if>
|
||||
</#if>
|
||||
<#macro applyConversion targetBeanName targetAccessorName conversion>
|
||||
|
@ -20,13 +20,13 @@ package org.mapstruct.ap.test.erroneous.attributereference;
|
||||
|
||||
public class AnotherTarget {
|
||||
|
||||
private int foo;
|
||||
private int bar;
|
||||
|
||||
public int getFoo() {
|
||||
return foo;
|
||||
public int getBar() {
|
||||
return bar;
|
||||
}
|
||||
|
||||
public void setFoo(int foo) {
|
||||
this.foo = foo;
|
||||
public void setBar(int bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,18 @@ package org.mapstruct.ap.test.erroneous.attributereference;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
|
||||
@Mapper
|
||||
public interface ErroneousMapper {
|
||||
|
||||
@Mapping(source = "foo", target = "bar")
|
||||
@Mappings({
|
||||
@Mapping(source = "bar", target = "foo"),
|
||||
@Mapping(source = "source1.foo", target = "foo"),
|
||||
@Mapping(source = "foo", target = "bar"),
|
||||
@Mapping(source = "source.foobar", target = "foo")
|
||||
})
|
||||
Target sourceToTarget(Source source);
|
||||
|
||||
@Mapping(source = "bar", target = "foo")
|
||||
AnotherTarget sourceToAnotherTarget(Source source);
|
||||
}
|
||||
|
@ -43,16 +43,24 @@ public class ErroneousMappingsTest extends MapperTestBase {
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousMapper.class,
|
||||
kind = Kind.ERROR,
|
||||
line = 27,
|
||||
messageRegExp = "Unknown property \"bar\" in return type"),
|
||||
@Diagnostic(type = ErroneousMapper.class,
|
||||
kind = Kind.WARNING,
|
||||
line = 28,
|
||||
messageRegExp = "Unmapped target property: \"foo\""),
|
||||
line = 29,
|
||||
messageRegExp = "No property named \"bar\" exists in source parameter\\(s\\)"),
|
||||
@Diagnostic(type = ErroneousMapper.class,
|
||||
kind = Kind.ERROR,
|
||||
line = 30,
|
||||
messageRegExp = "Unknown property \"bar\" in parameter type")
|
||||
messageRegExp = "Method has no parameter named \"source1\""),
|
||||
@Diagnostic(type = ErroneousMapper.class,
|
||||
kind = Kind.ERROR,
|
||||
line = 31,
|
||||
messageRegExp = "Unknown property \"bar\" in return type"),
|
||||
@Diagnostic(type = ErroneousMapper.class,
|
||||
kind = Kind.ERROR,
|
||||
line = 32,
|
||||
messageRegExp = "The type of parameter \"source\" has no property named \"foobar\""),
|
||||
@Diagnostic(type = ErroneousMapper.class,
|
||||
kind = Kind.WARNING,
|
||||
line = 36,
|
||||
messageRegExp = "Unmapped target property: \"bar\""),
|
||||
}
|
||||
)
|
||||
public void shouldFailToGenerateMappings() {
|
||||
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright 2012-2013 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.severalsources;
|
||||
|
||||
public class Address {
|
||||
|
||||
private String street;
|
||||
private int zipCode;
|
||||
private int houseNo;
|
||||
private String description;
|
||||
|
||||
public Address(String street, int zipCode, int houseNo, String description) {
|
||||
this.street = street;
|
||||
this.zipCode = zipCode;
|
||||
this.houseNo = houseNo;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
public int getZipCode() {
|
||||
return zipCode;
|
||||
}
|
||||
|
||||
public void setZipCode(int zipCode) {
|
||||
this.zipCode = zipCode;
|
||||
}
|
||||
|
||||
public int getHouseNo() {
|
||||
return houseNo;
|
||||
}
|
||||
|
||||
public void setHouseNo(int houseNo) {
|
||||
this.houseNo = houseNo;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright 2012-2013 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.severalsources;
|
||||
|
||||
public class DeliveryAddress {
|
||||
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private int height;
|
||||
private String street;
|
||||
private int zipCode;
|
||||
private int houseNumber;
|
||||
private String description;
|
||||
|
||||
public DeliveryAddress() {
|
||||
}
|
||||
|
||||
public DeliveryAddress(String firstName, String lastName, int height, String street, int zipCode, int houseNumber,
|
||||
String description) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.height = height;
|
||||
this.street = street;
|
||||
this.zipCode = zipCode;
|
||||
this.houseNumber = houseNumber;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
public int getZipCode() {
|
||||
return zipCode;
|
||||
}
|
||||
|
||||
public void setZipCode(int zipCode) {
|
||||
this.zipCode = zipCode;
|
||||
}
|
||||
|
||||
public int getHouseNumber() {
|
||||
return houseNumber;
|
||||
}
|
||||
|
||||
public void setHouseNumber(int houseNumber) {
|
||||
this.houseNumber = houseNumber;
|
||||
}
|
||||
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright 2012-2013 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.severalsources;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface ErroneousSourceTargetMapper {
|
||||
|
||||
ErroneousSourceTargetMapper INSTANCE = Mappers.getMapper( ErroneousSourceTargetMapper.class );
|
||||
|
||||
DeliveryAddress addressAndAddressToDeliveryAddress(Address address1, Address address2);
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright 2012-2013 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.severalsources;
|
||||
|
||||
public class Person {
|
||||
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private int height;
|
||||
private String description;
|
||||
|
||||
public Person(String firstName, String lastName, int height, String description) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.height = height;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright 2012-2013 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.severalsources;
|
||||
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.MapperTestBase;
|
||||
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.compilation.annotation.ProcessorOption;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for propagation of attribute without setter in source and getter in
|
||||
* target.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
@IssueKey("31")
|
||||
public class SeveralSourceParametersTest extends MapperTestBase {
|
||||
|
||||
@Test
|
||||
@WithClasses({ Person.class, Address.class, DeliveryAddress.class, SourceTargetMapper.class })
|
||||
public void shouldMapSeveralSourceAttributesToCombinedTarget() {
|
||||
Person person = new Person( "Bob", "Garner", 181, "An actor" );
|
||||
Address address = new Address( "Main street", 12345, 42, "His address" );
|
||||
|
||||
DeliveryAddress deliveryAddress = SourceTargetMapper.INSTANCE
|
||||
.personAndAddressToDeliveryAddress( person, address );
|
||||
|
||||
assertThat( deliveryAddress ).isNotNull();
|
||||
assertThat( deliveryAddress.getLastName() ).isEqualTo( "Garner" );
|
||||
assertThat( deliveryAddress.getZipCode() ).isEqualTo( 12345 );
|
||||
assertThat( deliveryAddress.getHouseNumber() ).isEqualTo( 42 );
|
||||
assertThat( deliveryAddress.getDescription() ).isEqualTo( "An actor" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ Person.class, Address.class, DeliveryAddress.class, SourceTargetMapper.class })
|
||||
public void shouldMapSeveralSourceAttributesToCombinedTargetWithTargetParameter() {
|
||||
Person person = new Person( "Bob", "Garner", 181, "An actor" );
|
||||
Address address = new Address( "Main street", 12345, 42, "His address" );
|
||||
|
||||
DeliveryAddress deliveryAddress = new DeliveryAddress();
|
||||
SourceTargetMapper.INSTANCE.personAndAddressToDeliveryAddress( person, address, deliveryAddress );
|
||||
|
||||
assertThat( deliveryAddress.getLastName() ).isEqualTo( "Garner" );
|
||||
assertThat( deliveryAddress.getZipCode() ).isEqualTo( 12345 );
|
||||
assertThat( deliveryAddress.getHouseNumber() ).isEqualTo( 42 );
|
||||
assertThat( deliveryAddress.getDescription() ).isEqualTo( "An actor" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ Person.class, Address.class, DeliveryAddress.class, SourceTargetMapper.class })
|
||||
public void shouldSetAttributesFromNonNullParameters() {
|
||||
Person person = new Person( "Bob", "Garner", 181, "An actor" );
|
||||
|
||||
DeliveryAddress deliveryAddress = SourceTargetMapper.INSTANCE
|
||||
.personAndAddressToDeliveryAddress( person, null );
|
||||
|
||||
assertThat( deliveryAddress ).isNotNull();
|
||||
assertThat( deliveryAddress.getLastName() ).isEqualTo( "Garner" );
|
||||
assertThat( deliveryAddress.getDescription() ).isEqualTo( "An actor" );
|
||||
assertThat( deliveryAddress.getHouseNumber() ).isEqualTo( 0 );
|
||||
assertThat( deliveryAddress.getStreet() ).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ Person.class, Address.class, DeliveryAddress.class, SourceTargetMapper.class })
|
||||
public void shouldReturnNullIfAllParametersAreNull() {
|
||||
DeliveryAddress deliveryAddress = SourceTargetMapper.INSTANCE
|
||||
.personAndAddressToDeliveryAddress( null, null );
|
||||
|
||||
assertThat( deliveryAddress ).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ErroneousSourceTargetMapper.class, Address.class, DeliveryAddress.class })
|
||||
@ProcessorOption(name = "unmappedTargetPolicy", value = "IGNORE")
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousSourceTargetMapper.class,
|
||||
kind = Kind.ERROR,
|
||||
line = 29,
|
||||
messageRegExp = "Several possible source properties for target property \"street\".")
|
||||
}
|
||||
)
|
||||
public void shouldFailToGenerateMappingsForAmbigiousSourceProperty() {
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright 2012-2013 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.severalsources;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mappers;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.Mappings;
|
||||
|
||||
@Mapper
|
||||
public interface SourceTargetMapper {
|
||||
|
||||
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "houseNo", target = "houseNumber"),
|
||||
@Mapping(source = "person.description", target = "description")
|
||||
})
|
||||
DeliveryAddress personAndAddressToDeliveryAddress(Person person, Address address);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "houseNo", target = "houseNumber"),
|
||||
@Mapping(source = "person.description", target = "description")
|
||||
})
|
||||
void personAndAddressToDeliveryAddress(Person person, Address address,
|
||||
@MappingTarget DeliveryAddress deliveryAddress);
|
||||
}
|
@ -167,8 +167,8 @@ public class DiagnosticDescriptor {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String sourceFileName = this.sourceFileName
|
||||
.substring( this.sourceFileName.lastIndexOf( File.separatorChar ) + 1 );
|
||||
return "DiagnosticDescriptor: " + kind + " " + sourceFileName + ":" + line + " " + message;
|
||||
String fileName = sourceFileName != null ?
|
||||
sourceFileName.substring( this.sourceFileName.lastIndexOf( File.separatorChar ) + 1 ) : null;
|
||||
return "DiagnosticDescriptor: " + kind + " " + fileName + ":" + line + " " + message;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user