@@ -92,6 +92,220 @@ public String getDescription() {
92
92
93
93
@ Override
94
94
public TreeVisitor <?, ExecutionContext > getVisitor () {
95
+ TreeVisitor <?, ExecutionContext > v = Preconditions .check (new UsesType <>(annotationType , false ), new JavaIsoVisitor <ExecutionContext >() {
96
+ private String attributeNameOrDefault () {
97
+ return attributeName == null ? "value" : attributeName ;
98
+ }
99
+
100
+ private List <JavaType .Method > getMethods (J .Annotation annotation ) {
101
+ return ((JavaType .FullyQualified ) requireNonNull (annotation .getAnnotationType ().getType ())).getMethods ();
102
+ }
103
+
104
+ private Optional <JavaType .Method > findMethod (J .Annotation annotation , String methodName ) {
105
+ for (JavaType .Method it : getMethods (annotation )) {
106
+ if (methodName .equals (it .getName ())) {
107
+ return Optional .of (it );
108
+ }
109
+ }
110
+ return Optional .empty ();
111
+ }
112
+
113
+ private String getUsefulNameFromFieldAccess (J .FieldAccess fa ) {
114
+ if (!(fa .getTarget () instanceof J .Identifier )) {
115
+ return fa .toString ();
116
+ }
117
+ return ((J .Identifier ) fa .getTarget ()).getSimpleName () + "." + fa .getSimpleName ();
118
+ }
119
+
120
+ private void addPossibleClassImports (@ Nullable String value ) {
121
+ if (value == null ) {
122
+ return ;
123
+ }
124
+ for (String singleVal : value .split ("," )) {
125
+ if (singleVal .endsWith (".class" ) && StringUtils .countOccurrences (singleVal , "." ) > 1 ) {
126
+ maybeAddImport (singleVal .substring (0 , singleVal .length () - 6 ));
127
+ }
128
+ }
129
+ }
130
+
131
+ private boolean attributeMatchesName (Expression e , String name ) {
132
+ if (e instanceof J .Assignment ) {
133
+ J .Assignment as = (J .Assignment ) e ;
134
+ if (as .getVariable () instanceof J .Identifier ) {
135
+ return ((J .Identifier ) as .getVariable ()).getSimpleName ().equals (name );
136
+ }
137
+ }
138
+ return name .equals ("value" );
139
+ }
140
+
141
+ private boolean alreadyContainsAttributeOfName (J .Annotation annotation , String name ) {
142
+ List <Expression > existingArguments = annotation .getArguments ();
143
+ if (existingArguments == null ) {
144
+ return false ;
145
+ }
146
+ for (Expression e : annotation .getArguments ()) {
147
+ if (attributeMatchesName (e , name )) {
148
+ return true ;
149
+ }
150
+ }
151
+ return false ;
152
+ }
153
+
154
+ private boolean valueMatches (Expression expression , String oldAttributeValue ) {
155
+ if (expression instanceof J .Literal ) {
156
+ return oldAttributeValue .equals (((J .Literal ) expression ).getValue ());
157
+ } else if (expression instanceof J .FieldAccess ) {
158
+ J .FieldAccess fa = (J .FieldAccess ) expression ;
159
+ String currentValue = getUsefulNameFromFieldAccess (fa );
160
+ return oldAttributeValue .equals (currentValue );
161
+ } else if (expression instanceof J .Identifier ) { // class names, static variables, ...
162
+ if (oldAttributeValue .endsWith (".class" )) {
163
+ String className = TypeUtils .toString (requireNonNull (expression .getType ())) + ".class" ;
164
+ return className .endsWith (oldAttributeValue );
165
+ }
166
+ return oldAttributeValue .equals (((J .Identifier ) expression ).getSimpleName ());
167
+ }
168
+ throw new IllegalArgumentException ("Unexpected expression type: " + expression .getClass ());
169
+ }
170
+
171
+ private J .Empty newEmpty () {
172
+ return new J .Empty (randomId (), SINGLE_SPACE , EMPTY );
173
+ }
174
+
175
+ private List <Expression > updateInitializerDroppingMatched (@ Nullable List <Expression > initializer , String searchValue ) {
176
+ List <Expression > updatedInitializer = ListUtils .filter (ListUtils .map (initializer , e -> {
177
+ if (valueMatches (e , searchValue )) {
178
+ return newEmpty ();
179
+ }
180
+ return e ;
181
+ }), e -> !(e instanceof J .Empty ));
182
+ return updatedInitializer == null ? emptyList () : updatedInitializer ;
183
+ }
184
+
185
+ private List <Expression > updateInitializerChangingMatched (@ Nullable List <Expression > initializer , String searchValue , String newValue ) {
186
+ List <Expression > updatedInitializer = ListUtils .map (initializer , e -> {
187
+ if (valueMatches (e , searchValue )) {
188
+ // TODO - Change from this to specific setup based on newValue and appendArray
189
+ return e ;
190
+ }
191
+ return e ;
192
+ });
193
+ return updatedInitializer == null ? emptyList () : updatedInitializer ;
194
+ }
195
+
196
+ // attributeValue == null
197
+ private J .Annotation tryRemoveAnnotationAttribute (J .Annotation annotation , String searchAttribute , @ Nullable String searchValue ) {
198
+ List <Expression > updatedArgs = ListUtils .map (annotation .getArguments (), it -> {
199
+ if (attributeMatchesName (it , searchAttribute )) {
200
+ if (searchValue == null ) {
201
+ return newEmpty ();
202
+ }
203
+ if (it instanceof J .Assignment ) {
204
+ J .Assignment as = (J .Assignment ) it ;
205
+ Expression asValue = as .getAssignment ();
206
+ if (asValue instanceof J .NewArray ) {
207
+ J .NewArray asArray = (J .NewArray ) asValue ;
208
+ List <Expression > updatedInitializer = updateInitializerDroppingMatched (asArray .getInitializer (), searchValue );
209
+ return as .withAssignment (asArray .withInitializer (updatedInitializer ));
210
+ }
211
+ if (valueMatches (asValue , searchValue )) {
212
+ return newEmpty ();
213
+ }
214
+ } else if (it instanceof J .NewArray ) {
215
+ J .NewArray itArray = (J .NewArray ) it ;
216
+ List <Expression > updatedInitializer = updateInitializerDroppingMatched (itArray .getInitializer (), searchValue );
217
+ return itArray .withInitializer (updatedInitializer );
218
+ } else if (valueMatches (it , searchValue )) {
219
+ return newEmpty ();
220
+ }
221
+ }
222
+ return it ;
223
+ });
224
+ return annotation .withArguments (ListUtils .filter (updatedArgs , it -> !(it instanceof J .Empty )));
225
+ }
226
+
227
+ private J .Annotation tryAddAnnotationAttribute (J .Annotation annotation , String newAttribute , String newValue ) {
228
+ // TODO
229
+ return annotation ;
230
+ }
231
+
232
+ private J .Annotation tryUpdateAnnotationAttribute (J .Annotation annotation , String searchAttribute , @ Nullable String searchValue , String newValue ) {
233
+ List <Expression > updatedArgs = ListUtils .map (annotation .getArguments (), it -> {
234
+ if (attributeMatchesName (it , searchAttribute )) {
235
+ if (searchValue == null ) {
236
+ if (it instanceof J .Assignment ) {
237
+ J .Assignment as = (J .Assignment ) it ;
238
+ // TODO - overwriting using as.withAssignment(...), but differs by newValue typing and appendArray
239
+ }
240
+ // TODO - overwriting using new, but differs by newValue typing and appendArray
241
+ } else {
242
+ if (it instanceof J .Assignment ) {
243
+ J .Assignment as = (J .Assignment ) it ;
244
+ Expression asValue = as .getAssignment ();
245
+ if (asValue instanceof J .NewArray ) {
246
+ J .NewArray asArray = (J .NewArray ) asValue ;
247
+ List <Expression > updatedInitializer = updateInitializerChangingMatched (asArray .getInitializer (), searchValue , newValue );
248
+ return as .withAssignment (asArray .withInitializer (updatedInitializer ));
249
+ }
250
+ if (valueMatches (asValue , searchValue )) {
251
+ // TODO instantiate the correct typing
252
+ }
253
+ }
254
+ // TODO: else
255
+ }
256
+ }
257
+ return it ;
258
+ });
259
+ return annotation .withArguments (updatedArgs );
260
+ }
261
+
262
+ @ Override
263
+ public J .Annotation visitAnnotation (J .Annotation original , ExecutionContext ctx ) {
264
+ J .Annotation a = super .visitAnnotation (original , ctx );
265
+ String searchAttribute = attributeNameOrDefault ();
266
+ String searchValue = oldAttributeValue ;
267
+ // if not the right type of annotation or cannot find the method for a non-shallow class
268
+ if (
269
+ !TypeUtils .isOfClassType (a .getType (), annotationType ) ||
270
+ !(a .getType () instanceof JavaType .ShallowClass || findMethod (a , searchAttribute ).isPresent ())
271
+ ) {
272
+ return a ;
273
+ }
274
+ boolean existingAttribute = alreadyContainsAttributeOfName (a , searchAttribute );
275
+ // if only want to add, but it already has attribute, ignores new attributeValue
276
+ if (TRUE .equals (addOnly ) && existingAttribute ) {
277
+ return a ;
278
+ }
279
+
280
+ // if you want to remove
281
+ if (attributeValue == null ) {
282
+ // if you can't update anything
283
+ if (!existingAttribute || TRUE .equals (addOnly )) {
284
+ return a ;
285
+ }
286
+ a = tryRemoveAnnotationAttribute (a , searchAttribute , searchValue );
287
+ } else {
288
+ // if you can't update anything
289
+ if (existingAttribute && TRUE .equals (addOnly )) {
290
+ return a ;
291
+ }
292
+ if (existingAttribute ) {
293
+ a = tryUpdateAnnotationAttribute (a , searchAttribute , searchValue , attributeValue );
294
+ } else {
295
+ a = tryAddAnnotationAttribute (a , searchAttribute , attributeValue );
296
+ }
297
+ }
298
+ addPossibleClassImports (attributeValue );
299
+
300
+ // TODO: double check this (and also simplification in general)
301
+ if (original != a ) {
302
+ doAfterVisit (new SimplifySingleElementAnnotation ().getVisitor ());
303
+ }
304
+ return maybeAutoFormat (original , a , ctx );
305
+ }
306
+ });
307
+
308
+
95
309
return Preconditions .check (new UsesType <>(annotationType , false ), new JavaIsoVisitor <ExecutionContext >() {
96
310
@ Override
97
311
public J .Annotation visitAnnotation (J .Annotation original , ExecutionContext ctx ) {
@@ -370,6 +584,26 @@ private String attributeName() {
370
584
}
371
585
372
586
private List <Expression > updateInitializer (J .Annotation annotation , List <Expression > initializerList , List <String > attributeList ) {
587
+ if (TRUE .equals (appendArray )) {
588
+ if (oldAttributeValue != null ) {
589
+ // if initializer contains old attribute value
590
+ // append new values (de-duped) to end of attribute's existing value
591
+ // else
592
+ // do not append
593
+ } else {
594
+ // append new values (de-duped) to end of attribute's existing value
595
+ }
596
+ } else {
597
+ if (oldAttributeValue != null ) {
598
+ // if initializer contains old attribute value
599
+ // replace existing old attribute value in initializer with new values
600
+ // else
601
+ // do not replace
602
+ } else {
603
+ // replace initializer with new values
604
+ }
605
+ }
606
+
373
607
// If `oldAttributeValue` is defined, replace the old value with the new value(s). Ignore the `appendArray` option in this case.
374
608
if (oldAttributeValue != null ) {
375
609
return ListUtils .flatMap (initializerList , it -> {
@@ -379,10 +613,16 @@ private List<Expression> updateInitializer(J.Annotation annotation, List<Express
379
613
J .Literal newLiteral = new J .Literal (randomId (), SINGLE_SPACE , EMPTY , attribute , maybeQuoteStringArgument (annotation , attribute ), null , JavaType .Primitive .String );
380
614
newItemsList .add (newLiteral );
381
615
}
382
- return newItemsList ;
616
+ if (!TRUE .equals (appendArray )) {
617
+ return newItemsList ;
618
+ }
383
619
} else if (it instanceof J .Empty ) {
384
620
return new ArrayList <>();
385
621
}
622
+ if (TRUE .equals (appendArray )) {
623
+ newItemsList .add (0 , it );
624
+ return newItemsList ;
625
+ }
386
626
return it ;
387
627
});
388
628
}
0 commit comments