KickJava   Java API By Example, From Geeks To Geeks.

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


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.BufferedInputStream JavaDoc;
14 import java.io.BufferedOutputStream JavaDoc;
15 import java.io.DataInputStream JavaDoc;
16 import java.io.DataOutputStream JavaDoc;
17 import java.io.File JavaDoc;
18 import java.io.FileInputStream JavaDoc;
19 import java.io.FileOutputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.util.*;
22
23 import org.eclipse.core.resources.*;
24 import org.eclipse.core.runtime.*;
25 import org.eclipse.jdt.core.*;
26 import org.eclipse.jdt.internal.core.util.Util;
27
28 /**
29  * Keep the global states used during Java element delta processing.
30  */

31 public class DeltaProcessingState implements IResourceChangeListener {
32     
33     /*
34      * Collection of listeners for Java element deltas
35      */

36     public IElementChangedListener[] elementChangedListeners = new IElementChangedListener[5];
37     public int[] elementChangedListenerMasks = new int[5];
38     public int elementChangedListenerCount = 0;
39     
40     /*
41      * Collection of pre Java resource change listeners
42      */

43     public IResourceChangeListener[] preResourceChangeListeners = new IResourceChangeListener[1];
44     public int[] preResourceChangeEventMasks = new int[1];
45     public int preResourceChangeListenerCount = 0;
46
47     /*
48      * The delta processor for the current thread.
49      */

50     private ThreadLocal JavaDoc deltaProcessors = new ThreadLocal JavaDoc();
51     
52     /* A table from IPath (from a classpath entry) to DeltaProcessor.RootInfo */
53     public HashMap roots = new HashMap();
54     
55     /* A table from IPath (from a classpath entry) to ArrayList of DeltaProcessor.RootInfo
56      * Used when an IPath corresponds to more than one root */

57     public HashMap otherRoots = new HashMap();
58     
59     /* A table from IPath (from a classpath entry) to DeltaProcessor.RootInfo
60      * from the last time the delta processor was invoked. */

61     public HashMap oldRoots = new HashMap();
62     
63     /* A table from IPath (from a classpath entry) to ArrayList of DeltaProcessor.RootInfo
64      * from the last time the delta processor was invoked.
65      * Used when an IPath corresponds to more than one root */

66     public HashMap oldOtherRoots = new HashMap();
67     
68     /* A table from IPath (a source attachment path from a classpath entry) to IPath (a root path) */
69     public HashMap sourceAttachments = new HashMap();
70     
71     /* A table from IJavaProject to IJavaProject[] (the list of direct dependent of the key) */
72     public HashMap projectDependencies = new HashMap();
73
74     /* Whether the roots tables should be recomputed */
75     public boolean rootsAreStale = true;
76     
77     /* Threads that are currently running initializeRoots() */
78     private Set initializingThreads = Collections.synchronizedSet(new HashSet());
79     
80     /* A table from file system absoulte path (String) to timestamp (Long) */
81     public Hashtable externalTimeStamps;
82     
83     /* A table from JavaProject to ClasspathValidation */
84     private HashMap classpathValidations = new HashMap();
85     
86     /* A table from JavaProject to ProjectReferenceChange */
87     private HashMap projectReferenceChanges= new HashMap();
88
89     /**
90      * Workaround for bug 15168 circular errors not reported
91      * This is a cache of the projects before any project addition/deletion has started.
92      */

93     private HashSet javaProjectNamesCache;
94     
95     /*
96      * Need to clone defensively the listener information, in case some listener is reacting to some notification iteration by adding/changing/removing
97      * any of the other (for example, if it deregisters itself).
98      */

99     public synchronized void addElementChangedListener(IElementChangedListener listener, int eventMask) {
100         for (int i = 0; i < this.elementChangedListenerCount; i++){
101             if (this.elementChangedListeners[i] == listener){
102                 
103                 // only clone the masks, since we could be in the middle of notifications and one listener decide to change
104
// any event mask of another listeners (yet not notified).
105
int cloneLength = this.elementChangedListenerMasks.length;
106                 System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[cloneLength], 0, cloneLength);
107                 this.elementChangedListenerMasks[i] |= eventMask; // could be different
108
return;
109             }
110         }
111         // may need to grow, no need to clone, since iterators will have cached original arrays and max boundary and we only add to the end.
112
int length;
113         if ((length = this.elementChangedListeners.length) == this.elementChangedListenerCount){
114             System.arraycopy(this.elementChangedListeners, 0, this.elementChangedListeners = new IElementChangedListener[length*2], 0, length);
115             System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[length*2], 0, length);
116         }
117         this.elementChangedListeners[this.elementChangedListenerCount] = listener;
118         this.elementChangedListenerMasks[this.elementChangedListenerCount] = eventMask;
119         this.elementChangedListenerCount++;
120     }
121
122     public synchronized void addPreResourceChangedListener(IResourceChangeListener listener, int eventMask) {
123         for (int i = 0; i < this.preResourceChangeListenerCount; i++){
124             if (this.preResourceChangeListeners[i] == listener) {
125                 this.preResourceChangeEventMasks[i] |= eventMask;
126                 return;
127             }
128         }
129         // may need to grow, no need to clone, since iterators will have cached original arrays and max boundary and we only add to the end.
130
int length;
131         if ((length = this.preResourceChangeListeners.length) == this.preResourceChangeListenerCount) {
132             System.arraycopy(this.preResourceChangeListeners, 0, this.preResourceChangeListeners = new IResourceChangeListener[length*2], 0, length);
133             System.arraycopy(this.preResourceChangeEventMasks, 0, this.preResourceChangeEventMasks = new int[length*2], 0, length);
134         }
135         this.preResourceChangeListeners[this.preResourceChangeListenerCount] = listener;
136         this.preResourceChangeEventMasks[this.preResourceChangeListenerCount] = eventMask;
137         this.preResourceChangeListenerCount++;
138     }
139
140     public DeltaProcessor getDeltaProcessor() {
141         DeltaProcessor deltaProcessor = (DeltaProcessor)this.deltaProcessors.get();
142         if (deltaProcessor != null) return deltaProcessor;
143         deltaProcessor = new DeltaProcessor(this, JavaModelManager.getJavaModelManager());
144         this.deltaProcessors.set(deltaProcessor);
145         return deltaProcessor;
146     }
147
148     public synchronized ClasspathValidation addClasspathValidation(JavaProject project) {
149         ClasspathValidation validation = (ClasspathValidation) this.classpathValidations.get(project);
150         if (validation == null) {
151             validation = new ClasspathValidation(project);
152             this.classpathValidations.put(project, validation);
153         }
154         return validation;
155     }
156     
157     public synchronized void addProjectReferenceChange(JavaProject project, IClasspathEntry[] oldResolvedClasspath) {
158         ProjectReferenceChange change = (ProjectReferenceChange) this.projectReferenceChanges.get(project);
159         if (change == null) {
160             change = new ProjectReferenceChange(project, oldResolvedClasspath);
161             this.projectReferenceChanges.put(project, change);
162         }
163     }
164     
165     public void initializeRoots() {
166         
167         // recompute root infos only if necessary
168
HashMap newRoots = null;
169         HashMap newOtherRoots = null;
170         HashMap newSourceAttachments = null;
171         HashMap newProjectDependencies = null;
172         if (this.rootsAreStale) {
173             Thread JavaDoc currentThread = Thread.currentThread();
174             boolean addedCurrentThread = false;
175             try {
176                 // if reentering initialization (through a container initializer for example) no need to compute roots again
177
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=47213
178
if (!this.initializingThreads.add(currentThread)) return;
179                 addedCurrentThread = true;
180                 
181                 // all classpaths in the workspace are going to be resolved
182
// ensure that containers are initialized in one batch
183
JavaModelManager.getJavaModelManager().batchContainerInitializations = true;
184
185                 newRoots = new HashMap();
186                 newOtherRoots = new HashMap();
187                 newSourceAttachments = new HashMap();
188                 newProjectDependencies = new HashMap();
189         
190                 IJavaModel model = JavaModelManager.getJavaModelManager().getJavaModel();
191                 IJavaProject[] projects;
192                 try {
193                     projects = model.getJavaProjects();
194                 } catch (JavaModelException e) {
195                     // nothing can be done
196
return;
197                 }
198                 for (int i = 0, length = projects.length; i < length; i++) {
199                     JavaProject project = (JavaProject) projects[i];
200                     IClasspathEntry[] classpath;
201                     try {
202                         classpath = project.getResolvedClasspath();
203                     } catch (JavaModelException e) {
204                         // continue with next project
205
continue;
206                     }
207                     for (int j= 0, classpathLength = classpath.length; j < classpathLength; j++) {
208                         IClasspathEntry entry = classpath[j];
209                         if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
210                             IJavaProject key = model.getJavaProject(entry.getPath().segment(0)); // TODO (jerome) reuse handle
211
IJavaProject[] dependents = (IJavaProject[]) newProjectDependencies.get(key);
212                             if (dependents == null) {
213                                 dependents = new IJavaProject[] {project};
214                             } else {
215                                 int dependentsLength = dependents.length;
216                                 System.arraycopy(dependents, 0, dependents = new IJavaProject[dependentsLength+1], 0, dependentsLength);
217                                 dependents[dependentsLength] = project;
218                             }
219                             newProjectDependencies.put(key, dependents);
220                             continue;
221                         }
222                         
223                         // root path
224
IPath path = entry.getPath();
225                         if (newRoots.get(path) == null) {
226                             newRoots.put(path, new DeltaProcessor.RootInfo(project, path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), entry.getEntryKind()));
227                         } else {
228                             ArrayList rootList = (ArrayList)newOtherRoots.get(path);
229                             if (rootList == null) {
230                                 rootList = new ArrayList();
231                                 newOtherRoots.put(path, rootList);
232                             }
233                             rootList.add(new DeltaProcessor.RootInfo(project, path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), entry.getEntryKind()));
234                         }
235                         
236                         // source attachment path
237
if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY) continue;
238                         String JavaDoc propertyString = null;
239                         try {
240                             propertyString = Util.getSourceAttachmentProperty(path);
241                         } catch (JavaModelException e) {
242                             e.printStackTrace();
243                         }
244                         IPath sourceAttachmentPath;
245                         if (propertyString != null) {
246                             int index= propertyString.lastIndexOf(PackageFragmentRoot.ATTACHMENT_PROPERTY_DELIMITER);
247                             sourceAttachmentPath = (index < 0) ? new Path(propertyString) : new Path(propertyString.substring(0, index));
248                         } else {
249                             sourceAttachmentPath = entry.getSourceAttachmentPath();
250                         }
251                         if (sourceAttachmentPath != null) {
252                             newSourceAttachments.put(sourceAttachmentPath, path);
253                         }
254                     }
255                 }
256             } finally {
257                 if (addedCurrentThread) {
258                     this.initializingThreads.remove(currentThread);
259                 }
260             }
261         }
262         synchronized(this) {
263             this.oldRoots = this.roots;
264             this.oldOtherRoots = this.otherRoots;
265             if (this.rootsAreStale && newRoots != null) { // double check again
266
this.roots = newRoots;
267                 this.otherRoots = newOtherRoots;
268                 this.sourceAttachments = newSourceAttachments;
269                 this.projectDependencies = newProjectDependencies;
270                 this.rootsAreStale = false;
271             }
272         }
273     }
274
275     public synchronized ClasspathValidation[] removeClasspathValidations() {
276         int length = this.classpathValidations.size();
277         if (length == 0) return null;
278         ClasspathValidation[] validations = new ClasspathValidation[length];
279         this.classpathValidations.values().toArray(validations);
280         this.classpathValidations.clear();
281         return validations;
282     }
283     
284     public synchronized ProjectReferenceChange[] removeProjectReferenceChanges() {
285         int length = this.projectReferenceChanges.size();
286         if (length == 0) return null;
287         ProjectReferenceChange[] updates = new ProjectReferenceChange[length];
288         this.projectReferenceChanges.values().toArray(updates);
289         this.projectReferenceChanges.clear();
290         return updates;
291     }
292     
293     public synchronized void removeElementChangedListener(IElementChangedListener listener) {
294         
295         for (int i = 0; i < this.elementChangedListenerCount; i++){
296             
297             if (this.elementChangedListeners[i] == listener){
298                 
299                 // need to clone defensively since we might be in the middle of listener notifications (#fire)
300
int length = this.elementChangedListeners.length;
301                 IElementChangedListener[] newListeners = new IElementChangedListener[length];
302                 System.arraycopy(this.elementChangedListeners, 0, newListeners, 0, i);
303                 int[] newMasks = new int[length];
304                 System.arraycopy(this.elementChangedListenerMasks, 0, newMasks, 0, i);
305                 
306                 // copy trailing listeners
307
int trailingLength = this.elementChangedListenerCount - i - 1;
308                 if (trailingLength > 0){
309                     System.arraycopy(this.elementChangedListeners, i+1, newListeners, i, trailingLength);
310                     System.arraycopy(this.elementChangedListenerMasks, i+1, newMasks, i, trailingLength);
311                 }
312                 
313                 // update manager listener state (#fire need to iterate over original listeners through a local variable to hold onto
314
// the original ones)
315
this.elementChangedListeners = newListeners;
316                 this.elementChangedListenerMasks = newMasks;
317                 this.elementChangedListenerCount--;
318                 return;
319             }
320         }
321     }
322
323     public synchronized void removePreResourceChangedListener(IResourceChangeListener listener) {
324         
325         for (int i = 0; i < this.preResourceChangeListenerCount; i++){
326             
327             if (this.preResourceChangeListeners[i] == listener){
328                 
329                 // need to clone defensively since we might be in the middle of listener notifications (#fire)
330
int length = this.preResourceChangeListeners.length;
331                 IResourceChangeListener[] newListeners = new IResourceChangeListener[length];
332                 int[] newEventMasks = new int[length];
333                 System.arraycopy(this.preResourceChangeListeners, 0, newListeners, 0, i);
334                 System.arraycopy(this.preResourceChangeEventMasks, 0, newEventMasks, 0, i);
335                 
336                 // copy trailing listeners
337
int trailingLength = this.preResourceChangeListenerCount - i - 1;
338                 if (trailingLength > 0) {
339                     System.arraycopy(this.preResourceChangeListeners, i+1, newListeners, i, trailingLength);
340                     System.arraycopy(this.preResourceChangeEventMasks, i+1, newEventMasks, i, trailingLength);
341                 }
342                 
343                 // update manager listener state (#fire need to iterate over original listeners through a local variable to hold onto
344
// the original ones)
345
this.preResourceChangeListeners = newListeners;
346                 this.preResourceChangeEventMasks = newEventMasks;
347                 this.preResourceChangeListenerCount--;
348                 return;
349             }
350         }
351     }
352
353     public void resourceChanged(final IResourceChangeEvent event) {
354         for (int i = 0; i < this.preResourceChangeListenerCount; i++) {
355             // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief
356
final IResourceChangeListener listener = this.preResourceChangeListeners[i];
357             if ((this.preResourceChangeEventMasks[i] & event.getType()) != 0)
358                 SafeRunner.run(new ISafeRunnable() {
359                     public void handleException(Throwable JavaDoc exception) {
360                         Util.log(exception, "Exception occurred in listener of pre Java resource change notification"); //$NON-NLS-1$
361
}
362                     public void run() throws Exception JavaDoc {
363                         listener.resourceChanged(event);
364                     }
365                 });
366         }
367         try {
368             getDeltaProcessor().resourceChanged(event);
369         } finally {
370             // TODO (jerome) see 47631, may want to get rid of following so as to reuse delta processor ?
371
if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
372                 this.deltaProcessors.set(null);
373             }
374         }
375
376     }
377     
378     public Hashtable getExternalLibTimeStamps() {
379         if (this.externalTimeStamps == null) {
380             Hashtable timeStamps = new Hashtable();
381             File JavaDoc timestampsFile = getTimeStampsFile();
382             DataInputStream JavaDoc in = null;
383             try {
384                 in = new DataInputStream JavaDoc(new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(timestampsFile)));
385                 int size = in.readInt();
386                 while (size-- > 0) {
387                     String JavaDoc key = in.readUTF();
388                     long timestamp = in.readLong();
389                     timeStamps.put(Path.fromPortableString(key), new Long JavaDoc(timestamp));
390                 }
391             } catch (IOException JavaDoc e) {
392                 if (timestampsFile.exists())
393                     Util.log(e, "Unable to read external time stamps"); //$NON-NLS-1$
394
} finally {
395                 if (in != null) {
396                     try {
397                         in.close();
398                     } catch (IOException JavaDoc e) {
399                         // nothing we can do: ignore
400
}
401                 }
402             }
403             this.externalTimeStamps = timeStamps;
404         }
405         return this.externalTimeStamps;
406     }
407     
408     public IJavaProject findJavaProject(String JavaDoc name) {
409         if (getOldJavaProjecNames().contains(name))
410             return JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(name);
411         return null;
412     }
413     
414     /*
415      * Workaround for bug 15168 circular errors not reported
416      * Returns the list of java projects before resource delta processing
417      * has started.
418      */

