KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > DeltaProcessor


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.core;
12
13 import java.io.File JavaDoc;
14 import java.util.*;
15
16 import org.eclipse.core.resources.IFile;
17 import org.eclipse.core.resources.IFolder;
18 import org.eclipse.core.resources.IProject;
19 import org.eclipse.core.resources.IResource;
20 import org.eclipse.core.resources.IResourceChangeEvent;
21 import org.eclipse.core.resources.IResourceDelta;
22 import org.eclipse.core.resources.IResourceDeltaVisitor;
23 import org.eclipse.core.resources.IWorkspaceRoot;
24 import org.eclipse.core.resources.IWorkspaceRunnable;
25 import org.eclipse.core.resources.ResourcesPlugin;
26 import org.eclipse.core.runtime.*;
27 import org.eclipse.jdt.core.*;
28 import org.eclipse.jdt.core.compiler.CharOperation;
29 import org.eclipse.jdt.internal.compiler.SourceElementParser;
30 import org.eclipse.jdt.internal.core.builder.JavaBuilder;
31 import org.eclipse.jdt.internal.core.hierarchy.TypeHierarchy;
32 import org.eclipse.jdt.internal.core.search.AbstractSearchScope;
33 import org.eclipse.jdt.internal.core.search.JavaWorkspaceScope;
34 import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
35 import org.eclipse.jdt.internal.core.util.Util;
36
37 /**
38  * This class is used by <code>JavaModelManager</code> to convert
39  * <code>IResourceDelta</code>s into <code>IJavaElementDelta</code>s.
40  * It also does some processing on the <code>JavaElement</code>s involved
41  * (e.g. closing them or updating classpaths).
42  * <p>
43  * High level summary of what the delta processor does:
44  * <ul>
45  * <li>reacts to resource deltas</li>
46  * <li>fires corresponding Java element deltas</li>
47  * <li>deltas also contain non-Java resources changes</li>
48  * <li>updates the model to reflect the Java element changes</li>
49  * <li>notifies type hierarchies of the changes</li>
50  * <li>triggers indexing of the changed elements</li>
51  * <li>refresh external archives (delta, model update, indexing)</li>
52  * <li>is thread safe (one delta processor instance per thread, see DeltaProcessingState#resourceChanged(...))</li>
53  * <li>handles .classpath changes (updates package fragment roots, update project references, validate classpath (.classpath format,
54  * resolved classpath, cycles))</li>
55  * </ul>
56  */

57 public class DeltaProcessor {
58     
59     static class OutputsInfo {
60         int outputCount;
61         IPath[] paths;
62         int[] traverseModes;
63         OutputsInfo(IPath[] paths, int[] traverseModes, int outputCount) {
64             this.paths = paths;
65             this.traverseModes = traverseModes;
66             this.outputCount = outputCount;
67         }
68         public String JavaDoc toString() {
69             if (this.paths == null) return "<none>"; //$NON-NLS-1$
70
StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
71             for (int i = 0; i < this.outputCount; i++) {
72                 buffer.append("path="); //$NON-NLS-1$
73
buffer.append(this.paths[i].toString());
74                 buffer.append("\n->traverse="); //$NON-NLS-1$
75
switch (this.traverseModes[i]) {
76                     case BINARY:
77                         buffer.append("BINARY"); //$NON-NLS-1$
78
break;
79                     case IGNORE:
80                         buffer.append("IGNORE"); //$NON-NLS-1$
81
break;
82                     case SOURCE:
83                         buffer.append("SOURCE"); //$NON-NLS-1$
84
break;
85                     default:
86                         buffer.append("<unknown>"); //$NON-NLS-1$
87
}
88                 if (i+1 < this.outputCount) {
89                     buffer.append('\n');
90                 }
91             }
92             return buffer.toString();
93         }
94     }
95     
96     static class RootInfo {
97         char[][] inclusionPatterns;
98         char[][] exclusionPatterns;
99         JavaProject project;
100         IPath rootPath;
101         int entryKind;
102         IPackageFragmentRoot root;
103         RootInfo(JavaProject project, IPath rootPath, char[][] inclusionPatterns, char[][] exclusionPatterns, int entryKind) {
104             this.project = project;
105             this.rootPath = rootPath;
106             this.inclusionPatterns = inclusionPatterns;
107             this.exclusionPatterns = exclusionPatterns;
108             this.entryKind = entryKind;
109         }
110         IPackageFragmentRoot getPackageFragmentRoot(IResource resource) {
111             if (this.root == null) {
112                 if (resource != null) {
113                     this.root = this.project.getPackageFragmentRoot(resource);
114                 } else {
115                     Object JavaDoc target = JavaModel.getTarget(ResourcesPlugin.getWorkspace().getRoot(), this.rootPath, false/*don't check existence*/);
116                     if (target instanceof IResource) {
117                         this.root = this.project.getPackageFragmentRoot((IResource)target);
118                     } else {
119                         this.root = this.project.getPackageFragmentRoot(this.rootPath.toOSString());
120                     }
121                 }
122             }
123             return this.root;
124         }
125         boolean isRootOfProject(IPath path) {
126             return this.rootPath.equals(path) && this.project.getProject().getFullPath().isPrefixOf(path);
127         }
128         public String JavaDoc toString() {
129             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("project="); //$NON-NLS-1$
130
if (this.project == null) {
131                 buffer.append("null"); //$NON-NLS-1$
132
} else {
133                 buffer.append(this.project.getElementName());
134             }
135             buffer.append("\npath="); //$NON-NLS-1$
136
if (this.rootPath == null) {
137                 buffer.append("null"); //$NON-NLS-1$
138
} else {
139                 buffer.append(this.rootPath.toString());
140             }
141             buffer.append("\nincluding="); //$NON-NLS-1$
142
if (this.inclusionPatterns == null) {
143                 buffer.append("null"); //$NON-NLS-1$
144
} else {
145                 for (int i = 0, length = this.inclusionPatterns.length; i < length; i++) {
146                     buffer.append(new String JavaDoc(this.inclusionPatterns[i]));
147                     if (i < length-1) {
148                         buffer.append("|"); //$NON-NLS-1$
149
}
150                 }
151             }
152             buffer.append("\nexcluding="); //$NON-NLS-1$
153
if (this.exclusionPatterns == null) {
154                 buffer.append("null"); //$NON-NLS-1$
155
} else {
156                 for (int i = 0, length = this.exclusionPatterns.length; i < length; i++) {
157                     buffer.append(new String JavaDoc(this.exclusionPatterns[i]));
158                     if (i < length-1) {
159                         buffer.append("|"); //$NON-NLS-1$
160
}
161                 }
162             }
163             return buffer.toString();
164         }
165     }
166
167     private final static int IGNORE = 0;
168     private final static int SOURCE = 1;
169     private final static int BINARY = 2;
170     
171     private final static String JavaDoc EXTERNAL_JAR_ADDED = "external jar added"; //$NON-NLS-1$
172
private final static String JavaDoc EXTERNAL_JAR_CHANGED = "external jar changed"; //$NON-NLS-1$
173
private final static String JavaDoc EXTERNAL_JAR_REMOVED = "external jar removed"; //$NON-NLS-1$
174
private final static String JavaDoc EXTERNAL_JAR_UNCHANGED = "external jar unchanged"; //$NON-NLS-1$
175
private final static String JavaDoc INTERNAL_JAR_IGNORE = "internal jar ignore"; //$NON-NLS-1$
176

177     private final static int NON_JAVA_RESOURCE = -1;
178     public static boolean DEBUG = false;
179     public static boolean VERBOSE = false;
180     public static boolean PERF = false;
181
182     public static final int DEFAULT_CHANGE_EVENT = 0; // must not collide with ElementChangedEvent event masks
183

184     /*
185      * Answer a combination of the lastModified stamp and the size.
186      * Used for detecting external JAR changes
187      */

188     public static long getTimeStamp(File JavaDoc file) {
189         return file.lastModified() + file.length();
190     }
191     
192     /*
193      * The global state of delta processing.
194      */

195     private DeltaProcessingState state;
196     
197     /*
198      * The Java model manager
199      */

200     JavaModelManager manager;
201     
202     /*
203      * The <code>JavaElementDelta</code> corresponding to the <code>IResourceDelta</code> being translated.
204      */

205     private JavaElementDelta currentDelta;
206
207     /* The java element that was last created (see createElement(IResource)).
208      * This is used as a stack of java elements (using getParent() to pop it, and
209      * using the various get*(...) to push it. */

210     private Openable currentElement;
211         
212     /*
213      * Queue of deltas created explicily by the Java Model that
214      * have yet to be fired.
215      */

216     public ArrayList javaModelDeltas= new ArrayList();
217     
218     /*
219      * Queue of reconcile deltas on working copies that have yet to be fired.
220      * This is a table form IWorkingCopy to IJavaElementDelta
221      */

222     public HashMap reconcileDeltas = new HashMap();
223
224     /*
225      * Turns delta firing on/off. By default it is on.
226      */

227     private boolean isFiring= true;
228     
229     /*
230      * Used to update the JavaModel for <code>IJavaElementDelta</code>s.
231      */

232     private final ModelUpdater modelUpdater = new ModelUpdater();
233
234     /* A set of IJavaProject whose caches need to be reset */
235     public HashSet projectCachesToReset = new HashSet();
236
237     /*
238      * A list of IJavaElement used as a scope for external archives refresh during POST_CHANGE.
239      * This is null if no refresh is needed.
240      */

241     private HashSet refreshedElements;
242     
243     /* A table from IJavaProject to an array of IPackageFragmentRoot.
244      * This table contains the pkg fragment roots of the project that are being deleted.
245      */

246     public Map oldRoots;
247     
248     /* A set of IJavaProject whose package fragment roots need to be refreshed */
249     private HashSet rootsToRefresh = new HashSet();
250     
251     /*
252      * Type of event that should be processed no matter what the real event type is.
253      */

254     public int overridenEventType = -1;
255     
256     /*
257      * Cache SourceElementParser for the project being visited
258      */

259     private SourceElementParser sourceElementParserCache;
260     
261     /*
262      * Map from IProject to ClasspathChange
263      */

264     public HashMap classpathChanges = new HashMap();
265
266     public DeltaProcessor(DeltaProcessingState state, JavaModelManager manager) {
267         this.state = state;
268         this.manager = manager;
269     }
270     
271     public ClasspathChange addClasspathChange(IProject project, IClasspathEntry[] oldRawClasspath, IPath oldOutputLocation, IClasspathEntry[] oldResolvedClasspath) {
272         ClasspathChange change = (ClasspathChange) this.classpathChanges.get(project);
273         if (change == null) {
274             change = new ClasspathChange((JavaProject) this.manager.getJavaModel().getJavaProject(project), oldRawClasspath, oldOutputLocation, oldResolvedClasspath);
275             this.classpathChanges.put(project, change);
276         } else {
277             if (change.oldRawClasspath == null)
278                 change.oldRawClasspath = oldRawClasspath;
279             if (change.oldOutputLocation == null)
280                 change.oldOutputLocation = oldOutputLocation;
281             if (change.oldResolvedClasspath == null)
282                 change.oldResolvedClasspath = oldResolvedClasspath;
283         }
284         return change;
285     }
286
287     /*
288      * Adds the dependents of the given project to the list of the projects
289      * to update.
290      */

291     private void addDependentProjects(IJavaProject project, HashMap projectDependencies, HashSet result) {
292         IJavaProject[] dependents = (IJavaProject[]) projectDependencies.get(project);
293         if (dependents == null) return;
294         for (int i = 0, length = dependents.length; i < length; i++) {
295             IJavaProject dependent = dependents[i];
296             if (result.contains(dependent))
297                 continue; // no need to go further as the project is already known
298
result.add(dependent);
299             addDependentProjects(dependent, projectDependencies, result);
300         }
301     }
302     /*
303      * Adds the given element to the list of elements used as a scope for external jars refresh.
304      */

305     public void addForRefresh(IJavaElement element) {
306         if (this.refreshedElements == null) {
307             this.refreshedElements = new HashSet();
308         }
309         this.refreshedElements.add(element);
310     }
311     /*
312      * Adds the given child handle to its parent's cache of children.
313      */

314     private void addToParentInfo(Openable child) {
315         Openable parent = (Openable) child.getParent();
316         if (parent != null && parent.isOpen()) {
317             try {
318                 JavaElementInfo info = (JavaElementInfo)parent.getElementInfo();
319                 info.addChild(child);
320             } catch (JavaModelException e) {
321                 // do nothing - we already checked if open
322
}
323         }
324     }
325     /*
326      * Adds the given project and its dependents to the list of the roots to refresh.
327      */

328     private void addToRootsToRefreshWithDependents(IJavaProject javaProject) {
329         this.rootsToRefresh.add(javaProject);
330         this.addDependentProjects(javaProject, this.state.projectDependencies, this.rootsToRefresh);
331     }
332     /*
333      * Check all external archive (referenced by given roots, projects or model) status and issue a corresponding root delta.
334      * Also triggers index updates
335      */

336     public void checkExternalArchiveChanges(IJavaElement[] elementsToRefresh, IProgressMonitor monitor) throws JavaModelException {
337         if (monitor != null && monitor.isCanceled())
338             throw new OperationCanceledException();
339         try {
340             if (monitor != null) monitor.beginTask("", 1); //$NON-NLS-1$
341

342             for (int i = 0, length = elementsToRefresh.length; i < length; i++) {
343                 this.addForRefresh(elementsToRefresh[i]);
344             }
345             boolean hasDelta = this.createExternalArchiveDelta(monitor);
346             if (hasDelta){
347                 // force classpath marker refresh of affected projects
348
JavaModel.flushExternalFileCache();
349                 
350                 // flush jar type cache
351
JavaModelManager.getJavaModelManager().resetJarTypeCache();
352                 
353                 IJavaElementDelta[] projectDeltas = this.currentDelta.getAffectedChildren();
354                 final int length = projectDeltas.length;
355                 final IProject[] projectsToTouch = new IProject[length];
356                 for (int i = 0; i < length; i++) {
357                     IJavaElementDelta delta = projectDeltas[i];
358                     JavaProject javaProject = (JavaProject)delta.getElement();
359                     projectsToTouch[i] = javaProject.getProject();
360                 }
361                 
362                 // touch the projects to force them to be recompiled while taking the workspace lock
363
// so that there is no concurrency with the Java builder
364
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96575
365
IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
366                     public void run(IProgressMonitor progressMonitor) throws CoreException {
367                         for (int i = 0; i < length; i++) {
368                             IProject project = projectsToTouch[i];
369                             
370                             // touch to force a build of this project
371
if (JavaBuilder.DEBUG)
372                                 System.out.println("Touching project " + project.getName() + " due to external jar file change"); //$NON-NLS-1$ //$NON-NLS-2$
373
project.touch(progressMonitor);
374                         }
375                     }
376                 };
377                 try {
378                     ResourcesPlugin.getWorkspace().run(runnable, monitor);
379                 } catch (CoreException e) {
380                     throw new JavaModelException(e);
381                 }
382                 
383                 if (this.currentDelta != null) { // if delta has not been fired while creating markers
384
this.fire(this.currentDelta, DEFAULT_CHANGE_EVENT);
385                 }
386             }
387         } finally {
388             this.currentDelta = null;
389             if (monitor != null) monitor.done();
390         }
391     }
392     /*
393      * Process the given delta and look for projects being added, opened, closed or
394      * with a java nature being added or removed.
395      * Note that projects being deleted are checked in deleting(IProject).
396      * In all cases, add the project's dependents to the list of projects to update
397      * so that the classpath related markers can be updated.
398      */

