### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: search/org/eclipse/jdt/core/search/SearchDocument.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchDocument.java,v retrieving revision 1.16 diff -u -r1.16 SearchDocument.java --- search/org/eclipse/jdt/core/search/SearchDocument.java 20 Oct 2008 19:04:26 -0000 1.16 +++ search/org/eclipse/jdt/core/search/SearchDocument.java 4 Nov 2008 15:31:19 -0000 @@ -93,7 +93,11 @@ */ public abstract char[] getCharContents(); - private String getContainerRelativePath() { + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + protected String getContainerRelativePath() { if (this.containerRelativePath == null) this.containerRelativePath = this.index.containerRelativePath(getPath()); return this.containerRelativePath; Index: search/org/eclipse/jdt/internal/core/search/processing/JobManager.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java,v retrieving revision 1.97 diff -u -r1.97 JobManager.java --- search/org/eclipse/jdt/internal/core/search/processing/JobManager.java 12 Sep 2008 13:17:49 -0000 1.97 +++ search/org/eclipse/jdt/internal/core/search/processing/JobManager.java 4 Nov 2008 15:31:20 -0000 @@ -17,10 +17,13 @@ public abstract class JobManager implements Runnable { - /* queue of jobs to execute */ - protected IJob[] awaitingJobs = new IJob[10]; - protected int jobStart = 0; - protected int jobEnd = -1; + /* queues of jobs to execute */ + public static final int HIGH_PRIORITY = 1; + public static final int LOW_PRIORITY = 0; + protected static final int MAX_PRIORITY = HIGH_PRIORITY; + protected IJob[][] awaitingJobs = new IJob[MAX_PRIORITY+1][10]; + protected int[] jobStart = new int[MAX_PRIORITY+1]; + protected int[] jobEnd = new int[MAX_PRIORITY+1]; protected boolean executing = false; /* background processing */ @@ -37,6 +40,13 @@ private int awaitingClients = 0; + public JobManager() { + for (int priorityIndex = 0; priorityIndex <= MAX_PRIORITY; priorityIndex++) { + this.jobStart[priorityIndex] = 0; + this.jobEnd[priorityIndex] = -1; + } + } + /** * Invoked exactly once, in background, before starting processing any job */ @@ -46,17 +56,52 @@ /** * Answer the amount of awaiting jobs. */ - public synchronized int awaitingJobsCount() { - // pretend busy in case concurrent job attempts performing before activated - return this.activated ? this.jobEnd - this.jobStart + 1 : 1; + public int awaitingJobsCount() { + return awaitingJobsCount(HIGH_PRIORITY); + } + + /** + * Answer the amount of awaiting jobs with a given priority (jobs with a lower priority are not considered). + */ + private synchronized int awaitingJobsCount(int maxPriority) { + if (!this.activated) { + // pretend busy in case concurrent job attempts performing before activated + return 1; + } + int count = 0; + for (int priorityIndex = 0; priorityIndex <= MAX_PRIORITY-maxPriority; priorityIndex++) { + int n = this.jobEnd[priorityIndex] - this.jobStart[priorityIndex] + 1; + if (VERBOSE) Util.verbose(" - jobs count for priority "+ (MAX_PRIORITY-priorityIndex) + " = " + n); //$NON-NLS-1$ //$NON-NLS-2$ + count += n; + } + return count; } /** * Answers the first job in the queue, or null if there is no job available * Until the job has completed, the job manager will keep answering the same job. */ - public synchronized IJob currentJob() { - if (this.enableCount > 0 && this.jobStart <= this.jobEnd) - return this.awaitingJobs[this.jobStart]; + public IJob currentJob() { + return currentJob(LOW_PRIORITY); + } + /** + * Answers the first job in the queue, or null if there is no job available + * Until the job has completed, the job manager will keep answering the same job. + */ + private synchronized IJob currentJob(int maxPriority) { + if (this.enableCount <= 0 || maxPriority < 0) + return null; + for (int priorityIndex = 0; priorityIndex <= MAX_PRIORITY-maxPriority; priorityIndex++) { + IJob job = currentJobForPriority(priorityIndex); + if (job != null) + return job; + } + return null; + } + + private IJob currentJobForPriority(int prorityIndex) { + int start = this.jobStart[prorityIndex]; + if (start <= this.jobEnd[prorityIndex]) + return this.awaitingJobs[prorityIndex][start]; return null; } public synchronized void disable() { @@ -65,86 +110,103 @@ Util.verbose("DISABLING background indexing"); //$NON-NLS-1$ } /** - * Remove the index from cache for a given project. + * Remove the jobs belonging to the given family. * Passing null as a job family discards them all. */ public void discardJobs(String jobFamily) { - if (VERBOSE) Util.verbose("DISCARD background job family - " + jobFamily); //$NON-NLS-1$ - - try { - IJob currentJob; - // cancel current job if it belongs to the given family - synchronized(this){ - currentJob = currentJob(); - disable(); - } - if (currentJob != null && (jobFamily == null || currentJob.belongsTo(jobFamily))) { - currentJob.cancel(); - - // wait until current active job has finished - while (this.processingThread != null && this.executing){ - try { - if (VERBOSE) - Util.verbose("-> waiting end of current background job - " + currentJob); //$NON-NLS-1$ - Thread.sleep(50); - } catch(InterruptedException e){ - // ignore - } + for (int priorityIndex = 0; priorityIndex <= MAX_PRIORITY; priorityIndex++) { + try { + IJob currentJob; + // cancel current job if it belongs to the given family + synchronized(this){ + currentJob = currentJobForPriority(priorityIndex); + disable(); } - } - - // flush and compact awaiting jobs - int loc = -1; - synchronized(this) { - for (int i = this.jobStart; i <= this.jobEnd; i++) { - currentJob = this.awaitingJobs[i]; - if (currentJob != null) { // sanity check - this.awaitingJobs[i] = null; - if (!(jobFamily == null || currentJob.belongsTo(jobFamily))) { // copy down, compacting - this.awaitingJobs[++loc] = currentJob; - } else { + if (currentJob != null && (jobFamily == null || currentJob.belongsTo(jobFamily))) { + currentJob.cancel(); + + // wait until current active job has finished + while (this.processingThread != null && this.executing){ + try { if (VERBOSE) - Util.verbose("-> discarding background job - " + currentJob); //$NON-NLS-1$ - currentJob.cancel(); + Util.verbose("-> waiting end of current background job - " + currentJob); //$NON-NLS-1$ + Thread.sleep(50); + } catch(InterruptedException e){ + // ignore + } + } + } + + // flush and compact awaiting jobs + int loc = -1; + synchronized(this) { + for (int i = this.jobStart[priorityIndex]; i <= this.jobEnd[priorityIndex]; i++) { + currentJob = this.awaitingJobs[priorityIndex][i]; + if (currentJob != null) { // sanity check + this.awaitingJobs[priorityIndex][i] = null; + if (!(jobFamily == null || currentJob.belongsTo(jobFamily))) { // copy down, compacting + this.awaitingJobs[priorityIndex][++loc] = currentJob; + } else { + if (VERBOSE) + Util.verbose("-> discarding background job - " + currentJob); //$NON-NLS-1$ + currentJob.cancel(); + } } } + this.jobStart[priorityIndex] = 0; + this.jobEnd[priorityIndex] = loc; } - this.jobStart = 0; - this.jobEnd = loc; + } finally { + enable(); } - } finally { - enable(); } if (VERBOSE) Util.verbose("DISCARD DONE with background job family - " + jobFamily); //$NON-NLS-1$ } + public synchronized void enable() { this.enableCount++; if (VERBOSE) Util.verbose("ENABLING background indexing"); //$NON-NLS-1$ notifyAll(); // wake up the background thread if it is waiting (context must be synchronized) } - protected synchronized boolean isJobWaiting(IJob request) { - for (int i = this.jobEnd; i > this.jobStart; i--) // don't check job at jobStart, as it may have already started - if (request.equals(this.awaitingJobs[i])) return true; + protected boolean isJobWaiting(IJob request) { + return isJobWaiting(request, HIGH_PRIORITY); + } + protected synchronized boolean isJobWaiting(IJob request, int maxPriority) { + for (int priorityIndex = 0; priorityIndex <= MAX_PRIORITY-maxPriority; priorityIndex++) { + for (int i = this.jobEnd[priorityIndex]; i > this.jobStart[priorityIndex]; i--) // don't check job at jobStart, as it may have already started + if (request.equals(this.awaitingJobs[priorityIndex][i])) + return true; + } return false; } /** * Advance to the next available job, once the current one has been completed. * Note: clients awaiting until the job count is zero are still waiting at this point. + * + * @return the priority of the moved job or a lower priority if there's no longer job in the queue */ - protected synchronized void moveToNextJob() { - //if (!enabled) return; - - if (this.jobStart <= this.jobEnd) { - this.awaitingJobs[this.jobStart++] = null; - if (this.jobStart > this.jobEnd) { - this.jobStart = 0; - this.jobEnd = -1; + protected synchronized int moveToNextJob() { + for (int priorityIndex=0; priorityIndex <= MAX_PRIORITY; priorityIndex++) { + if (this.jobStart[priorityIndex] <= this.jobEnd[priorityIndex]) { + int priority = MAX_PRIORITY - priorityIndex; + this.awaitingJobs[priorityIndex][this.jobStart[priorityIndex]++] = null; + if (this.jobStart[priorityIndex] > this.jobEnd[priorityIndex]) { + // no more job in this queue... + this.jobStart[priorityIndex] = 0; + this.jobEnd[priorityIndex] = -1; + // ... so, move to a lower priority + if (priority > LOW_PRIORITY) priority--; + } + if (VERBOSE) Util.verbose("Move to next job (priority="+(priority)+')'); //$NON-NLS-1$ + return priority; } } + // no job to move on, use the lowest priority + return LOW_PRIORITY; } /** * When idle, give chance to do something @@ -157,19 +219,25 @@ * Indeed since other jobs are performed in background, resource sharing might be * an issue.Therefore, this functionality allows a given job to be run without * colliding with background ones. + *

* Note: multiple thread might attempt to perform concurrent jobs at the same time, * and should synchronize (it is deliberately left to clients to decide whether * concurrent jobs might interfere or not. In general, multiple read jobs are ok). - * + *

* Waiting policy can be: - * IJobConstants.ForceImmediateSearch - * IJobConstants.CancelIfNotReadyToSearch - * IJobConstants.WaitUntilReadyToSearch - * + *

+ *

