#31 Providing support for bean mapping methods with several source parameters

This commit is contained in:
Gunnar Morling 2013-08-11 14:42:14 +02:00
parent b61f0ce915
commit 86a8e72692
25 changed files with 883 additions and 126 deletions

View File

@ -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;
}
}

View File

@ -36,6 +36,7 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementKindVisitor6; import javax.lang.model.util.ElementKindVisitor6;
import javax.tools.Diagnostic.Kind;
import net.java.dev.hickory.prism.GeneratePrism; import net.java.dev.hickory.prism.GeneratePrism;
import net.java.dev.hickory.prism.GeneratePrisms; import net.java.dev.hickory.prism.GeneratePrisms;
@ -152,7 +153,20 @@ public class MappingProcessor extends AbstractProcessor {
Object model = null; Object model = null;
for ( ModelElementProcessor<?, ?> processor : getProcessors() ) { for ( ModelElementProcessor<?, ?> processor : getProcessors() ) {
model = process( context, processor, mapperTypeElement, model ); try {
model = process( context, processor, mapperTypeElement, model );
}
catch ( AnnotationProcessingException e ) {
processingEnv.getMessager()
.printMessage(
Kind.ERROR,
e.getMessage(),
e.getElement(),
e.getAnnotationMirror(),
e.getAnnotationValue()
);
break;
}
} }
} }

View File