399     private void checkProjectsBeingAddedOrRemoved(IResourceDelta delta) {
400         IResource resource = delta.getResource();
401         IResourceDelta[] children = null;
402     
403         switch (resource.getType()) {
404             case IResource.ROOT :
405                 // workaround for bug 15168 circular errors not reported
406
this.state.getOldJavaProjecNames(); // force list to be computed
407
children = delta.getAffectedChildren();
408                 break;
409             case IResource.PROJECT :
410                 // NB: No need to check project's nature as if the project is not a java project:
411
// - if the project is added or changed this is a noop for projectsBeingDeleted
412
// - if the project is closed, it has already lost its java nature
413
IProject project = (IProject)resource;
414                 JavaProject javaProject = (JavaProject)JavaCore.create(project);
415                 switch (delta.getKind()) {
416                     case IResourceDelta.ADDED :
417                         this.manager.batchContainerInitializations = true;
418                     
419                         // remember project and its dependents
420
addToRootsToRefreshWithDependents(javaProject);
421                         
422                         // workaround for bug 15168 circular errors not reported
423
if (JavaProject.hasJavaNature(project)) {
424                             addToParentInfo(javaProject);
425                             readRawClasspath(javaProject);
426                             // ensure project references are updated (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=121569)
427
checkProjectReferenceChange(project, javaProject);
428                         }
429                         
430                         this.state.rootsAreStale = true;
431                         break;
432                         
433                     case IResourceDelta.CHANGED :
434                             if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
435                                 this.manager.batchContainerInitializations = true;
436         
437                                 // project opened or closed: remember project and its dependents
438
addToRootsToRefreshWithDependents(javaProject);
439                                 
440                                 // workaround for bug 15168 circular errors not reported
441
if (project.isOpen()) {
442                                     if (JavaProject.hasJavaNature(project)) {
443                                         addToParentInfo(javaProject);
444                                         readRawClasspath(javaProject);
445                                         // ensure project references are updated
446
checkProjectReferenceChange(project, javaProject);
447                                     }
448                                 } else {
449                                     try {
450                                         javaProject.close();
451                                     } catch (JavaModelException e) {
452                                         // java project doesn't exist: ignore
453
}
454                                     this.removeFromParentInfo(javaProject);
455                                     this.manager.removePerProjectInfo(javaProject);
456                                     this.manager.containerRemove(javaProject);
457                                 }
458                                 this.state.rootsAreStale = true;
459                             } else if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
460                                 boolean wasJavaProject = this.state.findJavaProject(project.getName()) != null;
461                                 boolean isJavaProject = JavaProject.hasJavaNature(project);
462                                 if (wasJavaProject != isJavaProject) {
463                                     this.manager.batchContainerInitializations = true;
464                                     
465                                     // java nature added or removed: remember project and its dependents
466
this.addToRootsToRefreshWithDependents(javaProject);
467         
468                                     // workaround for bug 15168 circular errors not reported
469
if (isJavaProject) {
470                                         this.addToParentInfo(javaProject);
471                                         readRawClasspath(javaProject);
472                                         // ensure project references are updated (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=172666)
473
checkProjectReferenceChange(project, javaProject);
474                                     } else {
475                                         // remove classpath cache so that initializeRoots() will not consider the project has a classpath
476
this.manager.removePerProjectInfo(javaProject);
477                                         // remove container cache for this project
478
this.manager.containerRemove(javaProject);
479                                         // close project
480
try {
481                                             javaProject.close();
482                                         } catch (JavaModelException e) {
483                                             // java project doesn't exist: ignore
484
}
485                                         this.removeFromParentInfo(javaProject);
486                                     }
487                                     this.state.rootsAreStale = true;
488                                 } else {
489                                     // in case the project was removed then added then changed (see bug 19799)
490
if (isJavaProject) { // need nature check - 18698
491
this.addToParentInfo(javaProject);
492                                         children = delta.getAffectedChildren();
493                                     }
494                                 }
495                             } else {
496                                 // workaround for bug 15168 circular errors not reported
497
// in case the project was removed then added then changed
498
if (JavaProject.hasJavaNature(project)) { // need nature check - 18698
499
this.addToParentInfo(javaProject);
500                                     children = delta.getAffectedChildren();
501                                 }
502                             }
503                             break;
504     
505                     case IResourceDelta.REMOVED :
506                         this.manager.batchContainerInitializations = true;
507     
508                         // remove classpath cache so that initializeRoots() will not consider the project has a classpath
509
this.manager.removePerProjectInfo(javaProject);
510                         // remove container cache for this project
511
this.manager.containerRemove(javaProject);
512                         
513                         this.state.rootsAreStale = true;
514                         break;
515                 }
516                 
517                 // in all cases, refresh the external jars for this project
518
addForRefresh(javaProject);
519                 
520                 break;
521             case IResource.FILE :
522                 IFile file = (IFile) resource;
523                 /* classpath file change */
524                 if (file.getName().equals(JavaProject.CLASSPATH_FILENAME)) {
525                     this.manager.batchContainerInitializations = true;
526                     switch (delta.getKind()) {
527                         case IResourceDelta.CHANGED :
528                             int flags = delta.getFlags();
529                             if ((flags & IResourceDelta.CONTENT) == 0 // only consider content change
530
&& (flags & IResourceDelta.ENCODING) == 0 // and encoding change
531
&& (flags & IResourceDelta.MOVED_FROM) == 0) {// and also move and overide scenario (see http://dev.eclipse.org/bugs/show_bug.cgi?id=21420)
532
break;
533                             }
534                         // fall through
535
case IResourceDelta.ADDED :
536                             javaProject = (JavaProject)JavaCore.create(file.getProject());
537                             
538                             // force to (re)read the .classpath file
539
try {
540                                 javaProject.getPerProjectInfo().readAndCacheClasspath(javaProject);
541                             } catch (JavaModelException e) {
542                                 // project doesn't exist
543
return;
544                             }
545                             break;
546                     }
547                     this.state.rootsAreStale = true;
548                 }
549                 break;
550                 
551         }
552         if (children != null) {
553             for (int i = 0; i < children.length; i++) {
554                 checkProjectsBeingAddedOrRemoved(children[i]);
555             }
556         }
557     }
558
559     private void checkProjectReferenceChange(IProject project, JavaProject javaProject) {
560         ClasspathChange change = (ClasspathChange) this.classpathChanges.get(project);
561         this.state.addProjectReferenceChange(javaProject, change == null ? null : change.oldResolvedClasspath);
562     }
563
564     private void readRawClasspath(JavaProject javaProject) {
565         try {
566             // force to (re)read the .classpath file
567
javaProject.getPerProjectInfo().readAndCacheClasspath(javaProject);
568         } catch (JavaModelException e) {
569             if (VERBOSE) {
570                 e.printStackTrace();
571             }
572         }
573     }
574     private void checkSourceAttachmentChange(IResourceDelta delta, IResource res) {
575         IPath rootPath = (IPath)this.state.sourceAttachments.get(res.getFullPath());
576         if (rootPath != null) {
577             RootInfo rootInfo = this.rootInfo(rootPath, delta.getKind());
578             if (rootInfo != null) {
579                 IJavaProject projectOfRoot = rootInfo.project;
580                 IPackageFragmentRoot root = null;
581                 try {
582                     // close the root so that source attachement cache is flushed
583
root = projectOfRoot.findPackageFragmentRoot(rootPath);
584                     if (root != null) {
585                         root.close();
586                     }
587                 } catch (JavaModelException e) {
588                     // root doesn't exist: ignore
589
}
590                 if (root == null) return;
591                 switch (delta.getKind()) {
592                     case IResourceDelta.ADDED:
593                         currentDelta().sourceAttached(root);
594                         break;
595                     case IResourceDelta.CHANGED:
596                         currentDelta().sourceDetached(root);
597                         currentDelta().sourceAttached(root);
598                         break;
599                     case IResourceDelta.REMOVED:
600                         currentDelta().sourceDetached(root);
601                         break;
602                 }
603             }
604         }
605     }
606     /*
607      * Closes the given element, which removes it from the cache of open elements.
608      */

609     private void close(Openable element) {
610         try {
611             element.close();
612         } catch (JavaModelException e) {
613             // do nothing
614
}
615     }
616     /*
617      * Generic processing for elements with changed contents:<ul>
618      * <li>The element is closed such that any subsequent accesses will re-open
619      * the element reflecting its new structure.
620      * <li>An entry is made in the delta reporting a content change (K_CHANGE with F_CONTENT flag set).
621      * </ul>
622      * Delta argument could be null if processing an external JAR change
623      */

624     private void contentChanged(Openable element) {
625
626         boolean isPrimary = false;
627         boolean isPrimaryWorkingCopy = false;
628         if (element.getElementType() == IJavaElement.COMPILATION_UNIT) {
629             CompilationUnit cu = (CompilationUnit)element;
630             isPrimary = cu.isPrimary();
631             isPrimaryWorkingCopy = isPrimary && cu.isWorkingCopy();
632         }
633         if (isPrimaryWorkingCopy) {
634             // filter out changes to primary compilation unit in working copy mode
635
// just report a change to the resource (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59500)
636
currentDelta().changed(element, IJavaElementDelta.F_PRIMARY_RESOURCE);
637         } else {
638             close(element);
639             int flags = IJavaElementDelta.F_CONTENT;
640             if (element instanceof JarPackageFragmentRoot){
641                 flags |= IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED;
642                 // need also to reset project cache otherwise it will be out-of-date
643
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=162621
644
this.projectCachesToReset.add(element.getJavaProject());
645             }
646             if (isPrimary) {
647                 flags |= IJavaElementDelta.F_PRIMARY_RESOURCE;
648             }
649             currentDelta().changed(element, flags);
650         }
651     }
652     /*
653      * Creates the openables corresponding to this resource.
654      * Returns null if none was found.
655      */

