blob: 8949928290ad0dbaf5ffa4daea12a333b87c61ff [file] [log] [blame]
/*******************************************************************************
* 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$
}
}