diff --git a/org.eclipse.jdt.ls.core/plugin.xml b/org.eclipse.jdt.ls.core/plugin.xml
index aa62acfc5f..ec551066dc 100644
--- a/org.eclipse.jdt.ls.core/plugin.xml
+++ b/org.eclipse.jdt.ls.core/plugin.xml
@@ -97,6 +97,12 @@
+
+
+
+
arguments, IProgress
projectNames = (ArrayList) arguments.get(1);
}
return ResolveSourceMappingHandler.resolveStackTraceLocation((String) arguments.get(0), projectNames);
+ case "java.navigate.resolveTypeHierarchy":
+ TypeHierarchyCommand resolveTypeHierarchyCommand = new TypeHierarchyCommand();
+ TypeHierarchyItem toResolve = JSONUtility.toModel(arguments.get(0), TypeHierarchyItem.class);
+ TypeHierarchyDirection resolveDirection = TypeHierarchyDirection.forValue(JSONUtility.toModel(arguments.get(1), Integer.class));
+ int resolveDepth = JSONUtility.toModel(arguments.get(2), Integer.class);
+ ResolveTypeHierarchyItemParams resolveParams = new ResolveTypeHierarchyItemParams();
+ resolveParams.setItem(toResolve);
+ resolveParams.setDirection(resolveDirection);
+ resolveParams.setResolve(resolveDepth);
+ TypeHierarchyItem resolvedItem = resolveTypeHierarchyCommand.resolveTypeHierarchy(resolveParams, monitor);
+ return resolvedItem;
+ case "java.navigate.openTypeHierarchy":
+ TypeHierarchyCommand typeHierarchyCommand = new TypeHierarchyCommand();
+ TypeHierarchyParams params = new TypeHierarchyParams();
+ TextDocumentPositionParams textParams = JSONUtility.toModel(arguments.get(0), TextDocumentPositionParams.class);
+ TypeHierarchyDirection direction = TypeHierarchyDirection.forValue(JSONUtility.toModel(arguments.get(1), Integer.class));
+ int resolve = JSONUtility.toModel(arguments.get(2), Integer.class);
+ params.setResolve(resolve);
+ params.setDirection(direction);
+ params.setTextDocument(textParams.getTextDocument());
+ params.setPosition(textParams.getPosition());
+ TypeHierarchyItem typeHierarchyItem = typeHierarchyCommand.typeHierarchy(params, monitor);
+ return typeHierarchyItem;
default:
break;
}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommand.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommand.java
new file mode 100644
index 0000000000..16434ba867
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommand.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Microsoft Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+package org.eclipse.jdt.ls.core.internal.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IOrdinaryClassFile;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
+import org.eclipse.jdt.ls.core.internal.JDTUtils;
+import org.eclipse.jdt.ls.core.internal.JSONUtility;
+import org.eclipse.jdt.ls.core.internal.JDTUtils.LocationType;
+import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
+import org.eclipse.jdt.ls.core.internal.handlers.DocumentSymbolHandler;
+import org.eclipse.lsp4j.Location;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.ResolveTypeHierarchyItemParams;
+import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.lsp4j.TypeHierarchyDirection;
+import org.eclipse.lsp4j.TypeHierarchyItem;
+import org.eclipse.lsp4j.TypeHierarchyParams;
+
+public class TypeHierarchyCommand {
+
+ public TypeHierarchyItem typeHierarchy(TypeHierarchyParams params, IProgressMonitor monitor) {
+ if (params == null) {
+ return null;
+ }
+ TextDocumentIdentifier textDocument = params.getTextDocument();
+ if (textDocument == null) {
+ return null;
+ }
+ Position position = params.getPosition();
+ String uri = textDocument.getUri();
+ TypeHierarchyDirection direction = params.getDirection();
+ int resolve = params.getResolve();
+ return getTypeHierarchy(uri, position, direction, resolve, null, monitor);
+ }
+
+ public TypeHierarchyItem resolveTypeHierarchy(ResolveTypeHierarchyItemParams params, IProgressMonitor monitor) {
+ if (params == null) {
+ return null;
+ }
+ TypeHierarchyItem item = params.getItem();
+ if (item == null) {
+ return null;
+ }
+ Range range = item.getRange();
+ if (range == null) {
+ return null;
+ }
+ Position position = range.getStart();
+ String uri = item.getUri();
+ TypeHierarchyDirection direction = params.getDirection();
+ int resolve = params.getResolve();
+ return getTypeHierarchy(uri, position, direction, resolve, item, monitor);
+ }
+
+ private TypeHierarchyItem getTypeHierarchy(String uri, Position position, TypeHierarchyDirection direction, int resolve, TypeHierarchyItem itemInput, IProgressMonitor monitor) {
+ if (uri == null || position == null || direction == null) {
+ return null;
+ }
+ try {
+ IType type = null;
+ if (itemInput == null) {
+ type = getType(uri, position, monitor);
+ } else {
+ String handleIdentifier = JSONUtility.toModel(itemInput.getData(), String.class);
+ IJavaElement element = JavaCore.create(handleIdentifier);
+ if (element instanceof IType) {
+ type = ((IType)element);
+ } else if (element instanceof IOrdinaryClassFile) {
+ type = ((IOrdinaryClassFile)element).getType();
+ } else {
+ return null;
+ }
+ }
+ TypeHierarchyItem item = TypeHierarchyCommand.toTypeHierarchyItem(type);
+ if (item == null) {
+ return null;
+ }
+ resolve(item, type, direction, resolve, monitor);
+ return item;
+ } catch (JavaModelException e) {
+ return null;
+ }
+ }
+
+ private IType getType(String uri, Position position, IProgressMonitor monitor) throws JavaModelException {
+ IJavaElement typeElement = findTypeElement(JDTUtils.resolveTypeRoot(uri), position, monitor);
+ if (typeElement instanceof IType) {
+ return (IType)typeElement;
+ } else if (typeElement instanceof IMethod) {
+ return ((IMethod)typeElement).getDeclaringType();
+ } else {
+ return null;
+ }
+ }
+
+ private static IJavaElement findTypeElement(ITypeRoot unit, Position position, IProgressMonitor monitor) throws JavaModelException {
+ if (unit == null) {
+ return null;
+ }
+ IJavaElement element = JDTUtils.findElementAtSelection(unit, position.getLine(), position.getCharacter(), JavaLanguageServerPlugin.getPreferencesManager(), monitor);
+ if (element == null) {
+ if (unit instanceof IOrdinaryClassFile) {
+ element = ((IOrdinaryClassFile) unit).getType();
+ } else if (unit instanceof ICompilationUnit) {
+ element = unit.findPrimaryType();
+ }
+ }
+ return element;
+ }
+
+ private static TypeHierarchyItem toTypeHierarchyItem(IType type) throws JavaModelException {
+ if (type == null) {
+ return null;
+ }
+ Location location = getLocation(type, LocationType.FULL_RANGE);
+ Location selectLocation = getLocation(type, LocationType.NAME_RANGE);
+ if (location == null || selectLocation == null) {
+ return null;
+ }
+ TypeHierarchyItem item = new TypeHierarchyItem();
+ item.setRange(location.getRange());
+ item.setUri(location.getUri());
+ item.setSelectionRange(selectLocation.getRange());
+ String fullyQualifiedName = type.getFullyQualifiedName();
+ int index = fullyQualifiedName.lastIndexOf('.');
+ if (index >= 1 && index < fullyQualifiedName.length() - 1 && !type.isAnonymous()) {
+ item.setName(fullyQualifiedName.substring(index + 1));
+ item.setDetail(fullyQualifiedName.substring(0, index));
+ } else {
+ item.setName(JDTUtils.getName(type));
+ IPackageFragment packageFragment = type.getPackageFragment();
+ if (packageFragment != null) {
+ item.setDetail(packageFragment.getElementName());
+ }
+ }
+ item.setKind(DocumentSymbolHandler.mapKind(type));
+ item.setDeprecated(JDTUtils.isDeprecated(type));
+ item.setData(type.getHandleIdentifier());
+ return item;
+ }
+
+ private static Location getLocation(IType type, LocationType locationType) throws JavaModelException {
+ Location location = locationType.toLocation(type);
+ if (location == null && type.getClassFile() != null) {
+ location = JDTUtils.toLocation(type.getClassFile());
+ }
+ return location;
+ }
+
+ private void resolve(TypeHierarchyItem item, IType type, TypeHierarchyDirection direction, int resolve, IProgressMonitor monitor) throws JavaModelException {
+ if (monitor.isCanceled() || resolve <= 0) {
+ return;
+ }
+ ITypeHierarchy typeHierarchy = (direction == TypeHierarchyDirection.Parents) ? type.newSupertypeHierarchy(DefaultWorkingCopyOwner.PRIMARY, monitor) : type.newTypeHierarchy(type.getJavaProject(), DefaultWorkingCopyOwner.PRIMARY, monitor);
+ if (direction == TypeHierarchyDirection.Children || direction == TypeHierarchyDirection.Both) {
+ List childrenItems = new ArrayList();
+ IType[] children = typeHierarchy.getSubtypes(type);
+ for (IType childType : children) {
+ TypeHierarchyItem childItem = TypeHierarchyCommand.toTypeHierarchyItem(childType);
+ if (childItem == null) {
+ continue;
+ }
+ resolve(childItem, childType, direction, resolve - 1, monitor);
+ childrenItems.add(childItem);
+ }
+ item.setChildren(childrenItems);
+ }
+ if (direction == TypeHierarchyDirection.Parents || direction == TypeHierarchyDirection.Both) {
+ List parentsItems = new ArrayList();
+ IType[] parents = typeHierarchy.getSupertypes(type);
+ for (IType parentType : parents) {
+ TypeHierarchyItem parentItem = TypeHierarchyCommand.toTypeHierarchyItem(parentType);
+ if (parentItem == null) {
+ continue;
+ }
+ resolve(parentItem, parentType, direction, resolve - 1, monitor);
+ parentsItems.add(parentItem);
+ }
+ item.setParents(parentsItems);
+ }
+ }
+}
diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommandTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommandTest.java
new file mode 100644
index 0000000000..6b3345e9fb
--- /dev/null
+++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommandTest.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Microsoft Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+package org.eclipse.jdt.ls.core.internal.commands;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.ls.core.internal.WorkspaceHelper;
+import org.eclipse.jdt.ls.core.internal.managers.AbstractInvisibleProjectBasedTest;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.lsp4j.TypeHierarchyDirection;
+import org.eclipse.lsp4j.TypeHierarchyItem;
+import org.eclipse.lsp4j.TypeHierarchyParams;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TypeHierarchyCommandTest extends AbstractInvisibleProjectBasedTest {
+
+ private IProject fJProject;
+ private TypeHierarchyCommand fCommand;
+
+ @Before
+ public void setup() throws Exception {
+ importProjects("maven/salut");
+ fJProject = WorkspaceHelper.getProject("salut");
+ fCommand = new TypeHierarchyCommand();
+ }
+
+ @Test
+ public void testTypeHierarchy() throws Exception {
+ IProgressMonitor monitor = new NullProgressMonitor();
+ TypeHierarchyParams params = new TypeHierarchyParams();
+ String uriString = fJProject.getFile("src/main/java/org/sample/TestJavadoc.java").getLocationURI().toString();
+ TextDocumentIdentifier identifier = new TextDocumentIdentifier(uriString);
+ Position position = new Position(4, 20);
+ params.setTextDocument(identifier);
+ params.setResolve(1);
+ params.setDirection(TypeHierarchyDirection.Both);
+ params.setPosition(position);
+ TypeHierarchyItem item = fCommand.typeHierarchy(params, monitor);
+ assertNotNull(item);
+ assertEquals(item.getName(), "TestJavadoc");
+ assertNotNull(item.getChildren());
+ assertEquals(item.getChildren().size(), 0);
+ assertNotNull(item.getParents());
+ assertEquals(item.getParents().size(), 1);
+ assertEquals(item.getParents().get(0).getName(), "Object");
+ }
+
+ @Test
+ public void testSuperTypeHierarchy() throws Exception {
+ IProgressMonitor monitor = new NullProgressMonitor();
+ TypeHierarchyParams params = new TypeHierarchyParams();
+ String uriString = fJProject.getFile("src/main/java/org/sample/CallHierarchy.java").getLocationURI().toString();
+ TextDocumentIdentifier identifier = new TextDocumentIdentifier(uriString);
+ Position position = new Position(7, 27);
+ params.setTextDocument(identifier);
+ params.setResolve(1);
+ params.setDirection(TypeHierarchyDirection.Parents);
+ params.setPosition(position);
+ TypeHierarchyItem item = fCommand.typeHierarchy(params, monitor);
+ assertNotNull(item);
+ assertEquals(item.getName(), "CallHierarchy$FooBuilder");
+ assertNull(item.getChildren());
+ assertEquals(item.getParents().size(), 2);
+ TypeHierarchyItem builder = item.getParents().get(0);
+ assertNotNull(builder);
+ assertEquals(builder.getName(), "Builder");
+ assertNull(builder.getParents());
+ TypeHierarchyItem object = item.getParents().get(1);
+ assertNotNull(object);
+ assertEquals(object.getName(), "Object");
+ assertNull(object.getParents());
+ }
+
+ @Test
+ public void testSubTypeHierarchy() throws Exception {
+ IProgressMonitor monitor = new NullProgressMonitor();
+ TypeHierarchyParams params = new TypeHierarchyParams();
+ String uriString = fJProject.getFile("src/main/java/org/sample/CallHierarchy.java").getLocationURI().toString();
+ TextDocumentIdentifier identifier = new TextDocumentIdentifier(uriString);
+ Position position = new Position(2, 43);
+ params.setTextDocument(identifier);
+ params.setResolve(2);
+ params.setDirection(TypeHierarchyDirection.Children);
+ params.setPosition(position);
+ TypeHierarchyItem item = fCommand.typeHierarchy(params, monitor);
+ assertNotNull(item);
+ assertEquals(item.getName(), "Builder");
+ assertNull(item.getParents());
+ assertEquals(item.getChildren().size(), 9);
+ for (TypeHierarchyItem child : item.getChildren()) {
+ List subChild = child.getChildren();
+ assertNotNull(subChild);
+ if (subChild.size() == 1) {
+ assertEquals(subChild.get(0).getName(), "ReflectionToStringBuilder");
+ }
+ }
+ }
+}