blob: b2ab4c687190413e829fe5231326f31ba10c293f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.search;
import java.util.ArrayList;
import java.util.HashSet;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.util.Util;
/**
* A Java-specific scope for searching relative to one or more java elements.
*/
public class JavaSearchScope extends AbstractSearchScope {
private ArrayList elements;
/* The paths of the resources in this search scope
(or the classpath entries' paths
if the resources are projects) */
private IPath[] paths;
private boolean[] pathWithSubFolders;
private AccessRestriction[] pathRestrictions;
private int pathsCount;
private IPath[] enclosingProjectsAndJars;
public final static AccessRestriction UNINIT_RESTRICTION = new AccessRestriction(null, null, null, null);
public JavaSearchScope() {
this.initialize();
//disabled for now as this could be expensive
//JavaModelManager.getJavaModelManager().rememberScope(this);
}
private void addEnclosingProjectOrJar(IPath path) {
int length = this.enclosingProjectsAndJars.length;
for (int i = 0; i < length; i++) {
if (this.enclosingProjectsAndJars[i].equals(path)) return;
}
System.arraycopy(
this.enclosingProjectsAndJars,
0,
this.enclosingProjectsAndJars = new IPath[length+1],
0,
length);
this.enclosingProjectsAndJars[length] = path;
}
/**
* Add java project all fragment roots to current java search scope.
* @see #add(JavaProject, IPath, int, HashSet, IClasspathEntry)
*/
public void add(JavaProject project, int includeMask, HashSet visitedProject) throws JavaModelException {
add(project, null, includeMask, visitedProject, null);
}
/**
* Add a path to current java search scope or all project fragment roots if null.
* Use project resolved classpath to retrieve and store access restriction on each classpath entry.
* Recurse if dependent projects are found.
* @param javaProject Project used to get resolved classpath entries
* @param pathToAdd Path to add in case of single element or null if user want to add all project package fragment roots
* @param includeMask Mask to apply on classpath entries
* @param visitedProjects Set to avoid infinite recursion
* @param referringEntry Project raw entry in referring project classpath
* @throws JavaModelException May happen while getting java model info
*/
void add(JavaProject javaProject, IPath pathToAdd, int includeMask, HashSet visitedProjects, IClasspathEntry referringEntry) throws JavaModelException {
IProject project = javaProject.getProject();
if (!project.isAccessible() || !visitedProjects.add(project)) return;
this.addEnclosingProjectOrJar(project.getFullPath());
IClasspathEntry[] entries = javaProject.getResolvedClasspath(true/*ignoreUnresolvedEntry*/, false/*don't generateMarkerOnError*/, false/*don't returnResolutionInProgress*/);
IJavaModel model = javaProject.getJavaModel();
JavaModelManager.PerProjectInfo perProjectInfo = javaProject.getPerProjectInfo();
for (int i = 0, length = entries.length; i < length; i++) {
IClasspathEntry entry = entries[i];
IClasspathEntry rawEntry = null;
if (perProjectInfo != null && perProjectInfo.resolvedPathToRawEntries != null) {
rawEntry = (IClasspathEntry) perProjectInfo.resolvedPathToRawEntries.get(entry.getPath());
}
if (rawEntry == null) continue;
AccessRestriction access = null;
ClasspathEntry cpEntry = null;
if (rawEntry instanceof ClasspathEntry) {
cpEntry = (ClasspathEntry) rawEntry;
if (referringEntry != null) {
cpEntry = cpEntry.combineWith(referringEntry);
// cpEntry = ((ClasspathEntry)referringEntry).combineWith(cpEntry);
}
access = cpEntry.getImportRestriction();
}
switch (entry.getEntryKind()) {
case IClasspathEntry.CPE_LIBRARY:
switch (rawEntry.getEntryKind()) {
case IClasspathEntry.CPE_LIBRARY:
case IClasspathEntry.CPE_VARIABLE:
if ((includeMask & APPLICATION_LIBRARIES) != 0) {
IPath path = entry.getPath();
if (pathToAdd == null || pathToAdd.equals(path)) {
add(path, true, access);
addEnclosingProjectOrJar(path);
}
}
break;
case IClasspathEntry.CPE_CONTAINER:
IClasspathContainer container = JavaCore.getClasspathContainer(rawEntry.getPath(), javaProject);
if (container == null) break;
if ((container.getKind() == IClasspathContainer.K_APPLICATION && (includeMask & APPLICATION_LIBRARIES) != 0)
|| (includeMask & SYSTEM_LIBRARIES) != 0) {
IPath path = entry.getPath();
if (pathToAdd == null || pathToAdd.equals(path)) {
add(path, true, access);
addEnclosingProjectOrJar(path);
}
}
break;
}
break;
case IClasspathEntry.CPE_PROJECT:
if ((includeMask & REFERENCED_PROJECTS) != 0) {
IPath path = entry.getPath();
if (pathToAdd == null || pathToAdd.equals(path)) {
add((JavaProject) model.getJavaProject(entry.getPath().lastSegment()), null, includeMask, visitedProjects, cpEntry);
}
}
break;
case IClasspathEntry.CPE_SOURCE:
if ((includeMask & SOURCES) != 0) {
IPath path = entry.getPath();
if (pathToAdd == null || pathToAdd.equals(path)) {
add(entry.getPath(), true, access);
}
}
break;
}
}
}
/**
* Add an element to the java search scope. use element project to retrieve and
* store access restriction corresponding to the provided element.
* @param element The element we want to add to current java search scope
* @throws JavaModelException May happen if some Java Model info are not available
*/
public void add(IJavaElement element) throws JavaModelException {
// add(element, element.getJavaProject());
add(element, null);
}
/**
* Add an element to the java search scope. If project is not null, then use it to
* retrieve and store access restriction corresponding to the provided element.
* @param element The element we want to add to current java search scope
* @throws JavaModelException May happen if some Java Model info are not available
*/
public void add(IJavaElement element, IJavaProject project) throws JavaModelException {
IPackageFragmentRoot root = null;
int includeMask = SOURCES | APPLICATION_LIBRARIES | SYSTEM_LIBRARIES;
switch (element.getElementType()) {
case IJavaElement.JAVA_MODEL:
// a workspace sope should be used
break;
case IJavaElement.JAVA_PROJECT:
if (project == null)
add((JavaProject)element, null, includeMask, new HashSet(2), null);
else
add((JavaProject)project, element.getPath(), includeMask, new HashSet(2), null);
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
root = (IPackageFragmentRoot)element;
if (project == null)
add(root.getPath(), true, null);
else
add((JavaProject)project, root.getPath(), includeMask, new HashSet(2), null);
break;
case IJavaElement.PACKAGE_FRAGMENT:
root = (IPackageFragmentRoot)element.getParent();
if (root.isArchive()) {
String relativePath = Util.concatWith(((PackageFragment) element).names, '/');
IPath path = root.getPath().append(new Path(relativePath));
if (project == null)
add(path, false, null);
else
add((JavaProject)project, path, includeMask, new HashSet(2), null);
} else {
IResource resource = element.getResource();
if (resource != null && resource.isAccessible()) {
if (project == null)
add(resource.getFullPath(), false, null);
else
add((JavaProject)project, resource.getFullPath(), includeMask, new HashSet(2), null);
}
}
break;
default:
// remember sub-cu (or sub-class file) java elements
if (element instanceof IMember) {
if (this.elements == null) {
this.elements = new ArrayList();
}
this.elements.add(element);
}
this.add(this.fullPath(element), true, null);
// find package fragment root including this java element
IJavaElement parent = element.getParent();
while (parent != null && !(parent instanceof IPackageFragmentRoot)) {
parent = parent.getParent();
}
if (parent instanceof IPackageFragmentRoot) {
root = (IPackageFragmentRoot)parent;
}
}
if (root != null) {
if (root.getKind() == IPackageFragmentRoot.K_BINARY) {
this.addEnclosingProjectOrJar(root.getPath());
} else {
this.addEnclosingProjectOrJar(root.getJavaProject().getProject().getFullPath());
}
}
}
/**
* Adds the given path to this search scope. Remember if subfolders need to be included
* and associated access restriction as well.
*/
private void add(IPath path, boolean withSubFolders, AccessRestriction access) {
if (this.paths.length == this.pathsCount) {
System.arraycopy(
this.paths,
0,
this.paths = new IPath[this.pathsCount * 2],
0,
this.pathsCount);
System.arraycopy(
this.pathWithSubFolders,
0,
this.pathWithSubFolders = new boolean[this.pathsCount * 2],
0,
this.pathsCount);
System.arraycopy(
this.pathRestrictions,
0,
this.pathRestrictions = new AccessRestriction[this.pathsCount * 2],
0,
this.pathsCount);
}
this.paths[this.pathsCount] = path;
this.pathWithSubFolders[this.pathsCount] = withSubFolders;
this.pathRestrictions[this.pathsCount++] = access;
}
/* (non-Javadoc)
* @see IJavaSearchScope#encloses(String)
*/
public boolean encloses(String resourcePathString) {
return this.encloses(fullPath(resourcePathString)) >= 0;
}
private IPath fullPath(String resourcePathString) {
IPath resourcePath;
int separatorIndex = resourcePathString.indexOf(JAR_FILE_ENTRY_SEPARATOR);
if (separatorIndex != -1) {
resourcePath =
new Path(resourcePathString.substring(0, separatorIndex)).
append(new Path(resourcePathString.substring(separatorIndex+1)));
} else {
resourcePath = new Path(resourcePathString);
}
return resourcePath;
}
/**
* Returns paths list index of given path or -1 if not found.
*/
private int encloses(IPath path) {
for (int i = 0; i < this.pathsCount; i++) {
if (this.pathWithSubFolders[i]) {
if (this.paths[i].isPrefixOf(path)) {
return i;
}
} else {
// if not looking at subfolders, this scope encloses the given path
// if this path is a direct child of the scope's ressource
// or if this path is the scope's resource (see bug 13919 Declaration for package not found if scope is not project)
IPath scopePath = this.paths[i];
if (scopePath.isPrefixOf(path)
&& ((scopePath.segmentCount() == path.segmentCount() - 1)
|| (scopePath.segmentCount() == path.segmentCount()))) {
return i;
}
}
}
return -1;
}
/* (non-Javadoc)
* @see IJavaSearchScope#encloses(IJavaElement)
*/
public boolean encloses(IJavaElement element) {
if (this.elements != null) {
for (int i = 0, length = this.elements.size(); i < length; i++) {
IJavaElement scopeElement = (IJavaElement)this.elements.get(i);
IJavaElement searchedElement = element;
while (searchedElement != null) {
if (searchedElement.equals(scopeElement))
return true;
searchedElement = searchedElement.getParent();
}
}
return false;
}
return this.encloses(this.fullPath(element)) >= 0;
}
/* (non-Javadoc)
* @see IJavaSearchScope#enclosingProjectsAndJars()
*/
public IPath[] enclosingProjectsAndJars() {
return this.enclosingProjectsAndJars;
}
private IPath fullPath(IJavaElement element) {
if (element instanceof IPackageFragmentRoot) {
return ((IPackageFragmentRoot)element).getPath();
}
IJavaElement parent = element.getParent();
IPath parentPath = parent == null ? null : this.fullPath(parent);
IPath childPath;
if (element instanceof PackageFragment) {
String relativePath = Util.concatWith(((PackageFragment) element).names, '/');
childPath = new Path(relativePath);
} else if (element instanceof IOpenable) {
childPath = new Path(element.getElementName());
} else {
return parentPath;
}
return parentPath == null ? childPath : parentPath.append(childPath);
}
/**
* Get access restriction corresponding to a given path.
* @param path The path user want to have restriction access
* @return The access restriction for given path or null if none is set for it.
* Returns specific uninit restriction when scope does not enclose the given path.
*/
public AccessRestriction getAccessRestriction(String path) {
int index = encloses(fullPath(path));
if (index == -1) {
// this search scope does not enclose given path
return UNINIT_RESTRICTION;
}
return this.pathRestrictions[index];
}
protected void initialize() {
this.paths = new IPath[1];
this.pathWithSubFolders = new boolean[1];
this.pathRestrictions = new AccessRestriction[1];
this.pathsCount = 0;
this.enclosingProjectsAndJars = new IPath[0];
}
/*
* @see AbstractSearchScope#processDelta(IJavaElementDelta)
*/
public void processDelta(IJavaElementDelta delta) {
switch (delta.getKind()) {
case IJavaElementDelta.CHANGED:
IJavaElementDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
IJavaElementDelta child = children[i];
this.processDelta(child);
}
break;
case IJavaElementDelta.REMOVED:
IJavaElement element = delta.getElement();
if (this.encloses(element)) {
if (this.elements != null) {
this.elements.remove(element);
}
IPath path = null;
switch (element.getElementType()) {
case IJavaElement.JAVA_PROJECT:
path = ((IJavaProject)element).getProject().getFullPath();
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
if (path == null) {
path = ((IPackageFragmentRoot)element).getPath();
}
int toRemove = -1;
for (int i = 0; i < this.pathsCount; i++) {
if (this.paths[i].equals(path)) {
toRemove = i;
break;
}
}
if (toRemove != -1) {
int last = this.pathsCount-1;
if (toRemove != last) {
this.paths[toRemove] = this.paths[last];
this.pathWithSubFolders[toRemove] = this.pathWithSubFolders[last];
}
this.pathsCount--;
}
}
}
break;
}
}
public String toString() {
StringBuffer result = new StringBuffer("JavaSearchScope on "); //$NON-NLS-1$
if (this.elements != null) {
result.append("["); //$NON-NLS-1$
for (int i = 0, length = this.elements.size(); i < length; i++) {
JavaElement element = (JavaElement)this.elements.get(i);
result.append("\n\t"); //$NON-NLS-1$
result.append(element.toStringWithAncestors());
}
result.append("\n]"); //$NON-NLS-1$
} else {
if (this.pathsCount == 0) {
result.append("[empty scope]"); //$NON-NLS-1$
} else {
result.append("["); //$NON-NLS-1$
for (int i = 0; i < this.pathsCount; i++) {
IPath path = this.paths[i];
result.append("\n\t"); //$NON-NLS-1$
result.append(path.toString());
}
result.append("\n]"); //$NON-NLS-1$
}
}
return result.toString();
}
}