From c36d4bb94d8d589a97b14cf04a1fac082fa1f250 Mon Sep 17 00:00:00 2001 From: Shi Chen Date: Thu, 24 Mar 2022 10:46:57 +0800 Subject: [PATCH] Move TypeHierarchy to LSP Signed-off-by: Shi Chen --- org.eclipse.jdt.ls.core/META-INF/MANIFEST.MF | 3 +- org.eclipse.jdt.ls.core/plugin.xml | 6 - .../internal/JDTDelegateCommandHandler.java | 29 -- .../commands/TypeHierarchyCommand.java | 279 ------------- .../core/internal/handlers/InitHandler.java | 1 + .../internal/handlers/JDTLanguageServer.java | 23 ++ .../handlers/TypeHierarchyHandler.java | 282 +++++++++++++ .../eclipse/lsp4j/legacy/typeHierarchy/ABOUT | 2 - .../ResolveTypeHierarchyItemParams.java | 154 ------- .../TypeHierarchyCapabilities.java | 69 ---- .../typeHierarchy/TypeHierarchyDirection.java | 57 --- .../typeHierarchy/TypeHierarchyItem.java | 390 ------------------ .../typeHierarchy/TypeHierarchyParams.java | 116 ------ .../commands/TypeHierarchyCommandTest.java | 181 -------- .../handlers/TypeHierarchyHandlerTest.java | 165 ++++++++ 15 files changed, 472 insertions(+), 1285 deletions(-) delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommand.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandler.java delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ABOUT delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ResolveTypeHierarchyItemParams.java delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyCapabilities.java delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyDirection.java delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyItem.java delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyParams.java delete mode 100644 org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommandTest.java create mode 100644 org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandlerTest.java diff --git a/org.eclipse.jdt.ls.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.ls.core/META-INF/MANIFEST.MF index 1f1a40f0c7..6876b92b77 100644 --- a/org.eclipse.jdt.ls.core/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.ls.core/META-INF/MANIFEST.MF @@ -58,8 +58,7 @@ Export-Package: org.eclipse.jdt.ls.core.contentassist;x-friends:="org.eclipse.jd org.eclipse.jdt.ls.core.internal.syntaxserver;x-friends:="org.eclipse.jdt.ls.tests.syntaxserver", org.eclipse.jdt.ls.core.internal.text.correction;x-friends:="org.eclipse.jdt.ls.tests", org.eclipse.jdt.ls.internal.gradle.checksums;x-friends:="org.eclipse.jdt.ls.tests", - org.eclipse.lsp4j.extended;x-friends:="org.eclipse.jdt.ls.tests", - org.eclipse.lsp4j.legacy.typeHierarchy;x-friends:="org.eclipse.jdt.ls.tests" + org.eclipse.lsp4j.extended;x-friends:="org.eclipse.jdt.ls.tests" Bundle-ClassPath: lib/jsoup-1.14.2.jar, lib/remark-1.2.0.jar, . diff --git a/org.eclipse.jdt.ls.core/plugin.xml b/org.eclipse.jdt.ls.core/plugin.xml index bd5f211bd3..f65230b86c 100644 --- a/org.eclipse.jdt.ls.core/plugin.xml +++ b/org.eclipse.jdt.ls.core/plugin.xml @@ -97,12 +97,6 @@ - - - - diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java index 20697b7a99..e386536b8a 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java @@ -31,7 +31,6 @@ import org.eclipse.jdt.ls.core.internal.commands.ProjectCommand; import org.eclipse.jdt.ls.core.internal.commands.ProjectCommand.ClasspathOptions; import org.eclipse.jdt.ls.core.internal.commands.SourceAttachmentCommand; -import org.eclipse.jdt.ls.core.internal.commands.TypeHierarchyCommand; import org.eclipse.jdt.ls.core.internal.framework.protobuf.ProtobufSupport; import org.eclipse.jdt.ls.core.internal.handlers.BundleUtils; import org.eclipse.jdt.ls.core.internal.handlers.CompletionHandler; @@ -42,12 +41,7 @@ import org.eclipse.jdt.ls.core.internal.handlers.ResolveSourceMappingHandler; import org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporter; import org.eclipse.lsp4j.SymbolInformation; -import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.WorkspaceEdit; -import org.eclipse.lsp4j.legacy.typeHierarchy.ResolveTypeHierarchyItemParams; -import org.eclipse.lsp4j.legacy.typeHierarchy.TypeHierarchyDirection; -import org.eclipse.lsp4j.legacy.typeHierarchy.TypeHierarchyItem; -import org.eclipse.lsp4j.legacy.typeHierarchy.TypeHierarchyParams; public class JDTDelegateCommandHandler implements IDelegateCommandHandler { @@ -108,29 +102,6 @@ public Object executeCommand(String commandId, List 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; case "java.project.upgradeGradle": String projectUri = (String) arguments.get(0); String gradleVersion = arguments.size() > 1 ? (String) arguments.get(1) : null; 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 deleted file mode 100644 index 2d68b3d0ac..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommand.java +++ /dev/null @@ -1,279 +0,0 @@ -/******************************************************************************* - * 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.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IMember; -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.internal.core.JavaModelManager; -import org.eclipse.jdt.ls.core.internal.JDTUtils; -import org.eclipse.jdt.ls.core.internal.JDTUtils.LocationType; -import org.eclipse.jdt.ls.core.internal.JSONUtility; -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.SymbolKind; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.legacy.typeHierarchy.ResolveTypeHierarchyItemParams; -import org.eclipse.lsp4j.legacy.typeHierarchy.TypeHierarchyDirection; -import org.eclipse.lsp4j.legacy.typeHierarchy.TypeHierarchyItem; -import org.eclipse.lsp4j.legacy.typeHierarchy.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 { - IMember member = null; - IMethod targetMethod = null; - if (itemInput == null) { - member = getMember(uri, position, monitor); - if (member instanceof IMethod) { - targetMethod = (IMethod) member; - } - } else { - Map data = JSONUtility.toModel(itemInput.getData(), Map.class); - String handleIdentifier = data.get("element"); - IJavaElement element = JavaCore.create(handleIdentifier); - String methodIdentifier = data.get("method"); - if (methodIdentifier != null) { - targetMethod = (IMethod) JavaCore.create(methodIdentifier); - } - if (element instanceof IType || element instanceof IMethod) { - member = (IMember) element; - } else if (element instanceof IOrdinaryClassFile classFile) { - member = classFile.getType(); - } else { - return null; - } - } - TypeHierarchyItem item = TypeHierarchyCommand.toTypeHierarchyItem(member); - if (item == null) { - return null; - } - resolve(item, member, targetMethod, direction, resolve, monitor); - return item; - } catch (JavaModelException e) { - return null; - } - } - - private IMember getMember(String uri, Position position, IProgressMonitor monitor) throws JavaModelException { - IJavaElement typeElement = findTypeElement(JDTUtils.resolveTypeRoot(uri), position, monitor); - if (typeElement instanceof IType type) { - return type; - } else if (typeElement instanceof IMethod method) { - return method; - } 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 classFile) { - element = classFile.getType(); - } else if (unit instanceof ICompilationUnit) { - element = unit.findPrimaryType(); - } - } - return element; - } - - private static TypeHierarchyItem toTypeHierarchyItem(IMember member) throws JavaModelException { - return toTypeHierarchyItem(member, false, null); - } - - private static TypeHierarchyItem toTypeHierarchyItem(IMember member, boolean excludeMember, IMethod targetMethod) throws JavaModelException { - if (member == null) { - return null; - } - Location location = getLocation(member, LocationType.FULL_RANGE); - Location selectLocation = getLocation(member, 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()); - - IType type = null; - if (member instanceof IType) { - type = (IType) member; - } else { - type = member.getDeclaringType(); - } - - 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(excludeMember ? SymbolKind.Null : DocumentSymbolHandler.mapKind(type)); - item.setDeprecated(JDTUtils.isDeprecated(member)); - Map data = new HashMap<>(); - data.put("element", member.getHandleIdentifier()); - if (targetMethod != null) { - data.put("method", targetMethod.getHandleIdentifier()); - data.put("method_name", targetMethod.getElementName()); - } else if (member instanceof IMethod) { - data.put("method", member.getHandleIdentifier()); - data.put("method_name", member.getElementName()); - } - item.setData(data); - return item; - } - - private static Location getLocation(IMember member, LocationType locationType) throws JavaModelException { - Location location = locationType.toLocation(member); - if (location == null && member.getClassFile() != null) { - location = JDTUtils.toLocation(member.getClassFile()); - } - return location; - } - - private void resolve(TypeHierarchyItem item, IMember member, IMethod targetMethod, TypeHierarchyDirection direction, int resolve, IProgressMonitor monitor) throws JavaModelException { - if (monitor.isCanceled() || resolve <= 0) { - return; - } - - IType type = null; - if (member instanceof IType) { - type = (IType) member; - } else { - type = member.getDeclaringType(); - } - - ITypeHierarchy typeHierarchy; - if (direction == TypeHierarchyDirection.Parents) { - typeHierarchy = type.newSupertypeHierarchy(DefaultWorkingCopyOwner.PRIMARY, monitor); - } else { - ICompilationUnit[] workingCopies = JavaModelManager.getJavaModelManager().getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, true); - typeHierarchy = type.newTypeHierarchy(workingCopies, monitor); - } - if (direction == TypeHierarchyDirection.Children || direction == TypeHierarchyDirection.Both) { - List childrenItems = new ArrayList<>(); - IType[] children = typeHierarchy.getSubtypes(type); - for (IType childType : children) { - if (monitor.isCanceled()) { - return; - } - TypeHierarchyItem childItem = null; - if (targetMethod != null) { - IMethod[] matches = childType.findMethods(targetMethod); - boolean excludeMember = matches == null || matches.length == 0; - childItem = TypeHierarchyCommand.toTypeHierarchyItem(excludeMember ? childType : matches[0], excludeMember, targetMethod); - } else { - childItem = TypeHierarchyCommand.toTypeHierarchyItem(childType); - } - if (childItem == null) { - continue; - } - resolve(childItem, childType, targetMethod, 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) { - if (monitor.isCanceled()) { - return; - } - TypeHierarchyItem parentItem = null; - if (targetMethod != null) { - IMethod[] matches = parentType.findMethods(targetMethod); - boolean excludeMember = matches == null || matches.length == 0; - // Do not show java.lang.Object unless target method is based there - if (!excludeMember || !"java.lang.Object".equals(parentType.getFullyQualifiedName())) { - parentItem = TypeHierarchyCommand.toTypeHierarchyItem(excludeMember ? parentType : matches[0], excludeMember, targetMethod); - } - } else { - parentItem = TypeHierarchyCommand.toTypeHierarchyItem(parentType); - } - if (parentItem == null) { - continue; - } - resolve(parentItem, parentType, targetMethod, direction, resolve - 1, monitor); - parentsItems.add(parentItem); - } - item.setParents(parentsItems); - } - } -} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java index 440baa2c9a..c1e80683f8 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java @@ -213,6 +213,7 @@ public void registerCapabilities(InitializeResult initializeResult) { semanticTokensOptions.setDocumentSelector(List.of(new DocumentFilter("java", "file", null), new DocumentFilter("java", "jdt", null))); semanticTokensOptions.setLegend(SemanticTokensHandler.legend()); capabilities.setSemanticTokensProvider(semanticTokensOptions); + capabilities.setTypeHierarchyProvider(Boolean.TRUE); initializeResult.setCapabilities(capabilities); } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java index 64ef25142c..1b44def77f 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java @@ -142,6 +142,10 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.TypeDefinitionParams; +import org.eclipse.lsp4j.TypeHierarchyItem; +import org.eclipse.lsp4j.TypeHierarchyPrepareParams; +import org.eclipse.lsp4j.TypeHierarchySubtypesParams; +import org.eclipse.lsp4j.TypeHierarchySupertypesParams; import org.eclipse.lsp4j.WillSaveTextDocumentParams; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.WorkspaceSymbol; @@ -174,6 +178,7 @@ public class JDTLanguageServer extends BaseJDTLanguageServer implements Language private ClasspathUpdateHandler classpathUpdateHandler; private JVMConfigurator jvmConfigurator; private WorkspaceExecuteCommandHandler commandHandler; + private TypeHierarchyHandler typeHierarchyHandler = new TypeHierarchyHandler(); private ProgressReporterManager progressReporterManager; /** @@ -1128,6 +1133,24 @@ public CompletableFuture checkExtractInterfaceSta return computeAsync((monitor) -> ExtractInterfaceHandler.checkExtractInterfaceStatus(params)); } + @Override + public CompletableFuture> prepareTypeHierarchy(TypeHierarchyPrepareParams params) { + logInfo(">> textDocument/prepareTypeHierarchy"); + return computeAsync(monitor -> typeHierarchyHandler.prepareTypeHierarchy(params, monitor)); + } + + @Override + public CompletableFuture> typeHierarchySupertypes(TypeHierarchySupertypesParams params) { + logInfo(">> typeHierarchy/supertypes"); + return computeAsync(monitor -> typeHierarchyHandler.getSupertypeItems(params, monitor)); + } + + @Override + public CompletableFuture> typeHierarchySubtypes(TypeHierarchySubtypesParams params) { + logInfo(">> typeHierarchy/subtypes"); + return computeAsync(monitor -> typeHierarchyHandler.getSubtypeItems(params, monitor)); + } + private CompletableFuture computeAsyncWithClientProgress(Function code) { return CompletableFutures.computeAsync((cc) -> { IProgressMonitor monitor = progressReporterManager.getProgressReporter(cc); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandler.java new file mode 100644 index 0000000000..af57ab43a2 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandler.java @@ -0,0 +1,282 @@ +/******************************************************************************* + * 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.handlers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +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.internal.core.JavaModelManager; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JDTUtils.LocationType; +import org.eclipse.jdt.ls.core.internal.JSONUtility; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.SymbolKind; +import org.eclipse.lsp4j.SymbolTag; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TypeHierarchyItem; +import org.eclipse.lsp4j.TypeHierarchyPrepareParams; +import org.eclipse.lsp4j.TypeHierarchySubtypesParams; +import org.eclipse.lsp4j.TypeHierarchySupertypesParams; + +public class TypeHierarchyHandler { + + public enum TypeHierarchyDirection { + Subtype, Supertype; + } + + private static class TypeHierarchyItemData { + private String handleIdentifier; + private String methodIdentifier; + private String methodName; + + public TypeHierarchyItemData(String handleIdentifier, String methodIdentifier, String methodName) { + this.handleIdentifier = handleIdentifier; + this.methodIdentifier = methodIdentifier; + this.methodName = methodName; + } + + private static TypeHierarchyItemData getTypeHierarchyItemData(Object data) { + if (data == null) { + return null; + } + Map map = JSONUtility.toModel(data, Map.class); + String handleIdentifier = map.get("element"); + String methodIdentifier = map.get("method"); + String methodName = map.get("method_name"); + return new TypeHierarchyItemData(handleIdentifier, methodIdentifier, methodName); + } + } + + public List prepareTypeHierarchy(TypeHierarchyPrepareParams params, IProgressMonitor monitor) { + if (params == null) { + return Collections.emptyList(); + } + TextDocumentIdentifier textDocument = params.getTextDocument(); + if (textDocument == null) { + return Collections.emptyList(); + } + Position position = params.getPosition(); + String uri = textDocument.getUri(); + return getTypeHierarchyItems(uri, position, monitor); + } + + private List getTypeHierarchyItems(String uri, Position position, IProgressMonitor monitor) { + if (uri == null || position == null) { + return Collections.emptyList(); + } + try { + IMember member = getMember(uri, position, monitor); + IMethod targetMethod = null; + if (member instanceof IMethod) { + targetMethod = (IMethod) member; + } + TypeHierarchyItem item = targetMethod == null ? TypeHierarchyHandler.toTypeHierarchyItem(member) : TypeHierarchyHandler.toTypeHierarchyItem(member, false, targetMethod); + if (item == null) { + return Collections.emptyList(); + } + return Arrays.asList(item); + } catch (JavaModelException e) { + return Collections.emptyList(); + } + } + + public List getSupertypeItems(TypeHierarchySupertypesParams params, IProgressMonitor monitor) { + return getTypeHierarchyItems(params.getItem(), TypeHierarchyDirection.Supertype, monitor); + } + + public List getSubtypeItems(TypeHierarchySubtypesParams params, IProgressMonitor monitor) { + return getTypeHierarchyItems(params.getItem(), TypeHierarchyDirection.Subtype, monitor); + } + + private List getTypeHierarchyItems(TypeHierarchyItem item, TypeHierarchyDirection direction, IProgressMonitor monitor) { + TypeHierarchyItemData data = TypeHierarchyItemData.getTypeHierarchyItemData(item.getData()); + if (data == null) { + return Collections.emptyList(); + } + IJavaElement element = JavaCore.create(data.handleIdentifier); + IMember member = null; + IMethod targetMethod = null; + if (data.methodIdentifier != null) { + targetMethod = (IMethod) JavaCore.create(data.methodIdentifier); + } + if (element instanceof IType || element instanceof IMethod) { + member = (IMember) element; + } else if (element instanceof IOrdinaryClassFile classFile) { + member = classFile.getType(); + } else { + return Collections.emptyList(); + } + return resolveTypeHierarchyItems(member, targetMethod, direction, monitor); + } + + private List resolveTypeHierarchyItems(IMember member, IMethod targetMethod, TypeHierarchyDirection direction, IProgressMonitor monitor) { + if (monitor.isCanceled()) { + return Collections.emptyList(); + } + IType type = null; + if (member instanceof IType) { + type = (IType) member; + } else { + type = member.getDeclaringType(); + } + try { + ITypeHierarchy typeHierarchy = null; + List items = new ArrayList<>(); + IType[] hierarchyTypes = null; + if (direction == TypeHierarchyDirection.Supertype) { + typeHierarchy = type.newSupertypeHierarchy(DefaultWorkingCopyOwner.PRIMARY, monitor); + hierarchyTypes = typeHierarchy.getSupertypes(type); + } else { + ICompilationUnit[] workingCopies = JavaModelManager.getJavaModelManager().getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, true); + typeHierarchy = type.newTypeHierarchy(workingCopies, monitor); + hierarchyTypes = typeHierarchy.getSubtypes(type); + } + for (IType hierarchyType : hierarchyTypes) { + if (monitor.isCanceled()) { + return Collections.emptyList(); + } + TypeHierarchyItem item = null; + if (targetMethod != null) { + IMethod[] matches = hierarchyType.findMethods(targetMethod); + boolean excludeMember = matches == null || matches.length == 0; + // Do not show java.lang.Object unless target method is based there + if (!excludeMember || !"java.lang.Object".equals(hierarchyType.getFullyQualifiedName())) { + item = TypeHierarchyHandler.toTypeHierarchyItem(excludeMember ? hierarchyType : matches[0], excludeMember, targetMethod); + } + } else { + item = TypeHierarchyHandler.toTypeHierarchyItem(hierarchyType); + } + if (item == null) { + continue; + } + items.add(item); + } + return items; + } catch (JavaModelException e) { + return Collections.emptyList(); + } + } + + private IMember getMember(String uri, Position position, IProgressMonitor monitor) throws JavaModelException { + IJavaElement typeElement = findTypeElement(JDTUtils.resolveTypeRoot(uri), position, monitor); + if (typeElement instanceof IType type) { + return type; + } else if (typeElement instanceof IMethod method) { + return method; + } 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 classFile) { + element = classFile.getType(); + } else if (unit instanceof ICompilationUnit) { + element = unit.findPrimaryType(); + } + } + return element; + } + + private static TypeHierarchyItem toTypeHierarchyItem(IMember member) throws JavaModelException { + return toTypeHierarchyItem(member, false, null); + } + + private static TypeHierarchyItem toTypeHierarchyItem(IMember member, boolean excludeMember, IMethod targetMethod) throws JavaModelException { + if (member == null) { + return null; + } + Location location = getLocation(member, LocationType.FULL_RANGE); + Location selectLocation = getLocation(member, LocationType.NAME_RANGE); + if (location == null || selectLocation == null) { + return null; + } + + Range range = location.getRange(); + String uri = location.getUri(); + Range selectionRange = selectLocation.getRange(); + + IType type = null; + if (member instanceof IType) { + type = (IType) member; + } else { + type = member.getDeclaringType(); + } + + String name = null; + String detail = null; + String fullyQualifiedName = type.getFullyQualifiedName(); + int index = fullyQualifiedName.lastIndexOf('.'); + if (index >= 1 && index < fullyQualifiedName.length() - 1 && !type.isAnonymous()) { + name = fullyQualifiedName.substring(index + 1); + detail = fullyQualifiedName.substring(0, index); + } else { + name = JDTUtils.getName(type); + IPackageFragment packageFragment = type.getPackageFragment(); + if (packageFragment != null) { + detail = packageFragment.getElementName(); + } + } + SymbolKind kind = excludeMember ? SymbolKind.Null : DocumentSymbolHandler.mapKind(type); + List tags = new ArrayList<>(); + if (JDTUtils.isDeprecated(member)) { + tags.add(SymbolTag.Deprecated); + } + Map data = new HashMap<>(); + data.put("element", member.getHandleIdentifier()); + if (targetMethod != null) { + data.put("method", targetMethod.getHandleIdentifier()); + data.put("method_name", targetMethod.getElementName()); + } else if (member instanceof IMethod) { + data.put("method", member.getHandleIdentifier()); + data.put("method_name", member.getElementName()); + } + TypeHierarchyItem item = new TypeHierarchyItem(name, kind, uri, range, selectionRange, detail); + item.setTags(tags); + item.setData(data); + return item; + } + + private static Location getLocation(IMember member, LocationType locationType) throws JavaModelException { + Location location = locationType.toLocation(member); + if (location == null && member.getClassFile() != null) { + location = JDTUtils.toLocation(member.getClassFile()); + } + return location; + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ABOUT b/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ABOUT deleted file mode 100644 index 953d69d44f..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ABOUT +++ /dev/null @@ -1,2 +0,0 @@ -These files are extracted from lsp4j-0.12.0-sources.jar. -They're still used by JDT-LS but aren't supported by newer LSP/LSP4J. \ No newline at end of file diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ResolveTypeHierarchyItemParams.java b/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ResolveTypeHierarchyItemParams.java deleted file mode 100644 index 77fbeb5236..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/ResolveTypeHierarchyItemParams.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (c) 2016-2018 TypeFox and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, - * or the Eclipse Distribution License v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -package org.eclipse.lsp4j.legacy.typeHierarchy; - -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -import org.eclipse.lsp4j.util.Preconditions; -import org.eclipse.xtext.xbase.lib.Pure; -import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; - -import com.google.common.annotations.Beta; - -/** - * Request to resolve an unresolved {@link TypeHierarchyItem type hierarchy item} which is indicated if the - * {@link TypeHierarchyItem#getParents parents} or the {@link TypeHierarchyItem#getChildren children} is not - * defined. If resolved and no {@code parents} or {@code children} are available then an empty list is returned. - */ -@Beta -@SuppressWarnings("all") -public class ResolveTypeHierarchyItemParams { - /** - * The hierarchy item to resolve. - */ - @NonNull - private TypeHierarchyItem item; - - /** - * The number of hierarchy levels to resolve. {@code 0} indicates no hierarchy level. - */ - private int resolve; - - /** - * The direction of the type hierarchy resolution. - */ - @NonNull - private TypeHierarchyDirection direction; - - public ResolveTypeHierarchyItemParams() { - } - - public ResolveTypeHierarchyItemParams(@NonNull final TypeHierarchyItem item, final int resolve, @NonNull final TypeHierarchyDirection direction) { - this.item = Preconditions.checkNotNull(item, "item"); - this.resolve = resolve; - this.direction = Preconditions.checkNotNull(direction, "direction"); - } - - /** - * The hierarchy item to resolve. - */ - @Pure - @NonNull - public TypeHierarchyItem getItem() { - return this.item; - } - - /** - * The hierarchy item to resolve. - */ - public void setItem(@NonNull final TypeHierarchyItem item) { - this.item = Preconditions.checkNotNull(item, "item"); - } - - /** - * The number of hierarchy levels to resolve. {@code 0} indicates no hierarchy level. - */ - @Pure - public int getResolve() { - return this.resolve; - } - - /** - * The number of hierarchy levels to resolve. {@code 0} indicates no hierarchy level. - */ - public void setResolve(final int resolve) { - this.resolve = resolve; - } - - /** - * The direction of the type hierarchy resolution. - */ - @Pure - @NonNull - public TypeHierarchyDirection getDirection() { - return this.direction; - } - - /** - * The direction of the type hierarchy resolution. - */ - public void setDirection(@NonNull final TypeHierarchyDirection direction) { - this.direction = Preconditions.checkNotNull(direction, "direction"); - } - - @Override - @Pure - public String toString() { - ToStringBuilder b = new ToStringBuilder(this); - b.add("item", this.item); - b.add("resolve", this.resolve); - b.add("direction", this.direction); - return b.toString(); - } - - @Override - @Pure - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ResolveTypeHierarchyItemParams other = (ResolveTypeHierarchyItemParams) obj; - if (this.item == null) { - if (other.item != null) { - return false; - } - } else if (!this.item.equals(other.item)) { - return false; - } - if (other.resolve != this.resolve) { - return false; - } - if (this.direction == null) { - if (other.direction != null) { - return false; - } - } else if (!this.direction.equals(other.direction)) { - return false; - } - return true; - } - - @Override - @Pure - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.item== null) ? 0 : this.item.hashCode()); - result = prime * result + this.resolve; - return prime * result + ((this.direction== null) ? 0 : this.direction.hashCode()); - } -} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyCapabilities.java b/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyCapabilities.java deleted file mode 100644 index e9129646e5..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyCapabilities.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2016-2018 TypeFox and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, - * or the Eclipse Distribution License v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -package org.eclipse.lsp4j.legacy.typeHierarchy; - -import org.eclipse.lsp4j.DynamicRegistrationCapabilities; -import org.eclipse.xtext.xbase.lib.Pure; -import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; - -import com.google.common.annotations.Beta; - -/** - * Capabilities specific to the {@code textDocument/typeHierarchy}. - * - *

