#975 Introduce @Context annotation for passing context parameters through generated mapping methods to custom methods

This commit is contained in:
Andreas Gudian 2016-12-20 19:29:38 +01:00 committed by Gunnar Morling
parent 00b8ae01a1
commit 44fe197d3b
34 changed files with 989 additions and 149 deletions

View File

@ -0,0 +1,89 @@
/**
* Copyright 2012-2016 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;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a parameter of a method to be treated as <em>mapping context</em>. Such parameters are passed to other mapping
* methods, {@link ObjectFactory} methods or {@link BeforeMapping}/{@link AfterMapping} methods when applicable and can
* thus be used in custom code. The {@link Context} parameters are otherwise ignored by MapStruct.
* <p>
* For generated code to call a method that is declared with {@link Context} parameters, the declaration of the mapping
* method being generated needs to contain at least those (or assignable) {@link Context} parameters as well. MapStruct
* will not create new instances of missing {@link Context} parameters nor will it pass {@code null} instead.
* <p>
* Example:
*
* <pre>
* <code>
* // multiple &#64;Context parameters can be added
* public abstract CarDto toCar(Car car, &#64;Context VehicleRegistration context, &#64;Context Locale localeToUse);
*
* protected OwnerManualDto translateOwnerManual(OwnerManual ownerManual, &#64;Context Locale locale) {
* // manually implemented logic to translate the OwnerManual with the given Locale
* }
*
* &#64;BeforeMapping
* protected void registerVehicle(Vehicle mappedVehicle, &#64;Context VehicleRegistration context) {
* context.register( mappedVehicle );
* }
*
* &#64;BeforeMapping
* protected void logMappedVehicle(Vehicle mappedVehicle) {
* // do something with the vehicle
* }
*
* &#64;BeforeMapping
* protected void notCalled(Vehicle mappedVehicle, &#64;Context DifferentMappingContextType context) {
* // not called, because DifferentMappingContextType is not available
* // within toCar(Car, VehicleRegistration, Locale)
* }
*
* // generates:
*
* public CarDto toCar(Car car, VehicleRegistration context, Locale localeToUse) {
* registerVehicle( car, context );
* logMappedVehicle( car );
*
* if ( car == null ) {
* return null;
* }
*
* CarDto carDto = new CarDto();
*
* carDto.setOwnerManual( translateOwnerManual( car.getOwnerManual(), localeToUse );
* // more generated mapping code
*
* return carDto;
* }
* </code>
* </pre>
*
* @author Andreas Gudian
* @since 1.2
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.CLASS)
public @interface Context {
}

View File

@ -23,7 +23,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.internal.model.common.Accessibility;
import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Parameter;
@ -44,8 +46,6 @@ import org.mapstruct.ap.internal.util.Strings;
* @author Sjaak Derksen
*/
public abstract class HelperMethod implements Method {
/**
* {@inheritDoc }
*
@ -84,6 +84,11 @@ public abstract class HelperMethod implements Method {
return getParameters();
}
@Override
public List<Parameter> getContextParameters() {
return Collections.emptyList();
}
/**
* {@inheritDoc}
* <p>

View File

@ -189,6 +189,7 @@ public class IterableMappingMethod extends MappingMethod {
targetType,
method.getMapperConfiguration(),
method.getExecutable(),
method.getContextParameters(),
forgedMethodHistory
);
@ -238,7 +239,7 @@ public class IterableMappingMethod extends MappingMethod {
public Parameter getSourceParameter() {
for ( Parameter parameter : getParameters() ) {
if ( !parameter.isMappingTarget() ) {
if ( !parameter.isMappingTarget() && !parameter.isMappingContext() ) {
return parameter;
}
}

View File

@ -199,6 +199,7 @@ public class MapMappingMethod extends MappingMethod {
targetType,
method.getMapperConfiguration(),
method.getExecutable(),
method.getContextParameters(),
history
);
@ -247,7 +248,7 @@ public class MapMappingMethod extends MappingMethod {
public Parameter getSourceParameter() {
for ( Parameter parameter : getParameters() ) {
if ( !parameter.isMappingTarget() ) {
if ( !parameter.isMappingTarget() && !parameter.isMappingContext() ) {
return parameter;
}
}

View File

@ -43,7 +43,8 @@ import org.mapstruct.ap.internal.model.source.Method;
public abstract class MappingMethod extends ModelElement {
private final String name;
private List<Parameter> parameters;
private final List<Parameter> parameters;
private final List<Parameter> sourceParameters;
private final Type returnType;
private final Parameter targetParameter;
private final Accessibility accessibility;
@ -77,6 +78,7 @@ public abstract class MappingMethod extends ModelElement {
List<ForgedMethod> forgedMethods) {
this.name = method.getName();
this.parameters = parameters;
this.sourceParameters = Parameter.getSourceParameters( parameters );
this.returnType = method.getReturnType();
this.targetParameter = method.getMappingTargetParameter();
this.accessibility = method.getAccessibility();
@ -144,14 +146,6 @@ public abstract class MappingMethod extends ModelElement {
}
public List<Parameter> getSourceParameters() {
List<Parameter> sourceParameters = new ArrayList<Parameter>();
for ( Parameter parameter : parameters ) {
if ( !parameter.isMappingTarget() ) {
sourceParameters.add( parameter );
}
}
return sourceParameters;
}

View File

@ -79,7 +79,7 @@ public class NestedPropertyMappingMethod extends MappingMethod {
public Parameter getSourceParameter() {
for ( Parameter parameter : getParameters() ) {
if ( !parameter.isMappingTarget() ) {
if ( !parameter.isMappingTarget() && !parameter.isMappingContext() ) {
return parameter;
}
}

View File

@ -479,12 +479,14 @@ public class PropertyMapping extends ModelElement {
// forge a method from the parameter type to the last entry type.
String forgedName = Strings.joinAndCamelize( sourceReference.getElementNames() );
forgedName = Strings.getSaveVariableName( forgedName, ctx.getNamesOfMappingsToGenerate() );
ForgedMethod methodRef = new ForgedMethod( forgedName,
sourceReference.getParameter().getType(),
sourceType,
config,
method.getExecutable()
);
ForgedMethod methodRef = new ForgedMethod(
forgedName,
sourceReference.getParameter().getType(),
sourceType,
config,
method.getExecutable(),
method.getContextParameters() );
NestedPropertyMappingMethod.Builder builder = new NestedPropertyMappingMethod.Builder();
NestedPropertyMappingMethod nestedPropertyMapping = builder
.method( methodRef )
@ -540,7 +542,13 @@ public class PropertyMapping extends ModelElement {
// copy mapper configuration from the source method, its the same mapper
MapperConfiguration config = method.getMapperConfiguration();
ForgedMethod methodRef = new ForgedMethod( name, sourceType, targetType, config, element,
ForgedMethod methodRef = new ForgedMethod(
name,
sourceType,
targetType,
config,
element,
method.getContextParameters(),
new ForgedMethodHistory( getForgedMethodHistory( source ),
source.getSourceErrorMessagePart(),
targetPropertyName,
@ -588,7 +596,14 @@ public class PropertyMapping extends ModelElement {
// copy mapper configuration from the source method, its the same mapper
MapperConfiguration config = method.getMapperConfiguration();
ForgedMethod methodRef = new ForgedMethod( name, sourceType, targetType, config, element,
ForgedMethod methodRef =
new ForgedMethod(
name,
sourceType,
targetType,
config,
element,
method.getContextParameters(),
new ForgedMethodHistory( getForgedMethodHistory( source ),
source.getSourceErrorMessagePart(),
targetPropertyName,
@ -638,6 +653,7 @@ public class PropertyMapping extends ModelElement {
targetType,
method.getMapperConfiguration(),
method.getExecutable(),
method.getContextParameters(),
getForgedMethodHistory( sourceRHS )
);

View File

@ -18,8 +18,15 @@
*/
package org.mapstruct.ap.internal.model.common;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.VariableElement;
import org.mapstruct.ap.internal.prism.ContextPrism;
import org.mapstruct.ap.internal.prism.MappingTargetPrism;
import org.mapstruct.ap.internal.prism.TargetTypePrism;
import org.mapstruct.ap.internal.util.Collections;
/**
@ -34,18 +41,20 @@ public class Parameter extends ModelElement {
private final Type type;
private final boolean mappingTarget;
private final boolean targetType;
private final boolean mappingContext;
public Parameter(String name, Type type, boolean mappingTarget, boolean targetType) {
private Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingContext) {
// issue #909: FreeMarker doesn't like "values" as a parameter name
this.name = "values".equals( name ) ? "values_" : name;
this.originalName = name;
this.type = type;
this.mappingTarget = mappingTarget;
this.targetType = targetType;
this.mappingContext = mappingContext;
}
public Parameter(String name, Type type) {
this( name, type, false, false );
this( name, type, false, false, false );
}
public String getName() {
@ -66,7 +75,9 @@ public class Parameter extends ModelElement {
@Override
public String toString() {
return ( mappingTarget ? "@MappingTarget " : "" ) + ( targetType ? "@TargetType " : "" )
return ( mappingTarget ? "@MappingTarget " : "" )
+ ( targetType ? "@TargetType " : "" )
+ ( mappingContext ? "@Context " : "" )
+ type.toString() + " " + name;
}
@ -79,6 +90,10 @@ public class Parameter extends ModelElement {
return targetType;
}
public boolean isMappingContext() {
return mappingContext;
}
@Override
public int hashCode() {
int hash = 5;
@ -100,4 +115,45 @@ public class Parameter extends ModelElement {
}
return true;
}
public static Parameter forElementAndType(VariableElement element, Type parameterType) {
return new Parameter(
element.getSimpleName().toString(),
parameterType,
MappingTargetPrism.getInstanceOn( element ) != null,
TargetTypePrism.getInstanceOn( element ) != null,
ContextPrism.getInstanceOn( element ) != null );
}
/**
* @param parameters the parameters to filter
* @return the parameters from the given list that are considered 'source parameters'
*/
public static List<Parameter> getSourceParameters(List<Parameter> parameters) {
List<Parameter> sourceParameters = new ArrayList<Parameter>( parameters.size() );
for ( Parameter parameter : parameters ) {
if ( !parameter.isMappingTarget() && !parameter.isTargetType() && !parameter.isMappingContext() ) {
sourceParameters.add( parameter );
}
}
return sourceParameters;
}
/**
* @param parameters the parameters to filter
* @return the parameters from the given list that are marked as 'mapping context parameters'
*/
public static List<Parameter> getContextParameters(List<Parameter> parameters) {
List<Parameter> contextParameters = new ArrayList<Parameter>( parameters.size() );
for ( Parameter parameter : parameters ) {
if ( parameter.isMappingContext() ) {
contextParameters.add( parameter );
}
}
return contextParameters;
}
}

