use of spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction in project spoon by INRIA.
the class CtCatchVariableImpl method getType.
@SuppressWarnings("unchecked")
@Override
@DerivedProperty
public CtTypeReference<T> getType() {
if (types.isEmpty()) {
return null;
} else if (types.size() == 1) {
return (CtTypeReference<T>) types.get(0);
}
// compute common super type of exceptions
List<CtTypeReference<?>> superTypesOfFirst = types.get(0).map(new SuperInheritanceHierarchyFunction().includingInterfaces(false).includingSelf(true).returnTypeReferences(true)).list();
if (superTypesOfFirst.isEmpty()) {
return null;
}
int commonSuperTypeIdx = 0;
// index of Throwable. Last is Object
int throwableIdx = superTypesOfFirst.size() - 2;
for (int i = 1; i < types.size() && commonSuperTypeIdx != throwableIdx; i++) {
CtTypeReference<?> nextException = types.get(i);
while (commonSuperTypeIdx < throwableIdx) {
if (nextException.isSubtypeOf(superTypesOfFirst.get(commonSuperTypeIdx))) {
// nextException is sub type of actually selected commonSuperType
break;
}
// try next super type
commonSuperTypeIdx++;
}
}
return (CtTypeReference<T>) superTypesOfFirst.get(commonSuperTypeIdx);
}
use of spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction in project spoon by INRIA.
the class SubInheritanceHierarchyResolver method forEachSubTypeInPackage.
/**
* Calls `outputConsumer.apply(subType)` for each sub type of the targetSuperTypes that are found in `inputPackage`.
* Each sub type is returned only once.
* It makes sense to call this method again for example after new super types are added
* by {@link #addSuperType(CtTypeInformation)}.
*
* If this method is called again with same input and configuration, nothing in sent to outputConsumer
* @param outputConsumer the consumer for found sub types
*/
public <T extends CtType<?>> void forEachSubTypeInPackage(final CtConsumer<T> outputConsumer) {
/*
* Set of qualified names of all visited types, independent on whether they are sub types or not.
*/
final Set<String> allVisitedTypeNames = new HashSet<>();
/*
* the queue of types whose super inheritance hierarchy we are just visiting.
* They are potential sub types of an `targetSuperTypes`
*/
final Deque<CtTypeReference<?>> currentSubTypes = new ArrayDeque<>();
// algorithm
// 1) query step: scan input package for sub classes and sub interfaces
final CtQuery q = inputPackage.map(new CtScannerFunction());
// 2) query step: visit only required CtTypes
if (includingInterfaces) {
// the client is interested in sub inheritance hierarchy of interfaces too. Check interfaces, classes, enums, Annotations, but not CtTypeParameters.
q.select(typeFilter);
} else {
// the client is not interested in sub inheritance hierarchy of interfaces. Check only classes and enums.
q.select(classFilter);
}
/*
* 3) query step: for each found CtType, visit it's super inheritance hierarchy and search there for a type which is equal to one of targetSuperTypes.
* If found then all sub types in hierarchy (variable `currentSubTypes`) are sub types of targetSuperTypes. So return them
*/
q.map(new SuperInheritanceHierarchyFunction().includingInterfaces(hasSuperInterface).failOnClassNotFound(failOnClassNotFound).setListener(new CtScannerListener() {
@Override
public ScanningMode enter(CtElement element) {
final CtTypeReference<?> typeRef = (CtTypeReference<?>) element;
String qName = typeRef.getQualifiedName();
if (targetSuperTypes.contains(qName)) {
/*
* FOUND! we are in super inheritance hierarchy, which extends from an searched super type(s).
* All `currentSubTypes` are sub types of searched super type
*/
while (currentSubTypes.size() > 0) {
final CtTypeReference<?> currentTypeRef = currentSubTypes.pop();
String currentQName = currentTypeRef.getQualifiedName();
/*
* Send them to outputConsumer and add then as targetSuperTypes too, to perform faster with detection of next sub types.
*/
if (!targetSuperTypes.contains(currentQName)) {
targetSuperTypes.add(currentQName);
outputConsumer.accept((T) currentTypeRef.getTypeDeclaration());
}
}
// but continue visiting of siblings (do not terminate query)
return SKIP_ALL;
}
if (allVisitedTypeNames.add(qName) == false) {
/*
* this type was already visited, by another way. So it is not sub type of `targetSuperTypes`.
* Stop visiting it's inheritance hierarchy.
*/
return SKIP_ALL;
}
/*
* This type was not visited yet.
* We still do not know whether this type is a sub type of any target super type(s)
* continue searching in super inheritance hierarchy
*/
currentSubTypes.push(typeRef);
return NORMAL;
}
@Override
public void exit(CtElement element) {
CtTypeInformation type = (CtTypeInformation) element;
if (currentSubTypes.isEmpty() == false) {
// remove current type, which is not a sub type of targetSuperTypes from the currentSubTypes
CtTypeInformation stackType = currentSubTypes.pop();
if (stackType != type) {
// the enter/exit was not called consistently. There is a bug in SuperInheritanceHierarchyFunction
throw new SpoonException("CtScannerListener#exit was not called after enter.");
}
}
}
})).forEach(new CtConsumer<CtType<?>>() {
@Override
public void accept(CtType<?> type) {
// we do not care about types visited by query `q`.
// the result of whole mapping function was already produced by `sendResult` call
// but we have to consume all these results to let query running
}
});
}
use of spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction in project spoon by INRIA.
the class MetamodelTest method testRoleOnField.
@Test
public void testRoleOnField() {
// contract: all non-final fields must be annotated with {@link spoon.reflect.annotations.MetamodelPropertyField}
SpoonAPI implementations = new Launcher();
implementations.addInputResource("src/main/java/spoon/support/reflect");
implementations.buildModel();
Factory factory = implementations.getFactory();
CtTypeReference metamodelPropertyField = factory.Type().get(MetamodelPropertyField.class).getReference();
final List<String> result = new ArrayList();
List<CtField> fieldWithoutAnnotation = (List<CtField>) implementations.getModel().getElements(new TypeFilter<CtField>(CtField.class) {
@Override
public boolean matches(CtField candidate) {
if (candidate.hasModifier(ModifierKind.FINAL) || candidate.hasModifier(ModifierKind.STATIC) || candidate.hasModifier(ModifierKind.TRANSIENT)) {
return false;
}
if (// not a role
"parent".equals(candidate.getSimpleName()) || "metadata".equals(candidate.getSimpleName()) || // cache field
"valueOfMethod".equals(candidate.getSimpleName())) {
return false;
}
CtClass parent = candidate.getParent(CtClass.class);
return parent != null && (parent.isSubtypeOf(candidate.getFactory().createCtTypeReference(CtReference.class)) || parent.isSubtypeOf(candidate.getFactory().createCtTypeReference(CtElement.class)));
}
}).stream().map(x -> {
result.add(x.toString());
return x;
}).filter(f -> f.getAnnotation(metamodelPropertyField) == null).collect(Collectors.toList());
assertTrue(result.contains("@spoon.reflect.annotations.MetamodelPropertyField(role = spoon.reflect.path.CtRole.IS_SHADOW)\nboolean isShadow;"));
assertTrue(result.contains("@spoon.reflect.annotations.MetamodelPropertyField(role = spoon.reflect.path.CtRole.TYPE)\nspoon.reflect.reference.CtTypeReference<T> type;"));
assertTrue(result.size() > 100);
Assert.assertEquals(Collections.emptyList(), fieldWithoutAnnotation);
final CtTypeReference propertySetter = factory.Type().get(PropertySetter.class).getReference();
final CtTypeReference propertyGetter = factory.Type().get(PropertyGetter.class).getReference();
List<CtField> fields = factory.getModel().getElements(new AnnotationFilter<CtField>(MetamodelPropertyField.class));
for (CtField field : fields) {
CtClass parent = field.getParent(CtClass.class);
CtExpression roleExpression = field.getAnnotation(metamodelPropertyField).getValue("role");
List<String> roles = new ArrayList<>();
if (roleExpression instanceof CtFieldRead) {
roles.add(((CtFieldRead) roleExpression).getVariable().getSimpleName());
} else if (roleExpression instanceof CtNewArray) {
List<CtFieldRead> elements = ((CtNewArray) roleExpression).getElements();
for (int i = 0; i < elements.size(); i++) {
CtFieldRead ctFieldRead = elements.get(i);
roles.add(ctFieldRead.getVariable().getSimpleName());
}
}
CtQuery superQuery = parent.map(new SuperInheritanceHierarchyFunction());
List<CtMethod> methods = superQuery.map((CtType type) -> type.getMethodsAnnotatedWith(propertyGetter, propertySetter)).list();
boolean setterFound = false;
boolean getterFound = false;
for (CtMethod method : methods) {
CtAnnotation getterAnnotation = method.getAnnotation(propertyGetter);
CtAnnotation setterAnnotation = method.getAnnotation(propertySetter);
if (getterAnnotation != null) {
getterFound |= roles.contains(((CtFieldRead) getterAnnotation.getValue("role")).getVariable().getSimpleName());
}
if (setterAnnotation != null) {
setterFound |= roles.contains(((CtFieldRead) setterAnnotation.getValue("role")).getVariable().getSimpleName());
}
}
assertTrue(roles + " must have a getter in " + parent.getQualifiedName(), getterFound);
assertTrue(roles + " must have a setter in " + parent.getQualifiedName(), setterFound);
}
}
use of spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction in project spoon by INRIA.
the class ClassTypingContext method resolveActualTypeArgumentsOf.
/**
* resolve actual type argument values of the provided type reference
* @param typeRef the reference to the type
* whose actual type argument values has to be resolved in scope of `scope` type
* @return actual type arguments of `typeRef` in scope of `scope` element or null if typeRef is not a super type of `scope`
*/
public List<CtTypeReference<?>> resolveActualTypeArgumentsOf(CtTypeReference<?> typeRef) {
final String typeQualifiedName = typeRef.getQualifiedName();
List<CtTypeReference<?>> args = typeToArguments.get(typeQualifiedName);
if (args != null) {
// the actual type arguments of `type` are already resolved
return args;
}
// resolve hierarchy of enclosing class first.
CtTypeReference<?> enclosingTypeRef = getEnclosingType(typeRef);
if (enclosingTypeRef != null) {
if (enclosingClassTypingContext == null) {
return null;
}
// `type` is inner class. Resolve it's enclosing class arguments first
if (enclosingClassTypingContext.resolveActualTypeArgumentsOf(enclosingTypeRef) == null) {
return null;
}
}
/*
* detect where to start/continue with resolving of super classes and super interfaces
* to found actual type arguments of input `type`
*/
if (lastResolvedSuperclass == null) {
/*
* whole super inheritance hierarchy was already resolved for this level.
* It means that `type` is not a super type of `scope` on the level `level`
*/
return null;
}
final HierarchyListener listener = new HierarchyListener(getVisitedSet());
/*
* remove last resolved class from the list of visited,
* because it would avoid visiting it's super hierarchy
*/
getVisitedSet().remove(lastResolvedSuperclass.getQualifiedName());
/*
* visit super inheritance class hierarchy of lastResolve type of level of `type` to found it's actual type arguments.
*/
((CtElement) lastResolvedSuperclass).map(new SuperInheritanceHierarchyFunction().includingSelf(false).returnTypeReferences(true).setListener(listener)).forEach(new CtConsumer<CtTypeReference<?>>() {
@Override
public void accept(CtTypeReference<?> typeRef) {
/*
* typeRef is a reference from sub type to super type.
* It contains actual type arguments in scope of sub type,
* which are going to be substituted as arguments to formal type parameters of super type
*/
String superTypeQualifiedName = typeRef.getQualifiedName();
List<CtTypeReference<?>> actualTypeArguments = typeRef.getActualTypeArguments();
if (actualTypeArguments.isEmpty()) {
// may be they are not set - check whether type declares some generic parameters
List<CtTypeParameter> typeParams;
try {
CtType<?> type = typeRef.getTypeDeclaration();
typeParams = type.getFormalCtTypeParameters();
} catch (final SpoonClassNotFoundException e) {
if (typeRef.getFactory().getEnvironment().getNoClasspath()) {
typeParams = Collections.emptyList();
} else {
throw e;
}
}
if (typeParams.size() > 0) {
// yes, there are generic type parameters. Reference should use actualTypeArguments computed from their bounds
actualTypeArguments = new ArrayList<>(typeParams.size());
for (CtTypeParameter typeParam : typeParams) {
actualTypeArguments.add(typeParam.getTypeErasure());
}
}
}
List<CtTypeReference<?>> superTypeActualTypeArgumentsResolvedFromSubType = resolveTypeParameters(actualTypeArguments);
// Remember actual type arguments of `type`
typeToArguments.put(superTypeQualifiedName, superTypeActualTypeArgumentsResolvedFromSubType);
if (typeQualifiedName.equals(superTypeQualifiedName)) {
/*
* we have found actual type arguments of input `type`
* We can finish. But only after all interfaces of last visited class are processed too
*/
listener.foundArguments = superTypeActualTypeArgumentsResolvedFromSubType;
}
}
});
if (listener.foundArguments == null) {
/*
* superclass was not found. We have scanned whole hierarchy
*/
lastResolvedSuperclass = null;
}
return listener.foundArguments;
}
use of spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction in project spoon by INRIA.
the class ImportTest method testSuperInheritanceHierarchyFunctionListener.
@Test
public void testSuperInheritanceHierarchyFunctionListener() throws Exception {
CtType<?> clientClass = (CtClass<?>) ModelUtils.buildClass(ClientClass.class);
CtTypeReference<?> childClass = clientClass.getSuperclass();
CtTypeReference<?> superClass = childClass.getSuperclass();
// contract: the enter and exit are always called with CtTypeReference instance
List<String> result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true).setListener(new CtScannerListener() {
@Override
public ScanningMode enter(CtElement element) {
assertTrue(element instanceof CtTypeReference);
return ScanningMode.NORMAL;
}
@Override
public void exit(CtElement element) {
assertTrue(element instanceof CtTypeReference);
}
})).map(e -> {
assertTrue(e instanceof CtType);
return ((CtType) e).getQualifiedName();
}).list();
assertTrue(result.contains(clientClass.getQualifiedName()));
assertTrue(result.contains(childClass.getQualifiedName()));
assertTrue(result.contains(superClass.getQualifiedName()));
assertTrue(result.contains(Object.class.getName()));
// contract: if listener skips ALL, then skipped element and all super classes are not returned
result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true).setListener(new CtScannerListener() {
@Override
public ScanningMode enter(CtElement element) {
assertTrue(element instanceof CtTypeReference);
if (superClass.getQualifiedName().equals(((CtTypeReference<?>) element).getQualifiedName())) {
return ScanningMode.SKIP_ALL;
}
return ScanningMode.NORMAL;
}
@Override
public void exit(CtElement element) {
assertTrue(element instanceof CtTypeReference);
}
})).map(e -> {
assertTrue(e instanceof CtType);
return ((CtType) e).getQualifiedName();
}).list();
assertTrue(result.contains(clientClass.getQualifiedName()));
assertTrue(result.contains(childClass.getQualifiedName()));
assertFalse(result.contains(superClass.getQualifiedName()));
assertFalse(result.contains(Object.class.getName()));
// contract: if listener skips CHIDLREN, then skipped element is returned but all super classes are not returned
result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true).setListener(new CtScannerListener() {
@Override
public ScanningMode enter(CtElement element) {
assertTrue(element instanceof CtTypeReference);
if (superClass.getQualifiedName().equals(((CtTypeReference<?>) element).getQualifiedName())) {
return ScanningMode.SKIP_CHILDREN;
}
return ScanningMode.NORMAL;
}
@Override
public void exit(CtElement element) {
assertTrue(element instanceof CtTypeReference);
}
})).map(e -> {
assertTrue(e instanceof CtType);
return ((CtType) e).getQualifiedName();
}).list();
assertTrue(result.contains(clientClass.getQualifiedName()));
assertTrue(result.contains(childClass.getQualifiedName()));
assertTrue(result.contains(superClass.getQualifiedName()));
assertFalse(result.contains(Object.class.getName()));
}
Aggregations