656     private Openable createElement(IResource resource, int elementType, RootInfo rootInfo) {
657         if (resource == null) return null;
658         
659         IPath path = resource.getFullPath();
660         IJavaElement element = null;
661         switch (elementType) {
662             
663             case IJavaElement.JAVA_PROJECT:
664             
665                 // note that non-java resources rooted at the project level will also enter this code with
666
// an elementType JAVA_PROJECT (see #elementType(...)).
667
if (resource instanceof IProject){
668
669                     this.popUntilPrefixOf(path);
670                     
671                     if (this.currentElement != null
672                         && this.currentElement.getElementType() == IJavaElement.JAVA_PROJECT
673                         && ((IJavaProject)this.currentElement).getProject().equals(resource)) {
674                         return this.currentElement;
675                     }
676                     if (rootInfo != null && rootInfo.project.getProject().equals(resource)){
677                         element = rootInfo.project;
678                         break;
679                     }
680                     IProject proj = (IProject)resource;
681                     if (JavaProject.hasJavaNature(proj)) {
682                         element = JavaCore.create(proj);
683                     } else {
684                         // java project may have been been closed or removed (look for
685
// element amongst old java project s list).
686
element = this.state.findJavaProject(proj.getName());
687                     }
688                 }
689                 break;
690             case IJavaElement.PACKAGE_FRAGMENT_ROOT:
691                 element = rootInfo == null ? JavaCore.create(resource) : rootInfo.getPackageFragmentRoot(resource);
692                 break;
693             case IJavaElement.PACKAGE_FRAGMENT:
694                 if (rootInfo != null) {
695                     if (rootInfo.project.contains(resource)) {
696                         PackageFragmentRoot root = (PackageFragmentRoot) rootInfo.getPackageFragmentRoot(null);
697                         // create package handle
698
IPath pkgPath = path.removeFirstSegments(rootInfo.rootPath.segmentCount());
699                         String JavaDoc[] pkgName = pkgPath.segments();
700                         element = root.getPackageFragment(pkgName);
701                     }
702                 } else {
703                     // find the element that encloses the resource
704
this.popUntilPrefixOf(path);
705                 
706                     if (this.currentElement == null) {
707                         element = JavaCore.create(resource);
708                     } else {
709                         // find the root
710
PackageFragmentRoot root = this.currentElement.getPackageFragmentRoot();
711                         if (root == null) {
712                             element = JavaCore.create(resource);
713                         } else if (((JavaProject)root.getJavaProject()).contains(resource)) {
714                             // create package handle
715
IPath pkgPath = path.removeFirstSegments(root.getPath().segmentCount());
716                             String JavaDoc[] pkgName = pkgPath.segments();
717                             element = root.getPackageFragment(pkgName);
718                         }
719                     }
720                 }
721                 break;
722             case IJavaElement.COMPILATION_UNIT:
723             case IJavaElement.CLASS_FILE:
724                 // find the element that encloses the resource
725
this.popUntilPrefixOf(path);
726                 
727                 if (this.currentElement == null) {
728                     element = rootInfo == null ? JavaCore.create(resource) : JavaModelManager.create(resource, rootInfo.project);
729                 } else {
730                     // find the package
731
IPackageFragment pkgFragment = null;
732                     switch (this.currentElement.getElementType()) {
733                         case IJavaElement.PACKAGE_FRAGMENT_ROOT:
734                             PackageFragmentRoot root = (PackageFragmentRoot)this.currentElement;
735                             IPath rootPath = root.getPath();
736                             IPath pkgPath = path.removeLastSegments(1);
737                             String JavaDoc[] pkgName = pkgPath.removeFirstSegments(rootPath.segmentCount()).segments();
738                             pkgFragment = root.getPackageFragment(pkgName);
739                             break;
740                         case IJavaElement.PACKAGE_FRAGMENT:
741                             Openable pkg = this.currentElement;
742                             if (pkg.getPath().equals(path.removeLastSegments(1))) {
743                                 pkgFragment = (IPackageFragment)pkg;
744                             } // else case of package x which is a prefix of x.y
745
break;
746                         case IJavaElement.COMPILATION_UNIT:
747                         case IJavaElement.CLASS_FILE:
748                             pkgFragment = (IPackageFragment)this.currentElement.getParent();
749                             break;
750                     }
751                     if (pkgFragment == null) {
752                         element = rootInfo == null ? JavaCore.create(resource) : JavaModelManager.create(resource, rootInfo.project);
753                     } else {
754                         if (elementType == IJavaElement.COMPILATION_UNIT) {
755                             // create compilation unit handle
756
// fileName validation has been done in elementType(IResourceDelta, int, boolean)
757
String JavaDoc fileName = path.lastSegment();
758                             element = pkgFragment.getCompilationUnit(fileName);
759                         } else {
760                             // create class file handle
761
// fileName validation has been done in elementType(IResourceDelta, int, boolean)
762
String JavaDoc fileName = path.lastSegment();
763                             element = pkgFragment.getClassFile(fileName);
764                         }
765                     }
766                 }
767                 break;
768         }
769         if (element == null) return null;
770         this.currentElement = (Openable)element;
771         return this.currentElement;
772     }
773     /*
774      * Check if external archives have changed and create the corresponding deltas.
775      * Returns whether at least on delta was created.
776      */

777     private boolean createExternalArchiveDelta(IProgressMonitor monitor) {
778         
779         if (this.refreshedElements == null) return false;
780             
781         HashMap externalArchivesStatus = new HashMap();
782         boolean hasDelta = false;
783         
784         // find JARs to refresh
785
HashSet archivePathsToRefresh = new HashSet();
786         Iterator iterator = this.refreshedElements.iterator();
787         this.refreshedElements = null; // null out early to avoid concurrent modification exception (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63534)
788
while (iterator.hasNext()) {
789             IJavaElement element = (IJavaElement)iterator.next();
790             switch(element.getElementType()){
791                 case IJavaElement.PACKAGE_FRAGMENT_ROOT :
792                     archivePathsToRefresh.add(element.getPath());
793                     break;
794                 case IJavaElement.JAVA_PROJECT :
795                     JavaProject javaProject = (JavaProject) element;
796                     if (!JavaProject.hasJavaNature(javaProject.getProject())) {
797                         // project is not accessible or has lost its Java nature
798
break;
799                     }
800                     IClasspathEntry[] classpath;
801                     try {
802                         classpath = javaProject.getResolvedClasspath();
803                         for (int j = 0, cpLength = classpath.length; j < cpLength; j++){
804                             if (classpath[j].getEntryKind() == IClasspathEntry.CPE_LIBRARY){
805                                 archivePathsToRefresh.add(classpath[j].getPath());
806                             }
807                         }
808                     } catch (JavaModelException e) {
809                         // project doesn't exist -> ignore
810
}
811                     break;
812                 case IJavaElement.JAVA_MODEL :
813                     Iterator projectNames = this.state.getOldJavaProjecNames().iterator();
814                     while (projectNames.hasNext()) {
815                         String JavaDoc projectName = (String JavaDoc) projectNames.next();
816                         IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
817                         if (!JavaProject.hasJavaNature(project)) {
818                             // project is not accessible or has lost its Java nature
819
continue;
820                         }
821                         javaProject = (JavaProject) JavaCore.create(project);
822                         try {
823                             classpath = javaProject.getResolvedClasspath();
824                         } catch (JavaModelException e2) {
825                             // project doesn't exist -> ignore
826
continue;
827                         }
828                         for (int k = 0, cpLength = classpath.length; k < cpLength; k++){
829                             if (classpath[k].getEntryKind() == IClasspathEntry.CPE_LIBRARY){
830                                 archivePathsToRefresh.add(classpath[k].getPath());
831                             }
832                         }
833                     }
834                     break;
835             }
836         }
837         
838         // perform refresh
839
Iterator projectNames = this.state.getOldJavaProjecNames().iterator();
840         IWorkspaceRoot wksRoot = ResourcesPlugin.getWorkspace().getRoot();
841         while (projectNames.hasNext()) {
842             
843             if (monitor != null && monitor.isCanceled()) break;
844             
845             String JavaDoc projectName = (String JavaDoc) projectNames.next();
846             IProject project = wksRoot.getProject(projectName);
847             if (!JavaProject.hasJavaNature(project)) {
848                 // project is not accessible or has lost its Java nature
849
continue;
850             }
851             JavaProject javaProject = (JavaProject) JavaCore.create(project);
852             IClasspathEntry[] entries;
853             try {
854                 entries = javaProject.getResolvedClasspath();
855             } catch (JavaModelException e1) {
856                 // project does not exist -> ignore
857
continue;
858             }
859             for (int j = 0; j < entries.length; j++){
860                 if (entries[j].getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
861                     
862                     IPath entryPath = entries[j].getPath();
863                     
864                     if (!archivePathsToRefresh.contains(entryPath)) continue; // not supposed to be refreshed
865

866                     String JavaDoc status = (String JavaDoc)externalArchivesStatus.get(entryPath);
867                     if (status == null){
868                         
869                         // compute shared status
870
Object JavaDoc targetLibrary = JavaModel.getTarget(wksRoot, entryPath, true);
871         
872                         if (targetLibrary == null){ // missing JAR
873
if (this.state.getExternalLibTimeStamps().remove(entryPath) != null){
874                                 externalArchivesStatus.put(entryPath, EXTERNAL_JAR_REMOVED);
875                                 // the jar was physically removed: remove the index
876
this.manager.indexManager.removeIndex(entryPath);
877                             }
878         
879                         } else if (targetLibrary instanceof File JavaDoc){ // external JAR
880

881                             File JavaDoc externalFile = (File JavaDoc)targetLibrary;
882                             
883                             // check timestamp to figure if JAR has changed in some way
884
Long JavaDoc oldTimestamp =(Long JavaDoc) this.state.getExternalLibTimeStamps().get(entryPath);
885                             long newTimeStamp = getTimeStamp(externalFile);
886                             if (oldTimestamp != null){
887         
888                                 if (newTimeStamp == 0){ // file doesn't exist
889
externalArchivesStatus.put(entryPath, EXTERNAL_JAR_REMOVED);
890                                     this.state.getExternalLibTimeStamps().remove(entryPath);
891                                     // remove the index
892
this.manager.indexManager.removeIndex(entryPath);
893         
894                                 } else if (oldTimestamp.longValue() != newTimeStamp){
895                                     externalArchivesStatus.put(entryPath, EXTERNAL_JAR_CHANGED);
896                                     this.state.getExternalLibTimeStamps().put(entryPath, new Long JavaDoc(newTimeStamp));
897                                     // first remove the index so that it is forced to be re-indexed
898
this.manager.indexManager.removeIndex(entryPath);
899                                     // then index the jar
900
this.manager.indexManager.indexLibrary(entryPath, project.getProject());
901                                 } else {
902                                     externalArchivesStatus.put(entryPath, EXTERNAL_JAR_UNCHANGED);
903                                 }
904                             } else {
905                                 if (newTimeStamp == 0){ // jar still doesn't exist
906
externalArchivesStatus.put(entryPath, EXTERNAL_JAR_UNCHANGED);
907                                 } else {
908                                     externalArchivesStatus.put(entryPath, EXTERNAL_JAR_ADDED);
909                                     this.state.getExternalLibTimeStamps().put(entryPath, new Long JavaDoc(newTimeStamp));
910                                     // index the new jar
911
this.manager.indexManager.indexLibrary(entryPath, project.getProject());
912                                 }
913                             }
914                         } else { // internal JAR
915
externalArchivesStatus.put(entryPath, INTERNAL_JAR_IGNORE);
916                         }
917                     }
918                     // according to computed status, generate a delta
919
status = (String JavaDoc)externalArchivesStatus.get(entryPath);
920                     if (status != null){
921                         if (status == EXTERNAL_JAR_ADDED){
922                             PackageFragmentRoot root = (PackageFragmentRoot) javaProject.getPackageFragmentRoot(entryPath.toString());
923                             if (VERBOSE){
924                                 System.out.println("- External JAR ADDED, affecting root: "+root.getElementName()); //$NON-NLS-1$
925
}
926                             elementAdded(root, null, null);
927                             this.state.addClasspathValidation(javaProject); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=185733
928
hasDelta = true;
929                         } else if (status == EXTERNAL_JAR_CHANGED) {
930                             PackageFragmentRoot root = (PackageFragmentRoot) javaProject.getPackageFragmentRoot(entryPath.toString());
931                             if (VERBOSE){
932                                 System.out.println("- External JAR CHANGED, affecting root: "+root.getElementName()); //$NON-NLS-1$
933
}
934                             contentChanged(root);
935                             hasDelta = true;
936                         } else if (status == EXTERNAL_JAR_REMOVED) {
937                             PackageFragmentRoot root = (PackageFragmentRoot) javaProject.getPackageFragmentRoot(entryPath.toString());
938                             if (VERBOSE){
939                                 System.out.println("- External JAR REMOVED, affecting root: "+root.getElementName()); //$NON-NLS-1$
940
}
941                             elementRemoved(root, null, null);
942                             this.state.addClasspathValidation(javaProject); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=185733
943
hasDelta = true;
944                         }
945                     }
946                 }
947             }
948         }
949         return hasDelta;
950     }
951     private JavaElementDelta currentDelta() {
952         if (this.currentDelta == null) {
953             this.currentDelta = new JavaElementDelta(this.manager.getJavaModel());
954         }
955         return this.currentDelta;
956     }
957     /*
958      * Note that the project is about to be deleted.
959      */

960     private void deleting(IProject project) {
961         
962         try {
963             // discard indexing jobs that belong to this project so that the project can be
964
// deleted without interferences from the index manager
965
this.manager.indexManager.discardJobs(project.getName());
966
967             JavaProject javaProject = (JavaProject)JavaCore.create(project);
968             
969             // remember roots of this project
970
if (this.oldRoots == null) {
971                 this.oldRoots = new HashMap();
972             }
973             if (javaProject.isOpen()) {
974                 this.oldRoots.put(javaProject, javaProject.getPackageFragmentRoots());
975             } else {
976                 // compute roots without opening project
977
this.oldRoots.put(
978                     javaProject,
979                     javaProject.computePackageFragmentRoots(
980                         javaProject.getResolvedClasspath(),
981                         false,
982                         null /*no reverse map*/));
983             }
984             
985             javaProject.close();
986
987             // workaround for bug 15168 circular errors not reported
988
this.state.getOldJavaProjecNames(); // foce list to be computed
989

990             this.removeFromParentInfo(javaProject);
991
992             // remove preferences from per project info
993
this.manager.resetProjectPreferences(javaProject);
994         } catch (JavaModelException e) {
995             // java project doesn't exist: ignore
996
}
997     }
998     /*
999      * Processing for an element that has been added:<ul>
1000     * <li>If the element is a project, do nothing, and do not process
1001     * children, as when a project is created it does not yet have any
1002     * natures - specifically a java nature.
1003     * <li>If the elemet is not a project, process it as added (see
1004     * <code>basicElementAdded</code>.
1005     * </ul>
1006     * Delta argument could be null if processing an external JAR change
1007     */

