mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#469 Consider return values of BeforeMapping/AfterMapping methods
This allows to handle cycles in object graphs, as demonstrated in the test case CallbacksWithReturnValuesTest
This commit is contained in:
parent
746f49fe3d
commit
075e763556
@ -2083,7 +2083,9 @@ public class VehicleMapperImpl extends VehicleMapper {
|
||||
if ( car == null ) {
|
||||
return null;
|
||||
}
|
||||
// ...
|
||||
|
||||
CarDto carDto = new CarDto();
|
||||
// attributes mapping ...
|
||||
|
||||
fillTank( car, carDto );
|
||||
|
||||
@ -2093,14 +2095,60 @@ public class VehicleMapperImpl extends VehicleMapper {
|
||||
----
|
||||
====
|
||||
|
||||
Only methods with return type `void` may be annotated with `@BeforeMapping` or `@AfterMapping`. The methods may or may not have parameters.
|
||||
|
||||
If the `@BeforeMapping` / `@AfterMapping` method has parameters, the method invocation is only generated if all parameters can be *assigned* by the source or target parameters of the mapping method:
|
||||
|
||||
* A parameter annotated with `@MappingTarget` is populated with the target instance of the mapping.
|
||||
* A parameter annotated with `@TargetType` is populated with the target type of the mapping.
|
||||
* Any other parameter is populated with a source parameter of the mapping, whereas each source parameter is used once at most.
|
||||
|
||||
If the before/after-mapping method has a return type other than `void`, it will be checked to match the target type of the mapping methods.
|
||||
Only the callback methods with a matching return type (or `void`) will be called in that mapping method.
|
||||
|
||||
If a callback method returns a non-null value, this value will be returned from the mapping method.
|
||||
|
||||
As with mapping methods, it is possible to specify type parameters for before/after-mapping methods.
|
||||
|
||||
.Mapper with @AfterMapping hook that returns a non-null value
|
||||
====
|
||||
[source, java, linenums]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
@Mapper
|
||||
public abstract class VehicleMapper {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
@AfterMapping
|
||||
protected <T> T attachEntity(@MappingTarget T entity) {
|
||||
return entityManager.merge(entity);
|
||||
}
|
||||
|
||||
public abstract CarDto toCarDto(Car car);
|
||||
}
|
||||
|
||||
// Generates something like this:
|
||||
public class VehicleMapperImpl extends VehicleMapper {
|
||||
|
||||
public CarDto toCarDto(Car car) {
|
||||
if ( car == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CarDto carDto = new CarDto();
|
||||
// attributes mapping ...
|
||||
|
||||
CarDto target = attachEntity( carDto );
|
||||
if ( target != null ) {
|
||||
return target;
|
||||
}
|
||||
|
||||
return carDto;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
All before/after-mapping methods that *can* be applied to a mapping method *will* be used. <<selection-based-on-qualifiers>> can be used to further control which methods may be chosen and which not. For that, the qualifier annotation needs to be applied to the before/after-method and referenced in `BeanMapping#qualifiedBy` or `IterableMapping#qualifiedBy`.
|
||||
|
||||
The order in which the selected methods are applied is roughly determined by their location of definition (although you should consider it a *code smell* if you need to rely on their order):
|
||||
|
@ -175,10 +175,13 @@ public class BeanMappingMethod extends MappingMethod {
|
||||
|
||||
sortPropertyMappingsByDependencies();
|
||||
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleCallbackFactory.beforeMappingMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
ctx,
|
||||
existingVariableNames );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx );
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx, existingVariableNames );
|
||||
|
||||
return new BeanMappingMethod(
|
||||
method,
|
||||
|
@ -21,7 +21,9 @@ package org.mapstruct.ap.internal.model;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
@ -99,10 +101,11 @@ public class EnumMappingMethod extends MappingMethod {
|
||||
|
||||
SelectionParameters selectionParameters = getSelecionParameters( method );
|
||||
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx );
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx );
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
|
||||
return new EnumMappingMethod( method, enumMappings, beforeMappingMethods, afterMappingMethods );
|
||||
}
|
||||
|
@ -149,10 +149,19 @@ public class IterableMappingMethod extends MappingMethod {
|
||||
factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, method.getResultType(), null );
|
||||
}
|
||||
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx );
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
existingVariables.add( loopVariableName );
|
||||
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleCallbackFactory.beforeMappingMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
ctx,
|
||||
existingVariables );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods = LifecycleCallbackFactory.afterMappingMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
ctx,
|
||||
existingVariables );
|
||||
|
||||
return new IterableMappingMethod(
|
||||
method,
|
||||
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
@ -45,35 +46,43 @@ public final class LifecycleCallbackFactory {
|
||||
* @param method the method to obtain the beforeMapping methods for
|
||||
* @param selectionParameters method selectionParameters
|
||||
* @param ctx the builder context
|
||||
* @param existingVariableNames the existing variable names in the mapping method
|
||||
* @return all applicable {@code @BeforeMapping} methods for the given method
|
||||
*/
|
||||
public static List<LifecycleCallbackMethodReference> beforeMappingMethods(
|
||||
Method method, SelectionParameters selectionParameters, MappingBuilderContext ctx) {
|
||||
public static List<LifecycleCallbackMethodReference> beforeMappingMethods(Method method,
|
||||
SelectionParameters selectionParameters,
|
||||
MappingBuilderContext ctx,
|
||||
Set<String> existingVariableNames) {
|
||||
return collectLifecycleCallbackMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
filterBeforeMappingMethods( ctx.getSourceModel() ),
|
||||
ctx );
|
||||
ctx,
|
||||
existingVariableNames );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param method the method to obtain the afterMapping methods for
|
||||
* @param selectionParameters method selectionParameters
|
||||
* @param ctx the builder context
|
||||
* @param existingVariableNames list of already used variable names
|
||||
* @return all applicable {@code @AfterMapping} methods for the given method
|
||||
*/
|
||||
public static List<LifecycleCallbackMethodReference> afterMappingMethods(
|
||||
Method method, SelectionParameters selectionParameters, MappingBuilderContext ctx) {
|
||||
public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method method,
|
||||
SelectionParameters selectionParameters,
|
||||
MappingBuilderContext ctx,
|
||||
Set<String> existingVariableNames) {
|
||||
return collectLifecycleCallbackMethods(
|
||||
method,
|
||||
selectionParameters,
|
||||
filterAfterMappingMethods( ctx.getSourceModel() ),
|
||||
ctx );
|
||||
ctx,
|
||||
existingVariableNames );
|
||||
}
|
||||
|
||||
private static List<LifecycleCallbackMethodReference> collectLifecycleCallbackMethods(
|
||||
Method method, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
|
||||
MappingBuilderContext ctx) {
|
||||
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
||||
|
||||
Map<SourceMethod, List<Parameter>> parameterAssignmentsForSourceMethod
|
||||
= new HashMap<SourceMethod, List<Parameter>>();
|
||||
@ -83,7 +92,12 @@ public final class LifecycleCallbackFactory {
|
||||
|
||||
candidates = filterCandidatesByQualifiers( method, selectionParameters, candidates, ctx );
|
||||
|
||||
return toLifecycleCallbackMethodRefs( candidates, parameterAssignmentsForSourceMethod, ctx );
|
||||
return toLifecycleCallbackMethodRefs(
|
||||
method,
|
||||
candidates,
|
||||
parameterAssignmentsForSourceMethod,
|
||||
ctx,
|
||||
existingVariableNames );
|
||||
}
|
||||
|
||||
private static List<SourceMethod> filterCandidatesByQualifiers(Method method,
|
||||
@ -99,16 +113,19 @@ public final class LifecycleCallbackFactory {
|
||||
false) );
|
||||
}
|
||||
|
||||
private static List<LifecycleCallbackMethodReference> toLifecycleCallbackMethodRefs(
|
||||
private static List<LifecycleCallbackMethodReference> toLifecycleCallbackMethodRefs(Method method,
|
||||
List<SourceMethod> candidates, Map<SourceMethod, List<Parameter>> parameterAssignmentsForSourceMethod,
|
||||
MappingBuilderContext ctx) {
|
||||
|
||||
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
||||
List<LifecycleCallbackMethodReference> result = new ArrayList<LifecycleCallbackMethodReference>();
|
||||
for ( SourceMethod candidate : candidates ) {
|
||||
markMapperReferenceAsUsed( ctx.getMapperReferences(), candidate );
|
||||
result.add( new LifecycleCallbackMethodReference(
|
||||
result.add(
|
||||
new LifecycleCallbackMethodReference(
|
||||
candidate,
|
||||
parameterAssignmentsForSourceMethod.get( candidate ) ) );
|
||||
parameterAssignmentsForSourceMethod.get( candidate ),
|
||||
method.getReturnType(),
|
||||
method.getResultType(),
|
||||
existingVariableNames ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -124,9 +141,7 @@ public final class LifecycleCallbackFactory {
|
||||
List<Parameter> parameterAssignments =
|
||||
ParameterAssignmentUtil.getParameterAssignments( availableParams, callback.getParameters() );
|
||||
|
||||
if ( parameterAssignments != null
|
||||
&& callback.matches( extractSourceTypes( parameterAssignments ), method.getResultType() ) ) {
|
||||
|
||||
if ( isValidCandidate( callback, method, parameterAssignments ) ) {
|
||||
parameterAssignmentsForSourceMethod.put( callback, parameterAssignments );
|
||||
candidates.add( callback );
|
||||
}
|
||||
@ -134,6 +149,18 @@ public final class LifecycleCallbackFactory {
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private static boolean isValidCandidate(SourceMethod candidate, Method method,
|
||||
List<Parameter> parameterAssignments) {
|
||||
if ( parameterAssignments == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( !candidate.matches( extractSourceTypes( parameterAssignments ), method.getResultType() ) ) {
|
||||
return false;
|
||||
}
|
||||
return ( candidate.getReturnType().isVoid() || candidate.getReturnType().isTypeVar()
|
||||
|| candidate.getReturnType().isAssignableTo( method.getResultType() ) );
|
||||
}
|
||||
|
||||
private static List<Parameter> getAvailableParameters(Method method, MappingBuilderContext ctx) {
|
||||
List<Parameter> availableParams = new ArrayList<Parameter>( method.getParameters() );
|
||||
if ( method.getMappingTargetParameter() == null ) {
|
||||
@ -154,7 +181,9 @@ public final class LifecycleCallbackFactory {
|
||||
private static void markMapperReferenceAsUsed(List<MapperReference> references, Method method) {
|
||||
for ( MapperReference ref : references ) {
|
||||
if ( ref.getType().equals( method.getDeclaringMapper() ) ) {
|
||||
ref.setUsed( !method.isStatic() );
|
||||
if ( !ref.isUsed() && !method.isStatic() ) {
|
||||
ref.setUsed( true );
|
||||
}
|
||||
ref.setTypeRequiresImport( true );
|
||||
|
||||
return;
|
||||
|
@ -24,6 +24,7 @@ import java.util.Set;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.internal.util.Collections;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
@ -37,11 +38,38 @@ public class LifecycleCallbackMethodReference extends MappingMethod {
|
||||
|
||||
private final Type declaringType;
|
||||
private final List<Parameter> parameterAssignments;
|
||||
private final Type methodReturnType;
|
||||
private final Type methodResultType;
|
||||
private final String instanceVariableName;
|
||||
private final String targetVariableName;
|
||||
|
||||
public LifecycleCallbackMethodReference(SourceMethod method, List<Parameter> parameterAssignments) {
|
||||
public LifecycleCallbackMethodReference(SourceMethod method, List<Parameter> parameterAssignments,
|
||||
Type methodReturnType, Type methodResultType,
|
||||
Set<String> existingVariableNames) {
|
||||
super( method );
|
||||
this.declaringType = method.getDeclaringMapper();
|
||||
this.parameterAssignments = parameterAssignments;
|
||||
this.methodReturnType = methodReturnType;
|
||||
this.methodResultType = methodResultType;
|
||||
|
||||
if ( isStatic() ) {
|
||||
this.instanceVariableName = declaringType.getName();
|
||||
}
|
||||
else if ( declaringType != null ) {
|
||||
this.instanceVariableName =
|
||||
Strings.getSaveVariableName( Introspector.decapitalize( declaringType.getName() ) );
|
||||
}
|
||||
else {
|
||||
this.instanceVariableName = null;
|
||||
}
|
||||
|
||||
if ( hasReturnType() ) {
|
||||
this.targetVariableName = Strings.getSaveVariableName( "target", existingVariableNames );
|
||||
existingVariableNames.add( this.targetVariableName );
|
||||
}
|
||||
else {
|
||||
this.targetVariableName = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Type getDeclaringType() {
|
||||
@ -49,7 +77,31 @@ public class LifecycleCallbackMethodReference extends MappingMethod {
|
||||
}
|
||||
|
||||
public String getInstanceVariableName() {
|
||||
return Strings.getSaveVariableName( Introspector.decapitalize( declaringType.getName() ) );
|
||||
return instanceVariableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the return type of the mapping method in which this callback method is called
|
||||
*
|
||||
* @return return type
|
||||
* @see Method#getReturnType()
|
||||
*/
|
||||
public Type getMethodReturnType() {
|
||||
return methodReturnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result type of the mapping method in which this callback method is called
|
||||
*
|
||||
* @return result type
|
||||
* @see Method#getResultType()
|
||||
*/
|
||||
public Type getMethodResultType() {
|
||||
return methodResultType;
|
||||
}
|
||||
|
||||
public String getTargetVariableName() {
|
||||
return targetVariableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,4 +122,11 @@ public class LifecycleCallbackMethodReference extends MappingMethod {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this callback method has a return type that is not void
|
||||
*/
|
||||
public boolean hasReturnType() {
|
||||
return !getReturnType().isVoid();
|
||||
}
|
||||
}
|
||||
|
@ -178,10 +178,11 @@ public class MapMappingMethod extends MappingMethod {
|
||||
keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes(), keyTargetType, false );
|
||||
valueAssignment = new LocalVarWrapper( valueAssignment, method.getThrownTypes(), valueTargetType, false );
|
||||
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, null, ctx );
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, null, ctx, existingVariables );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, null, ctx );
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, null, ctx, existingVariables );
|
||||
|
||||
return new MapMappingMethod(
|
||||
method,
|
||||
|
@ -21,7 +21,9 @@ package org.mapstruct.ap.internal.model;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
@ -113,13 +115,13 @@ public class ValueMappingMethod extends MappingMethod {
|
||||
|
||||
// do before / after lifecycle mappings
|
||||
SelectionParameters selectionParameters = getSelectionParameters( method );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods
|
||||
= LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods
|
||||
= LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx );
|
||||
Set<String> existingVariables = new HashSet<String>( method.getParameterNames() );
|
||||
List<LifecycleCallbackMethodReference> beforeMappingMethods =
|
||||
LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
List<LifecycleCallbackMethodReference> afterMappingMethods =
|
||||
LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx, existingVariables );
|
||||
|
||||
|
||||
// finallyn return a mapping
|
||||
// finally return a mapping
|
||||
return new ValueMappingMethod( method, mappingEntries, nullTarget, defaultTarget,
|
||||
throwIllegalArgumentException, beforeMappingMethods, afterMappingMethods );
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
}
|
||||
|
||||
private boolean isValidLifecycleCallbackMethod(ExecutableElement method, Type returnType) {
|
||||
return isVoid( returnType ) && Executables.isLifecycleCallbackMethod( method );
|
||||
return Executables.isLifecycleCallbackMethod( method );
|
||||
}
|
||||
|
||||
private boolean isValidReferencedMethod(List<Parameter> parameters) {
|
||||
|
@ -18,4 +18,16 @@
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<#if declaringType??>${instanceVariableName}.</#if>${name}(<#list parameterAssignments as param> <#if param.targetType><@includeModel object=ext.targetType raw=true/>.class<#elseif param.mappingTarget>${ext.targetBeanName}<#else>${param.name}</#if><#if param_has_next>,<#else> </#if></#list>);
|
||||
<@compress single_line=true>
|
||||
<#if hasReturnType()>
|
||||
<@includeModel object=methodResultType /> ${targetVariableName} =
|
||||
</#if>
|
||||
<#if declaringType??>${instanceVariableName}.</#if>${name}(
|
||||
<#list parameterAssignments as param>
|
||||
<#if param.targetType><@includeModel object=ext.targetType raw=true/>.class<#elseif param.mappingTarget>${ext.targetBeanName}<#else>${param.name}</#if><#if param_has_next>,<#else> </#if>
|
||||
</#list>);
|
||||
</@compress>
|
||||
<#if hasReturnType()><#nt>
|
||||
if ( ${targetVariableName} != null ) {
|
||||
return<#if methodReturnType.name != "void"> ${targetVariableName}</#if>;
|
||||
}</#if>
|
||||
|
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
public class Attribute {
|
||||
private Node node;
|
||||
|
||||
private String name;
|
||||
private String value;
|
||||
|
||||
public Attribute() {
|
||||
// default constructor for MapStruct
|
||||
}
|
||||
|
||||
public Attribute(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Node getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public void setNode(Node 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Attribute [name=" + name + ", value=" + value + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
public class AttributeDTO {
|
||||
private NodeDTO node;
|
||||
|
||||
private String name;
|
||||
private String value;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributeDTO [name=" + name + ", value=" + value + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.callbacks.returning.NodeMapperContext.ContextListener;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* Test case for https://github.com/mapstruct/mapstruct/issues/469
|
||||
*
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
@IssueKey( "469" )
|
||||
@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 )
|
||||
public class CallbacksWithReturnValuesTest {
|
||||
@Test( expected = StackOverflowError.class )
|
||||
public void mappingWithDefaultHandlingRaisesStackOverflowError() {
|
||||
Node root = buildNodes();
|
||||
NodeMapperDefault.INSTANCE.nodeToNodeDTO( root );
|
||||
}
|
||||
|
||||
@Test( expected = StackOverflowError.class )
|
||||
public void updatingWithDefaultHandlingRaisesStackOverflowError() {
|
||||
Node root = buildNodes();
|
||||
NodeMapperDefault.INSTANCE.nodeToNodeDTO( root, new NodeDTO() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingWithContextCorrectlyResolvesCycles() {
|
||||
final AtomicReference<Integer> contextLevel = new AtomicReference<Integer>( null );
|
||||
ContextListener contextListener = new ContextListener() {
|
||||
@Override
|
||||
public void methodCalled(Integer level, String method, Object source, Object target) {
|
||||
contextLevel.set( level );
|
||||
}
|
||||
};
|
||||
|
||||
NodeMapperContext.addContextListener( contextListener );
|
||||
try {
|
||||
Node root = buildNodes();
|
||||
NodeDTO rootDTO = NodeMapperWithContext.INSTANCE.nodeToNodeDTO( root );
|
||||
assertThat( rootDTO ).isNotNull();
|
||||
assertThat( contextLevel.get() ).isEqualTo( Integer.valueOf( 1 ) );
|
||||
}
|
||||
finally {
|
||||
NodeMapperContext.removeContextListener( contextListener );
|
||||
}
|
||||
}
|
||||
|
||||
private static Node buildNodes() {
|
||||
Node root = new Node( "root" );
|
||||
root.addAttribute( new Attribute( "name", "root" ) );
|
||||
|
||||
Node node1 = new Node( "node1" );
|
||||
node1.addAttribute( new Attribute( "name", "node1" ) );
|
||||
|
||||
root.addChild( node1 );
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numberMappingWithoutContextDoesNotUseCache() {
|
||||
Number n1 = NumberMapperDefault.INSTANCE.integerToNumber( 2342 );
|
||||
Number n2 = NumberMapperDefault.INSTANCE.integerToNumber( 2342 );
|
||||
assertThat( n1 ).isEqualTo( n2 );
|
||||
assertThat( n1 ).isNotSameAs( n2 );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numberMappingWithContextUsesCache() {
|
||||
NumberMapperContext.putCache( new Number( 2342 ) );
|
||||
Number n1 = NumberMapperWithContext.INSTANCE.integerToNumber( 2342 );
|
||||
Number n2 = NumberMapperWithContext.INSTANCE.integerToNumber( 2342 );
|
||||
assertThat( n1 ).isEqualTo( n2 );
|
||||
assertThat( n1 ).isSameAs( n2 );
|
||||
NumberMapperContext.clearCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numberMappingWithContextCallsVisitNumber() {
|
||||
Number n1 = NumberMapperWithContext.INSTANCE.integerToNumber( 1234 );
|
||||
Number n2 = NumberMapperWithContext.INSTANCE.integerToNumber( 5678 );
|
||||
assertThat( NumberMapperContext.getVisited() ).isEqualTo( Arrays.asList( n1, n2 ) );
|
||||
NumberMapperContext.clearVisited();
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
public class Node {
|
||||
private Node parent;
|
||||
|
||||
private String name;
|
||||
|
||||
private List<Node> children;
|
||||
private List<Attribute> attributes;
|
||||
|
||||
public Node() {
|
||||
// default constructor for MapStruct
|
||||
}
|
||||
|
||||
public Node(String name) {
|
||||
this.name = name;
|
||||
this.children = new ArrayList<Node>();
|
||||
this.attributes = new ArrayList<Attribute>();
|
||||
}
|
||||
|
||||
public Node getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Node parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<Node> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<Node> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public void addChild(Node node) {
|
||||
children.add( node );
|
||||
node.setParent( this );
|
||||
}
|
||||
|
||||
public List<Attribute> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(List<Attribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void addAttribute(Attribute attribute) {
|
||||
attributes.add( attribute );
|
||||
attribute.setNode( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Node [name=" + name + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
public class NodeDTO {
|
||||
private NodeDTO parent;
|
||||
|
||||
private String name;
|
||||
|
||||
private List<NodeDTO> children;
|
||||
private List<AttributeDTO> attributes;
|
||||
|
||||
public NodeDTO getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(NodeDTO parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NodeDTO [name=" + name + "]";
|
||||
}
|
||||
}
|
@ -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.callbacks.returning;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.BeforeMapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.TargetType;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
public class NodeMapperContext {
|
||||
private static final ThreadLocal<Integer> LEVEL = new ThreadLocal<Integer>();
|
||||
private static final ThreadLocal<Map<Object, Object>> MAPPING = new ThreadLocal<Map<Object, Object>>();
|
||||
|
||||
/** Only for test-inspection */
|
||||
private static final List<ContextListener> LISTENERS = new CopyOnWriteArrayList<ContextListener>();
|
||||
|
||||
private NodeMapperContext() {
|
||||
// Only allow static access
|
||||
}
|
||||
|
||||
@BeforeMapping
|
||||
@SuppressWarnings( "unchecked" )
|
||||
public static <T> T getInstance(Object source, @TargetType Class<T> type) {
|
||||
fireMethodCalled( LEVEL.get(), "getInstance", source, null );
|
||||
Map<Object, Object> mapping = MAPPING.get();
|
||||
if ( mapping == null ) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (T) mapping.get( source );
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeMapping
|
||||
public static void setInstance(Object source, @MappingTarget Object target) {
|
||||
Integer level = LEVEL.get();
|
||||
fireMethodCalled( level, "setInstance", source, target );
|
||||
if ( level == null ) {
|
||||
LEVEL.set( 1 );
|
||||
MAPPING.set( new IdentityHashMap<Object, Object>() );
|
||||
}
|
||||
else {
|
||||
LEVEL.set( level + 1 );
|
||||
}
|
||||
MAPPING.get().put( source, target );
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
public static void cleanup() {
|
||||
Integer level = LEVEL.get();
|
||||
fireMethodCalled( level, "cleanup", null, null );
|
||||
if ( level == 1 ) {
|
||||
MAPPING.set( null );
|
||||
LEVEL.set( null );
|
||||
}
|
||||
else {
|
||||
LEVEL.set( level - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for test-inspection
|
||||
*/
|
||||
static void addContextListener(ContextListener contextListener) {
|
||||
LISTENERS.add( contextListener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for test-inspection
|
||||
*/
|
||||
static void removeContextListener(ContextListener contextListener) {
|
||||
LISTENERS.remove( contextListener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for test-inspection
|
||||
*/
|
||||
private static void fireMethodCalled(Integer level, String method, Object source, Object target) {
|
||||
for ( ContextListener contextListener : LISTENERS ) {
|
||||
contextListener.methodCalled( level, method, source, target );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for test-inspection
|
||||
*/
|
||||
interface ContextListener {
|
||||
void methodCalled(Integer level, String method, Object source, Object target);
|
||||
}
|
||||
}
|
@ -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.callbacks.returning;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class NodeMapperDefault {
|
||||
public static final NodeMapperDefault INSTANCE = Mappers.getMapper( NodeMapperDefault.class );
|
||||
|
||||
public abstract NodeDTO nodeToNodeDTO(Node node);
|
||||
|
||||
public abstract void nodeToNodeDTO(Node node, @MappingTarget NodeDTO nodeDto);
|
||||
|
||||
protected abstract AttributeDTO attributeToAttributeDTO(Attribute attribute);
|
||||
|
||||
protected abstract void attributeToAttributeDTO(Attribute attribute, @MappingTarget AttributeDTO nodeDto);
|
||||
}
|
@ -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.callbacks.returning;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
@Mapper(uses = NodeMapperContext.class )
|
||||
public abstract class NodeMapperWithContext {
|
||||
public static final NodeMapperWithContext INSTANCE = Mappers.getMapper( NodeMapperWithContext.class );
|
||||
|
||||
public abstract NodeDTO nodeToNodeDTO(Node node);
|
||||
|
||||
public abstract void nodeToNodeDTO(Node node, @MappingTarget NodeDTO nodeDto);
|
||||
|
||||
protected abstract AttributeDTO attributeToAttributeDTO(Attribute attribute);
|
||||
|
||||
protected abstract void attributeToAttributeDTO(Attribute attribute, @MappingTarget AttributeDTO nodeDto);
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
public class Number {
|
||||
private int number;
|
||||
|
||||
public Number() {
|
||||
this( 0 );
|
||||
}
|
||||
|
||||
public Number(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public void setNumber(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 + number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( obj == null || getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
Number other = (Number) obj;
|
||||
return number == other.number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Number[number=" + number + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.BeforeMapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
public class NumberMapperContext {
|
||||
private static final Map<Number, Number> CACHE = new HashMap<Number, Number>();
|
||||
|
||||
private static final List<Number> VISITED = new ArrayList<Number>();
|
||||
|
||||
private NumberMapperContext() {
|
||||
// Only allow static access
|
||||
}
|
||||
|
||||
public static void putCache(Number number) {
|
||||
CACHE.put( number, number );
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
CACHE.clear();
|
||||
}
|
||||
|
||||
public static List<Number> getVisited() {
|
||||
return VISITED;
|
||||
}
|
||||
|
||||
public static void clearVisited() {
|
||||
VISITED.clear();
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
public static Number getInstance(Integer source, @MappingTarget Number target) {
|
||||
Number cached = CACHE.get( target );
|
||||
return ( cached == null ? null : cached );
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
public static <T extends Number> T visitNumber(@MappingTarget T number) {
|
||||
VISITED.add( number );
|
||||
return number;
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
public static Map<String, Integer> withMap(Map<String, Long> source, @MappingTarget Map<String, Integer> target) {
|
||||
return target;
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
public static List<String> withList(Set<Integer> source, @MappingTarget List<String> target) {
|
||||
return target;
|
||||
}
|
||||
|
||||
@BeforeMapping
|
||||
public static String neverCalled1(Integer integer) {
|
||||
throw new IllegalStateException( "This method must never be called, because the return type does not match!" );
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
public static String neverCalled2(Integer integer) {
|
||||
throw new IllegalStateException( "This method must never be called, because the return type does not match!" );
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class NumberMapperDefault {
|
||||
public static final NumberMapperDefault INSTANCE = Mappers.getMapper( NumberMapperDefault.class );
|
||||
|
||||
public abstract Number integerToNumber(Integer number);
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.callbacks.returning;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Pascal Grün
|
||||
*/
|
||||
@Mapper( uses = NumberMapperContext.class )
|
||||
public abstract class NumberMapperWithContext {
|
||||
public static final NumberMapperWithContext INSTANCE = Mappers.getMapper( NumberMapperWithContext.class );
|
||||
|
||||
public abstract Number integerToNumber(Integer number);
|
||||
|
||||
public abstract void integerToNumber(Integer number, @MappingTarget Number target);
|
||||
|
||||
public abstract Map<String, Integer> longMapToIntegerMap(Map<String, Long> target);
|
||||
|
||||
public abstract List<String> setToList(Set<Integer> target);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user