diff --git a/core-common/src/main/java/org/mapstruct/Context.java b/core-common/src/main/java/org/mapstruct/Context.java
new file mode 100644
index 000000000..b066f0ac9
--- /dev/null
+++ b/core-common/src/main/java/org/mapstruct/Context.java
@@ -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 mapping context. 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.
+ *
+ * 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.
+ *
+ * Example:
+ *
+ *
+ *
+ * // multiple @Context parameters can be added
+ * public abstract CarDto toCar(Car car, @Context VehicleRegistration context, @Context Locale localeToUse);
+ *
+ * protected OwnerManualDto translateOwnerManual(OwnerManual ownerManual, @Context Locale locale) {
+ * // manually implemented logic to translate the OwnerManual with the given Locale
+ * }
+ *
+ * @BeforeMapping
+ * protected void registerVehicle(Vehicle mappedVehicle, @Context VehicleRegistration context) {
+ * context.register( mappedVehicle );
+ * }
+ *
+ * @BeforeMapping
+ * protected void logMappedVehicle(Vehicle mappedVehicle) {
+ * // do something with the vehicle
+ * }
+ *
+ * @BeforeMapping
+ * protected void notCalled(Vehicle mappedVehicle, @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;
+ * }
+ *
+ *
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java
index 2dd04235d..f59a577ec 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java
@@ -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;
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java
index 35c8f81d2..4efece5ff 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java
@@ -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;
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java
index 26024250f..4c50ab320 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java
@@ -43,7 +43,8 @@ import org.mapstruct.ap.internal.model.source.Method;
public abstract class MappingMethod extends ModelElement {
private final String name;
- private List parameters;
+ private final List parameters;
+ private final List sourceParameters;
private final Type returnType;
private final Parameter targetParameter;
private final Accessibility accessibility;
@@ -77,6 +78,7 @@ public abstract class MappingMethod extends ModelElement {
List 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 getSourceParameters() {
- List sourceParameters = new ArrayList();
-
- for ( Parameter parameter : parameters ) {
- if ( !parameter.isMappingTarget() ) {
- sourceParameters.add( parameter );
- }
- }
-
return sourceParameters;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java
index 1a20414d6..007cda462 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java
@@ -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;
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
index 4192c5854..832693cf8 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
@@ -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 )
);
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java
index 76cffe221..2248fe34f 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java
@@ -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 getSourceParameters(List parameters) {
+ List sourceParameters = new ArrayList( 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 getContextParameters(List parameters) {
+ List contextParameters = new ArrayList( parameters.size() );
+
+ for ( Parameter parameter : parameters ) {
+ if ( parameter.isMappingContext() ) {
+ contextParameters.add( parameter );
+ }
+ }
+
+ return contextParameters;
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java
index 0b3382a9d..a435fbcf4 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java
@@ -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 fromParameters(List 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 );
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java
index db5674b21..34fc1cbac 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java
@@ -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;
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ForgedMethod.java
index 2202f9671..1a6a7e629 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ForgedMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ForgedMethod.java
@@ -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 thrownTypes;
private final MapperConfiguration mapperConfiguration;
- private ForgedMethodHistory history;
+ private final ForgedMethodHistory history;
+
+ private final List sourceParameters;
+ private final List 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();
- this.name = Strings.sanitizeIdentifierName( name );
- this.mapperConfiguration = mapperConfiguration;
- this.positionHintElement = positionHintElement;
+ ExecutableElement positionHintElement, List 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 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( 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();
this.name = Strings.sanitizeIdentifierName( name );
@@ -102,6 +105,11 @@ public class ForgedMethod implements Method {
this.thrownTypes = new ArrayList();
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 srcTypeIt = sourceTypes.iterator();
+ Iterator 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 getSourceParameters() {
- return parameters;
+ return sourceParameters;
+ }
+
+ @Override
+ public List getContextParameters() {
+ return contextParameters;
}
@Override
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java
index f292de91c..9ecbdff5e 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java
@@ -70,15 +70,23 @@ public interface Method {
List 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 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 getContextParameters();
+
+ /**
+ * Returns the parameter designated as mapping target (if present) {@link org.mapstruct.MappingTarget}
*
* @return mapping target parameter (when present) null otherwise.
*/
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java
index 13becab3f..5bc711ade 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java
@@ -69,7 +69,8 @@ public class SourceMethod implements Method {
private final List prototypeMethods;
private final Type mapperToImplement;
- private List sourceParameters;
+ private final List sourceParameters;
+ private final List contextParameters;
private List parameterNames;
@@ -100,9 +101,6 @@ public class SourceMethod implements Method {
private List prototypeMethods = Collections.emptyList();
private List 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 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 getParameters() {
return parameters;
}
- /**
- * {@inheritDoc} {@link Method}
- */
@Override
public List getSourceParameters() {
- if ( sourceParameters == null ) {
- sourceParameters = new ArrayList();
-
- for ( Parameter parameter : parameters ) {
- if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) {
- sourceParameters.add( parameter );
- }
- }
- }
-
return sourceParameters;
}
+ @Override
+ public List getContextParameters() {
+ return contextParameters;
+ }
+
@Override
public List 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 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;
}
-
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java
index a8aaa43c5..578a2519b 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java
@@ -103,6 +103,11 @@ public abstract class BuiltInMethod implements Method {
return getParameters();
}
+ @Override
+ public List getContextParameters() {
+ return Collections.emptyList();
+ }
+
/**
* {@inheritDoc}
*
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java
index 5ca492e65..99d212e0f 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java
@@ -62,7 +62,7 @@ public class TypeSelector implements MethodSelector {
availableBindings = getAvailableParameterBindingsFromMethod( mappingMethod );
}
else {
- availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType );
+ availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType, mappingMethod );
}
for ( SelectedMethod method : methods ) {
@@ -91,7 +91,7 @@ public class TypeSelector implements MethodSelector {
}
private List getAvailableParameterBindingsFromSourceTypes(List sourceTypes,
- Type targetType) {
+ Type targetType, Method mappingMethod) {
List availableParams = new ArrayList( 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 );
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java
index 47336e8be..a681896f5 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java
@@ -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),
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java
index 95f7a33ab..ff309d4c0 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java
@@ -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 prototypeMethods ) {
Type returnType = typeFactory.getReturnType( methodType );
List exceptionTypes = typeFactory.getThrownTypes( methodType );
- List sourceParameters = extractSourceParameters( parameters );
+ List sourceParameters = Parameter.getSourceParameters( parameters );
+ List contextParameters = Parameter.getContextParameters( parameters );
Parameter targetParameter = extractTargetParameter( parameters );
Type resultType = selectResultType( returnType, targetParameter );
@@ -223,6 +226,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters) {
@@ -342,17 +343,6 @@ public class MethodRetrievalProcessor implements ModelElementProcessor extractSourceParameters(List parameters) {
- List sourceParameters = new ArrayList( 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 sourceParameters,
- Parameter targetParameter, Type resultType, Type returnType,
+ Parameter targetParameter, List 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 contextParameterTypes = new HashSet();
+ 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;
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
index f1fe92861..691a513cb 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
@@ -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." ),
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl
index f098c9323..ea707f938 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl
@@ -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>
diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/AttributeDTO.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/AttributeDto.java
similarity index 87%
rename from processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/AttributeDTO.java
rename to processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/AttributeDto.java
index db2821cce..ae6588990 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/AttributeDTO.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/AttributeDto.java
@@ -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 + "]";
}
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java
index f609bf31e..f0cb5e039 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java
@@ -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 {
diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeDTO.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeDto.java
similarity index 74%
rename from processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeDTO.java
rename to processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeDto.java
index 97339009a..3f74866a0 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeDTO.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeDto.java
@@ -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 children;
+ private List attributes;
- private List children;
- private List 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 getChildren() {
+ public List getChildren() {
return children;
}
- public void setChildren(List children) {
+ public void setChildren(List children) {
this.children = children;
}
- public List getAttributes() {
+ public List getAttributes() {
return attributes;
}
- public void setAttributes(List attributes) {
+ public void setAttributes(List attributes) {
this.attributes = attributes;
}
@Override
public String toString() {
- return "NodeDTO [name=" + name + "]";
+ return "NodeDto [name=" + name + "]";
}
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperDefault.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperDefault.java
index 88be582b5..7cf98d527 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperDefault.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperDefault.java
@@ -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);
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperWithContext.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperWithContext.java
index 6ac7bdda0..391930fd6 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperWithContext.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperWithContext.java
@@ -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);
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/AutomappingNodeMapperWithContext.java b/processor/src/test/java/org/mapstruct/ap/test/context/AutomappingNodeMapperWithContext.java
new file mode 100644
index 000000000..19ef75a74
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/context/AutomappingNodeMapperWithContext.java
@@ -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);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java
new file mode 100644
index 000000000..654d54807
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java
@@ -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:
+ *
+ *
using the the same context parameter type twice in the same method
+ *
+ *
+ * @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() {
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterTest.java b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterTest.java
new file mode 100644
index 000000000..4e0721ca8
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterTest.java
@@ -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:
+ *
+ *
passing the parameter to property mapping methods (create and update)
+ *
passing the parameter to forged iterable methods
+ *
passing the parameter to forged bean mapping methods
+ *
passing the parameter to factory methods (with and without {@link ObjectFactory})
+ *
passing the parameter to lifecycle methods (in this case, {@link BeforeMapping}
+ *
passing multiple parameters, with varied order of context params and mapping source params
+ *
+ *
+ * @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;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/CycleContext.java b/processor/src/test/java/org/mapstruct/ap/test/context/CycleContext.java
new file mode 100644
index 000000000..842f42a4f
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/context/CycleContext.java
@@ -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