-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(matcher): Add new matchers for spoon elements (#804)
- Loading branch information
1 parent
e92cdb3
commit 43a8483
Showing
6 changed files
with
249 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
plugins { | ||
id 'xyz.keksdose.spoon.code_solver.java-common-conventions' | ||
} |
60 changes: 60 additions & 0 deletions
60
matcher/src/main/java/io/github/martinwitt/laughing_train/spoonutils/ConstructorMatcher.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,60 @@ | ||
package io.github.martinwitt.laughing_train.spoonutils; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
import spoon.reflect.code.CtConstructorCall; | ||
import spoon.reflect.code.CtExpression; | ||
import spoon.reflect.reference.CtTypeReference; | ||
import spoon.reflect.visitor.Filter; | ||
|
||
/** | ||
* A filter for matching constructor calls with a specific target type and argument types. | ||
*/ | ||
public class ConstructorMatcher implements Filter<CtConstructorCall<?>> { | ||
|
||
private final String fqTargetType; | ||
private final String[] argsFQN; | ||
|
||
/** | ||
* Creates a new constructor matcher with the given target type and argument types. | ||
* | ||
* @param fqTargetType the fully-qualified name of the target type | ||
* @param argsFQN the fully-qualified names of the argument types | ||
*/ | ||
public ConstructorMatcher(String fqTargetType, String... argsFQN) { | ||
this.fqTargetType = fqTargetType; | ||
this.argsFQN = argsFQN; | ||
} | ||
|
||
/** | ||
* Determines whether the given constructor call matches the target type and argument types. | ||
* | ||
* @param element the constructor call to match | ||
* @return true if the constructor call matches the target type and argument types, false otherwise | ||
*/ | ||
@Override | ||
public boolean matches(CtConstructorCall<?> element) { | ||
if (element == null) { | ||
return false; | ||
} | ||
|
||
if (!element.getType().getQualifiedName().equals(fqTargetType)) { | ||
return false; | ||
} | ||
if (argsFQN == null || argsFQN.length == 0) { | ||
return true; | ||
} | ||
if (element.getArguments().size() != argsFQN.length) { | ||
return false; | ||
} | ||
|
||
List<Pair<CtTypeReference<?>, CtExpression<?>>> zipped = new ArrayList<>(); | ||
for (int i = 0; i < argsFQN.length && i < element.getArguments().size(); i++) { | ||
zipped.add(Pair.of( | ||
element.getFactory().createReference(argsFQN[i]), | ||
element.getArguments().get(i))); | ||
} | ||
return zipped.stream().allMatch(pair -> pair.getRight().getType().isSubtypeOf(pair.getLeft())); | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
matcher/src/main/java/io/github/martinwitt/laughing_train/spoonutils/InvocationMatcher.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,82 @@ | ||
package io.github.martinwitt.laughing_train.spoonutils; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
import spoon.reflect.code.CtExpression; | ||
import spoon.reflect.code.CtInvocation; | ||
import spoon.reflect.code.CtTypeAccess; | ||
import spoon.reflect.reference.CtTypeReference; | ||
import spoon.reflect.visitor.Filter; | ||
|
||
/** | ||
* A matcher that checks if a given {@link CtInvocation} object matches a specified target type, method name, and argument types. | ||
*/ | ||
public class InvocationMatcher implements Filter<CtInvocation<?>> { | ||
private final String fqTargetType; | ||
private final String methodName; | ||
private final String[] argsFQN; | ||
|
||
/** | ||
* Creates a new {@link InvocationMatcher} object with the specified target type, method name, and argument types. | ||
* @param fqTargetType the fully qualified name of the target type | ||
* @param methodName the name of the method | ||
* @param argsFQN the fully qualified names of the argument types | ||
*/ | ||
public InvocationMatcher(String fqTargetType, String methodName, String... argsFQN) { | ||
this.fqTargetType = fqTargetType; | ||
this.methodName = methodName; | ||
this.argsFQN = argsFQN; | ||
} | ||
|
||
/** | ||
* Checks if the specified {@link CtInvocation} object matches the target type, method name, and argument types of this {@link InvocationMatcher}. | ||
* @param element the {@link CtInvocation} object to check | ||
* @return true if the invocation matches, false otherwise | ||
*/ | ||
public boolean matches(CtInvocation<?> element) { | ||
if (element == null) { | ||
return false; | ||
} | ||
|
||
// Check if the target type matches | ||
CtExpression<?> target = element.getTarget(); | ||
if (target == null || target.getType() == null) { | ||
return false; | ||
} | ||
if (target instanceof CtTypeAccess access) { | ||
if (!access.getAccessedType().getQualifiedName().equals(fqTargetType)) { | ||
return false; | ||
} | ||
} else { | ||
if (!target.getType().getQualifiedName().equals(fqTargetType)) { | ||
return false; | ||
} | ||
} | ||
|
||
// Check if the method name matches | ||
if (Optional.ofNullable(element.getExecutable()) | ||
.map(v -> v.getExecutableDeclaration()) | ||
.filter(v -> v.getSimpleName().equals(methodName)) | ||
.isEmpty()) { | ||
return false; | ||
} | ||
|
||
// Check if the argument types match | ||
if (argsFQN == null || argsFQN.length == 0) { | ||
return true; | ||
} | ||
if (element.getArguments().size() != argsFQN.length) { | ||
return false; | ||
} | ||
|
||
List<Pair<CtTypeReference<?>, CtExpression<?>>> zipped = new ArrayList<>(); | ||
for (int i = 0; i < argsFQN.length && i < element.getArguments().size(); i++) { | ||
zipped.add(Pair.of( | ||
element.getFactory().createReference(argsFQN[i]), | ||
element.getArguments().get(i))); | ||
} | ||
return zipped.stream().allMatch(pair -> pair.getRight().getType().isSubtypeOf(pair.getLeft())); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
matcher/src/main/java/io/github/martinwitt/laughing_train/spoonutils/matcher/Matcher.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,18 @@ | ||
package io.github.martinwitt.laughing_train.spoonutils.matcher; | ||
|
||
/** | ||
* A functional interface for matching elements of a certain type. | ||
* | ||
* @param <T> the type of elements to match | ||
*/ | ||
@FunctionalInterface | ||
public interface Matcher<T> { | ||
|
||
/** | ||
* Determines whether the given element matches a certain criteria. | ||
* | ||
* @param element the element to match | ||
* @return true if the element matches the criteria, false otherwise | ||
*/ | ||
boolean matches(T element); | ||
} |
84 changes: 84 additions & 0 deletions
84
matcher/src/main/java/io/github/martinwitt/laughing_train/spoonutils/matcher/Matchers.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,84 @@ | ||
package io.github.martinwitt.laughing_train.spoonutils.matcher; | ||
|
||
import spoon.reflect.code.CtExpression; | ||
import spoon.reflect.code.CtLiteral; | ||
import spoon.reflect.declaration.CtModifiable; | ||
import spoon.reflect.declaration.CtType; | ||
import spoon.reflect.declaration.ModifierKind; | ||
|
||
/** | ||
* A utility class for creating matchers for Spoon elements. | ||
*/ | ||
public final class Matchers { | ||
|
||
/** | ||
* Returns a matcher that matches elements that are public. | ||
* | ||
* @return a matcher that matches elements that are public | ||
*/ | ||
public static Matcher<CtModifiable> isPublic() { | ||
return v -> v.getModifiers().contains(ModifierKind.PUBLIC); | ||
} | ||
|
||
/** | ||
* Returns a matcher that matches elements that are private. | ||
* | ||
* @return a matcher that matches elements that are private | ||
*/ | ||
public static Matcher<CtModifiable> isPrivate() { | ||
return v -> v.getModifiers().contains(ModifierKind.PRIVATE); | ||
} | ||
|
||
/** | ||
* Returns a matcher that matches elements that are enums. | ||
* | ||
* @return a matcher that matches elements that are enums | ||
*/ | ||
public static Matcher<CtType<?>> isEnum() { | ||
return v -> v.isEnum(); | ||
} | ||
|
||
/** | ||
* Returns a matcher that matches elements that are integer literals with the given value. | ||
* | ||
* @param literal the value of the integer literal to match | ||
* @return a matcher that matches elements that are integer literals with the given value | ||
*/ | ||
public static Matcher<CtExpression<?>> isLiteral(int literal) { | ||
return v -> v instanceof CtLiteral | ||
&& ((CtLiteral<?>) v).getValue() instanceof Integer value | ||
&& value.equals(literal); | ||
} | ||
|
||
/** | ||
* Returns a matcher that matches elements that are final. | ||
* | ||
* @return a matcher that matches elements that are final | ||
*/ | ||
public static Matcher<CtModifiable> isFinal() { | ||
return v -> v.getModifiers().contains(ModifierKind.FINAL); | ||
} | ||
|
||
/** | ||
* Returns a matcher that matches elements that match all of the given matchers. | ||
* | ||
* @param matchers the matchers to match | ||
* @param <T> the type of elements to match | ||
* @return a matcher that matches elements that match all of the given matchers | ||
*/ | ||
@SafeVarargs | ||
public static <T> Matcher<T> allOf(Matcher<T>... matchers) { | ||
return v -> { | ||
for (Matcher<T> matcher : matchers) { | ||
if (!matcher.matches(v)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
} | ||
|
||
private Matchers() { | ||
throw new AssertionError("Utility class should not be instantiated"); | ||
} | ||
} |
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 |
---|---|---|
@@ -1,2 +1,2 @@ | ||
rootProject.name = 'laughing-train-project' | ||
include(':code-transformation',":commons", ":github-bot", ":application") | ||
include(':code-transformation',":commons", ":github-bot", ":application", ":matcher") |