001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core.search;
011:
012: import java.util.ArrayList;
013: import java.util.HashSet;
014: import java.util.Map;
015:
016: import org.eclipse.core.resources.IProject;
017: import org.eclipse.core.resources.IResource;
018: import org.eclipse.core.resources.ResourcesPlugin;
019: import org.eclipse.core.runtime.IPath;
020: import org.eclipse.core.runtime.Path;
021: import org.eclipse.jdt.core.IClasspathContainer;
022: import org.eclipse.jdt.core.IClasspathEntry;
023: import org.eclipse.jdt.core.IJavaElement;
024: import org.eclipse.jdt.core.IJavaElementDelta;
025: import org.eclipse.jdt.core.IJavaModel;
026: import org.eclipse.jdt.core.IJavaProject;
027: import org.eclipse.jdt.core.IMember;
028: import org.eclipse.jdt.core.IPackageFragmentRoot;
029: import org.eclipse.jdt.core.JavaCore;
030: import org.eclipse.jdt.core.JavaModelException;
031: import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
032: import org.eclipse.jdt.internal.core.ClasspathEntry;
033: import org.eclipse.jdt.internal.core.JavaElement;
034: import org.eclipse.jdt.internal.core.JavaModel;
035: import org.eclipse.jdt.internal.core.JavaModelManager;
036: import org.eclipse.jdt.internal.core.JavaProject;
037: import org.eclipse.jdt.internal.core.PackageFragment;
038: import org.eclipse.jdt.internal.core.util.Util;
039:
040: /**
041: * A Java-specific scope for searching relative to one or more java elements.
042: */
043: public class JavaSearchScope extends AbstractSearchScope {
044:
045: private ArrayList elements;
046:
047: /* The paths of the resources in this search scope
048: (or the classpath entries' paths if the resources are projects)
049: */
050: private ArrayList projectPaths = new ArrayList(); // container paths projects
051: private int[] projectIndexes; // Indexes of projects in list
052: private String[] containerPaths; // path to the container (e.g. /P/src, /P/lib.jar, c:\temp\mylib.jar)
053: private String[] relativePaths; // path relative to the container (e.g. x/y/Z.class, x/y, (empty))
054: private boolean[] isPkgPath; // in the case of packages, matches must be direct children of the folder
055: protected AccessRuleSet[] pathRestrictions;
056: private int pathsCount;
057: private int threshold;
058:
059: private IPath[] enclosingProjectsAndJars;
060: public final static AccessRuleSet NOT_ENCLOSED = new AccessRuleSet(
061: null, null);
062:
063: public JavaSearchScope() {
064: this (5);
065: }
066:
067: private JavaSearchScope(int size) {
068: initialize(size);
069:
070: //disabled for now as this could be expensive
071: //JavaModelManager.getJavaModelManager().rememberScope(this);
072: }
073:
074: private void addEnclosingProjectOrJar(IPath path) {
075: int length = this .enclosingProjectsAndJars.length;
076: for (int i = 0; i < length; i++) {
077: if (this .enclosingProjectsAndJars[i].equals(path))
078: return;
079: }
080: System.arraycopy(this .enclosingProjectsAndJars, 0,
081: this .enclosingProjectsAndJars = new IPath[length + 1],
082: 0, length);
083: this .enclosingProjectsAndJars[length] = path;
084: }
085:
086: /**
087: * Add java project all fragment roots to current java search scope.
088: * @see #add(JavaProject, IPath, int, HashSet, IClasspathEntry)
089: */
090: public void add(JavaProject project, int includeMask,
091: HashSet visitedProject) throws JavaModelException {
092: add(project, null, includeMask, visitedProject, null);
093: }
094:
095: /**
096: * Add a path to current java search scope or all project fragment roots if null.
097: * Use project resolved classpath to retrieve and store access restriction on each classpath entry.
098: * Recurse if dependent projects are found.
099: * @param javaProject Project used to get resolved classpath entries
100: * @param pathToAdd Path to add in case of single element or null if user want to add all project package fragment roots
101: * @param includeMask Mask to apply on classpath entries
102: * @param visitedProjects Set to avoid infinite recursion
103: * @param referringEntry Project raw entry in referring project classpath
104: * @throws JavaModelException May happen while getting java model info
105: */
106: void add(JavaProject javaProject, IPath pathToAdd, int includeMask,
107: HashSet visitedProjects, IClasspathEntry referringEntry)
108: throws JavaModelException {
109: IProject project = javaProject.getProject();
110: if (!project.isAccessible() || !visitedProjects.add(project))
111: return;
112:
113: IPath projectPath = project.getFullPath();
114: String projectPathString = projectPath.toString();
115: this .addEnclosingProjectOrJar(projectPath);
116:
117: IClasspathEntry[] entries = javaProject.getResolvedClasspath();
118: IJavaModel model = javaProject.getJavaModel();
119: JavaModelManager.PerProjectInfo perProjectInfo = javaProject
120: .getPerProjectInfo();
121: for (int i = 0, length = entries.length; i < length; i++) {
122: IClasspathEntry entry = entries[i];
123: AccessRuleSet access = null;
124: ClasspathEntry cpEntry = (ClasspathEntry) entry;
125: if (referringEntry != null) {
126: // Add only exported entries.
127: // Source folder are implicitly exported.
128: if (!entry.isExported()
129: && entry.getEntryKind() != IClasspathEntry.CPE_SOURCE)
130: continue;
131: cpEntry = cpEntry
132: .combineWith((ClasspathEntry) referringEntry);
133: // cpEntry = ((ClasspathEntry)referringEntry).combineWith(cpEntry);
134: }
135: access = cpEntry.getAccessRuleSet();
136: switch (entry.getEntryKind()) {
137: case IClasspathEntry.CPE_LIBRARY:
138: IClasspathEntry rawEntry = null;
139: Map rootPathToRawEntries = perProjectInfo.rootPathToRawEntries;
140: if (rootPathToRawEntries != null) {
141: rawEntry = (IClasspathEntry) rootPathToRawEntries
142: .get(entry.getPath());
143: }
144: if (rawEntry == null)
145: break;
146: rawKind: switch (rawEntry.getEntryKind()) {
147: case IClasspathEntry.CPE_LIBRARY:
148: case IClasspathEntry.CPE_VARIABLE:
149: if ((includeMask & APPLICATION_LIBRARIES) != 0) {
150: IPath path = entry.getPath();
151: if (pathToAdd == null || pathToAdd.equals(path)) {
152: String pathToString = path.getDevice() == null ? path
153: .toString()
154: : path.toOSString();
155: add(
156: projectPath.toString(),
157: "", pathToString, false/*not a package*/, access); //$NON-NLS-1$
158: addEnclosingProjectOrJar(path);
159: }
160: }
161: break;
162: case IClasspathEntry.CPE_CONTAINER:
163: IClasspathContainer container = JavaCore
164: .getClasspathContainer(rawEntry.getPath(),
165: javaProject);
166: if (container == null)
167: break;
168: switch (container.getKind()) {
169: case IClasspathContainer.K_APPLICATION:
170: if ((includeMask & APPLICATION_LIBRARIES) == 0)
171: break rawKind;
172: break;
173: case IClasspathContainer.K_SYSTEM:
174: case IClasspathContainer.K_DEFAULT_SYSTEM:
175: if ((includeMask & SYSTEM_LIBRARIES) == 0)
176: break rawKind;
177: break;
178: default:
179: break rawKind;
180: }
181: IPath path = entry.getPath();
182: if (pathToAdd == null || pathToAdd.equals(path)) {
183: String pathToString = path.getDevice() == null ? path
184: .toString()
185: : path.toOSString();
186: add(
187: projectPath.toString(),
188: "", pathToString, false/*not a package*/, access); //$NON-NLS-1$
189: addEnclosingProjectOrJar(path);
190: }
191: break;
192: }
193: break;
194: case IClasspathEntry.CPE_PROJECT:
195: if ((includeMask & REFERENCED_PROJECTS) != 0) {
196: IPath path = entry.getPath();
197: if (pathToAdd == null || pathToAdd.equals(path)) {
198: add((JavaProject) model.getJavaProject(entry
199: .getPath().lastSegment()), null,
200: includeMask, visitedProjects, cpEntry);
201: }
202: }
203: break;
204: case IClasspathEntry.CPE_SOURCE:
205: if ((includeMask & SOURCES) != 0) {
206: IPath path = entry.getPath();
207: if (pathToAdd == null || pathToAdd.equals(path)) {
208: add(projectPath.toString(), Util.relativePath(
209: path, 1/*remove project segment*/),
210: projectPathString,
211: false/*not a package*/, access);
212: }
213: }
214: break;
215: }
216: }
217: }
218:
219: /**
220: * Add an element to the java search scope.
221: * @param element The element we want to add to current java search scope
222: * @throws JavaModelException May happen if some Java Model info are not available
223: */
224: public void add(IJavaElement element) throws JavaModelException {
225: IPath containerPath = null;
226: String containerPathToString = null;
227: int includeMask = SOURCES | APPLICATION_LIBRARIES
228: | SYSTEM_LIBRARIES;
229: switch (element.getElementType()) {
230: case IJavaElement.JAVA_MODEL:
231: // a workspace sope should be used
232: break;
233: case IJavaElement.JAVA_PROJECT:
234: add((JavaProject) element, null, includeMask,
235: new HashSet(2), null);
236: break;
237: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
238: IPackageFragmentRoot root = (IPackageFragmentRoot) element;
239: IPath rootPath = root.getPath();
240: containerPath = root.getKind() == IPackageFragmentRoot.K_SOURCE ? root
241: .getParent().getPath()
242: : rootPath;
243: containerPathToString = containerPath.getDevice() == null ? containerPath
244: .toString()
245: : containerPath.toOSString();
246: IResource rootResource = root.getResource();
247: String projectPath = root.getJavaProject().getPath()
248: .toString();
249: if (rootResource != null && rootResource.isAccessible()) {
250: String relativePath = Util.relativePath(rootResource
251: .getFullPath(), containerPath.segmentCount());
252: add(projectPath, relativePath, containerPathToString,
253: false/*not a package*/, null);
254: } else {
255: add(
256: projectPath,
257: "", containerPathToString, false/*not a package*/, null); //$NON-NLS-1$
258: }
259: break;
260: case IJavaElement.PACKAGE_FRAGMENT:
261: root = (IPackageFragmentRoot) element.getParent();
262: projectPath = root.getJavaProject().getPath().toString();
263: if (root.isArchive()) {
264: String relativePath = Util.concatWith(
265: ((PackageFragment) element).names, '/');
266: containerPath = root.getPath();
267: containerPathToString = containerPath.getDevice() == null ? containerPath
268: .toString()
269: : containerPath.toOSString();
270: add(projectPath, relativePath, containerPathToString,
271: true/*package*/, null);
272: } else {
273: IResource resource = element.getResource();
274: if (resource != null) {
275: if (resource.isAccessible()) {
276: containerPath = root.getKind() == IPackageFragmentRoot.K_SOURCE ? root
277: .getParent().getPath()
278: : root.getPath();
279: } else {
280: // for working copies, get resource container full path
281: containerPath = resource.getParent()
282: .getFullPath();
283: }
284: containerPathToString = containerPath.getDevice() == null ? containerPath
285: .toString()
286: : containerPath.toOSString();
287: String relativePath = Util.relativePath(resource
288: .getFullPath(), containerPath
289: .segmentCount());
290: add(projectPath, relativePath,
291: containerPathToString, true/*package*/,
292: null);
293: }
294: }
295: break;
296: default:
297: // remember sub-cu (or sub-class file) java elements
298: if (element instanceof IMember) {
299: if (this .elements == null) {
300: this .elements = new ArrayList();
301: }
302: this .elements.add(element);
303: }
304: root = (IPackageFragmentRoot) element
305: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
306: projectPath = root.getJavaProject().getPath().toString();
307: String relativePath;
308: if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
309: containerPath = root.getParent().getPath();
310: relativePath = Util
311: .relativePath(
312: getPath(element, false/*full path*/),
313: 1/*remove project segmet*/);
314: } else {
315: containerPath = root.getPath();
316: relativePath = getPath(element, true/*relative path*/)
317: .toString();
318: }
319: containerPathToString = containerPath.getDevice() == null ? containerPath
320: .toString()
321: : containerPath.toOSString();
322: add(projectPath, relativePath, containerPathToString,
323: false/*not a package*/, null);
324: }
325:
326: if (containerPath != null)
327: addEnclosingProjectOrJar(containerPath);
328: }
329:
330: /**
331: * Adds the given path to this search scope. Remember if subfolders need to be included
332: * and associated access restriction as well.
333: */
334: private void add(String projectPath, String relativePath,
335: String containerPath, boolean isPackage,
336: AccessRuleSet access) {
337: // normalize containerPath and relativePath
338: containerPath = normalize(containerPath);
339: relativePath = normalize(relativePath);
340: int length = this .containerPaths.length, index = (containerPath
341: .hashCode() & 0x7FFFFFFF)
342: % length;
343: String currentRelativePath, currentContainerPath;
344: while ((currentRelativePath = this .relativePaths[index]) != null
345: && (currentContainerPath = this .containerPaths[index]) != null) {
346: if (currentRelativePath.equals(relativePath)
347: && currentContainerPath.equals(containerPath))
348: return;
349: if (++index == length) {
350: index = 0;
351: }
352: }
353: int idx = this .projectPaths.indexOf(projectPath);
354: if (idx == -1) {
355: // store project in separated list to minimize memory footprint
356: this .projectPaths.add(projectPath);
357: idx = this .projectPaths.indexOf(projectPath);
358: }
359: this .projectIndexes[index] = idx;
360: this .relativePaths[index] = relativePath;
361: this .containerPaths[index] = containerPath;
362: this .isPkgPath[index] = isPackage;
363: if (this .pathRestrictions != null)
364: this .pathRestrictions[index] = access;
365: else if (access != null) {
366: this .pathRestrictions = new AccessRuleSet[this .relativePaths.length];
367: this .pathRestrictions[index] = access;
368: }
369:
370: // assumes the threshold is never equal to the size of the table
371: if (++this .pathsCount > this .threshold)
372: rehash();
373: }
374:
375: /*
376: * E.g.
377: *
378: * 1. /P/src/pkg/X.java
379: * 2. /P/src/pkg
380: * 3. /P/lib.jar|org/eclipse/jdt/core/IJavaElement.class
381: * 4. /home/mylib.jar|x/y/z/X.class
382: * 5. c:\temp\mylib.jar|x/y/Y.class
383: *
384: * @see IJavaSearchScope#encloses(String)
385: */
386: public boolean encloses(String resourcePathString) {
387: int separatorIndex = resourcePathString
388: .indexOf(JAR_FILE_ENTRY_SEPARATOR);
389: if (separatorIndex != -1) {
390: // internal or external jar (case 3, 4, or 5)
391: String jarPath = resourcePathString.substring(0,
392: separatorIndex);
393: String relativePath = resourcePathString
394: .substring(separatorIndex + 1);
395: return indexOf(jarPath, relativePath) >= 0;
396: }
397: // resource in workspace (case 1 or 2)
398: return indexOf(resourcePathString) >= 0;
399: }
400:
401: /**
402: * Returns paths list index of given path or -1 if not found.
403: * NOTE: Use indexOf(String, String) for path inside jars
404: *
405: * @param fullPath the full path of the resource, e.g.
406: * 1. /P/src/pkg/X.java
407: * 2. /P/src/pkg
408: */
409: private int indexOf(String fullPath) {
410: // cannot guess the index of the container path
411: // fallback to sequentially looking at all known paths
412: for (int i = 0, length = this .relativePaths.length; i < length; i++) {
413: String currentRelativePath = this .relativePaths[i];
414: if (currentRelativePath == null)
415: continue;
416: String currentContainerPath = this .containerPaths[i];
417: String currentFullPath = currentRelativePath.length() == 0 ? currentContainerPath
418: : (currentContainerPath + '/' + currentRelativePath);
419: if (encloses(currentFullPath, fullPath, i))
420: return i;
421: }
422: return -1;
423: }
424:
425: /**
426: * Returns paths list index of given path or -1 if not found.
427: * @param containerPath the path of the container, e.g.
428: * 1. /P/src
429: * 2. /P
430: * 3. /P/lib.jar
431: * 4. /home/mylib.jar
432: * 5. c:\temp\mylib.jar
433: * @param relativePath the forward slash path relatively to the container, e.g.
434: * 1. x/y/Z.class
435: * 2. x/y
436: * 3. X.java
437: * 4. (empty)
438: */
439: private int indexOf(String containerPath, String relativePath) {
440: // use the hash to get faster comparison
441: int length = this .containerPaths.length, index = (containerPath
442: .hashCode() & 0x7FFFFFFF)
443: % length;
444: String currentContainerPath;
445: while ((currentContainerPath = this .containerPaths[index]) != null) {
446: if (currentContainerPath.equals(containerPath)) {
447: String currentRelativePath = this .relativePaths[index];
448: if (encloses(currentRelativePath, relativePath, index))
449: return index;
450: }
451: if (++index == length) {
452: index = 0;
453: }
454: }
455: return -1;
456: }
457:
458: /*
459: * Returns whether the enclosing path encloses the given path (or is equal to it)
460: */
461: private boolean encloses(String enclosingPath, String path,
462: int index) {
463: // normalize given path as it can come from outside
464: path = normalize(path);
465:
466: int pathLength = path.length();
467: int enclosingLength = enclosingPath.length();
468: if (pathLength < enclosingLength) {
469: return false;
470: }
471: if (enclosingLength == 0) {
472: return true;
473: }
474: if (pathLength == enclosingLength) {
475: return path.equals(enclosingPath);
476: }
477: if (!this .isPkgPath[index]) {
478: return path.startsWith(enclosingPath)
479: && path.charAt(enclosingLength) == '/';
480: } else {
481: // if looking at a package, this scope encloses the given path
482: // if the given path is a direct child of the folder
483: // or if the given path path is the folder path (see bug 13919 Declaration for package not found if scope is not project)
484: if (path.startsWith(enclosingPath)
485: && ((enclosingPath.length() == path
486: .lastIndexOf('/')) || (enclosingPath
487: .length() == path.length()))) {
488: return true;
489: }
490: }
491: return false;
492: }
493:
494: /* (non-Javadoc)
495: * @see IJavaSearchScope#encloses(IJavaElement)
496: */
497: public boolean encloses(IJavaElement element) {
498: if (this .elements != null) {
499: for (int i = 0, length = this .elements.size(); i < length; i++) {
500: IJavaElement scopeElement = (IJavaElement) this .elements
501: .get(i);
502: IJavaElement searchedElement = element;
503: while (searchedElement != null) {
504: if (searchedElement.equals(scopeElement))
505: return true;
506: searchedElement = searchedElement.getParent();
507: }
508: }
509: return false;
510: }
511: IPackageFragmentRoot root = (IPackageFragmentRoot) element
512: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
513: if (root != null && root.isArchive()) {
514: // external or internal jar
515: IPath rootPath = root.getPath();
516: String rootPathToString = rootPath.getDevice() == null ? rootPath
517: .toString()
518: : rootPath.toOSString();
519: IPath relativePath = getPath(element, true/*relative path*/);
520: return indexOf(rootPathToString, relativePath.toString()) >= 0;
521: }
522: // resource in workspace
523: String fullResourcePathString = getPath(element, false/*full path*/)
524: .toString();
525: return indexOf(fullResourcePathString) >= 0;
526: }
527:
528: /* (non-Javadoc)
529: * @see IJavaSearchScope#enclosingProjectsAndJars()
530: */
531: public IPath[] enclosingProjectsAndJars() {
532: return this .enclosingProjectsAndJars;
533: }
534:
535: private IPath getPath(IJavaElement element, boolean relativeToRoot) {
536: switch (element.getElementType()) {
537: case IJavaElement.JAVA_MODEL:
538: return Path.EMPTY;
539: case IJavaElement.JAVA_PROJECT:
540: return element.getPath();
541: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
542: if (relativeToRoot)
543: return Path.EMPTY;
544: return element.getPath();
545: case IJavaElement.PACKAGE_FRAGMENT:
546: String relativePath = Util.concatWith(
547: ((PackageFragment) element).names, '/');
548: return getPath(element.getParent(), relativeToRoot).append(
549: new Path(relativePath));
550: case IJavaElement.COMPILATION_UNIT:
551: case IJavaElement.CLASS_FILE:
552: return getPath(element.getParent(), relativeToRoot).append(
553: new Path(element.getElementName()));
554: default:
555: return getPath(element.getParent(), relativeToRoot);
556: }
557: }
558:
559: /**
560: * Get access rule set corresponding to a given path.
561: * @param relativePath The path user want to have restriction access
562: * @return The access rule set for given path or null if none is set for it.
563: * Returns specific uninit access rule set when scope does not enclose the given path.
564: */
565: public AccessRuleSet getAccessRuleSet(String relativePath,
566: String containerPath) {
567: int index = indexOf(containerPath, relativePath);
568: if (index == -1) {
569: // this search scope does not enclose given path
570: return NOT_ENCLOSED;
571: }
572: if (this .pathRestrictions == null)
573: return null;
574: return this .pathRestrictions[index];
575: }
576:
577: protected void initialize(int size) {
578: this .pathsCount = 0;
579: this .threshold = size; // size represents the expected number of elements
580: int extraRoom = (int) (size * 1.75f);
581: if (this .threshold == extraRoom)
582: extraRoom++;
583: this .relativePaths = new String[extraRoom];
584: this .containerPaths = new String[extraRoom];
585: this .projectPaths = new ArrayList();
586: this .projectIndexes = new int[extraRoom];
587: this .isPkgPath = new boolean[extraRoom];
588: this .pathRestrictions = null; // null to optimize case where no access rules are used
589:
590: this .enclosingProjectsAndJars = new IPath[0];
591: }
592:
593: /*
594: * Removes trailing slashes from the given path
595: */
596: private String normalize(String path) {
597: int pathLength = path.length();
598: int index = pathLength - 1;
599: while (index >= 0 && path.charAt(index) == '/')
600: index--;
601: if (index != pathLength - 1)
602: return path.substring(0, index + 1);
603: return path;
604: }
605:
606: /*
607: * @see AbstractSearchScope#processDelta(IJavaElementDelta)
608: */
609: public void processDelta(IJavaElementDelta delta) {
610: switch (delta.getKind()) {
611: case IJavaElementDelta.CHANGED:
612: IJavaElementDelta[] children = delta.getAffectedChildren();
613: for (int i = 0, length = children.length; i < length; i++) {
614: IJavaElementDelta child = children[i];
615: this .processDelta(child);
616: }
617: break;
618: case IJavaElementDelta.REMOVED:
619: IJavaElement element = delta.getElement();
620: if (this .encloses(element)) {
621: if (this .elements != null) {
622: this .elements.remove(element);
623: }
624: IPath path = null;
625: switch (element.getElementType()) {
626: case IJavaElement.JAVA_PROJECT:
627: path = ((IJavaProject) element).getProject()
628: .getFullPath();
629: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
630: if (path == null) {
631: path = ((IPackageFragmentRoot) element)
632: .getPath();
633: }
634: int toRemove = -1;
635: for (int i = 0; i < this .pathsCount; i++) {
636: if (this .relativePaths[i].equals(path)) { // TODO (jerome) this compares String and IPath !
637: toRemove = i;
638: break;
639: }
640: }
641: if (toRemove != -1) {
642: this .relativePaths[toRemove] = null;
643: rehash();
644: }
645: }
646: }
647: break;
648: }
649: }
650:
651: /**
652: * Returns the package fragment root corresponding to a given resource path.
653: *
654: * @param resourcePathString path of expected package fragment root.
655: * @return the {@link IPackageFragmentRoot package fragment root} which path
656: * match the given one or <code>null</code> if none was found.
657: */
658: public IPackageFragmentRoot packageFragmentRoot(
659: String resourcePathString) {
660: int index = -1;
661: int separatorIndex = resourcePathString
662: .indexOf(JAR_FILE_ENTRY_SEPARATOR);
663: boolean isJarFile = separatorIndex != -1;
664: if (isJarFile) {
665: // internal or external jar (case 3, 4, or 5)
666: String jarPath = resourcePathString.substring(0,
667: separatorIndex);
668: String relativePath = resourcePathString
669: .substring(separatorIndex + 1);
670: index = indexOf(jarPath, relativePath);
671: } else {
672: // resource in workspace (case 1 or 2)
673: index = indexOf(resourcePathString);
674: }
675: if (index >= 0) {
676: int idx = projectIndexes[index];
677: String projectPath = idx == -1 ? null
678: : (String) this .projectPaths.get(idx);
679: if (projectPath != null) {
680: IJavaProject project = JavaCore.create(ResourcesPlugin
681: .getWorkspace().getRoot().getProject(
682: projectPath));
683: if (isJarFile) {
684: return project
685: .getPackageFragmentRoot(this .containerPaths[index]);
686: }
687: Object target = JavaModel.getTarget(ResourcesPlugin
688: .getWorkspace().getRoot(), new Path(
689: this .containerPaths[index] + '/'
690: + this .relativePaths[index]), false);
691: if (target instanceof IProject) {
692: return project
693: .getPackageFragmentRoot((IProject) target);
694: }
695: if (target instanceof IResource) {
696: IJavaElement element = JavaCore
697: .create((IResource) target);
698: return (IPackageFragmentRoot) element
699: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
700: }
701: }
702: }
703: return null;
704: }
705:
706: private void rehash() {
707: JavaSearchScope newScope = new JavaSearchScope(
708: this .pathsCount * 2); // double the number of expected elements
709: newScope.projectPaths.ensureCapacity(this .projectPaths.size());
710: String currentPath;
711: for (int i = 0, length = this .relativePaths.length; i < length; i++)
712: if ((currentPath = this .relativePaths[i]) != null) {
713: int idx = this .projectIndexes[i];
714: String projectPath = idx == -1 ? null
715: : (String) this .projectPaths.get(idx);
716: newScope.add(projectPath, currentPath,
717: this .containerPaths[i], this .isPkgPath[i],
718: this .pathRestrictions == null ? null
719: : this .pathRestrictions[i]);
720: }
721:
722: this .relativePaths = newScope.relativePaths;
723: this .containerPaths = newScope.containerPaths;
724: this .projectPaths = newScope.projectPaths;
725: this .projectIndexes = newScope.projectIndexes;
726: this .isPkgPath = newScope.isPkgPath;
727: this .pathRestrictions = newScope.pathRestrictions;
728: this .threshold = newScope.threshold;
729: }
730:
731: public String toString() {
732: StringBuffer result = new StringBuffer("JavaSearchScope on "); //$NON-NLS-1$
733: if (this .elements != null) {
734: result.append("["); //$NON-NLS-1$
735: for (int i = 0, length = this .elements.size(); i < length; i++) {
736: JavaElement element = (JavaElement) this .elements
737: .get(i);
738: result.append("\n\t"); //$NON-NLS-1$
739: result.append(element.toStringWithAncestors());
740: }
741: result.append("\n]"); //$NON-NLS-1$
742: } else {
743: if (this .pathsCount == 0) {
744: result.append("[empty scope]"); //$NON-NLS-1$
745: } else {
746: result.append("["); //$NON-NLS-1$
747: for (int i = 0; i < this .relativePaths.length; i++) {
748: String path = this .relativePaths[i];
749: if (path == null)
750: continue;
751: result.append("\n\t"); //$NON-NLS-1$
752: result.append(this .containerPaths[i]);
753: if (path.length() > 0) {
754: result.append('/');
755: result.append(path);
756: }
757: }
758: result.append("\n]"); //$NON-NLS-1$
759: }
760: }
761: return result.toString();
762: }
763: }
|