Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template matcher implemented as Filter #1110

Merged
merged 5 commits into from
Jan 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions doc/matcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ To define a template matcher one must:

1. specify the "holes" of the template matcher
1. write the matcher in a dedicated method
1. instantiate TemplateMatcher and call method `find`.
1. instantiate TemplateMatcher and call method `find` or use it as Filter of a query.

Taking again the same example.

Expand All @@ -53,9 +53,9 @@ CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtIf templateRoot = (CtIf) ((CtMethod) templateKlass.getElements(new NameFilter("matcher1")).get(0)).getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
for (CtElement elems : matcher.find(aPackage)) { ... };

//or TemplateMatcher as a Filter of query
aPackage.filterChildren(matcher).forEach((CtElement elem)->{ ... });
```

For named elements, a wildcard can be specified: if the named element (eg a method) to be matched is called `f` and the template matcher class contains a template parameter called `f` (of type Object), all methods starting by `f` will be matched.


43 changes: 15 additions & 28 deletions src/main/java/spoon/template/TemplateMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import spoon.reflect.reference.CtTypeParameterReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.Query;
import spoon.reflect.visitor.filter.InvocationFilter;
import spoon.support.template.DefaultParameterMatcher;
Expand All @@ -58,7 +59,7 @@
/**
* This class defines an engine for matching a template to pieces of code.
*/
public class TemplateMatcher {
public class TemplateMatcher implements Filter<CtElement> {

private List<CtInvocation<?>> getMethods(CtClass<? extends Template<?>> root) {
CtExecutableReference<?> methodRef = root.getFactory().Executable()
Expand Down Expand Up @@ -132,14 +133,18 @@ private List<CtFieldReference<?>> getVarargs(CtClass<? extends Template<?>> root
* Constructs a matcher for a given template.
*
*/
@SuppressWarnings("unchecked")
public TemplateMatcher(CtElement templateRoot) {
this.templateType = templateRoot.getParent(CtClass.class);
this.templateRoot = templateRoot;
variables = getMethods(templateType);
typeVariables = getTemplateTypeParameters(templateType);
names = getTemplateNameParameters(templateType);
varArgs = getVarargs(templateType, variables);
this.templateType = templateType;
//check that template matches itself
if (helperMatch(this.templateRoot, this.templateRoot) == false) {
throw new SpoonException("TemplateMatcher was unable to find itself, it certainly indicates a bug. Please revise your template or report an issue.");
}
}

private boolean addMatch(Object template, Object target) {
Expand Down Expand Up @@ -177,31 +182,7 @@ private CtElement checkListStatements(List<?> teList) {
* @return the matched elements
*/
public <T extends CtElement> List<T> find(final CtElement targetRoot) {
CtScanner scanner = new CtScanner() {
@Override
public void scan(CtElement element) {
if (match(element, templateRoot)) {
finds.add(element);
// matches.clear();
}
super.scan(element);
}
};

scanner.scan(templateRoot);
if (!finds.contains(templateRoot)) {
throw new SpoonException("TemplateMatcher was unable to find itself, it certainly indicates a bug. Please revise your template or report an issue.");
}
finds.clear();

scanner.scan(targetRoot);

// This case can occur when we are scanning the entire package for example see TemplateTest#testTemplateMatcherWithWholePackage
if (finds.contains(templateRoot)) {
finds.remove(templateRoot);
}

return (List<T>) finds;
return targetRoot.filterChildren(this).list();
}

/**
Expand Down Expand Up @@ -476,7 +457,13 @@ private boolean isCurrentTemplate(Object object, CtElement inMulti) {
* @return true if matches
* @see #getMatches()
*/
private boolean match(CtElement targetRoot, CtElement templateRoot) {
@Override
public boolean matches(CtElement targetRoot) {
if (targetRoot == templateRoot) {
// This case can occur when we are scanning the entire package for example see TemplateTest#testTemplateMatcherWithWholePackage
// Correct template matches itself of course, but client does not want that
return false;
}
return helperMatch(targetRoot, templateRoot);
}

Expand Down
13 changes: 11 additions & 2 deletions src/test/java/spoon/test/template/TemplateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import spoon.reflect.visitor.filter.NameFilter;
import spoon.support.compiler.FileSystemFile;
import spoon.support.template.Parameters;
import spoon.template.Substitution;
import spoon.template.TemplateMatcher;
import spoon.test.template.testclasses.SecurityCheckerTemplate;

Expand All @@ -28,7 +27,6 @@
import java.rmi.Remote;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -418,6 +416,7 @@ public void testTemplateMatcherMatchTwoSnippets() throws Exception {
CtIf templateRoot = (CtIf) templateMethod.getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);

//match using legacy TemplateMatcher#find method
List<CtElement> matches = matcher.find(factory.getModel().getRootPackage());

assertEquals(2, matches.size());
Expand All @@ -426,5 +425,15 @@ public void testTemplateMatcherMatchTwoSnippets() throws Exception {
CtElement match2 = matches.get(1);

assertTrue(match1.equals(match2));

//match using TemplateMatcher#matches method and query filter
matches = factory.getModel().getRootPackage().filterChildren(matcher).list();

assertEquals(2, matches.size());

match1 = matches.get(0);
match2 = matches.get(1);

assertTrue(match1.equals(match2));
}
}