diff --git a/java/java-psi-api/src/com/intellij/lang/jvm/util/JvmHierarchyUtil.java b/java/java-psi-api/src/com/intellij/lang/jvm/util/JvmHierarchyUtil.java new file mode 100644 index 000000000000..709b12ca6614 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/lang/jvm/util/JvmHierarchyUtil.java @@ -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. + *

+ * Notes: + *

+ * + * @param start class to start traversal from + * @param type of the result + * @return first non-null result or null + */ + public static R traverseSupers(@NotNull JvmClass start, @NotNull Function f) { + // TODO implement method returning Stream + final Queue queue = new ArrayDeque<>(); + queue.offer(start); + + final Set 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 queue, @NotNull JvmClass current) { + JvmClass superClass = resolveClass(current.getSuperClassType()); + if (superClass != null) { + queue.offer(superClass); + } + for (JvmClass anInterface : resolveClasses(current.getInterfaceTypes())) { + queue.offer(anInterface); + } + } +} diff --git a/java/java-psi-api/src/com/intellij/lang/jvm/util/JvmUtil.java b/java/java-psi-api/src/com/intellij/lang/jvm/util/JvmUtil.java new file mode 100644 index 000000000000..8438735d535c --- /dev/null +++ b/java/java-psi-api/src/com/intellij/lang/jvm/util/JvmUtil.java @@ -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 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; + } +}