*/ public boolean performConcurrentJob(IJob searchJob, int waitingPolicy, IProgressMonitor progress) { + return performConcurrentJob(searchJob, waitingPolicy, HIGH_PRIORITY, progress); + } + public boolean performConcurrentJob(IJob searchJob, int waitingPolicy, int maxPriority, IProgressMonitor progress) { if (VERBOSE) - Util.verbose("STARTING concurrent job - " + searchJob); //$NON-NLS-1$ + Util.verbose("STARTING concurrent job - " + searchJob + "(priority="+maxPriority+')'); //$NON-NLS-1$ //$NON-NLS-2$ searchJob.ensureReadyToRun(); @@ -178,7 +246,7 @@ int concurrentJobWork = 100; if (progress != null) progress.beginTask("", concurrentJobWork); //$NON-NLS-1$ - if (awaitingJobsCount() > 0) { + if (awaitingJobsCount(maxPriority) > 0) { switch (waitingPolicy) { case IJob.ForceImmediate : @@ -225,10 +293,10 @@ int lastJobsCount = totalWork; float lastWorked = 0; float totalWorked = 0; - while ((awaitingJobsCount = awaitingJobsCount()) > 0) { + while ((awaitingJobsCount = awaitingJobsCount(maxPriority)) > 0) { if (subProgress != null && subProgress.isCanceled()) throw new OperationCanceledException(); - IJob currentJob = currentJob(); + IJob currentJob = currentJob(maxPriority); // currentJob can be null when jobs have been added to the queue but job manager is not enabled if (currentJob != null && currentJob != previousJob) { if (VERBOSE) @@ -284,21 +352,24 @@ } public abstract String processName(); - public synchronized void request(IJob job) { - + public void request(IJob job) { + request(job, HIGH_PRIORITY); + } + + public synchronized void request(IJob job, int priority) { job.ensureReadyToRun(); - + int priorityIndex = MAX_PRIORITY-priority; // reverse order // append the job to the list of ones to process later on - int size = this.awaitingJobs.length; - if (++this.jobEnd == size) { // when growing, relocate jobs starting at position 0 - this.jobEnd -= this.jobStart; - System.arraycopy(this.awaitingJobs, this.jobStart, this.awaitingJobs = new IJob[size * 2], 0, this.jobEnd); - this.jobStart = 0; + int size = this.awaitingJobs[priorityIndex].length; + if (++this.jobEnd[priorityIndex] == size) { // when growing, relocate jobs starting at position 0 + this.jobEnd[priorityIndex] -= this.jobStart[priorityIndex]; + System.arraycopy(this.awaitingJobs[priorityIndex], this.jobStart[priorityIndex], this.awaitingJobs[priorityIndex] = new IJob[size * 2], 0, this.jobEnd[priorityIndex]); + this.jobStart[priorityIndex] = 0; } - this.awaitingJobs[this.jobEnd] = job; + this.awaitingJobs[priorityIndex][this.jobEnd[priorityIndex]] = job; if (VERBOSE) { Util.verbose("REQUEST background job - " + job); //$NON-NLS-1$ - Util.verbose("AWAITING JOBS count: " + awaitingJobsCount()); //$NON-NLS-1$ + Util.verbose("AWAITING JOBS count: " + awaitingJobsCount(LOW_PRIORITY)); //$NON-NLS-1$ } notifyAll(); // wake up the background thread if it is waiting } @@ -351,6 +422,7 @@ } } this.progressJob = null; + int maxPriority = LOW_PRIORITY; // need the priority to get the job, default is the lowest one (i.e. any job will be kept) while (this.processingThread != null) { try { IJob job; @@ -359,7 +431,7 @@ if (this.processingThread == null) continue; // must check for new job inside this sync block to avoid timing hole - if ((job = currentJob()) == null) { + if ((job = currentJob(maxPriority)) == null) { if (this.progressJob != null) { this.progressJob.cancel(); this.progressJob = null; @@ -380,7 +452,7 @@ continue; } if (VERBOSE) { - Util.verbose(awaitingJobsCount() + " awaiting jobs"); //$NON-NLS-1$ + Util.verbose(awaitingJobsCount(LOW_PRIORITY) + " awaiting jobs"); //$NON-NLS-1$ Util.verbose("STARTING background job - " + job); //$NON-NLS-1$ } try { @@ -397,7 +469,7 @@ this.executing = false; if (VERBOSE) Util.verbose("FINISHED background job - " + job); //$NON-NLS-1$ - moveToNextJob(); + maxPriority = moveToNextJob(); if (this.awaitingClients == 0) Thread.sleep(50); } @@ -457,13 +529,16 @@ // ignore } } - public String toString() { + public synchronized String toString() { StringBuffer buffer = new StringBuffer(10); buffer.append("Enable count:").append(this.enableCount).append('\n'); //$NON-NLS-1$ - int numJobs = this.jobEnd - this.jobStart + 1; - buffer.append("Jobs in queue:").append(numJobs).append('\n'); //$NON-NLS-1$ - for (int i = 0; i < numJobs && i < 15; i++) { - buffer.append(i).append(" - job["+i+"]: ").append(this.awaitingJobs[this.jobStart+i]).append('\n'); //$NON-NLS-1$ //$NON-NLS-2$ + for (int priorityIndex = 0; priorityIndex <= MAX_PRIORITY; priorityIndex++) { + int numJobs = this.jobEnd[priorityIndex] - this.jobStart[priorityIndex] + 1; + buffer.append("Jobs in queue [").append(MAX_PRIORITY-priorityIndex).append("]:").append(numJobs).append('\n'); //$NON-NLS-1$ //$NON-NLS-2$ + for (int i = 0; i < numJobs && i < 15; i++) { + buffer.append(i).append(" - job[").append(i).append("]: ").append(this.awaitingJobs[priorityIndex][this.jobStart[priorityIndex]+i]).append('\n'); //$NON-NLS-1$ //$NON-NLS-2$ + } + } return buffer.toString(); } Index: search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java,v retrieving revision 1.87 diff -u -r1.87 IndexAllProject.java --- search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java 27 Jun 2008 16:03:49 -0000 1.87 +++ search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java 4 Nov 2008 15:31:20 -0000 @@ -77,7 +77,7 @@ if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && entry.getPath().equals(projectPath)) { // the project is also a library folder (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=89815) // ensure a job exists to index it as a binary folder - this.manager.indexLibrary(projectPath, this.project); + this.manager.indexLibrary(entry, javaProject); return true; } } Index: search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java,v retrieving revision 1.162 diff -u -r1.162 IndexManager.java --- search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java 3 Oct 2008 10:47:56 -0000 1.162 +++ search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java 4 Nov 2008 15:31:20 -0000 @@ -29,6 +29,7 @@ import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.core.*; +import org.eclipse.jdt.internal.core.DeltaProcessor.RootInfo; import org.eclipse.jdt.internal.core.index.*; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.jdt.internal.core.search.PatternSearchJob; @@ -380,7 +381,7 @@ for (int i = 0; i < entries.length; i++) { IClasspathEntry entry= entries[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) - indexLibrary(entry.getPath(), project); + indexLibrary(entry, javaProject); } } catch(JavaModelException e){ // cannot retrieve classpath info } @@ -391,38 +392,67 @@ request(request); } /** - * Trigger addition of a library to an index + * Trigger addition to an index of a library defined by a classpath entry * Note: the actual operation is performed in background */ -public void indexLibrary(IPath path, IProject requestingProject) { +public void indexLibrary(IClasspathEntry classpathEntry, IJavaProject requestingProject) { + IPath path = classpathEntry.getPath(); + IndexRequest request = indexRequest(path); + if (request == null) return; + if (request instanceof AddJarFileToIndex) { + AddJarFileToIndex jarFileRequest = (AddJarFileToIndex) request; + jarFileRequest.setJavaProject(requestingProject); + jarFileRequest.setSourceMapper(classpathEntry); + } + if (!isJobWaiting(request)) { + request(request); + } +} +/** + * Trigger addition to an index of a library defined by a jar package fragment root + * Note: the actual operation is performed in background + */ +public void indexPackageFragmentRoot(IPackageFragmentRoot fragmentRoot) { + IPath path = fragmentRoot.getPath(); + IndexRequest request = indexRequest(path); + if (request == null) return; + if (request instanceof AddJarFileToIndex) { + AddJarFileToIndex jarFileRequest = (AddJarFileToIndex) request; + jarFileRequest.setSourceMapper(fragmentRoot); + } + if (!isJobWaiting(request)) { + request(request); + } +} +private IndexRequest indexRequest(IPath path) { // requestingProject is no longer used to cancel jobs but leave it here just in case - if (JavaCore.getPlugin() == null) return; + if (JavaCore.getPlugin() == null) return null; Object target = JavaModel.getTarget(path, true); IndexRequest request = null; - if (target instanceof IFile) { + if (target instanceof IProject) { + IProject p = (IProject) target; + if (JavaProject.hasJavaNature(p)) + request = new IndexAllProject(p, this); + } else if (target instanceof IContainer) { + request = new IndexBinaryFolder((IContainer) target, this); + } else if (target instanceof IFile) { request = new AddJarFileToIndex((IFile) target, this); } else if (target instanceof File) { request = new AddJarFileToIndex(path, this); - } else if (target instanceof IContainer) { - request = new IndexBinaryFolder((IContainer) target, this); - } else { - return; } - - // check if the same request is not already in the queue - if (!isJobWaiting(request)) - request(request); + return request; } /** * Index the content of the given source folder. */ public void indexSourceFolder(JavaProject javaProject, IPath sourceFolder, char[][] inclusionPatterns, char[][] exclusionPatterns) { IProject project = javaProject.getProject(); - if (this.jobEnd > this.jobStart) { + int priorityIndex = MAX_PRIORITY - HIGH_PRIORITY; + if (this.jobEnd[priorityIndex] > this.jobStart[priorityIndex]) { // skip it if a job to index the project is already in the queue IndexRequest request = new IndexAllProject(project, this); - if (isJobWaiting(request)) return; + if (isJobWaiting(request, HIGH_PRIORITY)) return; } request(new AddFolderToIndex(sourceFolder, project, inclusionPatterns, exclusionPatterns, this)); @@ -440,10 +470,10 @@ * Advance to the next available job, once the current one has been completed. * Note: clients awaiting until the job count is zero are still waiting at this point. */ -protected synchronized void moveToNextJob() { +protected synchronized int moveToNextJob() { // remember that one job was executed, and we will need to save indexes at some point this.needToSave = true; - super.moveToNextJob(); + return super.moveToNextJob(); } /** * No more job awaiting. @@ -465,20 +495,19 @@ Util.verbose("-> request to rebuild index: "+indexLocation+" path: "+containerPath); //$NON-NLS-1$ //$NON-NLS-2$ updateIndexState(indexLocation, REBUILDING_STATE); - IndexRequest request = null; - if (target instanceof IProject) { - IProject p = (IProject) target; - if (JavaProject.hasJavaNature(p)) - request = new IndexAllProject(p, this); - } else if (target instanceof IFolder) { - request = new IndexBinaryFolder((IFolder) target, this); - } else if (target instanceof IFile) { - request = new AddJarFileToIndex((IFile) target, this); - } else if (target instanceof File) { - request = new AddJarFileToIndex(containerPath, this); + IndexRequest request = indexRequest(containerPath); + if (request == null) return; + if (request instanceof AddJarFileToIndex) { + DeltaProcessingState processingState = JavaModelManager.getDeltaState(); + RootInfo rootInfo = (RootInfo) processingState.roots.get(containerPath); + if (rootInfo != null) { + AddJarFileToIndex jarFileRequest = (AddJarFileToIndex) request; + jarFileRequest.setSourceMapper(rootInfo.getPackageFragmentRoot(null/*no resource hint*/)); + } } - if (request != null) + if (!isJobWaiting(request)) { request(request); + } } /** * Recreates the index for a given path, keeping the same read-write monitor. @@ -510,6 +539,33 @@ } } /** + * Resets the index for a given path. + * Returns true if the index was reset, false otherwise. + */ +public synchronized boolean resetIndex(IPath containerPath) { + // only called to over write an existing cached index... + String containerPathString = containerPath.getDevice() == null ? containerPath.toString() : containerPath.toOSString(); + try { + // Path is already canonical + IPath indexLocation = computeIndexLocation(containerPath); + Index index = getIndex(indexLocation); + + if (VERBOSE) { + Util.verbose("-> reseting index: "+indexLocation+" for path: "+containerPathString); //$NON-NLS-1$ //$NON-NLS-2$ + } + index.reset(true/*reuse index file*/); + return true; + } catch (IOException e) { + // The file could not be created. Possible reason: the project has been deleted. + if (VERBOSE) { + Util.verbose("-> failed to reset index for path: "+containerPathString); //$NON-NLS-1$ + e.printStackTrace(); + } + return false; + } +} + +/** * Trigger removal of a resource to an index * Note: the actual operation is performed in background */ @@ -602,10 +658,11 @@ */ public void removeSourceFolderFromIndex(JavaProject javaProject, IPath sourceFolder, char[][] inclusionPatterns, char[][] exclusionPatterns) { IProject project = javaProject.getProject(); - if (this.jobEnd > this.jobStart) { + int priorityIndex = MAX_PRIORITY - HIGH_PRIORITY; + if (this.jobEnd[priorityIndex] > this.jobStart[priorityIndex]) { // skip it if a job to index the project is already in the queue IndexRequest request = new IndexAllProject(project, this); - if (isJobWaiting(request)) return; + if (isJobWaiting(request, HIGH_PRIORITY)) return; } request(new RemoveFolderFromIndex(sourceFolder, inclusionPatterns, exclusionPatterns, project, this)); @@ -631,11 +688,13 @@ } synchronized (this) { IPath containerPath = new Path(index.containerPath); - if (this.jobEnd > this.jobStart) { - for (int i = this.jobEnd; i > this.jobStart; i--) { // skip the current job - IJob job = this.awaitingJobs[i]; - if (job instanceof IndexRequest) - if (((IndexRequest) job).containerPath.equals(containerPath)) return; + for (int priorityIndex = 0; priorityIndex <= MAX_PRIORITY; priorityIndex++) { + if (this.jobEnd[priorityIndex] > this.jobStart[priorityIndex]) { + for (int i = this.jobEnd[priorityIndex]; i > this.jobStart[priorityIndex]; i--) { // skip the current job + IJob job = this.awaitingJobs[priorityIndex][i]; + if (job instanceof IndexRequest) + if (((IndexRequest) job).containerPath.equals(containerPath)) return; + } } } IPath indexLocation = computeIndexLocation(containerPath); @@ -714,7 +773,7 @@ }); } -public String toString() { +public synchronized String toString() { StringBuffer buffer = new StringBuffer(10); buffer.append(super.toString()); buffer.append("In-memory indexes:\n"); //$NON-NLS-1$ Index: search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java,v retrieving revision 1.75 diff -u -r1.75 AddJarFileToIndex.java --- search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java 27 Jun 2008 16:03:49 -0000 1.75 +++ search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java 4 Nov 2008 15:31:20 -0000 @@ -10,10 +10,12 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.search.indexing; -import java.io.File; import java.io.IOException; -import java.net.URI; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -22,12 +24,23 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.compiler.util.Util; import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.PackageFragmentRoot; +import org.eclipse.jdt.internal.core.SourceMapper; import org.eclipse.jdt.internal.core.index.Index; import org.eclipse.jdt.internal.core.search.JavaSearchDocument; import org.eclipse.jdt.internal.core.search.processing.JobManager; @@ -36,204 +49,368 @@ private static final char JAR_SEPARATOR = IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR.charAt(0); IFile resource; + SourceMapper sourceMapper; + IPath sourcePath; + IJavaProject javaProject; + IPackageFragmentRoot packageFragmentRoot; + +public AddJarFileToIndex(IFile resource, IndexManager manager) { + super(resource.getFullPath(), manager); + this.resource = resource; +} +public AddJarFileToIndex(IPath jarPath, IndexManager manager) { + // external JAR scenario - no resource + super(jarPath, manager); +} + +void setJavaProject(IJavaProject javaProject) { + this.javaProject = javaProject; +} - public AddJarFileToIndex(IFile resource, IndexManager manager) { - super(resource.getFullPath(), manager); - this.resource = resource; - } - public AddJarFileToIndex(IPath jarPath, IndexManager manager) { - // external JAR scenario - no resource - super(jarPath, manager); - } - public boolean equals(Object o) { - if (o instanceof AddJarFileToIndex) { - if (this.resource != null) - return this.resource.equals(((AddJarFileToIndex) o).resource); - if (this.containerPath != null) - return this.containerPath.equals(((AddJarFileToIndex) o).containerPath); +void setSourceMapper(IClasspathEntry classpathEntry) { + if (this.javaProject != null) { + try { + IPackageFragmentRoot root= this.javaProject.findPackageFragmentRoot(classpathEntry.getPath()); + this.packageFragmentRoot = root; } - return false; + catch (JavaModelException jme) { + // ignore + } + } + this.sourcePath = classpathEntry.getSourceAttachmentPath(); + IPath rootPath = classpathEntry.getSourceAttachmentRootPath(); + if (this.packageFragmentRoot != null) { + try { + PackageFragmentRoot root = (PackageFragmentRoot) this.packageFragmentRoot; + if (this.sourcePath == null) { + this.sourcePath = root.sourceAttachmentPath(); + if (this.sourcePath == null) { + this.sourcePath = root.getPath(); + } + } + if (rootPath == null) { + rootPath = root.getSourceAttachmentRootPath(); + } + } catch (Exception e) { + // ignore + } + } + if (this.sourcePath != null) { + Map options = this.javaProject == null + ? JavaCore.getOptions() + : this.javaProject.getOptions(true); + this.sourceMapper = new SourceMapper( + this.sourcePath , + rootPath == null ? null : rootPath.toOSString(), + options); + } +} + +void setSourceMapper(IPackageFragmentRoot fragmentRoot) { + this.packageFragmentRoot = fragmentRoot; + if (this.javaProject == null) { + this.javaProject = fragmentRoot.getJavaProject(); } - public int hashCode() { + try { + this.sourcePath = ((PackageFragmentRoot)fragmentRoot).sourceAttachmentPath(); + if (this.sourcePath == null) { + this.sourcePath = fragmentRoot.getPath(); + } + IPath rootPath = ((PackageFragmentRoot)fragmentRoot).sourceAttachmentRootPath(); + this.sourceMapper = new SourceMapper( + this.sourcePath, + rootPath == null ? null : rootPath.toOSString(), + this.javaProject.getOptions(true)); + } catch (JavaModelException e) { + // skip + } +} + +public boolean equals(Object o) { + if (o instanceof AddJarFileToIndex) { if (this.resource != null) - return this.resource.hashCode(); + return this.resource.equals(((AddJarFileToIndex) o).resource); if (this.containerPath != null) - return this.containerPath.hashCode(); - return -1; + return this.containerPath.equals(((AddJarFileToIndex) o).containerPath); } - public boolean execute(IProgressMonitor progressMonitor) { + return false; +} +public int hashCode() { + if (this.resource != null) + return this.resource.hashCode(); + if (this.containerPath != null) + return this.containerPath.hashCode(); + return -1; +} +public boolean execute(IProgressMonitor progressMonitor) { - if (this.isCancelled || progressMonitor != null && progressMonitor.isCanceled()) return true; + boolean verbose = true; //JobManager.VERBOSE; - try { - // if index is already cached, then do not perform any check - // MUST reset the IndexManager if a jar file is changed - Index index = this.manager.getIndexForUpdate(this.containerPath, false, /*do not reuse index file*/ false /*do not create if none*/); + if (this.isCancelled || progressMonitor != null && progressMonitor.isCanceled()) return true; + + try { + // if index is already cached, then do not perform any check + // MUST reset the IndexManager if a jar file is changed + Index index = this.manager.getIndexForUpdate(this.containerPath, false, /*do not reuse index file*/ false /*do not create if none*/); + if (this.sourcePath == null) { // force reindexing if there's attached sources as there's no way to know if they have been changed or not if (index != null) { - if (JobManager.VERBOSE) + if (verbose) org.eclipse.jdt.internal.core.util.Util.verbose("-> no indexing required (index already exists) for " + this.containerPath); //$NON-NLS-1$ return true; } + } + if (index == null) { index = this.manager.getIndexForUpdate(this.containerPath, true, /*reuse index file*/ true /*create if none*/); - if (index == null) { - if (JobManager.VERBOSE) - org.eclipse.jdt.internal.core.util.Util.verbose("-> index could not be created for " + this.containerPath); //$NON-NLS-1$ - return true; - } - ReadWriteMonitor monitor = index.monitor; - if (monitor == null) { - if (JobManager.VERBOSE) - org.eclipse.jdt.internal.core.util.Util.verbose("-> index for " + this.containerPath + " just got deleted"); //$NON-NLS-1$//$NON-NLS-2$ - return true; // index got deleted since acquired - } - index.separator = JAR_SEPARATOR; - ZipFile zip = null; - try { - // this path will be a relative path to the workspace in case the zipfile in the workspace otherwise it will be a path in the - // local file system - Path zipFilePath = null; - - monitor.enterWrite(); // ask permission to write - if (this.resource != null) { - URI location = this.resource.getLocationURI(); - if (location == null) return false; - if (JavaModelManager.ZIP_ACCESS_VERBOSE) - System.out.println("(" + Thread.currentThread() + ") [AddJarFileToIndex.execute()] Creating ZipFile on " + location.getPath()); //$NON-NLS-1$ //$NON-NLS-2$ - File file = null; - try { - file = org.eclipse.jdt.internal.core.util.Util.toLocalFile(location, progressMonitor); - } catch (CoreException e) { - if (JobManager.VERBOSE) { - org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + location.getPath() + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$ - e.printStackTrace(); - } - } - if (file == null) { - if (JobManager.VERBOSE) - org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + location.getPath() + " because the file could not be fetched"); //$NON-NLS-1$ //$NON-NLS-2$ - return false; + } + if (index == null) { + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> index could not be created for " + this.containerPath); //$NON-NLS-1$ + return true; + } + ReadWriteMonitor monitor = index.monitor; + if (monitor == null) { + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> index for " + this.containerPath + " just got deleted"); //$NON-NLS-1$//$NON-NLS-2$ + return true; // index got deleted since acquired + } + index.separator = JAR_SEPARATOR; + ZipFile zip = null; + JavaModelManager modelManager = JavaModelManager.getJavaModelManager(); + modelManager.cacheZipFiles(); + try { + // this path will be a relative path to the workspace in case the zipfile in the workspace otherwise it will be a path in the + // local file system + Path zipFilePath = null; + + monitor.enterWrite(); // ask permission to write + if (this.resource != null) { + IPath resourcePath = this.resource.getFullPath(); + if (JavaModelManager.ZIP_ACCESS_VERBOSE) + System.out.println("(" + Thread.currentThread() + ") [AddJarFileToIndex.execute()] Creating ZipFile on " + resourcePath); //$NON-NLS-1$ //$NON-NLS-2$ + try { + zip = modelManager.getZipFile(resourcePath); + } catch (CoreException ce) { + if (verbose) { + org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + resourcePath + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$ + ce.printStackTrace(); } - zip = new ZipFile(file); - zipFilePath = (Path) this.resource.getFullPath().makeRelative(); - // absolute path relative to the workspace - } else { - if (JavaModelManager.ZIP_ACCESS_VERBOSE) - System.out.println("(" + Thread.currentThread() + ") [AddJarFileToIndex.execute()] Creating ZipFile on " + this.containerPath); //$NON-NLS-1$ //$NON-NLS-2$ - // external file -> it is ok to use toFile() - zip = new ZipFile(this.containerPath.toFile()); - zipFilePath = (Path) this.containerPath; - // path is already canonical since coming from a library classpath entry + // jar file is invalid, remove corresponding index + this.manager.removeIndex(this.containerPath); + return false; } + zipFilePath = (Path) resourcePath.makeRelative(); + // absolute path relative to the workspace - if (this.isCancelled) { - if (JobManager.VERBOSE) - org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing of " + zip.getName() + " has been cancelled"); //$NON-NLS-1$ //$NON-NLS-2$ + // Get package fragment root if not already stored + if (this.packageFragmentRoot == null) { + IJavaElement javaElement = JavaCore.create(this.resource, this.javaProject); + if (javaElement.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT) { + this.packageFragmentRoot = (IPackageFragmentRoot) javaElement; + } + } + } else { + if (JavaModelManager.ZIP_ACCESS_VERBOSE) + System.out.println("(" + Thread.currentThread() + ") [AddJarFileToIndex.execute()] Creating ZipFile on " + this.containerPath); //$NON-NLS-1$ //$NON-NLS-2$ + // external file -> it is ok to use toFile() + try { + zip = modelManager.getZipFile(this.containerPath); + } catch (CoreException ce) { + if (verbose) { + org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + this.containerPath + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$ + ce.printStackTrace(); + } + // jar file is invalid, remove corresponding index + this.manager.removeIndex(this.containerPath); return false; } + zipFilePath = (Path) this.containerPath; + // path is already canonical since coming from a library classpath entry + } - if (JobManager.VERBOSE) - org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing " + zip.getName()); //$NON-NLS-1$ - long initialTime = System.currentTimeMillis(); - - String[] paths = index.queryDocumentNames(""); // all file names //$NON-NLS-1$ - if (paths != null) { - int max = paths.length; - /* check integrity of the existing index file - * if the length is equal to 0, we want to index the whole jar again - * If not, then we want to check that there is no missing entry, if - * one entry is missing then we recreate the index - */ - String EXISTS = "OK"; //$NON-NLS-1$ - String DELETED = "DELETED"; //$NON-NLS-1$ - SimpleLookupTable indexedFileNames = new SimpleLookupTable(max == 0 ? 33 : max + 11); - for (int i = 0; i < max; i++) - indexedFileNames.put(paths[i], DELETED); - for (Enumeration e = zip.entries(); e.hasMoreElements();) { - // iterate each entry to index it - ZipEntry ze = (ZipEntry) e.nextElement(); - String zipEntryName = ze.getName(); - if (Util.isClassFileName(zipEntryName)) - indexedFileNames.put(zipEntryName, EXISTS); + if (this.isCancelled) { + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing of " + zip.getName() + " has been cancelled"); //$NON-NLS-1$ //$NON-NLS-2$ + return false; + } + + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing " + zip.getName()); //$NON-NLS-1$ + long initialTime = System.currentTimeMillis(); + + String[] paths = index.queryDocumentNames(""); // all file names //$NON-NLS-1$ + if (paths != null) { + int max = paths.length; + /* check integrity of the existing index file + * if the length is equal to 0, we want to index the whole jar again + * If not, then we want to check that there is no missing entry, if + * one entry is missing then we recreate the index + */ + String EXISTS = "OK"; //$NON-NLS-1$ + String DELETED = "DELETED"; //$NON-NLS-1$ + SimpleLookupTable indexedFileNames = new SimpleLookupTable(max == 0 ? 33 : max + 11); + for (int i = 0; i < max; i++) + indexedFileNames.put(paths[i], DELETED); + for (Enumeration e = zip.entries(); e.hasMoreElements();) { + // iterate each entry to index it + ZipEntry ze = (ZipEntry) e.nextElement(); + String zipEntryName = ze.getName(); + if (Util.isClassFileName(zipEntryName)) + indexedFileNames.put(zipEntryName, EXISTS); + } + boolean needToReindex = indexedFileNames.elementSize != max; // a new file was added + if (!needToReindex) { + Object[] valueTable = indexedFileNames.valueTable; + for (int i = 0, l = valueTable.length; i < l; i++) { + if (valueTable[i] == DELETED) { + needToReindex = true; // a file was deleted so re-index + break; + } } - boolean needToReindex = indexedFileNames.elementSize != max; // a new file was added if (!needToReindex) { - Object[] valueTable = indexedFileNames.valueTable; - for (int i = 0, l = valueTable.length; i < l; i++) { - if (valueTable[i] == DELETED) { - needToReindex = true; // a file was deleted so re-index - break; - } - } - if (!needToReindex) { - if (JobManager.VERBOSE) - org.eclipse.jdt.internal.core.util.Util.verbose("-> no indexing required (index is consistent with library) for " //$NON-NLS-1$ - + zip.getName() + " (" //$NON-NLS-1$ - + (System.currentTimeMillis() - initialTime) + "ms)"); //$NON-NLS-1$ - this.manager.saveIndex(index); // to ensure its placed into the saved state - return true; - } + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> no indexing required (index is consistent with library) for " //$NON-NLS-1$ + + zip.getName() + " (" //$NON-NLS-1$ + + (System.currentTimeMillis() - initialTime) + "ms)"); //$NON-NLS-1$ + this.manager.saveIndex(index); // to ensure its placed into the saved state + return true; } } + } - // Index the jar for the first time or reindex the jar in case the previous index file has been corrupted - // index already existed: recreate it so that we forget about previous entries - SearchParticipant participant = SearchEngine.getDefaultSearchParticipant(); - index = this.manager.recreateIndex(this.containerPath); - if (index == null) { - // failed to recreate index, see 73330 - this.manager.removeIndex(this.containerPath); - return false; - } - index.separator = JAR_SEPARATOR; + // Index the jar for the first time or re-index the jar in case the previous index file has been corrupted + // index already existed: reset it so that we forget about previous entries + // without changing the instance (which may have been stored locally by callers) + SearchParticipant participant = SearchEngine.getDefaultSearchParticipant(); + if (!this.manager.resetIndex(this.containerPath)) { + // failed to recreate index, see 73330 + this.manager.removeIndex(this.containerPath); + return false; + } + index.separator = JAR_SEPARATOR; + // Read zip entries + Map sources = this.sourcePath == null ? null : new HashMap(); + if (this.sourcePath != null && this.containerPath.toString().equals(this.sourcePath.toString())) { + + // if sources are in the jar itself index both class and source entries in sequence + List classFileNames = new ArrayList(); + for (Enumeration e = zip.entries(); e.hasMoreElements();) { + if (this.isCancelled) { + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing of " + zip.getName() + " has been cancelled"); //$NON-NLS-1$ //$NON-NLS-2$ + return false; + } + ZipEntry ze = (ZipEntry) e.nextElement(); + String zeName = ze.getName(); + if (Util.isClassFileName(zeName)) { + final byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip); + JavaSearchDocument searchDocument = new JavaSearchDocument(ze, zipFilePath, classFileBytes, participant); + this.manager.indexDocument(searchDocument, participant, index, this.containerPath); +// if (classFileNames.remove(zeName)) { +// String javaFileName = zeName.substring(0, zeName.length()-SuffixConstants.SUFFIX_class.length) + SuffixConstants.SUFFIX_STRING_java; +// sources.put(zeName, javaFileName); +// } else { + classFileNames.add(zeName); +// } + } + else if (Util.isJavaFileName(zeName)) { + String classFileName = zeName.substring(0, zeName.length()-SuffixConstants.SUFFIX_java.length) + SuffixConstants.SUFFIX_STRING_class; + if (classFileNames.remove(classFileName)) { + int idx = classFileName.lastIndexOf('/'); + String sourceName = zeName.substring(idx+1); + sources.put(classFileName, sourceName); +// } else { +// classFileNames.add(classFileName); + } + } + } + } else { + // common case where there's no sources or they are in another location for (Enumeration e = zip.entries(); e.hasMoreElements();) { if (this.isCancelled) { - if (JobManager.VERBOSE) + if (verbose) org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing of " + zip.getName() + " has been cancelled"); //$NON-NLS-1$ //$NON-NLS-2$ return false; } - // iterate each entry to index it + // only look at the class file entries ZipEntry ze = (ZipEntry) e.nextElement(); - if (Util.isClassFileName(ze.getName())) { + String zeName = ze.getName(); + if (Util.isClassFileName(zeName)) { + + // read class file contents final byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip); - JavaSearchDocument entryDocument = new JavaSearchDocument(ze, zipFilePath, classFileBytes, participant); - this.manager.indexDocument(entryDocument, participant, index, this.containerPath); + String documentPath = zipFilePath + IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR + ze; + ClassFileReader classFileReader; + try { + classFileReader = new ClassFileReader(classFileBytes, documentPath.toCharArray()); + } catch (ClassFormatException e1) { + // invalid class file + continue; + } + + // index the class file + JavaSearchDocument searchDocument = new JavaSearchDocument(ze, zipFilePath, classFileBytes, participant); + searchDocument.classFileReader = classFileReader; + this.manager.indexDocument(searchDocument, participant, index, this.containerPath); + + // store source name if any + if (this.sourcePath != null) { + char[] sourceFileName = classFileReader.sourceFileName(); + if (sourceFileName == null) { + // try by changing the file extension to .java + sourceFileName = (zeName.substring(0, zeName.length()-SuffixConstants.SUFFIX_java.length) + SuffixConstants.SUFFIX_STRING_class).toCharArray(); + } + if (sourceFileName != null) { + String sourceName = new String(sourceFileName); + if (classFileReader.getInnerSourceName() == null && classFileReader.getEnclosingTypeName() == null) { + // only store source for top level types + sources.put(zeName, sourceName); + } + } + } } } + } + if (sources == null || sources.size() == 0) { + // save index of jar file with attached source will be done while indexing source + // otherwise there will be a problem during the merge of the source index + // which contains only names and field references this.manager.saveIndex(index); - if (JobManager.VERBOSE) - org.eclipse.jdt.internal.core.util.Util.verbose("-> done indexing of " //$NON-NLS-1$ - + zip.getName() + " (" //$NON-NLS-1$ - + (System.currentTimeMillis() - initialTime) + "ms)"); //$NON-NLS-1$ - } finally { - if (zip != null) { - if (JavaModelManager.ZIP_ACCESS_VERBOSE) - System.out.println("(" + Thread.currentThread() + ") [AddJarFileToIndex.execute()] Closing ZipFile " + zip); //$NON-NLS-1$ //$NON-NLS-2$ - zip.close(); - } - monitor.exitWrite(); // free write lock - } - } catch (IOException e) { - if (JobManager.VERBOSE) { - org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + this.containerPath + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$ - e.printStackTrace(); + } else { + // create request to index stored sources + AddSourceZipToIndex zipToIndex = new AddSourceZipToIndex(this, zipFilePath, sources); + this.manager.request(zipToIndex, JobManager.LOW_PRIORITY); } - this.manager.removeIndex(this.containerPath); - return false; + + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> done indexing of " //$NON-NLS-1$ + + zip.getName() + " (" //$NON-NLS-1$ + + (System.currentTimeMillis() - initialTime) + "ms)"); //$NON-NLS-1$ + } finally { + modelManager.flushZipFiles(); + monitor.exitWrite(); // free write lock } - return true; - } - public String getJobFamily() { - if (this.resource != null) - return super.getJobFamily(); - return this.containerPath.toOSString(); // external jar - } - protected Integer updatedIndexState() { - return IndexManager.REBUILDING_STATE; - } - public String toString() { - return "indexing " + this.containerPath.toString(); //$NON-NLS-1$ + } catch (IOException e) { + if (verbose) { + org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + this.containerPath + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$ + e.printStackTrace(); + } + this.manager.removeIndex(this.containerPath); + return false; } + return true; +} +public String getJobFamily() { + if (this.resource != null) + return super.getJobFamily(); + return this.containerPath.toOSString(); // external jar +} +protected Integer updatedIndexState() { + return IndexManager.REBUILDING_STATE; +} +public String toString() { + return "indexing " + this.containerPath.toString(); //$NON-NLS-1$ +} } Index: search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java,v retrieving revision 1.68 diff -u -r1.68 BinaryIndexer.java --- search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java 27 Jun 2008 16:03:49 -0000 1.68 +++ search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java 4 Nov 2008 15:31:20 -0000 @@ -27,6 +27,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.search.JavaSearchDocument; import org.eclipse.jdt.internal.core.util.Util; public class BinaryIndexer extends AbstractIndexer implements SuffixConstants { @@ -616,11 +617,29 @@ public void indexDocument() { try { final byte[] contents = this.document.getByteContents(); - // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=107124 // contents can potentially be null if a IOException occurs while retrieving the contents - if (contents == null) return; + // (see see https://bugs.eclipse.org/bugs/show_bug.cgi?id=107124) + // or when there's an attached source (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=12044) + ClassFileReader reader = null; + if (this.document instanceof JavaSearchDocument) { + JavaSearchDocument javaSearchDocument = (JavaSearchDocument) this.document; + if (contents == null) { + if (javaSearchDocument.sourcePath != null) { + char[] source = javaSearchDocument.getCharContents(); + if (source.length != 0) { + // the source exists, then prefer to index it instead of the class file + SourceIndexer sourceIndexer = new SourceIndexer(this.document); + sourceIndexer.indexDocument(); + } + } + return; + } + reader = javaSearchDocument.classFileReader; + } final String path = this.document.getPath(); - ClassFileReader reader = new ClassFileReader(contents, path == null ? null : path.toCharArray()); + if (reader == null) { + reader = new ClassFileReader(contents, path == null ? null : path.toCharArray()); + } // first add type references char[] className = replace('/', '.', reader.getName()); // looks like java/lang/String Index: search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java,v retrieving revision 1.45 diff -u -r1.45 SourceIndexer.java --- search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java 18 Sep 2008 07:53:32 -0000 1.45 +++ search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java 4 Nov 2008 15:31:20 -0000 @@ -16,6 +16,7 @@ import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.search.SearchDocument; +import org.eclipse.jdt.internal.compiler.ISourceElementRequestor; import org.eclipse.jdt.internal.compiler.SourceElementParser; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.JavaModelManager; @@ -42,8 +43,14 @@ } public void indexDocument() { // Create a new Parser - SourceIndexerRequestor requestor = new SourceIndexerRequestor(this); String documentPath = this.document.getPath(); + ISourceElementRequestor requestor; + if (documentPath.endsWith(SUFFIX_STRING_class)) { + // specific case of attached source, use a reduced requestor + requestor = new AttachedSourceIndexerRequestor(this); + } else { + requestor = new SourceIndexerRequestor(this); + } SourceElementParser parser = this.document.getParser(); if (parser == null) { IPath path = new Path(documentPath); Index: model/org/eclipse/jdt/internal/core/DeltaProcessor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java,v retrieving revision 1.330 diff -u -r1.330 DeltaProcessor.java --- model/org/eclipse/jdt/internal/core/DeltaProcessor.java 9 Oct 2008 08:13:07 -0000 1.330 +++ model/org/eclipse/jdt/internal/core/DeltaProcessor.java 4 Nov 2008 15:31:18 -0000 @@ -862,8 +862,9 @@ continue; } for (int j = 0; j < entries.length; j++){ - if (entries[j].getEntryKind() == IClasspathEntry.CPE_LIBRARY) { - IPath entryPath = entries[j].getPath(); + IClasspathEntry entry = entries[j]; + if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { + IPath entryPath = entry.getPath(); if (!archivePathsToRefresh.contains(entryPath)) continue; // not supposed to be refreshed @@ -902,7 +903,7 @@ // first remove the index so that it is forced to be re-indexed this.manager.indexManager.removeIndex(entryPath); // then index the jar - this.manager.indexManager.indexLibrary(entryPath, project.getProject()); + this.manager.indexManager.indexLibrary(entry, javaProject); } else { externalArchivesStatus.put(entryPath, EXTERNAL_JAR_UNCHANGED); } @@ -913,7 +914,7 @@ externalArchivesStatus.put(entryPath, EXTERNAL_JAR_ADDED); this.state.getExternalLibTimeStamps().put(entryPath, new Long(newTimeStamp)); // index the new jar - this.manager.indexManager.indexLibrary(entryPath, project.getProject()); + this.manager.indexManager.indexLibrary(entry, javaProject); } } } else { // internal JAR @@ -2503,13 +2504,13 @@ switch (delta.getKind()) { case IResourceDelta.ADDED: // index the new jar - indexManager.indexLibrary(jarPath, root.getJavaProject().getProject()); + indexManager.indexPackageFragmentRoot(root); break; case IResourceDelta.CHANGED: // first remove the index so that it is forced to be re-indexed indexManager.removeIndex(jarPath); // then index the jar - indexManager.indexLibrary(jarPath, root.getJavaProject().getProject()); + indexManager.indexPackageFragmentRoot(root); break; case IResourceDelta.REMOVED: // the jar was physically removed: remove the index Index: model/org/eclipse/jdt/internal/core/SourceMapper.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java,v retrieving revision 1.139 diff -u -r1.139 SourceMapper.java --- model/org/eclipse/jdt/internal/core/SourceMapper.java 29 Aug 2008 18:53:24 -0000 1.139 +++ model/org/eclipse/jdt/internal/core/SourceMapper.java 4 Nov 2008 15:31:19 -0000 @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -24,7 +23,6 @@ import java.util.zip.ZipFile; import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; @@ -897,43 +895,70 @@ } else { newFullName = name; } - return this.findSource(newFullName); + return org.eclipse.jdt.internal.core.util.Util.findSource(this.sourcePath, newFullName); } - public char[] findSource(String fullName) { - char[] source = null; - Object target = JavaModel.getTarget(this.sourcePath, true); - if (target instanceof IContainer) { - IResource res = ((IContainer)target).findMember(fullName); - if (res instanceof IFile) { - try { - source = org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray((IFile)res); - } catch (JavaModelException e) { - // ignore - } - } - } else { - // try to get the entry - ZipEntry entry = null; - ZipFile zip = null; - JavaModelManager manager = JavaModelManager.getJavaModelManager(); - try { - zip = manager.getZipFile(this.sourcePath); - entry = zip.getEntry(fullName); - if (entry != null) { - // now read the source code - source = readSource(entry, zip); + /** + * Locates and returns the source file path for the given (binary) type, in this + * SourceMapper's ZIP file, or returns null if source file + * cannot be found. + * The given simpleSourceFileName is the .java file name (without the enclosing + * folder) used to create the given type (e.g. "A.java" for x/y/A$Inner.class) + * + * @see #findSource(IType, String) + */ + public String findSourceFilePath(IType type, String simpleSourceFileName) { + long time = 0; + if (VERBOSE) { + time = System.currentTimeMillis(); + } + PackageFragment pkgFrag = (PackageFragment) type.getPackageFragment(); + String name = org.eclipse.jdt.internal.core.util.Util.concatWith(pkgFrag.names, simpleSourceFileName, '/'); + + String sourceFilePath = null; + + if (this.rootPath != null) { + sourceFilePath = findSourceFilePath(this.rootPath, name); + } + + if (sourceFilePath == null) { + computeAllRootPaths(type); + if (this.rootPaths != null) { + loop: for (Iterator iterator = this.rootPaths.iterator(); iterator.hasNext(); ) { + String currentRootPath = (String) iterator.next(); + if (!currentRootPath.equals(this.rootPath)) { + sourceFilePath = findSourceFilePath(currentRootPath, name); + if (sourceFilePath != null) { + // remember right root path + this.rootPath = currentRootPath; + break loop; + } + } } - } catch (CoreException e) { - return null; - } finally { - manager.closeZipFile(zip); // handle null case } } - return source; + if (VERBOSE) { + System.out.println("spent " + (System.currentTimeMillis() - time) + "ms for " + type.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ + } + return sourceFilePath; } - + private String findSourceFilePath(String currentRootPath, String name) { + String newFullName; + if (!currentRootPath.equals(IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH)) { + if (currentRootPath.endsWith("/")) { //$NON-NLS-1$ + newFullName = currentRootPath + name; + } else { + newFullName = currentRootPath + '/' + name; + } + } else { + newFullName = name; + } + if (org.eclipse.jdt.internal.core.util.Util.findSource(this.sourcePath, newFullName, false/* do not get contents */) != null) { + return newFullName; + } + return null; + } /** * Returns the SourceRange for the name of the given element, or @@ -1245,17 +1270,6 @@ this.typeDepth = -1; } } - private char[] readSource(ZipEntry entry, ZipFile zip) { - try { - byte[] bytes = Util.getZipEntryByteContent(entry, zip); - if (bytes != null) { - return Util.bytesToChar(bytes, this.encoding); - } - } catch (IOException e) { - // ignore - } - return null; - } /** * Sets the mapping for this method to its parameter names. Index: model/org/eclipse/jdt/internal/core/ClasspathChange.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java,v retrieving revision 1.22 diff -u -r1.22 ClasspathChange.java --- model/org/eclipse/jdt/internal/core/ClasspathChange.java 9 Oct 2008 08:13:07 -0000 1.22 +++ model/org/eclipse/jdt/internal/core/ClasspathChange.java 4 Nov 2008 15:31:18 -0000 @@ -457,6 +457,8 @@ int newLength = newResolvedClasspath.length; int oldLength = this.oldResolvedClasspath.length; + + // First, look at removed classpath entries for (int i = 0; i < oldLength; i++) { int index = classpathContains(newResolvedClasspath, this.oldResolvedClasspath[i]); if (index == -1) { @@ -487,20 +489,22 @@ } } + // Then, look at created or changed classpath entries for (int i = 0; i < newLength; i++) { - int index = classpathContains(this.oldResolvedClasspath, newResolvedClasspath[i]); + IClasspathEntry entry = newResolvedClasspath[i]; + int index = classpathContains(this.oldResolvedClasspath, entry); if (index == -1) { // remote projects are not indexed in this project - if (newResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT){ + if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT){ continue; } - // Request indexing - int entryKind = newResolvedClasspath[i].getEntryKind(); + // Request indexing for the removed classpath entry + int entryKind = entry.getEntryKind(); switch (entryKind) { case IClasspathEntry.CPE_LIBRARY: boolean pathHasChanged = true; - IPath newPath = newResolvedClasspath[i].getPath(); + IPath newPath = entry.getPath(); for (int j = 0; j < oldLength; j++) { IClasspathEntry oldEntry = this.oldResolvedClasspath[j]; if (oldEntry.getPath().equals(newPath)) { @@ -509,11 +513,10 @@ } } if (pathHasChanged) { - indexManager.indexLibrary(newPath, this.project.getProject()); + indexManager.indexLibrary(entry, this.project); } break; case IClasspathEntry.CPE_SOURCE: - IClasspathEntry entry = newResolvedClasspath[i]; IPath path = entry.getPath(); char[][] inclusionPatterns = ((ClasspathEntry)entry).fullInclusionPatternChars(); char[][] exclusionPatterns = ((ClasspathEntry)entry).fullExclusionPatternChars(); @@ -521,6 +524,18 @@ break; } } + // since bug 12044, source attached are indexed instead of class files + // hence needs to verify whether the source attachment has changed or not + else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { + IPath srcPath = entry.getSourceAttachmentPath(); + IPath oldSrcPath = this.oldResolvedClasspath[index].getSourceAttachmentPath(); + Util.verbose("Classpath entry src change from "+oldSrcPath+" to "+srcPath); + if (srcPath == null && oldSrcPath != null) { + indexManager.removeIndex(entry.getPath()); + } else if (srcPath != null && !srcPath.equals(oldSrcPath)) { + indexManager.indexLibrary(entry, this.project); + } + } } } Index: model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java,v retrieving revision 1.133 diff -u -r1.133 PackageFragmentRoot.java --- model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java 27 Jun 2008 16:03:50 -0000 1.133 +++ model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java 4 Nov 2008 15:31:18 -0000 @@ -573,7 +573,16 @@ */ public IPath getSourceAttachmentPath() throws JavaModelException { if (getKind() != K_BINARY) return null; + return sourceAttachmentPath(); +} +/** + * @see IPackageFragmentRoot#getSourceAttachmentPath() + * Returns the same result but without opening the package fragment root. + * However, no verification is done on the package fragment root kind, hence + * provides an unpredictable result if it is not a {@link IPackageFragmentRoot#K_BINARY}. + */ +public IPath sourceAttachmentPath() throws JavaModelException { // 1) look source attachment property (set iff attachSource(...) was called IPath path = getPath(); String serverPathString= Util.getSourceAttachmentProperty(path); @@ -618,7 +627,16 @@ */ public IPath getSourceAttachmentRootPath() throws JavaModelException { if (getKind() != K_BINARY) return null; + return sourceAttachmentRootPath(); +} +/** + * @see IPackageFragmentRoot#getSourceAttachmentRootPath() + * Returns the same result but without opening the package fragment root. + * However, no verification is done on the package fragment root kind, hence + * provides an unpredictable result if it is not a {@link IPackageFragmentRoot#K_BINARY}. + */ +public IPath sourceAttachmentRootPath() throws JavaModelException { // 1) look source attachment property (set iff attachSource(...) was called IPath path = getPath(); String serverPathString= Util.getSourceAttachmentProperty(path); Index: model/org/eclipse/jdt/internal/core/util/Util.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java,v retrieving revision 1.125 diff -u -r1.125 Util.java --- model/org/eclipse/jdt/internal/core/util/Util.java 23 Oct 2008 13:56:41 -0000 1.125 +++ model/org/eclipse/jdt/internal/core/util/Util.java 4 Nov 2008 15:31:19 -0000 @@ -61,6 +61,7 @@ import org.eclipse.jdt.internal.core.Annotation; import org.eclipse.jdt.internal.core.ClassFile; 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.Member; import org.eclipse.jdt.internal.core.MemberValuePair; @@ -725,6 +726,72 @@ return null; } + /** + * Find source of the file name supposed to be located in the given source path or null + * if the corresponding file was not found. + * + * @param sourcePath The path of the container. May be a zip file or a folder + * @param fileName The full name of the file (e.g. java/lang/Object.java) + * @return The contents of the file or null if the file was not found. + */ + public static char[] findSource(IPath sourcePath, String fileName) { + return (char[]) findSource(sourcePath, fileName, true/*get contents*/); + } + + /** + * Find the file supposed to be located in the given source path + * or null if the corresponding file was not found. + * + * @param sourcePath The path of the container. May be a zip file or a folder + * @param fileName The full name of the file (e.g. java/lang/Object.java) + * @param contents Indicates whether the method returns the file or its contents + * @return If the file was found then returns a {@link IResource} or a + * {@link ZipEntry} if no contents was required, char[] otherwise. + * Returns null if the file was not found. + */ + public static Object findSource(IPath sourcePath, String fileName, boolean contents) { + Object target = JavaModel.getTarget(sourcePath, true); + if (target instanceof IContainer) { + IResource res = ((IContainer)target).findMember(fileName); + if (res instanceof IFile) { + if (contents) { + try { + return org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray((IFile)res); + } catch (JavaModelException e) { + // skip + } + return null; + } + return res; + } + } else { + // try to get the entry + ZipEntry entry = null; + ZipFile zip = null; + JavaModelManager manager = JavaModelManager.getJavaModelManager(); + try { + zip = manager.getZipFile(sourcePath); + entry = zip.getEntry(fileName); + if (entry != null) { + if (contents) { + try { + return org.eclipse.jdt.internal.compiler.util.Util.getZipEntryCharContent(entry, zip, null/*default charset encoding*/); + } catch (IOException e) { + // skip + } + return null; + } + return entry; + } + } catch (CoreException e) { + return null; + } finally { + manager.closeZipFile(zip); // handle null case + } + } + return null; + } + public static IClassFileAttribute getAttribute(IClassFileReader classFileReader, char[] attributeName) { IClassFileAttribute[] attributes = classFileReader.getAttributes(); for (int i = 0, max = attributes.length; i < max; i++) { Index: search/org/eclipse/jdt/internal/core/index/Index.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/Index.java,v retrieving revision 1.31 diff -u -r1.31 Index.java --- search/org/eclipse/jdt/internal/core/index/Index.java 27 Jun 2008 16:04:14 -0000 1.31 +++ search/org/eclipse/jdt/internal/core/index/Index.java 4 Nov 2008 15:31:19 -0000 @@ -172,6 +172,16 @@ public void remove(String containerRelativePath) { this.memoryIndex.remove(containerRelativePath); } +/** + * Reset memory and disk indexes. + * + * @throws IOException + */ +public void reset(boolean reuseExistingFile) throws IOException { + this.memoryIndex = new MemoryIndex(); + this.diskIndex = new DiskIndex(this.diskIndex.indexFile.getCanonicalPath()); + this.diskIndex.initialize(reuseExistingFile); +} public void save() throws IOException { // must own the write lock of the monitor if (!hasChanged()) return; Index: search/org/eclipse/jdt/internal/core/index/DiskIndex.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/DiskIndex.java,v retrieving revision 1.67 diff -u -r1.67 DiskIndex.java --- search/org/eclipse/jdt/internal/core/index/DiskIndex.java 27 Jun 2008 16:04:14 -0000 1.67 +++ search/org/eclipse/jdt/internal/core/index/DiskIndex.java 4 Nov 2008 15:31:19 -0000 @@ -46,7 +46,7 @@ private int streamEnd; // used when writing data from the streamBuffer to the file char separator = Index.DEFAULT_SEPARATOR; -public static final String SIGNATURE= "INDEX VERSION 1.125"; //$NON-NLS-1$ +public static final String SIGNATURE= "INDEX VERSION 1.126.1"; //$NON-NLS-1$ private static final char[] SIGNATURE_CHARS = SIGNATURE.toCharArray(); public static boolean DEBUG = false; @@ -616,6 +616,7 @@ try { if (size < 0) { // DEBUG System.err.println("-------------------- DEBUG --------------------"); //$NON-NLS-1$ + System.err.println("thread = "+Thread.currentThread()); //$NON-NLS-1$ System.err.println("file = "+this.indexFile); //$NON-NLS-1$ System.err.println("offset = "+offset); //$NON-NLS-1$ System.err.println("size = "+size); //$NON-NLS-1$ @@ -626,6 +627,7 @@ // DEBUG oom.printStackTrace(); System.err.println("-------------------- DEBUG --------------------"); //$NON-NLS-1$ + System.err.println("thread = "+Thread.currentThread()); //$NON-NLS-1$ System.err.println("file = "+this.indexFile); //$NON-NLS-1$ System.err.println("offset = "+offset); //$NON-NLS-1$ System.err.println("size = "+size); //$NON-NLS-1$ Index: search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java,v retrieving revision 1.16 diff -u -r1.16 JavaSearchDocument.java --- search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java 27 Jun 2008 16:04:09 -0000 1.16 +++ search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java 4 Nov 2008 15:31:20 -0000 @@ -16,9 +16,13 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchDocument; import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; import org.eclipse.jdt.internal.core.search.processing.JobManager; import org.eclipse.jdt.internal.core.util.Util; @@ -27,6 +31,10 @@ private IFile file; protected byte[] byteContents; protected char[] charContents; + public IPath sourcePath; // path of the folder or the zip file containing the attached source + private String relativeSourcePath; // relative path from the source path + private String containerRelativePath; // same as SearchDocument + public ClassFileReader classFileReader; // may be set by by the AddJarFileToIndex public JavaSearchDocument(String documentPath, SearchParticipant participant) { super(documentPath, participant); @@ -35,9 +43,15 @@ super(zipFilePath + IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR + zipEntry.getName(), participant); this.byteContents = contents; } + public JavaSearchDocument(String documentPath, IPath sourcePath, String filePath, SearchParticipant participant) { + this(documentPath, participant); + this.relativeSourcePath = filePath; + this.sourcePath = sourcePath; + } public byte[] getByteContents() { if (this.byteContents != null) return this.byteContents; + if (this.sourcePath != null) return null; try { return Util.getResourceContentsAsByteArray(getFile()); } catch (JavaModelException e) { @@ -49,14 +63,21 @@ } public char[] getCharContents() { if (this.charContents != null) return this.charContents; + + // The source may be found through an attachment + if (this.sourcePath != null) { + return Util.findSource(this.sourcePath, this.relativeSourcePath); + } + + // Get file contents try { return Util.getResourceContentsAsCharArray(getFile()); } catch (JavaModelException e) { if (BasicSearchEngine.VERBOSE || JobManager.VERBOSE) { // used during search and during indexing e.printStackTrace(); } - return null; } + return null; } public String getEncoding() { // Return the encoding of the associated file @@ -83,4 +104,90 @@ public String toString() { return "SearchDocument for " + getPath(); //$NON-NLS-1$ } + + /** + * Override the super implementation to change the document path + * if there's an attached source and the enclosing type names + * + * @see SearchDocument#addIndexEntry(char[], char[]) + */ + public void addIndexEntry(char[] category, char[] key) { + this.containerRelativePath = null; + if (this.sourcePath != null) { + switch (category[0]) { + case 's': + if (CharOperation.equals(IIndexConstants.SUPER_REF, category)) { + // decode the key to get enclosing type names (see SuperTypeReferencePattern.decodeIndexKey(char[]) + int slash = CharOperation.indexOf(IIndexConstants.SEPARATOR, key); + slash = CharOperation.indexOf(IIndexConstants.SEPARATOR, key, slash+1); + slash = CharOperation.indexOf(IIndexConstants.SEPARATOR, key, slash+1); + if (key[slash+1] != IIndexConstants.SEPARATOR) { + // there's enclosing types + int end = CharOperation.indexOf(IIndexConstants.SEPARATOR, key, slash+1); + String relativePath = getContainerRelativePath(); + char[] enclosingTypes = CharOperation.subarray(key, slash+1, end); + int length = enclosingTypes.length; + if (length != 1 || enclosingTypes[length-1] != '0') { + // neither an anonymous nor a local type + this.containerRelativePath = relativePath.substring(0, relativePath.length()-SuffixConstants.SUFFIX_STRING_class.length()) + + '$' + new String(enclosingTypes) + SuffixConstants.SUFFIX_STRING_class; + } + } + } + break; + case 't': + if (CharOperation.equals(IIndexConstants.TYPE_DECL, category)) { + // decode the key to get enclosing type names (see TypeDeclarationPattern.decodeIndexKey(char[]) + int end = key.length-1; + if (key[end] == 'S') { + end -= 2; + } + end -= 3; + if (key[end] != IIndexConstants.SEPARATOR) { + // there's enclosing types + int slash = CharOperation.lastIndexOf(IIndexConstants.SEPARATOR, key, 0, end); + String relativePath = getContainerRelativePath(); + char[] enclosingTypes = CharOperation.subarray(key, slash+1, end); + int length = enclosingTypes.length; + if (length != 1 || enclosingTypes[length-1] != '0') { + // neither an anonymous nor a local type + this.containerRelativePath = relativePath.substring(0, relativePath.length()-SuffixConstants.SUFFIX_STRING_class.length()) + + '$' + new String(enclosingTypes) + SuffixConstants.SUFFIX_STRING_class; + } + } + } + break; + } + } + super.addIndexEntry(category, key); + } + + /** + * Override the super implementation to take the changed document relative path + * if any. Reset this relative path to avoid any unwanted side effect. + * + * @see SearchDocument#getContainerRelativePath() + */ + protected String getContainerRelativePath() { + if (this.containerRelativePath == null) { + return super.getContainerRelativePath(); + } + String path = this.containerRelativePath; + this.containerRelativePath = null; + return path; + } + + /** + * Override the super implementation not to remove all entries when there's an + * attached source. Attached source indexing is not complete, hence previous + * entries set by the binary indexer need to be kept. + * + * @see SearchDocument#removeAllIndexEntries() + */ + public void removeAllIndexEntries() { + if (this.sourcePath != null) { + return; + } + super.removeAllIndexEntries(); + } } Index: search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java,v retrieving revision 1.57 diff -u -r1.57 BasicSearchEngine.java --- search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java 31 Oct 2008 16:53:33 -0000 1.57 +++ search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java 4 Nov 2008 15:31:20 -0000 @@ -30,6 +30,7 @@ import org.eclipse.jdt.internal.core.*; import org.eclipse.jdt.internal.core.search.indexing.*; import org.eclipse.jdt.internal.core.search.matching.*; +import org.eclipse.jdt.internal.core.search.processing.JobManager; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; @@ -206,6 +207,7 @@ indexManager.performConcurrentJob( new PatternSearchJob(pattern, participant, scope, pathCollector), IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, + JobManager.LOW_PRIORITY, // any search will wait for attached sources indexing to be finished monitor==null ? null : new SubProgressMonitor(monitor, 50)); if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); Index: compiler/org/eclipse/jdt/internal/compiler/util/Util.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java,v retrieving revision 1.68 diff -u -r1.68 Util.java --- compiler/org/eclipse/jdt/internal/compiler/util/Util.java 27 Jun 2008 16:04:05 -0000 1.68 +++ compiler/org/eclipse/jdt/internal/compiler/util/Util.java 4 Nov 2008 15:31:18 -0000 @@ -22,6 +22,9 @@ import java.util.StringTokenizer; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; @@ -450,10 +453,9 @@ } /** * Returns the contents of the given zip entry as a byte array. - * @throws IOException if a problem occured reading the zip entry. + * @throws IOException if a problem occurred reading the zip entry. */ - public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip) - throws IOException { + public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip) throws IOException { InputStream stream = null; try { @@ -470,6 +472,26 @@ } } } + /** + * Returns the contents of the given zip entry as a char array. + * @throws IOException if a problem occurred reading the zip entry or while + * converting the bytes array to chars. + */ + public static char[] getZipEntryCharContent(ZipEntry entry, ZipFile zip, String encoding) throws IOException { + byte[] bytes = getZipEntryByteContent(entry, zip); + if (bytes != null) { + String charEncoding = encoding; + try { + if (encoding == null) { + charEncoding = ResourcesPlugin.getWorkspace().getRoot().getDefaultCharset(); + } + } catch (CoreException e) { + // use no encoding + } + return bytesToChar(bytes, charEncoding); + } + return null; + } /** * Returns whether the given name is potentially a zip archive file name Index: search/org/eclipse/jdt/internal/core/search/indexing/AddSourceZipToIndex.java =================================================================== RCS file: search/org/eclipse/jdt/internal/core/search/indexing/AddSourceZipToIndex.java diff -N search/org/eclipse/jdt/internal/core/search/indexing/AddSourceZipToIndex.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ search/org/eclipse/jdt/internal/core/search/indexing/AddSourceZipToIndex.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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.indexing; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.internal.compiler.SourceElementParser; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.PackageFragmentRoot; +import org.eclipse.jdt.internal.core.SourceMapper; +import org.eclipse.jdt.internal.core.index.Index; +import org.eclipse.jdt.internal.core.search.JavaSearchDocument; +import org.eclipse.jdt.internal.core.search.processing.JobManager; +import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; + +class AddSourceZipToIndex extends IndexRequest { + + private static final char JAR_SEPARATOR = IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR.charAt(0); + SourceMapper sourceMapper; + IPath sourcePath; + Map sources; + IPackageFragmentRoot packageFragmentRoot; + private HashtableOfArrayToObject packageHandles = new HashtableOfArrayToObject(); + IPath zipFilePath; + private IJavaProject javaProject; + +public AddSourceZipToIndex(AddJarFileToIndex jarFileToIndex, IPath zipPath, Map sources) { + // external JAR scenario - no resource + super(jarFileToIndex.containerPath, jarFileToIndex.manager); + this.sourcePath = jarFileToIndex.sourcePath; + this.sources = sources; + this.zipFilePath = zipPath; + this.sourceMapper = jarFileToIndex.sourceMapper; + this.packageFragmentRoot = jarFileToIndex.packageFragmentRoot; + this.javaProject = jarFileToIndex.javaProject; +} + +private IType createType(String classFilePath) throws JavaModelException { + String[] simpleNames = new Path(classFilePath).segments(); + String[] pkgName; + int length = simpleNames.length-1; + if (length > 0) { + pkgName = new String[length]; + System.arraycopy(simpleNames, 0, pkgName, 0, length); + } else { + pkgName = CharOperation.NO_STRINGS; + } + IPackageFragment pkgFragment= (IPackageFragment) this.packageHandles.get(pkgName); + if (pkgFragment == null) { + pkgFragment= ((PackageFragmentRoot)this.packageFragmentRoot).getPackageFragment(pkgName); + this.packageHandles.put(pkgName, pkgFragment); + } + return pkgFragment.getClassFile(simpleNames[length]).getType(); +} + +public boolean equals(Object o) { + if (o instanceof AddSourceZipToIndex) { + if (this.sourcePath != null) { + return this.sourcePath.toOSString().equals(((AddSourceZipToIndex) o).sourcePath.toOSString()); + } + } + return false; +} +public int hashCode() { + if (this.sourcePath != null) + return this.sourcePath.hashCode(); + return -1; +} +public boolean execute(IProgressMonitor progressMonitor) { + + if (this.isCancelled || progressMonitor != null && progressMonitor.isCanceled()) return true; + boolean verbose = true; //JobManager.VERBOSE; + + try { + // always reuse existing index as there's no way to know whether sources has changed or not... + Index index = this.manager.getIndexForUpdate(this.containerPath, true, /*reuse index file*/ true /*create if none*/); + if (index == null) { + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> index could not be created for sources in " + this.containerPath); //$NON-NLS-1$ + return true; + } + ReadWriteMonitor monitor = index.monitor; + if (monitor == null) { + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> index for sources in " + this.containerPath + " just got deleted"); //$NON-NLS-1$//$NON-NLS-2$ + return true; // index got deleted since acquired + } + index.separator = JAR_SEPARATOR; + + JavaModelManager modelManager = JavaModelManager.getJavaModelManager(); + SourceElementParser parser = this.manager.getSourceElementParser(this.javaProject, null/*requestor will be set by indexer*/); + modelManager.cacheZipFiles(); + try { + monitor.enterWrite(); // ask permission to write (TODO see if this lock is necessary) + if (this.isCancelled) { + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing source files in " + this.sourcePath.toString() + " has been cancelled"); //$NON-NLS-1$ //$NON-NLS-2$ + return false; + } + + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing source files in " + this.sourcePath.toString()); //$NON-NLS-1$ + long initialTime = System.currentTimeMillis(); + + // Index the jar for the first time or reindex the jar in case the previous index file has been corrupted + // index already existed: recreate it so that we forget about previous entries + SearchParticipant participant = SearchEngine.getDefaultSearchParticipant(); + index.separator = JAR_SEPARATOR; + + // Index each attached source of the list + Iterator classFileNames = this.sources.keySet().iterator(); + String documentPrefix = this.zipFilePath.toString() + IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR; + while (classFileNames.hasNext()) { + String classFileName = (String) classFileNames.next(); + String sourceName = (String) this.sources.get(classFileName); + + // first get type + IType type = null; + try { + if (this.packageFragmentRoot != null) { + type = createType(classFileName); + } + } catch (JavaModelException jme) { + // ignore + } + if (type != null) { + // get path of the attached source file + String sourceFilePath = this.sourceMapper.findSourceFilePath(type, sourceName); + if (sourceFilePath != null) { + // index the document using the top level class file for its name + String documentPath = documentPrefix + classFileName; + JavaSearchDocument searchDocument = new JavaSearchDocument(documentPath, this.sourcePath, sourceFilePath, participant); + searchDocument.setParser(parser); + this.manager.indexDocument(searchDocument, participant, index, this.containerPath); + } + } + } + this.manager.saveIndex(index); + if (verbose) + org.eclipse.jdt.internal.core.util.Util.verbose("-> done indexing source files of " //$NON-NLS-1$ + + this.sourcePath.toString() + " (" //$NON-NLS-1$ + + (System.currentTimeMillis() - initialTime) + "ms)"); //$NON-NLS-1$ + } finally { + modelManager.flushZipFiles(); + monitor.exitWrite(); // free write lock + } + } catch (IOException e) { + if (verbose) { + org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index sources in " + this.containerPath + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$ + e.printStackTrace(); + } + this.manager.removeIndex(this.containerPath); + return false; + } + return true; +} +protected Integer updatedIndexState() { + return IndexManager.REBUILDING_STATE; +} +public String toString() { + return "indexing " + this.sourcePath.toString(); //$NON-NLS-1$ +} +} Index: search/org/eclipse/jdt/internal/core/search/indexing/AttachedSourceIndexerRequestor.java =================================================================== RCS file: search/org/eclipse/jdt/internal/core/search/indexing/AttachedSourceIndexerRequestor.java diff -N search/org/eclipse/jdt/internal/core/search/indexing/AttachedSourceIndexerRequestor.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ search/org/eclipse/jdt/internal/core/search/indexing/AttachedSourceIndexerRequestor.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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.indexing; + +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.internal.compiler.ISourceElementRequestor; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; + +/** + * This class is used by the JavaParserIndexer. When parsing the java file, the requestor + * recognizes the java elements (methods, fields, ...) and add them to an index. + */ +public class AttachedSourceIndexerRequestor implements ISourceElementRequestor, IIndexConstants { + SourceIndexer indexer; + +public AttachedSourceIndexerRequestor(SourceIndexer indexer) { + this.indexer = indexer; +} +/** + * @see ISourceElementRequestor#acceptAnnotationTypeReference(char[][], int, int) + */ +public void acceptAnnotationTypeReference(char[][] typeName, int sourceStart, int sourceEnd) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptAnnotationTypeReference(char[], int) + */ +public void acceptAnnotationTypeReference(char[] simpleTypeName, int sourcePosition) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptConstructorReference(char[], int, int) + */ +public void acceptConstructorReference(char[] typeName, int argCount, int sourcePosition) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptFieldReference(char[], int) + */ +public void acceptFieldReference(char[] fieldName, int sourcePosition) { + this.indexer.addFieldReference(fieldName); +} +/** + * @see ISourceElementRequestor#acceptImport(int, int, char[][], boolean, int) + */ +public void acceptImport(int declarationStart, int declarationEnd, char[][] tokens, boolean onDemand, int modifiers) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptLineSeparatorPositions(int[]) + */ +public void acceptLineSeparatorPositions(int[] positions) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptMethodReference(char[], int, int) + */ +public void acceptMethodReference(char[] methodName, int argCount, int sourcePosition) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptPackage(ImportReference) + */ +public void acceptPackage(ImportReference importReference) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptProblem(CategorizedProblem) + */ +public void acceptProblem(CategorizedProblem problem) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptTypeReference(char[][], int, int) + */ +public void acceptTypeReference(char[][] typeName, int sourceStart, int sourceEnd) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptTypeReference(char[], int) + */ +public void acceptTypeReference(char[] simpleTypeName, int sourcePosition) { + // already done while indexing the associated class file +} +/** + * @see ISourceElementRequestor#acceptUnknownReference(char[][], int, int) + */ +public void acceptUnknownReference(char[][] name, int sourceStart, int sourceEnd) { + for (int i = 0; i < name.length; i++) { + this.indexer.addNameReference(name[i]); + } +} +/** + * @see ISourceElementRequestor#acceptUnknownReference(char[], int) + */ +public void acceptUnknownReference(char[] name, int sourcePosition) { + this.indexer.addNameReference(name); +} +public void enterCompilationUnit() { + // do nothing +} +public void enterConstructor(MethodInfo methodInfo) { + // do nothing +} +public void enterField(FieldInfo fieldInfo) { + // do nothing +} +public void enterInitializer(int declarationStart, int modifiers) { + // do nothing +} +public void enterMethod(MethodInfo methodInfo) { + // do nothing +} +public void enterType(TypeInfo typeInfo) { + // do nothing +} +public void exitCompilationUnit(int declarationEnd) { + // do nothing +} +public void exitConstructor(int declarationEnd) { + // do nothing +} +public void exitField(int initializationStart, int declarationEnd, int declarationSourceEnd) { + // do nothing +} +public void exitInitializer(int declarationEnd) { + // do nothing +} +public void exitMethod(int declarationEnd, Expression defaultValue) { + // do nothing +} +public void exitType(int declarationEnd) { + // do nothing +} +} #P org.eclipse.jdt.core.tests.model Index: src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java,v retrieving revision 1.227 diff -u -r1.227 AbstractJavaModelTests.java --- src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java 23 Oct 2008 13:56:38 -0000 1.227 +++ src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java 4 Nov 2008 15:31:22 -0000 @@ -436,8 +436,11 @@ protected void addLibraryEntry(String path, boolean exported) throws JavaModelException { addLibraryEntry(this.currentProject, new Path(path), null, null, null, null, exported); } + protected void addLibraryEntry(IJavaProject project, String path) throws JavaModelException { + addLibraryEntry(project, new Path(path), null, null, null, null, false); + } protected void addLibraryEntry(IJavaProject project, String path, boolean exported) throws JavaModelException { - addLibraryEntry(project, new Path(path), exported); + addLibraryEntry(project, new Path(path), null, null, null, null, exported); } protected void addLibraryEntry(IJavaProject project, IPath path, boolean exported) throws JavaModelException { addLibraryEntry(project, path, null, null, null, null, exported); @@ -2872,4 +2875,24 @@ } catch (CoreException e) { } } + + /** + * Following method might be carefully used while debugging tests when it's necessary + * to be sure that all indexing jobs are finished (including low priority ones) + * @deprecated To get a warning when used... + */ + public static void waitUntilAllIndexesReady() { + // dummy query for waiting until the indexes are ready + SearchEngine engine = new SearchEngine(); + IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); + try { + SearchPattern pattern = SearchPattern.createPattern("!@$#!@", IJavaSearchConstants.FIELD, IJavaSearchConstants.REFERENCES, SearchPattern.R_EXACT_MATCH); + SearchRequestor requestor = new SearchRequestor() { + public void acceptSearchMatch(SearchMatch match) throws CoreException { + } + }; + engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope, requestor, null); + } catch (CoreException e) { + } + } } Index: src/org/eclipse/jdt/core/tests/model/JavaSearchBugsTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchBugsTests.java,v retrieving revision 1.162 diff -u -r1.162 JavaSearchBugsTests.java --- src/org/eclipse/jdt/core/tests/model/JavaSearchBugsTests.java 23 Oct 2008 17:39:32 -0000 1.162 +++ src/org/eclipse/jdt/core/tests/model/JavaSearchBugsTests.java 4 Nov 2008 15:31:23 -0000 @@ -130,6 +130,191 @@ } /** + * @bug 12044: [search] Reference to package is not found in qualified annotation + * @test Ensure that references to package are also found in qualified annotation + * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=12044" + */ +private String[] sourcesBug12044() throws CoreException { + return new String[] { + "Def.java", + "public class Def {\n" + + " public static final int ONE = 1;\n" + + " public static final int TWO = 2;\n" + + " public static final int THREE = 3;\n" + + "}\n", + "Ref.java", + "public class Ref {\n" + + " class Sub {\n" + + " int x = Def.ONE;\n" + + " int getX() {\n" + + " return x;\n" + + " }\n" + + " }\n" + + " void foo(Sub sub) {\n" + + " switch (sub.getX()) {\n" + + " case Def.ONE:\n" + + " break;\n" + + " case Def.TWO:\n" + + " break;\n" + + " case Def.THREE:\n" + + " break;\n" + + " }\n" + + " }\n" + + "}\n" + }; +} +//static { TESTS_PREFIX = "testBug12044"; } +public void testBug12044_no_src() throws CoreException, IOException { + IJavaProject javaProject = null; + try { + javaProject = createJavaProject("b012044", new String[] { "src" }, new String[] {"JCL15_LIB" }, "bin", "1.5"); + String jarPath = getWorkspacePath()+"b012044"+File.separator+"b012044.jar"; + createJar(sourcesBug12044(), jarPath); + refresh(javaProject); + String resourcePath = "/b012044/b012044.jar"; + addLibraryEntry(javaProject, resourcePath); + IClassFile classFile = getClassFile("b012044", resourcePath, "", "Def.class"); + IField field = classFile.getType().getField("ONE"); + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject } ); + this.resultCollector.showJarFile(); + search(field, ALL_OCCURRENCES, scope); + assertSearchResults( + "b012044.jar|Def.class Def.ONE [No source] EXACT_MATCH" + ); + } + finally { + if (javaProject != null) { + deleteProject(javaProject); + } + } +} +// jar with sources inside +public void testBug12044_src_in_jar() throws CoreException, IOException { + IJavaProject javaProject = null; + try { + javaProject = createJavaProject("b012044", new String[] { "src" }, new String[] {"JCL15_LIB" }, "bin", "1.5"); + String path = getWorkspacePath()+"b012044"+File.separator+"b012044"; + String jarPath = path+".jar"; + Util.createJar(sourcesBug12044(), null, jarPath, null/*no classpath*/, "1.4", true/*include sources*/); + refresh(javaProject); + String resourcePath = "/b012044/b012044.jar"; + addLibraryEntry(javaProject, resourcePath); + IClassFile classFile = getClassFile("b012044", resourcePath, "", "Def.class"); + IField field = classFile.getType().getField("ONE"); + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject } ); + this.resultCollector.showJarFile(); + search(field, ALL_OCCURRENCES, scope); + assertSearchResults( + "b012044.jar|Def.class Def.ONE EXACT_MATCH\n" + + "b012044.jar|Ref.class void Ref.foo(Ref.Sub) EXACT_MATCH\n" + + "b012044.jar|Ref$Sub.class Ref$Sub.x EXACT_MATCH" + ); + } + finally { + if (javaProject != null) { + deleteProject(javaProject); + } + } +} +// jar with sources in another zip file +public void testBug12044_src_in_zip() throws CoreException, IOException { + IJavaProject javaProject = null; + try { + javaProject = createJavaProject("b012044", new String[] { "src" }, new String[] {"JCL15_LIB" }, "bin", "1.5"); + String path = getWorkspacePath()+"b012044"+File.separator+"b012044"; + String jarPath = path+".jar"; + createJar(sourcesBug12044(), jarPath); + String zipPath = path+".zip"; + Util.createSourceZip(sourcesBug12044(), zipPath); + refresh(javaProject); + String resourcePath = "/b012044/b012044.jar"; + String resourceZipPath = "/b012044/b012044.zip"; + addLibraryEntry(javaProject, resourcePath, resourceZipPath); + IClassFile classFile = getClassFile("b012044", resourcePath, "", "Def.class"); + IField field = classFile.getType().getField("ONE"); + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject } ); + this.resultCollector.showJarFile(); + search(field, ALL_OCCURRENCES, scope); + assertSearchResults( + "b012044.jar|Def.class Def.ONE EXACT_MATCH\n" + + "b012044.jar|Ref.class void Ref.foo(Ref.Sub) EXACT_MATCH\n" + + "b012044.jar|Ref$Sub.class Ref$Sub.x EXACT_MATCH" + ); + } + finally { + if (javaProject != null) { + deleteProject(javaProject); + } + } +} +public void testBug12044_src_in_zip_string_pattern() throws CoreException, IOException { + IJavaProject javaProject = null; + try { + javaProject = createJavaProject("b012044", new String[] { "src" }, new String[] {"JCL15_LIB" }, "bin", "1.5"); + String path = getWorkspacePath()+"b012044"+File.separator+"b012044"; + String jarPath = path+".jar"; + createJar(sourcesBug12044(), jarPath); + String zipPath = path+".zip"; + Util.createSourceZip(sourcesBug12044(), zipPath); + refresh(javaProject); + String resourcePath = "/b012044/b012044.jar"; + String resourceZipPath = "/b012044/b012044.zip"; + addLibraryEntry(javaProject, resourcePath, resourceZipPath); + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject } ); + this.resultCollector.showJarFile(); + search("ONE", FIELD, ALL_OCCURRENCES, scope); + assertSearchResults( + "b012044.jar|Def.class Def.ONE EXACT_MATCH\n" + + "b012044.jar|Ref.class void Ref.foo(Ref.Sub) EXACT_MATCH\n" + + "b012044.jar|Ref$Sub.class Ref$Sub.x EXACT_MATCH" + ); + } + finally { + if (javaProject != null) { + deleteProject(javaProject); + } + } +} +// jar with sources in a folder +public void testBug12044_src_in_folder() throws CoreException, IOException { + IJavaProject javaProject = null, javaProjectBis = null; + try { + javaProject = createJavaProject("b012044", new String[] { "src" }, new String[] {"JCL15_LIB" }, "bin", "1.5"); + String[] sources = sourcesBug12044(); + for (int i=0; itrue. Index: src/org/eclipse/jdt/core/tests/compiler/regression/Requestor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/Requestor.java,v retrieving revision 1.21 diff -u -r1.21 Requestor.java --- src/org/eclipse/jdt/core/tests/compiler/regression/Requestor.java 27 Jun 2008 16:04:44 -0000 1.21 +++ src/org/eclipse/jdt/core/tests/compiler/regression/Requestor.java 4 Nov 2008 15:31:25 -0000 @@ -20,6 +20,7 @@ import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; public class Requestor extends Assert implements ICompilerRequestor { public boolean hasErrors = false; @@ -30,6 +31,7 @@ public ICompilerRequestor clientRequestor; public boolean showCategory = false; public boolean showWarningToken = false; + public boolean writeSource = false; public Requestor(boolean forceOutputGeneration, ICompilerRequestor clientRequestor, boolean showCategory, boolean showWarningToken) { this.forceOutputGeneration = forceOutputGeneration; @@ -52,10 +54,12 @@ for (int i = 0, fileCount = classFiles.length; i < fileCount; i++) { // retrieve the key and the corresponding classfile ClassFile classFile = classFiles[i]; - String relativeName = - new String(classFile.fileName()).replace('/', File.separatorChar) + ".class"; + String relativeName = new String(classFile.fileName()).replace('/', File.separatorChar); try { - org.eclipse.jdt.internal.compiler.util.Util.writeToDisk(true, this.outputPath, relativeName, classFile); + org.eclipse.jdt.internal.compiler.util.Util.writeToDisk(true, this.outputPath, relativeName+SuffixConstants.SUFFIX_STRING_class, classFile); + if (this.writeSource && !classFile.isNestedType) { + Util.writeToFile(unitResult.compilationUnit.getContents(), this.outputPath+File.separator+relativeName+SuffixConstants.SUFFIX_STRING_java); + } } catch(IOException e) { e.printStackTrace(); } Index: src/org/eclipse/jdt/core/tests/util/Util.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java,v retrieving revision 1.74 diff -u -r1.74 Util.java --- src/org/eclipse/jdt/core/tests/util/Util.java 23 Oct 2008 13:56:34 -0000 1.74 +++ src/org/eclipse/jdt/core/tests/util/Util.java 4 Nov 2008 15:31:25 -0000 @@ -155,6 +155,9 @@ compile(pathsAndContents, options, null, outputPath); } public static void compile(String[] pathsAndContents, Map options, String[] classpath, String outputPath) { + compile(pathsAndContents, options, classpath, outputPath, false/*do not write sources*/); +} +public static void compile(String[] pathsAndContents, Map options, String[] classpath, String outputPath, boolean writeSource) { IProblemFactory problemFactory = new DefaultProblemFactory(Locale.getDefault()); Requestor requestor = new Requestor( @@ -163,6 +166,7 @@ false, /* show category */ false /* show warning token*/); requestor.outputPath = outputPath.endsWith(File.separator) ? outputPath : outputPath + File.separator; + requestor.writeSource = writeSource; String[] classLibs = getJavaClassLibs(); if (classpath != null) { @@ -336,11 +340,14 @@ createJar(pathsAndContents, null, options, null, jarPath); } public static void createJar(String[] pathsAndContents, String[] extraPathsAndContents, Map options, String[] classpath, String jarPath) throws IOException { + createJar(pathsAndContents, extraPathsAndContents, options, classpath, jarPath, false/*do not write source*/); +} +public static void createJar(String[] pathsAndContents, String[] extraPathsAndContents, Map options, String[] classpath, String jarPath, boolean writeSource) throws IOException { String classesPath = getOutputDirectory() + File.separator + "classes"; File classesDir = new File(classesPath); flushDirectoryContent(classesDir); if (pathsAndContents != null) { - compile(pathsAndContents, options, classpath, classesPath); + compile(pathsAndContents, options, classpath, classesPath, writeSource); } if (extraPathsAndContents != null) { for (int i = 0, l = extraPathsAndContents == null ? 0 : extraPathsAndContents.length; i < l; /* inc in loop */) { @@ -358,7 +365,10 @@ createJar(javaPathsAndContents, extraPathsAndContents, jarPath, null/*no classpath*/, compliance); } public static void createJar(String[] javaPathsAndContents, String[] extraPathsAndContents, String jarPath, String[] classpath, String compliance) throws IOException { - createJar(javaPathsAndContents, extraPathsAndContents, getCompileOptions(compliance), classpath, jarPath); + createJar(javaPathsAndContents, extraPathsAndContents, getCompileOptions(compliance), classpath, jarPath, false/*do not write source*/); +} +public static void createJar(String[] javaPathsAndContents, String[] extraPathsAndContents, String jarPath, String[] classpath, String compliance, boolean writeSource) throws IOException { + createJar(javaPathsAndContents, extraPathsAndContents, getCompileOptions(compliance), classpath, jarPath, writeSource); } public static void createSourceZip(String[] pathsAndContents, String zipPath) throws IOException { String sourcesPath = getOutputDirectory() + File.separator + "sources"; @@ -1255,6 +1265,26 @@ System.out.println(); return false; } +public static void writeToFile(char[] contents, String destinationFilePath) { + File destFile = new File(destinationFilePath); + FileOutputStream output = null; + try { + output = new FileOutputStream(destFile); + PrintWriter writer = new PrintWriter(output); + writer.print(contents); + writer.flush(); + } catch (IOException e) { + e.printStackTrace(); + return; + } finally { + if (output != null) { + try { + output.close(); + } catch (IOException e2) { + } + } + } +} public static void writeToFile(String contents, String destinationFilePath) { File destFile = new File(destinationFilePath); FileOutputStream output = null; #P org.eclipse.jdt.core.tests.performance Index: src/org/eclipse/jdt/core/tests/performance/FullSourceWorkspaceTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/FullSourceWorkspaceTests.java,v retrieving revision 1.51 diff -u -r1.51 FullSourceWorkspaceTests.java --- src/org/eclipse/jdt/core/tests/performance/FullSourceWorkspaceTests.java 3 Oct 2008 12:36:18 -0000 1.51 +++ src/org/eclipse/jdt/core/tests/performance/FullSourceWorkspaceTests.java 4 Nov 2008 15:31:26 -0000 @@ -628,6 +628,16 @@ dirLength = directories.length; } + // Init JRE_LIB variable + System.out.println("Set JRE classpath variables:"); + IPath jreLibPath = getJRE(); + JavaCore.setClasspathVariable("JRE_LIB", jreLibPath, null); + System.out.println(" - JRE_LIB: "+JavaCore.getClasspathVariable("JRE_LIB")); + IPath jreSrcPath = jreLibPath.removeLastSegments(3).append("src.zip"); + assertTrue("Cannot find sources path "+jreSrcPath+" for lib "+jreLibPath, jreSrcPath.toFile().exists()); + JavaCore.setClasspathVariable("JRE_SRC", jreSrcPath, null); + System.out.println(" - JRE_SRC: "+JavaCore.getClasspathVariable("JRE_SRC")); + // Init environment with existing porjects System.out.print("Create and open projects in environment..."); start = System.currentTimeMillis(); @@ -642,24 +652,10 @@ } System.out.println("("+(System.currentTimeMillis()-start)+"ms)"); - // Init JRE_LIB variable - String[] jdkLibs = Util.getJavaClassLibs(); - int length = jdkLibs.length; - String jdkLib = null; - for (int i=0; i *
  • declarations using string pattern