/*******************************************************************************
* Copyright (c) 2000, 2010 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
*
* 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.
*/
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);
public JavaSearchScope() {
this(5);
}
private JavaSearchScope(int size) {
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];
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)
*/
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;
}
/* (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;
}
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;
}
/* (non-Javadoc)
* @see IJavaSearchScope#enclosingProjectsAndJars()
*/
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);
}
}
/**
* Get access rule set corresponding to a given path.
*
* @param relativePath The path user want to have restriction access
* @return The access rule set for given path or null if none is set for it. Returns specific
* uninit access rule set when scope does not enclose the given path.
*/
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;
}
/*
* @see AbstractSearchScope#processDelta(IJavaElementDelta)
*/
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)
*/
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); // 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;
}
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();
}
}