View File

@ -34,12 +34,15 @@ public class ParameterBinding {
private final String variableName;
private final boolean targetType;
private final boolean mappingTarget;
private final boolean mappingContext;
private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType) {
private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType,
boolean mappingContext) {
this.type = parameterType;
this.variableName = variableName;
this.targetType = targetType;
this.mappingTarget = mappingTarget;
this.mappingContext = mappingContext;
}
/**
@ -63,6 +66,13 @@ public class ParameterBinding {
return mappingTarget;
}
/**
* @return {@code true}, if the parameter being bound is a {@code @MappingContext} parameter.
*/
public boolean isMappingContext() {
return mappingContext;
}
/**
* @return the type of the parameter that is bound
*/
@ -87,7 +97,8 @@ public class ParameterBinding {
parameter.getType(),
parameter.getName(),
parameter.isMappingTarget(),
parameter.isTargetType() );
parameter.isTargetType(),
parameter.isMappingContext() );
}
public static List<ParameterBinding> fromParameters(List<Parameter> parameters) {
@ -103,7 +114,7 @@ public class ParameterBinding {
* @return a parameter binding representing a target type parameter
*/
public static ParameterBinding forTargetTypeBinding(Type classTypeOf) {
return new ParameterBinding( classTypeOf, null, false, true );
return new ParameterBinding( classTypeOf, null, false, true, false );
}
/**
@ -111,7 +122,7 @@ public class ParameterBinding {
* @return a parameter binding representing a mapping target parameter
*/
public static ParameterBinding forMappingTargetBinding(Type resultType) {
return new ParameterBinding( resultType, null, true, false );
return new ParameterBinding( resultType, null, true, false, false );
}
/**
@ -119,6 +130,6 @@ public class ParameterBinding {
* @return a parameter binding representing a mapping source type
*/
public static ParameterBinding forSourceTypeBinding(Type sourceType) {
return new ParameterBinding( sourceType, null, false, false );
return new ParameterBinding( sourceType, null, false, false, false );
}
}

View File

@ -36,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
@ -52,8 +53,6 @@ import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.internal.prism.MappingTargetPrism;
import org.mapstruct.ap.internal.prism.TargetTypePrism;
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
import org.mapstruct.ap.internal.util.Collections;
import org.mapstruct.ap.internal.util.accessor.Accessor;
@ -321,11 +320,7 @@ public class TypeFactory {
VariableElement parameter = varIt.next();
TypeMirror parameterType = typesIt.next();
result.add( new Parameter(
parameter.getSimpleName().toString(),
getType( parameterType ),
MappingTargetPrism.getInstanceOn( parameter ) != null,
TargetTypePrism.getInstanceOn( parameter ) != null ) );
result.add( Parameter.forElementAndType( parameter, getType( parameterType ) ) );
}
return result;

View File

@ -18,10 +18,8 @@
*/
package org.mapstruct.ap.internal.model.source;
import static org.mapstruct.ap.internal.util.Collections.first;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.lang.model.element.ExecutableElement;
@ -45,7 +43,10 @@ public class ForgedMethod implements Method {
private final ExecutableElement positionHintElement;
private final List<Type> thrownTypes;
private final MapperConfiguration mapperConfiguration;
private ForgedMethodHistory history;
private final ForgedMethodHistory history;
private final List<Parameter> sourceParameters;
private final List<Parameter> contextParameters;
/**
* Creates a new forged method with the given name.
@ -55,17 +56,11 @@ public class ForgedMethod implements Method {
* @param targetType the target type.
* @param mapperConfiguration the mapper configuration
* @param positionHintElement element used to for reference to the position in the source file.
* @param additionalParameters additional parameters to add to the forged method
*/
public ForgedMethod(String name, Type sourceType, Type targetType, MapperConfiguration mapperConfiguration,
ExecutableElement positionHintElement) {
String sourceParamName = Strings.decapitalize( sourceType.getName() );
String sourceParamSafeName = Strings.getSaveVariableName( sourceParamName );
this.parameters = Arrays.asList( new Parameter( sourceParamSafeName, sourceType ) );
this.returnType = targetType;
this.thrownTypes = new ArrayList<Type>();
this.name = Strings.sanitizeIdentifierName( name );
this.mapperConfiguration = mapperConfiguration;
this.positionHintElement = positionHintElement;
ExecutableElement positionHintElement, List<Parameter> additionalParameters) {
this( name, sourceType, targetType, mapperConfiguration, positionHintElement, additionalParameters, null );
}
/**
@ -76,13 +71,21 @@ public class ForgedMethod implements Method {
* @param targetType the target type.
* @param mapperConfiguration the mapper configuration
* @param positionHintElement element used to for reference to the position in the source file.
* @param additionalParameters additional parameters to add to the forged method
* @param history a parent forged method if this is a forged method within a forged method
*/
public ForgedMethod(String name, Type sourceType, Type targetType, MapperConfiguration mapperConfiguration,
ExecutableElement positionHintElement, ForgedMethodHistory history) {
ExecutableElement positionHintElement, List<Parameter> additionalParameters,
ForgedMethodHistory history) {
String sourceParamName = Strings.decapitalize( sourceType.getName() );
String sourceParamSafeName = Strings.getSaveVariableName( sourceParamName );
this.parameters = Arrays.asList( new Parameter( sourceParamSafeName, sourceType ) );
this.parameters = new ArrayList<Parameter>( 1 + additionalParameters.size() );
this.parameters.add( new Parameter( sourceParamSafeName, sourceType ) );
this.parameters.addAll( additionalParameters );
this.sourceParameters = Parameter.getSourceParameters( parameters );
this.contextParameters = Parameter.getContextParameters( parameters );
this.returnType = targetType;
this.thrownTypes = new ArrayList<Type>();
this.name = Strings.sanitizeIdentifierName( name );
@ -102,6 +105,11 @@ public class ForgedMethod implements Method {
this.thrownTypes = new ArrayList<Type>();
this.mapperConfiguration = forgedMethod.mapperConfiguration;
this.positionHintElement = forgedMethod.positionHintElement;
this.history = forgedMethod.history;
this.sourceParameters = Parameter.getSourceParameters( parameters );
this.contextParameters = Parameter.getContextParameters( parameters );
this.name = name;
}
@ -112,12 +120,19 @@ public class ForgedMethod implements Method {
return false;
}
if ( parameters.size() != 1 || sourceTypes.size() != 1 ) {
if ( parameters.size() != sourceTypes.size() ) {
return false;
}
if ( !first( sourceTypes ).equals( first( parameters ).getType() ) ) {
return false;
Iterator<Type> srcTypeIt = sourceTypes.iterator();
Iterator<Parameter> paramIt = parameters.iterator();
while ( srcTypeIt.hasNext() && paramIt.hasNext() ) {
Type sourceType = srcTypeIt.next();
Parameter param = paramIt.next();
if ( !sourceType.equals( param.getType() ) ) {
return false;
}
}
return true;
@ -140,7 +155,12 @@ public class ForgedMethod implements Method {
@Override
public List<Parameter> getSourceParameters() {
return parameters;
return sourceParameters;
}
@Override
public List<Parameter> getContextParameters() {
return contextParameters;
}
@Override

View File

@ -70,15 +70,23 @@ public interface Method {
List<Parameter> getParameters();
/**
* returns the list of 'true' source parameters excluding the parameter(s) that is designated as
* target by means of the target annotation {@link #getMappingTargetParameter() }.
* returns the list of 'true' source parameters excluding the parameter(s) that are designated as target, target
* type or context parameter.
*
* @return list of 'true' source parameters
*/
List<Parameter> getSourceParameters();
/**
* Returns the parameter designated as mapping target (if present) {@link org.mapstruct.MappingTarget }
* returns the list of mapping context parameters, i.e. those parameters that are annotated with
* {@link org.mapstruct.Context}.
*
* @return list of context parameters
*/
List<Parameter> getContextParameters();
/**
* Returns the parameter designated as mapping target (if present) {@link org.mapstruct.MappingTarget}
*
* @return mapping target parameter (when present) null otherwise.
*/

View File

@ -69,7 +69,8 @@ public class SourceMethod implements Method {
private final List<SourceMethod> prototypeMethods;
private final Type mapperToImplement;
private List<Parameter> sourceParameters;
private final List<Parameter> sourceParameters;
private final List<Parameter> contextParameters;
private List<String> parameterNames;
@ -100,9 +101,6 @@ public class SourceMethod implements Method {
private List<SourceMethod> prototypeMethods = Collections.emptyList();
private List<ValueMapping> valueMappings;
public Builder() {
}
public Builder setDeclaringMapper(Type declaringMapper) {
this.declaringMapper = declaringMapper;
return this;
@ -226,6 +224,9 @@ public class SourceMethod implements Method {
this.mappingOptions = mappingOptions;
this.sourceParameters = Parameter.getSourceParameters( parameters );
this.contextParameters = Parameter.getContextParameters( parameters );
this.mappingTargetParameter = determineMappingTargetParameter( parameters );
this.targetTypeParameter = determineTargetTypeParameter( parameters );
this.isObjectFactory = determineIfIsObjectFactory( executable );
@ -239,9 +240,11 @@ public class SourceMethod implements Method {
private boolean determineIfIsObjectFactory(ExecutableElement executable) {
boolean hasFactoryAnnotation = ObjectFactoryPrism.getInstanceOn( executable ) != null;
boolean isFactoryWithTargeTypeAnnotation = getTargetTypeParameter() != null && getSourceParameters().isEmpty();
return !returnType.isVoid()
&& ( hasFactoryAnnotation || isFactoryWithTargeTypeAnnotation || parameters.isEmpty() );
boolean hasNoSourceParameters = getSourceParameters().isEmpty();
boolean hasNoMappingTargetParam = getMappingTargetParameter() == null;
return !isLifecycleCallbackMethod() && !returnType.isVoid()
&& hasNoMappingTargetParam
&& ( hasFactoryAnnotation || hasNoSourceParameters );
}
private Parameter determineMappingTargetParameter(Iterable<Parameter> parameters) {
@ -264,9 +267,6 @@ public class SourceMethod implements Method {
return null;
}
/**
* {@inheritDoc} {@link Method}
*/
@Override
public Type getDeclaringMapper() {
return declaringMapper;
@ -277,40 +277,26 @@ public class SourceMethod implements Method {
return executable;
}
/**
* {@inheritDoc} {@link Method}
*/
@Override
public String getName() {
return executable.getSimpleName().toString();
}
/**
* {@inheritDoc} {@link Method}
*/
@Override
public List<Parameter> getParameters() {
return parameters;
}
/**
* {@inheritDoc} {@link Method}
*/
@Override
public List<Parameter> getSourceParameters() {
if ( sourceParameters == null ) {
sourceParameters = new ArrayList<Parameter>();
for ( Parameter parameter : parameters ) {
if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) {
sourceParameters.add( parameter );
}
}
}
return sourceParameters;
}
@Override
public List<Parameter> getContextParameters() {
return contextParameters;
}
@Override
public List<String> getParameterNames() {
if ( parameterNames == null ) {
@ -331,9 +317,6 @@ public class SourceMethod implements Method {
return mappingTargetParameter != null ? mappingTargetParameter.getType() : returnType;
}
/**
* {@inheritDoc} {@link Method}
*/
@Override
public Type getReturnType() {
return returnType;
@ -544,9 +527,6 @@ public class SourceMethod implements Method {
return declaringMapper == null && executable.getModifiers().contains( Modifier.ABSTRACT );
}
/**
* {@inheritDoc} {@link Method}
*/
@Override
public boolean matches(List<Type> sourceTypes, Type targetType) {
MethodMatcher matcher = new MethodMatcher( typeUtils, typeFactory, this );
@ -614,5 +594,4 @@ public class SourceMethod implements Method {
public boolean isUpdateMethod() {
return getMappingTargetParameter() != null;
}
}

View File

@ -103,6 +103,11 @@ public abstract class BuiltInMethod implements Method {
return getParameters();
}
@Override
public List<Parameter> getContextParameters() {
return Collections.emptyList();
}
/**
* {@inheritDoc}
* <p>

View File

@ -62,7 +62,7 @@ public class TypeSelector implements MethodSelector {
availableBindings = getAvailableParameterBindingsFromMethod( mappingMethod );
}
else {
availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType );
availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType, mappingMethod );
}
for ( SelectedMethod<T> method : methods ) {
@ -91,7 +91,7 @@ public class TypeSelector implements MethodSelector {
}
private List<ParameterBinding> getAvailableParameterBindingsFromSourceTypes(List<Type> sourceTypes,
Type targetType) {
Type targetType, Method mappingMethod) {
List<ParameterBinding> availableParams = new ArrayList<ParameterBinding>( sourceTypes.size() + 2 );
@ -101,6 +101,12 @@ public class TypeSelector implements MethodSelector {
availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) );
}
for ( Parameter param : mappingMethod.getParameters() ) {
if ( param.isMappingContext() ) {
availableParams.add( ParameterBinding.fromParameter( param ) );
}
}
return availableParams;
}
@ -185,7 +191,8 @@ public class TypeSelector implements MethodSelector {
for ( ParameterBinding candidate : candidateParameters ) {
if ( parameter.isTargetType() == candidate.isTargetType()
&& parameter.isMappingTarget() == candidate.isMappingTarget() ) {
&& parameter.isMappingTarget() == candidate.isMappingTarget()
&& parameter.isMappingContext() == candidate.isMappingContext() ) {
result.add( candidate );
}
}

View File

@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlElementRef;
import org.mapstruct.AfterMapping;
import org.mapstruct.BeanMapping;
import org.mapstruct.BeforeMapping;
import org.mapstruct.Context;
import org.mapstruct.DecoratedWith;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
@ -69,6 +70,7 @@ import net.java.dev.hickory.prism.GeneratePrisms;
@GeneratePrism(value = BeforeMapping.class, publicAccess = true),
@GeneratePrism(value = ValueMapping.class, publicAccess = true),
@GeneratePrism(value = ValueMappings.class, publicAccess = true),
@GeneratePrism(value = Context.class, publicAccess = true),
// external types
@GeneratePrism(value = XmlElementDecl.class, publicAccess = true),

View File

@ -23,8 +23,10 @@ import static org.mapstruct.ap.internal.util.Executables.getAllEnclosedExecutabl
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
@ -215,7 +217,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
List<SourceMethod> prototypeMethods ) {
Type returnType = typeFactory.getReturnType( methodType );
List<Type> exceptionTypes = typeFactory.getThrownTypes( methodType );
List<Parameter> sourceParameters = extractSourceParameters( parameters );
List<Parameter> sourceParameters = Parameter.getSourceParameters( parameters );
List<Parameter> contextParameters = Parameter.getContextParameters( parameters );
Parameter targetParameter = extractTargetParameter( parameters );
Type resultType = selectResultType( returnType, targetParameter );
@ -223,6 +226,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
method,
sourceParameters,
targetParameter,
contextParameters,
resultType,
returnType,
containsTargetTypeParameter
@ -316,20 +320,17 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
if ( param.isMappingTarget() ) {
targetParameters++;
}
if ( param.isTargetType() ) {
else if ( param.isTargetType() ) {
targetTypeParameters++;
}
if ( !param.isMappingTarget() && !param.isTargetType() ) {
else if ( !param.isMappingContext() ) {
validSourceParameters++;
}
}
return validSourceParameters == sourceParamCount
&& targetParameters <= targetParamCount
&& targetTypeParameters <= 1
&& parameters.size() == validSourceParameters + targetParameters + targetTypeParameters;
&& targetTypeParameters <= 1;
}
private Parameter extractTargetParameter(List<Parameter> parameters) {
@ -342,17 +343,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return null;
}
private List<Parameter> extractSourceParameters(List<Parameter> parameters) {
List<Parameter> sourceParameters = new ArrayList<Parameter>( parameters.size() );
for ( Parameter param : parameters ) {
if ( !param.isMappingTarget() ) {
sourceParameters.add( param );
}
}
return sourceParameters;
}
private Type selectResultType(Type returnType, Parameter targetParameter) {
if ( null != targetParameter ) {
return targetParameter.getType();
@ -363,14 +353,16 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
}
private boolean checkParameterAndReturnType(ExecutableElement method, List<Parameter> sourceParameters,
Parameter targetParameter, Type resultType, Type returnType,
Parameter targetParameter, List<Parameter> contextParameters,
Type resultType, Type returnType,
boolean containsTargetTypeParameter) {
if ( sourceParameters.isEmpty() ) {
messager.printMessage( method, Message.RETRIEVAL_NO_INPUT_ARGS );
return false;
}
if ( targetParameter != null && ( sourceParameters.size() + 1 != method.getParameters().size() ) ) {
if ( targetParameter != null
&& ( sourceParameters.size() + contextParameters.size() + 1 != method.getParameters().size() ) ) {
messager.printMessage( method, Message.RETRIEVAL_DUPLICATE_MAPPING_TARGETS );
return false;
}
@ -393,6 +385,14 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
}
}
Set<Type> contextParameterTypes = new HashSet<Type>();
for ( Parameter contextParameter : contextParameters ) {
if ( !contextParameterTypes.add( contextParameter.getType() ) ) {
messager.printMessage( method, Message.RETRIEVAL_CONTEXT_PARAMS_WITH_SAME_TYPE );
return false;
}
}
if ( returnType.isTypeVar() || resultType.isTypeVar() ) {
messager.printMessage( method, Message.RETRIEVAL_TYPE_VAR_RESULT );
return false;

View File

@ -98,6 +98,7 @@ public enum Message {
RETRIEVAL_TYPE_VAR_RESULT( "Can't generate mapping method for a generic type variable target." ),
RETRIEVAL_WILDCARD_SUPER_BOUND_SOURCE( "Can't generate mapping method for a wildcard super bound source." ),
RETRIEVAL_WILDCARD_EXTENDS_BOUND_RESULT( "Can't generate mapping method for a wildcard extends bound result." ),
RETRIEVAL_CONTEXT_PARAMS_WITH_SAME_TYPE( "The types of @Context parameters must be unique." ),
INHERITCONFIGURATION_BOTH( "Method cannot be annotated with both a @InheritConfiguration and @InheritInverseConfiguration." ),
INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ),

View File

@ -37,7 +37,9 @@
<#-- a class is passed on for casting, see @TargetType -->
<@includeModel object=ext.targetType raw=true/>.class<#t>
<#elseif param.mappingTarget>
${ext.targetBeanName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}</#if><#t>
${ext.targetBeanName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}</#if><#t>
<#elseif param.mappingContext>
${param.variableName}<#t>
<#elseif assignment??>
<@_assignment/><#t>
<#else>

View File

@ -21,17 +21,17 @@ package org.mapstruct.ap.test.callbacks.returning;
/**
* @author Pascal Grün
*/
public class AttributeDTO {
private NodeDTO node;
public class AttributeDto {
private NodeDto node;
private String name;
private String value;
public NodeDTO getNode() {
public NodeDto getNode() {
return node;
}
public void setNode(NodeDTO node) {
public void setNode(NodeDto node) {
this.node = node;
}
@ -53,6 +53,6 @@ public class AttributeDTO {
@Override
public String toString() {
return "AttributeDTO [name=" + name + ", value=" + value + "]";
return "AttributeDto [name=" + name + ", value=" + value + "]";
}
}

View File

@ -36,7 +36,7 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
* @author Pascal Grün
*/
@IssueKey( "469" )
@WithClasses( { Attribute.class, AttributeDTO.class, Node.class, NodeDTO.class, NodeMapperDefault.class,
@WithClasses( { Attribute.class, AttributeDto.class, Node.class, NodeDto.class, NodeMapperDefault.class,
NodeMapperWithContext.class, NodeMapperContext.class, Number.class, NumberMapperDefault.class,
NumberMapperContext.class, NumberMapperWithContext.class } )
@RunWith( AnnotationProcessorTestRunner.class )
@ -44,13 +44,13 @@ public class CallbacksWithReturnValuesTest {
@Test( expected = StackOverflowError.class )
public void mappingWithDefaultHandlingRaisesStackOverflowError() {
Node root = buildNodes();
NodeMapperDefault.INSTANCE.nodeToNodeDTO( root );
NodeMapperDefault.INSTANCE.nodeToNodeDto( root );
}
@Test( expected = StackOverflowError.class )
public void updatingWithDefaultHandlingRaisesStackOverflowError() {
Node root = buildNodes();
NodeMapperDefault.INSTANCE.nodeToNodeDTO( root, new NodeDTO() );
NodeMapperDefault.INSTANCE.nodeToNodeDto( root, new NodeDto() );
}
@Test
@ -66,8 +66,8 @@ public class CallbacksWithReturnValuesTest {
NodeMapperContext.addContextListener( contextListener );
try {
Node root = buildNodes();
NodeDTO rootDTO = NodeMapperWithContext.INSTANCE.nodeToNodeDTO( root );
assertThat( rootDTO ).isNotNull();
NodeDto rootDto = NodeMapperWithContext.INSTANCE.nodeToNodeDto( root );
assertThat( rootDto ).isNotNull();
assertThat( contextLevel.get() ).isEqualTo( Integer.valueOf( 1 ) );
}
finally {

View File

@ -23,19 +23,18 @@ import java.util.List;
/**
* @author Pascal Grün
*/
public class NodeDTO {
private NodeDTO parent;
public class NodeDto {
private NodeDto parent;
private String name;
private List<NodeDto> children;
private List<AttributeDto> attributes;
private List<NodeDTO> children;
private List<AttributeDTO> attributes;
public NodeDTO getParent() {
public NodeDto getParent() {
return parent;
}
public void setParent(NodeDTO parent) {
public void setParent(NodeDto parent) {
this.parent = parent;
}
@ -47,24 +46,24 @@ public class NodeDTO {
this.name = name;
}
public List<NodeDTO> getChildren() {
public List<NodeDto> getChildren() {
return children;
}
public void setChildren(List<NodeDTO> children) {
public void setChildren(List<NodeDto> children) {
this.children = children;
}
public List<AttributeDTO> getAttributes() {
public List<AttributeDto> getAttributes() {
return attributes;
}
public void setAttributes(List<AttributeDTO> attributes) {
public void setAttributes(List<AttributeDto> attributes) {
this.attributes = attributes;
}
@Override
public String toString() {
return "NodeDTO [name=" + name + "]";
return "NodeDto [name=" + name + "]";
}
}

View File

@ -29,11 +29,11 @@ import org.mapstruct.factory.Mappers;
public abstract class NodeMapperDefault {
public static final NodeMapperDefault INSTANCE = Mappers.getMapper( NodeMapperDefault.class );
public abstract NodeDTO nodeToNodeDTO(Node node);
public abstract NodeDto nodeToNodeDto(Node node);
public abstract void nodeToNodeDTO(Node node, @MappingTarget NodeDTO nodeDto);
public abstract void nodeToNodeDto(Node node, @MappingTarget NodeDto nodeDto);
protected abstract AttributeDTO attributeToAttributeDTO(Attribute attribute);
protected abstract AttributeDto attributeToAttributeDto(Attribute attribute);
protected abstract void attributeToAttributeDTO(Attribute attribute, @MappingTarget AttributeDTO nodeDto);
protected abstract void attributeToAttributeDto(Attribute attribute, @MappingTarget AttributeDto nodeDto);
}

View File

@ -29,11 +29,11 @@ import org.mapstruct.factory.Mappers;
public abstract class NodeMapperWithContext {
public static final NodeMapperWithContext INSTANCE = Mappers.getMapper( NodeMapperWithContext.class );
public abstract NodeDTO nodeToNodeDTO(Node node);
public abstract NodeDto nodeToNodeDto(Node node);
public abstract void nodeToNodeDTO(Node node, @MappingTarget NodeDTO nodeDto);
public abstract void nodeToNodeDto(Node node, @MappingTarget NodeDto nodeDto);
protected abstract AttributeDTO attributeToAttributeDTO(Attribute attribute);
protected abstract AttributeDto attributeToAttributeDto(Attribute attribute);
protected abstract void attributeToAttributeDTO(Attribute attribute, @MappingTarget AttributeDTO nodeDto);
protected abstract void attributeToAttributeDto(Attribute attribute, @MappingTarget AttributeDto nodeDto);
}

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2016 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.context;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
/**
* @author Andreas Gudian
*/
@Mapper(uses = CycleContextLifecycleMethods.class)
public interface AutomappingNodeMapperWithContext {
AutomappingNodeMapperWithContext INSTANCE =
Mappers.getMapper( AutomappingNodeMapperWithContext.class );
NodeDto nodeToNodeDto(Node node, @Context CycleContext cycleContext, @Context FactoryContext factoryContext);
void nodeToNodeDto(Node node, @MappingTarget NodeDto nodeDto, @Context CycleContext cycleContext,
@Context FactoryContext factoryContext);
}

View File

@ -0,0 +1,60 @@
/**
* Copyright 2012-2016 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.context;
import javax.tools.Diagnostic.Kind;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.Context;
import org.mapstruct.ap.test.context.erroneous.ErroneousNodeMapperWithNonUniqueContextTypes;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
/**
* Tests the erroneous usage of the {@link Context} annotation in the following situations:
* <ul>
* <li>using the the same context parameter type twice in the same method
* </ul>
*
* @author Andreas Gudian
*/
@IssueKey("975")
@WithClasses({
Node.class,
NodeDto.class,
CycleContext.class })
@RunWith(AnnotationProcessorTestRunner.class)
public class ContextParameterErroneousTest {
@Test
@WithClasses(ErroneousNodeMapperWithNonUniqueContextTypes.class)
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
diagnostics = @Diagnostic(
kind = Kind.ERROR,
line = 33,
type = ErroneousNodeMapperWithNonUniqueContextTypes.class,
messageRegExp = "The types of @Context parameters must be unique"))
public void reportsNonUniqueContextParamType() {
}
}

View File

@ -0,0 +1,117 @@
/**
* Copyright 2012-2016 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.context;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.BeforeMapping;
import org.mapstruct.Context;
import org.mapstruct.ObjectFactory;
import org.mapstruct.ap.test.context.Node.Attribute;
import org.mapstruct.ap.test.context.NodeDto.AttributeDto;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
/**
* Tests the usage of the {@link Context} annotation in the following situations:
* <ul>
* <li>passing the parameter to property mapping methods (create and update)
* <li>passing the parameter to forged iterable methods
* <li>passing the parameter to forged bean mapping methods
* <li>passing the parameter to factory methods (with and without {@link ObjectFactory})
* <li>passing the parameter to lifecycle methods (in this case, {@link BeforeMapping}
* <li>passing multiple parameters, with varied order of context params and mapping source params
* </ul>
*
* @author Andreas Gudian
*/
@IssueKey("975")
@WithClasses({
Node.class,
NodeDto.class,
NodeMapperWithContext.class,
AutomappingNodeMapperWithContext.class,
CycleContext.class,
FactoryContext.class,
CycleContextLifecycleMethods.class })
@RunWith(AnnotationProcessorTestRunner.class)
public class ContextParameterTest {
private static final int MAGIC_NUMBER_OFFSET = 10;
@Test
public void mappingWithContextCorrectlyResolvesCycles() {
Node root = buildNodes();
NodeDto rootDto =
NodeMapperWithContext.INSTANCE.nodeToNodeDto( new FactoryContext( 0, 10 ), root, new CycleContext() );
assertResult( rootDto );
NodeDto updated = new NodeDto( 0 );
NodeMapperWithContext.INSTANCE.nodeToNodeDto( new FactoryContext( 1, 10 ), root, updated, new CycleContext() );
assertResult( updated );
}
@Test
public void automappingWithContextCorrectlyResolvesCycles() {
Node root = buildNodes();
NodeDto rootDto = AutomappingNodeMapperWithContext.INSTANCE
.nodeToNodeDto( root, new CycleContext(), new FactoryContext( 0, MAGIC_NUMBER_OFFSET ) );
assertResult( rootDto );
NodeDto updated = new NodeDto( 0 );
AutomappingNodeMapperWithContext.INSTANCE
.nodeToNodeDto( root, updated, new CycleContext(), new FactoryContext( 1, 10 ) );
assertResult( updated );
}
private void assertResult(NodeDto rootDto) {
assertThat( rootDto ).isNotNull();
assertThat( rootDto.getId() ).isEqualTo( 0 );
AttributeDto rootAttribute = rootDto.getAttributes().get( 0 );
assertThat( rootAttribute.getNode() ).isSameAs( rootDto );
assertThat( rootAttribute.getMagicNumber() ).isEqualTo( 1 + MAGIC_NUMBER_OFFSET );
assertThat( rootDto.getChildren() ).hasSize( 1 );
NodeDto node1 = rootDto.getChildren().get( 0 );
assertThat( node1.getParent() ).isSameAs( rootDto );
assertThat( node1.getId() ).isEqualTo( 1 );
AttributeDto node1Attribute = node1.getAttributes().get( 0 );
assertThat( node1Attribute.getNode() ).isSameAs( node1 );
assertThat( node1Attribute.getMagicNumber() ).isEqualTo( 2 + MAGIC_NUMBER_OFFSET );
}
private static Node buildNodes() {
Node root = new Node( "root" );
root.addAttribute( new Attribute( "name", "root", 1 ) );
Node node1 = new Node( "node1" );
node1.addAttribute( new Attribute( "name", "node1", 2 ) );
root.addChild( node1 );
return root;
}
}

View File

@ -0,0 +1,42 @@
/**
* Copyright 2012-2016 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.context;
import java.util.IdentityHashMap;
import java.util.Map;
import org.mapstruct.Context;
/**
* A type to be used as {@link Context} parameter to track cycles in graphs
*
* @author Andreas Gudian
*/
public class CycleContext {
private Map<Object, Object> knownInstances = new IdentityHashMap<Object, Object>();
@SuppressWarnings("unchecked")
public <T> T getMappedInstance(Object source, Class<T> targetType) {
return (T) knownInstances.get( source );
}
public void storeMappedInstance(Object source, Object target) {
knownInstances.put( source, target );
}
}

View File

@ -0,0 +1,52 @@
/**
* Copyright 2012-2016 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.context;
import org.mapstruct.BeforeMapping;
import org.mapstruct.Context;
import org.mapstruct.MappingTarget;
import org.mapstruct.ObjectFactory;
import org.mapstruct.TargetType;
import org.mapstruct.ap.test.context.Node.Attribute;
import org.mapstruct.ap.test.context.NodeDto.AttributeDto;
/**
* @author Andreas Gudian
*/
public class CycleContextLifecycleMethods {
public NodeDto createNodeDto(@Context FactoryContext context) {
return context.createNode();
}
@ObjectFactory
public AttributeDto createAttributeDto(Attribute source, @Context FactoryContext context) {
return context.createAttributeDto( source );
}
@BeforeMapping
public <T> T getInstance(Object source, @TargetType Class<T> type, @Context CycleContext cycleContext) {
return cycleContext.getMappedInstance( source, type );
}
@BeforeMapping
public void setInstance(Object source, @MappingTarget Object target, @Context CycleContext cycleContext) {
cycleContext.storeMappedInstance( source, target );
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright 2012-2016 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.context;
import org.mapstruct.Context;
import org.mapstruct.ap.test.context.Node.Attribute;
import org.mapstruct.ap.test.context.NodeDto.AttributeDto;
/**
* A type to be used as {@link Context} parameter to create NodeDto and AttributeDto instances
*
* @author Andreas Gudian
*/
public class FactoryContext {
private int nodeCounter;
private int attributeMagicNumberOffset;
public FactoryContext(int initialCounter, int attributeMaticNumberOffset) {
this.nodeCounter = initialCounter;
this.attributeMagicNumberOffset = attributeMaticNumberOffset;
}
public NodeDto createNode() {
return new NodeDto( nodeCounter++ );
}
public AttributeDto createAttributeDto(Attribute source) {
return new AttributeDto( source.getMagicNumber() + attributeMagicNumberOffset );
}
}

View File

@ -0,0 +1,100 @@
/**
* Copyright 2012-2016 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.context;
import java.util.ArrayList;
import java.util.List;
/**
* @author Andreas Gudian
*/
public class Node {
private Node parent;
private String name;
private List<Node> children;
private List<Attribute> attributes;
public Node(String name) {
this.name = name;
this.children = new ArrayList<Node>();
this.attributes = new ArrayList<Attribute>();
}
public Node getParent() {
return parent;
}
public String getName() {
return name;
}
public List<Node> getChildren() {
return children;
}
public void addChild(Node node) {
children.add( node );
node.parent = this;
}
public List<Attribute> getAttributes() {
return attributes;
}
public void addAttribute(Attribute attribute) {
attributes.add( attribute );
attribute.setNode( this );
}
public static class Attribute {
private Node node;
private String name;
private String value;
private int magicNumber;
public Attribute(String name, String value, int magicNumber) {
this.name = name;
this.value = value;
this.magicNumber = magicNumber;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public int getMagicNumber() {
return magicNumber;
}
}
}

View File

@ -0,0 +1,114 @@
/**
* Copyright 2012-2016 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.context;
import java.util.List;
/**
* @author Andreas Gudian
*/
public class NodeDto {
private NodeDto parent;
private int id;
private String name;
private List<NodeDto> children;
private List<AttributeDto> attributes;
public NodeDto(int id) {
this.id = id;
}
public NodeDto getParent() {
return parent;
}
public void setParent(NodeDto parent) {
this.parent = parent;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<NodeDto> getChildren() {
return children;
}
public void setChildren(List<NodeDto> children) {
this.children = children;
}
public List<AttributeDto> getAttributes() {
return attributes;
}
public void setAttributes(List<AttributeDto> attributes) {
this.attributes = attributes;
}
public static class AttributeDto {
private NodeDto node;
private String name;
private String value;
private int magicNumber;
public AttributeDto(int magicNumber) {
this.magicNumber = magicNumber;
}
public NodeDto getNode() {
return node;
}
public void setNode(NodeDto node) {
this.node = node;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public int getMagicNumber() {
return magicNumber;
}
}
}

View File

@ -0,0 +1,45 @@
/**
* Copyright 2012-2016 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.context;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.ap.test.context.Node.Attribute;
import org.mapstruct.ap.test.context.NodeDto.AttributeDto;
import org.mapstruct.factory.Mappers;
/**
* @author Andreas Gudian
*/
@Mapper(uses = CycleContextLifecycleMethods.class)
public interface NodeMapperWithContext {
NodeMapperWithContext INSTANCE = Mappers.getMapper( NodeMapperWithContext.class );
NodeDto nodeToNodeDto(@Context FactoryContext factoryContext, Node node, @Context CycleContext cycleContext);
void nodeToNodeDto(@Context FactoryContext factoryContext, Node node, @MappingTarget NodeDto nodeDto,
@Context CycleContext cycleContext);
AttributeDto attributeToAttributeDto(Attribute attribute, @Context CycleContext cycleContext,
@Context FactoryContext factoryContext);
void attributeToAttributeDto(Attribute attribute, @MappingTarget AttributeDto nodeDto,
@Context CycleContext cycleContext, @Context FactoryContext factoryContext);
}

View File

@ -0,0 +1,34 @@
/**
* Copyright 2012-2016 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.context.erroneous;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.context.CycleContext;
import org.mapstruct.ap.test.context.Node;
import org.mapstruct.ap.test.context.NodeDto;
/**
* @author Andreas Gudian
*/
@Mapper
public interface ErroneousNodeMapperWithNonUniqueContextTypes {
NodeDto nodeToNodeDto(Node node, @Context CycleContext cycleContext, @Context CycleContext otherCycleContext);
}