1008    private void elementAdded(Openable element, IResourceDelta delta, RootInfo rootInfo) {
1009        int elementType = element.getElementType();
1010        
1011        if (elementType == IJavaElement.JAVA_PROJECT) {
1012            // project add is handled by JavaProject.configure() because
1013
// when a project is created, it does not yet have a java nature
1014
if (delta != null && JavaProject.hasJavaNature((IProject)delta.getResource())) {
1015                addToParentInfo(element);
1016                if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
1017                    Openable movedFromElement = (Openable)element.getJavaModel().getJavaProject(delta.getMovedFromPath().lastSegment());
1018                    currentDelta().movedTo(element, movedFromElement);
1019                } else {
1020                    // Force the project to be closed as it might have been opened
1021
// before the resource modification came in and it might have a new child
1022
// For example, in an IWorkspaceRunnable:
1023
// 1. create a Java project P (where P=src)
1024
// 2. open project P
1025
// 3. add folder f in P's pkg fragment root
1026
// When the resource delta comes in, only the addition of P is notified,
1027
// but the pkg fragment root of project P is already opened, thus its children are not recomputed
1028
// and it appears to contain only the default package.
1029
close(element);
1030
1031                    currentDelta().added(element);
1032                }
1033                this.state.updateRoots(element.getPath(), delta, this);
1034                
1035                // refresh pkg fragment roots and caches of the project (and its dependents)
1036
this.rootsToRefresh.add(element);
1037                this.projectCachesToReset.add(element);
1038            }
1039        } else {
1040            if (delta == null || (delta.getFlags() & IResourceDelta.MOVED_FROM) == 0) {
1041                // regular element addition
1042
if (isPrimaryWorkingCopy(element, elementType) ) {
1043                    // filter out changes to primary compilation unit in working copy mode
1044
// just report a change to the resource (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59500)
1045
currentDelta().changed(element, IJavaElementDelta.F_PRIMARY_RESOURCE);
1046                } else {
1047                    addToParentInfo(element);
1048                    
1049                    // Force the element to be closed as it might have been opened
1050
// before the resource modification came in and it might have a new child
1051
// For example, in an IWorkspaceRunnable:
1052
// 1. create a package fragment p using a java model operation
1053
// 2. open package p
1054
// 3. add file X.java in folder p
1055
// When the resource delta comes in, only the addition of p is notified,
1056
// but the package p is already opened, thus its children are not recomputed
1057
// and it appears empty.
1058
close(element);
1059            
1060                    currentDelta().added(element);
1061                }
1062            } else {
1063                // element is moved
1064
addToParentInfo(element);
1065                close(element);
1066            
1067                IPath movedFromPath = delta.getMovedFromPath();
1068                IResource res = delta.getResource();
1069                IResource movedFromRes;
1070                if (res instanceof IFile) {
1071                    movedFromRes = res.getWorkspace().getRoot().getFile(movedFromPath);
1072                } else {
1073                    movedFromRes = res.getWorkspace().getRoot().getFolder(movedFromPath);
1074                }
1075                
1076                // find the element type of the moved from element
1077
RootInfo movedFromInfo = this.enclosingRootInfo(movedFromPath, IResourceDelta.REMOVED);
1078                int movedFromType =
1079                    this.elementType(
1080                        movedFromRes,
1081                        IResourceDelta.REMOVED,
1082                        element.getParent().getElementType(),
1083                        movedFromInfo);
1084                
1085                // reset current element as it might be inside a nested root (popUntilPrefixOf() may use the outer root)
1086
this.currentElement = null;
1087            
1088                // create the moved from element
1089
Openable movedFromElement =
1090                    elementType != IJavaElement.JAVA_PROJECT && movedFromType == IJavaElement.JAVA_PROJECT ?
1091                        null : // outside classpath
1092
this.createElement(movedFromRes, movedFromType, movedFromInfo);
1093                if (movedFromElement == null) {
1094                    // moved from outside classpath
1095
currentDelta().added(element);
1096                } else {
1097                    currentDelta().movedTo(element, movedFromElement);
1098                }
1099            }
1100            
1101            switch (elementType) {
1102                case IJavaElement.PACKAGE_FRAGMENT_ROOT :
1103                    // when a root is added, and is on the classpath, the project must be updated
1104
JavaProject project = (JavaProject) element.getJavaProject();
1105
1106                    // refresh pkg fragment roots and caches of the project (and its dependents)
1107
this.rootsToRefresh.add(project);
1108                    this.projectCachesToReset.add(project);
1109                    
1110                    break;
1111                case IJavaElement.PACKAGE_FRAGMENT :
1112                    // reset project's package fragment cache
1113
project = (JavaProject) element.getJavaProject();
1114                    this.projectCachesToReset.add(project);
1115
1116                    break;
1117            }
1118        }
1119    }
1120    /*
1121     * Generic processing for a removed element:<ul>
1122     * <li>Close the element, removing its structure from the cache
1123     * <li>Remove the element from its parent's cache of children
1124     * <li>Add a REMOVED entry in the delta
1125     * </ul>
1126     * Delta argument could be null if processing an external JAR change
1127     */

1128    private void elementRemoved(Openable element, IResourceDelta delta, RootInfo rootInfo) {
1129        
1130        int elementType = element.getElementType();
1131        if (delta == null || (delta.getFlags() & IResourceDelta.MOVED_TO) == 0) {
1132            // regular element removal
1133
if (isPrimaryWorkingCopy(element, elementType) ) {
1134                // filter out changes to primary compilation unit in working copy mode
1135
// just report a change to the resource (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59500)
1136
currentDelta().changed(element, IJavaElementDelta.F_PRIMARY_RESOURCE);
1137            } else {
1138                close(element);
1139                removeFromParentInfo(element);
1140                currentDelta().removed(element);
1141            }
1142        } else {
1143            // element is moved
1144
close(element);
1145            removeFromParentInfo(element);
1146            IPath movedToPath = delta.getMovedToPath();
1147            IResource res = delta.getResource();
1148            IResource movedToRes;
1149            switch (res.getType()) {
1150                case IResource.PROJECT:
1151                    movedToRes = res.getWorkspace().getRoot().getProject(movedToPath.lastSegment());
1152                    break;
1153                case IResource.FOLDER:
1154                    movedToRes = res.getWorkspace().getRoot().getFolder(movedToPath);
1155                    break;
1156                case IResource.FILE:
1157                    movedToRes = res.getWorkspace().getRoot().getFile(movedToPath);
1158                    break;
1159                default:
1160                    return;
1161            }
1162
1163            // find the element type of the moved from element
1164
RootInfo movedToInfo = this.enclosingRootInfo(movedToPath, IResourceDelta.ADDED);
1165            int movedToType =
1166                this.elementType(
1167                    movedToRes,
1168                    IResourceDelta.ADDED,
1169                    element.getParent().getElementType(),
1170                    movedToInfo);
1171
1172            // reset current element as it might be inside a nested root (popUntilPrefixOf() may use the outer root)
1173
this.currentElement = null;
1174            
1175            // create the moved To element
1176
Openable movedToElement =
1177                elementType != IJavaElement.JAVA_PROJECT && movedToType == IJavaElement.JAVA_PROJECT ?
1178                    null : // outside classpath
1179
this.createElement(movedToRes, movedToType, movedToInfo);
1180            if (movedToElement == null) {
1181                // moved outside classpath
1182
currentDelta().removed(element);
1183            } else {
1184                currentDelta().movedFrom(element, movedToElement);
1185            }
1186        }
1187
1188        switch (elementType) {
1189            case IJavaElement.JAVA_MODEL :
1190                this.manager.indexManager.reset();
1191                break;
1192            case IJavaElement.JAVA_PROJECT :
1193                this.state.updateRoots(element.getPath(), delta, this);
1194
1195                // refresh pkg fragment roots and caches of the project (and its dependents)
1196
this.rootsToRefresh.add(element);
1197                this.projectCachesToReset.add(element);
1198
1199                break;
1200            case IJavaElement.PACKAGE_FRAGMENT_ROOT :
1201                JavaProject project = (JavaProject) element.getJavaProject();
1202
1203                // refresh pkg fragment roots and caches of the project (and its dependents)
1204
this.rootsToRefresh.add(project);
1205                this.projectCachesToReset.add(project);
1206
1207                break;
1208            case IJavaElement.PACKAGE_FRAGMENT :
1209                // reset package fragment cache
1210
project = (JavaProject) element.getJavaProject();
1211                this.projectCachesToReset.add(project);
1212
1213                break;
1214        }
1215    }
1216    /*
1217     * Returns the type of the java element the given delta matches to.
1218     * Returns NON_JAVA_RESOURCE if unknown (e.g. a non-java resource or excluded .java file)
1219     */

1220    private int elementType(IResource res, int kind, int parentType, RootInfo rootInfo) {
1221        switch (parentType) {
1222            case IJavaElement.JAVA_MODEL:
1223                // case of a movedTo or movedFrom project (other cases are handled in processResourceDelta(...)
1224
return IJavaElement.JAVA_PROJECT;
1225            
1226            case NON_JAVA_RESOURCE:
1227            case IJavaElement.JAVA_PROJECT:
1228                if (rootInfo == null) {
1229                    rootInfo = this.enclosingRootInfo(res.getFullPath(), kind);
1230                }
1231                if (rootInfo != null && rootInfo.isRootOfProject(res.getFullPath())) {
1232                    return IJavaElement.PACKAGE_FRAGMENT_ROOT;
1233                }
1234                // not yet in a package fragment root or root of another project
1235
// or package fragment to be included (see below)
1236
// -> let it go through
1237

1238            case IJavaElement.PACKAGE_FRAGMENT_ROOT:
1239            case IJavaElement.PACKAGE_FRAGMENT:
1240                if (rootInfo == null) {
1241                    rootInfo = this.enclosingRootInfo(res.getFullPath(), kind);
1242                }
1243                if (rootInfo == null) {
1244                    return NON_JAVA_RESOURCE;
1245                }
1246                if (Util.isExcluded(res, rootInfo.inclusionPatterns, rootInfo.exclusionPatterns)) {
1247                    return NON_JAVA_RESOURCE;
1248                }
1249                if (res.getType() == IResource.FOLDER) {
1250                    if (parentType == NON_JAVA_RESOURCE && !Util.isExcluded(res.getParent(), rootInfo.inclusionPatterns, rootInfo.exclusionPatterns)) {
1251                        // parent is a non-Java resource because it doesn't have a valid package name (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=130982)
1252
return NON_JAVA_RESOURCE;
1253                    }
1254                    String JavaDoc sourceLevel = rootInfo.project == null ? null : rootInfo.project.getOption(JavaCore.COMPILER_SOURCE, true);
1255                    String JavaDoc complianceLevel = rootInfo.project == null ? null : rootInfo.project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
1256                    if (Util.isValidFolderNameForPackage(res.getName(), sourceLevel, complianceLevel)) {
1257                        return IJavaElement.PACKAGE_FRAGMENT;
1258                    }
1259                    return NON_JAVA_RESOURCE;
1260                }
1261                String JavaDoc fileName = res.getName();
1262                String JavaDoc sourceLevel = rootInfo.project == null ? null : rootInfo.project.getOption(JavaCore.COMPILER_SOURCE, true);
1263                String JavaDoc complianceLevel = rootInfo.project == null ? null : rootInfo.project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
1264                if (Util.isValidCompilationUnitName(fileName, sourceLevel, complianceLevel)) {
1265                    return IJavaElement.COMPILATION_UNIT;
1266                } else if (Util.isValidClassFileName(fileName, sourceLevel, complianceLevel)) {
1267                    return IJavaElement.CLASS_FILE;
1268                } else if ((rootInfo = this.rootInfo(res.getFullPath(), kind)) != null
1269                        && rootInfo.project.getProject().getFullPath().isPrefixOf(res.getFullPath()) /*ensure root is a root of its project (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=185310) */) {
1270                    // case of proj=src=bin and resource is a jar file on the classpath
1271
return IJavaElement.PACKAGE_FRAGMENT_ROOT;
1272                } else {
1273                    return NON_JAVA_RESOURCE;
1274                }
1275                
1276            default:
1277                return NON_JAVA_RESOURCE;
1278        }
1279    }
1280    /*
1281     * Flushes all deltas without firing them.
1282     */

1283    public void flush() {
1284        this.javaModelDeltas = new ArrayList();
1285    }
1286
1287    private SourceElementParser getSourceElementParser(Openable element) {
1288        if (this.sourceElementParserCache == null)
1289            this.sourceElementParserCache = this.manager.indexManager.getSourceElementParser(element.getJavaProject(), null/*requestor will be set by indexer*/);
1290        return this.sourceElementParserCache;
1291    }
1292    /*
1293     * Finds the root info this path is included in.
1294     * Returns null if not found.
1295     */

