use of spoon.reflect.path.CtRole in project spoon by INRIA.
the class CtScannerTest method testScannerCallsAllProperties.
@Test
public void testScannerCallsAllProperties() throws Exception {
// contract: CtScanner must visit all metamodel properties and use correct CtRole!
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/main/java/spoon/reflect/");
launcher.run();
CtTypeReference<?> ctElementRef = launcher.getFactory().createCtTypeReference(CtElement.class);
CtTypeReference<?> ctRefRef = launcher.getFactory().createCtTypeReference(CtReference.class);
CtClass<?> scannerCtClass = (CtClass<?>) launcher.getFactory().Type().get(CtScanner.class);
List<String> problems = new ArrayList<>();
Set<String> ignoredInvocations = new HashSet(Arrays.asList("scan", "enter", "exit"));
SpoonMetaModel metaModel = new SpoonMetaModel(new File("./src/main/java"));
// collect all scanner visit methods, to check if all were checked
Map<String, CtMethod<?>> scannerVisitMethodsByName = new HashMap<>();
scannerCtClass.getAllMethods().forEach(m -> {
if (m.getSimpleName().startsWith("visit")) {
scannerVisitMethodsByName.put(m.getSimpleName(), m);
}
});
class Counter {
int nbChecks = 0;
}
Counter c = new Counter();
for (MetamodelConcept leafConcept : metaModel.getConcepts()) {
// we only consider leaf, actual classes of the metamodel (eg CtInvocation) and not abstract ones (eg CtModifiable)
if (leafConcept.getKind() != MMTypeKind.LEAF) {
continue;
}
CtMethod<?> visitMethod = scannerVisitMethodsByName.remove("visit" + leafConcept.getName());
assertNotNull("CtScanner#" + "visit" + leafConcept.getName() + "(...) not found", visitMethod);
Set<String> calledMethods = new HashSet<>();
Set<String> checkedMethods = new HashSet<>();
// go over the roles and the corresponding fields of this type
leafConcept.getRoleToProperty().forEach((role, mmField) -> {
if (mmField.isDerived()) {
// return of the lambda
return;
}
// ignore fields, which doesn't return CtElement
if (mmField.getItemValueType().isSubtypeOf(ctElementRef) == false) {
// return of the lambda
return;
}
MMMethod getter = mmField.getMethod(MMMethodKind.GET);
checkedMethods.add(getter.getSignature());
// System.out.println("checking "+m.getSignature() +" in "+visitMethod.getSignature());
// now, we collect at least one invocation to this getter in the visit method
CtInvocation invocation = visitMethod.filterChildren(new TypeFilter<CtInvocation>(CtInvocation.class) {
@Override
public boolean matches(CtInvocation element) {
if (ignoredInvocations.contains(element.getExecutable().getSimpleName())) {
return false;
}
calledMethods.add(element.getExecutable().getSignature());
return super.matches(element) && element.getExecutable().getSimpleName().equals(getter.getName());
}
}).first();
if (getter.getName().equals("getComments") && leafConcept.getModelInterface().isSubtypeOf(ctRefRef)) {
// one cannot set comments on references see the @UnsettableProperty of CtReference#setComments
return;
}
// contract: there ia at least one invocation to all non-derived, role-based getters in the visit method of the Scanner
if (invocation == null) {
problems.add("no " + getter.getSignature() + " in " + visitMethod);
} else {
c.nbChecks++;
// System.out.println(invocation.toString());
// contract: the scan method is called with the same role as the one set on field / property
CtRole expectedRole = metaModel.getRoleOfMethod((CtMethod<?>) invocation.getExecutable().getDeclaration());
CtInvocation<?> scanInvocation = invocation.getParent(CtInvocation.class);
String realRoleName = ((CtFieldRead<?>) scanInvocation.getArguments().get(0)).getVariable().getSimpleName();
if (expectedRole.name().equals(realRoleName) == false) {
problems.add("Wrong role " + realRoleName + " used in " + scanInvocation.getPosition());
}
}
});
calledMethods.removeAll(checkedMethods);
// contract: CtScanner only calls methods that have a role and the associated getter
if (calledMethods.size() > 0) {
problems.add("CtScanner " + visitMethod.getPosition() + " calls unexpected methods: " + calledMethods);
}
}
// contract: all visit* methods in CtScanner have been checked
if (scannerVisitMethodsByName.isEmpty() == false) {
problems.add("These CtScanner visit methods were not checked: " + scannerVisitMethodsByName.keySet());
}
if (problems.size() > 0) {
fail(String.join("\n", problems));
}
assertTrue("not enough checks", c.nbChecks >= 200);
}
use of spoon.reflect.path.CtRole 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);
}
use of spoon.reflect.path.CtRole in project spoon by INRIA.
the class CtModuleImpl method addModuleDirectiveAt.
@Override
public <T extends CtModule> T addModuleDirectiveAt(int position, CtModuleDirective moduleDirective) {
if (moduleDirective == null) {
return (T) this;
}
if (this.moduleDirectives == CtElementImpl.<CtModuleDirective>emptyList()) {
this.moduleDirectives = new SortedList<>(new CtLineElementComparator());
}
if (!this.moduleDirectives.contains(moduleDirective)) {
moduleDirective.setParent(this);
CtRole role = this.computeRoleFromModuleDirectory(moduleDirective);
getFactory().getEnvironment().getModelChangeListener().onListAdd(this, role, this.moduleDirectives, position, moduleDirective);
this.moduleDirectives.add(position, moduleDirective);
}
return (T) this;
}
use of spoon.reflect.path.CtRole in project spoon by INRIA.
the class RoleHandlersGenerator method process.
@Override
public void process() {
SpoonMetaModel metaModel = new SpoonMetaModel(getFactory());
// all root super MMFields
List<MetamodelProperty> superFields = new ArrayList<>();
metaModel.getConcepts().forEach(mmConcept -> {
mmConcept.getRoleToProperty().forEach((role, rim) -> {
addUniqueObject(superFields, rim.getRootSuperField());
});
});
superFields.sort((a, b) -> {
int d = a.getRole().name().compareTo(b.getRole().name());
if (d != 0) {
return d;
}
return a.getOwnerConcept().getName().compareTo(b.getOwnerConcept().getName());
});
PrinterHelper concept = new PrinterHelper(getFactory().getEnvironment());
superFields.forEach(mmField -> {
concept.write(mmField.getOwnerConcept().getName() + " CtRole." + mmField.getRole().name()).writeln().incTab().write("ItemType: ").write(mmField.getValueType().toString()).writeln();
for (MMMethodKind mk : MMMethodKind.values()) {
MMMethod mmMethod = mmField.getMethod(mk);
if (mmMethod != null) {
concept.write(mk.name()).write(": ").write(mmMethod.getSignature()).write(" : ").write(mmMethod.getReturnType().toString()).writeln();
}
}
concept.decTab();
concept.write("----------------------------------------------------------").writeln();
});
try (Writer w = new OutputStreamWriter(new FileOutputStream(file("target/report/concept.txt")))) {
w.write(concept.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
CtType<?> template = getTemplate("spoon.generating.meta.ModelRoleHandlerTemplate");
CtClass<?> modelRoleHandlersClass = Substitution.createTypeFromTemplate(TARGET_PACKAGE + ".ModelRoleHandlers", template, new HashMap<>());
CtNewArray<?> roleHandlersFieldExpr = (CtNewArray<?>) modelRoleHandlersClass.getField("roleHandlers").getDefaultExpression();
superFields.forEach(rim -> {
Map<String, Object> params = new HashMap<>();
params.put("$getterName$", rim.getMethod(MMMethodKind.GET).getName());
if (rim.getMethod(MMMethodKind.SET) != null) {
params.put("$setterName$", rim.getMethod(MMMethodKind.SET).getName());
}
params.put("$Role$", getFactory().Type().createReference(CtRole.class));
params.put("ROLE", rim.getRole().name());
params.put("$TargetType$", rim.getOwnerConcept().getModelInterface().getReference());
// params.put("AbstractHandler", getFactory().Type().createReference("spoon.reflect.meta.impl.AbstractRoleHandler"));
params.put("AbstractHandler", getRoleHandlerSuperTypeQName(rim));
params.put("Node", rim.getOwnerConcept().getModelInterface().getReference());
params.put("ValueType", fixMainValueType(getRoleHandlerSuperTypeQName(rim).endsWith("SingleHandler") ? rim.getValueType() : rim.getItemValueType()));
CtClass<?> modelRoleHandlerClass = Substitution.createTypeFromTemplate(getHandlerName(rim), getTemplate("spoon.generating.meta.RoleHandlerTemplate"), params);
if (rim.getMethod(MMMethodKind.SET) == null) {
modelRoleHandlerClass.getMethodsByName("setValue").forEach(m -> m.delete());
}
modelRoleHandlerClass.addModifier(ModifierKind.STATIC);
modelRoleHandlersClass.addNestedType(modelRoleHandlerClass);
roleHandlersFieldExpr.addElement(getFactory().createCodeSnippetExpression("new " + modelRoleHandlerClass.getSimpleName() + "()"));
});
}
use of spoon.reflect.path.CtRole in project spoon by INRIA.
the class CtTypeImpl method removeTypeMember.
@Override
public boolean removeTypeMember(CtTypeMember member) {
CtRole role;
if (member instanceof CtMethod) {
role = METHOD;
} else if (member instanceof CtConstructor) {
role = CONSTRUCTOR;
} else if (member instanceof CtField) {
role = FIELD;
} else if (member instanceof CtAnonymousExecutable) {
role = ANNONYMOUS_EXECUTABLE;
} else {
role = NESTED_TYPE;
}
if (typeMembers.size() == 1) {
if (typeMembers.contains(member)) {
getFactory().getEnvironment().getModelChangeListener().onListDelete(this, role, this.typeMembers, this.typeMembers.indexOf(member), member);
typeMembers = emptyList();
return true;
} else {
return false;
}
}
if (typeMembers.contains(member)) {
getFactory().getEnvironment().getModelChangeListener().onListDelete(this, role, this.typeMembers, this.typeMembers.indexOf(member), member);
return typeMembers.remove(member);
}
return false;
}
Aggregations