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