3434import org .codehaus .groovy .ast .expr .ClassExpression ;
3535import org .codehaus .groovy .ast .expr .DeclarationExpression ;
3636import org .codehaus .groovy .ast .expr .Expression ;
37- import org .codehaus .groovy .ast .expr .ListExpression ;
37+ import org .codehaus .groovy .ast .expr .PropertyExpression ;
3838import org .codehaus .groovy .ast .expr .VariableExpression ;
3939import org .codehaus .groovy .ast .stmt .ReturnStatement ;
4040import org .codehaus .groovy .ast .stmt .Statement ;
4444import groovyjarjarasm .asm .Opcodes ;
4545
4646import java .lang .annotation .Retention ;
47- import java .lang .annotation .RetentionPolicy ;
4847import java .util .ArrayList ;
4948import java .util .Iterator ;
5049import java .util .LinkedHashMap ;
5150import java .util .List ;
5251import java .util .Map ;
53- import java .util .stream .Collectors ;
5452
53+ import static java .util .stream .Collectors .toList ;
5554import static org .codehaus .groovy .ast .AnnotationNode .ANNOTATION_TARGET ;
5655import static org .codehaus .groovy .ast .AnnotationNode .CONSTRUCTOR_TARGET ;
5756import static org .codehaus .groovy .ast .AnnotationNode .FIELD_TARGET ;
6362import static org .codehaus .groovy .ast .AnnotationNode .TYPE_PARAMETER_TARGET ;
6463import static org .codehaus .groovy .ast .AnnotationNode .TYPE_TARGET ;
6564import static org .codehaus .groovy .ast .AnnotationNode .TYPE_USE_TARGET ;
65+ import static org .codehaus .groovy .ast .ClassHelper .makeCached ;
6666import static org .codehaus .groovy .ast .tools .GeneralUtils .getInterfacesAndSuperInterfaces ;
67+ import static org .codehaus .groovy .ast .tools .GeneralUtils .listX ;
6768import static org .codehaus .groovy .ast .tools .GenericsUtils .correctToGenericsSpec ;
6869import static org .codehaus .groovy .ast .tools .GenericsUtils .correctToGenericsSpecRecurse ;
6970import static org .codehaus .groovy .ast .tools .GenericsUtils .createGenericsSpec ;
@@ -349,10 +350,10 @@ private boolean isTypeUseScenario(AnnotationNode visited, int target) {
349350 }
350351
351352 private void processDuplicateAnnotationContainers (AnnotatedNode node , Map <String , List <AnnotationNode >> nonSourceAnnotations ) {
352- for (Map .Entry <String , List <AnnotationNode >> next : nonSourceAnnotations .entrySet ()) {
353- if (next .getValue ().size () > 1 ) {
353+ for (Map .Entry <String , List <AnnotationNode >> entry : nonSourceAnnotations .entrySet ()) {
354+ if (entry .getValue ().size () > 1 ) {
354355 ClassNode repeatable = null ;
355- AnnotationNode repeatee = next .getValue ().get (0 );
356+ AnnotationNode repeatee = entry .getValue ().get (0 );
356357 for (AnnotationNode anno : repeatee .getClassNode ().getAnnotations ()) {
357358 if (anno .getClassNode ().getName ().equals ("java.lang.annotation.Repeatable" )) {
358359 Expression value = anno .getMember ("value" );
@@ -364,29 +365,32 @@ private void processDuplicateAnnotationContainers(AnnotatedNode node, Map<String
364365 }
365366 if (repeatable != null ) {
366367 if (nonSourceAnnotations .containsKey (repeatable .getName ())) {
367- addError ("Cannot specify duplicate annotation on the same member. Explicit " + repeatable .getName ()
368- + " found when creating implicit container for " + next .getKey (), node );
368+ addError ("Cannot specify duplicate annotation on the same member. Explicit " + repeatable .getName () + " found when creating implicit container for " + entry .getKey (), node );
369369 }
370370 AnnotationNode collector = new AnnotationNode (repeatable );
371- if (repeatee .hasRuntimeRetention ()) {
371+ if (repeatee .hasClassRetention ()) {
372+ collector .setClassRetention (true );
373+ } else if (repeatee .hasRuntimeRetention ()) {
372374 collector .setRuntimeRetention (true );
373- } else if (repeatable .hasClass ()) { // GRECLIPSE edit
374- Class <?> repeatableType = repeatable .getTypeClass ();
375- Retention retention = repeatableType .getAnnotation (Retention .class );
376- collector .setRuntimeRetention (retention != null && retention .value ().equals (RetentionPolicy .RUNTIME ));
377- } else {
378- for (AnnotationNode annotation : repeatable .getAnnotations ()) {
379- if (annotation .getClassNode ().getName ().equals ("java.lang.annotation.Retention" )) {
380- Expression value = annotation .getMember ("value" ); assert value != null ;
381- Object retention = evaluateExpression (value , source .getConfiguration ());
382- collector .setRuntimeRetention (retention != null && retention .toString ().equals ("RUNTIME" ));
383- break ;
375+ } else { // load retention policy from annotation definition
376+ List <AnnotationNode > retention = repeatable .getAnnotations (makeCached (Retention .class ));
377+ if (!retention .isEmpty ()) {
378+ Object policy ;
379+ Expression value = retention .get (0 ).getMember ("value" );
380+ if (value instanceof PropertyExpression ) {
381+ policy = ((PropertyExpression ) value ).getPropertyAsString ();
382+ } else { // NOTE: it is risky to evaluate the expression from repeatable's source this way:
383+ policy = evaluateExpression (value , source .getConfiguration (), source .getClassLoader ());
384+ }
385+ if ("CLASS" .equals (policy )) {
386+ collector .setClassRetention (true );
387+ } else if ("RUNTIME" .equals (policy )) {
388+ collector .setRuntimeRetention (true );
384389 }
385390 }
386391 }
387- collector .addMember ("value" , new ListExpression (next .getValue ().stream ()
388- .map (AnnotationConstantExpression ::new ).collect (Collectors .toList ())));
389- node .getAnnotations ().removeAll (next .getValue ());
392+ collector .addMember ("value" , listX (entry .getValue ().stream ().map (AnnotationConstantExpression ::new ).collect (toList ())));
393+ node .getAnnotations ().removeAll (entry .getValue ());
390394 node .addAnnotation (collector );
391395 }
392396 }
0 commit comments