use of spoon.reflect.declaration.CtElement in project spoon by INRIA.
the class LocalVariableReferenceFunction method apply.
@Override
public void apply(final CtElement scope, CtConsumer<Object> outputConsumer) {
CtVariable<?> var = targetVariable;
if (var == null) {
if (variableClass.isInstance(scope)) {
var = (CtVariable<?>) scope;
} else {
throw new SpoonException("The input of " + getClass().getSimpleName() + " must be a " + variableClass.getSimpleName() + " but is " + scope.getClass().getSimpleName());
}
}
final CtVariable<?> variable = var;
final String simpleName = variable.getSimpleName();
// the context which knows whether we are scanning in scope of local type or not
final Context context = new Context();
CtQuery scopeQuery;
if (scope == variable) {
// we are starting search from local variable declaration
scopeQuery = createScopeQuery(variable, scope, context);
} else {
// we are starting search later, somewhere deep in scope of variable declaration
final CtElement variableParent = variable.getParent();
/*
* search in parents of searching scope for the variableParent
* 1) to check that scope is a child of variableParent
* 2) to detect if there is an local class between variable declaration and scope
*/
if (scope.map(new ParentFunction()).select(new Filter<CtElement>() {
@Override
public boolean matches(CtElement element) {
if (element instanceof CtType) {
// detected that the search scope is in local class declared in visibility scope of variable
context.nrTypes++;
}
return variableParent == element;
}
}).first() == null) {
// the scope is not under children of localVariable
throw new SpoonException("Cannot search for references of variable in wrong scope.");
}
// search in all children of the scope element
scopeQuery = scope.map(new CtScannerFunction().setListener(context));
}
scopeQuery.select(new Filter<CtElement>() {
@Override
public boolean matches(CtElement element) {
if (variableReferenceClass.isInstance(element)) {
CtVariableReference<?> varRef = (CtVariableReference<?>) element;
if (simpleName.equals(varRef.getSimpleName())) {
// we have found a variable reference of required type in visibility scope of targetVariable
if (context.hasLocalType()) {
// so finally check that found variable reference is really a reference to target variable
return variable == varRef.getDeclaration();
}
// else we can be sure that found reference is reference to variable
return true;
}
}
return false;
}
}).forEach(outputConsumer);
}
use of spoon.reflect.declaration.CtElement in project spoon by INRIA.
the class APITest method testSetterInNodes.
@Test
public void testSetterInNodes() throws Exception {
// that the new value is != null to avoid NPE when we set the parent.
class SetterMethodWithoutCollectionsFilter extends TypeFilter<CtMethod<?>> {
private final List<CtTypeReference<?>> collections = new ArrayList<>(4);
public SetterMethodWithoutCollectionsFilter(Factory factory) {
super(CtMethod.class);
for (Class<?> aCollectionClass : Arrays.asList(Collection.class, List.class, Map.class, Set.class)) {
collections.add(factory.Type().createReference(aCollectionClass));
}
}
@Override
public boolean matches(CtMethod<?> element) {
boolean isSetter = isSetterMethod(element);
boolean isNotSubType = !isSubTypeOfCollection(element);
// setter with unsettableProperty should not respect the contract, as well as derived properties
boolean doesNotHaveUnsettableAnnotation = doesNotHaveUnsettableAnnotation(element);
boolean isNotSetterForADerivedProperty = isNotSetterForADerivedProperty(element);
boolean superMatch = super.matches(element);
return isSetter && doesNotHaveUnsettableAnnotation && isNotSetterForADerivedProperty && isNotSubType && superMatch;
}
private boolean isNotSetterForADerivedProperty(CtMethod<?> method) {
String methodName = method.getSimpleName();
String getterName = methodName.replace("set", "get");
if (getterName.equals(methodName)) {
return false;
}
CtType<?> zeClass = (CtType) method.getParent();
List<CtMethod<?>> getterMethods = zeClass.getMethodsByName(getterName);
if (getterMethods.size() != 1) {
return false;
}
CtMethod<?> getterMethod = getterMethods.get(0);
return (getterMethod.getAnnotation(DerivedProperty.class) == null);
}
private boolean doesNotHaveUnsettableAnnotation(CtMethod<?> element) {
return (element.getAnnotation(UnsettableProperty.class) == null);
}
private boolean isSubTypeOfCollection(CtMethod<?> element) {
final List<CtParameter<?>> parameters = element.getParameters();
if (parameters.size() != 1) {
return false;
}
final CtTypeReference<?> type = parameters.get(0).getType();
for (CtTypeReference<?> aCollectionRef : collections) {
if (type.isSubtypeOf(aCollectionRef) || type.equals(aCollectionRef)) {
return true;
}
}
return false;
}
private boolean isSetterMethod(CtMethod<?> element) {
final List<CtParameter<?>> parameters = element.getParameters();
if (parameters.size() != 1) {
return false;
}
final CtTypeReference<?> typeParameter = parameters.get(0).getType();
final CtTypeReference<CtElement> ctElementRef = element.getFactory().Type().createReference(CtElement.class);
// isSubtypeOf will return true in case of equality
boolean isSubtypeof = typeParameter.isSubtypeOf(ctElementRef);
if (!isSubtypeof) {
return false;
}
return element.getSimpleName().startsWith("set") && element.getDeclaringType().getSimpleName().startsWith("Ct") && element.getBody() != null;
}
}
class CheckNotNullToSetParentMatcher extends CtElementImpl {
public TemplateParameter<CtVariableAccess<?>> _parameter_access_;
public void matcher() {
if (_parameter_access_.S() != null) {
_parameter_access_.S().setParent(this);
}
}
@Override
@Local
public void accept(CtVisitor visitor) {
}
}
final Launcher launcher = new Launcher();
launcher.setArgs(new String[] { "--output-type", "nooutput" });
launcher.getEnvironment().setNoClasspath(true);
// Implementations
launcher.addInputResource("./src/main/java/spoon/support/reflect/code");
launcher.addInputResource("./src/main/java/spoon/support/reflect/declaration");
launcher.addInputResource("./src/main/java/spoon/support/reflect/reference");
launcher.addInputResource("./src/test/java/" + this.getClass().getCanonicalName().replace(".", "/") + ".java");
// Needed for #isSubTypeOf method.
launcher.addInputResource("./src/main/java/spoon/reflect/");
launcher.buildModel();
// Template matcher.
CtClass<CheckNotNullToSetParentMatcher> matcherCtClass = launcher.getFactory().Class().get(CheckNotNullToSetParentMatcher.class);
CtIf templateRoot = matcherCtClass.getMethod("matcher").getBody().getStatement(0);
final List<CtMethod<?>> setters = Query.getElements(launcher.getFactory(), new SetterMethodWithoutCollectionsFilter(launcher.getFactory()));
assertTrue("Number of setters found null", setters.size() > 0);
for (CtStatement statement : setters.stream().map((Function<CtMethod<?>, CtStatement>) ctMethod -> ctMethod.getBody().getStatement(0)).collect(Collectors.toList())) {
// First statement should be a condition to protect the setter of the parent.
assertTrue("Check the method " + statement.getParent(CtMethod.class).getSignature() + " in the declaring class " + statement.getParent(CtType.class).getQualifiedName(), statement instanceof CtIf);
CtIf ifCondition = (CtIf) statement;
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
assertEquals("Check the number of if in method " + statement.getParent(CtMethod.class).getSignature() + " in the declaring class " + statement.getParent(CtType.class).getQualifiedName(), 1, matcher.find(ifCondition).size());
}
}
use of spoon.reflect.declaration.CtElement in project spoon by INRIA.
the class CommentTest method testDocumentationContract.
@Test
public void testDocumentationContract() throws Exception {
// contract: all metamodel classes must be commented with an example.
final Launcher launcher = new Launcher();
launcher.getEnvironment().setNoClasspath(true);
launcher.getEnvironment().setCommentEnabled(true);
// interfaces.
launcher.addInputResource("./src/main/java/spoon/reflect/");
launcher.addInputResource("./src/main/java/spoon/support/reflect/");
launcher.buildModel();
StringBuffer codeElementsDocumentationPage = new StringBuffer();
codeElementsDocumentationPage.append(IOUtils.toString(new FileReader("doc/code_elements_header.md")));
codeElementsDocumentationPage.append("\n\n");
launcher.getModel().getElements(new TypeFilter<>(CtInterface.class)).stream().forEach(x -> {
assertTrue(x.getSimpleName() + " has no documentation", x.getDocComment() != null);
assertTrue(x.getSimpleName() + " has no documentation", x.getDocComment().length() > 0);
// we only consider instantiable interfaces
if (launcher.getModel().getElements(new AbstractFilter<CtElement>() {
@Override
public boolean matches(CtElement element) {
return (element instanceof CtNamedElement) && ((CtNamedElement) element).getSimpleName().equals(x.getSimpleName() + "Impl") && (element instanceof CtClass) && !((CtClass) element).hasModifier(ModifierKind.ABSTRACT);
}
}).size() == 0) {
return;
}
// we don't consider references
if (x.getSimpleName().endsWith("Reference")) {
return;
}
if (x.isSubtypeOf(launcher.getFactory().Type().get(CtStatement.class).getReference()) || x.isSubtypeOf(launcher.getFactory().Type().get(CtExpression.class).getReference())) {
// no meaningful snippet
if (x.getSimpleName().equals("CtCodeSnippetStatement")) {
return;
}
// no meaningful snippet
if (x.getSimpleName().equals("CtCodeSnippetExpression")) {
return;
}
// no comment in snippet mode
if (x.getSimpleName().equals("CtComment")) {
return;
}
// a statement in really rare cases
if (x.getSimpleName().equals("CtEnum")) {
return;
}
// too hard to snippetize
if (x.getSimpleName().equals("CtAnnotationFieldAccess")) {
return;
}
codeElementsDocumentationPage.append("### " + x.getSimpleName() + "\n");
codeElementsDocumentationPage.append("[(javadoc)](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/" + x.getQualifiedName().replace('.', '/') + ".html)\n\n");
codeElementsDocumentationPage.append("```java" + "\n");
Pattern p = Pattern.compile("<pre>(.*?)</pre>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE | Pattern.UNIX_LINES);
Matcher m = p.matcher(x.getDocComment());
m.find();
do {
String snippet = null;
try {
snippet = m.group(1);
} catch (IllegalStateException e) {
fail(x + " does not have code snippet");
}
snippet = StringEscapeUtils.unescapeHtml4(snippet);
// it must compile
CtElement el = launcher.getFactory().Code().createCodeSnippetStatement(snippet).compile();
// the snippet contains this element
assertTrue(snippet + " does not contain a " + x.getSimpleName(), el.getElements(new TypeFilter<>(x.getActualClass())).size() > 0);
codeElementsDocumentationPage.append(snippet + "\n");
} while (m.find());
codeElementsDocumentationPage.append("```" + "\n");
}
});
try {
assertEquals("doc outdated, please commit doc/code_elements.md", codeElementsDocumentationPage.toString(), IOUtils.toString(new FileReader("doc/code_elements.md")));
} finally {
write(codeElementsDocumentationPage.toString(), new FileOutputStream("doc/code_elements.md"));
}
}
use of spoon.reflect.declaration.CtElement in project spoon by INRIA.
the class CommentTest method testInLineComment.
@Test
public void testInLineComment() {
Factory f = getSpoonFactory();
CtClass<?> type = (CtClass<?>) f.Type().get(InlineComment.class);
String strType = type.toString();
List<CtComment> comments = type.getElements(new TypeFilter<CtComment>(CtComment.class));
// verify that the number of comment present in the AST is correct
assertEquals(64, comments.size());
// verify that all comments present in the AST is printed
for (CtComment comment : comments) {
if (comment.getCommentType() == CtComment.CommentType.FILE) {
// the header of the file is not printed with the toString
continue;
}
assertNotNull(comment.getParent());
assertTrue(comment.toString() + ":" + comment.getParent() + " is not printed", strType.contains(comment.toString()));
}
assertEquals(3, type.getComments().size());
assertEquals(CtComment.CommentType.FILE, type.getComments().get(0).getCommentType());
assertEquals(createFakeComment(f, "comment class"), type.getComments().get(1));
CtField<?> field = type.getField("field");
assertEquals(3, field.getComments().size());
assertEquals(createFakeComment(f, "Comment Field"), field.getComments().get(0));
assertEquals("// Comment Field" + newLine + "// comment field 2" + newLine + "// comment in field" + newLine + "private int field = 10;", field.toString());
CtAnonymousExecutable ctAnonymousExecutable = type.getAnonymousExecutables().get(0);
assertEquals(1, ctAnonymousExecutable.getComments().size());
assertEquals(createFakeComment(f, "comment static block"), ctAnonymousExecutable.getComments().get(0));
assertEquals(createFakeComment(f, "comment inside static"), ctAnonymousExecutable.getBody().getStatement(0));
assertEquals("// comment static block" + newLine + "static {" + newLine + " // comment inside static" + newLine + "}", ctAnonymousExecutable.toString());
CtConstructor constructor = type.getConstructor();
assertEquals(1, constructor.getComments().size());
assertEquals(createFakeComment(f, "comment constructor"), constructor.getComments().get(0));
// index 0 is the implicit super call
assertEquals(createFakeComment(f, "Comment in constructor"), constructor.getBody().getStatement(1));
assertEquals("// comment constructor" + newLine + "public InlineComment() {" + newLine + " // Comment in constructor" + newLine + "}", constructor.toString());
CtMethod<Object> m = type.getMethod("m");
assertEquals(1, m.getComments().size());
assertEquals(createFakeComment(f, "comment method"), m.getComments().get(0));
assertEquals(createFakeComment(f, "comment empty method block"), m.getBody().getStatement(0));
assertEquals("// comment method" + newLine + "public void m() {" + newLine + " // comment empty method block" + newLine + "}", m.toString());
CtMethod<Object> m1 = type.getMethod("m1");
CtSwitch ctSwitch = m1.getBody().getStatement(0);
assertEquals(createFakeComment(f, "comment switch"), ctSwitch.getComments().get(0));
assertEquals("// comment switch" + newLine + "switch (1) {" + newLine + " // before first case" + newLine + " case 0 :" + newLine + " // comment case 0: empty case" + newLine + " case 1 :" + newLine + " // comment case 1" + newLine + " int i = 0;" + newLine + " default :" + newLine + " // comment default" + newLine + "}", ctSwitch.toString());
CtFor ctFor = m1.getBody().getStatement(1);
assertEquals(createFakeComment(f, "comment for"), ctFor.getComments().get(0));
assertEquals("// comment for" + newLine + "for (int i = 0; i < 10; i++) {" + newLine + " // comment for block" + newLine + "}", ctFor.toString());
CtIf ctIf = m1.getBody().getStatement(2);
assertEquals(createFakeComment(f, "comment if"), ctIf.getComments().get(0));
assertEquals("// comment if" + newLine + "if ((1 % 2) == 0) {" + newLine + " // comment unary operator" + newLine + " (field)++;" + newLine + "}", ctIf.toString());
CtConstructorCall ctConstructorCall = m1.getBody().getStatement(3);
assertEquals(createFakeComment(f, "comment constructor call"), ctConstructorCall.getComments().get(0));
assertEquals("// comment constructor call" + newLine + "new spoon.test.comment.testclasses.InlineComment()", ctConstructorCall.toString());
CtInvocation ctInvocation = m1.getBody().getStatement(4);
assertEquals(createFakeComment(f, "comment invocation"), ctInvocation.getComments().get(0));
assertEquals("// comment invocation" + newLine + "this.m()", ctInvocation.toString());
CtLocalVariable ctLocalVariable = m1.getBody().getStatement(5);
assertEquals(createFakeComment(f, "comment local variable"), ctLocalVariable.getComments().get(0));
assertEquals("// comment local variable" + newLine + "int i = 0", ctLocalVariable.toString());
CtLocalVariable ctLocalVariable2 = m1.getBody().getStatement(6);
assertEquals(createFakeComment(f, "comment multi assignments"), ctLocalVariable2.getComments().get(0));
assertEquals("// comment multi assignments" + newLine + "int j = 2", ctLocalVariable2.toString());
CtDo ctDo = m1.getBody().getStatement(7);
assertEquals(createFakeComment(f, "comment dowhile"), ctDo.getComments().get(0));
assertEquals("// comment dowhile" + newLine + "do {" + newLine + " // comment in do while" + newLine + " i++;" + newLine + " // comment end do while" + newLine + "} while (i < 10 )", ctDo.toString());
CtTry ctTry = m1.getBody().getStatement(8);
assertEquals(createFakeComment(f, "comment try"), ctTry.getComments().get(0));
assertEquals("// comment try" + newLine + "try {" + newLine + " // comment in try" + newLine + " i++;" + newLine + "}// between" + newLine + "// try/catch" + newLine + " catch (java.lang.Exception e) {" + newLine + " // comment in catch" + newLine + "}", ctTry.toString());
CtSynchronized ctSynchronized = m1.getBody().getStatement(9);
assertEquals(createFakeComment(f, "comment synchronized"), ctSynchronized.getComments().get(0));
assertEquals("// comment synchronized" + newLine + "synchronized(this) {" + newLine + " // comment in synchronized" + newLine + "}", ctSynchronized.toString());
CtLocalVariable ctLocalVariable1 = m1.getBody().getStatement(10);
CtConditional ctConditional = (CtConditional) ctLocalVariable1.getDefaultExpression();
assertEquals(createFakeComment(f, "comment after condition CtConditional"), ctConditional.getCondition().getComments().get(0));
assertEquals(createFakeComment(f, "comment before then CtConditional"), ctConditional.getThenExpression().getComments().get(0));
assertEquals(createFakeComment(f, "comment after then CtConditional"), ctConditional.getThenExpression().getComments().get(1));
assertEquals(createFakeComment(f, "comment before else CtConditional"), ctConditional.getElseExpression().getComments().get(0));
assertEquals(createFakeComment(f, "comment after else CtConditional"), ctLocalVariable1.getComments().get(0));
assertEquals("java.lang.Double dou = (i == 1// comment after condition CtConditional" + newLine + ") ? // comment before then CtConditional" + newLine + "null// comment after then CtConditional" + newLine + " : // comment before else CtConditional" + newLine + "new java.lang.Double((j / ((double) (i - 1))))", ctLocalVariable1.toString());
CtNewArray ctNewArray = (CtNewArray) ((CtLocalVariable) m1.getBody().getStatement(11)).getDefaultExpression();
assertEquals(createFakeComment(f, "last comment at the end of array"), ctNewArray.getComments().get(0));
CtElement arrayValue = (CtElement) ctNewArray.getElements().get(0);
assertEquals(createFakeComment(f, "comment before array value"), arrayValue.getComments().get(0));
assertEquals(createFakeComment(f, "comment after array value"), arrayValue.getComments().get(1));
CtLocalVariable ctLocalVariableString = m1.getBody().getStatement(12);
assertEquals(createFakeComment(f, "comment multi line string"), ((CtBinaryOperator) ((CtBinaryOperator) ctLocalVariableString.getDefaultExpression()).getRightHandOperand()).getLeftHandOperand().getComments().get(0));
assertEquals("\"\" + (\"\"// comment multi line string" + newLine + " + \"\")", ctLocalVariableString.getDefaultExpression().toString());
ctLocalVariable1 = m1.getBody().getStatement(13);
ctConditional = (CtConditional) ctLocalVariable1.getDefaultExpression();
assertEquals("boolean c = (i == 1) ? // comment before then boolean CtConditional" + newLine + "i == 1// comment after then boolean CtConditional" + newLine + " : i == 2", ctLocalVariable1.toString());
CtReturn ctReturn = m1.getBody().getStatement(14);
assertEquals(createFakeComment(f, "comment return"), ctReturn.getComments().get(0));
assertEquals("// comment return" + newLine + "return", ctReturn.toString());
CtMethod m2 = type.getMethodsByName("m2").get(0);
assertEquals(6, m2.getComments().size());
CtParameter ctParameter = (CtParameter) m2.getParameters().get(0);
assertEquals(4, ctParameter.getComments().size());
assertEquals("// comment before type" + newLine + "// comment after parameter" + newLine + "// comment before throws" + newLine + "// comment before exception 1" + newLine + "// comment before exception 2" + newLine + "// comment before block" + newLine + "public void m2(// comment before name" + newLine + "// comment before parameters" + newLine + "// comment before type parameter" + newLine + "// comment before name parameter" + newLine + "int i) throws java.lang.Error, java.lang.Exception {" + newLine + "}", m2.toString());
}
use of spoon.reflect.declaration.CtElement in project spoon by INRIA.
the class CtScannerTest method testScan.
@Test
public void testScan() throws Exception {
// contract: all AST nodes are visisted through method "scan"
Launcher launcher;
launcher = new Launcher();
launcher.getEnvironment().setNoClasspath(true);
launcher.addInputResource("src/test/resources/noclasspath/draw2d");
launcher.buildModel();
class Counter {
int nEnter = 0;
int nExit = 0;
int nObject = 0;
int nElement = 0;
Deque<CollectionContext> contexts = new ArrayDeque<>();
}
;
Counter counter = new Counter();
launcher.getModel().getRootPackage().accept(new CtScanner() {
@Override
public void scan(Object o) {
counter.nObject++;
super.scan(o);
}
@Override
public void scan(CtElement o) {
counter.nElement++;
super.scan(o);
}
@Override
public void enter(CtElement o) {
counter.nEnter++;
super.enter(o);
}
@Override
public void exit(CtElement o) {
counter.nExit++;
super.exit(o);
}
});
// interesting, this is never called because of covariance, only CtElement or Collection is called
assertEquals(0, counter.nObject);
// this is a coarse-grain check to see if the scanner changes
// no more exec ref in paramref
assertEquals(3616, counter.nElement);
assertEquals(2396, counter.nEnter);
assertEquals(2396, counter.nExit);
// contract: all AST nodes which are part of Collection or Map are visited first by method "scan(Collection|Map)" and then by method "scan(CtElement)"
Counter counter2 = new Counter();
launcher.getModel().getRootPackage().accept(new CtScanner() {
@Override
public void scan(Object o) {
counter2.nObject++;
super.scan(o);
}
@Override
public void scan(CtRole role, CtElement o) {
if (o == null) {
// there is no collection involved in scanning of this single value NULL attribute
assertNull(counter2.contexts.peek().col);
} else {
RoleHandler rh = RoleHandlerHelper.getRoleHandler(o.getParent().getClass(), role);
if (rh.getContainerKind() == ContainerKind.SINGLE) {
// there is no collection involved in scanning of this single value attribute
assertNull(counter2.contexts.peek().col);
} else {
counter2.contexts.peek().assertRemoveSame(o);
}
}
counter2.nElement++;
super.scan(o);
}
@Override
public void scan(CtRole role, Collection<? extends CtElement> elements) {
// contract: before processed collection is finished before it starts with next collection
counter2.contexts.peek().initCollection(elements);
super.scan(role, elements);
// contract: all elements of collection are processed in previous super.scan call
counter2.contexts.peek().assertCollectionIsEmpty();
}
@Override
public void scan(CtRole role, Map<String, ? extends CtElement> elements) {
// contract: before processed collection is finished before it starts with next collection
counter2.contexts.peek().initCollection(elements.values());
super.scan(role, elements);
// contract: all elements of collection are processed in previous super.scan call
counter2.contexts.peek().assertCollectionIsEmpty();
}
@Override
public void enter(CtElement o) {
counter2.nEnter++;
counter2.contexts.push(new CollectionContext());
}
@Override
public void exit(CtElement o) {
counter2.nExit++;
counter2.contexts.peek().assertCollectionIsEmpty();
counter2.contexts.pop();
}
});
assertEquals(counter.nObject, counter2.nObject);
assertEquals(counter.nElement, counter2.nElement);
assertEquals(counter.nEnter, counter2.nEnter);
assertEquals(counter.nExit, counter2.nExit);
}
Aggregations