1296    private RootInfo enclosingRootInfo(IPath path, int kind) {
1297        while (path != null && path.segmentCount() > 0) {
1298            RootInfo rootInfo = this.rootInfo(path, kind);
1299            if (rootInfo != null) return rootInfo;
1300            path = path.removeLastSegments(1);
1301        }
1302        return null;
1303    }
1304    /*
1305     * Fire Java Model delta, flushing them after the fact after post_change notification.
1306     * If the firing mode has been turned off, this has no effect.
1307     */

1308    public void fire(IJavaElementDelta customDelta, int eventType) {
1309        if (!this.isFiring) return;
1310        
1311        if (DEBUG) {
1312            System.out.println("-----------------------------------------------------------------------------------------------------------------------");//$NON-NLS-1$
1313
}
1314
1315        IJavaElementDelta deltaToNotify;
1316        if (customDelta == null){
1317            deltaToNotify = this.mergeDeltas(this.javaModelDeltas);
1318        } else {
1319            deltaToNotify = customDelta;
1320        }
1321            
1322        // Refresh internal scopes
1323
if (deltaToNotify != null) {
1324            Iterator scopes = this.manager.searchScopes.keySet().iterator();
1325            while (scopes.hasNext()) {
1326                AbstractSearchScope scope = (AbstractSearchScope)scopes.next();
1327                scope.processDelta(deltaToNotify);
1328            }
1329            JavaWorkspaceScope workspaceScope = this.manager.workspaceScope;
1330            if (workspaceScope != null)
1331                workspaceScope.processDelta(deltaToNotify);
1332        }
1333            
1334        // Notification
1335

1336        // Important: if any listener reacts to notification by updating the listeners list or mask, these lists will
1337
// be duplicated, so it is necessary to remember original lists in a variable (since field values may change under us)
1338
IElementChangedListener[] listeners;
1339        int[] listenerMask;
1340        int listenerCount;
1341        synchronized (this.state) {
1342            listeners = this.state.elementChangedListeners;
1343            listenerMask = this.state.elementChangedListenerMasks;
1344            listenerCount = this.state.elementChangedListenerCount;
1345        }
1346
1347        switch (eventType) {
1348            case DEFAULT_CHANGE_EVENT:
1349                firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount);
1350                fireReconcileDelta(listeners, listenerMask, listenerCount);
1351                break;
1352            case ElementChangedEvent.POST_CHANGE:
1353                firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount);
1354                fireReconcileDelta(listeners, listenerMask, listenerCount);
1355                break;
1356        }
1357    }
1358
1359    private void firePostChangeDelta(
1360        IJavaElementDelta deltaToNotify,
1361        IElementChangedListener[] listeners,
1362        int[] listenerMask,
1363        int listenerCount) {
1364            
1365        // post change deltas
1366
if (DEBUG){
1367            System.out.println("FIRING POST_CHANGE Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
1368
System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
1369
}
1370        if (deltaToNotify != null) {
1371            // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
1372
this.flush();
1373            
1374            // mark the operation stack has not modifying resources since resource deltas are being fired
1375
JavaModelOperation.setAttribute(JavaModelOperation.HAS_MODIFIED_RESOURCE_ATTR, null);
1376            
1377            notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE, listeners, listenerMask, listenerCount);
1378        }
1379    }
1380    private void fireReconcileDelta(
1381        IElementChangedListener[] listeners,
1382        int[] listenerMask,
1383        int listenerCount) {
1384
1385
1386        IJavaElementDelta deltaToNotify = mergeDeltas(this.reconcileDeltas.values());
1387        if (DEBUG){
1388            System.out.println("FIRING POST_RECONCILE Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
1389
System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
1390
}
1391        if (deltaToNotify != null) {
1392            // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
1393
this.reconcileDeltas = new HashMap();
1394        
1395            notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE, listeners, listenerMask, listenerCount);
1396        }
1397    }
1398    /*
1399     * Returns whether a given delta contains some information relevant to the JavaModel,
1400     * in particular it will not consider SYNC or MARKER only deltas.
1401     */

1402    private boolean isAffectedBy(IResourceDelta rootDelta){
1403        //if (rootDelta == null) System.out.println("NULL DELTA");
1404
//long start = System.currentTimeMillis();
1405
if (rootDelta != null) {
1406            // use local exception to quickly escape from delta traversal
1407
class FoundRelevantDeltaException extends RuntimeException JavaDoc {
1408                private static final long serialVersionUID = 7137113252936111022L; // backward compatible
1409
// only the class name is used (to differenciate from other RuntimeExceptions)
1410
}
1411            try {
1412                rootDelta.accept(new IResourceDeltaVisitor() {
1413                    public boolean visit(IResourceDelta delta) /* throws CoreException */ {
1414                        switch (delta.getKind()){
1415                            case IResourceDelta.ADDED :
1416                            case IResourceDelta.REMOVED :
1417                                throw new FoundRelevantDeltaException();
1418                            case IResourceDelta.CHANGED :
1419                                // if any flag is set but SYNC or MARKER, this delta should be considered
1420
if (delta.getAffectedChildren().length == 0 // only check leaf delta nodes
1421
&& (delta.getFlags() & ~(IResourceDelta.SYNC | IResourceDelta.MARKERS)) != 0) {
1422                                    throw new FoundRelevantDeltaException();
1423                                }
1424                        }
1425                        return true;
1426                    }
1427                });
1428            } catch(FoundRelevantDeltaException e) {
1429                //System.out.println("RELEVANT DELTA detected in: "+ (System.currentTimeMillis() - start));
1430
return true;
1431            } catch(CoreException e) { // ignore delta if not able to traverse
1432
}
1433        }
1434        //System.out.println("IGNORE SYNC DELTA took: "+ (System.currentTimeMillis() - start));
1435
return false;
1436    }
1437    /*
1438     * Returns whether the given element is a primary compilation unit in working copy mode.
1439     */

1440    private boolean isPrimaryWorkingCopy(IJavaElement element, int elementType) {
1441        if (elementType == IJavaElement.COMPILATION_UNIT) {
1442            CompilationUnit cu = (CompilationUnit)element;
1443            return cu.isPrimary() && cu.isWorkingCopy();
1444        }
1445        return false;
1446    }
1447    /*
1448     * Returns whether the given resource is in one of the given output folders and if
1449     * it is filtered out from this output folder.
1450     */

1451    private boolean isResFilteredFromOutput(RootInfo rootInfo, OutputsInfo info, IResource res, int elementType) {
1452        if (info != null) {
1453            JavaProject javaProject = null;
1454            String JavaDoc sourceLevel = null;
1455            String JavaDoc complianceLevel = null;
1456            IPath resPath = res.getFullPath();
1457            for (int i = 0; i < info.outputCount; i++) {
1458                if (info.paths[i].isPrefixOf(resPath)) {
1459                    if (info.traverseModes[i] != IGNORE) {
1460                        // case of bin=src
1461
if (info.traverseModes[i] == SOURCE && elementType == IJavaElement.CLASS_FILE) {
1462                            return true;
1463                        }
1464                        // case of .class file under project and no source folder
1465
// proj=bin
1466
if (elementType == IJavaElement.JAVA_PROJECT && res instanceof IFile) {
1467                            if (sourceLevel == null) {
1468                                // Get java project to use its source and compliance levels
1469
javaProject = rootInfo == null ?
1470                                    (JavaProject)this.createElement(res.getProject(), IJavaElement.JAVA_PROJECT, null) :
1471                                    rootInfo.project;
1472                                if (javaProject != null) {
1473                                    sourceLevel = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
1474                                    complianceLevel = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
1475                                }
1476                            }
1477                            if (Util.isValidClassFileName(res.getName(), sourceLevel, complianceLevel)) {
1478                                return true;
1479                            }
1480                        }
1481                    } else {
1482                        return true;
1483                    }
1484                }
1485            }
1486        }
1487        return false;
1488    }
1489    /*
1490     * Merges all awaiting deltas.
1491     */

