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