66package org .mapstruct .intellij .util ;
77
88import java .util .ArrayList ;
9+ import java .util .Arrays ;
910import java .util .Collections ;
11+ import java .util .HashSet ;
1012import java .util .List ;
1113import java .util .Objects ;
1214import java .util .Optional ;
15+ import java .util .Set ;
1316import java .util .stream .Collectors ;
1417import java .util .stream .Stream ;
1518
1619import com .intellij .codeInsight .AnnotationUtil ;
17- import com .intellij .codeInsight .MetaAnnotationUtil ;
1820import com .intellij .openapi .command .WriteCommandAction ;
1921import com .intellij .openapi .command .undo .UndoUtil ;
2022import com .intellij .openapi .editor .Editor ;
4042import com .intellij .psi .PsiFile ;
4143import com .intellij .psi .PsiJavaCodeReferenceElement ;
4244import com .intellij .psi .PsiMethod ;
45+ import com .intellij .psi .PsiModifierList ;
4346import com .intellij .psi .PsiModifierListOwner ;
4447import com .intellij .psi .PsiNameValuePair ;
4548import com .intellij .psi .PsiReference ;
4649import com .intellij .psi .codeStyle .JavaCodeStyleManager ;
4750import com .intellij .util .IncorrectOperationException ;
51+ import com .intellij .util .containers .ContainerUtil ;
4852import org .jetbrains .annotations .NotNull ;
4953import org .mapstruct .ReportingPolicy ;
5054
@@ -281,17 +285,27 @@ private static boolean canUseRepeatableMapping(PsiElement psiElement) {
281285 && MapstructUtil .isMapStructJdk8Present ( module );
282286 }
283287
284- public static Stream <PsiAnnotation > findAllDefinedMappingAnnotations (@ NotNull PsiMethod method ,
285- MapStructVersion mapStructVersion ) {
288+ public static Stream <PsiAnnotation > findAllDefinedMappingAnnotations (@ NotNull PsiModifierListOwner owner ,
289+ MapStructVersion mapStructVersion ) {
290+
291+ // Meta annotations support was added when constructor support was added
292+ boolean includeMetaAnnotations = mapStructVersion .isConstructorSupported ();
293+
294+ return findAllDefinedMappingAnnotations ( owner , includeMetaAnnotations );
295+ }
296+
297+ @ NotNull
298+ private static Stream <PsiAnnotation > findAllDefinedMappingAnnotations (@ NotNull PsiModifierListOwner owner ,
299+ boolean includeMetaAnnotations ) {
286300 //TODO cache
287301 Stream <PsiAnnotation > mappingsAnnotations = Stream .empty ();
288- PsiAnnotation mappings = findAnnotation ( method , true , MapstructUtil .MAPPINGS_ANNOTATION_FQN );
302+ PsiAnnotation mappings = findAnnotation ( owner , true , MapstructUtil .MAPPINGS_ANNOTATION_FQN );
289303 if ( mappings != null ) {
290304 //TODO maybe there is a better way to do this, but currently I don't have that much knowledge
291305 PsiNameValuePair mappingsValue = findDeclaredAttribute ( mappings , null );
292306 if ( mappingsValue != null && mappingsValue .getValue () instanceof PsiArrayInitializerMemberValue ) {
293307 mappingsAnnotations = Stream .of ( ( (PsiArrayInitializerMemberValue ) mappingsValue .getValue () )
294- .getInitializers () )
308+ .getInitializers () )
295309 .filter ( MapstructAnnotationUtils ::isMappingPsiAnnotation )
296310 .map ( memberValue -> (PsiAnnotation ) memberValue );
297311 }
@@ -300,19 +314,61 @@ else if ( mappingsValue != null && mappingsValue.getValue() instanceof PsiAnnota
300314 }
301315 }
302316
303- Stream <PsiAnnotation > mappingAnnotations = findMappingAnnotations ( method , mapStructVersion );
317+ Stream <PsiAnnotation > mappingAnnotations = findMappingAnnotations ( owner , includeMetaAnnotations );
304318
305319 return Stream .concat ( mappingAnnotations , mappingsAnnotations );
306320 }
307321
308- private static Stream <PsiAnnotation > findMappingAnnotations (@ NotNull PsiMethod method ,
309- MapStructVersion mapStructVersion ) {
310- if ( mapStructVersion .isConstructorSupported () ) {
311- // Meta annotations support was added when constructor support was added
312- return MetaAnnotationUtil .findMetaAnnotations ( method , Collections .singleton ( MAPPING_ANNOTATION_FQN ) );
322+ private static Stream <PsiAnnotation > findMappingAnnotations (@ NotNull PsiModifierListOwner method ,
323+ boolean includeMetaAnnotations ) {
324+
325+ Stream <PsiAnnotation > metaAnnotations = Stream .empty ();
326+
327+ if ( includeMetaAnnotations ) {
328+ // do not use MetaAnnotationUtil#findMetaAnnotations since it only finds the first @Mapping annotation
329+ metaAnnotations = findMetaAnnotations ( method , new HashSet <>() ).stream ();
313330 }
314- return Stream .of ( method .getModifierList ().getAnnotations () )
331+
332+ Stream <PsiAnnotation > directAnnotations = Stream .of ( method .getModifierList () )
333+ .filter ( Objects ::nonNull )
334+ .flatMap ( psiModifierList -> Arrays .stream ( psiModifierList .getAnnotations () ) )
315335 .filter ( MapstructAnnotationUtils ::isMappingAnnotation );
336+
337+ return Stream .concat ( directAnnotations , metaAnnotations );
338+ }
339+
340+ @ NotNull
341+ private static Set <PsiAnnotation > findMetaAnnotations (@ NotNull PsiModifierListOwner owner ,
342+ Set <? super PsiClass > visited ) {
343+
344+ Set <PsiAnnotation > result = new HashSet <>();
345+
346+ // to avoid infinite loops, do not include meta annotations at this point
347+ findAllDefinedMappingAnnotations ( owner , false ).forEach ( result ::add );
348+
349+ List <PsiClass > annotationClasses = getResolvedClassesInAnnotationsList ( owner );
350+
351+ for ( PsiClass annotationClass : annotationClasses ) {
352+ if ( visited .add ( annotationClass ) ) {
353+ result .addAll ( findMetaAnnotations ( annotationClass , visited ) );
354+ }
355+ }
356+
357+ return result ;
358+ }
359+
360+ /**
361+ * copy of private method <code>MetaAnnotationUtil#getResolvedClassesInAnnotationsList(PsiModifierListOwner)</code>
362+ */
363+ private static List <PsiClass > getResolvedClassesInAnnotationsList (PsiModifierListOwner owner ) {
364+ PsiModifierList modifierList = owner .getModifierList ();
365+ if ( modifierList != null ) {
366+ return ContainerUtil .mapNotNull (
367+ modifierList .getApplicableAnnotations (),
368+ PsiAnnotation ::resolveAnnotationType
369+ );
370+ }
371+ return Collections .emptyList ();
316372 }
317373
318374 public static Stream <PsiAnnotation > findAllDefinedValueMappingAnnotations (@ NotNull PsiMethod method ) {
@@ -483,52 +539,52 @@ private static Stream<PsiClass> findReferencedMappersOfMapperConfig(PsiAnnotatio
483539 }
484540
485541 @ NotNull
486- public static ReportingPolicy getUnmappedTargetPolicy ( @ NotNull PsiMethod method ) {
542+ public static ReportingPolicy getUnmappedTargetPolicy (@ NotNull PsiMethod method ) {
487543 PsiAnnotation beanMapping = method .getAnnotation ( MapstructUtil .BEAN_MAPPING_FQN );
488- if (beanMapping != null ) {
544+ if ( beanMapping != null ) {
489545 PsiAnnotationMemberValue beanAnnotationOverwrite =
490- beanMapping .findDeclaredAttributeValue ( UNMAPPED_TARGET_POLICY );
491- if (beanAnnotationOverwrite != null ) {
546+ beanMapping .findDeclaredAttributeValue ( UNMAPPED_TARGET_POLICY );
547+ if ( beanAnnotationOverwrite != null ) {
492548 return getUnmappedTargetPolicyPolicyFromAnnotation ( beanAnnotationOverwrite );
493549 }
494550 }
495551 PsiClass containingClass = method .getContainingClass ();
496- if (containingClass == null ) {
552+ if ( containingClass == null ) {
497553 return ReportingPolicy .WARN ;
498554 }
499555 return getUnmappedTargetPolicyFromClass ( containingClass );
500556 }
501557
502558 @ NotNull
503- private static ReportingPolicy getUnmappedTargetPolicyFromClass ( @ NotNull PsiClass containingClass ) {
559+ private static ReportingPolicy getUnmappedTargetPolicyFromClass (@ NotNull PsiClass containingClass ) {
504560 PsiAnnotation mapperAnnotation = containingClass .getAnnotation ( MapstructUtil .MAPPER_ANNOTATION_FQN );
505- if (mapperAnnotation == null ) {
561+ if ( mapperAnnotation == null ) {
506562 return ReportingPolicy .WARN ;
507563 }
508564
509565 PsiAnnotationMemberValue classAnnotationOverwrite = mapperAnnotation .findDeclaredAttributeValue (
510- UNMAPPED_TARGET_POLICY );
511- if (classAnnotationOverwrite != null ) {
566+ UNMAPPED_TARGET_POLICY );
567+ if ( classAnnotationOverwrite != null ) {
512568 return getUnmappedTargetPolicyPolicyFromAnnotation ( classAnnotationOverwrite );
513569 }
514570 return getUnmappedTargetPolicyFromMapperConfig ( mapperAnnotation );
515571 }
516572
517573 @ NotNull
518- private static ReportingPolicy getUnmappedTargetPolicyFromMapperConfig ( @ NotNull PsiAnnotation mapperAnnotation ) {
519- PsiModifierListOwner mapperConfigReference = findMapperConfigReference ( mapperAnnotation );
574+ private static ReportingPolicy getUnmappedTargetPolicyFromMapperConfig (@ NotNull PsiAnnotation mapperAnnotation ) {
575+ PsiModifierListOwner mapperConfigReference = findMapperConfigReference ( mapperAnnotation );
520576 if ( mapperConfigReference == null ) {
521577 return ReportingPolicy .WARN ;
522578 }
523579 PsiAnnotation mapperConfigAnnotation = mapperConfigReference .getAnnotation (
524- MapstructUtil .MAPPER_CONFIG_ANNOTATION_FQN );
580+ MapstructUtil .MAPPER_CONFIG_ANNOTATION_FQN );
525581
526- if (mapperConfigAnnotation == null ) {
582+ if ( mapperConfigAnnotation == null ) {
527583 return ReportingPolicy .WARN ;
528584 }
529585 PsiAnnotationMemberValue configValue =
530- mapperConfigAnnotation .findDeclaredAttributeValue ( UNMAPPED_TARGET_POLICY );
531- if (configValue == null ) {
586+ mapperConfigAnnotation .findDeclaredAttributeValue ( UNMAPPED_TARGET_POLICY );
587+ if ( configValue == null ) {
532588 return ReportingPolicy .WARN ;
533589 }
534590 return getUnmappedTargetPolicyPolicyFromAnnotation ( configValue );
@@ -544,8 +600,8 @@ private static ReportingPolicy getUnmappedTargetPolicyFromMapperConfig( @NotNull
544600 */
545601 @ NotNull
546602 private static ReportingPolicy getUnmappedTargetPolicyPolicyFromAnnotation (
547- @ NotNull PsiAnnotationMemberValue configValue ) {
548- switch (configValue .getText ()) {
603+ @ NotNull PsiAnnotationMemberValue configValue ) {
604+ switch ( configValue .getText () ) {
549605 case "IGNORE" :
550606 case "ReportingPolicy.IGNORE" :
551607 return ReportingPolicy .IGNORE ;
0 commit comments