diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index a33bc7520e..b8bf827648 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -328,6 +328,11 @@ public boolean overridesMethod() { return false; } + @Override + public boolean deprecatedMethod() { + return false; + } + @Override public ExecutableElement getExecutable() { return basedOn.getExecutable(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java index 35b35aee73..410d412583 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java @@ -219,6 +219,11 @@ public boolean overridesMethod() { return false; } + @Override + public boolean deprecatedMethod() { + return false; + } + @Override public ExecutableElement getExecutable() { return null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index 9b7729e8fa..8b83d0b202 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -155,7 +155,10 @@ private Mapper(TypeFactory typeFactory, String packageName, String name, this.customPackage = customPackage; this.customImplName = customImplName; customAnnotations.forEach( this::addAnnotation ); - + TypeElement typeElement = this.getMapperDefinitionType().getTypeElement(); + if ( typeElement.getAnnotation( Deprecated.class ) != null) { + addAnnotation( new Annotation(typeFactory.getType( Deprecated.class )) ); + } this.decorator = decorator; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java index fcd8d70d52..7d17e5ef77 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java @@ -22,6 +22,9 @@ public abstract class NormalTypeMappingMethod extends MappingMethod { private final MethodReference factoryMethod; private final boolean overridden; + + private final boolean deprecated; + private final boolean mapNullToDefault; NormalTypeMappingMethod(Method method, Collection existingVariableNames, MethodReference factoryMethod, @@ -31,6 +34,7 @@ public abstract class NormalTypeMappingMethod extends MappingMethod { super( method, existingVariableNames, beforeMappingReferences, afterMappingReferences ); this.factoryMethod = factoryMethod; this.overridden = method.overridesMethod(); + this.deprecated = method.deprecatedMethod(); this.mapNullToDefault = mapNullToDefault; } @@ -56,6 +60,10 @@ public boolean isOverridden() { return overridden; } + public boolean isDeprecated() { + return deprecated; + } + public MethodReference getFactoryMethod() { return this.factoryMethod; } 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 0c60f41364..c3a3441904 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 @@ -148,6 +148,13 @@ default boolean isPresenceCheck() { */ boolean overridesMethod(); + + /** + * + * @return Returns true when the method is modified by @Deprecated + */ + boolean deprecatedMethod(); + ExecutableElement getExecutable(); /** 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 37b57d9025..4233933bf1 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 @@ -11,9 +11,11 @@ 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.ExecutableElement; import javax.lang.model.element.Modifier; +import javax.lang.model.type.TypeKind; import org.mapstruct.ap.internal.gem.ConditionGem; import org.mapstruct.ap.internal.gem.ObjectFactoryGem; @@ -349,6 +351,10 @@ public Accessibility getAccessibility() { return accessibility; } + public TypeFactory getTypeFactory() { + return typeFactory; + } + public boolean inverses(SourceMethod method) { return method.getDeclaringMapper() == null && method.isAbstract() @@ -515,6 +521,18 @@ public boolean overridesMethod() { return declaringMapper == null && executable.getModifiers().contains( Modifier.ABSTRACT ); } + @Override + public boolean deprecatedMethod() { + Type deprecatedType = typeFactory.getType( Deprecated.class ); + return executable != null && executable.getAnnotationMirrors() + .stream() + .map( AnnotationMirror::getAnnotationType ) + .filter( annotationType -> TypeKind.DECLARED == annotationType.getKind() ) + .map( typeFactory::getType ) + .anyMatch( annotationType -> deprecatedType.equals( annotationType ) ); + + } + @Override public boolean matches(List sourceTypes, Type targetType) { MethodMatcher matcher = new MethodMatcher( typeUtils, typeFactory, this ); 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 f46b201576..a9eb84d4ac 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 @@ -233,6 +233,11 @@ public boolean overridesMethod() { return false; } + @Override + public boolean deprecatedMethod() { + return false; + } + @Override public ExecutableElement getExecutable() { return null; diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 1b402a57f0..0533529d92 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -10,6 +10,9 @@ <#nt><@includeModel object=annotation/> <#if overridden>@Override +<#if deprecated> + @Deprecated + <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#assign targetType = resultType /> <#if !existingInstanceMapping> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl index 495111b7a1..b2db945363 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl @@ -7,6 +7,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.IterableMappingMethod" --> <#if overridden>@Override +<#if deprecated> + @Deprecated + <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#list beforeMappingReferencesWithoutMappingTarget as callback> <@includeModel object=callback targetBeanName=resultName targetType=resultType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl index 443d87cba7..d3b1e6fd4d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl @@ -7,6 +7,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MapMappingMethod" --> <#if overridden>@Override +<#if deprecated> + @Deprecated + <#lt>${accessibility.keyword} <@includeModel object=returnType /> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#list beforeMappingReferencesWithoutMappingTarget as callback> <@includeModel object=callback targetBeanName=resultName targetType=resultType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl index 860859fc3a..d40b0f35a8 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl @@ -7,6 +7,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.StreamMappingMethod" --> <#if overridden>@Override +<#if deprecated> + @Deprecated + <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#--TODO does it even make sense to do a callback if the result is a Stream, as they are immutable--> <#list beforeMappingReferencesWithoutMappingTarget as callback> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/ChartEntry.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/ChartEntry.java new file mode 100644 index 0000000000..27fd59a6f7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/ChartEntry.java @@ -0,0 +1,67 @@ +/* + * 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.bugs._2773; + +/** + * @author orange add + */ +public class ChartEntry { + + private String chartName; + private String songTitle; + private String artistName; + private String recordedAt; + private String city; + private int position; + + public String getChartName() { + return chartName; + } + + public void setChartName(String chartName) { + this.chartName = chartName; + } + + public String getSongTitle() { + return songTitle; + } + + public void setSongTitle(String songTitle) { + this.songTitle = songTitle; + } + + public String getArtistName() { + return artistName; + } + + public void setArtistName(String artistName) { + this.artistName = artistName; + } + + public String getRecordedAt() { + return recordedAt; + } + + public void setRecordedAt(String recordedAt) { + this.recordedAt = recordedAt; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Issue2773Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Issue2773Mapper.java new file mode 100644 index 0000000000..ad470fa0ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Issue2773Mapper.java @@ -0,0 +1,51 @@ +/* + * 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.bugs._2773; + +import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +/** + * @author orange add + */ + +@Mapper +@Deprecated +public interface Issue2773Mapper { + + Issue2773Mapper INSTANCE = Mappers.getMapper( Issue2773Mapper.class ); + + @Deprecated + @TestAnnotation(name = "hello", id = 1, address = {"shenzhen", "guangzhou"}) + @Mapping(target = "name", source = "chartEntry1.recordedAt") + @Mapping(target = "city", source = "chartEntry2.city") + Studio toStudio(ChartEntry chartEntry1, ChartEntry chartEntry2); + + @Mappings({@Mapping(target = "city", source = "city"), @Mapping(target = "name", source = "recordedAt")}) + Studio map(ChartEntry chartEntry); + + @Deprecated + @IterableMapping(numberFormat = "$#.00") + List prices(List prices); + + @Deprecated + Set integerStreamToStringSet(Stream integers); + + @Deprecated + @MapMapping(valueDateFormat = "dd.MM.yyyy") + Map longDateMapToStringStringMap(Map source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Issue2773Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Issue2773Test.java new file mode 100644 index 0000000000..0b19447c55 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Issue2773Test.java @@ -0,0 +1,41 @@ +/* + * 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.bugs._2773; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author orange add + */ +@IssueKey("2773") +public class Issue2773Test { + + @ProcessorTest + @WithClasses({ChartEntry.class, Issue2773Mapper.class, Studio.class, TestAnnotation.class}) + public void shouldContainMethodAnnotations() throws NoSuchMethodException { + Issue2773Mapper issue2773Mapper = Issue2773Mapper.INSTANCE; + Class mapperClass = issue2773Mapper.getClass(); + Method toStudio = mapperClass.getMethod( "toStudio", ChartEntry.class, ChartEntry.class ); + Method prices = mapperClass.getMethod( "prices", List.class ); + Method integerStreamToStringSet = mapperClass.getMethod( "integerStreamToStringSet", Stream.class ); + Method longDateMapToStringStringMap = mapperClass.getMethod( "longDateMapToStringStringMap", Map.class ); + assertThat( mapperClass.getAnnotation( Deprecated.class ) ).isNotNull(); + assertThat( toStudio.getAnnotation( Deprecated.class ) ).isNotNull(); + assertThat( prices.getAnnotation( Deprecated.class ) ).isNotNull(); + assertThat( integerStreamToStringSet.getAnnotation( Deprecated.class ) ).isNotNull(); + assertThat( longDateMapToStringStringMap.getAnnotation( Deprecated.class ) ).isNotNull(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Studio.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Studio.java new file mode 100644 index 0000000000..9ade71f47d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/Studio.java @@ -0,0 +1,31 @@ +/* + * 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.bugs._2773; + +/** + * @author orange add + */ +public class Studio { + + private String city; + private String name; + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/TestAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/TestAnnotation.java new file mode 100644 index 0000000000..bf6d8cc833 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2773/TestAnnotation.java @@ -0,0 +1,25 @@ +/* + * 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.bugs._2773; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +/** + * @author orange add + */ + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestAnnotation { + + int id(); + + String name(); + + String[] address(); +}