org.eclipse.jdt.internal.core.search.JavaSearchScope.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.core.search.JavaSearchScope.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.core.search;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;

import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathContainer;
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.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.jdt.internal.core.ExternalFoldersManager;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.JavaModel;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jdt.internal.core.PackageFragmentRoot;
import org.eclipse.jdt.internal.core.util.Util;

/**
 * A Java-specific scope for searching relative to one or more java elements.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class JavaSearchScope extends AbstractJavaSearchScope {

    private ArrayList elements;

    /* The paths of the resources in this search scope
        (or the classpath entries' paths if the resources are projects)
    */
    private ArrayList projectPaths = new ArrayList(); // container paths projects
    private int[] projectIndexes; // Indexes of projects in list
    private String[] containerPaths; // path to the container (e.g. /P/src, /P/lib.jar, c:\temp\mylib.jar)
    private String[] relativePaths; // path relative to the container (e.g. x/y/Z.class, x/y, (empty))
    private boolean[] isPkgPath; // in the case of packages, matches must be direct children of the folder
    protected AccessRuleSet[] pathRestrictions;
    private int pathsCount;
    private int threshold;

    private IPath[] enclosingProjectsAndJars;
    public final static AccessRuleSet NOT_ENCLOSED = new AccessRuleSet(null, (byte) 0, null);

    private final boolean excludeTestCode;

    public JavaSearchScope(boolean excludeTestCode) {
        this(5, excludeTestCode);
    }

    private JavaSearchScope(int size, boolean excludeTestCode) {
        this.excludeTestCode = excludeTestCode;
        initialize(size);

        //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, HashSet, IClasspathEntry)
     */
    public void add(JavaProject project, int includeMask, HashSet projectsToBeAdded) throws JavaModelException {
        add(project, null, includeMask, projectsToBeAdded, new HashSet(2), 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 projectsToBeAdded Set to avoid infinite recursion
     * @param visitedProjects Set to avoid adding twice the same project
     * @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 projectsToBeAdded,
            HashSet visitedProjects, IClasspathEntry referringEntry) throws JavaModelException {
        IProject project = javaProject.getProject();
        if (!project.isAccessible() || !visitedProjects.add(project))
            return;

        IPath projectPath = project.getFullPath();
        String projectPathString = projectPath.toString();
        addEnclosingProjectOrJar(projectPath);

        IClasspathEntry[] entries = javaProject.getResolvedClasspath();
        IJavaModel model = javaProject.getJavaModel();
        JavaModelManager.PerProjectInfo perProjectInfo = javaProject.getPerProjectInfo();
        for (int i = 0, length = entries.length; i < length; i++) {
            IClasspathEntry entry = entries[i];
            if (this.excludeTestCode && entry.isTest()) {
                continue;
            }
            AccessRuleSet access = null;
            ClasspathEntry cpEntry = (ClasspathEntry) entry;
            if (referringEntry != null) {
                // Add only exported entries.
                // Source folder are implicitly exported.
                if (!entry.isExported() && entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
                    continue;
                }
                cpEntry = cpEntry.combineWith((ClasspathEntry) referringEntry);
                //            cpEntry = ((ClasspathEntry)referringEntry).combineWith(cpEntry);
            }
            access = cpEntry.getAccessRuleSet();
            switch (entry.getEntryKind()) {
            case IClasspathEntry.CPE_LIBRARY:
                IClasspathEntry rawEntry = null;
                Map rootPathToRawEntries = perProjectInfo.rootPathToRawEntries;
                if (rootPathToRawEntries != null) {
                    rawEntry = (IClasspathEntry) rootPathToRawEntries.get(entry.getPath());
                }
                if (rawEntry == null)
                    break;
                rawKind: 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)) {
                            Object target = JavaModel.getTarget(path, false/*don't check existence*/);
                            if (target instanceof IFolder) // case of an external folder
                                path = ((IFolder) target).getFullPath();
                            String pathToString = path.getDevice() == null ? path.toString() : path.toOSString();
                            add(projectPath.toString(), "", pathToString, false/*not a package*/, access); //$NON-NLS-1$
                            addEnclosingProjectOrJar(entry.getPath());
                        }
                    }
                    break;
                case IClasspathEntry.CPE_CONTAINER:
                    IClasspathContainer container = JavaCore.getClasspathContainer(rawEntry.getPath(), javaProject);
                    if (container == null)
                        break;
                    switch (container.getKind()) {
                    case IClasspathContainer.K_APPLICATION:
                        if ((includeMask & APPLICATION_LIBRARIES) == 0)
                            break rawKind;
                        break;
                    case IClasspathContainer.K_SYSTEM:
                    case IClasspathContainer.K_DEFAULT_SYSTEM:
                        if ((includeMask & SYSTEM_LIBRARIES) == 0)
                            break rawKind;
                        break;
                    default:
                        break rawKind;
                    }
                    IPath path = entry.getPath();
                    if (pathToAdd == null || pathToAdd.equals(path)) {
                        Object target = JavaModel.getTarget(path, false/*don't check existence*/);
                        if (target instanceof IFolder) // case of an external folder
                            path = ((IFolder) target).getFullPath();
                        String pathToString = path.getDevice() == null ? path.toString() : path.toOSString();
                        add(projectPath.toString(), "", pathToString, false/*not a package*/, access); //$NON-NLS-1$
                        addEnclosingProjectOrJar(entry.getPath());
                    }
                    break;
                }
                break;
            case IClasspathEntry.CPE_PROJECT:
                if ((includeMask & REFERENCED_PROJECTS) != 0) {
                    IPath path = entry.getPath();
                    if (pathToAdd == null || pathToAdd.equals(path)) {
                        JavaProject referencedProject = (JavaProject) model.getJavaProject(path.lastSegment());
                        if (!projectsToBeAdded.contains(referencedProject)) { // do not recurse if depending project was used to create the scope
                            add(referencedProject, null, includeMask, projectsToBeAdded, visitedProjects, cpEntry);
                        }
                    }
                }
                break;
            case IClasspathEntry.CPE_SOURCE:
                if ((includeMask & SOURCES) != 0) {
                    IPath path = entry.getPath();
                    if (pathToAdd == null || pathToAdd.equals(path)) {
                        add(projectPath.toString(), Util.relativePath(path, 1/*remove project segment*/),
                                projectPathString, false/*not a package*/, access);
                    }
                }
                break;
            }
        }
    }

    /**
     * Add an element to the java search scope.
     * @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 {
        IPath containerPath = null;
        String containerPathToString = null;
        PackageFragmentRoot 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:
            add((JavaProject) element, null, includeMask, new HashSet(2), new HashSet(2), null);
            break;
        case IJavaElement.PACKAGE_FRAGMENT_ROOT:
            root = (PackageFragmentRoot) element;
            IPath rootPath = root.internalPath();
            containerPath = root.getKind() == IPackageFragmentRoot.K_SOURCE ? root.getParent().getPath() : rootPath;
            containerPathToString = containerPath.getDevice() == null ? containerPath.toString()
                    : containerPath.toOSString();
            IResource rootResource = root.resource();
            String projectPath = root.getJavaProject().getPath().toString();
            if (rootResource != null && rootResource.isAccessible()) {
                String relativePath = Util.relativePath(rootResource.getFullPath(), containerPath.segmentCount());
                add(projectPath, relativePath, containerPathToString, false/*not a package*/, null);
            } else {
                add(projectPath, "", containerPathToString, false/*not a package*/, null); //$NON-NLS-1$
            }
            break;
        case IJavaElement.PACKAGE_FRAGMENT:
            root = (PackageFragmentRoot) element.getParent();
            projectPath = root.getJavaProject().getPath().toString();
            if (root.isArchive()) {
                String relativePath = Util.concatWith(((PackageFragment) element).names, '/');
                containerPath = root.getPath();
                containerPathToString = containerPath.getDevice() == null ? containerPath.toString()
                        : containerPath.toOSString();
                add(projectPath, relativePath, containerPathToString, true/*package*/, null);
            } else {
                IResource resource = ((JavaElement) element).resource();
                if (resource != null) {
                    if (resource.isAccessible()) {
                        containerPath = root.getKind() == IPackageFragmentRoot.K_SOURCE ? root.getParent().getPath()
                                : root.internalPath();
                    } else {
                        // for working copies, get resource container full path
                        containerPath = resource.getParent().getFullPath();
                    }
                    containerPathToString = containerPath.getDevice() == null ? containerPath.toString()
                            : containerPath.toOSString();
                    String relativePath = Util.relativePath(resource.getFullPath(), containerPath.segmentCount());
                    add(projectPath, relativePath, containerPathToString, true/*package*/, 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);
            }
            root = (PackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
            projectPath = root.getJavaProject().getPath().toString();
            String relativePath;
            if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
                containerPath = root.getParent().getPath();
                relativePath = Util.relativePath(getPath(element, false/*full path*/), 1/*remove project segment*/);
            } else {
                containerPath = root.internalPath();
                relativePath = getPath(element, true/*relative path*/).toString();
            }
            containerPathToString = containerPath.getDevice() == null ? containerPath.toString()
                    : containerPath.toOSString();
            add(projectPath, relativePath, containerPathToString, false/*not a package*/, null);
        }

        if (root != null)
            addEnclosingProjectOrJar(
                    root.getKind() == IPackageFragmentRoot.K_SOURCE ? root.getParent().getPath() : root.getPath());
    }

    /**
     * Adds the given path to this search scope. Remember if subfolders need to be included
     * and associated access restriction as well.
     */
    private void add(String projectPath, String relativePath, String containerPath, boolean isPackage,
            AccessRuleSet access) {
        // normalize containerPath and relativePath
        containerPath = normalize(containerPath);
        relativePath = normalize(relativePath);
        int length = this.containerPaths.length, index = (containerPath.hashCode() & 0x7FFFFFFF) % length;
        String currentRelativePath, currentContainerPath;
        while ((currentRelativePath = this.relativePaths[index]) != null
                && (currentContainerPath = this.containerPaths[index]) != null) {
            if (currentRelativePath.equals(relativePath) && currentContainerPath.equals(containerPath))
                return;
            if (++index == length) {
                index = 0;
            }
        }
        int idx = this.projectPaths.indexOf(projectPath);
        if (idx == -1) {
            // store project in separated list to minimize memory footprint
            this.projectPaths.add(projectPath);
            idx = this.projectPaths.indexOf(projectPath);
        }
        this.projectIndexes[index] = idx;
        this.relativePaths[index] = relativePath;
        this.containerPaths[index] = containerPath;
        this.isPkgPath[index] = isPackage;
        if (this.pathRestrictions != null)
            this.pathRestrictions[index] = access;
        else if (access != null) {
            this.pathRestrictions = new AccessRuleSet[this.relativePaths.length];
            this.pathRestrictions[index] = access;
        }

        // assumes the threshold is never equal to the size of the table
        if (++this.pathsCount > this.threshold)
            rehash();
    }

    /*
     * E.g.
     *
     * 1. /P/src/pkg/X.java
     * 2. /P/src/pkg
     * 3. /P/lib.jar|org/eclipse/jdt/core/IJavaElement.class
     * 4. /home/mylib.jar|x/y/z/X.class
     * 5. c:\temp\mylib.jar|x/y/Y.class
     *
     * @see IJavaSearchScope#encloses(String)
     */
    @Override
    public boolean encloses(String resourcePathString) {
        int separatorIndex = resourcePathString.indexOf(JAR_FILE_ENTRY_SEPARATOR);
        if (separatorIndex != -1) {
            // internal or external jar (case 3, 4, or 5)
            String jarPath = resourcePathString.substring(0, separatorIndex);
            String relativePath = resourcePathString.substring(separatorIndex + 1);
            return indexOf(jarPath, relativePath) >= 0;
        }
        // resource in workspace (case 1 or 2)
        return indexOf(resourcePathString) >= 0;
    }

    /**
     * Returns paths list index of given path or -1 if not found.
     * NOTE: Use indexOf(String, String) for path inside jars
     *
     * @param fullPath the full path of the resource, e.g.
     *   1. /P/src/pkg/X.java
     *   2. /P/src/pkg
     */
    private int indexOf(String fullPath) {
        // cannot guess the index of the container path
        // fallback to sequentially looking at all known paths
        for (int i = 0, length = this.relativePaths.length; i < length; i++) {
            String currentRelativePath = this.relativePaths[i];
            if (currentRelativePath == null)
                continue;
            String currentContainerPath = this.containerPaths[i];
            String currentFullPath = currentRelativePath.length() == 0 ? currentContainerPath
                    : (currentContainerPath + '/' + currentRelativePath);
            if (encloses(currentFullPath, fullPath, i))
                return i;
        }
        return -1;
    }

    /**
     * Returns paths list index of given path or -1 if not found.
     * @param containerPath the path of the container, e.g.
     *   1. /P/src
     *   2. /P
     *   3. /P/lib.jar
     *   4. /home/mylib.jar
     *   5. c:\temp\mylib.jar
     * @param relativePath the forward slash path relatively to the container, e.g.
     *   1. x/y/Z.class
     *   2. x/y
     *   3. X.java
     *   4. (empty)
     */
    private int indexOf(String containerPath, String relativePath) {
        // use the hash to get faster comparison
        int length = this.containerPaths.length, index = (containerPath.hashCode() & 0x7FFFFFFF) % length;
        String currentContainerPath;
        while ((currentContainerPath = this.containerPaths[index]) != null) {
            if (currentContainerPath.equals(containerPath)) {
                String currentRelativePath = this.relativePaths[index];
                if (encloses(currentRelativePath, relativePath, index))
                    return index;
            }
            if (++index == length) {
                index = 0;
            }
        }
        return -1;
    }

    /*
     * Returns whether the enclosing path encloses the given path (or is equal to it)
     */
    private boolean encloses(String enclosingPath, String path, int index) {
        // normalize given path as it can come from outside
        path = normalize(path);

        int pathLength = path.length();
        int enclosingLength = enclosingPath.length();
        if (pathLength < enclosingLength) {
            return false;
        }
        if (enclosingLength == 0) {
            return true;
        }
        if (pathLength == enclosingLength) {
            return path.equals(enclosingPath);
        }
        if (!this.isPkgPath[index]) {
            return path.startsWith(enclosingPath) && path.charAt(enclosingLength) == '/';
        } else {
            // if looking at a package, this scope encloses the given path
            // if the given path is a direct child of the folder
            // or if the given path path is the folder path (see bug 13919 Declaration for package not found if scope is not project)
            if (path.startsWith(enclosingPath) && ((enclosingPath.length() == path.lastIndexOf('/'))
                    || (enclosingPath.length() == path.length()))) {
                return true;
            }
        }
        return false;
    }

    @Override
    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;
        }
        IPackageFragmentRoot root = (IPackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
        if (root != null && root.isArchive()) {
            // external or internal jar
            IPath rootPath = root.getPath();
            String rootPathToString = rootPath.getDevice() == null ? rootPath.toString() : rootPath.toOSString();
            IPath relativePath = getPath(element, true/*relative path*/);
            return indexOf(rootPathToString, relativePath.toString()) >= 0;
        }
        // resource in workspace
        String fullResourcePathString = getPath(element, false/*full path*/).toString();
        return indexOf(fullResourcePathString) >= 0;
    }

    @Override
    public IPath[] enclosingProjectsAndJars() {
        return this.enclosingProjectsAndJars;
    }

    private IPath getPath(IJavaElement element, boolean relativeToRoot) {
        switch (element.getElementType()) {
        case IJavaElement.JAVA_MODEL:
            return Path.EMPTY;
        case IJavaElement.JAVA_PROJECT:
            return element.getPath();
        case IJavaElement.PACKAGE_FRAGMENT_ROOT:
            if (relativeToRoot)
                return Path.EMPTY;
            return element.getPath();
        case IJavaElement.PACKAGE_FRAGMENT:
            String relativePath = Util.concatWith(((PackageFragment) element).names, '/');
            return getPath(element.getParent(), relativeToRoot).append(new Path(relativePath));
        case IJavaElement.COMPILATION_UNIT:
        case IJavaElement.CLASS_FILE:
            return getPath(element.getParent(), relativeToRoot).append(new Path(element.getElementName()));
        default:
            return getPath(element.getParent(), relativeToRoot);
        }
    }

    @Override
    public AccessRuleSet getAccessRuleSet(String relativePath, String containerPath) {
        int index = indexOf(containerPath, relativePath);
        if (index == -1) {
            // this search scope does not enclose given path
            return NOT_ENCLOSED;
        }
        if (this.pathRestrictions == null)
            return null;
        return this.pathRestrictions[index];
    }

    protected void initialize(int size) {
        this.pathsCount = 0;
        this.threshold = size; // size represents the expected number of elements
        int extraRoom = (int) (size * 1.75f);
        if (this.threshold == extraRoom)
            extraRoom++;
        this.relativePaths = new String[extraRoom];
        this.containerPaths = new String[extraRoom];
        this.projectPaths = new ArrayList();
        this.projectIndexes = new int[extraRoom];
        this.isPkgPath = new boolean[extraRoom];
        this.pathRestrictions = null; // null to optimize case where no access rules are used

        this.enclosingProjectsAndJars = new IPath[0];
    }

    /*
     * Removes trailing slashes from the given path
     */
    private String normalize(String path) {
        int pathLength = path.length();
        int index = pathLength - 1;
        while (index >= 0 && path.charAt(index) == '/')
            index--;
        if (index != pathLength - 1)
            return path.substring(0, index + 1);
        return path;
    }

    @Override
    public void processDelta(IJavaElementDelta delta, int eventType) {
        switch (delta.getKind()) {
        case IJavaElementDelta.CHANGED:
            IJavaElementDelta[] children = delta.getAffectedChildren();
            for (int i = 0, length = children.length; i < length; i++) {
                IJavaElementDelta child = children[i];
                processDelta(child, eventType);
            }
            break;
        case IJavaElementDelta.REMOVED:
            IJavaElement element = delta.getElement();
            if (this.encloses(element)) {
                if (this.elements != null) {
                    this.elements.remove(element);
                }
                String path = null;
                switch (element.getElementType()) {
                case IJavaElement.JAVA_PROJECT:
                    path = ((IJavaProject) element).getProject().getFullPath().toString();
                    break;
                case IJavaElement.PACKAGE_FRAGMENT_ROOT:
                    path = ((IPackageFragmentRoot) element).getPath().toString();
                    break;
                default:
                    return;
                }
                for (int i = 0; i < this.pathsCount; i++) {
                    if (this.relativePaths[i].equals(path)) {
                        this.relativePaths[i] = null;
                        rehash();
                        break;
                    }
                }
            }
            break;
        }
    }

    /**
     * @see AbstractJavaSearchScope#packageFragmentRoot(String, int, String)
     */
    @Override
    public IPackageFragmentRoot packageFragmentRoot(String resourcePathString, int jarSeparatorIndex,
            String jarPath) {
        int index = -1;
        boolean isJarFile = jarSeparatorIndex != -1;
        if (isJarFile) {
            // internal or external jar (case 3, 4, or 5)
            String relativePath = resourcePathString.substring(jarSeparatorIndex + 1);
            index = indexOf(jarPath, relativePath);
        } else {
            // resource in workspace (case 1 or 2)
            index = indexOf(resourcePathString);
        }
        if (index >= 0) {
            int idx = this.projectIndexes[index];
            String projectPath = idx == -1 ? null : (String) this.projectPaths.get(idx);
            if (projectPath != null) {
                IJavaProject project = JavaCore
                        .create(ResourcesPlugin.getWorkspace().getRoot().getProject(projectPath));
                if (isJarFile) {
                    IResource resource = JavaModel.getWorkspaceTarget(new Path(jarPath));
                    if (resource != null)
                        return project.getPackageFragmentRoot(resource);
                    return project.getPackageFragmentRoot(jarPath);
                }
                Object target = JavaModel
                        .getWorkspaceTarget(new Path(this.containerPaths[index] + '/' + this.relativePaths[index]));
                if (target != null) {
                    if (target instanceof IProject) {
                        return project.getPackageFragmentRoot((IProject) target);
                    }
                    IJavaElement element = JavaModelManager.create((IResource) target, project);
                    return (IPackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
                }
            }
        }
        return null;
    }

    private void rehash() {
        JavaSearchScope newScope = new JavaSearchScope(this.pathsCount * 2, this.excludeTestCode); // double the number of expected elements
        newScope.projectPaths.ensureCapacity(this.projectPaths.size());
        String currentPath;
        for (int i = 0, length = this.relativePaths.length; i < length; i++)
            if ((currentPath = this.relativePaths[i]) != null) {
                int idx = this.projectIndexes[i];
                String projectPath = idx == -1 ? null : (String) this.projectPaths.get(idx);
                newScope.add(projectPath, currentPath, this.containerPaths[i], this.isPkgPath[i],
                        this.pathRestrictions == null ? null : this.pathRestrictions[i]);
            }

        this.relativePaths = newScope.relativePaths;
        this.containerPaths = newScope.containerPaths;
        this.projectPaths = newScope.projectPaths;
        this.projectIndexes = newScope.projectIndexes;
        this.isPkgPath = newScope.isPkgPath;
        this.pathRestrictions = newScope.pathRestrictions;
        this.threshold = newScope.threshold;
    }

    @Override
    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$
                String[] paths = new String[this.relativePaths.length];
                int index = 0;
                for (int i = 0; i < this.relativePaths.length; i++) {
                    String path = this.relativePaths[i];
                    if (path == null)
                        continue;
                    String containerPath;
                    if (ExternalFoldersManager.isInternalPathForExternalFolder(new Path(this.containerPaths[i]))) {
                        Object target = JavaModel.getWorkspaceTarget(new Path(this.containerPaths[i]));
                        containerPath = ((IFolder) target).getLocation().toOSString();
                    } else {
                        containerPath = this.containerPaths[i];
                    }
                    if (path.length() > 0) {
                        paths[index++] = containerPath + '/' + path;
                    } else {
                        paths[index++] = containerPath;
                    }
                }
                System.arraycopy(paths, 0, paths = new String[index], 0, index);
                Util.sort(paths);
                for (int i = 0; i < index; i++) {
                    result.append("\n\t"); //$NON-NLS-1$
                    result.append(paths[i]);
                }
                result.append("\n]"); //$NON-NLS-1$
            }
        }
        return result.toString();
    }
}