419     public synchronized HashSet getOldJavaProjecNames() {
420         if (this.javaProjectNamesCache == null) {
421             HashSet result = new HashSet();
422             IJavaProject[] projects;
423             try {
424                 projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
425             } catch (JavaModelException e) {
426                 return this.javaProjectNamesCache;
427             }
428             for (int i = 0, length = projects.length; i < length; i++) {
429                 IJavaProject project = projects[i];
430                 result.add(project.getElementName());
431             }
432             return this.javaProjectNamesCache = result;
433         }
434         return this.javaProjectNamesCache;
435     }
436     
437     public synchronized void resetOldJavaProjectNames() {
438         this.javaProjectNamesCache = null;
439     }
440     
441     private File JavaDoc getTimeStampsFile() {
442         return JavaCore.getPlugin().getStateLocation().append("externalLibsTimeStamps").toFile(); //$NON-NLS-1$
443
}
444     
445     public void saveExternalLibTimeStamps() throws CoreException {
446         if (this.externalTimeStamps == null) return;
447         File JavaDoc timestamps = getTimeStampsFile();
448         DataOutputStream JavaDoc out = null;
449         try {
450             out = new DataOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(timestamps)));
451             out.writeInt(this.externalTimeStamps.size());
452             Iterator entries = this.externalTimeStamps.entrySet().iterator();
453             while (entries.hasNext()) {
454                 Map.Entry entry = (Map.Entry) entries.next();
455                 IPath key = (IPath) entry.getKey();
456                 out.writeUTF(key.toPortableString());
457                 Long JavaDoc timestamp = (Long JavaDoc) entry.getValue();
458                 out.writeLong(timestamp.longValue());
459             }
460         } catch (IOException JavaDoc e) {
461             IStatus status = new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, "Problems while saving timestamps", e); //$NON-NLS-1$
462
throw new CoreException(status);
463         } finally {
464             if (out != null) {
465                 try {
466                     out.close();
467                 } catch (IOException JavaDoc e) {
468                     // nothing we can do: ignore
469
}
470             }
471         }
472     }
473
474     /*
475      * Update the roots that are affected by the addition or the removal of the given container resource.
476      */