1492    private IJavaElementDelta mergeDeltas(Collection deltas) {
1493        if (deltas.size() == 0) return null;
1494        if (deltas.size() == 1) return (IJavaElementDelta)deltas.iterator().next();
1495        
1496        if (VERBOSE) {
1497            System.out.println("MERGING " + deltas.size() + " DELTAS ["+Thread.currentThread()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1498
}
1499        
1500        Iterator iterator = deltas.iterator();
1501        JavaElementDelta rootDelta = new JavaElementDelta(this.manager.javaModel);
1502        boolean insertedTree = false;
1503        while (iterator.hasNext()) {
1504            JavaElementDelta delta = (JavaElementDelta)iterator.next();
1505            if (VERBOSE) {
1506                System.out.println(delta.toString());
1507            }
1508            IJavaElement element = delta.getElement();
1509            if (this.manager.javaModel.equals(element)) {
1510                IJavaElementDelta[] children = delta.getAffectedChildren();
1511                for (int j = 0; j < children.length; j++) {
1512                    JavaElementDelta projectDelta = (JavaElementDelta) children[j];
1513                    rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta);
1514                    insertedTree = true;
1515                }
1516                IResourceDelta[] resourceDeltas = delta.getResourceDeltas();
1517                if (resourceDeltas != null) {
1518                    for (int i = 0, length = resourceDeltas.length; i < length; i++) {
1519                        rootDelta.addResourceDelta(resourceDeltas[i]);
1520                        insertedTree = true;
1521                    }
1522                }
1523            } else {
1524                rootDelta.insertDeltaTree(element, delta);
1525                insertedTree = true;
1526            }
1527        }
1528        if (insertedTree) return rootDelta;
1529        return null;
1530    }
1531    private void notifyListeners(IJavaElementDelta deltaToNotify, int eventType, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) {
1532        final ElementChangedEvent extraEvent = new ElementChangedEvent(deltaToNotify, eventType);
1533        for (int i= 0; i < listenerCount; i++) {
1534            if ((listenerMask[i] & eventType) != 0){
1535                final IElementChangedListener listener = listeners[i];
1536                long start = -1;
1537                if (VERBOSE) {
1538                    System.out.print("Listener #" + (i+1) + "=" + listener.toString());//$NON-NLS-1$//$NON-NLS-2$
1539
start = System.currentTimeMillis();
1540                }
1541                // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief
1542
SafeRunner.run(new ISafeRunnable() {
1543                    public void handleException(Throwable JavaDoc exception) {
1544                        Util.log(exception, "Exception occurred in listener of Java element change notification"); //$NON-NLS-1$
1545
}
1546                    public void run() throws Exception JavaDoc {
1547                        PerformanceStats stats = null;
1548                        if(PERF) {
1549                            stats = PerformanceStats.getStats(JavaModelManager.DELTA_LISTENER_PERF, listener);
1550                            stats.startRun();
1551                        }
1552                        listener.elementChanged(extraEvent);
1553                        if(PERF) {
1554                            stats.endRun();
1555                        }
1556                    }
1557                });
1558                if (VERBOSE) {
1559                    System.out.println(" -> " + (System.currentTimeMillis()-start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1560
}
1561            }
1562        }
1563    }
1564    private void notifyTypeHierarchies(IElementChangedListener[] listeners, int listenerCount) {
1565        for (int i= 0; i < listenerCount; i++) {
1566            final IElementChangedListener listener = listeners[i];
1567            if (!(listener instanceof TypeHierarchy)) continue;
1568
1569            // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief
1570
SafeRunner.run(new ISafeRunnable() {
1571                public void handleException(Throwable JavaDoc exception) {
1572                    Util.log(exception, "Exception occurred in listener of Java element change notification"); //$NON-NLS-1$
1573
}
1574                public void run() throws Exception JavaDoc {
1575                    TypeHierarchy typeHierarchy = (TypeHierarchy)listener;
1576                    if (typeHierarchy.hasFineGrainChanges()) {
1577                        // case of changes in primary working copies
1578
typeHierarchy.needsRefresh = true;
1579                        typeHierarchy.fireChange();
1580                    }
1581                }
1582            });
1583        }
1584    }
1585    /*
1586     * Generic processing for elements with changed contents:<ul>
1587     * <li>The element is closed such that any subsequent accesses will re-open
1588     * the element reflecting its new structure.
1589     * <li>An entry is made in the delta reporting a content change (K_CHANGE with F_CONTENT flag set).
1590     * </ul>
1591     */

1592    private void nonJavaResourcesChanged(Openable element, IResourceDelta delta)
1593        throws JavaModelException {
1594
1595        // reset non-java resources if element was open
1596
if (element.isOpen()) {
1597            JavaElementInfo info = (JavaElementInfo)element.getElementInfo();
1598            switch (element.getElementType()) {
1599                case IJavaElement.JAVA_MODEL :
1600                    ((JavaModelInfo) info).nonJavaResources = null;
1601                    currentDelta().addResourceDelta(delta);
1602                    return;
1603                case IJavaElement.JAVA_PROJECT :
1604                    ((JavaProjectElementInfo) info).setNonJavaResources(null);
1605    
1606                    // if a package fragment root is the project, clear it too
1607
JavaProject project = (JavaProject) element;
1608                    PackageFragmentRoot projectRoot =
1609                        (PackageFragmentRoot) project.getPackageFragmentRoot(project.getProject());
1610                    if (projectRoot.isOpen()) {
1611                        ((PackageFragmentRootInfo) projectRoot.getElementInfo()).setNonJavaResources(
1612                            null);
1613                    }
1614                    break;
1615                case IJavaElement.PACKAGE_FRAGMENT :
1616                     ((PackageFragmentInfo) info).setNonJavaResources(null);
1617                    break;
1618                case IJavaElement.PACKAGE_FRAGMENT_ROOT :
1619                     ((PackageFragmentRootInfo) info).setNonJavaResources(null);
1620            }
1621        }
1622
1623        JavaElementDelta current = currentDelta();
1624        JavaElementDelta elementDelta = current.find(element);
1625        if (elementDelta == null) {
1626            // don't use find after creating the delta as it can be null (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63434)
1627
elementDelta = current.changed(element, IJavaElementDelta.F_CONTENT);
1628        }
1629        elementDelta.addResourceDelta(delta);
1630    }
1631    /*
1632     * Returns the other root infos for the given path. Look in the old other roots table if kind is REMOVED.
1633     */

1634    private ArrayList otherRootsInfo(IPath path, int kind) {
1635        if (kind == IResourceDelta.REMOVED) {
1636            return (ArrayList)this.state.oldOtherRoots.get(path);
1637        }
1638        return (ArrayList)this.state.otherRoots.get(path);
1639    }
1640    
1641    private OutputsInfo outputsInfo(RootInfo rootInfo, IResource res) {
1642        try {
1643            JavaProject proj =
1644                rootInfo == null ?
1645                    (JavaProject)this.createElement(res.getProject(), IJavaElement.JAVA_PROJECT, null) :
1646                    rootInfo.project;
1647            if (proj != null) {
1648                IPath projectOutput = proj.getOutputLocation();
1649                int traverseMode = IGNORE;
1650                if (proj.getProject().getFullPath().equals(projectOutput)){ // case of proj==bin==src
1651
return new OutputsInfo(new IPath[] {projectOutput}, new int[] {SOURCE}, 1);
1652                }
1653                IClasspathEntry[] classpath = proj.getResolvedClasspath();
1654                IPath[] outputs = new IPath[classpath.length+1];
1655                int[] traverseModes = new int[classpath.length+1];
1656                int outputCount = 1;
1657                outputs[0] = projectOutput;
1658                traverseModes[0] = traverseMode;
1659                for (int i = 0, length = classpath.length; i < length; i++) {
1660                    IClasspathEntry entry = classpath[i];
1661                    IPath entryPath = entry.getPath();
1662                    IPath output = entry.getOutputLocation();
1663                    if (output != null) {
1664                        outputs[outputCount] = output;
1665                        // check case of SRC==bin
1666
if (entryPath.equals(output)) {
1667                            traverseModes[outputCount++] = (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) ? SOURCE : BINARY;
1668                        } else {
1669                            traverseModes[outputCount++] = IGNORE;
1670                        }
1671                    }
1672                    
1673                    // check case of SRC==bin
1674
if (entryPath.equals(projectOutput)) {
1675                        traverseModes[0] = (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) ? SOURCE : BINARY;
1676                    }
1677                }
1678                return new OutputsInfo(outputs, traverseModes, outputCount);
1679            }
1680        } catch (JavaModelException e) {
1681            // java project doesn't exist: ignore
1682
}
1683        return null;
1684    }
1685    private void popUntilPrefixOf(IPath path) {
1686        while (this.currentElement != null) {
1687            IPath currentElementPath = null;
1688            if (this.currentElement instanceof IPackageFragmentRoot) {
1689                currentElementPath = ((IPackageFragmentRoot)this.currentElement).getPath();
1690            } else {
1691                IResource currentElementResource = this.currentElement.getResource();
1692                if (currentElementResource != null) {
1693                    currentElementPath = currentElementResource.getFullPath();
1694                }
1695            }
1696            if (currentElementPath != null) {
1697                if (this.currentElement instanceof IPackageFragment
1698                    && ((IPackageFragment) this.currentElement).isDefaultPackage()
1699                    && currentElementPath.segmentCount() != path.segmentCount()-1) {
1700                        // default package and path is not a direct child
1701
this.currentElement = (Openable)this.currentElement.getParent();
1702                }
1703                if (currentElementPath.isPrefixOf(path)) {
1704                    return;
1705                }
1706            }
1707            this.currentElement = (Openable)this.currentElement.getParent();
1708        }
1709    }
1710    /*
1711     * Converts a <code>IResourceDelta</code> rooted in a <code>Workspace</code> into
1712     * the corresponding set of <code>IJavaElementDelta</code>, rooted in the
1713     * relevant <code>JavaModel</code>s.
1714     */

1715    private IJavaElementDelta processResourceDelta(IResourceDelta changes) {
1716
1717        try {
1718            IJavaModel model = this.manager.getJavaModel();
1719            if (!model.isOpen()) {
1720                // force opening of java model so that java element delta are reported
1721
try {
1722                    model.open(null);
1723                } catch (JavaModelException e) {
1724                    if (VERBOSE) {
1725                        e.printStackTrace();
1726                    }
1727                    return null;
1728                }
1729            }
1730            this.state.initializeRoots();
1731            this.currentElement = null;
1732            
1733            // get the workspace delta, and start processing there.
1734
IResourceDelta[] deltas = changes.getAffectedChildren();
1735            for (int i = 0; i < deltas.length; i++) {
1736                IResourceDelta delta = deltas[i];
1737                IResource res = delta.getResource();
1738                
1739                // find out the element type
1740
RootInfo rootInfo = null;
1741                int elementType;
1742                IProject proj = (IProject)res;
1743                boolean wasJavaProject = this.state.findJavaProject(proj.getName()) != null;
1744                boolean isJavaProject = JavaProject.hasJavaNature(proj);
1745                if (!wasJavaProject && !isJavaProject) {
1746                    elementType = NON_JAVA_RESOURCE;
1747                } else {
1748                    rootInfo = this.enclosingRootInfo(res.getFullPath(), delta.getKind());
1749                    if (rootInfo != null && rootInfo.isRootOfProject(res.getFullPath())) {
1750                        elementType = IJavaElement.PACKAGE_FRAGMENT_ROOT;
1751                    } else {
1752                        elementType = IJavaElement.JAVA_PROJECT;
1753                    }
1754                }
1755                
1756                // traverse delta
1757
this.traverseDelta(delta, elementType, rootInfo, null);
1758                
1759                if (elementType == NON_JAVA_RESOURCE
1760                        || (wasJavaProject != isJavaProject && (delta.getKind()) == IResourceDelta.CHANGED)) { // project has changed nature (description or open/closed)
1761
try {
1762                        // add child as non java resource
1763
nonJavaResourcesChanged((JavaModel)model, delta);
1764                    } catch (JavaModelException e) {
1765                        // java model could not be opened
1766
}
1767                }
1768
1769            }
1770            refreshPackageFragmentRoots();
1771            resetProjectCaches();
1772
1773            return this.currentDelta;
1774        } finally {
1775            this.currentDelta = null;
1776            this.rootsToRefresh.clear();
1777            this.projectCachesToReset.clear();
1778        }
1779    }
1780    /*
1781     * Traverse the set of projects which have changed namespace, and reset their
1782     * caches and their dependents
1783     */

1784    public void resetProjectCaches() {
1785        if (this.projectCachesToReset.size() == 0)
1786            return;
1787        
1788        JavaModelManager.getJavaModelManager().resetJarTypeCache();
1789        
1790        Iterator iterator = this.projectCachesToReset.iterator();
1791        HashMap projectDepencies = this.state.projectDependencies;
1792        HashSet affectedDependents = new HashSet();
1793        while (iterator.hasNext()) {
1794            JavaProject project = (JavaProject)iterator.next();
1795            project.resetCaches();
1796            addDependentProjects(project, projectDepencies, affectedDependents);
1797        }
1798        // reset caches of dependent projects
1799
iterator = affectedDependents.iterator();
1800        while (iterator.hasNext()) {
1801            JavaProject project = (JavaProject) iterator.next();
1802            project.resetCaches();
1803        }
1804    }
1805    /*
1806     * Refresh package fragment roots of projects that were affected
1807     */

1808    private void refreshPackageFragmentRoots() {
1809        Iterator iterator = this.rootsToRefresh.iterator();
1810        while (iterator.hasNext()) {
1811            JavaProject project = (JavaProject)iterator.next();
1812            project.updatePackageFragmentRoots();
1813        }
1814    }
1815    /*
1816     * Registers the given delta with this delta processor.
1817     */

1818    public void registerJavaModelDelta(IJavaElementDelta delta) {
1819        this.javaModelDeltas.add(delta);
1820    }
1821    /*
1822     * Removes the given element from its parents cache of children. If the
1823     * element does not have a parent, or the parent is not currently open,
1824     * this has no effect.
1825     */

1826    private void removeFromParentInfo(Openable child) {
1827
1828        Openable parent = (Openable) child.getParent();
1829        if (parent != null && parent.isOpen()) {
1830            try {
1831                JavaElementInfo info = (JavaElementInfo)parent.getElementInfo();
1832                info.removeChild(child);
1833            } catch (JavaModelException e) {
1834                // do nothing - we already checked if open
1835
}
1836        }
1837    }
1838    /*
1839     * Notification that some resource changes have happened
1840     * on the platform, and that the Java Model should update any required
1841     * internal structures such that its elements remain consistent.
1842     * Translates <code>IResourceDeltas</code> into <code>IJavaElementDeltas</code>.
1843     *
1844     * @see IResourceDelta
1845     * @see IResource
1846     */

1847    public void resourceChanged(IResourceChangeEvent event) {
1848
1849        int eventType = this.overridenEventType == -1 ? event.getType() : this.overridenEventType;
1850        IResource resource = event.getResource();
1851        IResourceDelta delta = event.getDelta();
1852        
1853        switch(eventType){
1854            case IResourceChangeEvent.PRE_DELETE :
1855                try {
1856                    if(resource.getType() == IResource.PROJECT
1857                        && ((IProject) resource).hasNature(JavaCore.NATURE_ID)) {
1858                            
1859                        deleting((IProject)resource);
1860                    }
1861                } catch(CoreException e){
1862                    // project doesn't exist or is not open: ignore
1863
}
1864                return;
1865                
1866            case IResourceChangeEvent.POST_CHANGE :
1867                if (isAffectedBy(delta)) { // avoid populating for SYNC or MARKER deltas
1868
try {
1869                        try {
1870                            stopDeltas();
1871                            checkProjectsBeingAddedOrRemoved(delta);
1872                            
1873                            // generate classpath change deltas
1874
if (this.classpathChanges.size() > 0) {
1875                                boolean hasDelta = this.currentDelta != null;
1876                                JavaElementDelta javaDelta = currentDelta();
1877                                Iterator changes = this.classpathChanges.values().iterator();
1878                                while (changes.hasNext()) {
1879                                    ClasspathChange change = (ClasspathChange) changes.next();
1880                                    int result = change.generateDelta(javaDelta);
1881                                    if ((result & ClasspathChange.HAS_DELTA) != 0) {
1882                                        hasDelta = true;
1883                                        change.requestIndexing();
1884                                        this.state.addClasspathValidation(change.project);
1885                                    }
1886                                    if ((result & ClasspathChange.HAS_PROJECT_CHANGE) != 0) {
1887                                        this.state.addProjectReferenceChange(change.project, change.oldResolvedClasspath);
1888                                    }
1889                                }
1890                                this.classpathChanges.clear();
1891                                if (!hasDelta)
1892                                    this.currentDelta = null;
1893                            }
1894                            
1895                            // generate external archive change deltas
1896
if (this.refreshedElements != null) {
1897                                createExternalArchiveDelta(null);
1898                            }
1899                            
1900                            // generate Java deltas from resource changes
1901
IJavaElementDelta translatedDelta = processResourceDelta(delta);
1902                            if (translatedDelta != null) {
1903                                registerJavaModelDelta(translatedDelta);
1904                            }
1905                        } finally {
1906                            this.sourceElementParserCache = null; // don't hold onto parser longer than necessary
1907
startDeltas();
1908                        }
1909                        IElementChangedListener[] listeners;
1910                        int listenerCount;
1911                        synchronized (this.state) {
1912                            listeners = this.state.elementChangedListeners;
1913                            listenerCount = this.state.elementChangedListenerCount;
1914                        }
1915                        notifyTypeHierarchies(listeners, listenerCount);
1916                        fire(null, ElementChangedEvent.POST_CHANGE);
1917                    } finally {
1918                        // workaround for bug 15168 circular errors not reported
1919
this.state.resetOldJavaProjectNames();
1920                        this.oldRoots = null;
1921                    }
1922                }
1923                return;
1924                
1925            case IResourceChangeEvent.PRE_BUILD :
1926                if(!isAffectedBy(delta))
1927                    return; // avoid populating for SYNC or MARKER deltas
1928

1929                // create classpath markers if necessary
1930
boolean needCycleValidation = validateClasspaths(delta);
1931                ClasspathValidation[] validations = this.state.removeClasspathValidations();
1932                if (validations != null) {
1933                    for (int i = 0, length = validations.length; i < length; i++) {
1934                        ClasspathValidation validation = validations[i];
1935                        validation.validate();
1936                    }
1937                }
1938                
1939                // update project references if necessary
1940
ProjectReferenceChange[] projectRefChanges = this.state.removeProjectReferenceChanges();
1941                if (projectRefChanges != null) {
1942                    for (int i = 0, length = projectRefChanges.length; i < length; i++) {
1943                        try {
1944                            projectRefChanges[i].updateProjectReferencesIfNecessary();
1945                        } catch(JavaModelException e) {
1946                            // project doesn't exist any longer, continue with next one
1947
}
1948                    }
1949                }
1950                
1951                if (needCycleValidation || projectRefChanges != null) {
1952                    // update all cycle markers since the project references changes may have affected cycles
1953
try {
1954                        JavaProject.validateCycles(null);
1955                    } catch (JavaModelException e) {
1956                        // a project no longer exists
1957
}
1958                }
1959                
1960                JavaModel.flushExternalFileCache();
1961                JavaBuilder.buildStarting();
1962                
1963                // does not fire any deltas
1964
return;
1965
1966            case IResourceChangeEvent.POST_BUILD :
1967                JavaBuilder.buildFinished();
1968                return;
1969        }
1970    }
1971
1972    /*
1973     * Returns the root info for the given path. Look in the old roots table if kind is REMOVED.
1974     */

1975    private RootInfo rootInfo(IPath path, int kind) {
1976        if (kind == IResourceDelta.REMOVED) {
1977            return (RootInfo)this.state.oldRoots.get(path);
1978        }
1979        return (RootInfo)this.state.roots.get(path);
1980    }
1981    /*
1982     * Turns the firing mode to on. That is, deltas that are/have been
1983     * registered will be fired.
1984     */

1985    private void startDeltas() {
1986        this.isFiring= true;
1987    }
1988    /*
1989     * Turns the firing mode to off. That is, deltas that are/have been
1990     * registered will not be fired until deltas are started again.
1991     */

1992    private void stopDeltas() {
1993        this.isFiring= false;
1994    }
1995    /*
1996     * Converts an <code>IResourceDelta</code> and its children into
1997     * the corresponding <code>IJavaElementDelta</code>s.
1998     */

1999    private void traverseDelta(
2000        IResourceDelta delta,
2001        int elementType,
2002        RootInfo rootInfo,
2003        OutputsInfo outputsInfo) {
2004            
2005        IResource res = delta.getResource();
2006    
2007        // set stack of elements
2008
if (this.currentElement == null && rootInfo != null) {
2009            this.currentElement = rootInfo.project;
2010        }
2011        
2012        // process current delta
2013
boolean processChildren = true;
2014        if (res instanceof IProject) {
2015            // reset source element parser cache
2016
this.sourceElementParserCache = null;
2017            
2018            processChildren =
2019                this.updateCurrentDeltaAndIndex(
2020                    delta,
2021                    elementType == IJavaElement.PACKAGE_FRAGMENT_ROOT ?
2022                        IJavaElement.JAVA_PROJECT : // case of prj=src
2023
elementType,
2024                    rootInfo);
2025        } else if (rootInfo != null) {
2026            processChildren = this.updateCurrentDeltaAndIndex(delta, elementType, rootInfo);
2027        } else {
2028            // not yet inside a package fragment root
2029
processChildren = true;
2030        }
2031        
2032        // get the project's output locations and traverse mode
2033
if (outputsInfo == null) outputsInfo = this.outputsInfo(rootInfo, res);
2034    
2035        // process children if needed
2036
if (processChildren) {
2037            IResourceDelta[] children = delta.getAffectedChildren();
2038            boolean oneChildOnClasspath = false;
2039            int length = children.length;
2040            IResourceDelta[] orphanChildren = null;
2041            Openable parent = null;
2042            boolean isValidParent = true;
2043            for (int i = 0; i < length; i++) {
2044                IResourceDelta child = children[i];
2045                IResource childRes = child.getResource();
2046    
2047                // check source attachment change
2048
this.checkSourceAttachmentChange(child, childRes);
2049                
2050                // find out whether the child is a package fragment root of the current project
2051
IPath childPath = childRes.getFullPath();
2052                int childKind = child.getKind();
2053                RootInfo childRootInfo = this.rootInfo(childPath, childKind);
2054                if (childRootInfo != null && !childRootInfo.isRootOfProject(childPath)) {
2055                    // package fragment root of another project (dealt with later)
2056
childRootInfo = null;
2057                }
2058                
2059                // compute child type
2060
int childType =
2061                    this.elementType(
2062                        childRes,
2063                        childKind,
2064                        elementType,
2065                        rootInfo == null ? childRootInfo : rootInfo
2066                    );
2067                        
2068                // is childRes in the output folder and is it filtered out ?
2069
boolean isResFilteredFromOutput = this.isResFilteredFromOutput(rootInfo, outputsInfo, childRes, childType);
2070
2071                boolean isNestedRoot = rootInfo != null && childRootInfo != null;
2072                if (!isResFilteredFromOutput
2073                        && !isNestedRoot) { // do not treat as non-java rsc if nested root
2074

2075                    this.traverseDelta(child, childType, rootInfo == null ? childRootInfo : rootInfo, outputsInfo); // traverse delta for child in the same project
2076

2077                    if (childType == NON_JAVA_RESOURCE) {
2078                        if (rootInfo != null) { // if inside a package fragment root
2079
if (!isValidParent) continue;
2080                            if (parent == null) {
2081                                // find the parent of the non-java resource to attach to
2082
if (this.currentElement == null
2083                                        || !rootInfo.project.equals(this.currentElement.getJavaProject())) { // note if currentElement is the IJavaModel, getJavaProject() is null
2084
// force the currentProject to be used
2085
this.currentElement = rootInfo.project;
2086                                }
2087                                if (elementType == IJavaElement.JAVA_PROJECT
2088                                    || (elementType == IJavaElement.PACKAGE_FRAGMENT_ROOT
2089                                        && res instanceof IProject)) {
2090                                    // NB: attach non-java resource to project (not to its package fragment root)
2091
parent = rootInfo.project;
2092                                } else {
2093                                    parent = this.createElement(res, elementType, rootInfo);
2094                                }
2095                                if (parent == null) {
2096                                    isValidParent = false;
2097                                    continue;
2098                                }
2099                            }
2100                            // add child as non java resource
2101
try {
2102                                nonJavaResourcesChanged(parent, child);
2103                            } catch (JavaModelException e) {
2104                                // ignore
2105
}
2106                        } else {
2107                            // the non-java resource (or its parent folder) will be attached to the java project
2108
if (orphanChildren == null) orphanChildren = new IResourceDelta[length];
2109                            orphanChildren[i] = child;
2110                        }
2111                    } else {
2112                        oneChildOnClasspath = true;
2113                    }
2114                } else {
2115                    oneChildOnClasspath = true; // to avoid reporting child delta as non-java resource delta
2116
}
2117                                
2118                // if child is a nested root
2119
// or if it is not a package fragment root of the current project
2120
// but it is a package fragment root of another project, traverse delta too
2121
if (isNestedRoot
2122                        || (childRootInfo == null && (childRootInfo = this.rootInfo(childPath, childKind)) != null)) {
2123                    this.traverseDelta(child, IJavaElement.PACKAGE_FRAGMENT_ROOT, childRootInfo, null); // binary output of childRootInfo.project cannot be this root
2124
}
2125    
2126                // if the child is a package fragment root of one or several other projects
2127
ArrayList rootList;
2128                if ((rootList = this.otherRootsInfo(childPath, childKind)) != null) {
2129                    Iterator iterator = rootList.iterator();
2130                    while (iterator.hasNext()) {
2131                        childRootInfo = (RootInfo) iterator.next();
2132                        this.traverseDelta(child, IJavaElement.PACKAGE_FRAGMENT_ROOT, childRootInfo, null); // binary output of childRootInfo.project cannot be this root
2133
}
2134                }
2135            }
2136            if (orphanChildren != null
2137                    && (oneChildOnClasspath // orphan children are siblings of a package fragment root
2138
|| res instanceof IProject)) { // non-java resource directly under a project
2139

2140                // attach orphan children
2141
IProject rscProject = res.getProject();
2142                JavaProject adoptiveProject = (JavaProject)JavaCore.create(rscProject);
2143                if (adoptiveProject != null
2144                        && JavaProject.hasJavaNature(rscProject)) { // delta iff Java project (18698)
2145
for (int i = 0; i < length; i++) {
2146                        if (orphanChildren[i] != null) {
2147                            try {
2148                                nonJavaResourcesChanged(adoptiveProject, orphanChildren[i]);
2149                            } catch (JavaModelException e) {
2150                                // ignore
2151
}
2152                        }
2153                    }
2154                }
2155            } // else resource delta will be added by parent
2156
} // else resource delta will be added by parent
2157
}
2158
2159    private void validateClasspaths(IResourceDelta delta, HashSet affectedProjects) {
2160        IResource resource = delta.getResource();
2161        boolean processChildren = false;
2162        switch (resource.getType()) {
2163            case IResource.ROOT :
2164                if (delta.getKind() == IResourceDelta.CHANGED) {
2165                    processChildren = true;
2166                }
2167                break;
2168            case IResource.PROJECT :
2169                IProject project = (IProject)resource;
2170                int kind = delta.getKind();
2171                boolean isJavaProject = JavaProject.hasJavaNature(project);
2172                switch (kind) {
2173                    case IResourceDelta.ADDED:
2174                        processChildren = isJavaProject;
2175                        affectedProjects.add(project.getFullPath());
2176                        break;
2177                    case IResourceDelta.CHANGED:
2178                        processChildren = isJavaProject;
2179                        if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
2180                            // project opened or closed
2181
if (isJavaProject) {
2182                                JavaProject javaProject = (JavaProject)JavaCore.create(project);
2183                                this.state.addClasspathValidation(javaProject); // in case .classpath got modified while closed
2184
}
2185                            affectedProjects.add(project.getFullPath());
2186                        } else if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
2187                            boolean wasJavaProject = this.state.findJavaProject(project.getName()) != null;
2188                            if (wasJavaProject != isJavaProject) {
2189                                // project gained or lost Java nature
2190
JavaProject javaProject = (JavaProject)JavaCore.create(project);
2191                                this.state.addClasspathValidation(javaProject); // add/remove classpath markers
2192
affectedProjects.add(project.getFullPath());
2193                            }
2194                        }
2195                        break;
2196                    case IResourceDelta.REMOVED:
2197                        affectedProjects.add(project.getFullPath());
2198                        break;
2199                }
2200                break;
2201            case IResource.FILE :
2202                /* check classpath or prefs files change */
2203                IFile file = (IFile) resource;
2204                String JavaDoc fileName = file.getName();
2205                if (fileName.equals(JavaProject.CLASSPATH_FILENAME)) {
2206                    JavaProject javaProject = (JavaProject)JavaCore.create(file.getProject());
2207                    this.state.addClasspathValidation(javaProject);
2208                    affectedProjects.add(file.getProject().getFullPath());
2209                }
2210                break;
2211        }
2212        if (processChildren) {
2213            IResourceDelta[] children = delta.getAffectedChildren();
2214            for (int i = 0; i < children.length; i++) {
2215                validateClasspaths(children[i], affectedProjects);
2216            }
2217        }
2218    }
2219
2220    /*
2221     * Validate the classpaths of the projects affected by the given delta.
2222     * Create markers if necessary.
2223     * Returns whether cycle markers should be recomputed.
2224     */

