Index: search/org/eclipse/jdt/internal/core/search/JavaSearchScope.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchScope.java,v retrieving revision 1.44 diff -u -r1.44 JavaSearchScope.java --- search/org/eclipse/jdt/internal/core/search/JavaSearchScope.java 23 Jun 2005 16:15:24 -0000 1.44 +++ search/org/eclipse/jdt/internal/core/search/JavaSearchScope.java 29 Jun 2005 14:03:33 -0000 @@ -24,7 +24,6 @@ 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.AccessRuleSet; @@ -42,12 +41,12 @@ private ArrayList elements; /* The paths of the resources in this search scope - (or the classpath entries' paths - if the resources are projects) */ - private String[] paths; - private boolean[] pathWithSubFolders; + (or the classpath entries' paths if the resources are projects) + */ + 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 String[] containerPaths; private int pathsCount; private int threshold; @@ -134,7 +133,7 @@ IPath path = entry.getPath(); if (pathToAdd == null || pathToAdd.equals(path)) { String pathToString = path.getDevice() == null ? path.toString() : path.toOSString(); - add("", pathToString, true, access); //$NON-NLS-1$ + add("", pathToString, false/*not a package*/, access); //$NON-NLS-1$ addEnclosingProjectOrJar(path); } } @@ -147,7 +146,7 @@ IPath path = entry.getPath(); if (pathToAdd == null || pathToAdd.equals(path)) { String pathToString = path.getDevice() == null ? path.toString() : path.toOSString(); - add("", pathToString, true, access); //$NON-NLS-1$ + add("", pathToString, false/*not a package*/, access); //$NON-NLS-1$ addEnclosingProjectOrJar(path); } } @@ -166,7 +165,7 @@ if ((includeMask & SOURCES) != 0) { IPath path = entry.getPath(); if (pathToAdd == null || pathToAdd.equals(path)) { - add(Util.relativePath(path,1/*remove project segment*/), projectPathString, true, access); + add(Util.relativePath(path,1/*remove project segment*/), projectPathString, false/*not a package*/, access); } } break; @@ -197,9 +196,9 @@ IResource rootResource = root.getResource(); if (rootResource != null && rootResource.isAccessible()) { String relativePath = Util.relativePath(rootResource.getFullPath(), containerPath.segmentCount()); - add(relativePath, containerPathToString, true, null); + add(relativePath, containerPathToString, false/*not a package*/, null); } else { - add("", containerPathToString, true, null); //$NON-NLS-1$ + add("", containerPathToString, false/*not a package*/, null); //$NON-NLS-1$ } break; case IJavaElement.PACKAGE_FRAGMENT: @@ -208,14 +207,14 @@ String relativePath = Util.concatWith(((PackageFragment) element).names, '/'); containerPath = root.getPath(); containerPathToString = containerPath.getDevice() == null ? containerPath.toString() : containerPath.toOSString(); - add(relativePath, containerPathToString, false, null); + add(relativePath, containerPathToString, true/*package*/, null); } else { IResource resource = element.getResource(); if (resource != null && resource.isAccessible()) { containerPath = root.getKind() == IPackageFragmentRoot.K_SOURCE ? root.getParent().getPath() : root.getPath(); containerPathToString = containerPath.getDevice() == null ? containerPath.toString() : containerPath.toOSString(); String relativePath = Util.relativePath(resource.getFullPath(), containerPath.segmentCount()); - add(relativePath, containerPathToString, false, null); + add(relativePath, containerPathToString, true/*package*/, null); } } break; @@ -237,7 +236,7 @@ relativePath = getPath(element, true/*relative path*/).toString(); } containerPathToString = containerPath.getDevice() == null ? containerPath.toString() : containerPath.toOSString(); - add(relativePath, containerPathToString, true, null); + add(relativePath, containerPathToString, false/*not a package*/, null); } if (containerPath != null) @@ -248,95 +247,135 @@ * 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 relativePath, String containerPath, boolean withSubFolders, AccessRuleSet access) { - int index = (containerPath.hashCode() & 0x7FFFFFFF) % this.paths.length; - String currentPath, currentContainerPath; - while ((currentPath = this.paths[index]) != null && (currentContainerPath = this.containerPaths[index]) != null) { - if (currentPath.equals(relativePath) && currentContainerPath.equals(containerPath)) +private void add(String relativePath, String containerPath, boolean isPackage, AccessRuleSet access) { + // normalize containerPath and relativePath + containerPath = normalize(containerPath); + relativePath = normalize(relativePath); + + int index = (containerPath.hashCode() & 0x7FFFFFFF) % this.containerPaths.length; + String currentRelativePath, currentContainerPath; + while ((currentRelativePath = this.relativePaths[index]) != null && (currentContainerPath = this.containerPaths[index]) != null) { + if (currentRelativePath.equals(relativePath) && currentContainerPath.equals(containerPath)) return; - index = (index + 1) % this.paths.length; + index = (index + 1) % this.relativePaths.length; } - this.paths[index] = relativePath; + this.relativePaths[index] = relativePath; this.containerPaths[index] = containerPath; - this.pathWithSubFolders[index] = withSubFolders; + this.isPkgPath[index] = isPackage; if (this.pathRestrictions != null) this.pathRestrictions[index] = access; else if (access != null) { - this.pathRestrictions = new AccessRuleSet[this.paths.length]; + 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(); - } -/* (non-Javadoc) +/* + * 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) { - return indexOf(resourcePathString.substring(separatorIndex+1), resourcePathString.substring(0, separatorIndex)) >= 0; + // 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; } - return indexOf(resourcePathString, null) >= 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 relativePath, String containerPath) { - if (containerPath != null) { - // if container path is known, use the hash to get faster comparison - int index = (containerPath.hashCode()& 0x7FFFFFFF) % this.paths.length; - String currentContainerPath; - while ((currentContainerPath = this.containerPaths[index]) != null) { - if (currentContainerPath.equals(containerPath)) { - String scopePath = this.paths[index]; - if (encloses(scopePath, relativePath, index)) - return index; - } - index = (index + 1) % this.paths.length; - } - return -1; - } - +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.paths.length; i < length; i++) { - String scopePath = this.paths[i]; - if (scopePath == null) continue; - if (encloses(this.containerPaths[i] + '/' + scopePath, relativePath, i)) + 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; } -private boolean encloses(String scopePath, String path, int index) { - if (this.pathWithSubFolders[index]) { - // TODO (frederic) apply similar change also if not looking at subfolders - int pathLength = path.length(); - int scopeLength = scopePath.length(); - if (pathLength < scopeLength) { - return false; - } - if (scopeLength == 0) { - return true; - } - if (pathLength == scopeLength) { - return path.equals(scopePath); - } - if (path.startsWith(scopePath)) { - if (scopePath.charAt(scopeLength-1) == '/') scopeLength--; - return path.charAt(scopeLength) == '/'; +/** + * 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 index = (containerPath.hashCode()& 0x7FFFFFFF) % this.containerPaths.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; } + index = (index + 1) % this.relativePaths.length; + } + 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 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) - if (path.startsWith(scopePath) - && ((scopePath.length() == path.lastIndexOf('/')) - || (scopePath.length() == path.length()))) { + // 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; } } @@ -361,12 +400,15 @@ } 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(relativePath.toString(), rootPathToString) >= 0; + return indexOf(rootPathToString, relativePath.toString()) >= 0; } - return this.indexOf(getPath(element, false/*full path*/).toString(), null) >= 0; + // resource in workspace + String fullResourcePathString = getPath(element, false/*full path*/).toString(); + return indexOf(fullResourcePathString) >= 0; } /* (non-Javadoc) @@ -376,23 +418,24 @@ return this.enclosingProjectsAndJars; } private IPath getPath(IJavaElement element, boolean relativeToRoot) { - if (element instanceof IPackageFragmentRoot) { - if (relativeToRoot) + switch (element.getElementType()) { + case IJavaElement.JAVA_MODEL: return Path.EMPTY; - return ((IPackageFragmentRoot)element).getPath(); - } - IJavaElement parent = element.getParent(); - IPath parentPath = parent == null ? null : getPath(parent, relativeToRoot); - 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; + 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); } - return parentPath == null ? childPath : parentPath.append(childPath); } /** @@ -402,7 +445,7 @@ * Returns specific uninit access rule set when scope does not enclose the given path. */ public AccessRuleSet getAccessRuleSet(String relativePath, String containerPath) { - int index = indexOf(relativePath, containerPath); + int index = indexOf(containerPath, relativePath); if (index == -1) { // this search scope does not enclose given path return NOT_ENCLOSED; @@ -418,13 +461,27 @@ int extraRoom = (int) (size * 1.75f); if (this.threshold == extraRoom) extraRoom++; - this.paths = new String[extraRoom]; + this.relativePaths = new String[extraRoom]; this.containerPaths = new String[extraRoom]; - this.pathWithSubFolders = new boolean[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) */ @@ -453,13 +510,13 @@ } int toRemove = -1; for (int i = 0; i < this.pathsCount; i++) { - if (this.paths[i].equals(path)) { + if (this.relativePaths[i].equals(path)) { // TODO (jerome) this compares String and IPath ! toRemove = i; break; } } if (toRemove != -1) { - this.paths[toRemove] = null; + this.relativePaths[toRemove] = null; rehash(); } } @@ -471,13 +528,13 @@ private void rehash() { JavaSearchScope newScope = new JavaSearchScope(this.pathsCount * 2); // double the number of expected elements String currentPath; - for (int i = this.paths.length; --i >= 0;) - if ((currentPath = this.paths[i]) != null) - newScope.add(currentPath, this.containerPaths[i], this.pathWithSubFolders[i], this.pathRestrictions == null ? null : this.pathRestrictions[i]); + for (int i = this.relativePaths.length; --i >= 0;) + if ((currentPath = this.relativePaths[i]) != null) + newScope.add(currentPath, this.containerPaths[i], this.isPkgPath[i], this.pathRestrictions == null ? null : this.pathRestrictions[i]); - this.paths = newScope.paths; + this.relativePaths = newScope.relativePaths; this.containerPaths = newScope.containerPaths; - this.pathWithSubFolders = newScope.pathWithSubFolders; + this.isPkgPath = newScope.isPkgPath; this.pathRestrictions = newScope.pathRestrictions; this.threshold = newScope.threshold; } @@ -497,8 +554,8 @@ result.append("[empty scope]"); //$NON-NLS-1$ } else { result.append("["); //$NON-NLS-1$ - for (int i = 0; i < this.paths.length; i++) { - String path = this.paths[i]; + for (int i = 0; i < this.relativePaths.length; i++) { + String path = this.relativePaths[i]; if (path == null) continue; result.append("\n\t"); //$NON-NLS-1$ result.append(this.containerPaths[i]);