KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.ArrayList JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.Map JavaDoc;
18
19 import org.eclipse.core.resources.IFolder;
20 import org.eclipse.core.resources.IResource;
21 import org.eclipse.core.resources.IWorkspace;
22 import org.eclipse.core.resources.ResourcesPlugin;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IPath;
25 import org.eclipse.jdt.core.IClasspathEntry;
26 import org.eclipse.jdt.core.IJavaElementDelta;
27 import org.eclipse.jdt.core.IPackageFragment;
28 import org.eclipse.jdt.core.IPackageFragmentRoot;
29 import org.eclipse.jdt.core.JavaModelException;
30 import org.eclipse.jdt.internal.compiler.util.ObjectVector;
31 import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
32 import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
33 import org.eclipse.jdt.internal.core.util.Util;
34
35 public class ClasspathChange {
36     public static int NO_DELTA = 0x00;
37     public static int HAS_DELTA = 0x01;
38     public static int HAS_PROJECT_CHANGE = 0x10;
39     
40     JavaProject project;
41     IClasspathEntry[] oldRawClasspath;
42     IPath oldOutputLocation;
43     IClasspathEntry[] oldResolvedClasspath;
44     
45     public ClasspathChange(JavaProject project, IClasspathEntry[] oldRawClasspath, IPath oldOutputLocation, IClasspathEntry[] oldResolvedClasspath) {
46         this.project = project;
47         this.oldRawClasspath = oldRawClasspath;
48         this.oldOutputLocation = oldOutputLocation;
49         this.oldResolvedClasspath = oldResolvedClasspath;
50     }
51     
52     private void addClasspathDeltas(JavaElementDelta delta, IPackageFragmentRoot[] roots, int flag) {
53         for (int i = 0; i < roots.length; i++) {
54             IPackageFragmentRoot root = roots[i];
55             delta.changed(root, flag);
56             if ((flag & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0
57                     || (flag & IJavaElementDelta.F_SOURCEATTACHED) != 0
58                     || (flag & IJavaElementDelta.F_SOURCEDETACHED) != 0){
59                 try {
60                     root.close();
61                 } catch (JavaModelException e) {
62                     // ignore
63
}
64             }
65         }
66     }
67
68     /*
69      * Returns the index of the item in the list if the given list contains the specified entry. If the list does
70      * not contain the entry, -1 is returned.
71      */

72     private int classpathContains(IClasspathEntry[] list, IClasspathEntry entry) {
73         IPath[] exclusionPatterns = entry.getExclusionPatterns();
74         IPath[] inclusionPatterns = entry.getInclusionPatterns();
75         nextEntry: for (int i = 0; i < list.length; i++) {
76             IClasspathEntry other = list[i];
77             if (other.getContentKind() == entry.getContentKind()
78                 && other.getEntryKind() == entry.getEntryKind()
79                 && other.isExported() == entry.isExported()
80                 && other.getPath().equals(entry.getPath())) {
81                     // check custom outputs
82
IPath entryOutput = entry.getOutputLocation();
83                     IPath otherOutput = other.getOutputLocation();
84                     if (entryOutput == null) {
85                         if (otherOutput != null)
86                             continue;
87                     } else {
88                         if (!entryOutput.equals(otherOutput))
89                             continue;
90                     }
91                     
92                     // check inclusion patterns
93
IPath[] otherIncludes = other.getInclusionPatterns();
94                     if (inclusionPatterns != otherIncludes) {
95                         if (inclusionPatterns == null) continue;
96                         int includeLength = inclusionPatterns.length;
97                         if (otherIncludes == null || otherIncludes.length != includeLength)
98                             continue;
99                         for (int j = 0; j < includeLength; j++) {
100                             // compare toStrings instead of IPaths
101
// since IPath.equals is specified to ignore trailing separators
102
if (!inclusionPatterns[j].toString().equals(otherIncludes[j].toString()))
103                                 continue nextEntry;
104                         }
105                     }
106                     // check exclusion patterns
107
IPath[] otherExcludes = other.getExclusionPatterns();
108                     if (exclusionPatterns != otherExcludes) {
109                         if (exclusionPatterns == null) continue;
110                         int excludeLength = exclusionPatterns.length;
111                         if (otherExcludes == null || otherExcludes.length != excludeLength)
112                             continue;
113                         for (int j = 0; j < excludeLength; j++) {
114                             // compare toStrings instead of IPaths
115
// since IPath.equals is specified to ignore trailing separators
116
if (!exclusionPatterns[j].toString().equals(otherExcludes[j].toString()))
117                                 continue nextEntry;
118                         }
119                     }
120                     return i;
121             }
122         }
123         return -1;
124     }
125
126     /*
127      * Recursively adds all subfolders of <code>folder</code> to the given collection.
128      */

129     private void collectAllSubfolders(IFolder folder, ArrayList JavaDoc collection) throws JavaModelException {
130         try {
131             IResource[] members= folder.members();
132             for (int i = 0, max = members.length; i < max; i++) {
133                 IResource r= members[i];
134                 if (r.getType() == IResource.FOLDER) {
135                     collection.add(r);
136                     collectAllSubfolders((IFolder)r, collection);
137                 }
138             }
139         } catch (CoreException e) {
140             throw new JavaModelException(e);
141         }
142     }
143
144     /*
145      * Returns a collection of package fragments that have been added/removed
146      * as the result of changing the output location to/from the given
147      * location. The collection is empty if no package fragments are
148      * affected.
149      */

150     private ArrayList JavaDoc determineAffectedPackageFragments(IPath location) throws JavaModelException {
151         ArrayList JavaDoc fragments = new ArrayList JavaDoc();
152     
153         // see if this will cause any package fragments to be affected
154
IWorkspace workspace = ResourcesPlugin.getWorkspace();
155         IResource resource = null;
156         if (location != null) {
157             resource = workspace.getRoot().findMember(location);
158         }
159         if (resource != null && resource.getType() == IResource.FOLDER) {
160             IFolder folder = (IFolder) resource;
161             // only changes if it actually existed
162
IClasspathEntry[] classpath = this.project.getExpandedClasspath();
163             for (int i = 0; i < classpath.length; i++) {
164                 IClasspathEntry entry = classpath[i];
165                 IPath path = classpath[i].getPath();
166                 if (entry.getEntryKind() != IClasspathEntry.CPE_PROJECT && path.isPrefixOf(location) && !path.equals(location)) {
167                     IPackageFragmentRoot[] roots = this.project.computePackageFragmentRoots(classpath[i]);
168                     PackageFragmentRoot root = (PackageFragmentRoot) roots[0];
169                     // now the output location becomes a package fragment - along with any subfolders
170
ArrayList JavaDoc folders = new ArrayList JavaDoc();
171                     folders.add(folder);
172                     collectAllSubfolders(folder, folders);
173                     Iterator JavaDoc elements = folders.iterator();
174                     int segments = path.segmentCount();
175                     while (elements.hasNext()) {
176                         IFolder f = (IFolder) elements.next();
177                         IPath relativePath = f.getFullPath().removeFirstSegments(segments);
178                         String JavaDoc[] pkgName = relativePath.segments();
179                         IPackageFragment pkg = root.getPackageFragment(pkgName);
180                         if (!Util.isExcluded(pkg))
181                             fragments.add(pkg);
182                     }
183                 }
184             }
185         }
186         return fragments;
187     }
188     
189     public boolean equals(Object JavaDoc obj) {
190         if (!(obj instanceof ClasspathChange))
191             return false;
192         return this.project.equals(((ClasspathChange) obj).project);
193     }
194
195     /*
196      * Generates a classpath change delta for this classpath change.
197      * Returns whether a delta was generated, and whether project reference have changed.
198      */

199     public int generateDelta(JavaElementDelta delta) {
200         JavaModelManager manager = JavaModelManager.getJavaModelManager();
201         DeltaProcessingState state = manager.deltaState;
202         if (state.findJavaProject(this.project.getElementName()) == null)
203             // project doesn't exist yet (we're in an IWorkspaceRunnable)
204
// no need to create a delta here and no need to index (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=133334)
205
// the delta processor will create an ADDED project delta, and index the project
206
return NO_DELTA;
207
208         DeltaProcessor deltaProcessor = state.getDeltaProcessor();
209         IClasspathEntry[] newResolvedClasspath = null;
210         IPath newOutputLocation = null;
211         int result = NO_DELTA;
212         try {
213             PerProjectInfo perProjectInfo = this.project.getPerProjectInfo();
214             
215             // get new info
216
this.project.resolveClasspath(perProjectInfo);
217             IClasspathEntry[] newRawClasspath;
218             
219             // use synchronized block to ensure consistency
220
synchronized (perProjectInfo) {
221                 newRawClasspath = perProjectInfo.rawClasspath;
222                 newResolvedClasspath = perProjectInfo.resolvedClasspath;
223                 newOutputLocation = perProjectInfo.outputLocation;
224             }
225             
226             // check if raw classpath has changed
227
if (this.oldRawClasspath != null && !JavaProject.areClasspathsEqual(this.oldRawClasspath, newRawClasspath, this.oldOutputLocation, newOutputLocation)) {
228                 delta.changed(this.project, IJavaElementDelta.F_CLASSPATH_CHANGED);
229                 result |= HAS_DELTA;
230             }
231                     
232             // if no changes to resolved classpath, nothing more to do
233
if (this.oldResolvedClasspath != null && JavaProject.areClasspathsEqual(this.oldResolvedClasspath, newResolvedClasspath, this.oldOutputLocation, newOutputLocation))
234                 return NO_DELTA;
235             
236             // close cached info
237
this.project.close();
238         } catch (JavaModelException e) {
239             if (DeltaProcessor.VERBOSE) {
240                 e.printStackTrace();
241             }
242             // project no longer exist
243
return NO_DELTA;
244         }
245         
246         if (this.oldResolvedClasspath == null)
247             return NO_DELTA;
248         
249         Map JavaDoc removedRoots = null;
250         IPackageFragmentRoot[] roots = null;
251         Map JavaDoc allOldRoots ;
252         if ((allOldRoots = deltaProcessor.oldRoots) != null) {
253             roots = (IPackageFragmentRoot[]) allOldRoots.get(this.project);
254         }
255         if (roots != null) {
256             removedRoots = new HashMap JavaDoc();
257             for (int i = 0; i < roots.length; i++) {
258                 IPackageFragmentRoot root = roots[i];
259                 removedRoots.put(root.getPath(), root);
260             }
261         }
262
263         int newLength = newResolvedClasspath.length;
264         int oldLength = this.oldResolvedClasspath.length;
265         for (int i = 0; i < oldLength; i++) {
266             int index = classpathContains(newResolvedClasspath, this.oldResolvedClasspath[i]);
267             if (index == -1) {
268                 // remote project changes
269
if (this.oldResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT) {
270                     result |= HAS_PROJECT_CHANGE;
271                     continue;
272                 }
273
274                 IPackageFragmentRoot[] pkgFragmentRoots = null;
275                 if (removedRoots != null) {
276                     IPackageFragmentRoot oldRoot = (IPackageFragmentRoot) removedRoots.get(this.oldResolvedClasspath[i].getPath());
277                     if (oldRoot != null) { // use old root if any (could be none if entry wasn't bound)
278
pkgFragmentRoots = new IPackageFragmentRoot[] { oldRoot };
279                     }
280                 }
281                 if (pkgFragmentRoots == null) {
282                     try {
283                         ObjectVector accumulatedRoots = new ObjectVector();
284                         HashSet JavaDoc rootIDs = new HashSet JavaDoc(5);
285                         rootIDs.add(this.project.rootID());
286                         this.project.computePackageFragmentRoots(
287                             this.oldResolvedClasspath[i],
288                             accumulatedRoots,
289                             rootIDs,
290                             null, // inside original project
291
false, // don't check existency
292
false, // don't retrieve exported roots
293
null); /*no reverse map*/
294                         pkgFragmentRoots = new IPackageFragmentRoot[accumulatedRoots.size()];
295                         accumulatedRoots.copyInto(pkgFragmentRoots);
296                     } catch (JavaModelException e) {
297                         pkgFragmentRoots = new IPackageFragmentRoot[] {};
298                     }
299                 }
300                 addClasspathDeltas(delta, pkgFragmentRoots, IJavaElementDelta.F_REMOVED_FROM_CLASSPATH);
301                 result |= HAS_DELTA;
302             } else {
303                 // remote project changes
304
if (this.oldResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT) {
305                     result |= HAS_PROJECT_CHANGE;
306                     continue;
307                 }
308                 if (index != i) { //reordering of the classpath
309
addClasspathDeltas(delta, this.project.computePackageFragmentRoots(this.oldResolvedClasspath[i]), IJavaElementDelta.F_REORDER);
310                     result |= HAS_DELTA;
311                 }
312                 
313                 // check source attachment
314
IPath newSourcePath = newResolvedClasspath[index].getSourceAttachmentPath();
315                 int sourceAttachmentFlags = getSourceAttachmentDeltaFlag(this.oldResolvedClasspath[i].getSourceAttachmentPath(), newSourcePath);
316                 IPath oldRootPath = this.oldResolvedClasspath[i].getSourceAttachmentRootPath();
317                 IPath newRootPath = newResolvedClasspath[index].getSourceAttachmentRootPath();
318                 int sourceAttachmentRootFlags = getSourceAttachmentDeltaFlag(oldRootPath, newRootPath);
319                 int flags = sourceAttachmentFlags | sourceAttachmentRootFlags;
320                 if (flags != 0) {
321                     addClasspathDeltas(delta, this.project.computePackageFragmentRoots(this.oldResolvedClasspath[i]), flags);
322                     result |= HAS_DELTA;
323                 } else {
324                     if (oldRootPath == null && newRootPath == null) {
325                         // if source path is specified and no root path, it needs to be recomputed dynamically
326
// force detach source on jar package fragment roots (source will be lazily computed when needed)
327
IPackageFragmentRoot[] computedRoots = this.project.computePackageFragmentRoots(this.oldResolvedClasspath[i]);
328                         for (int j = 0; j < computedRoots.length; j++) {
329                             IPackageFragmentRoot root = computedRoots[j];
330                             // force detach source on jar package fragment roots (source will be lazily computed when needed)
331
try {
332                                 root.close();
333                             } catch (JavaModelException e) {
334                                 // ignore
335
}
336                         }
337                     }
338                 }
339             }
340         }
341
342         for (int i = 0; i < newLength; i++) {
343             int index = classpathContains(this.oldResolvedClasspath, newResolvedClasspath[i]);
344             if (index == -1) {
345                 // remote project changes
346
if (newResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT) {
347                     result |= HAS_PROJECT_CHANGE;
348                     continue;
349                 }
350                 addClasspathDeltas(delta, this.project.computePackageFragmentRoots(newResolvedClasspath[i]), IJavaElementDelta.F_ADDED_TO_CLASSPATH);
351                 result |= HAS_DELTA;
352             } // classpath reordering has already been generated in previous loop
353
}
354
355         // see if a change in output location will cause any package fragments to be added/removed
356
if ((newOutputLocation == null && this.oldOutputLocation != null)
357                 || (newOutputLocation != null && !newOutputLocation.equals(this.oldOutputLocation))) {
358             try {
359                 ArrayList JavaDoc added= determineAffectedPackageFragments(this.oldOutputLocation);
360                 Iterator JavaDoc iter = added.iterator();
361                 while (iter.hasNext()){
362                     IPackageFragment frag= (IPackageFragment)iter.next();
363                     ((IPackageFragmentRoot)frag.getParent()).close();
364                     delta.added(frag);
365                     result |= HAS_DELTA;
366                 }
367             
368                 // see if this will cause any package fragments to be removed
369
ArrayList JavaDoc removed= determineAffectedPackageFragments(newOutputLocation);
370                 iter = removed.iterator();
371                 while (iter.hasNext()) {
372                     IPackageFragment frag= (IPackageFragment)iter.next();
373                     ((IPackageFragmentRoot)frag.getParent()).close();
374                     delta.removed(frag);
375                     result |= HAS_DELTA;
376                 }
377             } catch (JavaModelException e) {
378                 if (DeltaProcessor.VERBOSE)
379                     e.printStackTrace();
380             }
381         }
382
383         return result;
384     }
385     
386     /*
387      * Returns the source attachment flag for the delta between the 2 give source paths.
388      * Returns either F_SOURCEATTACHED, F_SOURCEDETACHED, F_SOURCEATTACHED | F_SOURCEDETACHED
389      * or 0 if there is no difference.
390      */

391     private int getSourceAttachmentDeltaFlag(IPath oldPath, IPath newPath) {
392         if (oldPath == null) {
393             if (newPath != null) {
394                 return IJavaElementDelta.F_SOURCEATTACHED;
395             } else {
396                 return 0;
397             }
398         } else if (newPath == null) {
399             return IJavaElementDelta.F_SOURCEDETACHED;
400         } else if (!oldPath.equals(newPath)) {
401             return IJavaElementDelta.F_SOURCEATTACHED | IJavaElementDelta.F_SOURCEDETACHED;
402         } else {
403             return 0;
404         }
405     }
406     
407     public int hashCode() {
408         return this.project.hashCode();
409     }
410
411     /*
412      * Request the indexing of entries that have been added, and remove the index for removed entries.
413      */

414     public void requestIndexing() {
415         IClasspathEntry[] newResolvedClasspath = null;
416         try {
417             newResolvedClasspath = this.project.getResolvedClasspath();
418         } catch (JavaModelException e) {
419             // project doesn't exist
420
return;
421         }
422         
423         JavaModelManager manager = JavaModelManager.getJavaModelManager();
424         IndexManager indexManager = manager.indexManager;
425         if (indexManager == null)
426             return;
427         DeltaProcessingState state = manager.deltaState;
428
429         int newLength = newResolvedClasspath.length;
430         int oldLength = this.oldResolvedClasspath.length;
431         for (int i = 0; i < oldLength; i++) {
432             int index = classpathContains(newResolvedClasspath, this.oldResolvedClasspath[i]);
433             if (index == -1) {
434                 // remote projects are not indexed in this project
435
if (this.oldResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT){
436                     continue;
437                 }
438
439                 // Remove the .java files from the index for a source folder
440
// For a lib folder or a .jar file, remove the corresponding index if not shared.
441
IClasspathEntry oldEntry = this.oldResolvedClasspath[i];
442                 final IPath path = oldEntry.getPath();
443                 int changeKind = this.oldResolvedClasspath[i].getEntryKind();
444                 switch (changeKind) {
445                     case IClasspathEntry.CPE_SOURCE:
446                         char[][] inclusionPatterns = ((ClasspathEntry)oldEntry).fullInclusionPatternChars();
447                         char[][] exclusionPatterns = ((ClasspathEntry)oldEntry).fullExclusionPatternChars();
448                         indexManager.removeSourceFolderFromIndex(this.project, path, inclusionPatterns, exclusionPatterns);
449                         break;
450                     case IClasspathEntry.CPE_LIBRARY:
451                         if (state.otherRoots.get(path) == null) { // if root was not shared
452
indexManager.discardJobs(path.toString());
453                             indexManager.removeIndex(path);
454                             // TODO (kent) we could just remove the in-memory index and have the indexing check for timestamps
455
}
456                         break;
457                 }
458             }
459         }
460
461         for (int i = 0; i < newLength; i++) {
462             int index = classpathContains(this.oldResolvedClasspath, newResolvedClasspath[i]);
463             if (index == -1) {
464                 // remote projects are not indexed in this project
465
if (newResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT){
466                     continue;
467                 }
468                 
469                 // Request indexing
470
int entryKind = newResolvedClasspath[i].getEntryKind();
471                 switch (entryKind) {
472                     case IClasspathEntry.CPE_LIBRARY:
473                         boolean pathHasChanged = true;
474                         IPath newPath = newResolvedClasspath[i].getPath();
475                         for (int j = 0; j < oldLength; j++) {
476                             IClasspathEntry oldEntry = this.oldResolvedClasspath[j];
477                             if (oldEntry.getPath().equals(newPath)) {
478                                 pathHasChanged = false;
479                                 break;
480                             }
481                         }
482                         if (pathHasChanged) {
483                             indexManager.indexLibrary(newPath, this.project.getProject());
484                         }
485                         break;
486                     case IClasspathEntry.CPE_SOURCE:
487                         IClasspathEntry entry = newResolvedClasspath[i];
488                         IPath path = entry.getPath();
489                         char[][] inclusionPatterns = ((ClasspathEntry)entry).fullInclusionPatternChars();
490                         char[][] exclusionPatterns = ((ClasspathEntry)entry).fullExclusionPatternChars();
491                         indexManager.indexSourceFolder(this.project, path, inclusionPatterns, exclusionPatterns);
492                         break;
493                 }
494             }
495         }
496     }
497     
498     public String JavaDoc toString() {
499         return "ClasspathChange: " + this.project.getElementName(); //$NON-NLS-1$
500
}
501 }
502
Popular Tags