2225    private boolean validateClasspaths(IResourceDelta delta) {
2226        HashSet affectedProjects = new HashSet(5);
2227        validateClasspaths(delta, affectedProjects);
2228        boolean needCycleValidation = false;
2229    
2230        // validate classpaths of affected projects (dependent projects
2231
// or projects that reference a library in one of the projects that have changed)
2232
if (!affectedProjects.isEmpty()) {
2233            IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
2234            IProject[] projects = workspaceRoot.getProjects();
2235            int length = projects.length;
2236            for (int i = 0; i < length; i++){
2237                IProject project = projects[i];
2238                JavaProject javaProject = (JavaProject)JavaCore.create(project);
2239                try {
2240                    IPath projectPath = project.getFullPath();
2241                    IClasspathEntry[] classpath = javaProject.getResolvedClasspath(); // allowed to reuse model cache
2242
for (int j = 0, cpLength = classpath.length; j < cpLength; j++) {
2243                        IClasspathEntry entry = classpath[j];
2244                        switch (entry.getEntryKind()) {
2245                            case IClasspathEntry.CPE_PROJECT:
2246                                if (affectedProjects.contains(entry.getPath())) {
2247                                    this.state.addClasspathValidation(javaProject);
2248                                    needCycleValidation = true;
2249                                }
2250                                break;
2251                            case IClasspathEntry.CPE_LIBRARY:
2252                                IPath entryPath = entry.getPath();
2253                                IPath libProjectPath = entryPath.removeLastSegments(entryPath.segmentCount()-1);
2254                                if (!libProjectPath.equals(projectPath) // if library contained in another project
2255
&& affectedProjects.contains(libProjectPath)) {
2256                                    this.state.addClasspathValidation(javaProject);
2257                                }
2258                                break;
2259                        }
2260                    }
2261                } catch(JavaModelException e) {
2262                        // project no longer exists
2263
}
2264            }
2265        }
2266        return needCycleValidation;
2267    }
2268    
2269    /*
2270     * Update the current delta (ie. add/remove/change the given element) and update the correponding index.
2271     * Returns whether the children of the given delta must be processed.
2272     * @throws a JavaModelException if the delta doesn't correspond to a java element of the given type.
2273     */

