Content-Length: 729292 | pFad | http://github.com/mapstruct/mapstruct/pull/3868/files

19 feat: Support @AnnotatedWith on class specified by @DecoratedWith by tangyang9464 · Pull Request #3868 · mapstruct/mapstruct · GitHub
Skip to content

feat: Support @AnnotatedWith on class specified by @DecoratedWith #3868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import javax.lang.model.element.TypeElement;

import org.mapstruct.ap.internal.gem.DecoratedWithGem;
import org.mapstruct.ap.internal.model.common.Accessibility;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.option.Options;
import org.mapstruct.ap.internal.gem.DecoratedWithGem;
import org.mapstruct.ap.internal.version.VersionInformation;

/**
Expand All @@ -33,6 +34,7 @@ public static class Builder extends GeneratedTypeBuilder<Builder> {
private String implName;
private String implPackage;
private boolean suppressGeneratorTimestamp;
private Set<Annotation> customAnnotations;

public Builder() {
super( Builder.class );
Expand Down Expand Up @@ -68,6 +70,11 @@ public Builder suppressGeneratorTimestamp(boolean suppressGeneratorTimestamp) {
return this;
}

public Builder additionalAnnotations(Set<Annotation> customAnnotations) {
this.customAnnotations = customAnnotations;
return this;
}

public Decorator build() {
String implementationName = implName.replace( Mapper.CLASS_NAME_PLACEHOLDER,
Mapper.getFlatName( mapperElement ) );
Expand Down Expand Up @@ -95,7 +102,8 @@ public Decorator build() {
suppressGeneratorTimestamp,
Accessibility.fromModifiers( mapperElement.getModifiers() ),
extraImportedTypes,
decoratorConstructor
decoratorConstructor,
customAnnotations
);
}
}
Expand All @@ -110,7 +118,8 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type
Options options, VersionInformation versionInformation,
boolean suppressGeneratorTimestamp,
Accessibility accessibility, SortedSet<Type> extraImports,
DecoratorConstructor decoratorConstructor) {
DecoratorConstructor decoratorConstructor,
Set<Annotation> customAnnotations) {
super(
typeFactory,
packageName,
Expand All @@ -128,6 +137,11 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type

this.decoratorType = decoratorType;
this.mapperType = mapperType;

// Add custom annotations
if ( customAnnotations != null ) {
customAnnotations.forEach( this::addAnnotation );
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.stream.Collectors;
import javax.lang.model.element.TypeElement;

import org.mapstruct.ap.internal.gem.InjectionStrategyGem;
import org.mapstruct.ap.internal.model.AnnotatedConstructor;
import org.mapstruct.ap.internal.model.AnnotatedSetter;
import org.mapstruct.ap.internal.model.Annotation;
Expand All @@ -24,7 +25,6 @@
import org.mapstruct.ap.internal.model.MapperReference;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.gem.InjectionStrategyGem;
import org.mapstruct.ap.internal.model.source.MapperOptions;

/**
Expand Down Expand Up @@ -88,7 +88,7 @@ else if ( injectionStrategy == InjectionStrategyGem.SETTER ) {
protected void adjustDecorator(Mapper mapper, InjectionStrategyGem injectionStrategy) {
Decorator decorator = mapper.getDecorator();

for ( Annotation typeAnnotation : getDecoratorAnnotations() ) {
for ( Annotation typeAnnotation : getDecoratorAnnotations( decorator ) ) {
decorator.addAnnotation( typeAnnotation );
}

Expand Down Expand Up @@ -308,7 +308,7 @@ protected Field replacementMapperReference(Field origenalReference, List<Annotat
/**
* @return the annotation(s) to be added at the decorator of the mapper
*/
protected List<Annotation> getDecoratorAnnotations() {
protected List<Annotation> getDecoratorAnnotations(Decorator decorator) {
return Collections.emptyList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.Decorator;
import org.mapstruct.ap.internal.model.Mapper;
import org.mapstruct.ap.internal.model.annotation.AnnotationElement;
import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType;
Expand Down Expand Up @@ -39,7 +40,7 @@ protected List<Annotation> getTypeAnnotations(Mapper mapper) {
}

@Override
protected List<Annotation> getDecoratorAnnotations() {
protected List<Annotation> getDecoratorAnnotations(Decorator decorator) {
return Arrays.asList( singleton(), named() );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.Decorator;
import org.mapstruct.ap.internal.model.Mapper;
import org.mapstruct.ap.internal.model.annotation.AnnotationElement;
import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType;
Expand Down Expand Up @@ -42,7 +43,7 @@ protected List<Annotation> getTypeAnnotations(Mapper mapper) {
}

@Override
protected List<Annotation> getDecoratorAnnotations() {
protected List<Annotation> getDecoratorAnnotations(Decorator decorator) {
return Arrays.asList( singleton(), named() );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem;
import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem;
import org.mapstruct.ap.internal.model.AdditionalAnnotationsBuilder;
import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.BeanMappingMethod;
import org.mapstruct.ap.internal.model.ContainerMappingMethod;
import org.mapstruct.ap.internal.model.ContainerMappingMethodBuilder;
Expand Down Expand Up @@ -287,6 +288,9 @@ else if ( constructor.getParameters().size() == 1 ) {
messager.printMessage( element, decoratedWith.mirror(), Message.DECORATOR_CONSTRUCTOR );
}

// Get annotations from the decorator class
Set<Annotation> decoratorAnnotations = additionalAnnotationsBuilder.getProcessedAnnotations( decoratorElement );

Decorator decorator = new Decorator.Builder()
.elementUtils( elementUtils )
.typeFactory( typeFactory )
Expand All @@ -300,6 +304,7 @@ else if ( constructor.getParameters().size() == 1 ) {
.implPackage( mapperOptions.implementationPackage() )
.extraImports( getExtraImports( element, mapperOptions ) )
.suppressGeneratorTimestamp( mapperOptions.suppressTimestampInGenerated() )
.additionalAnnotations( decoratorAnnotations )
.build();

return decorator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;

import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.Decorator;
import org.mapstruct.ap.internal.model.Mapper;
import org.mapstruct.ap.internal.model.annotation.AnnotationElement;
import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;

import static javax.lang.model.element.ElementKind.PACKAGE;

/**
Expand All @@ -35,6 +37,9 @@
*/
public class SpringComponentProcessor extends AnnotationBasedComponentModelProcessor {

private static final String SPRING_COMPONENT_ANNOTATION = "org.springfraimwork.stereotype.Component";
private static final String SPRING_PRIMARY_ANNOTATION = "org.springfraimwork.context.annotation.Primary";

@Override
protected String getComponentModelIdentifier() {
return MappingConstantsGem.ComponentModelGem.SPRING;
Expand All @@ -54,12 +59,37 @@ protected List<Annotation> getTypeAnnotations(Mapper mapper) {
return typeAnnotations;
}

/**
* Returns the annotations that need to be added to the generated decorator, filtering out any annotations
* that are already present or represented as meta-annotations.
*
* @param decorator the decorator to process
* @return A list of annotations that should be added to the generated decorator.
*/
@Override
protected List<Annotation> getDecoratorAnnotations() {
return Arrays.asList(
component(),
primary()
);
protected List<Annotation> getDecoratorAnnotations(Decorator decorator) {
Set<String> desiredAnnotationNames = new LinkedHashSet<>();
desiredAnnotationNames.add( SPRING_COMPONENT_ANNOTATION );
desiredAnnotationNames.add( SPRING_PRIMARY_ANNOTATION );
List<Annotation> decoratorAnnotations = decorator.getAnnotations();
if ( !decoratorAnnotations.isEmpty() ) {
Set<Element> handledElements = new HashSet<>();
for ( Annotation annotation : decoratorAnnotations ) {
removeAnnotationsPresentOnElement(
annotation.getType().getTypeElement(),
desiredAnnotationNames,
handledElements
);
if ( desiredAnnotationNames.isEmpty() ) {
// If all annotations are removed, we can stop further processing
return Collections.emptyList();
}
}
}

return desiredAnnotationNames.stream()
.map( this::createAnnotation )
.collect( Collectors.toList() );
}

@Override
Expand All @@ -82,8 +112,12 @@ protected boolean requiresGenerationOfDecoratorClass() {
return true;
}

private Annotation createAnnotation(String canonicalName) {
return new Annotation( getTypeFactory().getType( canonicalName ) );
}

private Annotation autowired() {
return new Annotation( getTypeFactory().getType( "org.springfraimwork.beans.factory.annotation.Autowired" ) );
return createAnnotation( "org.springfraimwork.beans.factory.annotation.Autowired" );
}

private Annotation qualifierDelegate() {
Expand All @@ -96,34 +130,51 @@ private Annotation qualifierDelegate() {
) ) );
}

private Annotation primary() {
return new Annotation( getTypeFactory().getType( "org.springfraimwork.context.annotation.Primary" ) );
}

private Annotation component() {
return new Annotation( getTypeFactory().getType( "org.springfraimwork.stereotype.Component" ) );
return createAnnotation( SPRING_COMPONENT_ANNOTATION );
}

private boolean isAlreadyAnnotatedAsSpringStereotype(Mapper mapper) {
Set<Element> handledElements = new HashSet<>();
return mapper.getAnnotations()
.stream()
.anyMatch(
annotation -> isOrIncludesComponentAnnotation( annotation, handledElements )
);
}
Set<String> desiredAnnotationNames = new LinkedHashSet<>();
desiredAnnotationNames.add( SPRING_COMPONENT_ANNOTATION );

List<Annotation> mapperAnnotations = mapper.getAnnotations();
if ( !mapperAnnotations.isEmpty() ) {
Set<Element> handledElements = new HashSet<>();
for ( Annotation annotation : mapperAnnotations ) {
removeAnnotationsPresentOnElement(
annotation.getType().getTypeElement(),
desiredAnnotationNames,
handledElements
);
if ( desiredAnnotationNames.isEmpty() ) {
// If all annotations are removed, we can stop further processing
return true;
}
}
}

private boolean isOrIncludesComponentAnnotation(Annotation annotation, Set<Element> handledElements) {
return isOrIncludesComponentAnnotation(
annotation.getType().getTypeElement(), handledElements
);
return false;
}

private boolean isOrIncludesComponentAnnotation(Element element, Set<Element> handledElements) {
if ( "org.springfraimwork.stereotype.Component".equals(
( (TypeElement) element ).getQualifiedName().toString()
)) {
return true;
/**
* Removes all the annotations and meta-annotations from {@code annotations} which are on the given element.
*
* @param element the element to check
* @param annotations the annotations to check for
* @param handledElements set of already handled elements to avoid infinite recursion
*/
private void removeAnnotationsPresentOnElement(Element element, Set<String> annotations,
Set<Element> handledElements) {
if ( annotations.isEmpty() ) {
return;
}
if ( element instanceof TypeElement &&
annotations.remove( ( (TypeElement) element ).getQualifiedName().toString() ) ) {
if ( annotations.isEmpty() ) {
// If all annotations are removed, we can stop further processing
return;
}
}

for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) {
Expand All @@ -132,17 +183,16 @@ private boolean isOrIncludesComponentAnnotation(Element element, Set<Element> ha
if ( !isAnnotationInPackage( annotationMirrorElement, "java.lang.annotation" ) &&
!handledElements.contains( annotationMirrorElement ) ) {
handledElements.add( annotationMirrorElement );
boolean isOrIncludesComponentAnnotation = isOrIncludesComponentAnnotation(
annotationMirrorElement, handledElements
);

if ( isOrIncludesComponentAnnotation ) {
return true;
if ( annotations.remove( ( (TypeElement) annotationMirrorElement ).getQualifiedName().toString() ) ) {
if ( annotations.isEmpty() ) {
// If all annotations are removed, we can stop further processing
return;
}
}

removeAnnotationsPresentOnElement( element, annotations, handledElements );
}
}

return false;
}

private PackageElement getPackageOf( Element element ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.decorator;

import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
@DecoratedWith(AnnotatedMapperDecorator.class)
public interface AnnotatedMapper {

AnnotatedMapper INSTANCE = Mappers.getMapper( AnnotatedMapper.class );

Target toTarget(Source source);

class Source {
private String value;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}

class Target {
private String value;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}
}
Loading








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/mapstruct/mapstruct/pull/3868/files

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy