| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * 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 |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.core.hierarchy; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.DLTKLanguageManager; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IFileHierarchyInfo; |
| import org.eclipse.dltk.core.IFileHierarchyResolver; |
| import org.eclipse.dltk.core.ISearchPatternProcessor; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.IType; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule; |
| import org.eclipse.dltk.core.index2.search.ModelAccess; |
| import org.eclipse.dltk.core.search.IDLTKSearchConstants; |
| import org.eclipse.dltk.core.search.IDLTKSearchScope; |
| import org.eclipse.dltk.core.search.SearchEngine; |
| import org.eclipse.dltk.core.search.SearchPattern; |
| import org.eclipse.dltk.core.search.TypeNameRequestor; |
| import org.eclipse.dltk.internal.core.ModelElement; |
| import org.eclipse.dltk.internal.core.Openable; |
| import org.eclipse.dltk.internal.core.util.HandleFactory; |
| |
| public class HierarchyResolver { |
| |
| private HierarchyBuilder hierarchyBuilder; |
| |
| public HierarchyResolver(HierarchyBuilder hierarchy) { |
| this.hierarchyBuilder = hierarchy; |
| } |
| |
| public void resolve(boolean computeSubtypes) throws CoreException { |
| |
| IType focusType = hierarchyBuilder.getType(); |
| hierarchyBuilder.hierarchy.initialize(0); |
| |
| if (computeSubtypes) { |
| computeSubtypes(focusType); |
| } |
| computeSupertypes(focusType); |
| } |
| |
| private IType[] findTypes(String pattern, IDLTKSearchScope scope) |
| throws ModelException { |
| |
| // First try to use new indexing infrastructure: |
| IType[] types = new ModelAccess().findTypes(pattern, |
| pattern == null ? MatchRule.PREFIX : MatchRule.EXACT, 0, 0, |
| scope, hierarchyBuilder.hierarchy.progressMonitor); |
| if (types != null) { |
| return types; |
| } |
| |
| // Use JDT-like index: |
| final List<IType> result = new LinkedList<>(); |
| final HandleFactory handleFactory = new HandleFactory(); |
| TypeNameRequestor typesCollector = new TypeNameRequestor() { |
| @Override |
| public void acceptType(int modifiers, char[] packageName, |
| char[] simpleTypeName, char[][] enclosingTypeNames, |
| char[][] superTypes, String path) { |
| |
| if (superTypes != null) { |
| for (int i = 0; i < superTypes.length; i++) { |
| Openable openable = handleFactory.createOpenable(path, |
| hierarchyBuilder.hierarchy.scope); |
| ModelElement parent = openable; |
| boolean binary = false; |
| if (openable instanceof ISourceModule) { |
| binary = ((ISourceModule) openable).isBinary(); |
| } |
| if (enclosingTypeNames != null) { |
| if (!binary) { |
| for (int j = 0; j < enclosingTypeNames.length; ++j) { |
| parent = new FakeType(parent, |
| new String(enclosingTypeNames[j])); |
| } |
| } else { |
| for (int j = 0; j < enclosingTypeNames.length; ++j) { |
| if (parent instanceof ISourceModule) { |
| parent = (ModelElement) ((ISourceModule) parent) |
| .getType(new String( |
| enclosingTypeNames[j])); |
| } else if (parent instanceof IType) { |
| parent = (ModelElement) ((IType) parent) |
| .getType(new String( |
| enclosingTypeNames[j])); |
| } |
| if (parent == null) { |
| break; |
| } |
| } |
| } |
| } |
| if (parent != null) { |
| if (binary) { |
| IType type = null; |
| if (parent instanceof ISourceModule) { |
| type = ((ISourceModule) parent).getType( |
| new String(simpleTypeName)); |
| } else if (parent instanceof IType) { |
| type = ((IType) parent).getType( |
| new String(simpleTypeName)); |
| } |
| if (type != null) { |
| result.add(type); |
| } |
| |
| } else { |
| FakeType type = new FakeType(parent, |
| new String(simpleTypeName), modifiers); |
| result.add(type); |
| } |
| } |
| } |
| } |
| } |
| }; |
| int matchRule = SearchPattern.R_EXACT_MATCH; |
| if (pattern == null) { |
| pattern = "*"; //$NON-NLS-1$ |
| matchRule = SearchPattern.R_PATTERN_MATCH; |
| } |
| |
| SearchEngine searchEngine = new SearchEngine(); |
| searchEngine.searchAllTypeNames(null, 0, pattern.toCharArray(), |
| matchRule, IDLTKSearchConstants.DECLARATIONS, |
| hierarchyBuilder.hierarchy.scope, typesCollector, |
| IDLTKSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, |
| hierarchyBuilder.hierarchy.progressMonitor); |
| |
| return result.toArray(new IType[result.size()]); |
| } |
| |
| protected void computeSubtypes(IType focusType) throws CoreException { |
| |
| // Collect all inheritance information: |
| final Map<String, List<String>> superTypeToExtender = new HashMap<>(); |
| final String delimiter = getDelimiterReplacementString(focusType); |
| |
| Map<String, Set<IType>> tmpCache = new HashMap<>(); |
| |
| IType[] types = findTypes(null, hierarchyBuilder.hierarchy.scope); |
| for (IType type : types) { |
| String[] superTypes = type.getSuperClasses(); |
| if (superTypes != null) { |
| for (int i = 0; i < superTypes.length; i++) { |
| String s = superTypes[i]; |
| List<String> extenders = superTypeToExtender.get(s); |
| if (extenders == null) { |
| extenders = new LinkedList<>(); |
| superTypeToExtender.put(s, extenders); |
| } |
| extenders.add(type.getTypeQualifiedName(delimiter)); |
| } |
| } |
| |
| // Cache this type for further searches |
| String elementName = type.getElementName(); |
| Set<IType> set = tmpCache.get(elementName); |
| if (set == null) { |
| set = new HashSet<>(); |
| tmpCache.put(elementName, set); |
| } |
| set.add(type); |
| } |
| |
| // Rebuild temporary cache in a useful format: |
| HashMap<String, IType[]> cache = new HashMap<>(); |
| Iterator<String> i = tmpCache.keySet().iterator(); |
| while (i.hasNext()) { |
| String typeName = i.next(); |
| Set<IType> typeElements = tmpCache.get(typeName); |
| cache.put(typeName, |
| typeElements.toArray(new IType[typeElements.size()])); |
| } |
| |
| // Create file hierarchy resolver for filtering non-included elements |
| IFileHierarchyResolver fileHierarchyResolver = createFileHierarchyResolver( |
| focusType); |
| IFileHierarchyInfo hierarchyInfo = null; |
| if (fileHierarchyResolver != null) { |
| hierarchyInfo = fileHierarchyResolver.resolveDown( |
| focusType.getSourceModule(), |
| hierarchyBuilder.hierarchy.progressMonitor); |
| } |
| |
| computeSubtypesFor(focusType, superTypeToExtender, cache, hierarchyInfo, |
| new HashSet<IType>(), delimiter); |
| } |
| |
| protected void computeSubtypesFor(IType focusType, |
| Map<String, List<String>> superTypeToExtender, |
| Map<String, IType[]> subTypesCache, |
| IFileHierarchyInfo hierarchyInfo, Set<IType> processedTypes, |
| String delimiter) throws CoreException { |
| |
| List<String> extenders = superTypeToExtender |
| .get(focusType.getTypeQualifiedName(delimiter)); |
| if (extenders != null) { |
| IType[] subTypes = searchTypes( |
| extenders.toArray(new String[extenders.size()]), |
| subTypesCache, hierarchyInfo); |
| for (int i = 0; i < subTypes.length; i++) { |
| IType subType = subTypes[i]; |
| hierarchyBuilder.hierarchy.addSubtype(focusType, subType); |
| } |
| |
| for (int i = 0; i < subTypes.length; i++) { |
| IType subType = subTypes[i]; |
| if (processedTypes.add(subType)) { |
| computeSubtypesFor(subType, superTypeToExtender, |
| subTypesCache, hierarchyInfo, processedTypes, |
| delimiter); |
| } |
| } |
| } |
| } |
| |
| protected void computeSupertypes(IType focusType) throws CoreException { |
| IFileHierarchyResolver fileHierarchyResolver = createFileHierarchyResolver( |
| focusType); |
| IFileHierarchyInfo hierarchyInfo = null; |
| if (fileHierarchyResolver != null) { |
| hierarchyInfo = fileHierarchyResolver.resolveUp( |
| focusType.getSourceModule(), |
| hierarchyBuilder.hierarchy.progressMonitor); |
| } |
| |
| computeSupertypesFor(focusType, new HashMap<String, IType[]>(), |
| hierarchyInfo, new HashSet<IType>()); |
| } |
| |
| protected void computeSupertypesFor(IType focusType, |
| Map<String, IType[]> superTypesCache, |
| IFileHierarchyInfo hierarchyInfo, Set<IType> processedTypes) |
| throws CoreException { |
| |
| processedTypes.add(focusType); |
| |
| // Build superclasses hieararchy: |
| String[] superClasses = focusType.getSuperClasses(); |
| if (superClasses != null && superClasses.length > 0) { |
| IType[] searchTypes = searchTypes(superClasses, superTypesCache, |
| hierarchyInfo); |
| |
| for (int i = 0; i < searchTypes.length; i++) { |
| IType superclass = searchTypes[i]; |
| hierarchyBuilder.hierarchy.cacheSuperclass(focusType, |
| superclass); |
| } |
| |
| for (int i = 0; i < searchTypes.length; i++) { |
| IType superclass = searchTypes[i]; |
| if (!processedTypes.contains(superclass)) { |
| computeSupertypesFor(superclass, superTypesCache, |
| hierarchyInfo, processedTypes); |
| } |
| } |
| } else { |
| if (!hierarchyBuilder.hierarchy.contains(focusType)) { |
| hierarchyBuilder.hierarchy.addRootClass(focusType); |
| } |
| } |
| } |
| |
| protected IType[] searchTypes(String[] typeNames, |
| Map<String, IType[]> cache, IFileHierarchyInfo hierarchyInfo) |
| throws CoreException { |
| List<IType> result = new LinkedList<>(); |
| for (int i = 0; i < typeNames.length; i++) { |
| String typeName = typeNames[i]; |
| result.addAll( |
| Arrays.asList(searchTypes(typeName, cache, hierarchyInfo))); |
| } |
| return result.toArray(new IType[result.size()]); |
| } |
| |
| protected IType[] searchTypes(String type, IFileHierarchyInfo hierarchyInfo) |
| throws CoreException { |
| return searchTypes(type, null, hierarchyInfo); |
| } |
| |
| protected IType[] searchTypes(final String typeName, |
| Map<String, IType[]> cache, final IFileHierarchyInfo hierarchyInfo) |
| throws CoreException { |
| if (cache != null && cache.containsKey(typeName)) { |
| return cache.get(typeName); |
| } |
| |
| final List<IType> result = new LinkedList<>(); |
| final List<IType> filteredTypes = new LinkedList<>(); |
| |
| IType[] types = findTypes(typeName, hierarchyBuilder.hierarchy.scope); |
| for (IType type : types) { |
| String delimiter = getDelimiterReplacementString(type); |
| String qualifiedName = type.getTypeQualifiedName(delimiter); |
| if (!typeName.equalsIgnoreCase(qualifiedName)) { |
| continue; |
| } |
| |
| if (hierarchyInfo != null |
| && !hierarchyInfo.exists(type.getSourceModule())) { |
| filteredTypes.add(type); |
| } else { |
| result.add(type); |
| } |
| } |
| |
| // If all results where filtered that means we could find a path to any |
| // of elements. |
| // In this case return all elements. |
| if (result.isEmpty()) { |
| result.addAll(filteredTypes); |
| } |
| |
| types = result.toArray(new IType[result.size()]); |
| if (cache != null) { |
| cache.put(typeName, types); |
| } |
| return types; |
| } |
| |
| public void resolve(Openable[] openables, HashSet<String> localTypes) { |
| try { |
| resolve(true); |
| } catch (CoreException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| private static IFileHierarchyResolver createFileHierarchyResolver( |
| IType type) throws CoreException { |
| IFileHierarchyResolver fileHierarchyResolver = null; |
| IDLTKLanguageToolkit toolkit = DLTKLanguageManager |
| .getLanguageToolkit(type); |
| if (toolkit != null) { |
| fileHierarchyResolver = DLTKLanguageManager |
| .getFileHierarchyResolver(toolkit.getNatureId()); |
| } |
| return fileHierarchyResolver; |
| } |
| |
| private static ISearchPatternProcessor getSearchPatternProcessor( |
| IType type) { |
| return DLTKLanguageManager.getSearchPatternProcessor( |
| DLTKLanguageManager.getLanguageToolkit(type)); |
| } |
| |
| protected String getDelimiterReplacementString(IType type) { |
| ISearchPatternProcessor searchPatternProcessor = getSearchPatternProcessor( |
| type); |
| if (searchPatternProcessor != null) { |
| return searchPatternProcessor.getDelimiterReplacementString(); |
| } |
| return "::"; // $NON-NLS-N$ |
| } |
| } |