forked from openrewrite/rewrite
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7a34304
commit 69addbf
Showing
8 changed files
with
455 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
rewrite-java/src/main/java/org/openrewrite/java/trait/Annotated.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* Copyright 2024 the original author or authors. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.openrewrite.java.trait; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.Value; | ||
import org.openrewrite.Cursor; | ||
import org.openrewrite.internal.lang.Nullable; | ||
import org.openrewrite.java.AnnotationMatcher; | ||
import org.openrewrite.java.tree.Expression; | ||
import org.openrewrite.java.tree.J; | ||
import org.openrewrite.trait.SimpleTraitMatcher; | ||
import org.openrewrite.trait.Trait; | ||
|
||
import java.util.Optional; | ||
|
||
@Value | ||
public class Annotated implements Trait<J.Annotation> { | ||
Cursor cursor; | ||
|
||
/** | ||
* @param defaultAlias The name of the annotation attribute that is aliased to | ||
* "value", if any. | ||
* @return The attribute value. | ||
*/ | ||
public Optional<Literal> getDefaultAttribute(@Nullable String defaultAlias) { | ||
if (getTree().getArguments() == null) { | ||
return Optional.empty(); | ||
} | ||
for (Expression argument : getTree().getArguments()) { | ||
if (!(argument instanceof J.Assignment)) { | ||
return new Literal.Matcher().get(argument, cursor); | ||
} | ||
} | ||
Optional<Literal> valueAttr = getAttribute("value"); | ||
if (valueAttr.isPresent()) { | ||
return valueAttr; | ||
} | ||
return defaultAlias != null ? | ||
getAttribute(defaultAlias) : | ||
Optional.empty(); | ||
} | ||
|
||
public Optional<Literal> getAttribute(String attribute) { | ||
if (getTree().getArguments() == null) { | ||
return Optional.empty(); | ||
} | ||
for (Expression argument : getTree().getArguments()) { | ||
if (argument instanceof J.Assignment) { | ||
J.Assignment assignment = (J.Assignment) argument; | ||
if (assignment.getVariable() instanceof J.Identifier) { | ||
J.Identifier identifier = (J.Identifier) assignment.getVariable(); | ||
if (identifier.getSimpleName().equals(attribute)) { | ||
return new Literal.Matcher().get( | ||
assignment.getAssignment(), | ||
new Cursor(cursor, argument) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@RequiredArgsConstructor | ||
public static class Matcher extends SimpleTraitMatcher<Annotated> { | ||
private final AnnotationMatcher matcher; | ||
|
||
public Matcher(String signature) { | ||
this.matcher = new AnnotationMatcher(signature); | ||
} | ||
|
||
@Override | ||
protected @Nullable Annotated test(Cursor cursor) { | ||
Object value = cursor.getValue(); | ||
if (value instanceof J.Annotation) { | ||
J.Annotation annotation = (J.Annotation) value; | ||
if (matcher.matches(annotation)) { | ||
return new Annotated(cursor); | ||
} | ||
} | ||
return null; | ||
} | ||
} | ||
} |
139 changes: 139 additions & 0 deletions
139
rewrite-java/src/main/java/org/openrewrite/java/trait/Literal.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/* | ||
* Copyright 2024 the original author or authors. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.openrewrite.java.trait; | ||
|
||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.JavaType; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
import org.openrewrite.Cursor; | ||
import org.openrewrite.internal.lang.Nullable; | ||
import org.openrewrite.java.tree.Expression; | ||
import org.openrewrite.java.tree.J; | ||
import org.openrewrite.trait.SimpleTraitMatcher; | ||
import org.openrewrite.trait.Trait; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import static java.util.Collections.singletonList; | ||
import static java.util.Objects.requireNonNull; | ||
|
||
/** | ||
* A literal in Java is either a {@link J.Literal} or a {@link J.NewArray} | ||
* with a non-null initializer that itself literals or new arrays that recursively | ||
* contain these constraints. In other languages this trait is inclusive | ||
* of constructs like list or map literals. | ||
*/ | ||
@RequiredArgsConstructor | ||
public class Literal implements Trait<Expression> { | ||
@Getter | ||
private final Cursor cursor; | ||
|
||
private final ObjectMapper mapper; | ||
|
||
public boolean isNull() { | ||
return getTree() instanceof J.Literal && ((J.Literal) getTree()).getValue() == null; | ||
} | ||
|
||
public boolean isNotNull() { | ||
return !isNull(); | ||
} | ||
|
||
public <@Nullable T> T getValue(Class<T> type) { | ||
return getValue(mapper.constructType(type)); | ||
} | ||
|
||
public <@Nullable T> T getValue(TypeReference<T> type) { | ||
return getValue(mapper.constructType(type)); | ||
} | ||
|
||
public <@Nullable T> T getValue(JavaType type) { | ||
Expression lit = getTree(); | ||
if (lit instanceof J.Literal) { | ||
J.Literal literal = (J.Literal) lit; | ||
if (literal.getValue() == null) { | ||
//noinspection DataFlowIssue | ||
return null; | ||
} else if (type.isCollectionLikeType()) { | ||
List<?> l = singletonList(literal.getValue()); | ||
return mapper.convertValue(l, type); | ||
} else { | ||
return mapper.convertValue(literal.getValue(), type); | ||
} | ||
} else if (lit instanceof J.NewArray) { | ||
List<Object> untyped = untypedInitializerLiterals((J.NewArray) lit); | ||
return mapper.convertValue(untyped, type); | ||
} | ||
//noinspection DataFlowIssue | ||
return null; | ||
} | ||
|
||
private List<Object> untypedInitializerLiterals(J.NewArray newArray) { | ||
List<Object> acc = new ArrayList<>(); | ||
for (Expression init : requireNonNull(newArray.getInitializer())) { | ||
if (init instanceof J.Literal) { | ||
acc.add(((J.Literal) init).getValue()); | ||
} else { | ||
acc.add(untypedInitializerLiterals((J.NewArray) init)); | ||
} | ||
} | ||
return acc; | ||
} | ||
|
||
public static class Matcher extends SimpleTraitMatcher<Literal> { | ||
private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper(); | ||
|
||
private ObjectMapper mapper = DEFAULT_MAPPER; | ||
|
||
/** | ||
* @param mapper A customized mapper, which should be rare, | ||
* but possibly when you want a custom type factory. | ||
* @return This matcher with a customized mapper set. | ||
*/ | ||
public Matcher mapper(ObjectMapper mapper) { | ||
this.mapper = mapper; | ||
return this; | ||
} | ||
|
||
@Override | ||
protected @Nullable Literal test(Cursor cursor) { | ||
Object value = cursor.getValue(); | ||
return value instanceof J.Literal || | ||
isNewArrayWithLiteralInitializer(value) ? | ||
new Literal(cursor, mapper) : | ||
null; | ||
} | ||
|
||
private boolean isNewArrayWithLiteralInitializer(Object value) { | ||
if (value instanceof J.NewArray) { | ||
List<Expression> init = ((J.NewArray) value).getInitializer(); | ||
if (init == null) { | ||
return false; | ||
} | ||
for (Expression expr : init) { | ||
if (!(expr instanceof J.Literal) && | ||
!isNewArrayWithLiteralInitializer(expr)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
rewrite-java/src/main/java/org/openrewrite/java/trait/Traits.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright 2024 the original author or authors. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.openrewrite.java.trait; | ||
|
||
import org.openrewrite.java.AnnotationMatcher; | ||
import org.openrewrite.java.MethodMatcher; | ||
|
||
public class Traits { | ||
|
||
public static Literal.Matcher literal() { | ||
return new Literal.Matcher(); | ||
} | ||
|
||
public static VariableAccess.Matcher variableAccess() { | ||
return new VariableAccess.Matcher(); | ||
} | ||
|
||
public static MethodAccess.Matcher methodAccess(MethodMatcher matcher) { | ||
return new MethodAccess.Matcher(matcher); | ||
} | ||
|
||
public static MethodAccess.Matcher methodAccess(String signature) { | ||
return new MethodAccess.Matcher(signature); | ||
} | ||
|
||
public static Annotated.Matcher annotated(AnnotationMatcher matcher) { | ||
return new Annotated.Matcher(matcher); | ||
} | ||
|
||
public static Annotated.Matcher annotated(String signature) { | ||
return new Annotated.Matcher(signature); | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
rewrite-java/src/test/java/org/openrewrite/java/trait/AnnotatedTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright 2024 the original author or authors. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.openrewrite.java.trait; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.openrewrite.marker.SearchResult; | ||
import org.openrewrite.test.RewriteTest; | ||
|
||
import static org.openrewrite.java.Assertions.java; | ||
import static org.openrewrite.java.trait.Traits.annotated; | ||
|
||
class AnnotatedTest implements RewriteTest { | ||
|
||
@Test | ||
void attributes() { | ||
rewriteRun( | ||
spec -> spec.recipe(RewriteTest.toRecipe(() -> | ||
annotated("@Example").asVisitor(a -> SearchResult.found(a.getTree(), | ||
a.getDefaultAttribute("name") | ||
.map(lit -> lit.getValue(String.class)) | ||
.orElse("unknown")) | ||
) | ||
)), | ||
java( | ||
""" | ||
import java.lang.annotation.Repeatable; | ||
@Repeatable | ||
@interface Example { | ||
String value() default ""; | ||
String name() default ""; | ||
} | ||
""" | ||
), | ||
java( | ||
""" | ||
@Example("test") | ||
@Example(value = "test") | ||
@Example(name = "test") | ||
class Test { | ||
} | ||
""", | ||
""" | ||
/*~~(test)~~>*/@Example("test") | ||
/*~~(test)~~>*/@Example(value = "test") | ||
/*~~(test)~~>*/@Example(name = "test") | ||
class Test { | ||
} | ||
""" | ||
) | ||
); | ||
} | ||
} |
Oops, something went wrong.