@ -18,7 +18,10 @@
*/ */
package org.mapstruct.ap.model; package org.mapstruct.ap.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
@ -43,6 +46,22 @@ public class BeanMappingMethod extends MappingMethod {
return propertyMappings; 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 @Override
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
Set<Type> types = super.getImportTypes(); Set<Type> types = super.getImportTypes();

View File

@ -42,6 +42,16 @@ public class IterableMappingMethod extends MappingMethod {
this.conversion = conversion; 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() { public MappingMethodReference getElementMappingMethod() {
return elementMappingMethod; return elementMappingMethod;
} }
@ -64,7 +74,7 @@ public class IterableMappingMethod extends MappingMethod {
public String getLoopVariableName() { public String getLoopVariableName() {
return Strings.getSaveVariableName( return Strings.getSaveVariableName(
Introspector.decapitalize( Introspector.decapitalize(
getSingleSourceParameter().getType() getSourceParameter().getType()
.getTypeParameters() .getTypeParameters()
.get( 0 ) .get( 0 )
.getName() .getName()

View File

@ -46,6 +46,16 @@ public class MapMappingMethod extends MappingMethod {
this.valueConversion = valueConversion; 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() { public MappingMethodReference getKeyMappingMethod() {
return keyMappingMethod; return keyMappingMethod;
} }

View File

@ -37,14 +37,12 @@ public abstract class MappingMethod extends AbstractModelElement {
private final String name; private final String name;
private final List<Parameter> parameters; private final List<Parameter> parameters;
private final Type returnType; private final Type returnType;
private final Parameter singleSourceParameter;
private final Parameter targetParameter; private final Parameter targetParameter;
public MappingMethod(Method method) { public MappingMethod(Method method) {
this.name = method.getName(); this.name = method.getName();
this.parameters = method.getParameters(); this.parameters = method.getParameters();
this.returnType = method.getReturnType(); this.returnType = method.getReturnType();
this.singleSourceParameter = method.getSingleSourceParameter();
this.targetParameter = method.getTargetParameter(); this.targetParameter = method.getTargetParameter();
} }
@ -56,8 +54,16 @@ public abstract class MappingMethod extends AbstractModelElement {
return parameters; return parameters;
} }
public Parameter getSingleSourceParameter() { public List<Parameter> getSourceParameters() {
return singleSourceParameter; List<Parameter> sourceParameters = new ArrayList<Parameter>();
for ( Parameter parameter : parameters ) {
if ( !parameter.isMappingTarget() ) {
sourceParameters.add( parameter );
}
}
return sourceParameters;
} }
public Type getResultType() { public Type getResultType() {

View File

@ -31,6 +31,7 @@ import java.util.Set;
*/ */
public class PropertyMapping extends AbstractModelElement { public class PropertyMapping extends AbstractModelElement {
private final String sourceBeanName;
private final String sourceName; private final String sourceName;
private final String sourceAccessorName; private final String sourceAccessorName;
private final Type sourceType; private final Type sourceType;
@ -42,10 +43,11 @@ public class PropertyMapping extends AbstractModelElement {
private final MappingMethodReference mappingMethod; private final MappingMethodReference mappingMethod;
private final TypeConversion conversion; private final TypeConversion conversion;
public PropertyMapping(String sourceName, String sourceAccessorName, Type sourceType, String targetName, public PropertyMapping(String sourceBeanName, String sourceName, String sourceAccessorName, Type sourceType,
String targetAccessorName, Type targetType, MappingMethodReference mappingMethod, String targetName, String targetAccessorName, Type targetType,
TypeConversion conversion) { MappingMethodReference mappingMethod, TypeConversion conversion) {
this.sourceBeanName = sourceBeanName;
this.sourceName = sourceName; this.sourceName = sourceName;
this.sourceAccessorName = sourceAccessorName; this.sourceAccessorName = sourceAccessorName;
this.sourceType = sourceType; this.sourceType = sourceType;
@ -58,6 +60,10 @@ public class PropertyMapping extends AbstractModelElement {
this.conversion = conversion; this.conversion = conversion;
} }
public String getSourceBeanName() {
return sourceBeanName;
}
public String getSourceName() { public String getSourceName() {
return sourceName; return sourceName;
} }

View File

@ -22,7 +22,9 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; 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.MappingPrism;
import org.mapstruct.ap.MappingsPrism; import org.mapstruct.ap.MappingsPrism;
@ -34,25 +36,36 @@ import org.mapstruct.ap.MappingsPrism;
public class Mapping { public class Mapping {
private final String sourceName; private final String sourceName;
private final String sourceParameterName;
private final String sourcePropertyName;
private final String targetName; private final String targetName;
private final String dateFormat; private final String dateFormat;
private final AnnotationMirror mirror; private final AnnotationMirror mirror;
private final AnnotationValue sourceAnnotationValue; private final AnnotationValue sourceAnnotationValue;
private final AnnotationValue targetAnnotationValue; 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>(); Map<String, Mapping> mappings = new HashMap<String, Mapping>();
for ( MappingPrism mapping : mappingsAnnotation.value() ) { for ( MappingPrism mapping : mappingsAnnotation.value() ) {
mappings.put( mapping.source(), fromMappingPrism( mapping ) ); mappings.put( mapping.source(), fromMappingPrism( mapping, element ) );
} }
return mappings; 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( return new Mapping(
mapping.source(), mapping.source(),
sourceNameParts != null ? sourceNameParts[0] : null,
sourceNameParts != null ? sourceNameParts[1] : mapping.source(),
mapping.target(), mapping.target(),
mapping.dateFormat(), mapping.dateFormat(),
mapping.mirror, mapping.mirror,
@ -61,9 +74,31 @@ public class Mapping {
); );
} }
private Mapping(String sourceName, String targetName, String dateFormat, AnnotationMirror mirror, private static String[] getSourceNameParts(String sourceName, Element element, AnnotationMirror annotationMirror,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) { 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.sourceName = sourceName;
this.sourceParameterName = sourceParameterName;
this.sourcePropertyName = sourcePropertyName;
this.targetName = targetName.equals( "" ) ? sourceName : targetName; this.targetName = targetName.equals( "" ) ? sourceName : targetName;
this.dateFormat = dateFormat; this.dateFormat = dateFormat;
this.mirror = mirror; this.mirror = mirror;
@ -71,10 +106,34 @@ public class Mapping {
this.targetAnnotationValue = targetAnnotationValue; 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() { public String getSourceName() {
return sourceName; 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() { public String getTargetName() {
return targetName; return targetName;
} }
@ -96,7 +155,16 @@ public class Mapping {
} }
public Mapping reverse() { public Mapping reverse() {
return new Mapping( targetName, sourceName, dateFormat, mirror, sourceAnnotationValue, targetAnnotationValue ); return new Mapping(
targetName,
null,
targetName,
sourceName,
dateFormat,
mirror,
sourceAnnotationValue,
targetAnnotationValue
);
} }
@Override @Override

View File

@ -45,7 +45,6 @@ public class Method {
private IterableMapping iterableMapping; private IterableMapping iterableMapping;
private MapMapping mapMapping; private MapMapping mapMapping;
private final Parameter singleSourceParameter;
private final Parameter targetParameter; private final Parameter targetParameter;
public static Method forMethodRequiringImplementation(ExecutableElement executable, List<Parameter> parameters, public static Method forMethodRequiringImplementation(ExecutableElement executable, List<Parameter> parameters,
@ -83,12 +82,10 @@ public class Method {
this.executable = executable; this.executable = executable;
this.parameters = parameters; this.parameters = parameters;
this.returnType = returnType; this.returnType = returnType;
this.mappings = mappings; this.mappings = mappings;
this.iterableMapping = iterableMapping; this.iterableMapping = iterableMapping;
this.mapMapping = mapMapping; this.mapMapping = mapMapping;
this.singleSourceParameter = determineSingleSourceParameter();
this.targetParameter = determineTargetParameter( parameters ); this.targetParameter = determineTargetParameter( parameters );
} }
@ -102,16 +99,6 @@ public class Method {
return null; 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 * Returns the mapper type declaring this method if it is not declared by
* the mapper interface currently processed but by another mapper imported * the mapper interface currently processed but by another mapper imported
@ -135,6 +122,18 @@ public class Method {
return parameters; 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() { public List<String> getParameterNames() {
List<String> parameterNames = new ArrayList<String>( parameters.size() ); List<String> parameterNames = new ArrayList<String>( parameters.size() );
@ -179,12 +178,10 @@ public class Method {
public boolean reverses(Method method) { public boolean reverses(Method method) {
return return
equals( getSingleSourceParameter().getType(), method.getResultType() ) getSourceParameters().size() == 1 &&
&& equals( getResultType(), method.getSingleSourceParameter().getType() ); method.getSourceParameters().size() == 1 &&
} equals( getSourceParameters().iterator().next().getType(), method.getResultType() ) &&
equals( getResultType(), method.getSourceParameters().iterator().next().getType() );
public Parameter getSingleSourceParameter() {
return singleSourceParameter;
} }
public Parameter getTargetParameter() { public Parameter getTargetParameter() {
@ -192,11 +189,13 @@ public class Method {
} }
public boolean isIterableMapping() { public boolean isIterableMapping() {
return getSingleSourceParameter().getType().isIterableType() && getResultType().isIterableType(); return getSourceParameters().size() == 1 &&
getSourceParameters().iterator().next().getType().isIterableType() && getResultType().isIterableType();
} }
public boolean isMapMapping() { 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) { private boolean equals(Object o1, Object o2) {
@ -207,4 +206,24 @@ public class Method {
public String toString() { public String toString() {
return returnType + " " + getName() + "(" + Strings.join( parameters, ", " ) + ")"; 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;
}
} }

View File

@ -181,7 +181,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
method.setMappings( reverse( reverseMappingMethod.getMappings() ) ); 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; return mappingMethods;
@ -210,60 +213,91 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return reversed; 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, private MappingMethod getBeanMappingMethod(List<Method> methods, Method method,
ReportingPolicy unmappedTargetPolicy) { ReportingPolicy unmappedTargetPolicy) {
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>(); List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
Set<String> mappedTargetProperties = new HashSet<String>(); 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 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( List<ExecutableElement> targetSetters = Filters.setterMethodsIn(
elementUtils.getAllMembers( resultTypeElement ) elementUtils.getAllMembers( resultTypeElement )
); );
Set<String> sourceProperties = executables.getPropertyNames( for ( ExecutableElement setterMethod : targetSetters ) {
Filters.getterMethodsIn( sourceGetters ) String targetPropertyName = executables.getPropertyName( setterMethod );
);
Set<String> targetProperties = executables.getPropertyNames(
Filters.setterMethodsIn( targetSetters )
);
reportErrorIfMappedPropertiesDontExist( method, sourceProperties, targetProperties ); Mapping mapping = method.getMapping( targetPropertyName );
for ( ExecutableElement getterMethod : sourceGetters ) { PropertyMapping propertyMapping = null;
String sourcePropertyName = executables.getPropertyName( getterMethod ); if ( mapping != null && mapping.getSourceParameterName() != null ) {
Mapping mapping = mappings.get( sourcePropertyName ); Parameter parameter = method.getSourceParameter( mapping.getSourceParameterName() );
String dateFormat = mapping != null ? mapping.getDateFormat() : null; propertyMapping = getPropertyMapping( methods, method, setterMethod, parameter );
}
for ( ExecutableElement setterMethod : targetSetters ) { if ( propertyMapping == null ) {
String targetPropertyName = executables.getPropertyName( setterMethod ); for ( Parameter sourceParameter : method.getSourceParameters() ) {
PropertyMapping newPropertyMapping = getPropertyMapping(
if ( targetPropertyName.equals( mapping != null ? mapping.getTargetName() : sourcePropertyName ) ) {
PropertyMapping property = getPropertyMapping(
methods, methods,
method, method,
getterMethod,
setterMethod, setterMethod,
dateFormat sourceParameter
); );
if ( propertyMapping != null && newPropertyMapping != null ) {
propertyMappings.add( property ); messager.printMessage(
Kind.ERROR,
mappedTargetProperties.add( targetPropertyName ); "Several possible source properties for target property \"" + targetPropertyName + "\".",
method.getExecutable()
);
break;
}
else if ( newPropertyMapping != null ) {
propertyMapping = newPropertyMapping;
}
} }
} }
if ( propertyMapping != null ) {
propertyMappings.add( propertyMapping );
mappedTargetProperties.add( targetPropertyName );
}
} }
Set<String> targetProperties = executables.getPropertyNames( targetSetters );
reportErrorForUnmappedTargetPropertiesIfRequired( reportErrorForUnmappedTargetPropertiesIfRequired(
method, method,
unmappedTargetPolicy, unmappedTargetPolicy,
@ -303,21 +337,85 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return null; return null;
} }
private void reportErrorIfMappedPropertiesDontExist(Method method, Set<String> sourceProperties, private boolean hasSourceProperty(Method method, String propertyName) {
Set<String> targetProperties) { 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() ) { 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(
"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( messager.printMessage(
Kind.ERROR, Kind.ERROR,
String.format( String.format(
"Unknown property \"%s\" in parameter type %s.", "No property named \"%s\" exists in source parameter(s).",
mappedProperty.getSourceName(), mappedProperty.getSourceName()
method.getSingleSourceParameter().getType()
), ),
method.getExecutable(), method.getExecutable(),
mappedProperty.getMirror(), mappedProperty.getMirror(),
mappedProperty.getSourceAnnotationValue() mappedProperty.getSourceAnnotationValue()
); );
foundUnmappedProperty = true;
} }
if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) { if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
messager.printMessage( messager.printMessage(
@ -331,12 +429,16 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
mappedProperty.getMirror(), mappedProperty.getMirror(),
mappedProperty.getTargetAnnotationValue() mappedProperty.getTargetAnnotationValue()
); );
foundUnmappedProperty = true;
} }
} }
return !foundUnmappedProperty;
} }
private PropertyMapping getPropertyMapping(List<Method> methods, Method method, ExecutableElement getterMethod, private PropertyMapping getPropertyMapping(List<Method> methods, Method method, Parameter parameter,
ExecutableElement setterMethod, String dateFormat) { ExecutableElement getterMethod, ExecutableElement setterMethod,
String dateFormat) {
Type sourceType = executables.retrieveReturnType( getterMethod ); Type sourceType = executables.retrieveReturnType( getterMethod );
Type targetType = executables.retrieveSingleParameter( setterMethod ).getType(); Type targetType = executables.retrieveSingleParameter( setterMethod ).getType();
@ -345,11 +447,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
sourceType, sourceType,
targetType, targetType,
dateFormat, dateFormat,
method.getSingleSourceParameter().getName() + "." parameter.getName() + "." + getterMethod.getSimpleName().toString() + "()"
+ getterMethod.getSimpleName().toString() + "()"
); );
PropertyMapping property = new PropertyMapping( PropertyMapping property = new PropertyMapping(
parameter.getName(),
executables.getPropertyName( getterMethod ), executables.getPropertyName( getterMethod ),
getterMethod.getSimpleName().toString(), getterMethod.getSimpleName().toString(),
sourceType, sourceType,
@ -369,7 +471,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
private MappingMethod getIterableMappingMethod(List<Method> methods, Method method) { 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 ); Type targetElementType = method.getResultType().getTypeParameters().get( 0 );
TypeConversion conversion = getConversion( TypeConversion conversion = getConversion(
@ -390,7 +492,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
private MappingMethod getMapMappingMethod(List<Method> methods, Method method) { 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 sourceKeyType = sourceTypeParams.get( 0 );
Type sourceValueType = sourceTypeParams.get( 1 ); Type sourceValueType = sourceTypeParams.get( 1 );
@ -431,12 +533,16 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType, private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType,
Type returnType) { Type returnType) {
for ( Method oneMethod : methods ) { for ( Method method : methods ) {
Parameter singleSourceParam = oneMethod.getSingleSourceParameter(); if ( method.getSourceParameters().size() > 1 ) {
continue;
}
Parameter singleSourceParam = method.getSourceParameters().iterator().next();
if ( singleSourceParam.getType().equals( parameterType ) && if ( singleSourceParam.getType().equals( parameterType ) &&
oneMethod.getResultType().equals( returnType ) ) { method.getResultType().equals( returnType ) ) {
return new MappingMethodReference( oneMethod ); return new MappingMethodReference( method );
} }
} }

View File

@ -194,15 +194,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return false; 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() ) ) { if ( targetParameter != null && ( sourceParameters.size() + 1 != method.getParameters().size() ) ) {
messager.printMessage( messager.printMessage(
Kind.ERROR, Kind.ERROR,
@ -274,11 +265,11 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( method ); MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( method );
if ( mappingAnnotation != null ) { if ( mappingAnnotation != null ) {
mappings.put( mappingAnnotation.source(), Mapping.fromMappingPrism( mappingAnnotation ) ); mappings.put( mappingAnnotation.source(), Mapping.fromMappingPrism( mappingAnnotation, method ) );
} }
if ( mappingsAnnotation != null ) { if ( mappingsAnnotation != null ) {
mappings.putAll( Mapping.fromMappingsPrism( mappingsAnnotation ) ); mappings.putAll( Mapping.fromMappingsPrism( mappingsAnnotation, method ) );
} }
return mappings; return mappings;

View File

@ -20,17 +20,29 @@
--> -->
@Override @Override
public ${returnType.name} ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) { 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>; return<#if returnType.name != "void"> null</#if>;
} }
<#if !existingInstanceMapping> <#if !existingInstanceMapping>
${resultType.name} ${resultName} = new ${resultType.name}(); ${resultType.name} ${resultName} = new ${resultType.name}();
</#if>
<#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>
<#list propertyMappings as propertyMapping>
<@includeModel object=propertyMapping sourceBeanName=singleSourceParameter.name targetBeanName=resultName/>
</#list>
<#if returnType.name != "void"> <#if returnType.name != "void">
return ${resultName}; return ${resultName};

View File

@ -20,7 +20,7 @@
--> -->
@Override @Override
public <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) { 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>; 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 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> </#if>
for ( <@includeModel object=singleSourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${singleSourceParameter.name} ) { for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
<#if elementMappingMethod??> <#if elementMappingMethod??>
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> ); ${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
<#else> <#else>

View File

@ -20,7 +20,7 @@
--> -->
@Override @Override
public <@includeModel object=returnType /> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) { 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>; 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>(); <@includeModel object=resultType /> ${resultName} = new <#if resultType.mapImplementationType??><@includeModel object=resultType.mapImplementationType /><#else><@includeModel object=resultType /></#if>();
</#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 --> <#-- key -->
<#if keyMappingMethod??> <#if keyMappingMethod??>

View File

@ -20,11 +20,11 @@
--> -->
<#-- a) invoke mapping method --> <#-- a) invoke mapping method -->
<#if mappingMethod??> <#if mappingMethod??>
${ext.targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${ext.sourceBeanName}.${sourceAccessorName}()"/> ); ${ext.targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()"/> );
<#-- b) simple conversion --> <#-- b) simple conversion -->
<#elseif conversion??> <#elseif conversion??>
<#if sourceType.primitive == false> <#if sourceType.primitive == false>
if ( ${ext.sourceBeanName}.${sourceAccessorName}() != null ) { if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<@applyConversion targetBeanName=ext.targetBeanName targetAccessorName=targetAccessorName conversion=conversion/> <@applyConversion targetBeanName=ext.targetBeanName targetAccessorName=targetAccessorName conversion=conversion/>
} }
<#else> <#else>
@ -33,11 +33,11 @@
<#-- c) simply set --> <#-- c) simply set -->
<#else> <#else>
<#if targetType.collectionType == true> <#if targetType.collectionType == true>
if ( ${ext.sourceBeanName}.${sourceAccessorName}() != null ) { 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>( ${ext.sourceBeanName}.${sourceAccessorName}() ) ); ${ext.targetBeanName}.${targetAccessorName}( new <#if targetType.collectionImplementationType??>${targetType.collectionImplementationType.name}<#else>${targetType.name}</#if><#if targetType.elementType??><${targetType.elementType.name}></#if>( ${sourceBeanName}.${sourceAccessorName}() ) );
} }
<#else> <#else>
${ext.targetBeanName}.${targetAccessorName}( ${ext.sourceBeanName}.${sourceAccessorName}() ); ${ext.targetBeanName}.${targetAccessorName}( ${sourceBeanName}.${sourceAccessorName}() );
</#if> </#if>
</#if> </#if>
<#macro applyConversion targetBeanName targetAccessorName conversion> <#macro applyConversion targetBeanName targetAccessorName conversion>

View File

@ -20,13 +20,13 @@ package org.mapstruct.ap.test.erroneous.attributereference;
public class AnotherTarget { public class AnotherTarget {
private int foo; private int bar;
public int getFoo() { public int getBar() {
return foo; return bar;
} }
public void setFoo(int foo) { public void setBar(int bar) {
this.foo = foo; this.bar = bar;
} }
} }

View File

@ -20,13 +20,18 @@ package org.mapstruct.ap.test.erroneous.attributereference;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
@Mapper @Mapper
public interface ErroneousMapper { 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); Target sourceToTarget(Source source);
@Mapping(source = "bar", target = "foo")
AnotherTarget sourceToAnotherTarget(Source source); AnotherTarget sourceToAnotherTarget(Source source);
} }

View File

@ -43,16 +43,24 @@ public class ErroneousMappingsTest extends MapperTestBase {
diagnostics = { diagnostics = {
@Diagnostic(type = ErroneousMapper.class, @Diagnostic(type = ErroneousMapper.class,
kind = Kind.ERROR, kind = Kind.ERROR,
line = 27, line = 29,
messageRegExp = "Unknown property \"bar\" in return type"), messageRegExp = "No property named \"bar\" exists in source parameter\\(s\\)"),
@Diagnostic(type = ErroneousMapper.class,
kind = Kind.WARNING,
line = 28,
messageRegExp = "Unmapped target property: \"foo\""),
@Diagnostic(type = ErroneousMapper.class, @Diagnostic(type = ErroneousMapper.class,
kind = Kind.ERROR, kind = Kind.ERROR,
line = 30, 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() { public void shouldFailToGenerateMappings() {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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() {
}
}

View File

@ -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);
}

View File

@ -167,8 +167,8 @@ public class DiagnosticDescriptor {
@Override @Override
public String toString() { public String toString() {
String sourceFileName = this.sourceFileName String fileName = sourceFileName != null ?
.substring( this.sourceFileName.lastIndexOf( File.separatorChar ) + 1 ); sourceFileName.substring( this.sourceFileName.lastIndexOf( File.separatorChar ) + 1 ) : null;
return "DiagnosticDescriptor: " + kind + " " + sourceFileName + ":" + line + " " + message; return "DiagnosticDescriptor: " + kind + " " + fileName + ":" + line + " " + message;
} }
} }