6c96e82e760d1df67a4fce6ae70f1ee0ab9b84c2
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / SetClasspathOperation.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.Map;
18
19 import net.sourceforge.phpdt.core.IClasspathEntry;
20 import net.sourceforge.phpdt.core.IJavaElement;
21 import net.sourceforge.phpdt.core.IJavaElementDelta;
22 import net.sourceforge.phpdt.core.IJavaModel;
23 import net.sourceforge.phpdt.core.IJavaModelStatus;
24 import net.sourceforge.phpdt.core.IJavaProject;
25 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
26 import net.sourceforge.phpdt.core.JavaModelException;
27 import net.sourceforge.phpdt.internal.compiler.util.ObjectVector;
28 import net.sourceforge.phpdt.internal.core.util.Util;
29
30 import org.eclipse.core.resources.IFolder;
31 import org.eclipse.core.resources.IProject;
32 import org.eclipse.core.resources.IProjectDescription;
33 import org.eclipse.core.resources.IResource;
34 import org.eclipse.core.resources.IWorkspaceRoot;
35 import org.eclipse.core.runtime.CoreException;
36 import org.eclipse.core.runtime.IPath;
37 import org.eclipse.core.runtime.Path;
38
39 /**
40  * This operation sets an <code>IJavaProject</code>'s classpath.
41  * 
42  * @see IJavaProject
43  */
44 public class SetClasspathOperation extends JavaModelOperation {
45
46         IClasspathEntry[] oldResolvedPath, newResolvedPath;
47
48         IClasspathEntry[] newRawPath;
49
50         boolean canChangeResources;
51
52         boolean classpathWasSaved;
53
54         boolean needCycleCheck;
55
56         boolean needValidation;
57
58         boolean needSave;
59
60         IPath newOutputLocation;
61
62         JavaProject project;
63
64         boolean identicalRoots;
65
66         public static final IClasspathEntry[] ReuseClasspath = new IClasspathEntry[0];
67
68         public static final IClasspathEntry[] UpdateClasspath = new IClasspathEntry[0];
69
70         // if reusing output location, then also reuse clean flag
71         public static final IPath ReuseOutputLocation = new Path(
72                         "Reuse Existing Output Location"); //$NON-NLS-1$
73
74         /**
75          * When executed, this operation sets the classpath of the given project.
76          */
77         public SetClasspathOperation(JavaProject project,
78                         IClasspathEntry[] oldResolvedPath, IClasspathEntry[] newRawPath,
79                         IPath newOutputLocation, boolean canChangeResource,
80                         boolean needValidation, boolean needSave) {
81
82                 super(new IJavaElement[] { project });
83                 this.oldResolvedPath = oldResolvedPath;
84                 this.newRawPath = newRawPath;
85                 this.newOutputLocation = newOutputLocation;
86                 this.canChangeResources = canChangeResource;
87                 this.needValidation = needValidation;
88                 this.needSave = needSave;
89                 this.project = project;
90         }
91
92         /**
93          * Adds deltas for the given roots, with the specified change flag, and
94          * closes the root. Helper method for #setClasspath
95          */
96         protected void addClasspathDeltas(IPackageFragmentRoot[] roots, int flag,
97                         JavaElementDelta delta) {
98
99                 for (int i = 0; i < roots.length; i++) {
100                         IPackageFragmentRoot root = roots[i];
101                         delta.changed(root, flag);
102                         if ((flag & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0
103                                         || (flag & IJavaElementDelta.F_SOURCEATTACHED) != 0
104                                         || (flag & IJavaElementDelta.F_SOURCEDETACHED) != 0) {
105                                 try {
106                                         root.close();
107                                 } catch (JavaModelException e) {
108                                 }
109                                 // force detach source on jar package fragment roots (source
110                                 // will be lazily computed when needed)
111                                 ((PackageFragmentRoot) root).setSourceAttachmentProperty(null);// loose
112                                                                                                                                                                 // info
113                                                                                                                                                                 // -
114                                                                                                                                                                 // will
115                                                                                                                                                                 // be
116                                                                                                                                                                 // recomputed
117                         }
118                 }
119         }
120
121         /**
122          * Returns the index of the item in the list if the given list contains the
123          * specified entry. If the list does not contain the entry, -1 is returned.
124          * A helper method for #setClasspath
125          */
126         protected int classpathContains(IClasspathEntry[] list,
127                         IClasspathEntry entry) {
128
129                 IPath[] exclusionPatterns = entry.getExclusionPatterns();
130                 nextEntry: for (int i = 0; i < list.length; i++) {
131                         IClasspathEntry other = list[i];
132                         if (other.getContentKind() == entry.getContentKind()
133                                         && other.getEntryKind() == entry.getEntryKind()
134                                         && other.isExported() == entry.isExported()
135                                         && other.getPath().equals(entry.getPath())) {
136                                 // check custom outputs
137                                 IPath entryOutput = entry.getOutputLocation();
138                                 IPath otherOutput = other.getOutputLocation();
139                                 if (entryOutput == null) {
140                                         if (otherOutput != null)
141                                                 continue;
142                                 } else {
143                                         if (!entryOutput.equals(otherOutput))
144                                                 continue;
145                                 }
146
147                                 // check exclusion patterns
148                                 IPath[] otherExcludes = other.getExclusionPatterns();
149                                 if (exclusionPatterns != otherExcludes) {
150                                         int excludeLength = exclusionPatterns.length;
151                                         if (otherExcludes.length != excludeLength)
152                                                 continue;
153                                         for (int j = 0; j < excludeLength; j++) {
154                                                 // compare toStrings instead of IPaths
155                                                 // since IPath.equals is specified to ignore trailing
156                                                 // separators
157                                                 if (!exclusionPatterns[j].toString().equals(
158                                                                 otherExcludes[j].toString()))
159                                                         continue nextEntry;
160                                         }
161                                 }
162                                 return i;
163                         }
164                 }
165                 return -1;
166         }
167
168         /**
169          * Recursively adds all subfolders of <code>folder</code> to the given
170          * collection.
171          */
172         protected void collectAllSubfolders(IFolder folder, ArrayList collection)
173                         throws JavaModelException {
174                 try {
175                         IResource[] members = folder.members();
176                         for (int i = 0, max = members.length; i < max; i++) {
177                                 IResource r = members[i];
178                                 if (r.getType() == IResource.FOLDER) {
179                                         collection.add(r);
180                                         collectAllSubfolders((IFolder) r, collection);
181                                 }
182                         }
183                 } catch (CoreException e) {
184                         throw new JavaModelException(e);
185                 }
186         }
187
188         /**
189          * Returns a collection of package fragments that have been added/removed as
190          * the result of changing the output location to/from the given location.
191          * The collection is empty if no package fragments are affected.
192          */
193         // protected ArrayList determineAffectedPackageFragments(IPath location)
194         // throws JavaModelException {
195         // ArrayList fragments = new ArrayList();
196         // JavaProject project =getProject();
197         //      
198         // // see if this will cause any package fragments to be affected
199         // IWorkspace workspace = ResourcesPlugin.getWorkspace();
200         // IResource resource = null;
201         // if (location != null) {
202         // resource = workspace.getRoot().findMember(location);
203         // }
204         // if (resource != null && resource.getType() == IResource.FOLDER) {
205         // IFolder folder = (IFolder) resource;
206         // // only changes if it actually existed
207         // IClasspathEntry[] classpath = project.getExpandedClasspath(true);
208         // for (int i = 0; i < classpath.length; i++) {
209         // IClasspathEntry entry = classpath[i];
210         // IPath path = classpath[i].getPath();
211         // if (entry.getEntryKind() != IClasspathEntry.CPE_PROJECT &&
212         // path.isPrefixOf(location) && !path.equals(location)) {
213         // IPackageFragmentRoot[] roots =
214         // project.computePackageFragmentRoots(classpath[i]);
215         // IPackageFragmentRoot root = roots[0];
216         // // now the output location becomes a package fragment - along with any
217         // subfolders
218         // ArrayList folders = new ArrayList();
219         // folders.add(folder);
220         // collectAllSubfolders(folder, folders);
221         // Iterator elements = folders.iterator();
222         // int segments = path.segmentCount();
223         // while (elements.hasNext()) {
224         // IFolder f = (IFolder) elements.next();
225         // IPath relativePath = f.getFullPath().removeFirstSegments(segments);
226         // String name = relativePath.toOSString();
227         // name = name.replace(File.pathSeparatorChar, '.');
228         // if (name.endsWith(".")) { //$NON-NLS-1$
229         // name = name.substring(0, name.length() - 1);
230         // }
231         // IPackageFragment pkg = root.getPackageFragment(name);
232         // fragments.add(pkg);
233         // }
234         // }
235         // }
236         // }
237         // return fragments;
238         // }
239         /**
240          * Sets the classpath of the pre-specified project.
241          */
242         protected void executeOperation() throws JavaModelException {
243                 // project reference updated - may throw an exception if unable to write
244                 // .project file
245                 updateProjectReferencesIfNecessary();
246
247                 // classpath file updated - may throw an exception if unable to write
248                 // .classpath file
249                 saveClasspathIfNecessary();
250
251                 // perform classpath and output location updates, if exception occurs in
252                 // classpath update,
253                 // make sure the output location is updated before surfacing the
254                 // exception (in case the output
255                 // location update also throws an exception, give priority to the
256                 // classpath update one).
257                 JavaModelException originalException = null;
258
259                 try {
260                         if (this.newRawPath == UpdateClasspath)
261                                 this.newRawPath = project.getRawClasspath();
262                         if (this.newRawPath != ReuseClasspath) {
263                                 updateClasspath();
264                                 project.updatePackageFragmentRoots();
265                                 JavaModelManager.getJavaModelManager().getDeltaProcessor()
266                                                 .addForRefresh(project);
267                         }
268
269                 } catch (JavaModelException e) {
270                         originalException = e;
271                         throw e;
272
273                 } finally { // if traversed by an exception we still need to update the
274                                         // output location when necessary
275
276                         try {
277                                 if (this.newOutputLocation != ReuseOutputLocation)
278                                         updateOutputLocation();
279
280                         } catch (JavaModelException e) {
281                                 if (originalException != null)
282                                         throw originalException;
283                                 throw e;
284                         } finally {
285                                 // ensures the project is getting rebuilt if only variable is
286                                 // modified
287                                 if (!this.identicalRoots && this.canChangeResources) {
288                                         try {
289                                                 this.project.getProject().touch(this.progressMonitor);
290                                         } catch (CoreException e) {
291                                                 if (JavaModelManager.CP_RESOLVE_VERBOSE) {
292                                                         Util
293                                                                         .verbose(
294                                                                                         "CPContainer INIT - FAILED to touch project: " + this.project.getElementName(), System.err); //$NON-NLS-1$
295                                                         e.printStackTrace();
296                                                 }
297                                         }
298                                 }
299                         }
300                 }
301                 done();
302         }
303
304         /**
305          * Generates the delta of removed/added/reordered roots. Use three deltas in
306          * case the same root is removed/added/reordered (for instance, if it is
307          * changed from K_SOURCE to K_BINARY or vice versa)
308          */
309         protected void generateClasspathChangeDeltas() {
310
311                 JavaModelManager manager = JavaModelManager.getJavaModelManager();
312                 boolean needToUpdateDependents = false;
313                 JavaElementDelta delta = new JavaElementDelta(getJavaModel());
314                 boolean hasDelta = false;
315                 if (this.classpathWasSaved) {
316                         delta.changed(this.project, IJavaElementDelta.F_CLASSPATH_CHANGED);
317                         hasDelta = true;
318                 }
319                 int oldLength = oldResolvedPath.length;
320                 int newLength = newResolvedPath.length;
321
322                 // final IndexManager indexManager = manager.getIndexManager();
323                 Map oldRoots = null;
324                 IPackageFragmentRoot[] roots = null;
325                 if (project.isOpen()) {
326                         try {
327                                 roots = project.getPackageFragmentRoots();
328                         } catch (JavaModelException e) {
329                                 // ignore
330                         }
331                 } else {
332                         Map allRemovedRoots;
333                         if ((allRemovedRoots = manager.getDeltaProcessor().removedRoots) != null) {
334                                 roots = (IPackageFragmentRoot[]) allRemovedRoots.get(project);
335                         }
336                 }
337                 if (roots != null) {
338                         oldRoots = new HashMap();
339                         for (int i = 0; i < roots.length; i++) {
340                                 IPackageFragmentRoot root = roots[i];
341                                 oldRoots.put(root.getPath(), root);
342                         }
343                 }
344                 for (int i = 0; i < oldLength; i++) {
345
346                         int index = classpathContains(newResolvedPath, oldResolvedPath[i]);
347                         if (index == -1) {
348                                 // do not notify remote project changes
349                                 if (oldResolvedPath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT) {
350                                         needToUpdateDependents = true;
351                                         this.needCycleCheck = true;
352                                         continue;
353                                 }
354
355                                 IPackageFragmentRoot[] pkgFragmentRoots = null;
356                                 if (oldRoots != null) {
357                                         IPackageFragmentRoot oldRoot = (IPackageFragmentRoot) oldRoots
358                                                         .get(oldResolvedPath[i].getPath());
359                                         if (oldRoot != null) { // use old root if any (could be
360                                                                                         // none if entry wasn't bound)
361                                                 pkgFragmentRoots = new IPackageFragmentRoot[] { oldRoot };
362                                         }
363                                 }
364                                 if (pkgFragmentRoots == null) {
365                                         try {
366                                                 ObjectVector accumulatedRoots = new ObjectVector();
367                                                 HashSet rootIDs = new HashSet(5);
368                                                 rootIDs.add(project.rootID());
369                                                 project.computePackageFragmentRoots(oldResolvedPath[i],
370                                                                 accumulatedRoots, rootIDs, true, // inside
371                                                                                                                                         // original
372                                                                                                                                         // project
373                                                                 false, // don't check existency
374                                                                 false); // don't retrieve exported roots
375                                                 pkgFragmentRoots = new IPackageFragmentRoot[accumulatedRoots
376                                                                 .size()];
377                                                 accumulatedRoots.copyInto(pkgFragmentRoots);
378                                         } catch (JavaModelException e) {
379                                                 pkgFragmentRoots = new IPackageFragmentRoot[] {};
380                                         }
381                                 }
382                                 addClasspathDeltas(pkgFragmentRoots,
383                                                 IJavaElementDelta.F_REMOVED_FROM_CLASSPATH, delta);
384
385                                 int changeKind = oldResolvedPath[i].getEntryKind();
386                                 needToUpdateDependents |= (changeKind == IClasspathEntry.CPE_SOURCE)
387                                                 || oldResolvedPath[i].isExported();
388
389                                 // Remove the .java files from the index for a source folder
390                                 // For a lib folder or a .jar file, remove the corresponding
391                                 // index if not shared.
392                                 // if (indexManager != null) {
393                                 // IClasspathEntry oldEntry = oldResolvedPath[i];
394                                 // final IPath path = oldEntry.getPath();
395                                 // switch (changeKind) {
396                                 // case IClasspathEntry.CPE_SOURCE:
397                                 // final char[][] inclusionPatterns = null;
398                                 // //((ClasspathEntry)oldEntry).fullInclusionPatternChars();
399                                 // final char[][] exclusionPatterns =
400                                 // ((ClasspathEntry)oldEntry).fullExclusionPatternChars();
401                                 // postAction(new IPostAction() {
402                                 // public String getID() {
403                                 // return path.toString();
404                                 // }
405                                 // public void run() /* throws JavaModelException */ {
406                                 // indexManager.removeSourceFolderFromIndex(project, path,
407                                 // inclusionPatterns, exclusionPatterns);
408                                 // }
409                                 // },
410                                 // REMOVEALL_APPEND);
411                                 // break;
412                                 // case IClasspathEntry.CPE_LIBRARY:
413                                 // final DeltaProcessingState deltaState = manager.deltaState;
414                                 // postAction(new IPostAction() {
415                                 // public String getID() {
416                                 // return path.toString();
417                                 // }
418                                 // public void run() /* throws JavaModelException */ {
419                                 // if (deltaState.otherRoots.get(path) == null) { // if root was
420                                 // not shared
421                                 // indexManager.discardJobs(path.toString());
422                                 // indexManager.removeIndex(path);
423                                 // // TODO (kent) we could just remove the in-memory index and
424                                 // have the indexing check for timestamps
425                                 // }
426                                 // }
427                                 // },
428                                 // REMOVEALL_APPEND);
429                                 // break;
430                                 // }
431                                 // }
432                                 hasDelta = true;
433
434                         } else {
435                                 // do not notify remote project changes
436                                 if (oldResolvedPath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT) {
437                                         this.needCycleCheck |= (oldResolvedPath[i].isExported() != newResolvedPath[index]
438                                                         .isExported());
439                                         continue;
440                                 }
441                                 needToUpdateDependents |= (oldResolvedPath[i].isExported() != newResolvedPath[index]
442                                                 .isExported());
443                                 if (index != i) { // reordering of the classpath
444                                         addClasspathDeltas(project
445                                                         .computePackageFragmentRoots(oldResolvedPath[i]),
446                                                         IJavaElementDelta.F_REORDER, delta);
447                                         int changeKind = oldResolvedPath[i].getEntryKind();
448                                         needToUpdateDependents |= (changeKind == IClasspathEntry.CPE_SOURCE);
449
450                                         hasDelta = true;
451                                 }
452
453                                 // check source attachment
454                                 IPath newSourcePath = newResolvedPath[index]
455                                                 .getSourceAttachmentPath();
456                                 int sourceAttachmentFlags = this.getSourceAttachmentDeltaFlag(
457                                                 oldResolvedPath[i].getSourceAttachmentPath(),
458                                                 newSourcePath);
459                                 IPath oldRootPath = oldResolvedPath[i]
460                                                 .getSourceAttachmentRootPath();
461                                 IPath newRootPath = newResolvedPath[index]
462                                                 .getSourceAttachmentRootPath();
463                                 int sourceAttachmentRootFlags = getSourceAttachmentDeltaFlag(
464                                                 oldRootPath, newRootPath);
465                                 int flags = sourceAttachmentFlags | sourceAttachmentRootFlags;
466                                 if (flags != 0) {
467                                         addClasspathDeltas(project
468                                                         .computePackageFragmentRoots(oldResolvedPath[i]),
469                                                         flags, delta);
470                                         hasDelta = true;
471                                 } else {
472                                         if (oldRootPath == null && newRootPath == null) {
473                                                 // if source path is specified and no root path, it
474                                                 // needs to be recomputed dynamically
475                                                 // force detach source on jar package fragment roots
476                                                 // (source will be lazily computed when needed)
477                                                 IPackageFragmentRoot[] computedRoots = project
478                                                                 .computePackageFragmentRoots(oldResolvedPath[i]);
479                                                 for (int j = 0; j < computedRoots.length; j++) {
480                                                         IPackageFragmentRoot root = computedRoots[j];
481                                                         // force detach source on jar package fragment roots
482                                                         // (source will be lazily computed when needed)
483                                                         try {
484                                                                 root.close();
485                                                         } catch (JavaModelException e) {
486                                                                 // ignore
487                                                         }
488                                                         ((PackageFragmentRoot) root)
489                                                                         .setSourceAttachmentProperty(null);// loose
490                                                                                                                                                 // info
491                                                                                                                                                 // -
492                                                                                                                                                 // will
493                                                                                                                                                 // be
494                                                                                                                                                 // recomputed
495                                                 }
496                                         }
497                                 }
498                         }
499                 }
500
501                 for (int i = 0; i < newLength; i++) {
502
503                         int index = classpathContains(oldResolvedPath, newResolvedPath[i]);
504                         if (index == -1) {
505                                 // do not notify remote project changes
506                                 if (newResolvedPath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT) {
507                                         needToUpdateDependents = true;
508                                         this.needCycleCheck = true;
509                                         continue;
510                                 }
511                                 addClasspathDeltas(project
512                                                 .computePackageFragmentRoots(newResolvedPath[i]),
513                                                 IJavaElementDelta.F_ADDED_TO_CLASSPATH, delta);
514                                 int changeKind = newResolvedPath[i].getEntryKind();
515
516                                 // Request indexing
517                                 // if (indexManager != null) {
518                                 // switch (changeKind) {
519                                 // case IClasspathEntry.CPE_LIBRARY:
520                                 // boolean pathHasChanged = true;
521                                 // final IPath newPath = newResolvedPath[i].getPath();
522                                 // for (int j = 0; j < oldLength; j++) {
523                                 // IClasspathEntry oldEntry = oldResolvedPath[j];
524                                 // if (oldEntry.getPath().equals(newPath)) {
525                                 // pathHasChanged = false;
526                                 // break;
527                                 // }
528                                 // }
529                                 // if (pathHasChanged) {
530                                 // postAction(new IPostAction() {
531                                 // public String getID() {
532                                 // return newPath.toString();
533                                 // }
534                                 // public void run() /* throws JavaModelException */ {
535                                 // indexManager.indexLibrary(newPath, project.getProject());
536                                 // }
537                                 // },
538                                 // REMOVEALL_APPEND);
539                                 // }
540                                 // break;
541                                 // case IClasspathEntry.CPE_SOURCE:
542                                 // IClasspathEntry entry = newResolvedPath[i];
543                                 // final IPath path = entry.getPath();
544                                 // final char[][] inclusionPatterns = null;
545                                 // //((ClasspathEntry)entry).fullInclusionPatternChars();
546                                 // final char[][] exclusionPatterns =
547                                 // ((ClasspathEntry)entry).fullExclusionPatternChars();
548                                 // postAction(new IPostAction() {
549                                 // public String getID() {
550                                 // return path.toString();
551                                 // }
552                                 // public void run() /* throws JavaModelException */ {
553                                 // indexManager.indexSourceFolder(project, path,
554                                 // inclusionPatterns, exclusionPatterns);
555                                 // }
556                                 // },
557                                 // APPEND); // append so that a removeSourceFolder action is not
558                                 // removed
559                                 // break;
560                                 // }
561                                 // }
562
563                                 needToUpdateDependents |= (changeKind == IClasspathEntry.CPE_SOURCE)
564                                                 || newResolvedPath[i].isExported();
565                                 hasDelta = true;
566
567                         } // classpath reordering has already been generated in previous
568                                 // loop
569                 }
570
571                 if (hasDelta) {
572                         this.addDelta(delta);
573                 } else {
574                         this.identicalRoots = true;
575                 }
576                 if (needToUpdateDependents) {
577                         updateAffectedProjects(project.getProject().getFullPath());
578                 }
579         }
580
581         protected void saveClasspathIfNecessary() throws JavaModelException {
582
583                 if (!this.canChangeResources || !this.needSave)
584                         return;
585
586                 IClasspathEntry[] classpathForSave;
587                 if (this.newRawPath == ReuseClasspath
588                                 || this.newRawPath == UpdateClasspath) {
589                         classpathForSave = project.getRawClasspath();
590                 } else {
591                         classpathForSave = this.newRawPath;
592                 }
593                 IPath outputLocationForSave;
594                 if (this.newOutputLocation == ReuseOutputLocation) {
595                         outputLocationForSave = project.getOutputLocation();
596                 } else {
597                         outputLocationForSave = this.newOutputLocation;
598                 }
599                 // if read-only .classpath, then the classpath setting will never been
600                 // performed completely
601                 if (project.saveClasspath(classpathForSave, outputLocationForSave)) {
602                         this.classpathWasSaved = true;
603                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
604                 }
605         }
606
607         protected JavaProject getProject() {
608                 return ((JavaProject) getElementsToProcess()[0]);
609         }
610
611         /*
612          * Returns the source attachment flag for the delta between the 2 give
613          * source paths. Returns either F_SOURCEATTACHED, F_SOURCEDETACHED,
614          * F_SOURCEATTACHED | F_SOURCEDETACHED or 0 if there is no difference.
615          */
616 //      private int getSourceAttachmentDeltaFlag(IPath oldPath, IPath newPath,
617 //                      IPath sourcePath) {
618 //              if (oldPath == null) {
619 //                      if (newPath != null) {
620 //                              return IJavaElementDelta.F_SOURCEATTACHED;
621 //                      } else {
622 //                              if (sourcePath != null) {
623 //                                      // if source path is specified and no root path, it needs to
624 //                                      // be recomputed dynamically
625 //                                      return IJavaElementDelta.F_SOURCEATTACHED
626 //                                                      | IJavaElementDelta.F_SOURCEDETACHED;
627 //                              } else {
628 //                                      return 0;
629 //                              }
630 //                      }
631 //              } else if (newPath == null) {
632 //                      return IJavaElementDelta.F_SOURCEDETACHED;
633 //              } else if (!oldPath.equals(newPath)) {
634 //                      return IJavaElementDelta.F_SOURCEATTACHED
635 //                                      | IJavaElementDelta.F_SOURCEDETACHED;
636 //              } else {
637 //                      return 0;
638 //              }
639 //      }
640
641         /*
642          * Returns the source attachment flag for the delta between the 2 give
643          * source paths. Returns either F_SOURCEATTACHED, F_SOURCEDETACHED,
644          * F_SOURCEATTACHED | F_SOURCEDETACHED or 0 if there is no difference.
645          */
646         private int getSourceAttachmentDeltaFlag(IPath oldPath, IPath newPath) {
647                 if (oldPath == null) {
648                         if (newPath != null) {
649                                 return IJavaElementDelta.F_SOURCEATTACHED;
650                         } else {
651                                 return 0;
652                         }
653                 } else if (newPath == null) {
654                         return IJavaElementDelta.F_SOURCEDETACHED;
655                 } else if (!oldPath.equals(newPath)) {
656                         return IJavaElementDelta.F_SOURCEATTACHED
657                                         | IJavaElementDelta.F_SOURCEDETACHED;
658                 } else {
659                         return 0;
660                 }
661         }
662
663         /**
664          * Returns <code>true</code> if this operation performs no resource
665          * modifications, otherwise <code>false</code>. Subclasses must override.
666          */
667         public boolean isReadOnly() {
668                 return !this.canChangeResources;
669         }
670
671         // protected void saveClasspathIfNecessary() throws JavaModelException {
672         //              
673         // if (!this.canChangeResources || !this.needSave) return;
674         //                              
675         // IClasspathEntry[] classpathForSave;
676         // JavaProject project = getProject();
677         // if (this.newRawPath == ReuseClasspath || this.newRawPath ==
678         // UpdateClasspath){
679         // classpathForSave = project.getRawClasspath();
680         // } else {
681         // classpathForSave = this.newRawPath;
682         // }
683         // IPath outputLocationForSave;
684         // if (this.newOutputLocation == ReuseOutputLocation){
685         // outputLocationForSave = project.getOutputLocation();
686         // } else {
687         // outputLocationForSave = this.newOutputLocation;
688         // }
689         // // if read-only .classpath, then the classpath setting will never been
690         // performed completely
691         // if (project.saveClasspath(classpathForSave, outputLocationForSave)) {
692         // this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
693         // }
694         // }
695
696         public String toString() {
697                 StringBuffer buffer = new StringBuffer(20);
698                 buffer.append("SetClasspathOperation\n"); //$NON-NLS-1$
699                 buffer.append(" - classpath : "); //$NON-NLS-1$
700                 if (this.newRawPath == ReuseClasspath) {
701                         buffer.append("<Reuse Existing Classpath>"); //$NON-NLS-1$
702                 } else {
703                         buffer.append("{"); //$NON-NLS-1$
704                         for (int i = 0; i < this.newRawPath.length; i++) {
705                                 if (i > 0)
706                                         buffer.append(","); //$NON-NLS-1$
707                                 IClasspathEntry element = this.newRawPath[i];
708                                 buffer.append(" ").append(element.toString()); //$NON-NLS-1$
709                         }
710                 }
711                 buffer.append("\n - output location : "); //$NON-NLS-1$
712                 if (this.newOutputLocation == ReuseOutputLocation) {
713                         buffer.append("<Reuse Existing Output Location>"); //$NON-NLS-1$
714                 } else {
715                         buffer.append(this.newOutputLocation.toString()); //$NON-NLS-1$
716                 }
717                 return buffer.toString();
718         }
719
720         // private void updateClasspath() throws JavaModelException {
721         //
722         // JavaProject project = ((JavaProject) getElementsToProcess()[0]);
723         //
724         // beginTask(Util.bind("classpath.settingProgress",
725         // project.getElementName()), 2); //$NON-NLS-1$
726         //
727         // // SIDE-EFFECT: from thereon, the classpath got modified
728         // project.setRawClasspath0(this.newRawPath);
729         //
730         // // resolve new path (asking for marker creation if problems)
731         // if (this.newResolvedPath == null) {
732         // this.newResolvedPath = project.getResolvedClasspath(true,
733         // this.canChangeResources);
734         // }
735         //              
736         // // if (this.oldResolvedPath != null) {
737         // // generateClasspathChangeDeltas(
738         // // this.oldResolvedPath,
739         // // this.newResolvedPath,
740         // // project);
741         // // } else {
742         // this.needCycleCheck = true;
743         // updateAffectedProjects(project.getProject().getFullPath());
744         // // }
745         //              
746         // updateCycleMarkersIfNecessary(newResolvedPath);
747         // }
748         private void updateClasspath() throws JavaModelException {
749
750                 beginTask(Util.bind(
751                                 "classpath.settingProgress", project.getElementName()), 2); //$NON-NLS-1$
752
753                 // SIDE-EFFECT: from thereon, the classpath got modified
754                 project.getPerProjectInfo().updateClasspathInformation(this.newRawPath);
755
756                 // resolve new path (asking for marker creation if problems)
757                 if (this.newResolvedPath == null) {
758                         this.newResolvedPath = project
759                                         .getResolvedClasspath(true, this.canChangeResources, false/*
760                                                                                                                                                                  * don't
761                                                                                                                                                                  * returnResolutionInProgress
762                                                                                                                                                                  */);
763                 }
764
765                 if (this.oldResolvedPath != null) {
766                         generateClasspathChangeDeltas();
767                 } else {
768                         this.needCycleCheck = true;
769                         updateAffectedProjects(project.getProject().getFullPath());
770                 }
771
772                 updateCycleMarkersIfNecessary();
773         }
774
775         /**
776          * Update projects which are affected by this classpath change: those which
777          * refers to the current project as source
778          */
779         protected void updateAffectedProjects(IPath prerequisiteProjectPath) {
780
781                 try {
782                         IJavaModel model = JavaModelManager.getJavaModelManager()
783                                         .getJavaModel();
784                         IJavaProject originatingProject = getProject();
785                         IJavaProject[] projects = model.getJavaProjects();
786                         for (int i = 0, projectCount = projects.length; i < projectCount; i++) {
787                                 try {
788                                         JavaProject project = (JavaProject) projects[i];
789                                         if (project.equals(originatingProject))
790                                                 continue; // skip itself
791
792                                         // consider ALL dependents (even indirect ones), since they
793                                         // may need to
794                                         // flush their respective namelookup caches (all pkg
795                                         // fragment roots).
796
797                                         IClasspathEntry[] classpath = project
798                                                         .getExpandedClasspath(true);
799                                         for (int j = 0, entryCount = classpath.length; j < entryCount; j++) {
800                                                 IClasspathEntry entry = classpath[j];
801                                                 if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT
802                                                                 && entry.getPath().equals(
803                                                                                 prerequisiteProjectPath)) {
804                                                         project.setRawClasspath(UpdateClasspath,
805                                                                         SetClasspathOperation.ReuseOutputLocation,
806                                                                         this.progressMonitor,
807                                                                         this.canChangeResources, project
808                                                                                         .getResolvedClasspath(true), false, // updating
809                                                                                                                                                                 // only
810                                                                                                                                                                 // - no
811                                                                                                                                                                 // validation
812                                                                         false); // updating only - no need to save
813                                                         break;
814                                                 }
815                                         }
816                                 } catch (JavaModelException e) {
817                                 }
818                         }
819                 } catch (JavaModelException e) {
820                 }
821
822         }
823
824         /**
825          * Update cycle markers
826          */
827         protected void updateCycleMarkersIfNecessary() {
828
829                 if (!this.needCycleCheck)
830                         return;
831                 if (!this.canChangeResources)
832                         return;
833
834                 if (!project.hasCycleMarker()
835                                 && !project.hasClasspathCycle(newResolvedPath)) {
836                         return;
837                 }
838
839                 postAction(new IPostAction() {
840                         public String getID() {
841                                 return "updateCycleMarkers"; //$NON-NLS-1$
842                         }
843
844                         public void run() throws JavaModelException {
845                                 JavaProject.updateAllCycleMarkers();
846                         }
847                 }, REMOVEALL_APPEND);
848         }
849
850         // /**
851         // * Update cycle markers
852         // */
853         // protected void updateCycleMarkersIfNecessary(IClasspathEntry[]
854         // newResolvedPath) {
855         //
856         // if (!this.needCycleCheck) return;
857         // if (!this.canChangeResources) return;
858         //               
859         // try {
860         // JavaProject project = getProject();
861         // if (!project.hasCycleMarker() &&
862         // !project.hasClasspathCycle(project.getResolvedClasspath(true))){
863         // return;
864         // }
865         //              
866         // postAction(
867         // new IPostAction() {
868         // public String getID() {
869         // return "updateCycleMarkers"; //$NON-NLS-1$
870         // }
871         // public void run() throws JavaModelException {
872         // JavaProject.updateAllCycleMarkers();
873         // }
874         // },
875         // REMOVEALL_APPEND);
876         // } catch(JavaModelException e){
877         // }
878         // }
879
880         /**
881          * Sets the output location of the pre-specified project.
882          * 
883          * <p>
884          * This can cause changes in package fragments, in case either the old or
885          * new output location folder are considered as a package fragment.
886          */
887         protected void updateOutputLocation() throws JavaModelException {
888
889                 JavaProject project = ((JavaProject) getElementsToProcess()[0]);
890
891                 beginTask(
892                                 Util
893                                                 .bind(
894                                                                 "classpath.settingOutputLocationProgress", project.getElementName()), 2); //$NON-NLS-1$
895
896                 IPath oldLocation = project.getOutputLocation();
897
898                 // see if this will cause any package fragments to be added
899                 boolean deltaToFire = false;
900                 JavaElementDelta delta = newJavaElementDelta();
901                 // ArrayList added= determineAffectedPackageFragments(oldLocation);
902                 // Iterator iter = added.iterator();
903                 // while (iter.hasNext()){
904                 // IPackageFragment frag= (IPackageFragment)iter.next();
905                 // ((IPackageFragmentRoot)frag.getParent()).close();
906                 // if (!ProjectPrefUtil.isExcluded(frag)) {
907                 // delta.added(frag);
908                 // deltaToFire = true;
909                 // }
910                 // }
911
912                 // see if this will cause any package fragments to be removed
913                 // ArrayList removed=
914                 // determineAffectedPackageFragments(this.newOutputLocation);
915                 // iter = removed.iterator();
916                 // while (iter.hasNext()){
917                 // IPackageFragment frag= (IPackageFragment)iter.next();
918                 // ((IPackageFragmentRoot)frag.getParent()).close();
919                 // if (!ProjectPrefUtil.isExcluded(frag)) {
920                 // delta.removed(frag);
921                 // deltaToFire = true;
922                 // }
923                 // }
924
925                 JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager
926                                 .getJavaModelManager().getPerProjectInfoCheckExistence(
927                                                 project.getProject());
928                 synchronized (perProjectInfo) {
929                         perProjectInfo.outputLocation = this.newOutputLocation;
930                 }
931
932                 if (deltaToFire) {
933                         addDelta(delta);
934                 }
935                 worked(1);
936         }
937
938         /**
939          * Update projects references so that the build order is consistent with the
940          * classpath
941          */
942         protected void updateProjectReferencesIfNecessary()
943                         throws JavaModelException {
944
945                 if (!this.canChangeResources)
946                         return;
947                 if (this.newRawPath == ReuseClasspath
948                                 || this.newRawPath == UpdateClasspath)
949                         return;
950
951                 JavaProject jproject = getProject();
952                 String[] oldRequired = jproject
953                                 .projectPrerequisites(this.oldResolvedPath);
954
955                 if (this.newResolvedPath == null) {
956                         this.newResolvedPath = jproject
957                                         .getResolvedClasspath(this.newRawPath, null, true,
958                                                         this.needValidation, null /* no reverse map */);
959                 }
960                 String[] newRequired = jproject
961                                 .projectPrerequisites(this.newResolvedPath);
962
963                 try {
964                         IProject project = jproject.getProject();
965                         IProjectDescription description = project.getDescription();
966
967                         IProject[] projectReferences = description.getReferencedProjects();
968
969                         HashSet oldReferences = new HashSet(projectReferences.length);
970                         for (int i = 0; i < projectReferences.length; i++) {
971                                 String projectName = projectReferences[i].getName();
972                                 oldReferences.add(projectName);
973                         }
974                         HashSet newReferences = (HashSet) oldReferences.clone();
975
976                         for (int i = 0; i < oldRequired.length; i++) {
977                                 String projectName = oldRequired[i];
978                                 newReferences.remove(projectName);
979                         }
980                         for (int i = 0; i < newRequired.length; i++) {
981                                 String projectName = newRequired[i];
982                                 newReferences.add(projectName);
983                         }
984
985                         Iterator iter;
986                         int newSize = newReferences.size();
987
988                         checkIdentity: {
989                                 if (oldReferences.size() == newSize) {
990                                         iter = newReferences.iterator();
991                                         while (iter.hasNext()) {
992                                                 if (!oldReferences.contains(iter.next())) {
993                                                         break checkIdentity;
994                                                 }
995                                         }
996                                         return;
997                                 }
998                         }
999                         String[] requiredProjectNames = new String[newSize];
1000                         int index = 0;
1001                         iter = newReferences.iterator();
1002                         while (iter.hasNext()) {
1003                                 requiredProjectNames[index++] = (String) iter.next();
1004                         }
1005                         Util.sort(requiredProjectNames); // ensure that if changed, the
1006                                                                                                 // order is consistent
1007
1008                         IProject[] requiredProjectArray = new IProject[newSize];
1009                         IWorkspaceRoot wksRoot = project.getWorkspace().getRoot();
1010                         for (int i = 0; i < newSize; i++) {
1011                                 requiredProjectArray[i] = wksRoot
1012                                                 .getProject(requiredProjectNames[i]);
1013                         }
1014
1015                         description.setReferencedProjects(requiredProjectArray);
1016                         project.setDescription(description, this.progressMonitor);
1017
1018                 } catch (CoreException e) {
1019                         throw new JavaModelException(e);
1020                 }
1021         }
1022
1023         public IJavaModelStatus verify() {
1024
1025                 IJavaModelStatus status = super.verify();
1026                 if (!status.isOK()) {
1027                         return status;
1028                 }
1029
1030                 if (needValidation) {
1031                         IJavaProject project = (IJavaProject) getElementToProcess();
1032                         // retrieve classpath
1033                         IClasspathEntry[] entries = this.newRawPath;
1034                         if (entries == ReuseClasspath) {
1035                                 try {
1036                                         entries = project.getRawClasspath();
1037                                 } catch (JavaModelException e) {
1038                                         return e.getJavaModelStatus();
1039                                 }
1040                         }
1041                         // retrieve output location
1042                         IPath outputLocation = this.newOutputLocation;
1043                         if (outputLocation == ReuseOutputLocation) {
1044                                 try {
1045                                         outputLocation = project.getOutputLocation();
1046                                 } catch (JavaModelException e) {
1047                                         return e.getJavaModelStatus();
1048                                 }
1049                         }
1050
1051                         // perform validation
1052                         // return JavaConventions.validateClasspath(
1053                         // project,
1054                         // entries,
1055                         // outputLocation);
1056                 }
1057
1058                 return JavaModelStatus.VERIFIED_OK;
1059         }
1060 }