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