2274    public boolean updateCurrentDeltaAndIndex(IResourceDelta delta, int elementType, RootInfo rootInfo) {
2275        Openable element;
2276        switch (delta.getKind()) {
2277            case IResourceDelta.ADDED :
2278                IResource deltaRes = delta.getResource();
2279                element = createElement(deltaRes, elementType, rootInfo);
2280                if (element == null) {
2281                    // resource might be containing shared roots (see bug 19058)
2282
this.state.updateRoots(deltaRes.getFullPath(), delta, this);
2283                    return rootInfo != null && rootInfo.inclusionPatterns != null;
2284                }
2285                updateIndex(element, delta);
2286                elementAdded(element, delta, rootInfo);
2287                if (elementType == IJavaElement.PACKAGE_FRAGMENT_ROOT)
2288                    this.state.addClasspathValidation(rootInfo.project);
2289                return elementType == IJavaElement.PACKAGE_FRAGMENT;
2290            case IResourceDelta.REMOVED :
2291                deltaRes = delta.getResource();
2292                element = createElement(deltaRes, elementType, rootInfo);
2293                if (element == null) {
2294                    // resource might be containing shared roots (see bug 19058)
2295
this.state.updateRoots(deltaRes.getFullPath(), delta, this);
2296                    return rootInfo != null && rootInfo.inclusionPatterns != null;
2297                }
2298                updateIndex(element, delta);
2299                elementRemoved(element, delta, rootInfo);
2300                if (elementType == IJavaElement.PACKAGE_FRAGMENT_ROOT)
2301                    this.state.addClasspathValidation(rootInfo.project);
2302    
2303                if (deltaRes.getType() == IResource.PROJECT){
2304                    // reset the corresponding project built state, since cannot reuse if added back
2305
if (JavaBuilder.DEBUG)
2306                        System.out.println("Clearing last state for removed project : " + deltaRes); //$NON-NLS-1$
2307
this.manager.setLastBuiltState((IProject)deltaRes, null /*no state*/);
2308                    
2309                    // clean up previous session containers (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=89850)
2310
this.manager.previousSessionContainers.remove(element);
2311                }
2312                return elementType == IJavaElement.PACKAGE_FRAGMENT;
2313            case IResourceDelta.CHANGED :
2314                int flags = delta.getFlags();
2315                if ((flags & IResourceDelta.CONTENT) != 0 || (flags & IResourceDelta.ENCODING) != 0) {
2316                    // content or encoding has changed
2317
element = createElement(delta.getResource(), elementType, rootInfo);
2318                    if (element == null) return false;
2319                    updateIndex(element, delta);
2320                    contentChanged(element);
2321                } else if (elementType == IJavaElement.JAVA_PROJECT) {
2322                    if ((flags & IResourceDelta.OPEN) != 0) {
2323                        // project has been opened or closed
2324
IProject res = (IProject)delta.getResource();
2325                        element = createElement(res, elementType, rootInfo);
2326                        if (element == null) {
2327                            // resource might be containing shared roots (see bug 19058)
2328
this.state.updateRoots(res.getFullPath(), delta, this);
2329                            return false;
2330                        }
2331                        if (res.isOpen()) {
2332                            if (JavaProject.hasJavaNature(res)) {
2333                                addToParentInfo(element);
2334                                currentDelta().opened(element);
2335                                this.state.updateRoots(element.getPath(), delta, this);
2336                                
2337                                // refresh pkg fragment roots and caches of the project (and its dependents)
2338
this.rootsToRefresh.add(element);
2339                                this.projectCachesToReset.add(element);
2340                                
2341                                this.manager.indexManager.indexAll(res);
2342                            }
2343                        } else {
2344                            boolean wasJavaProject = this.state.findJavaProject(res.getName()) != null;
2345                            if (wasJavaProject) {
2346                                close(element);
2347                                removeFromParentInfo(element);
2348                                currentDelta().closed(element);
2349                                this.manager.indexManager.discardJobs(element.getElementName());
2350                                this.manager.indexManager.removeIndexFamily(res.getFullPath());
2351                            }
2352                        }
2353                        return false; // when a project is open/closed don't process children
2354
}
2355                    if ((flags & IResourceDelta.DESCRIPTION) != 0) {
2356                        IProject res = (IProject)delta.getResource();
2357                        boolean wasJavaProject = this.state.findJavaProject(res.getName()) != null;
2358                        boolean isJavaProject = JavaProject.hasJavaNature(res);
2359                        if (wasJavaProject != isJavaProject) {
2360                            // project's nature has been added or removed
2361
element = this.createElement(res, elementType, rootInfo);
2362                            if (element == null) return false; // note its resources are still visible as roots to other projects
2363
if (isJavaProject) {
2364                                elementAdded(element, delta, rootInfo);
2365                                this.manager.indexManager.indexAll(res);
2366                            } else {
2367                                elementRemoved(element, delta, rootInfo);
2368                                this.manager.indexManager.discardJobs(element.getElementName());
2369                                this.manager.indexManager.removeIndexFamily(res.getFullPath());
2370                                // reset the corresponding project built state, since cannot reuse if added back
2371
if (JavaBuilder.DEBUG)
2372                                    System.out.println("Clearing last state for project loosing Java nature: " + res); //$NON-NLS-1$
2373
this.manager.setLastBuiltState(res, null /*no state*/);
2374                            }
2375                            return false; // when a project's nature is added/removed don't process children
2376
}
2377                    }
2378                }
2379                return true;
2380        }
2381        return true;
2382    }
2383    private void updateIndex(Openable element, IResourceDelta delta) {
2384    
2385        IndexManager indexManager = this.manager.indexManager;
2386        if (indexManager == null)
2387            return;
2388    
2389        switch (element.getElementType()) {
2390            case IJavaElement.JAVA_PROJECT :
2391                switch (delta.getKind()) {
2392                    case IResourceDelta.ADDED :
2393                        indexManager.indexAll(element.getJavaProject().getProject());
2394                        break;
2395                    case IResourceDelta.REMOVED :
2396                        indexManager.removeIndexFamily(element.getJavaProject().getProject().getFullPath());
2397                        // NB: Discarding index jobs belonging to this project was done during PRE_DELETE
2398
break;
2399                    // NB: Update of index if project is opened, closed, or its java nature is added or removed
2400
// is done in updateCurrentDeltaAndIndex
2401
}
2402                break;
2403            case IJavaElement.PACKAGE_FRAGMENT_ROOT :
2404                if (element instanceof JarPackageFragmentRoot) {
2405                    JarPackageFragmentRoot root = (JarPackageFragmentRoot)element;
2406                    // index jar file only once (if the root is in its declaring project)
2407
IPath jarPath = root.getPath();
2408                    switch (delta.getKind()) {
2409                        case IResourceDelta.ADDED:
2410                            // index the new jar
2411
indexManager.indexLibrary(jarPath, root.getJavaProject().getProject());
2412                            break;
2413                        case IResourceDelta.CHANGED:
2414                            // first remove the index so that it is forced to be re-indexed
2415
indexManager.removeIndex(jarPath);
2416                            // then index the jar
2417
indexManager.indexLibrary(jarPath, root.getJavaProject().getProject());
2418                            break;
2419                        case IResourceDelta.REMOVED:
2420                            // the jar was physically removed: remove the index
2421
indexManager.discardJobs(jarPath.toString());
2422                            indexManager.removeIndex(jarPath);
2423                            break;
2424                    }
2425                    break;
2426                }
2427                int kind = delta.getKind();
2428                if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED) {
2429                    PackageFragmentRoot root = (PackageFragmentRoot)element;
2430                    this.updateRootIndex(root, CharOperation.NO_STRINGS, delta);
2431                    break;
2432                }
2433                // don't break as packages of the package fragment root can be indexed below
2434
case IJavaElement.PACKAGE_FRAGMENT :
2435                switch (delta.getKind()) {
2436                    case IResourceDelta.ADDED:
2437                    case IResourceDelta.REMOVED:
2438                        IPackageFragment pkg = null;
2439                        if (element instanceof IPackageFragmentRoot) {
2440                            PackageFragmentRoot root = (PackageFragmentRoot)element;
2441                            pkg = root.getPackageFragment(CharOperation.NO_STRINGS);
2442                        } else {
2443                            pkg = (IPackageFragment)element;
2444                        }
2445                        RootInfo rootInfo = rootInfo(pkg.getParent().getPath(), delta.getKind());
2446                        boolean isSource =
2447                            rootInfo == null // if null, defaults to source
2448
|| rootInfo.entryKind == IClasspathEntry.CPE_SOURCE;
2449                        IResourceDelta[] children = delta.getAffectedChildren();
2450                        for (int i = 0, length = children.length; i < length; i++) {
2451                            IResourceDelta child = children[i];
2452                            IResource resource = child.getResource();
2453                            // TODO (philippe) Why do this? Every child is added anyway as the delta is walked
2454
if (resource instanceof IFile) {
2455                                String JavaDoc name = resource.getName();
2456                                if (isSource) {
2457                                    if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(name)) {
2458                                        Openable cu = (Openable)pkg.getCompilationUnit(name);
2459                                        this.updateIndex(cu, child);
2460                                    }
2461                                } else if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(name)) {
2462                                    Openable classFile = (Openable)pkg.getClassFile(name);
2463                                    this.updateIndex(classFile, child);
2464                                }
2465                            }
2466                        }
2467                        break;
2468                }
2469                break;
2470            case IJavaElement.CLASS_FILE :
2471                IFile file = (IFile) delta.getResource();
2472                IJavaProject project = element.getJavaProject();
2473                IPath binaryFolderPath = element.getPackageFragmentRoot().getPath();
2474                // if the class file is part of the binary output, it has been created by
2475
// the java builder -> ignore
2476
try {
2477                    if (binaryFolderPath.equals(project.getOutputLocation())) {
2478                        break;
2479                    }
2480                } catch (JavaModelException e) {
2481                    // project doesn't exist: ignore
2482
}
2483                switch (delta.getKind()) {
2484                    case IResourceDelta.CHANGED :
2485                        // no need to index if the content has not changed
2486
int flags = delta.getFlags();
2487                        if ((flags & IResourceDelta.CONTENT) == 0 && (flags & IResourceDelta.ENCODING) == 0)
2488                            break;
2489                    case IResourceDelta.ADDED :
2490                        indexManager.addBinary(file, binaryFolderPath);
2491                        break;
2492                    case IResourceDelta.REMOVED :
2493                        String JavaDoc containerRelativePath = Util.relativePath(file.getFullPath(), binaryFolderPath.segmentCount());
2494                        indexManager.remove(containerRelativePath, binaryFolderPath);
2495                        break;
2496                }
2497                break;
2498            case IJavaElement.COMPILATION_UNIT :
2499                file = (IFile) delta.getResource();
2500                switch (delta.getKind()) {
2501                    case IResourceDelta.CHANGED :
2502                        // no need to index if the content has not changed
2503
int flags = delta.getFlags();
2504                        if ((flags & IResourceDelta.CONTENT) == 0 && (flags & IResourceDelta.ENCODING) == 0)
2505                            break;
2506                    case IResourceDelta.ADDED :
2507                        indexManager.addSource(file, file.getProject().getFullPath(), getSourceElementParser(element));
2508                        // Clean file from secondary types cache but do not update indexing secondary type cache as it will be updated through indexing itself
2509
this.manager.secondaryTypesRemoving(file, false);
2510                        break;
2511                    case IResourceDelta.REMOVED :
2512                        indexManager.remove(Util.relativePath(file.getFullPath(), 1/*remove project segment*/), file.getProject().getFullPath());
2513                        // Clean file from secondary types cache and update indexing secondary type cache as indexing cannot remove secondary types from cache
2514
this.manager.secondaryTypesRemoving(file, true);
2515                        break;
2516                }
2517        }
2518    }
2519    /*
2520     * Update Java Model given some delta
2521     */

2522    public void updateJavaModel(IJavaElementDelta customDelta) {
2523
2524        if (customDelta == null){
2525            for (int i = 0, length = this.javaModelDeltas.size(); i < length; i++){
2526                IJavaElementDelta delta = (IJavaElementDelta)this.javaModelDeltas.get(i);
2527                this.modelUpdater.processJavaDelta(delta);
2528            }
2529        } else {
2530            this.modelUpdater.processJavaDelta(customDelta);
2531        }
2532    }
2533    /*
2534     * Updates the index of the given root (assuming it's an addition or a removal).
2535     * This is done recusively, pkg being the current package.
2536     */

2537    private void updateRootIndex(PackageFragmentRoot root, String JavaDoc[] pkgName, IResourceDelta delta) {
2538        Openable pkg = root.getPackageFragment(pkgName);
2539        this.updateIndex(pkg, delta);
2540        IResourceDelta[] children = delta.getAffectedChildren();
2541        for (int i = 0, length = children.length; i < length; i++) {
2542            IResourceDelta child = children[i];
2543            IResource resource = child.getResource();
2544            if (resource instanceof IFolder) {
2545                String JavaDoc[] subpkgName = Util.arrayConcat(pkgName, resource.getName());
2546                this.updateRootIndex(root, subpkgName, child);
2547            }
2548        }
2549    }
2550}
2551
Popular Tags