477     public synchronized void updateRoots(IPath containerPath, IResourceDelta containerDelta, DeltaProcessor deltaProcessor) {
478         Map updatedRoots;
479         Map otherUpdatedRoots;
480         if (containerDelta.getKind() == IResourceDelta.REMOVED) {
481             updatedRoots = this.oldRoots;
482             otherUpdatedRoots = this.oldOtherRoots;
483         } else {
484             updatedRoots = this.roots;
485             otherUpdatedRoots = this.otherRoots;
486         }
487         int containerSegmentCount = containerPath.segmentCount();
488         boolean containerIsProject = containerSegmentCount == 1;
489         Iterator iterator = updatedRoots.entrySet().iterator();
490         while (iterator.hasNext()) {
491             Map.Entry entry = (Map.Entry) iterator.next();
492             IPath path = (IPath) entry.getKey();
493             if (containerPath.isPrefixOf(path) && !containerPath.equals(path)) {
494                 IResourceDelta rootDelta = containerDelta.findMember(path.removeFirstSegments(containerSegmentCount));
495                 if (rootDelta == null) continue;
496                 DeltaProcessor.RootInfo rootInfo = (DeltaProcessor.RootInfo) entry.getValue();
497     
498                 if (!containerIsProject
499                         || !rootInfo.project.getPath().isPrefixOf(path)) { // only consider folder roots that are not included in the container
500
deltaProcessor.updateCurrentDeltaAndIndex(rootDelta, IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
501                 }
502                 
503                 ArrayList rootList = (ArrayList)otherUpdatedRoots.get(path);
504                 if (rootList != null) {
505                     Iterator otherProjects = rootList.iterator();
506                     while (otherProjects.hasNext()) {
507                         rootInfo = (DeltaProcessor.RootInfo)otherProjects.next();
508                         if (!containerIsProject
509                                 || !rootInfo.project.getPath().isPrefixOf(path)) { // only consider folder roots that are not included in the container
510
deltaProcessor.updateCurrentDeltaAndIndex(rootDelta, IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
511                         }
512                     }
513                 }
514             }
515         }
516     }
517
518 }
519
Popular Tags