mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#14 Adapt javadoc and order of method-usage within a type hierarchy
This commit is contained in:
parent
619f8023d5
commit
ccedca8890
@ -26,12 +26,77 @@ import java.lang.annotation.Target;
|
||||
import org.mapstruct.util.Experimental;
|
||||
|
||||
/**
|
||||
* Marks a method to be invoked at the end of a generated the mapping method.
|
||||
* Marks a method to be invoked at the end of a generated mapping method, right before the last {@code return} statement
|
||||
* of the mapping method. The method can be implemented in an abstract mapper class or be declared in a type (class or
|
||||
* interface) referenced in {@link Mapper#uses()} in order to be used in a mapping method.
|
||||
* <p>
|
||||
* If the 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.
|
||||
* Only methods with return type {@code void} may be annotated with this annotation.
|
||||
* <p>
|
||||
* If the method has parameters, the method invocation is only generated if all parameters can be <em>assigned</em> by
|
||||
* the source or target parameters of the mapping method:
|
||||
* <ul>
|
||||
* <li>A parameter annotated with {@code @}{@link MappingTarget} is populated with the target instance of the mapping.
|
||||
* </li>
|
||||
* <li>A parameter annotated with {@code @}{@link TargetType} is populated with the target type of the mapping.</li>
|
||||
* <li>Any other parameter is populated with a source parameter of the mapping, whereas each source parameter is used
|
||||
* once at most.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* All <em>after-mapping</em> methods that can be applied to a mapping method will be used. Their order is determined by
|
||||
* their location of definition:
|
||||
* <ul>
|
||||
* <li>The order of methods within one type can not be guaranteed, as it depends on the compiler and the processing
|
||||
* environment implementation.</li>
|
||||
* <li>Methods declared in one type are used after methods declared in their super-type.</li>
|
||||
* <li>Methods implemented in the mapper itself are used before methods from types referenced in {@link Mapper#uses()}.
|
||||
* </li>
|
||||
* <li>Types referenced in {@link Mapper#uses()} are searched for <em>after-mapping</em> methods in the order specified
|
||||
* in the annotation.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Example:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @AfterMapping
|
||||
* public void calledWithoutArgs() {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @AfterMapping
|
||||
* public void calledWithSourceAndTargetType(SourceEntity anySource, @TargetType Class<?> targetType) {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @AfterMapping
|
||||
* public void calledWithSourceAndTarget(Object anySource, @MappingTarget TargetDto target) {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* public abstract TargetDto toTargetDto(SourceEntity source);
|
||||
*
|
||||
* // generates:
|
||||
*
|
||||
* public TargetDto toTargetDto(SourceEntity source) {
|
||||
* if ( source == null ) {
|
||||
* return null;
|
||||
* }
|
||||
*
|
||||
* TargetDto targetDto = new TargetDto();
|
||||
*
|
||||
* // actual mapping code
|
||||
*
|
||||
* calledWithoutArgs();
|
||||
* calledWithSourceAndTargetType( source, TargetDto.class );
|
||||
* calledWithSourceAndTarget( source, targetDto );
|
||||
*
|
||||
* return targetDto;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
* @see BeforeMapping
|
||||
*/
|
||||
@Experimental
|
||||
@Target(ElementType.METHOD)
|
||||
|
@ -26,12 +26,81 @@ import java.lang.annotation.Target;
|
||||
import org.mapstruct.util.Experimental;
|
||||
|
||||
/**
|
||||
* Marks a method to be invoked at the beginning of a generated mapping method.
|
||||
* Marks a method to be invoked at the beginning of a generated mapping method. The method can be implemented in an
|
||||
* abstract mapper class or be declared in a type (class or interface) referenced in {@link Mapper#uses()} in order to
|
||||
* be used in a mapping method.
|
||||
* <p>
|
||||
* If the 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.
|
||||
* Only methods with return type {@code void} may be annotated with this annotation.
|
||||
* <p>
|
||||
* If the method has parameters, the method invocation is only generated if all parameters can be <em>assigned</em> by
|
||||
* the source or target parameters of the mapping method:
|
||||
* <ul>
|
||||
* <li>A parameter annotated with {@code @}{@link MappingTarget} is populated with the target instance of the mapping.
|
||||
* </li>
|
||||
* <li>A parameter annotated with {@code @}{@link TargetType} is populated with the target type of the mapping.</li>
|
||||
* <li>Any other parameter is populated with a source parameter of the mapping, whereas each source parameter is used
|
||||
* once at most.</li>
|
||||
* </ul>
|
||||
* If a <em>before-mapping</em> method does not contain a {@code @}{@link MappingTarget} parameter, it is invoked
|
||||
* directly at the beginning of the applicable mapping method. If it contains a {@code @}{@link MappingTarget}
|
||||
* parameter, the method is invoked after the target parameter has been initialized in the mapping method.
|
||||
* <p>
|
||||
* All <em>before-mapping</em> methods that can be applied to a mapping method will be used. Their order is determined
|
||||
* by their location of definition:
|
||||
* <ul>
|
||||
* <li>The order of methods within one type can not be guaranteed, as it depends on the compiler and the processing
|
||||
* environment implementation.</li>
|
||||
* <li>Methods declared in one type are used after methods declared in their super-type.</li>
|
||||
* <li>Methods implemented in the mapper itself are used before methods from types referenced in {@link Mapper#uses()}.
|
||||
* </li>
|
||||
* <li>Types referenced in {@link Mapper#uses()} are searched for <em>after-mapping</em> methods in the order specified
|
||||
* in the annotation.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Example:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @BeforeMapping
|
||||
* public void calledWithoutArgs() {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @BeforeMapping
|
||||
* public void calledWithSourceAndTargetType(SourceEntity anySource, @TargetType Class<?> targetType) {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @BeforeMapping
|
||||
* public void calledWithSourceAndTarget(Object anySource, @MappingTarget TargetDto target) {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* public abstract TargetDto toTargetDto(SourceEntity source);
|
||||
*
|
||||
* // generates:
|
||||
*
|
||||
* public TargetDto toTargetDto(SourceEntity source) {
|
||||
* calledWithoutArgs();
|
||||
* calledWithSourceAndTargetType( source, TargetDto.class );
|
||||
*
|
||||
* if ( source == null ) {
|
||||
* return null;
|
||||
* }
|
||||
*
|
||||
* TargetDto targetDto = new TargetDto();
|
||||
*
|
||||
* calledWithSourceAndTarget( source, targetDto );
|
||||
*
|
||||
* // actual mapping code
|
||||
*
|
||||
* return targetDto;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
* @see AfterMapping
|
||||
*/
|
||||
@Experimental
|
||||
@Target(ElementType.METHOD)
|
||||
|
@ -67,9 +67,10 @@ public @interface MapMapping {
|
||||
|
||||
|
||||
/**
|
||||
* A value qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case
|
||||
* multiple mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error.
|
||||
*
|
||||
* A value qualifier can be specified to aid the selection process of a suitable mapper for the values in the map.
|
||||
* This is useful in case multiple mappers (hand written of internal) qualify and result in an 'Ambiguous mapping
|
||||
* methods found' error.
|
||||
* <p>
|
||||
* A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method.
|
||||
*
|
||||
* @return the qualifiers
|
||||
|
@ -38,6 +38,7 @@ import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.common.TypeFactory;
|
||||
|
||||
import static org.mapstruct.ap.util.Collections.hasNonNullElements;
|
||||
import static org.mapstruct.ap.util.SpecificCompilerWorkarounds.isSubType;
|
||||
|
||||
/**
|
||||
@ -146,7 +147,7 @@ public class MethodMatcher {
|
||||
Type candidateSourceType,
|
||||
Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
|
||||
if ( isNotObjectClass( candidateSourceType.getTypeMirror() ) ) {
|
||||
if ( !isJavaLangObject( candidateSourceType.getTypeMirror() ) ) {
|
||||
TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM, genericTypesMap );
|
||||
if ( !parameterMatcher.visit( candidateSourceType.getTypeMirror(), sourceType.getTypeMirror() ) ) {
|
||||
if ( sourceType.isPrimitive() ) {
|
||||
@ -171,7 +172,7 @@ public class MethodMatcher {
|
||||
Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
|
||||
|
||||
if ( isNotObjectClass( candidateResultType.getTypeMirror() ) && !candidateResultType.isVoid() ) {
|
||||
if ( !isJavaLangObject( candidateResultType.getTypeMirror() ) && !candidateResultType.isVoid() ) {
|
||||
|
||||
TypeMatcher returnTypeMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_TO, genericTypesMap );
|
||||
if ( !returnTypeMatcher.visit( candidateResultType.getTypeMirror(), resultType.getTypeMirror() ) ) {
|
||||
@ -205,23 +206,12 @@ public class MethodMatcher {
|
||||
|
||||
/**
|
||||
* @param type the type
|
||||
* @return {@code true}, if the type does NOT represent java.lang.Object
|
||||
* @return {@code true}, if the type represents java.lang.Object
|
||||
*/
|
||||
private boolean isNotObjectClass(TypeMirror type) {
|
||||
return !( type.getKind() == TypeKind.DECLARED
|
||||
&& ( (TypeElement) ( (DeclaredType) type ).asElement() ).getQualifiedName().contentEquals(
|
||||
Object.class.getName() ) );
|
||||
}
|
||||
|
||||
private static <E> boolean hasNonNullElements(List<E> elements) {
|
||||
if ( elements != null ) {
|
||||
for ( E e : elements ) {
|
||||
if ( e != null ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
private boolean isJavaLangObject(TypeMirror type) {
|
||||
return type.getKind() == TypeKind.DECLARED
|
||||
&& ( (TypeElement) ( (DeclaredType) type ).asElement() ).getQualifiedName().contentEquals(
|
||||
Object.class.getName() );
|
||||
}
|
||||
|
||||
private enum Assignability {
|
||||
|
@ -34,8 +34,7 @@ import org.mapstruct.ap.model.common.Parameter;
|
||||
import org.mapstruct.ap.model.common.Type;
|
||||
import org.mapstruct.ap.model.common.TypeFactory;
|
||||
import org.mapstruct.ap.model.source.SourceReference.PropertyEntry;
|
||||
import org.mapstruct.ap.prism.AfterMappingPrism;
|
||||
import org.mapstruct.ap.prism.BeforeMappingPrism;
|
||||
import org.mapstruct.ap.util.Executables;
|
||||
import org.mapstruct.ap.util.FormattingMessager;
|
||||
import org.mapstruct.ap.util.MapperConfiguration;
|
||||
import org.mapstruct.ap.util.Strings;
|
||||
@ -520,26 +519,14 @@ public class SourceMethod implements Method {
|
||||
|
||||
@Override
|
||||
public boolean isLifecycleCallbackMethod() {
|
||||
return isBeforeMappingMethod() || isAfterMappingMethod();
|
||||
return Executables.isLifecycleCallbackMethod( getExecutable() );
|
||||
}
|
||||
|
||||
public boolean isAfterMappingMethod() {
|
||||
return isAfterMappingMethod( getExecutable() );
|
||||
return Executables.isAfterMappingMethod( getExecutable() );
|
||||
}
|
||||
|
||||
public boolean isBeforeMappingMethod() {
|
||||
return isBeforeMappingMethod( getExecutable() );
|
||||
}
|
||||
|
||||
public static boolean isLifecycleCallbackMethod(ExecutableElement executableElement) {
|
||||
return isBeforeMappingMethod( executableElement ) || isAfterMappingMethod( executableElement );
|
||||
}
|
||||
|
||||
private static boolean isAfterMappingMethod(ExecutableElement executableElement) {
|
||||
return AfterMappingPrism.getInstanceOn( executableElement ) != null;
|
||||
}
|
||||
|
||||
private static boolean isBeforeMappingMethod(ExecutableElement executableElement) {
|
||||
return BeforeMappingPrism.getInstanceOn( executableElement ) != null;
|
||||
return Executables.isBeforeMappingMethod( getExecutable() );
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ import org.mapstruct.ap.prism.MapMappingPrism;
|
||||
import org.mapstruct.ap.prism.MappingPrism;
|
||||
import org.mapstruct.ap.prism.MappingsPrism;
|
||||
import org.mapstruct.ap.util.AnnotationProcessingException;
|
||||
import org.mapstruct.ap.util.Executables;
|
||||
import org.mapstruct.ap.util.FormattingMessager;
|
||||
import org.mapstruct.ap.util.MapperConfiguration;
|
||||
import org.mapstruct.ap.util.Message;
|
||||
@ -272,7 +273,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
||||
}
|
||||
|
||||
private boolean isValidLifecycleCallbackMethod(ExecutableElement method, Type returnType) {
|
||||
return isVoid( returnType ) && SourceMethod.isLifecycleCallbackMethod( method );
|
||||
return isVoid( returnType ) && Executables.isLifecycleCallbackMethod( method );
|
||||
}
|
||||
|
||||
private boolean isValidReferencedMethod(List<Parameter> parameters) {
|
||||
|
@ -85,4 +85,15 @@ public class Collections {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <E> boolean hasNonNullElements(Iterable<E> elements) {
|
||||
if ( elements != null ) {
|
||||
for ( E e : elements ) {
|
||||
if ( e != null ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,9 @@ import org.mapstruct.ap.services.Services;
|
||||
import org.mapstruct.ap.spi.AccessorNamingStrategy;
|
||||
import org.mapstruct.ap.spi.MethodType;
|
||||
|
||||
import org.mapstruct.ap.prism.AfterMappingPrism;
|
||||
import org.mapstruct.ap.prism.BeforeMappingPrism;
|
||||
|
||||
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||
import static org.mapstruct.ap.util.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
||||
|
||||
@ -163,7 +166,7 @@ public class Executables {
|
||||
}
|
||||
}
|
||||
|
||||
alreadyCollected.addAll( safeToAdd );
|
||||
alreadyCollected.addAll( 0, safeToAdd );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,4 +212,29 @@ public class Executables {
|
||||
return element.getSuperclass().getKind() == TypeKind.DECLARED
|
||||
&& asTypeElement( element.getSuperclass() ).getSuperclass().getKind() == TypeKind.DECLARED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param executableElement the element to check
|
||||
* @return {@code true}, if the executable element is a method annotated with {@code @BeforeMapping} or
|
||||
* {@code @AfterMapping}
|
||||
*/
|
||||
public static boolean isLifecycleCallbackMethod(ExecutableElement executableElement) {
|
||||
return isBeforeMappingMethod( executableElement ) || isAfterMappingMethod( executableElement );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param executableElement the element to check
|
||||
* @return {@code true}, if the executable element is a method annotated with {@code @AfterMapping}
|
||||
*/
|
||||
public static boolean isAfterMappingMethod(ExecutableElement executableElement) {
|
||||
return AfterMappingPrism.getInstanceOn( executableElement ) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param executableElement the element to check
|
||||
* @return {@code true}, if the executable element is a method annotated with {@code @BeforeMapping}
|
||||
*/
|
||||
public static boolean isBeforeMappingMethod(ExecutableElement executableElement) {
|
||||
return BeforeMappingPrism.getInstanceOn( executableElement ) != null;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user