- * Note: the {@code textDocument/typeHierarchy} - * language feature is not yet part of the official LSP specification. - */ -@Beta -@SuppressWarnings("all") -public class TypeHierarchyCapabilities extends DynamicRegistrationCapabilities { - public TypeHierarchyCapabilities() { - } - - public TypeHierarchyCapabilities(final Boolean dynamicRegistration) { - super(dynamicRegistration); - } - - @Override - @Pure - public String toString() { - ToStringBuilder b = new ToStringBuilder(this); - b.add("dynamicRegistration", getDynamicRegistration()); - return b.toString(); - } - - @Override - @Pure - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - if (!super.equals(obj)) { - return false; - } - return true; - } - - @Override - @Pure - public int hashCode() { - return super.hashCode(); - } -} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyDirection.java b/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyDirection.java deleted file mode 100644 index acc45835d1..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyDirection.java +++ /dev/null @@ -1,57 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2019 TypeFox and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, - * or the Eclipse Distribution License v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - ******************************************************************************/ -package org.eclipse.lsp4j.legacy.typeHierarchy; - -import org.eclipse.lsp4j.services.TextDocumentService; - -/** - * Direction specific to the - * {@link TextDocumentService#typeHierarchy(TypeHierarchyParams) - * textDocument/typeHierarchy} and - * {@link TextDocumentService#resolveTypeHierarchy(ResolveTypeHierarchyItemParams) - * typeHierarchy/resolve} LS methods. - * - *

- * Valid values are: - *

    - *
  • {@link TypeHierarchyDirection#Children Children},
  • - *
  • {@link TypeHierarchyDirection#Parents Parents},
  • - *
  • {@link TypeHierarchyDirection#Both Both}.
  • - *
- * - */ -public enum TypeHierarchyDirection { - - /** - * Flag for retrieving/resolving the subtypes. Value: {@code 0}. - */ - Children, - - /** - * Flag to use when retrieving/resolving the supertypes. Value: {@code 1}. - */ - Parents, - - /** - * Flag for resolving both the super- and subtypes. Value: {@code 2}. - */ - Both; - - public static TypeHierarchyDirection forValue(int value) { - TypeHierarchyDirection[] values = TypeHierarchyDirection.values(); - if (value < 0 || value >= values.length) { - throw new IllegalArgumentException("Illegal enum value: " + value); - } - return values[value]; - } - -} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyItem.java b/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyItem.java deleted file mode 100644 index 68668f73e1..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyItem.java +++ /dev/null @@ -1,390 +0,0 @@ -/** - * Copyright (c) 2016-2018 TypeFox and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, - * or the Eclipse Distribution License v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -package org.eclipse.lsp4j.legacy.typeHierarchy; - -import java.util.List; - -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.SymbolKind; -import org.eclipse.lsp4j.jsonrpc.json.adapters.JsonElementTypeAdapter; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -import org.eclipse.lsp4j.util.Preconditions; -import org.eclipse.xtext.xbase.lib.Pure; -import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; - -import com.google.common.annotations.Beta; -import com.google.gson.annotations.JsonAdapter; - -/** - * Representation of an item that carries type information (such as class, interface, enumeration, etc) with additional parentage details. - */ -@Beta -@SuppressWarnings("all") -public class TypeHierarchyItem { - /** - * The human readable name of the hierarchy item. - */ - @NonNull - private String name; - - /** - * Optional detail for the hierarchy item. It can be, for instance, the signature of a function or method. - */ - private String detail; - - /** - * The kind of the hierarchy item. For instance, class or interface. - */ - @NonNull - private SymbolKind kind; - - /** - * {@code true} if the hierarchy item is deprecated. Otherwise, {@code false}. It is {@code false} by default. - */ - private Boolean deprecated; - - /** - * The URI of the text document where this type hierarchy item belongs to. - */ - @NonNull - private String uri; - - /** - * The range enclosing this type hierarchy item not including leading/trailing whitespace but everything else - * like comments. This information is typically used to determine if the clients cursor is inside the type - * hierarchy item to reveal in the symbol in the UI. - * - * @see TypeHierarchyItem#selectionRange - */ - @NonNull - private Range range; - - /** - * The range that should be selected and revealed when this type hierarchy item is being picked, e.g the name of a function. - * Must be contained by the the {@link TypeHierarchyItem#getRange range}. - * - * @see TypeHierarchyItem#range - */ - @NonNull - private Range selectionRange; - - /** - * If this type hierarchy item is resolved, it contains the direct parents. Could be empty if the item does not have any - * direct parents. If not defined, the parents have not been resolved yet. - */ - private List parents; - - /** - * If this type hierarchy item is resolved, it contains the direct children of the current item. - * Could be empty if the item does not have any descendants. If not defined, the children have not been resolved. - */ - private List children; - - /** - * An optional data field can be used to identify a type hierarchy item in a resolve request. - */ - @JsonAdapter(JsonElementTypeAdapter.Factory.class) - private Object data; - - /** - * The human readable name of the hierarchy item. - */ - @Pure - @NonNull - public String getName() { - return this.name; - } - - /** - * The human readable name of the hierarchy item. - */ - public void setName(@NonNull final String name) { - this.name = Preconditions.checkNotNull(name, "name"); - } - - /** - * Optional detail for the hierarchy item. It can be, for instance, the signature of a function or method. - */ - @Pure - public String getDetail() { - return this.detail; - } - - /** - * Optional detail for the hierarchy item. It can be, for instance, the signature of a function or method. - */ - public void setDetail(final String detail) { - this.detail = detail; - } - - /** - * The kind of the hierarchy item. For instance, class or interface. - */ - @Pure - @NonNull - public SymbolKind getKind() { - return this.kind; - } - - /** - * The kind of the hierarchy item. For instance, class or interface. - */ - public void setKind(@NonNull final SymbolKind kind) { - this.kind = Preconditions.checkNotNull(kind, "kind"); - } - - /** - * {@code true} if the hierarchy item is deprecated. Otherwise, {@code false}. It is {@code false} by default. - */ - @Pure - public Boolean getDeprecated() { - return this.deprecated; - } - - /** - * {@code true} if the hierarchy item is deprecated. Otherwise, {@code false}. It is {@code false} by default. - */ - public void setDeprecated(final Boolean deprecated) { - this.deprecated = deprecated; - } - - /** - * The URI of the text document where this type hierarchy item belongs to. - */ - @Pure - @NonNull - public String getUri() { - return this.uri; - } - - /** - * The URI of the text document where this type hierarchy item belongs to. - */ - public void setUri(@NonNull final String uri) { - this.uri = Preconditions.checkNotNull(uri, "uri"); - } - - /** - * The range enclosing this type hierarchy item not including leading/trailing whitespace but everything else - * like comments. This information is typically used to determine if the clients cursor is inside the type - * hierarchy item to reveal in the symbol in the UI. - * - * @see TypeHierarchyItem#selectionRange - */ - @Pure - @NonNull - public Range getRange() { - return this.range; - } - - /** - * The range enclosing this type hierarchy item not including leading/trailing whitespace but everything else - * like comments. This information is typically used to determine if the clients cursor is inside the type - * hierarchy item to reveal in the symbol in the UI. - * - * @see TypeHierarchyItem#selectionRange - */ - public void setRange(@NonNull final Range range) { - this.range = Preconditions.checkNotNull(range, "range"); - } - - /** - * The range that should be selected and revealed when this type hierarchy item is being picked, e.g the name of a function. - * Must be contained by the the {@link TypeHierarchyItem#getRange range}. - * - * @see TypeHierarchyItem#range - */ - @Pure - @NonNull - public Range getSelectionRange() { - return this.selectionRange; - } - - /** - * The range that should be selected and revealed when this type hierarchy item is being picked, e.g the name of a function. - * Must be contained by the the {@link TypeHierarchyItem#getRange range}. - * - * @see TypeHierarchyItem#range - */ - public void setSelectionRange(@NonNull final Range selectionRange) { - this.selectionRange = Preconditions.checkNotNull(selectionRange, "selectionRange"); - } - - /** - * If this type hierarchy item is resolved, it contains the direct parents. Could be empty if the item does not have any - * direct parents. If not defined, the parents have not been resolved yet. - */ - @Pure - public List getParents() { - return this.parents; - } - - /** - * If this type hierarchy item is resolved, it contains the direct parents. Could be empty if the item does not have any - * direct parents. If not defined, the parents have not been resolved yet. - */ - public void setParents(final List parents) { - this.parents = parents; - } - - /** - * If this type hierarchy item is resolved, it contains the direct children of the current item. - * Could be empty if the item does not have any descendants. If not defined, the children have not been resolved. - */ - @Pure - public List getChildren() { - return this.children; - } - - /** - * If this type hierarchy item is resolved, it contains the direct children of the current item. - * Could be empty if the item does not have any descendants. If not defined, the children have not been resolved. - */ - public void setChildren(final List children) { - this.children = children; - } - - /** - * An optional data field can be used to identify a type hierarchy item in a resolve request. - */ - @Pure - public Object getData() { - return this.data; - } - - /** - * An optional data field can be used to identify a type hierarchy item in a resolve request. - */ - public void setData(final Object data) { - this.data = data; - } - - @Override - @Pure - public String toString() { - ToStringBuilder b = new ToStringBuilder(this); - b.add("name", this.name); - b.add("detail", this.detail); - b.add("kind", this.kind); - b.add("deprecated", this.deprecated); - b.add("uri", this.uri); - b.add("range", this.range); - b.add("selectionRange", this.selectionRange); - b.add("parents", this.parents); - b.add("children", this.children); - b.add("data", this.data); - return b.toString(); - } - - @Override - @Pure - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - TypeHierarchyItem other = (TypeHierarchyItem) obj; - if (this.name == null) { - if (other.name != null) { - return false; - } - } else if (!this.name.equals(other.name)) { - return false; - } - if (this.detail == null) { - if (other.detail != null) { - return false; - } - } else if (!this.detail.equals(other.detail)) { - return false; - } - if (this.kind == null) { - if (other.kind != null) { - return false; - } - } else if (!this.kind.equals(other.kind)) { - return false; - } - if (this.deprecated == null) { - if (other.deprecated != null) { - return false; - } - } else if (!this.deprecated.equals(other.deprecated)) { - return false; - } - if (this.uri == null) { - if (other.uri != null) { - return false; - } - } else if (!this.uri.equals(other.uri)) { - return false; - } - if (this.range == null) { - if (other.range != null) { - return false; - } - } else if (!this.range.equals(other.range)) { - return false; - } - if (this.selectionRange == null) { - if (other.selectionRange != null) { - return false; - } - } else if (!this.selectionRange.equals(other.selectionRange)) { - return false; - } - if (this.parents == null) { - if (other.parents != null) { - return false; - } - } else if (!this.parents.equals(other.parents)) { - return false; - } - if (this.children == null) { - if (other.children != null) { - return false; - } - } else if (!this.children.equals(other.children)) { - return false; - } - if (this.data == null) { - if (other.data != null) { - return false; - } - } else if (!this.data.equals(other.data)) { - return false; - } - return true; - } - - @Override - @Pure - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.name== null) ? 0 : this.name.hashCode()); - result = prime * result + ((this.detail== null) ? 0 : this.detail.hashCode()); - result = prime * result + ((this.kind== null) ? 0 : this.kind.hashCode()); - result = prime * result + ((this.deprecated== null) ? 0 : this.deprecated.hashCode()); - result = prime * result + ((this.uri== null) ? 0 : this.uri.hashCode()); - result = prime * result + ((this.range== null) ? 0 : this.range.hashCode()); - result = prime * result + ((this.selectionRange== null) ? 0 : this.selectionRange.hashCode()); - result = prime * result + ((this.parents== null) ? 0 : this.parents.hashCode()); - result = prime * result + ((this.children== null) ? 0 : this.children.hashCode()); - return prime * result + ((this.data== null) ? 0 : this.data.hashCode()); - } -} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyParams.java b/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyParams.java deleted file mode 100644 index a6ef5f4a34..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/lsp4j/legacy/typeHierarchy/TypeHierarchyParams.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2016-2018 TypeFox and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, - * or the Eclipse Distribution License v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -package org.eclipse.lsp4j.legacy.typeHierarchy; - -import org.eclipse.lsp4j.TextDocumentPositionParams; -import org.eclipse.xtext.xbase.lib.Pure; -import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; - -import com.google.common.annotations.Beta; - -/** - * The type hierarchy request is sent from the client resolve a {@link TypeHierarchyItem type hierarchy item} for - * a give cursor location in the text document. The request would also allow to specify if the item should be resolved - * and whether sub- or supertypes are to be resolved. - */ -@Beta -@SuppressWarnings("all") -public class TypeHierarchyParams extends TextDocumentPositionParams { - /** - * The number of hierarchy levels to resolve. {@code 0} indicates no hierarchy level. It defaults to {@code 0}. - */ - private int resolve; - - /** - * The direction of the type hierarchy resolution. If not defined, defaults to {@link TypeHierarchyDirection#Children Children}. - */ - private TypeHierarchyDirection direction; - - /** - * The number of hierarchy levels to resolve. {@code 0} indicates no hierarchy level. It defaults to {@code 0}. - */ - @Pure - public int getResolve() { - return this.resolve; - } - - /** - * The number of hierarchy levels to resolve. {@code 0} indicates no hierarchy level. It defaults to {@code 0}. - */ - public void setResolve(final int resolve) { - this.resolve = resolve; - } - - /** - * The direction of the type hierarchy resolution. If not defined, defaults to {@link TypeHierarchyDirection#Children Children}. - */ - @Pure - public TypeHierarchyDirection getDirection() { - return this.direction; - } - - /** - * The direction of the type hierarchy resolution. If not defined, defaults to {@link TypeHierarchyDirection#Children Children}. - */ - public void setDirection(final TypeHierarchyDirection direction) { - this.direction = direction; - } - - @Override - @Pure - public String toString() { - ToStringBuilder b = new ToStringBuilder(this); - b.add("resolve", this.resolve); - b.add("direction", this.direction); - b.add("textDocument", getTextDocument()); - b.add("position", getPosition()); - return b.toString(); - } - - @Override - @Pure - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - if (!super.equals(obj)) { - return false; - } - TypeHierarchyParams other = (TypeHierarchyParams) obj; - if (other.resolve != this.resolve) { - return false; - } - if (this.direction == null) { - if (other.direction != null) { - return false; - } - } else if (!this.direction.equals(other.direction)) { - return false; - } - return true; - } - - @Override - @Pure - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + this.resolve; - return prime * result + ((this.direction== null) ? 0 : this.direction.hashCode()); - } -} 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 deleted file mode 100644 index 1d3678cf06..0000000000 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/commands/TypeHierarchyCommandTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/******************************************************************************* - * 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.SymbolKind; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.legacy.typeHierarchy.ResolveTypeHierarchyItemParams; -import org.eclipse.lsp4j.legacy.typeHierarchy.TypeHierarchyDirection; -import org.eclipse.lsp4j.legacy.typeHierarchy.TypeHierarchyItem; -import org.eclipse.lsp4j.legacy.typeHierarchy.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"); - } - } - } - - // https://github.com/redhat-developer/vscode-java/issues/2871 - @Test - public void testMultipleProjects() throws Exception { - importProjects("eclipse/gh2871"); - IProject project = WorkspaceHelper.getProject("project1"); - IProgressMonitor monitor = new NullProgressMonitor(); - TypeHierarchyParams params = new TypeHierarchyParams(); - String uriString = project.getFile("src/org/sample/First.java").getLocationURI().toString(); - TextDocumentIdentifier identifier = new TextDocumentIdentifier(uriString); - Position position = new Position(1, 22); - params.setTextDocument(identifier); - params.setResolve(1); - params.setDirection(TypeHierarchyDirection.Both); - params.setPosition(position); - TypeHierarchyItem item = fCommand.typeHierarchy(params, monitor); - assertNotNull(item); - assertEquals(item.getName(), "First"); - assertNotNull(item.getChildren()); - assertEquals(item.getChildren().size(), 1); - assertEquals(item.getChildren().get(0).getName(), "Second"); - } - - @Test - public void testMethodHierarchy() throws Exception { - importProjects("maven/type-hierarchy"); - IProject project = WorkspaceHelper.getProject("type-hierarchy"); - String uriString = project.getFile("src/main/java/org/example/Zero.java").getLocationURI().toString(); - TextDocumentIdentifier identifier = new TextDocumentIdentifier(uriString); - Position position = new Position(3, 17); // public void f[o]o() - TypeHierarchyParams zeroParams = new TypeHierarchyParams(); - zeroParams.setTextDocument(identifier); - zeroParams.setResolve(1); - zeroParams.setDirection(TypeHierarchyDirection.Both); - zeroParams.setPosition(position); - TypeHierarchyItem zero = fCommand.typeHierarchy(zeroParams, monitor); - - // do not show java.lang.Object if target method isn't from there - assertEquals(0, zero.getParents().size()); - - assertEquals(SymbolKind.Class, zero.getKind()); // zero - assertEquals(SymbolKind.Class, zero.getChildren().get(0).getKind()); // one - assertEquals(SymbolKind.Null, zero.getChildren().get(1).getKind()); // two - - ResolveTypeHierarchyItemParams oneParams = new ResolveTypeHierarchyItemParams(); - oneParams.setItem(zero.getChildren().get(0)); - oneParams.setDirection(TypeHierarchyDirection.Both); - oneParams.setResolve(1); - TypeHierarchyItem one = fCommand.resolveTypeHierarchy(oneParams, monitor); - - assertEquals(SymbolKind.Null, one.getChildren().get(1).getKind()); // three - assertEquals(SymbolKind.Class, one.getChildren().get(0).getKind()); // four - - ResolveTypeHierarchyItemParams twoParams = new ResolveTypeHierarchyItemParams(); - twoParams.setItem(zero.getChildren().get(1)); - twoParams.setDirection(TypeHierarchyDirection.Both); - twoParams.setResolve(1); - TypeHierarchyItem two = fCommand.resolveTypeHierarchy(twoParams, monitor); - - assertEquals(SymbolKind.Null, two.getChildren().get(0).getKind()); // five - assertEquals(SymbolKind.Class, two.getChildren().get(1).getKind()); // six - } -} diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandlerTest.java new file mode 100644 index 0000000000..ca7219a027 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandlerTest.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * 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.handlers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +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.SymbolKind; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TypeHierarchyItem; +import org.eclipse.lsp4j.TypeHierarchyPrepareParams; +import org.eclipse.lsp4j.TypeHierarchySubtypesParams; +import org.eclipse.lsp4j.TypeHierarchySupertypesParams; +import org.junit.Before; +import org.junit.Test; + +public class TypeHierarchyHandlerTest extends AbstractInvisibleProjectBasedTest { + + private IProject fJProject; + private TypeHierarchyHandler fHandler; + + @Before + public void setup() throws Exception { + importProjects("maven/salut"); + fJProject = WorkspaceHelper.getProject("salut"); + fHandler = new TypeHierarchyHandler(); + } + + @Test + public void testSuperTypeHierarchy() throws Exception { + IProgressMonitor monitor = new NullProgressMonitor(); + TypeHierarchyPrepareParams params = new TypeHierarchyPrepareParams(); + 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.setPosition(position); + List items = fHandler.prepareTypeHierarchy(params, monitor); + assertNotNull(items); + assertEquals(1, items.size()); + assertEquals(items.get(0).getName(), "CallHierarchy$FooBuilder"); + TypeHierarchySupertypesParams supertypesParams = new TypeHierarchySupertypesParams(); + supertypesParams.setItem(items.get(0)); + List supertypesItems = fHandler.getSupertypeItems(supertypesParams, monitor); + assertNotNull(supertypesItems); + assertEquals(2, supertypesItems.size()); + assertEquals(supertypesItems.get(0).getName(), "Builder"); + assertEquals(supertypesItems.get(0).getKind(), SymbolKind.Interface); + assertEquals(supertypesItems.get(1).getName(), "Object"); + assertEquals(supertypesItems.get(1).getKind(), SymbolKind.Class); + } + + @Test + public void testSubTypeHierarchy() throws Exception { + IProgressMonitor monitor = new NullProgressMonitor(); + TypeHierarchyPrepareParams params = new TypeHierarchyPrepareParams(); + 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.setPosition(position); + List items = fHandler.prepareTypeHierarchy(params, monitor); + assertNotNull(items); + assertEquals(1, items.size()); + assertEquals(items.get(0).getName(), "Builder"); + TypeHierarchySubtypesParams supertypesParams = new TypeHierarchySubtypesParams(); + supertypesParams.setItem(items.get(0)); + List subtypesItems = fHandler.getSubtypeItems(supertypesParams, monitor); + assertNotNull(subtypesItems); + assertEquals(9, subtypesItems.size()); + } + + // https://github.com/redhat-developer/vscode-java/issues/2871 + @Test + public void testMultipleProjects() throws Exception { + importProjects("eclipse/gh2871"); + IProject project = WorkspaceHelper.getProject("project1"); + IProgressMonitor monitor = new NullProgressMonitor(); + TypeHierarchyPrepareParams params = new TypeHierarchyPrepareParams(); + String uriString = project.getFile("src/org/sample/First.java").getLocationURI().toString(); + TextDocumentIdentifier identifier = new TextDocumentIdentifier(uriString); + Position position = new Position(1, 22); + params.setTextDocument(identifier); + params.setPosition(position); + List items = fHandler.prepareTypeHierarchy(params, monitor); + assertNotNull(items); + assertEquals(1, items.size()); + assertEquals("First", items.get(0).getName()); + TypeHierarchySubtypesParams supertypesParams = new TypeHierarchySubtypesParams(); + supertypesParams.setItem(items.get(0)); + List subtypesItems = fHandler.getSubtypeItems(supertypesParams, monitor); + assertNotNull(subtypesItems); + assertEquals(1, subtypesItems.size()); + assertEquals("Second", subtypesItems.get(0).getName()); + } + + @Test + public void testMethodHierarchy() throws Exception { + importProjects("maven/type-hierarchy"); + IProject project = WorkspaceHelper.getProject("type-hierarchy"); + String uriString = project.getFile("src/main/java/org/example/Zero.java").getLocationURI().toString(); + TextDocumentIdentifier identifier = new TextDocumentIdentifier(uriString); + Position position = new Position(3, 17); // public void f[o]o() + TypeHierarchyPrepareParams zeroParams = new TypeHierarchyPrepareParams(); + zeroParams.setTextDocument(identifier); + zeroParams.setPosition(position); + List zeroItems = fHandler.prepareTypeHierarchy(zeroParams, monitor); + assertNotNull(zeroItems); + assertEquals(1, zeroItems.size()); + assertEquals("Zero", zeroItems.get(0).getName()); + assertEquals(SymbolKind.Class, zeroItems.get(0).getKind()); + + TypeHierarchySupertypesParams supertypesParams = new TypeHierarchySupertypesParams(); + supertypesParams.setItem(zeroItems.get(0)); + List supertypesItems = fHandler.getSupertypeItems(supertypesParams, monitor); + assertNotNull(supertypesItems); + // do not show java.lang.Object if target method isn't from there + assertEquals(0, supertypesItems.size()); + + TypeHierarchySubtypesParams subtypesParams = new TypeHierarchySubtypesParams(); + subtypesParams.setItem(zeroItems.get(0)); + List subtypesItems = fHandler.getSubtypeItems(subtypesParams, monitor); + assertNotNull(subtypesItems); + assertEquals(SymbolKind.Class, subtypesItems.get(0).getKind()); // one + assertEquals("One", subtypesItems.get(0).getName()); // one + assertEquals(SymbolKind.Null, subtypesItems.get(1).getKind()); // two + assertEquals("Two", subtypesItems.get(1).getName()); // two + + TypeHierarchyItem one = subtypesItems.get(0); + TypeHierarchyItem two = subtypesItems.get(1); + subtypesParams.setItem(one); + subtypesItems = fHandler.getSubtypeItems(subtypesParams, monitor); + assertNotNull(subtypesItems); + assertEquals(SymbolKind.Null, subtypesItems.get(1).getKind()); // three + assertEquals("Three", subtypesItems.get(1).getName()); // three + assertEquals(SymbolKind.Class, subtypesItems.get(0).getKind()); // four + assertEquals("Four", subtypesItems.get(0).getName()); // four + + subtypesParams.setItem(two); + subtypesItems = fHandler.getSubtypeItems(subtypesParams, monitor); + assertNotNull(subtypesItems); + assertEquals(SymbolKind.Null, subtypesItems.get(0).getKind()); // five + assertEquals("Five", subtypesItems.get(0).getName()); // five + assertEquals(SymbolKind.Class, subtypesItems.get(1).getKind()); // six + assertEquals("Six", subtypesItems.get(1).getName()); // six + } +}