[jvm-lang] add ability to traverse super classes

This commit is contained in:
Daniil Ovchinnikov
2018-03-20 17:52:10 +03:00
parent 37e0ce4ca6
commit e5c3819e76
2 changed files with 96 additions and 0 deletions
@@ -0,0 +1,67 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.jvm.util;
import com.intellij.lang.jvm.JvmClass;
import com.intellij.openapi.progress.ProgressManager;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Set;
import java.util.function.Function;
import static com.intellij.lang.jvm.util.JvmUtil.resolveClass;
import static com.intellij.lang.jvm.util.JvmUtil.resolveClasses;
public class JvmHierarchyUtil {
private JvmHierarchyUtil() {}
/**
* Traverses class tree in BFS order applying the function to each superclass.
* <p/>
* Notes:
* <ul>
* <li><i>supers</i> term includes both classes and interfaces</li>
* <li>the function will also be applied to the start class</li>
* <li>if the function returns non-null result for any class then traversal is stopped and result is returned</li>
* <li>the function is applied to each class at most once</li>
* <li>unresolvable supertypes are skipped</li>
* </ul>
*
* @param start class to start traversal from
* @param <R> type of the result
* @return first non-null result or null
*/
public static <R> R traverseSupers(@NotNull JvmClass start, @NotNull Function<? super JvmClass, R> f) {
// TODO implement method returning Stream<JvmClass>
final Queue<JvmClass> queue = new ArrayDeque<>();
queue.offer(start);
final Set<JvmClass> visited = new THashSet<>();
while (!queue.isEmpty()) {
ProgressManager.checkCanceled();
JvmClass current = queue.remove();
if (!visited.add(current)) continue;
R result = f.apply(current);
if (result != null) return result;
queueSupers(queue, current);
}
return null;
}
private static void queueSupers(@NotNull Queue<JvmClass> queue, @NotNull JvmClass current) {
JvmClass superClass = resolveClass(current.getSuperClassType());
if (superClass != null) {
queue.offer(superClass);
}
for (JvmClass anInterface : resolveClasses(current.getInterfaceTypes())) {
queue.offer(anInterface);
}
}
}
@@ -0,0 +1,29 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.jvm.util;
import com.intellij.lang.jvm.JvmClass;
import com.intellij.lang.jvm.JvmTypeDeclaration;
import com.intellij.lang.jvm.types.JvmReferenceType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.util.containers.ContainerUtil.mapNotNull;
class JvmUtil {
private JvmUtil() {}
@NotNull
static Iterable<JvmClass> resolveClasses(@NotNull JvmReferenceType[] types) {
return mapNotNull(types, JvmUtil::resolveClass);
}
@Contract("null -> null")
@Nullable
static JvmClass resolveClass(@Nullable JvmReferenceType type) {
if (type == null) return null;
JvmTypeDeclaration resolved = type.resolve();
return resolved instanceof JvmClass ? (JvmClass)resolved : null;
}
}