/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.java.server; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.dto.server.DtoFactory; import org.eclipse.che.ide.ext.java.shared.dto.ImplementationsDescriptorDTO; import org.eclipse.che.ide.ext.java.shared.dto.Region; import org.eclipse.che.ide.ext.java.shared.dto.model.Member; import org.eclipse.che.ide.ext.java.shared.dto.model.Type; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.ICodeAssist; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.corext.util.MethodOverrideTester; import java.util.ArrayList; import java.util.List; import static java.util.Collections.emptyList; /** * A type hierarchy provides navigations between a type and its resolved supertypes * and subtypes for a specific type or for all types within a region. * * @author Valeriy Svydenko */ @Singleton public class JavaTypeHierarchy { @Inject public JavaTypeHierarchy() { } /** * Get all implementations of selected Java Element. * * @param project * opened project * @param fqn * fully qualified name of the class file * @param offset * cursor position * @return descriptor of the implementations * @throws JavaModelException * when JavaModel has a failure */ public ImplementationsDescriptorDTO getImplementations(IJavaProject project, String fqn, int offset) throws JavaModelException { ImplementationsDescriptorDTO implementationDescriptor = DtoFactory.newDto(ImplementationsDescriptorDTO.class); IJavaElement element = getJavaElement(project, fqn, offset); if (element == null){ return implementationDescriptor.withImplementations(emptyList()); } List<Type> implementations = new ArrayList<>(); implementationDescriptor.setImplementations(implementations); switch (element.getElementType()) { case 7: //type findSubTypes(element, implementations); implementationDescriptor.setMemberName(element.getElementName()); break; case 9: //method findTypesWithSubMethods(element, implementations); implementationDescriptor.setMemberName(element.getElementName()); break; default: break; } return implementationDescriptor; } private IJavaElement getJavaElement(IJavaProject project, String fqn, int offset) throws JavaModelException { IJavaElement originalElement = null; IType type = project.findType(fqn); ICodeAssist codeAssist; if (type.isBinary()) { codeAssist = type.getClassFile(); } else { codeAssist = type.getCompilationUnit(); } IJavaElement[] elements = null; if (codeAssist != null) { elements = codeAssist.codeSelect(offset, 0); } if (elements != null && elements.length > 0) { originalElement = elements[0]; } return originalElement; } private void findSubTypes(IJavaElement element, List<Type> implementations) throws JavaModelException { IType type = (IType)element; ITypeHierarchy typeHierarchy = type.newTypeHierarchy(new NullProgressMonitor()); IType[] implTypes = typeHierarchy.getAllSubtypes(type); for (IType implType : implTypes) { Type dto = convertToTypeDTO(implType); implementations.add(dto); } } private void findTypesWithSubMethods(IJavaElement element, List<Type> implementations) throws JavaModelException { IMethod selectedMethod = (IMethod)element; IType parentType = selectedMethod.getDeclaringType(); if (parentType == null) { return; } ITypeHierarchy typeHierarchy = parentType.newTypeHierarchy(new NullProgressMonitor()); IType[] subTypes = typeHierarchy.getAllSubtypes(parentType); MethodOverrideTester methodOverrideTester = new MethodOverrideTester(parentType, typeHierarchy); for (IType type : subTypes) { IMethod method = methodOverrideTester.findOverridingMethodInType(type, selectedMethod); if (method == null) { continue; } Type openDeclaration = convertToTypeDTO(type); setRange(openDeclaration, method); implementations.add(openDeclaration); } } private void setRange(Member member, IMember iMember) throws JavaModelException { ISourceRange nameRange = iMember.getNameRange(); if (iMember.isBinary()) { nameRange = iMember.getSourceRange(); } if (nameRange == null) { return; } member.setFileRegion(convertToRegionDTO(iMember.getSourceRange())); } private Type convertToTypeDTO(IType type) throws JavaModelException { Type dto = DtoFactory.newDto(Type.class); String typeName = type.getElementName(); if (typeName.isEmpty()) { dto.setElementName("Anonymous in " + type.getParent().getElementName()); } else { dto.setElementName(type.getElementName()); } if (type.isBinary()) { dto.setRootPath(type.getFullyQualifiedName()); dto.setLibId(type.getAncestor(IPackageFragmentRoot.PACKAGE_FRAGMENT_ROOT).hashCode()); dto.setBinary(true); } else { dto.setRootPath(type.getPath().toOSString()); dto.setBinary(false); } setRange(dto, type); return dto; } private Region convertToRegionDTO(ISourceRange iSourceRange) { Region region = DtoFactory.newDto(Region.class); return iSourceRange == null ? region : region.withLength(iSourceRange.getLength()).withOffset(iSourceRange.getOffset()); } }