blob: 48267d9da88351165bd252d4e3b13cc86d737747 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 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.search;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.dltk.compiler.env.IDependent;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IMember;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IModelElementDelta;
import org.eclipse.dltk.core.IProjectFragment;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.IType;
import org.eclipse.dltk.core.ITypeHierarchy;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.WorkingCopyOwner;
import org.eclipse.dltk.core.environment.IFileHandle;
import org.eclipse.dltk.internal.core.ArchiveProjectFragment;
import org.eclipse.dltk.internal.core.ExternalProjectFragment;
import org.eclipse.dltk.internal.core.Model;
import org.eclipse.dltk.internal.core.ModelElement;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.ScriptProject;
import org.eclipse.dltk.internal.core.hierarchy.TypeHierarchy;
/**
* Scope limited to the subtype and supertype hierarchy of a given type.
*/
public class HierarchyScope extends DLTKSearchScope {
public IType focusType;
protected String focusPath;
protected WorkingCopyOwner owner;
protected ITypeHierarchy hierarchy;
protected IType[] types;
protected HashSet resourcePaths;
protected IPath[] enclosingProjectsAndZips;
protected IResource[] elements;
protected int elementCount;
public boolean needsRefresh;
/*
* (non-Javadoc) Adds the given resource to this search scope.
*/
public void add(IResource element) {
if (this.elementCount == this.elements.length) {
System.arraycopy(this.elements, 0,
this.elements = new IResource[this.elementCount * 2], 0,
this.elementCount);
}
elements[elementCount++] = element;
IModelElement modelElement = DLTKCore.create(element);
try {
add(modelElement);
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
/*
* (non-Javadoc) Creates a new hiearchy scope for the given type.
*/
public HierarchyScope(IDLTKLanguageToolkit languageToolkit, IType type,
WorkingCopyOwner owner) throws ModelException {
super(languageToolkit);
this.focusType = type;
this.owner = owner;
this.enclosingProjectsAndZips = this.computeProjectsAndZips(type);
// resource path
IProjectFragment root = (IProjectFragment) type.getScriptFolder()
.getParent();
if (root.isArchive()) {
IPath jarPath = root.getPath();
Object target = Model.getTarget(ResourcesPlugin.getWorkspace()
.getRoot(), jarPath, true);
String zipFileName;
if (target instanceof IFile) {
// internal jar
zipFileName = jarPath.toString();
} else if (target instanceof IFileHandle) {
// external jar
// TODO Check this
zipFileName = ((IFileHandle) target).toOSString();
} else {
return; // unknown target
}
this.focusPath = zipFileName
+ IDependent.ARCHIVE_FILE_ENTRY_SEPARATOR
+ type.getFullyQualifiedName().replace('.', '/')
/* + SUFFIX_STRING_class */;
} else {
this.focusPath = type.getPath().toString();
}
this.needsRefresh = true;
add((ScriptProject) type.getScriptProject(),
DLTKSearchScope.APPLICATION_LIBRARIES | DLTKSearchScope.SOURCES
| DLTKSearchScope.SYSTEM_LIBRARIES
| DLTKSearchScope.REFERENCED_PROJECTS, new HashSet());
// disabled for now as this could be expensive
// JavaModelManager.getJavaModelManager().rememberScope(this);
}
private void buildResourceVector() {
HashMap resources = new HashMap();
HashMap paths = new HashMap();
this.types = this.hierarchy.getAllTypes();
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
for (int i = 0; i < this.types.length; i++) {
IType type = this.types[i];
IResource resource = type.getResource();
if (resource != null && resources.get(resource) == null) {
resources.put(resource, resource);
add(resource);
}
IProjectFragment root = (IProjectFragment) type.getScriptFolder()
.getParent();
if (root instanceof ArchiveProjectFragment) {
// type in a jar
ArchiveProjectFragment jar = (ArchiveProjectFragment) root;
IPath jarPath = jar.getPath();
Object target = Model.getTarget(workspaceRoot, jarPath, true);
String zipFileName;
if (target instanceof IFile) {
// internal jar
zipFileName = jarPath.toString();
} else if (target instanceof IFileHandle) {
// external jar
// TODO Check this
zipFileName = ((IFileHandle) target).toOSString();
} else {
continue; // unknown target
}
String resourcePath = zipFileName
+ IDependent.ARCHIVE_FILE_ENTRY_SEPARATOR
+ type.getFullyQualifiedName().replace('.', '/')
/* + SUFFIX_STRING_class */;
this.resourcePaths.add(resourcePath);
paths.put(jarPath, type);
} else if (root instanceof ExternalProjectFragment) {
paths.put(root.getPath(), type);
} else {
// type is a project
paths.put(type.getScriptProject().getProject().getFullPath(),
type);
}
}
this.enclosingProjectsAndZips = new IPath[paths.size()];
int i = 0;
for (Iterator iter = paths.keySet().iterator(); iter.hasNext();) {
this.enclosingProjectsAndZips[i++] = (IPath) iter.next();
}
}
/*
* Computes the paths of projects and jars that the hierarchy on the given
* type could contain. This is a super set of the project and jar paths once
* the hierarchy is computed.
*/
private IPath[] computeProjectsAndZips(IType type) throws ModelException {
HashSet set = new HashSet();
IProjectFragment root = (IProjectFragment) type.getScriptFolder()
.getParent();
if (root.isArchive()) {
// add the root
set.add(root.getPath());
// add all projects that reference this archive and their dependents
IPath rootPath = root.getPath();
Model model = ModelManager.getModelManager().getModel();
IScriptProject[] projects = model.getScriptProjects();
HashSet visited = new HashSet();
for (int i = 0; i < projects.length; i++) {
IScriptProject project = projects[i];
IBuildpathEntry entry = ((ScriptProject) project)
.getBuildpathEntryFor(rootPath);
if (entry != null) {
// add the project and its binary pkg fragment roots
IProjectFragment[] roots = project.getAllProjectFragments();
set.add(project.getPath());
for (int j = 0; j < roots.length; j++) {
IProjectFragment pkgFragmentRoot = roots[j];
if (pkgFragmentRoot.getKind() == IProjectFragment.K_BINARY) {
set.add(pkgFragmentRoot.getPath());
}
}
// add the dependent projects
this.computeDependents(project, set, visited);
}
}
} else {
// add all the project's pkg fragment roots
IScriptProject project = (IScriptProject) root.getParent();
IProjectFragment[] roots = project.getProjectFragments();
for (int j = 0; j < roots.length; j++) {
IProjectFragment pkgFragmentRoot = roots[j];
if (pkgFragmentRoot.getKind() == IProjectFragment.K_BINARY) {
set.add(pkgFragmentRoot.getPath());
} else {
set.add(pkgFragmentRoot.getParent().getPath());
}
}
// add the dependent projects
this.computeDependents(project, set, new HashSet());
}
IPath[] result = new IPath[set.size()];
set.toArray(result);
return result;
}
private void computeDependents(IScriptProject project, HashSet set,
HashSet visited) {
if (visited.contains(project)) {
return;
}
visited.add(project);
IProject[] dependents = project.getProject().getReferencingProjects();
for (int i = 0; i < dependents.length; i++) {
IProject dependent2 = dependents[i];
try {
IScriptProject dependent = DLTKCore.create(dependent2);
IProjectFragment[] roots = dependent.getProjectFragments();
set.add(dependent.getPath());
for (int j = 0; j < roots.length; j++) {
IProjectFragment pkgFragmentRoot = roots[j];
if (pkgFragmentRoot.isArchive()) {
set.add(pkgFragmentRoot.getPath());
}
}
this.computeDependents(dependent, set, visited);
} catch (ModelException e) {
// project is not a java project
}
}
}
@Override
public boolean encloses(String resourcePath) {
if (this.hierarchy == null) {
if (resourcePath.equals(this.focusPath)) {
return true;
} else {
if (this.needsRefresh) {
try {
this.initialize();
} catch (ModelException e) {
return false;
}
} else {
// the scope is used only to find enclosing projects and
// jars
// clients is responsible for filtering out elements not in
// the hierarchy (see SearchEngine)
return true;
}
}
}
if (this.needsRefresh) {
try {
this.refresh();
} catch (ModelException e) {
return false;
}
}
int separatorIndex = resourcePath
.indexOf(IDependent.ARCHIVE_FILE_ENTRY_SEPARATOR);
if (separatorIndex != -1) {
return this.resourcePaths.contains(resourcePath);
} else {
for (int i = 0; i < this.elementCount; i++) {
if (resourcePath.startsWith(this.elements[i].getFullPath()
.toString())) {
return true;
}
}
}
return false;
}
@Override
public boolean encloses(IModelElement element) {
if (this.hierarchy == null) {
if (this.focusType.equals(element.getAncestor(IModelElement.TYPE))) {
return true;
} else {
if (this.needsRefresh) {
try {
this.initialize();
} catch (ModelException e) {
return false;
}
} else {
// the scope is used only to find enclosing projects and
// jars
// clients is responsible for filtering out elements not in
// the hierarchy (see SearchEngine)
return true;
}
}
}
if (this.needsRefresh) {
try {
this.refresh();
} catch (ModelException e) {
return false;
}
}
IType type = null;
if (element instanceof IType) {
type = (IType) element;
} else if (element instanceof IMember) {
type = ((IMember) element).getDeclaringType();
}
if (type != null) {
if (this.hierarchy.contains(type)) {
return true;
} else {
// be flexible: look at original element (see bug 14106
// Declarations in Hierarchy does not find declarations in
// hierarchy)
IType original;
if (/*
* !type.isBinary() &&
*/(original = (IType) type.getPrimaryElement()) != null) {
return this.hierarchy.contains(original);
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see IJavaSearchScope#enclosingProjectsAndZips()
*
* @deprecated
*/
@Override
public IPath[] enclosingProjectsAndZips() {
if (this.needsRefresh) {
try {
this.refresh();
} catch (ModelException e) {
return new IPath[0];
}
}
return this.enclosingProjectsAndZips;
}
/**
* This implementation creates a full (supertype and subtype) hierarchy for
* the given focus type.
*
* @param owner
* the owner of working copies that take precedence over their
* original compilation units
* @param monitor
* the given progress monitor
* @return a type hierarchy for this type containing this type, all of its
* supertypes, and all its subtypes in the workspace
* @throws ModelException
*/
protected ITypeHierarchy createHierarchy(IType focusType,
WorkingCopyOwner owner, IProgressMonitor monitor)
throws ModelException {
return focusType.newTypeHierarchy(owner, monitor);
}
protected void initialize() throws ModelException {
this.resourcePaths = new HashSet();
this.elements = new IResource[5];
this.elementCount = 0;
this.needsRefresh = false;
if (this.hierarchy == null) {
this.hierarchy = createHierarchy(this.focusType, this.owner, null);
} else {
this.hierarchy.refresh(null);
}
this.buildResourceVector();
}
/*
* @see AbstractSearchScope#processDelta(IModelElementDelta)
*/
public void processDelta(IModelElementDelta delta, int eventType) {
if (this.needsRefresh) {
return;
}
this.needsRefresh = this.hierarchy == null ? false
: ((TypeHierarchy) this.hierarchy).isAffected(delta/*
* ,
* eventType
*/);
}
protected void refresh() throws ModelException {
// if (this.hierarchy != null) {
this.initialize();
// }
}
@Override
public String toString() {
return "HierarchyScope on " + ((ModelElement) this.focusType).toStringWithAncestors(); //$NON-NLS-1$
}
}