use of spoon.reflect.meta.RoleHandler in project spoon by INRIA.
the class ReplaceParametrizedTest method testContract.
@Test
public void testContract() throws Throwable {
List<String> problems = new ArrayList<>();
// contract: all elements are replaceable wherever they are in the model
// this test puts them at all possible locations
CtType<?> toTest = typeToTest.getModelInterface();
CtElement o = factory.Core().create((Class<? extends CtElement>) toTest.getActualClass());
for (MetamodelProperty mmField : typeToTest.getRoleToProperty().values()) {
Class<?> argType = mmField.getItemValueType().getActualClass();
if (!CtElement.class.isAssignableFrom(argType)) {
continue;
}
CtTypeReference<?> itemType = mmField.getItemValueType();
// special cases...
if (itemType.getQualifiedName().equals(CtStatement.class.getName())) {
// the children of CtLoop wraps CtStatement into an implicit CtBlock. So make a block directly to test plain get/set and not wrapping.
itemType = factory.createCtTypeReference(CtBlock.class);
}
if (o.getClass().getSimpleName().equals("CtAnnotationFieldAccessImpl") && mmField.getRole() == CtRole.VARIABLE) {
itemType = factory.createCtTypeReference(CtFieldReference.class);
} else if (CtFieldAccess.class.isAssignableFrom(o.getClass()) && mmField.getRole() == CtRole.VARIABLE) {
itemType = factory.createCtTypeReference(CtFieldReference.class);
}
CtElement argument = (CtElement) createCompatibleObject(itemType);
assertNotNull(argument);
// we create a fresh object
CtElement receiver = ((CtElement) o).clone();
RoleHandler rh = RoleHandlerHelper.getRoleHandler(o.getClass(), mmField.getRole());
if (mmField.isUnsettable()) {
try {
// we invoke the setter
invokeSetter(rh, receiver, argument);
} catch (SpoonException e) {
// ok this unsettable property has no setter at all
return;
}
// this unsettable property has setter, but it should do nothing
CtRole argumentsRoleInParent = argument.getRoleInParent();
if (argumentsRoleInParent == null) {
// OK - unsettable property set no value
continue;
}
if (argumentsRoleInParent == mmField.getRole()) {
problems.add("UnsettableProperty " + mmField + " sets the value");
} else {
if (mmField.isDerived()) {
// it is OK, that setting of value into derived unsettable field influences other field
// Example 1: CtCatchVariable.setType(x) influences result of getMultitype()
// Example 2: CtEnumValue.setAssignment(x) influences result of getDefaultExpression()
} else {
problems.add("UnsettableProperty " + mmField + " sets the value into different role " + argumentsRoleInParent);
}
}
continue;
}
// we invoke the setter
invokeSetter(rh, receiver, argument);
// contract: a property setter sets properties that are visitable by a scanner
CtElement finalArgument = argument;
class Scanner extends CtScanner {
boolean found = false;
@Override
public void scan(CtRole role, CtElement e) {
super.scan(role, e);
if (e == finalArgument) {
if (rh.getRole() == role || rh.getRole().getSuperRole() == role) {
found = true;
return;
}
// if (rh.getRole()==CtRole.TYPE && role==CtRole.MULTI_TYPE) {
// //CtCatchVaraible#type sets CtCatchVaraible#multiType - OK
// found = true;
// return;
// }
problems.add("Argument was set into " + rh.getRole() + " but was found in " + role);
}
}
}
;
Scanner s = new Scanner();
receiver.accept(s);
assertTrue("Settable field " + mmField.toString() + " should set value.\n" + getReport(problems), s.found);
// contract: a property getter on the same role can be used to get the value back
assertSame(argument, invokeGetter(rh, receiver));
final CtElement argument2 = argument.clone();
assertNotSame(argument, argument2);
// we do the replace
argument.replace(argument2);
// the new element is indeed now in this AST
assertTrue(receiver.getClass().getSimpleName() + " failed for " + mmField, receiver.getElements(new Filter<CtElement>() {
@Override
public boolean matches(CtElement element) {
return element == argument2;
}
}).size() == 1);
}
if (problems.size() > 0) {
fail(getReport(problems));
}
}
use of spoon.reflect.meta.RoleHandler in project spoon by INRIA.
the class MetaModelTest method singleValueRoleAddSetRemove.
@Test
public void singleValueRoleAddSetRemove() {
// contract: single value roles supports multivalue interface too
Launcher launcher = new Launcher();
Factory factory = launcher.getFactory();
CtTypeReference<?> typeRef = factory.Type().createReference("some.test.package.TestType");
RoleHandler rh = RoleHandlerHelper.getRoleHandler(typeRef.getClass(), CtRole.PACKAGE_REF);
// contract: single value role provides a List
List<CtPackageReference> packages = rh.asList(typeRef);
assertListContracts(packages, typeRef, 1, "some.test.package");
// contract: adding of existing value fails and changes nothing
try {
packages.add(typeRef.getPackage());
fail();
} catch (Exception e) {
// OK
}
assertListContracts(packages, typeRef, 1, "some.test.package");
// contract: adding of null fails and changes nothing
try {
assertFalse(packages.add(null));
fail();
} catch (Exception e) {
// OK
}
assertListContracts(packages, typeRef, 1, "some.test.package");
// contract: adding of different value fails, and changes nothing
try {
packages.add(factory.Package().createReference("some.test.another_package"));
fail();
} catch (SpoonException e) {
// OK
}
assertListContracts(packages, typeRef, 1, "some.test.package");
// contract remove of different value changes nothing
assertFalse(packages.remove(factory.Package().createReference("some.test.another_package")));
assertListContracts(packages, typeRef, 1, "some.test.package");
// contract remove of null value changes nothing
assertFalse(packages.remove(null));
assertListContracts(packages, typeRef, 1, "some.test.package");
// contract remove of existing value sets value to null and size to 0
assertTrue(packages.remove(factory.Package().createReference("some.test.package")));
assertListContracts(packages, typeRef, 0, null);
// contract add of null into empty collection changes size to 1, but value is still null
assertTrue(packages.add(null));
assertListContracts(packages, typeRef, 1, null);
// contract: adding of new value into collection with single null value fails and changes nothing
try {
packages.add(factory.Package().createReference("some.test.another_package"));
fail();
} catch (SpoonException e) {
// OK
}
assertListContracts(packages, typeRef, 1, null);
// contract: set of new value replaces existing value
assertEquals(null, packages.set(0, factory.Package().createReference("some.test.package")));
assertListContracts(packages, typeRef, 1, "some.test.package");
// contract: set of null value keeps size==1 even if value is replaced by null
assertEquals("some.test.package", packages.set(0, null).getQualifiedName());
assertListContracts(packages, typeRef, 1, null);
// contract: remove of null value by index sets size==0 the value is still null
assertNull(packages.remove(0));
assertListContracts(packages, typeRef, 0, null);
// contract: add of null value sets size==1 the value is still null
assertTrue(packages.add(null));
assertListContracts(packages, typeRef, 1, null);
// contract: remove of null value by value sets size==0 the value is still null
assertTrue(packages.remove(null));
assertListContracts(packages, typeRef, 0, null);
// contract: set of new value on empty collection fails with IndexOutOfBounds and changes nothing
try {
packages.set(0, factory.Package().createReference("some.test.another_package"));
fail();
} catch (IndexOutOfBoundsException e) {
// OK
}
assertListContracts(packages, typeRef, 0, null);
// contract: adding of value into empty collection adds value
assertTrue(packages.add(factory.Package().createReference("some.test.another_package")));
assertListContracts(packages, typeRef, 1, "some.test.another_package");
// contract: remove of value by index from collection removes that value
assertEquals("some.test.another_package", packages.remove(0).getQualifiedName());
assertListContracts(packages, typeRef, 0, null);
}
use of spoon.reflect.meta.RoleHandler in project spoon by INRIA.
the class MetaModelTest method listValueRoleSetOn.
@Test
public void listValueRoleSetOn() {
// contract: multi-value role supports set(int, Object)
Launcher launcher = new Launcher();
Factory factory = launcher.getFactory();
CtClass<?> ctClass = factory.Class().create("some.test.TestClass");
RoleHandler rh = RoleHandlerHelper.getRoleHandler(ctClass.getClass(), CtRole.TYPE_MEMBER);
List<CtTypeMember> typeMembers = rh.asList(ctClass);
assertEquals(0, typeMembers.size());
CtField<?> field1 = createField(factory, "field1");
CtField<?> field2 = createField(factory, "field2");
CtField<?> field3 = createField(factory, "field3");
// check that field was not added in type yet
assertEquals(0, typeMembers.size());
// contract: call of add on RoleHandler collection adds the item into real collection too
typeMembers.add(field1);
assertEquals(1, typeMembers.size());
assertEquals(1, ctClass.getTypeMembers().size());
assertSame(ctClass, field1.getDeclaringType());
assertThat(asList("field1"), is(ctClass.filterChildren(new TypeFilter(CtField.class)).map((CtField e) -> e.getSimpleName()).list()));
// contract: call of add on RoleHandler collection adds the item into real collection too
typeMembers.add(field2);
assertSame(ctClass, field2.getDeclaringType());
assertThat(asList("field1", "field2"), is(ctClass.filterChildren(new TypeFilter(CtField.class)).map((CtField e) -> e.getSimpleName()).list()));
// contract: call of set on RoleHandler collection replaces the item in real collection
typeMembers.set(0, field3);
assertSame(ctClass, field3.getDeclaringType());
assertThat(asList("field3", "field2"), is(ctClass.filterChildren(new TypeFilter(CtField.class)).map((CtField e) -> e.getSimpleName()).list()));
typeMembers.set(1, field1);
assertThat(asList("field3", "field1"), is(ctClass.filterChildren(new TypeFilter(CtField.class)).map((CtField e) -> e.getSimpleName()).list()));
// contract: call of remove(int) on RoleHandler collection removes the item in real collection
assertSame(field3, typeMembers.remove(0));
assertThat(asList("field1"), is(ctClass.filterChildren(new TypeFilter(CtField.class)).map((CtField e) -> e.getSimpleName()).list()));
// contract: call of remove(Object) which does not exist does nothing
assertFalse(typeMembers.remove(field2));
assertThat(asList("field1"), is(ctClass.filterChildren(new TypeFilter(CtField.class)).map((CtField e) -> e.getSimpleName()).list()));
// contract: call of remove(Object) on RoleHandler collection removes the item in real collection
assertTrue(typeMembers.remove(field1));
assertThat(asList(), is(ctClass.filterChildren(new TypeFilter(CtField.class)).map((CtField e) -> e.getSimpleName()).list()));
}
use of spoon.reflect.meta.RoleHandler 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.meta.RoleHandler in project spoon by INRIA.
the class CtElementImpl method setValueByRole.
@Override
public <E extends CtElement, T> E setValueByRole(CtRole role, T value) {
RoleHandler rh = RoleHandlerHelper.getRoleHandler(this.getClass(), role);
rh.setValue(this, value);
return (E) this;
}
Aggregations