KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > resources > Workspace


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Red Hat Incorporated - loadProjectDescription(InputStream)
11  *******************************************************************************/

12 package org.eclipse.core.internal.resources;
13
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.net.URI JavaDoc;
17 import java.util.*;
18 import org.eclipse.core.internal.events.*;
19 import org.eclipse.core.internal.localstore.FileSystemResourceManager;
20 import org.eclipse.core.internal.properties.IPropertyManager;
21 import org.eclipse.core.internal.refresh.RefreshManager;
22 import org.eclipse.core.internal.utils.*;
23 import org.eclipse.core.internal.watson.*;
24 import org.eclipse.core.resources.*;
25 import org.eclipse.core.resources.team.*;
26 import org.eclipse.core.runtime.*;
27 import org.eclipse.core.runtime.jobs.ISchedulingRule;
28 import org.eclipse.core.runtime.jobs.Job;
29 import org.eclipse.osgi.util.NLS;
30 import org.osgi.framework.Bundle;
31 import org.xml.sax.InputSource JavaDoc;
32
33 /**
34  * The workspace class is the monolithic nerve center of the resources plugin.
35  * All interesting functionality stems from this class.
36  * </p>
37  * <p>
38  * The lifecycle of the resources plugin is encapsulated by the {@link #open(IProgressMonitor)}
39  * and {@link #close(IProgressMonitor)} methods. A closed workspace is completely
40  * unusable - any attempt to access or modify interesting workspace state on a closed
41  * workspace will fail.
42  * </p>
43  * <p>
44  * All modifications to the workspace occur within the context of a workspace operation.
45  * A workspace operation is implemented using the following sequence:
46  * <pre>
47  * try {
48  * prepareOperation(...);
49  * //check preconditions
50  * beginOperation(...);
51  * //perform changes
52  * } finally {
53  * endOperation(...);
54  * }
55  * </pre>
56  * Workspace operations can be nested arbitrarily. A "top level" workspace operation
57  * is an operation that is not nested within another workspace operation in the current
58  * thread.
59  * See the javadoc of {@link #prepareOperation(ISchedulingRule, IProgressMonitor)},
60  * {@link #beginOperation(boolean)}, and {@link #endOperation(ISchedulingRule, boolean, IProgressMonitor)}
61  * for more details.
62  * </p>
63  * <p>
64  * Major areas of functionality are farmed off to various manager classes. Open a
65  * type hierarchy on {@link IManager} to see all the different managers. Each
66  * manager is typically referenced three times in this class: Once in {@link #startup(IProgressMonitor)}
67  * when it is instantiated, once in {@link #shutdown(IProgressMonitor)} when it
68  * is destroyed, and once in a manager accessor method.
69  * </p>
70  */

71 public class Workspace extends PlatformObject implements IWorkspace, ICoreConstants {
72     public static final boolean caseSensitive = Platform.OS_MACOSX.equals(Platform.getOS()) ? false : new java.io.File JavaDoc("a").compareTo(new java.io.File JavaDoc("A")) != 0; //$NON-NLS-1$ //$NON-NLS-2$
73
// whether the resources plugin is in debug mode.
74
public static boolean DEBUG = false;
75
76     /**
77      * Work manager should never be accessed directly because accessor
78      * asserts that workspace is still open.
79      */

80     protected WorkManager _workManager;
81     protected AliasManager aliasManager;
82     protected BuildManager buildManager;
83     protected IProject[] buildOrder = null;
84     protected CharsetManager charsetManager;
85     protected ContentDescriptionManager contentDescriptionManager;
86     /** indicates if the workspace crashed in a previous session */
87     protected boolean crashed = false;
88     protected final IWorkspaceRoot defaultRoot = new WorkspaceRoot(Path.ROOT, this);
89     protected WorkspacePreferences description;
90     protected FileSystemResourceManager fileSystemManager;
91     protected final HashSet lifecycleListeners = new HashSet(10);
92     protected LocalMetaArea localMetaArea;
93     /**
94      * Helper class for performing validation of resource names and locations.
95      */

96     protected final LocationValidator locationValidator = new LocationValidator(this);
97     protected MarkerManager markerManager;
98     /**
99      * The currently installed Move/Delete hook.
100      */

101     protected IMoveDeleteHook moveDeleteHook = null;
102     protected NatureManager natureManager;
103     protected long nextMarkerId = 0;
104     protected long nextNodeId = 1;
105
106     protected NotificationManager notificationManager;
107     protected boolean openFlag = false;
108     protected ElementTree operationTree; // tree at the start of the current operation
109
protected PathVariableManager pathVariableManager;
110     protected IPropertyManager propertyManager;
111
112     protected RefreshManager refreshManager;
113
114     /**
115      * Scheduling rule factory
116      */

117     private IResourceRuleFactory ruleFactory;
118
119     protected SaveManager saveManager;
120     /**
121      * File modification validation. If it is true and validator is null, we try/initialize
122      * validator first time through. If false, there is no validator.
123      */

124     protected boolean shouldValidate = true;
125
126     /**
127      * Job that performs periodic string pool canonicalization.
128      */

129     private StringPoolJob stringPoolJob;
130
131     /**
132      * The synchronizer
133      */

134     protected Synchronizer synchronizer;
135
136     /**
137      * The currently installed team hook.
138      */

139     protected TeamHook teamHook = null;
140
141     /**
142      * The workspace tree. The tree is an in-memory representation
143      * of the resources that make up the workspace. The tree caches
144      * the structure and state of files and directories on disk (their existence
145      * and last modified times). When external parties make changes to
146      * the files on disk, this representation becomes out of sync. A local refresh
147      * reconciles the state of the files on disk with this tree (@link {@link IResource#refreshLocal(int, IProgressMonitor)}).
148      * The tree is also used to store metadata associated with resources in
149      * the workspace (markers, properties, etc).
150      *
151      * While the ElementTree data structure can hand both concurrent
152      * reads and concurrent writes, write access to the tree is governed
153      * by {@link WorkManager}.
154      */

155     protected ElementTree tree;
156
157     /**
158      * This field is used to control access to the workspace tree during
159      * resource change notifications. It tracks which thread, if any, is
160      * in the middle of a resource change notification. This is used to cause
161      * attempts to modify the workspace during notifications to fail.
162      */

163     protected Thread JavaDoc treeLocked = null;
164
165     /**
166      * The currently installed file modification validator.
167      */

168     protected IFileModificationValidator validator = null;
169
170     /**
171      * Deletes all the files and directories from the given root down (inclusive).
172      * Returns false if we could not delete some file or an exception occurred
173      * at any point in the deletion.
174      * Even if an exception occurs, a best effort is made to continue deleting.
175      */

176     public static boolean clear(java.io.File JavaDoc root) {
177         boolean result = clearChildren(root);
178         try {
179             if (root.exists())
180                 result &= root.delete();
181         } catch (Exception JavaDoc e) {
182             result = false;
183         }
184         return result;
185     }
186
187     /**
188      * Deletes all the files and directories from the given root down, except for
189      * the root itself.
190      * Returns false if we could not delete some file or an exception occurred
191      * at any point in the deletion.
192      * Even if an exception occurs, a best effort is made to continue deleting.
193      */

194     public static boolean clearChildren(java.io.File JavaDoc root) {
195         boolean result = true;
196         if (root.isDirectory()) {
197             String JavaDoc[] list = root.list();
198             // for some unknown reason, list() can return null.
199
// Just skip the children If it does.
200
if (list != null)
201                 for (int i = 0; i < list.length; i++)
202                     result &= clear(new java.io.File JavaDoc(root, list[i]));
203         }
204         return result;
205     }
206
207     public static WorkspaceDescription defaultWorkspaceDescription() {
208         return new WorkspaceDescription("Workspace"); //$NON-NLS-1$
209
}
210
211     /**
212      * Returns true if the object at the specified position has any
213      * other copy in the given array.
214      */

215     private static boolean isDuplicate(Object JavaDoc[] array, int position) {
216         if (array == null || position >= array.length)
217             return false;
218         for (int j = position - 1; j >= 0; j--)
219             if (array[j].equals(array[position]))
220                 return true;
221         return false;
222     }
223
224     public Workspace() {
225         super();
226         localMetaArea = new LocalMetaArea();
227         tree = new ElementTree();
228         /* tree should only be modified during operations */
229         tree.immutable();
230         treeLocked = Thread.currentThread();
231         tree.setTreeData(newElement(IResource.ROOT));
232     }
233
234     /**
235      * Indicates that a build is about to occur. Broadcasts the necessary
236      * deltas before the build starts. Note that this will cause POST_BUILD
237      * to be automatically done at the end of the operation in which
238      * the build occurs.
239      */

240     protected void aboutToBuild(Object JavaDoc source, int trigger) {
241         //fire a POST_CHANGE first to ensure everyone is up to date before firing PRE_BUILD
242
broadcastPostChange();
243         broadcastBuildEvent(source, IResourceChangeEvent.PRE_BUILD, trigger);
244     }
245
246     /**
247      * Adds a listener for internal workspace lifecycle events. There is no way to
248      * remove lifecycle listeners.
249      */

250     public void addLifecycleListener(ILifecycleListener listener) {
251         lifecycleListeners.add(listener);
252     }
253
254     /* (non-Javadoc)
255      * @see IWorkspace#addResourceChangeListener(IResourceChangeListener)
256      */

257     public void addResourceChangeListener(IResourceChangeListener listener) {
258         notificationManager.addListener(listener, IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.POST_CHANGE);
259     }
260
261     /* (non-Javadoc)
262      * @see IWorkspace#addResourceChangeListener(IResourceChangeListener, int)
263      */

264     public void addResourceChangeListener(IResourceChangeListener listener, int eventMask) {
265         notificationManager.addListener(listener, eventMask);
266     }
267
268     /* (non-Javadoc)
269      * @see IWorkspace#addSaveParticipant(Plugin, ISaveParticipant)
270      */

271     public ISavedState addSaveParticipant(Plugin plugin, ISaveParticipant participant) throws CoreException {
272         Assert.isNotNull(plugin, "Plugin must not be null"); //$NON-NLS-1$
273
Assert.isNotNull(participant, "Participant must not be null"); //$NON-NLS-1$
274
return saveManager.addParticipant(plugin, participant);
275     }
276
277     public void beginOperation(boolean createNewTree) throws CoreException {
278         WorkManager workManager = getWorkManager();
279         workManager.incrementNestedOperations();
280         if (!workManager.isBalanced())
281             Assert.isTrue(false, "Operation was not prepared."); //$NON-NLS-1$
282
if (workManager.getPreparedOperationDepth() > 1) {
283             if (createNewTree && tree.isImmutable())
284                 newWorkingTree();
285             return;
286         }
287         // stash the current tree as the basis for this operation.
288
operationTree = tree;
289         if (createNewTree && tree.isImmutable())
290             newWorkingTree();
291     }
292
293     public void broadcastBuildEvent(Object JavaDoc source, int type, int buildTrigger) {
294         ResourceChangeEvent event = new ResourceChangeEvent(source, type, buildTrigger, null);
295         notificationManager.broadcastChanges(tree, event, false);
296     }
297
298     /**
299      * Broadcasts an internal workspace lifecycle event to interested
300      * internal listeners.
301      */

302     protected void broadcastEvent(LifecycleEvent event) throws CoreException {
303         for (Iterator it = lifecycleListeners.iterator(); it.hasNext();) {
304             ILifecycleListener listener = (ILifecycleListener) it.next();
305             listener.handleEvent(event);
306         }
307     }
308
309     public void broadcastPostChange() {
310         ResourceChangeEvent event = new ResourceChangeEvent(this, IResourceChangeEvent.POST_CHANGE, 0, null);
311         notificationManager.broadcastChanges(tree, event, true);
312     }
313
314     /* (non-Javadoc)
315      * @see IWorkspace#build(int, IProgressMonitor)
316      */

317     public void build(int trigger, IProgressMonitor monitor) throws CoreException {
318         monitor = Policy.monitorFor(monitor);
319         final ISchedulingRule rule = getRuleFactory().buildRule();
320         try {
321             monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
322
try {
323                 prepareOperation(rule, monitor);
324                 beginOperation(true);
325                 aboutToBuild(this, trigger);
326                 IStatus result;
327                 try {
328                     result = getBuildManager().build(trigger, Policy.subMonitorFor(monitor, Policy.opWork));
329                 } finally {
330                     //must fire POST_BUILD if PRE_BUILD has occurred
331
broadcastBuildEvent(this, IResourceChangeEvent.POST_BUILD, trigger);
332                 }
333                 if (!result.isOK())
334                     throw new ResourceException(result);
335             } finally {
336                 //building may close the tree, but we are still inside an operation so open it
337
if (tree.isImmutable())
338                     newWorkingTree();
339                 endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
340             }
341         } finally {
342             monitor.done();
343         }
344     }
345
346     /**
347      * Returns whether creating executable extensions is acceptable
348      * at this point in time. In particular, returns <code>false</code>
349      * when the system bundle is shutting down, which only occurs
350      * when the entire framework is exiting.
351      */

352     private boolean canCreateExtensions() {
353         return Platform.getBundle("org.eclipse.osgi").getState() != Bundle.STOPPING; //$NON-NLS-1$
354
}
355
356     /* (non-Javadoc)
357      * @see IWorkspace#checkpoint(boolean)
358      */

359     public void checkpoint(boolean build) {
360         try {
361             final ISchedulingRule rule = getWorkManager().getNotifyRule();
362             try {
363                 prepareOperation(rule, null);
364                 beginOperation(true);
365                 broadcastPostChange();
366             } finally {
367                 endOperation(rule, build, null);
368             }
369         } catch (CoreException e) {
370             Policy.log(e.getStatus());
371         }
372     }
373
374     /**
375      * Closes this workspace; ignored if this workspace is not open.
376      * The state of this workspace is not saved before the workspace
377      * is shut down.
378      * <p>
379      * If the workspace was saved immediately prior to closing,
380      * it will have the same set of projects
381      * (open or closed) when reopened for a subsequent session.
382      * Otherwise, closing a workspace may lose some or all of the
383      * changes made since the last save or snapshot.
384      * </p>
385      * <p>
386      * Note that session properties are discarded when a workspace is closed.
387      * </p>
388      * <p>
389      * This method is long-running; progress and cancellation are provided
390      * by the given progress monitor.
391      * </p>
392      *
393      * @param monitor a progress monitor, or <code>null</code> if progress
394      * reporting and cancellation are not desired
395      * @exception CoreException if the workspace could not be shutdown.
396      */

397     public void close(IProgressMonitor monitor) throws CoreException {
398         //nothing to do if the workspace failed to open
399
if (!isOpen())
400             return;
401         monitor = Policy.monitorFor(monitor);
402         try {
403             String JavaDoc msg = Messages.resources_closing_0;
404             int rootCount = tree.getChildCount(Path.ROOT);
405             monitor.beginTask(msg, rootCount + 2);
406             monitor.subTask(msg);
407             //this operation will never end because the world is going away
408
try {
409                 stringPoolJob.cancel();
410                 //shutdown save manager now so a last snapshot can be taken before we close
411
//note: you can't call #save() from within a nested operation
412
saveManager.shutdown(null);
413                 prepareOperation(getRoot(), monitor);
414                 //shutdown notification first to avoid calling third parties during shutdown
415
notificationManager.shutdown(null);
416                 beginOperation(true);
417                 IProject[] projects = getRoot().getProjects();
418                 for (int i = 0; i < projects.length; i++) {
419                     //notify managers of closing so they can cleanup
420
broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, projects[i]));
421                     monitor.worked(1);
422                 }
423                 //empty the workspace tree so we leave in a clean state
424
deleteResource(getRoot());
425                 openFlag = false;
426                 // endOperation not needed here
427
} finally {
428                 // Shutdown needs to be executed regardless of failures
429
shutdown(Policy.subMonitorFor(monitor, 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
430             }
431         } finally {
432             //release the scheduling rule to be a good job citizen
433
Job.getJobManager().endRule(getRoot());
434             monitor.done();
435         }
436     }
437
438     /**
439      * Computes the global total ordering of all open projects in the
440      * workspace based on project references. If an existing and open project P
441      * references another existing and open project Q also included in the list,
442      * then Q should come before P in the resulting ordering. Closed and non-
443      * existent projects are ignored, and will not appear in the result. References
444      * to non-existent or closed projects are also ignored, as are any self-
445      * references.
446      * <p>
447      * When there are choices, the choice is made in a reasonably stable way. For
448      * example, given an arbitrary choice between two projects, the one with the
449      * lower collating project name is usually selected.
450      * </p>
451      * <p>
452      * When the project reference graph contains cyclic references, it is
453      * impossible to honor all of the relationships. In this case, the result
454      * ignores as few relationships as possible. For example, if P2 references P1,
455      * P4 references P3, and P2 and P3 reference each other, then exactly one of the
456      * relationships between P2 and P3 will have to be ignored. The outcome will be
457      * either [P1, P2, P3, P4] or [P1, P3, P2, P4]. The result also contains
458      * complete details of any cycles present.
459      * </p>
460      *
461      * @return result describing the global project order
462      * @since 2.1
463      */

464     private ProjectOrder computeFullProjectOrder() {
465
466         // determine the full set of accessible projects in the workspace
467
// order the set in descending alphabetical order of project name
468
SortedSet allAccessibleProjects = new TreeSet(new Comparator() {
469             public int compare(Object JavaDoc x, Object JavaDoc y) {
470                 IProject px = (IProject) x;
471                 IProject py = (IProject) y;
472                 return py.getName().compareTo(px.getName());
473             }
474         });
475         IProject[] allProjects = getRoot().getProjects();
476         // List<IProject[]> edges
477
List edges = new ArrayList(allProjects.length);
478         for (int i = 0; i < allProjects.length; i++) {
479             Project project = (Project) allProjects[i];
480             // ignore projects that are not accessible
481
if (!project.isAccessible())
482                 continue;
483             ProjectDescription desc = project.internalGetDescription();
484             if (desc == null)
485                 continue;
486             //obtain both static and dynamic project references
487
IProject[] refs = desc.getAllReferences(false);
488             allAccessibleProjects.add(project);
489             for (int j = 0; j < refs.length; j++) {
490                 IProject ref = refs[j];
491                 // ignore self references and references to projects that are not accessible
492
if (ref.isAccessible() && !ref.equals(project))
493                     edges.add(new IProject[] {project, ref});
494             }
495         }
496
497         ProjectOrder fullProjectOrder = ComputeProjectOrder.computeProjectOrder(allAccessibleProjects, edges);
498         return fullProjectOrder;
499     }
500
501     /**
502      * Implementation of API method declared on IWorkspace.
503      *
504      * @see IWorkspace#computePrerequisiteOrder(IProject[])
505      * @deprecated Replaced by <code>IWorkspace.computeProjectOrder</code>, which
506      * produces a more usable result when there are cycles in project reference
507      * graph.
508      */

509     public IProject[][] computePrerequisiteOrder(IProject[] targets) {
510         return computePrerequisiteOrder1(targets);
511     }
512
513     /*
514      * Compatible reimplementation of
515      * <code>IWorkspace.computePrerequisiteOrder</code> using
516      * <code>IWorkspace.computeProjectOrder</code>.
517      *
518      * @since 2.1
519      */

520     private IProject[][] computePrerequisiteOrder1(IProject[] projects) {
521         IWorkspace.ProjectOrder r = computeProjectOrder(projects);
522         if (!r.hasCycles) {
523             return new IProject[][] {r.projects, new IProject[0]};
524         }
525         // when there are cycles, we need to remove all knotted projects from
526
// r.projects to form result[0] and merge all knots to form result[1]
527
// Set<IProject> bad
528
Set bad = new HashSet();
529         // Set<IProject> bad
530
Set keepers = new HashSet(Arrays.asList(r.projects));
531         for (int i = 0; i < r.knots.length; i++) {
532             IProject[] knot = r.knots[i];
533             for (int j = 0; j < knot.length; j++) {
534                 IProject project = knot[j];
535                 // keep only selected projects in knot
536
if (keepers.contains(project)) {
537                     bad.add(project);
538                 }
539             }
540         }
541         IProject[] result2 = new IProject[bad.size()];
542         bad.toArray(result2);
543         // List<IProject> p
544
List p = new LinkedList();
545         p.addAll(Arrays.asList(r.projects));
546         for (Iterator it = p.listIterator(); it.hasNext();) {
547             IProject project = (IProject) it.next();
548             if (bad.contains(project)) {
549                 // remove knotted projects from the main answer
550
it.remove();
551             }
552         }
553         IProject[] result1 = new IProject[p.size()];
554         p.toArray(result1);
555         return new IProject[][] {result1, result2};
556     }
557
558     /* (non-Javadoc)
559      * @see IWorkspace#computeProjectOrder(IProject[])
560      * @since 2.1
561      */

562     public ProjectOrder computeProjectOrder(IProject[] projects) {
563
564         // compute the full project order for all accessible projects
565
ProjectOrder fullProjectOrder = computeFullProjectOrder();
566
567         // "fullProjectOrder.projects" contains no inaccessible projects
568
// but might contain accessible projects omitted from "projects"
569
// optimize common case where "projects" includes everything
570
int accessibleCount = 0;
571         for (int i = 0; i < projects.length; i++) {
572             if (projects[i].isAccessible()) {
573                 accessibleCount++;
574             }
575         }
576         // no filtering required if the subset accounts for the full list
577
if (accessibleCount == fullProjectOrder.projects.length) {
578             return fullProjectOrder;
579         }
580
581         // otherwise we need to eliminate mention of other projects...
582
// ... from "fullProjectOrder.projects"...
583
// Set<IProject> keepers
584
Set keepers = new HashSet(Arrays.asList(projects));
585         // List<IProject> projects
586
List reducedProjects = new ArrayList(fullProjectOrder.projects.length);
587         for (int i = 0; i < fullProjectOrder.projects.length; i++) {
588             IProject project = fullProjectOrder.projects[i];
589             if (keepers.contains(project)) {
590                 // remove projects not in the initial subset
591
reducedProjects.add(project);
592             }
593         }
594         IProject[] p1 = new IProject[reducedProjects.size()];
595         reducedProjects.toArray(p1);
596
597         // ... and from "fullProjectOrder.knots"
598
// List<IProject[]> knots
599
List reducedKnots = new ArrayList(fullProjectOrder.knots.length);
600         for (int i = 0; i < fullProjectOrder.knots.length; i++) {
601             IProject[] knot = fullProjectOrder.knots[i];
602             List x = new ArrayList(knot.length);
603             for (int j = 0; j < knot.length; j++) {
604                 IProject project = knot[j];
605                 if (keepers.contains(project)) {
606                     x.add(project);
607                 }
608             }
609             // keep knots containing 2 or more projects in the specified subset
610
if (x.size() > 1) {
611                 reducedKnots.add(x.toArray(new IProject[x.size()]));
612             }
613         }
614         IProject[][] k1 = new IProject[reducedKnots.size()][];
615         // okay to use toArray here because reducedKnots elements are IProject[]
616
reducedKnots.toArray(k1);
617         return new ProjectOrder(p1, (k1.length > 0), k1);
618     }
619
620     /* (non-Javadoc)
621      * @see IWorkspace#copy(IResource[], IPath, boolean, IProgressMonitor)
622      */

623     public IStatus copy(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
624         int updateFlags = force ? IResource.FORCE : IResource.NONE;
625         return copy(resources, destination, updateFlags, monitor);
626     }
627
628     /* (non-Javadoc)
629      * @see IWorkspace#copy(IResource[], IPath, int, IProgressMonitor)
630      */

631     public IStatus copy(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
632         monitor = Policy.monitorFor(monitor);
633         try {
634             Assert.isLegal(resources != null);
635             int opWork = Math.max(resources.length, 1);
636             int totalWork = Policy.totalWork * opWork / Policy.opWork;
637             String JavaDoc message = Messages.resources_copying_0;
638             monitor.beginTask(message, totalWork);
639             if (resources.length == 0)
640                 return Status.OK_STATUS;
641             // to avoid concurrent changes to this array
642
resources = (IResource[]) resources.clone();
643             IPath parentPath = null;
644             message = Messages.resources_copyProblem;
645             MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
646             try {
647                 prepareOperation(getRoot(), monitor);
648                 beginOperation(true);
649                 for (int i = 0; i < resources.length; i++) {
650                     Policy.checkCanceled(monitor);
651                     IResource resource = resources[i];
652                     if (resource == null || isDuplicate(resources, i)) {
653                         monitor.worked(1);
654                         continue;
655                     }
656                     // test siblings
657
if (parentPath == null)
658                         parentPath = resource.getFullPath().removeLastSegments(1);
659                     if (parentPath.equals(resource.getFullPath().removeLastSegments(1))) {
660                         // test copy requirements
661
try {
662                             IPath destinationPath = destination.append(resource.getName());
663                             IStatus requirements = ((Resource) resource).checkCopyRequirements(destinationPath, resource.getType(), updateFlags);
664                             if (requirements.isOK()) {
665                                 try {
666                                     resource.copy(destinationPath, updateFlags, Policy.subMonitorFor(monitor, 1));
667                                 } catch (CoreException e) {
668                                     status.merge(e.getStatus());
669                                 }
670                             } else {
671                                 monitor.worked(1);
672                                 status.merge(requirements);
673                             }
674                         } catch (CoreException e) {
675                             monitor.worked(1);
676                             status.merge(e.getStatus());
677                         }
678                     } else {
679                         monitor.worked(1);
680                         message = NLS.bind(Messages.resources_notChild, resources[i].getFullPath(), parentPath);
681                         status.merge(new ResourceStatus(IResourceStatus.OPERATION_FAILED, resources[i].getFullPath(), message));
682                     }
683                 }
684             } catch (OperationCanceledException e) {
685                 getWorkManager().operationCanceled();
686                 throw e;
687             } finally {
688                 endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
689             }
690             if (status.matches(IStatus.ERROR))
691                 throw new ResourceException(status);
692             return status.isOK() ? Status.OK_STATUS : (IStatus) status;
693         } finally {
694             monitor.done();
695         }
696     }
697
698     protected void copyTree(IResource source, IPath destination, int depth, int updateFlags, boolean keepSyncInfo) throws CoreException {
699         // retrieve the resource at the destination if there is one (phantoms included).
700
// if there isn't one, then create a new handle based on the type that we are
701
// trying to copy
702
IResource destinationResource = getRoot().findMember(destination, true);
703         int destinationType;
704         if (destinationResource == null) {
705             if (source.getType() == IResource.FILE)
706                 destinationType = IResource.FILE;
707             else if (destination.segmentCount() == 1)
708                 destinationType = IResource.PROJECT;
709             else
710                 destinationType = IResource.FOLDER;
711             destinationResource = newResource(destination, destinationType);
712         } else
713             destinationType = destinationResource.getType();
714
715         // create the resource at the destination
716
ResourceInfo sourceInfo = ((Resource) source).getResourceInfo(true, false);
717         if (destinationType != source.getType()) {
718             sourceInfo = (ResourceInfo) sourceInfo.clone();
719             sourceInfo.setType(destinationType);
720         }
721         ResourceInfo newInfo = createResource(destinationResource, sourceInfo, false, false, keepSyncInfo);
722         // get/set the node id from the source's resource info so we can later put it in the
723
// info for the destination resource. This will help us generate the proper deltas,
724
// indicating a move rather than a add/delete
725
newInfo.setNodeId(sourceInfo.getNodeId());
726
727         // preserve local sync info but not location info
728
newInfo.setFlags(newInfo.getFlags() | (sourceInfo.getFlags() & M_LOCAL_EXISTS));
729         newInfo.setFileStoreRoot(null);
730
731         // forget content-related caching flags
732
newInfo.clear(M_CONTENT_CACHE);
733
734         // update link locations in project descriptions
735
if (source.isLinked()) {
736             LinkDescription linkDescription;
737             if ((updateFlags & IResource.SHALLOW) != 0) {
738                 //for shallow move the destination is a linked resource with the same location
739
newInfo.set(ICoreConstants.M_LINK);
740                 linkDescription = new LinkDescription(destinationResource, source.getLocationURI());
741             } else {
742                 //for deep move the destination is not a linked resource
743
newInfo.clear(ICoreConstants.M_LINK);
744                 linkDescription = null;
745             }
746             Project project = (Project) destinationResource.getProject();
747             project.internalGetDescription().setLinkLocation(destinationResource.getProjectRelativePath(), linkDescription);
748             project.writeDescription(updateFlags);
749         }
750
751         // do the recursion. if we have a file then it has no members so return. otherwise
752
// recursively call this method on the container's members if the depth tells us to
753
if (depth == IResource.DEPTH_ZERO || source.getType() == IResource.FILE)
754             return;
755         if (depth == IResource.DEPTH_ONE)
756             depth = IResource.DEPTH_ZERO;
757         //copy .project file first if project is being copied, otherwise links won't be able to update description
758
boolean projectCopy = source.getType() == IResource.PROJECT && destinationType == IResource.PROJECT;
759         if (projectCopy) {
760             IResource dotProject = ((Project) source).findMember(IProjectDescription.DESCRIPTION_FILE_NAME);
761             if (dotProject != null)
762                 copyTree(dotProject, destination.append(dotProject.getName()), depth, updateFlags, keepSyncInfo);
763         }
764         IResource[] children = ((IContainer) source).members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
765         for (int i = 0, imax = children.length; i < imax; i++) {
766             String JavaDoc childName = children[i].getName();
767             if (!projectCopy || !childName.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
768                 IPath childPath = destination.append(childName);
769                 copyTree(children[i], childPath, depth, updateFlags, keepSyncInfo);
770             }
771         }
772     }
773
774     /**
775      * Returns the number of resources in a subtree of the resource tree.
776      *
777      * @param root The subtree to count resources for
778      * @param depth The depth of the subtree to count
779      * @param phantom If true, phantoms are included, otherwise they are ignored.
780      */

781     public int countResources(IPath root, int depth, final boolean phantom) {
782         if (!tree.includes(root))
783             return 0;
784         switch (depth) {
785             case IResource.DEPTH_ZERO :
786                 return 1;
787             case IResource.DEPTH_ONE :
788                 return 1 + tree.getChildCount(root);
789             case IResource.DEPTH_INFINITE :
790                 final int[] count = new int[1];
791                 IElementContentVisitor visitor = new IElementContentVisitor() {
792                     public boolean visitElement(ElementTree aTree, IPathRequestor requestor, Object JavaDoc elementContents) {
793                         if (phantom || !((ResourceInfo) elementContents).isSet(M_PHANTOM))
794                             count[0]++;
795                         return true;
796                     }
797                 };
798                 new ElementTreeIterator(tree, root).iterate(visitor);
799                 return count[0];
800         }
801         return 0;
802     }
803
804     /*
805      * Creates the given resource in the tree and returns the new resource info object.
806      * If phantom is true, the created element is marked as a phantom.
807      * If there is already be an element in the tree for the given resource
808      * in the given state (i.e., phantom), a CoreException is thrown.
809      * If there is already a phantom in the tree and the phantom flag is false,
810      * the element is overwritten with the new element. (but the synchronization
811      * information is preserved)
812      */

813     public ResourceInfo createResource(IResource resource, boolean phantom) throws CoreException {
814         return createResource(resource, null, phantom, false, false);
815     }
816
817     /**
818      * Creates a resource, honoring update flags requesting that the resource
819      * be immediately made derived and/or team private
820      */

821     public ResourceInfo createResource(IResource resource, int updateFlags) throws CoreException {
822         ResourceInfo info = createResource(resource, null, false, false, false);
823         if ((updateFlags & IResource.DERIVED) != 0)
824             info.set(M_DERIVED);
825         if ((updateFlags & IResource.TEAM_PRIVATE) != 0)
826             info.set(M_TEAM_PRIVATE_MEMBER);
827         return info;
828     }
829
830     /*
831      * Creates the given resource in the tree and returns the new resource info object.
832      * If phantom is true, the created element is marked as a phantom.
833      * If there is already be an element in the tree for the given resource
834      * in the given state (i.e., phantom), a CoreException is thrown.
835      * If there is already a phantom in the tree and the phantom flag is false,
836      * the element is overwritten with the new element. (but the synchronization
837      * information is preserved) If the specified resource info is null, then create
838      * a new one.
839      *
840      * If keepSyncInfo is set to be true, the sync info in the given ResourceInfo is NOT
841      * cleared before being created and thus any sync info already existing at that namespace
842      * (as indicated by an already existing phantom resource) will be lost.
843      */

844     public ResourceInfo createResource(IResource resource, ResourceInfo info, boolean phantom, boolean overwrite, boolean keepSyncInfo) throws CoreException {
845         info = info == null ? newElement(resource.getType()) : (ResourceInfo) info.clone();
846         ResourceInfo original = getResourceInfo(resource.getFullPath(), true, false);
847         if (phantom) {
848             info.set(M_PHANTOM);
849             info.clearModificationStamp();
850         }
851         // if nothing existed at the destination then just create the resource in the tree
852
if (original == null) {
853             // we got here from a copy/move. we don't want to copy over any sync info
854
// from the source so clear it.
855
if (!keepSyncInfo)
856                 info.setSyncInfo(null);
857             tree.createElement(resource.getFullPath(), info);
858         } else {
859             // if overwrite==true then slam the new info into the tree even if one existed before
860
if (overwrite || (!phantom && original.isSet(M_PHANTOM))) {
861                 // copy over the sync info and flags from the old resource info
862
// since we are replacing a phantom with a real resource
863
// DO NOT set the sync info dirty flag because we want to
864
// preserve the old sync info so its not dirty
865
// XXX: must copy over the generic sync info from the old info to the new
866
// XXX: do we really need to clone the sync info here?
867
if (!keepSyncInfo)
868                     info.setSyncInfo(original.getSyncInfo(true));
869                 // mark the markers bit as dirty so we snapshot an empty marker set for
870
// the new resource
871
info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
872                 tree.setElementData(resource.getFullPath(), info);
873             } else {
874                 String JavaDoc message = NLS.bind(Messages.resources_mustNotExist, resource.getFullPath());
875                 throw new ResourceException(IResourceStatus.RESOURCE_EXISTS, resource.getFullPath(), message, null);
876             }
877         }
878         return info;
879     }
880
881     /* (non-Javadoc)
882      * @see IWorkspace#delete(IResource[], boolean, IProgressMonitor)
883      */

884     public IStatus delete(IResource[] resources, boolean force, IProgressMonitor monitor) throws CoreException {
885         int updateFlags = force ? IResource.FORCE : IResource.NONE;
886         updateFlags |= IResource.KEEP_HISTORY;
887         return delete(resources, updateFlags, monitor);
888     }
889
890     /* (non-Javadoc)
891      * @see IWorkspace#delete(IResource[], int, IProgressMonitor)
892      */

893     public IStatus delete(IResource[] resources, int updateFlags, IProgressMonitor monitor) throws CoreException {
894         monitor = Policy.monitorFor(monitor);
895         try {
896             int opWork = Math.max(resources.length, 1);
897             int totalWork = Policy.totalWork * opWork / Policy.opWork;
898             String JavaDoc message = Messages.resources_deleting_0;
899             monitor.beginTask(message, totalWork);
900             message = Messages.resources_deleteProblem;
901             MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
902             if (resources.length == 0)
903                 return result;
904             resources = (IResource[]) resources.clone(); // to avoid concurrent changes to this array
905
try {
906                 prepareOperation(getRoot(), monitor);
907                 beginOperation(true);
908                 for (int i = 0; i < resources.length; i++) {
909                     Policy.checkCanceled(monitor);
910                     Resource resource = (Resource) resources[i];
911                     if (resource == null) {
912                         monitor.worked(1);
913                         continue;
914                     }
915                     try {
916                         resource.delete(updateFlags, Policy.subMonitorFor(monitor, 1));
917                     } catch (CoreException e) {
918                         // Don't really care about the exception unless the resource is still around.
919
ResourceInfo info = resource.getResourceInfo(false, false);
920                         if (resource.exists(resource.getFlags(info), false)) {
921                             message = NLS.bind(Messages.resources_couldnotDelete, resource.getFullPath());
922                             result.merge(new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, resource.getFullPath(), message));
923                             result.merge(e.getStatus());
924                         }
925                     }
926                 }
927                 if (result.matches(IStatus.ERROR))
928                     throw new ResourceException(result);
929                 return result;
930             } catch (OperationCanceledException e) {
931                 getWorkManager().operationCanceled();
932                 throw e;
933             } finally {
934                 endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
935             }
936         } finally {
937             monitor.done();
938         }
939     }
940
941     /* (non-Javadoc)
942      * @see IWorkspace#deleteMarkers(IMarker[])
943      */

944     public void deleteMarkers(IMarker[] markers) throws CoreException {
945         Assert.isNotNull(markers);
946         if (markers.length == 0)
947             return;
948         // clone to avoid outside changes
949
markers = (IMarker[]) markers.clone();
950         try {
951             prepareOperation(null, null);
952             beginOperation(true);
953             for (int i = 0; i < markers.length; ++i)
954                 if (markers[i] != null && markers[i].getResource() != null)
955                     markerManager.removeMarker(markers[i].getResource(), markers[i].getId());
956         } finally {
957             endOperation(null, false, null);
958         }
959     }
960
961     /**
962      * Delete the given resource from the current tree of the receiver.
963      * This method simply removes the resource from the tree. No cleanup or
964      * other management is done. Use IResource.delete for proper deletion.
965      * If the given resource is the root, all of its children (i.e., all projects) are
966      * deleted but the root is left.
967      */

968     void deleteResource(IResource resource) {
969         IPath path = resource.getFullPath();
970         if (path.equals(Path.ROOT)) {
971             IProject[] children = getRoot().getProjects();
972             for (int i = 0; i < children.length; i++)
973                 tree.deleteElement(children[i].getFullPath());
974         } else
975             tree.deleteElement(path);
976     }
977
978     /**
979      * End an operation (group of resource changes).
980      * Notify interested parties that resource changes have taken place. All
981      * registered resource change listeners are notified. If autobuilding is
982      * enabled, a build is run.
983      */

984     public void endOperation(ISchedulingRule rule, boolean build, IProgressMonitor monitor) throws CoreException {
985         WorkManager workManager = getWorkManager();
986         //don't do any end operation work if we failed to check in
987
if (workManager.checkInFailed(rule))
988             return;
989         // This is done in a try finally to ensure that we always decrement the operation count
990
// and release the workspace lock. This must be done at the end because snapshot
991
// and "hasChanges" comparison have to happen without interference from other threads.
992
boolean hasTreeChanges = false;
993         boolean depthOne = false;
994         try {
995             workManager.setBuild(build);
996             // if we are not exiting a top level operation then just decrement the count and return
997
depthOne = workManager.getPreparedOperationDepth() == 1;
998             if (!(notificationManager.shouldNotify() || depthOne)) {
999                 notificationManager.requestNotify();
1000                return;
1001            }
1002            // do the following in a try/finally to ensure that the operation tree is nulled at the end
1003
// as we are completing a top level operation.
1004
try {
1005                notificationManager.beginNotify();
1006                // check for a programming error on using beginOperation/endOperation
1007
Assert.isTrue(workManager.getPreparedOperationDepth() > 0, "Mismatched begin/endOperation"); //$NON-NLS-1$
1008

1009                // At this time we need to re-balance the nested operations. It is necessary because
1010
// build() and snapshot() should not fail if they are called.
1011
workManager.rebalanceNestedOperations();
1012
1013                //find out if any operation has potentially modified the tree
1014
hasTreeChanges = workManager.shouldBuild();
1015                //double check if the tree has actually changed
1016
if (hasTreeChanges)
1017                    hasTreeChanges = operationTree != null && ElementTree.hasChanges(tree, operationTree, ResourceComparator.getBuildComparator(), true);
1018                broadcastPostChange();
1019                // Request a snapshot if we are sufficiently out of date.
1020
saveManager.snapshotIfNeeded(hasTreeChanges);
1021            } finally {
1022                // make sure the tree is immutable if we are ending a top-level operation.
1023
if (depthOne) {
1024                    tree.immutable();
1025                    operationTree = null;
1026                } else
1027                    newWorkingTree();
1028            }
1029        } finally {
1030            workManager.checkOut(rule);
1031        }
1032        if (depthOne)
1033            buildManager.endTopLevel(hasTreeChanges);
1034    }
1035
1036    /**
1037     * Flush the build order cache for the workspace. Only needed if the
1038     * description does not already have a build order. That is, if this
1039     * is really a cache.
1040     */

1041    protected void flushBuildOrder() {
1042        if (description.getBuildOrder(false) == null)
1043            buildOrder = null;
1044    }
1045
1046    /* (non-Javadoc)
1047     * @see IWorkspace#forgetSavedTree(String)
1048     */

1049    public void forgetSavedTree(String JavaDoc pluginId) {
1050        Assert.isNotNull(pluginId, "PluginId must not be null"); //$NON-NLS-1$
1051
saveManager.forgetSavedTree(pluginId);
1052    }
1053
1054    public AliasManager getAliasManager() {
1055        return aliasManager;
1056    }
1057
1058    /**
1059     * Returns this workspace's build manager
1060     */

1061    public BuildManager getBuildManager() {
1062        return buildManager;
1063    }
1064
1065    /**
1066     * Returns the order in which open projects in this workspace will be built.
1067     * <p>
1068     * The project build order is based on information specified in the workspace
1069     * description. The projects are built in the order specified by
1070     * <code>IWorkspaceDescription.getBuildOrder</code>; closed or non-existent
1071     * projects are ignored and not included in the result. If
1072     * <code>IWorkspaceDescription.getBuildOrder</code> is non-null, the default
1073     * build order is used; again, only open projects are included in the result.
1074     * </p>
1075     * <p>
1076     * The returned value is cached in the <code>buildOrder</code> field.
1077     * </p>
1078     *
1079     * @return the list of currently open projects in the workspace in the order in
1080     * which they would be built by <code>IWorkspace.build</code>.
1081     * @see IWorkspace#build(int, IProgressMonitor)
1082     * @see IWorkspaceDescription#getBuildOrder()
1083     * @since 2.1
1084     */

1085    public IProject[] getBuildOrder() {
1086        if (buildOrder != null) {
1087            // return previously-computed and cached project build order
1088
return buildOrder;
1089        }
1090        // see if a particular build order is specified
1091
String JavaDoc[] order = description.getBuildOrder(false);
1092        if (order != null) {
1093            // convert from project names to project handles
1094
// and eliminate non-existent and closed projects
1095
List projectList = new ArrayList(order.length);
1096            for (int i = 0; i < order.length; i++) {
1097                IProject project = getRoot().getProject(order[i]);
1098                if (project.isAccessible()) {
1099                    projectList.add(project);
1100                }
1101            }
1102            buildOrder = new IProject[projectList.size()];
1103            projectList.toArray(buildOrder);
1104        } else {
1105            // use default project build order
1106
// computed for all accessible projects in workspace
1107
buildOrder = computeFullProjectOrder().projects;
1108        }
1109        return buildOrder;
1110    }
1111
1112    public CharsetManager getCharsetManager() {
1113        return charsetManager;
1114    }
1115
1116    public ContentDescriptionManager getContentDescriptionManager() {
1117        return contentDescriptionManager;
1118    }
1119
1120    /* (non-Javadoc)
1121     * @see IWorkspace#getDanglingReferences()
1122     */

1123    public Map getDanglingReferences() {
1124        IProject[] projects = getRoot().getProjects();
1125        Map result = new HashMap(projects.length);
1126        for (int i = 0; i < projects.length; i++) {
1127            Project project = (Project) projects[i];
1128            if (!project.isAccessible())
1129                continue;
1130            IProject[] refs = project.internalGetDescription().getReferencedProjects(false);
1131            List dangling = new ArrayList(refs.length);
1132            for (int j = 0; j < refs.length; j++)
1133                if (!refs[i].exists())
1134                    dangling.add(refs[i]);
1135            if (!dangling.isEmpty())
1136                result.put(projects[i], dangling.toArray(new IProject[dangling.size()]));
1137        }
1138        return result;
1139    }
1140
1141    /* (non-Javadoc)
1142     * @see IWorkspace#getDescription()
1143     */

1144    public IWorkspaceDescription getDescription() {
1145        WorkspaceDescription workingCopy = defaultWorkspaceDescription();
1146        description.copyTo(workingCopy);
1147        return workingCopy;
1148    }
1149
1150    /**
1151     * Returns the current element tree for this workspace
1152     */

1153    public ElementTree getElementTree() {
1154        return tree;
1155    }
1156
1157    public FileSystemResourceManager getFileSystemManager() {
1158        return fileSystemManager;
1159    }
1160
1161    /**
1162     * Returns the marker manager for this workspace
1163     */

1164    public MarkerManager getMarkerManager() {
1165        return markerManager;
1166    }
1167
1168    public LocalMetaArea getMetaArea() {
1169        return localMetaArea;
1170    }
1171
1172    protected IMoveDeleteHook getMoveDeleteHook() {
1173        if (moveDeleteHook == null)
1174            initializeMoveDeleteHook();
1175        return moveDeleteHook;
1176    }
1177
1178    /* (non-Javadoc)
1179     * @see IWorkspace#getNatureDescriptor(String)
1180     */

1181    public IProjectNatureDescriptor getNatureDescriptor(String JavaDoc natureId) {
1182        return natureManager.getNatureDescriptor(natureId);
1183    }
1184
1185    /* (non-Javadoc)
1186     * @see IWorkspace#getNatureDescriptors()
1187     */

1188    public IProjectNatureDescriptor[] getNatureDescriptors() {
1189        return natureManager.getNatureDescriptors();
1190    }
1191
1192    /**
1193     * Returns the nature manager for this workspace.
1194     */

1195    public NatureManager getNatureManager() {
1196        return natureManager;
1197    }
1198
1199    public NotificationManager getNotificationManager() {
1200        return notificationManager;
1201    }
1202
1203    /* (non-Javadoc)
1204     * @see IWorkspace#getPathVariableManager()
1205     */

1206    public IPathVariableManager getPathVariableManager() {
1207        return pathVariableManager;
1208    }
1209
1210    public IPropertyManager getPropertyManager() {
1211        return propertyManager;
1212    }
1213
1214    /**
1215     * Returns the refresh manager for this workspace
1216     */

1217    public RefreshManager getRefreshManager() {
1218        return refreshManager;
1219    }
1220
1221    /**
1222     * Returns the resource info for the identified resource.
1223     * null is returned if no such resource can be found.
1224     * If the phantom flag is true, phantom resources are considered.
1225     * If the mutable flag is true, the info is opened for change.
1226     *
1227     * This method DOES NOT throw an exception if the resource is not found.
1228     */

1229    public ResourceInfo getResourceInfo(IPath path, boolean phantom, boolean mutable) {
1230        try {
1231            if (path.segmentCount() == 0) {
1232                ResourceInfo info = (ResourceInfo) tree.getTreeData();
1233                Assert.isNotNull(info, "Tree root info must never be null"); //$NON-NLS-1$
1234
return info;
1235            }
1236            ResourceInfo result = null;
1237            if (!tree.includes(path))
1238                return null;
1239            if (mutable)
1240                result = (ResourceInfo) tree.openElementData(path);
1241            else
1242                result = (ResourceInfo) tree.getElementData(path);
1243            if (result != null && (!phantom && result.isSet(M_PHANTOM)))
1244                return null;
1245            return result;
1246        } catch (IllegalArgumentException JavaDoc e) {
1247            return null;
1248        }
1249    }
1250
1251    /* (non-Javadoc)
1252     * @see IWorkspace#getRoot()
1253     */

1254    public IWorkspaceRoot getRoot() {
1255        return defaultRoot;
1256    }
1257
1258    /* (non-Javadoc)
1259     * @see IWorkspace#getRuleFactory()
1260     */

1261    public IResourceRuleFactory getRuleFactory() {
1262        //note that the rule factory is created lazily because it
1263
//requires loading the teamHook extension
1264
if (ruleFactory == null)
1265            ruleFactory = new Rules(this);
1266        return ruleFactory;
1267    }
1268
1269    public SaveManager getSaveManager() {
1270        return saveManager;
1271    }
1272
1273    /* (non-Javadoc)
1274     * @see IWorkspace#getSynchronizer()
1275     */

1276    public ISynchronizer getSynchronizer() {
1277        return synchronizer;
1278    }
1279
1280    /**
1281     * Returns the installed team hook. Never returns null.
1282     */

1283    protected TeamHook getTeamHook() {
1284        if (teamHook == null)
1285            initializeTeamHook();
1286        return teamHook;
1287    }
1288
1289    /**
1290     * We should not have direct references to this field. All references should go through
1291     * this method.
1292     */

1293    public WorkManager getWorkManager() throws CoreException {
1294        if (_workManager == null) {
1295            String JavaDoc message = Messages.resources_shutdown;
1296            throw new ResourceException(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, null, message));
1297        }
1298        return _workManager;
1299    }
1300
1301    /**
1302     * A move/delete hook hasn't been initialized. Check the extension point and
1303     * try to create a new hook if a user has one defined as an extension. Otherwise
1304     * use the Core's implementation as the default.
1305     */

1306    protected void initializeMoveDeleteHook() {
1307        try {
1308            if (!canCreateExtensions())
1309                return;
1310            IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_MOVE_DELETE_HOOK);
1311            // no-one is plugged into the extension point so disable validation
1312
if (configs == null || configs.length == 0) {
1313                return;
1314            }
1315            // can only have one defined at a time. log a warning
1316
if (configs.length > 1) {
1317                //XXX: should provide a meaningful status code
1318
IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneHook, null);
1319                Policy.log(status);
1320                return;
1321            }
1322            // otherwise we have exactly one hook extension. Try to create a new instance
1323
// from the user-specified class.
1324
try {
1325                IConfigurationElement config = configs[0];
1326                moveDeleteHook = (IMoveDeleteHook) config.createExecutableExtension("class"); //$NON-NLS-1$
1327
} catch (CoreException e) {
1328                //ignore the failure if we are shutting down (expected since extension
1329
//provider plugin has probably already shut down
1330
if (canCreateExtensions()) {
1331                    IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initHook, e);
1332                    Policy.log(status);
1333                }
1334            }
1335        } finally {
1336            // for now just use Core's implementation
1337
if (moveDeleteHook == null)
1338                moveDeleteHook = new MoveDeleteHook();
1339        }
1340    }
1341
1342    /**
1343     * A team hook hasn't been initialized. Check the extension point and
1344     * try to create a new hook if a user has one defined as an extension.
1345     * Otherwise use the Core's implementation as the default.
1346     */

1347    protected void initializeTeamHook() {
1348        try {
1349            if (!canCreateExtensions())
1350                return;
1351            IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_TEAM_HOOK);
1352            // no-one is plugged into the extension point so disable validation
1353
if (configs == null || configs.length == 0) {
1354                return;
1355            }
1356            // can only have one defined at a time. log a warning
1357
if (configs.length > 1) {
1358                //XXX: should provide a meaningful status code
1359
IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneTeamHook, null);
1360                Policy.log(status);
1361                return;
1362            }
1363            // otherwise we have exactly one hook extension. Try to create a new instance
1364
// from the user-specified class.
1365
try {
1366                IConfigurationElement config = configs[0];
1367                teamHook = (TeamHook) config.createExecutableExtension("class"); //$NON-NLS-1$
1368
} catch (CoreException e) {
1369                //ignore the failure if we are shutting down (expected since extension
1370
//provider plugin has probably already shut down
1371
if (canCreateExtensions()) {
1372                    IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initTeamHook, e);
1373                    Policy.log(status);
1374                }
1375            }
1376        } finally {
1377            // default to use Core's implementation
1378
//create anonymous subclass because TeamHook is abstract
1379
if (teamHook == null)
1380                teamHook = new TeamHook() {
1381                    // empty
1382
};
1383        }
1384    }
1385
1386    /**
1387     * A file modification validator hasn't been initialized. Check the extension point and
1388     * try to create a new validator if a user has one defined as an extension.
1389     */

1390    protected void initializeValidator() {
1391        shouldValidate = false;
1392        if (!canCreateExtensions())
1393            return;
1394        IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_FILE_MODIFICATION_VALIDATOR);
1395        // no-one is plugged into the extension point so disable validation
1396
if (configs == null || configs.length == 0) {
1397            return;
1398        }
1399        // can only have one defined at a time. log a warning, disable validation, but continue with
1400
// the #setContents (e.g. don't throw an exception)
1401
if (configs.length > 1) {
1402            //XXX: should provide a meaningful status code
1403
IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneValidator, null);
1404            Policy.log(status);
1405            return;
1406        }
1407        // otherwise we have exactly one validator extension. Try to create a new instance
1408
// from the user-specified class.
1409
try {
1410            IConfigurationElement config = configs[0];
1411            validator = (IFileModificationValidator) config.createExecutableExtension("class"); //$NON-NLS-1$
1412
shouldValidate = true;
1413        } catch (CoreException e) {
1414            //ignore the failure if we are shutting down (expected since extension
1415
//provider plugin has probably already shut down
1416
if (canCreateExtensions()) {
1417                IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initValidator, e);
1418                Policy.log(status);
1419            }
1420        }
1421    }
1422
1423    public WorkspaceDescription internalGetDescription() {
1424        return description;
1425    }
1426
1427    /* (non-Javadoc)
1428     * @see IWorkspace#isAutoBuilding()
1429     */

1430    public boolean isAutoBuilding() {
1431        return description.isAutoBuilding();
1432    }
1433
1434    public boolean isOpen() {
1435        return openFlag;
1436    }
1437
1438    /* (non-Javadoc)
1439     * @see IWorkspace#isTreeLocked()
1440     */

1441    public boolean isTreeLocked() {
1442        return treeLocked == Thread.currentThread();
1443    }
1444
1445    /**
1446     * Link the given tree into the receiver's tree at the specified resource.
1447     */

1448    protected void linkTrees(IPath path, ElementTree[] newTrees) {
1449        tree = tree.mergeDeltaChain(path, newTrees);
1450    }
1451
1452    /* (non-Javadoc)
1453     * @see IWorkspace#loadProjectDescription(InputStream)
1454     * @since 3.1
1455     */

1456    public IProjectDescription loadProjectDescription(InputStream JavaDoc stream) throws CoreException {
1457        IProjectDescription result = null;
1458        result = new ProjectDescriptionReader().read(new InputSource JavaDoc(stream));
1459        if (result == null) {
1460            String JavaDoc message = NLS.bind(Messages.resources_errorReadProject, stream.toString());
1461            IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message, null);
1462            throw new ResourceException(status);
1463        }
1464        return result;
1465    }
1466
1467    /* (non-Javadoc)
1468     * @see IWorkspace#loadProjectDescription(IPath)
1469     * @since 2.0
1470     */

1471    public IProjectDescription loadProjectDescription(IPath path) throws CoreException {
1472        IProjectDescription result = null;
1473        IOException JavaDoc e = null;
1474        try {
1475            result = new ProjectDescriptionReader().read(path);
1476            if (result != null) {
1477                // check to see if we are using in the default area or not. use java.io.File for
1478
// testing equality because it knows better w.r.t. drives and case sensitivity
1479
IPath user = path.removeLastSegments(1);
1480                IPath platform = getRoot().getLocation().append(result.getName());
1481                if (!user.toFile().equals(platform.toFile()))
1482                    result.setLocation(user);
1483            }
1484        } catch (IOException JavaDoc ex) {
1485            e = ex;
1486        }
1487        if (result == null || e != null) {
1488            String JavaDoc message = NLS.bind(Messages.resources_errorReadProject, path.toOSString());
1489            IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message, e);
1490            throw new ResourceException(status);
1491        }
1492        return result;
1493    }
1494
1495    /* (non-Javadoc)
1496     * @see IWorkspace#move(IResource[], IPath, boolean, IProgressMonitor)
1497     */

1498    public IStatus move(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
1499        int updateFlags = force ? IResource.FORCE : IResource.NONE;
1500        updateFlags |= IResource.KEEP_HISTORY;
1501        return move(resources, destination, updateFlags, monitor);
1502    }
1503
1504    /* (non-Javadoc)
1505     * @see IWorkspace#move(IResource[], IPath, int, IProgressMonitor)
1506     */

1507    public IStatus move(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
1508        monitor = Policy.monitorFor(monitor);
1509        try {
1510            Assert.isLegal(resources != null);
1511            int opWork = Math.max(resources.length, 1);
1512            int totalWork = Policy.totalWork * opWork / Policy.opWork;
1513            String JavaDoc message = Messages.resources_moving_0;
1514            monitor.beginTask(message, totalWork);
1515            if (resources.length == 0)
1516                return Status.OK_STATUS;
1517            resources = (IResource[]) resources.clone(); // to avoid concurrent changes to this array
1518
IPath parentPath = null;
1519            message = Messages.resources_moveProblem;
1520            MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
1521            try {
1522                prepareOperation(getRoot(), monitor);
1523                beginOperation(true);
1524                for (int i = 0; i < resources.length; i++) {
1525                    Policy.checkCanceled(monitor);
1526                    Resource resource = (Resource) resources[i];
1527                    if (resource == null || isDuplicate(resources, i)) {
1528                        monitor.worked(1);
1529                        continue;
1530                    }
1531                    // test siblings
1532
if (parentPath == null)
1533                        parentPath = resource.getFullPath().removeLastSegments(1);
1534                    if (parentPath.equals(resource.getFullPath().removeLastSegments(1))) {
1535                        // test move requirements
1536
try {
1537                            IStatus requirements = resource.checkMoveRequirements(destination.append(resource.getName()), resource.getType(), updateFlags);
1538                            if (requirements.isOK()) {
1539                                try {
1540                                    resource.move(destination.append(resource.getName()), updateFlags, Policy.subMonitorFor(monitor, 1));
1541                                } catch (CoreException e) {
1542                                    status.merge(e.getStatus());
1543                                }
1544                            } else {
1545                                monitor.worked(1);
1546                                status.merge(requirements);
1547                            }
1548                        } catch (CoreException e) {
1549                            monitor.worked(1);
1550                            status.merge(e.getStatus());
1551                        }
1552                    } else {
1553                        monitor.worked(1);
1554                        message = NLS.bind(Messages.resources_notChild, resource.getFullPath(), parentPath);
1555                        status.merge(new ResourceStatus(IResourceStatus.OPERATION_FAILED, resource.getFullPath(), message));
1556                    }
1557                }
1558            } catch (OperationCanceledException e) {
1559                getWorkManager().operationCanceled();
1560                throw e;
1561            } finally {
1562                endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
1563            }
1564            if (status.matches(IStatus.ERROR))
1565                throw new ResourceException(status);
1566            return status.isOK() ? (IStatus) Status.OK_STATUS : (IStatus) status;
1567        } finally {
1568            monitor.done();
1569        }
1570    }
1571
1572    /**
1573     * Moves this resource's subtree to the destination. This operation should only be
1574     * used by move methods. Destination must be a valid destination for this resource.
1575     * The keepSyncInfo boolean is used to indicated whether or not the sync info should
1576     * be moved from the source to the destination.
1577     */

1578
1579    /* package */
1580    void move(Resource source, IPath destination, int depth, int updateFlags, boolean keepSyncInfo) throws CoreException {
1581        // overlay the tree at the destination path, preserving any important info
1582
// in any already existing resource information
1583
copyTree(source, destination, depth, updateFlags, keepSyncInfo);
1584        source.fixupAfterMoveSource();
1585    }
1586
1587    /**
1588     * Create and return a new tree element of the given type.
1589     */

1590    protected ResourceInfo newElement(int type) {
1591        ResourceInfo result = null;
1592        switch (type) {
1593            case IResource.FILE :
1594            case IResource.FOLDER :
1595                result = new ResourceInfo();
1596                break;
1597            case IResource.PROJECT :
1598                result = new ProjectInfo();
1599                break;
1600            case IResource.ROOT :
1601                result = new RootInfo();
1602                break;
1603        }
1604        result.setNodeId(nextNodeId());
1605        updateModificationStamp(result);
1606        result.setType(type);
1607        return result;
1608    }
1609
1610    /* (non-Javadoc)
1611     * @see IWorkspace#newProjectDescription(String)
1612     */

1613    public IProjectDescription newProjectDescription(String JavaDoc projectName) {
1614        IProjectDescription result = new ProjectDescription();
1615        result.setName(projectName);
1616        return result;
1617    }
1618
1619    public Resource newResource(IPath path, int type) {
1620        String JavaDoc message;
1621        switch (type) {
1622            case IResource.FOLDER :
1623                if (path.segmentCount() < ICoreConstants.MINIMUM_FOLDER_SEGMENT_LENGTH) {
1624                    message = "Path must include project and resource name: " + path.toString(); //$NON-NLS-1$
1625
Assert.isLegal(false, message);
1626                }
1627                return new Folder(path.makeAbsolute(), this);
1628            case IResource.FILE :
1629                if (path.segmentCount() < ICoreConstants.MINIMUM_FILE_SEGMENT_LENGTH) {
1630                    message = "Path must include project and resource name: " + path.toString(); //$NON-NLS-1$
1631
Assert.isLegal(false, message);
1632                }
1633                return new File(path.makeAbsolute(), this);
1634            case IResource.PROJECT :
1635                return (Resource) getRoot().getProject(path.lastSegment());
1636            case IResource.ROOT :
1637                return (Resource) getRoot();
1638        }
1639        Assert.isLegal(false);
1640        // will never get here because of assertion.
1641
return null;
1642    }
1643
1644    /**
1645     * Opens a new mutable element tree layer, thus allowing
1646     * modifications to the tree.
1647     */

1648    public ElementTree newWorkingTree() {
1649        tree = tree.newEmptyDelta();
1650        return tree;
1651    }
1652
1653    /**
1654     * Returns the next, previously unassigned, marker id.
1655     */

1656    protected long nextMarkerId() {
1657        return nextMarkerId++;
1658    }
1659
1660    protected long nextNodeId() {
1661        return nextNodeId++;
1662    }
1663
1664    /**
1665     * Opens this workspace using the data at its location in the local file system.
1666     * This workspace must not be open.
1667     * If the operation succeeds, the result will detail any serious
1668     * (but non-fatal) problems encountered while opening the workspace.
1669     * The status code will be <code>OK</code> if there were no problems.
1670     * An exception is thrown if there are fatal problems opening the workspace,
1671     * in which case the workspace is left closed.
1672     * <p>
1673     * This method is long-running; progress and cancellation are provided
1674     * by the given progress monitor.
1675     * </p>
1676     *
1677     * @param monitor a progress monitor, or <code>null</code> if progress
1678     * reporting and cancellation are not desired
1679     * @return status with code <code>OK</code> if no problems;
1680     * otherwise status describing any serious but non-fatal problems.
1681     *
1682     * @exception CoreException if the workspace could not be opened.
1683     * Reasons include:
1684     * <ul>
1685     * <li> There is no valid workspace structure at the given location
1686     * in the local file system.</li>
1687     * <li> The workspace structure on disk appears to be hopelessly corrupt.</li>
1688     * </ul>
1689     * @see ResourcesPlugin#getWorkspace()
1690     */

1691    public IStatus open(IProgressMonitor monitor) throws CoreException {
1692        // This method is not inside an operation because it is the one responsible for
1693
// creating the WorkManager object (who takes care of operations).
1694
String JavaDoc message = Messages.resources_workspaceOpen;
1695        Assert.isTrue(!isOpen(), message);
1696        if (!getMetaArea().hasSavedWorkspace()) {
1697            message = Messages.resources_readWorkspaceMeta;
1698            throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, Platform.getLocation(), message, null);
1699        }
1700        description = new WorkspacePreferences();
1701
1702        // if we have an old description file, read it (getting rid of it)
1703
WorkspaceDescription oldDescription = getMetaArea().readOldWorkspace();
1704        if (oldDescription != null) {
1705            description.copyFrom(oldDescription);
1706            ResourcesPlugin.getPlugin().savePluginPreferences();
1707        }
1708
1709        // create root location
1710
localMetaArea.locationFor(getRoot()).toFile().mkdirs();
1711
1712        IProgressMonitor nullMonitor = Policy.monitorFor(null);
1713        startup(nullMonitor);
1714        //restart the notification manager so it is initialized with the right tree
1715
notificationManager.startup(null);
1716        openFlag = true;
1717        if (crashed || refreshRequested()) {
1718            try {
1719                refreshManager.refresh(getRoot());
1720            } catch (RuntimeException JavaDoc e) {
1721                //don't fail entire open if refresh failed, just report as warning
1722
return new ResourceStatus(IResourceStatus.INTERNAL_ERROR, Path.ROOT, Messages.resources_errorMultiRefresh, e);
1723            }
1724        }
1725        //finally register a string pool participant
1726
stringPoolJob = new StringPoolJob();
1727        stringPoolJob.addStringPoolParticipant(saveManager, getRoot());
1728        return Status.OK_STATUS;
1729    }
1730
1731    /**
1732     * Called before checking the pre-conditions of an operation. Optionally supply
1733     * a scheduling rule to determine when the operation is safe to run. If a scheduling
1734     * rule is supplied, this method will block until it is safe to run.
1735     *
1736     * @param rule the scheduling rule that describes what this operation intends to modify.
1737     */

1738    public void prepareOperation(ISchedulingRule rule, IProgressMonitor monitor) throws CoreException {
1739        try {
1740            //make sure autobuild is not running
1741
if (rule != null)
1742                buildManager.interrupt();
1743        } finally {
1744            getWorkManager().checkIn(rule, monitor);
1745        }
1746        if (!isOpen()) {
1747            String JavaDoc message = Messages.resources_workspaceClosed;
1748            throw new ResourceException(IResourceStatus.OPERATION_FAILED, null, message, null);
1749        }
1750    }
1751
1752    protected boolean refreshRequested() {
1753        String JavaDoc[] args = Platform.getCommandLineArgs();
1754        for (int i = 0; i < args.length; i++)
1755            if (args[i].equalsIgnoreCase(REFRESH_ON_STARTUP))
1756                return true;
1757        return false;
1758    }
1759
1760    /* (non-Javadoc)
1761     * @see IWorkspace#removeResourceChangeListener(IResourceChangeListener)
1762     */

1763    public void removeResourceChangeListener(IResourceChangeListener listener) {
1764        notificationManager.removeListener(listener);
1765    }
1766
1767    /* (non-Javadoc)
1768     * @see IWorkspace#removeSaveParticipant(Plugin)
1769     */

1770    public void removeSaveParticipant(Plugin plugin) {
1771        Assert.isNotNull(plugin, "Plugin must not be null"); //$NON-NLS-1$
1772
saveManager.removeParticipant(plugin);
1773    }
1774
1775    /* (non-Javadoc)
1776     * @see IWorkspace#run(IWorkspaceRunnable, IProgressMonitor)
1777     */

1778    public void run(IWorkspaceRunnable action, IProgressMonitor monitor) throws CoreException {
1779        run(action, defaultRoot, IWorkspace.AVOID_UPDATE, monitor);
1780    }
1781
1782    /* (non-Javadoc)
1783     * @see IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor)
1784     */

1785    public void run(IWorkspaceRunnable action, ISchedulingRule rule, int options, IProgressMonitor monitor) throws CoreException {
1786        monitor = Policy.monitorFor(monitor);
1787        try {
1788            monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
1789
int depth = -1;
1790            boolean avoidNotification = (options & IWorkspace.AVOID_UPDATE) != 0;
1791            try {
1792                prepareOperation(rule, monitor);
1793                beginOperation(true);
1794                if (avoidNotification)
1795                    avoidNotification = notificationManager.beginAvoidNotify();
1796                depth = getWorkManager().beginUnprotected();
1797                action.run(Policy.subMonitorFor(monitor, Policy.opWork, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
1798            } catch (OperationCanceledException e) {
1799                getWorkManager().operationCanceled();
1800                throw e;
1801            } finally {
1802                if (avoidNotification)
1803                    notificationManager.endAvoidNotify();
1804                if (depth >= 0)
1805                    getWorkManager().endUnprotected(depth);
1806                endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
1807            }
1808        } finally {
1809            monitor.done();
1810        }
1811    }
1812
1813    /* (non-Javadoc)
1814     * @see IWorkspace#save(boolean, IProgressMonitor)
1815     */

1816    public IStatus save(boolean full, IProgressMonitor monitor) throws CoreException {
1817        String JavaDoc message;
1818        if (full) {
1819            //according to spec it is illegal to start a full save inside another operation
1820
if (getWorkManager().isLockAlreadyAcquired()) {
1821                message = Messages.resources_saveOp;
1822                throw new ResourceException(IResourceStatus.OPERATION_FAILED, null, message, new IllegalStateException JavaDoc());
1823            }
1824            return saveManager.save(ISaveContext.FULL_SAVE, null, monitor);
1825        }
1826        // A snapshot was requested. Start an operation (if not already started) and
1827
// signal that a snapshot should be done at the end.
1828
try {
1829            prepareOperation(getRoot(), monitor);
1830            beginOperation(false);
1831            saveManager.requestSnapshot();
1832            message = Messages.resources_snapRequest;
1833            return new ResourceStatus(IStatus.OK, message);
1834        } finally {
1835            endOperation(getRoot(), false, null);
1836        }
1837    }
1838
1839    public void setCrashed(boolean value) {
1840        crashed = value;
1841        if (crashed) {
1842            String JavaDoc msg = "The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes."; //$NON-NLS-1$
1843
Policy.log(new ResourceStatus(ICoreConstants.CRASH_DETECTED, msg));
1844            if (Policy.DEBUG)
1845                System.out.println(msg);
1846        }
1847    }
1848
1849    /* (non-Javadoc)
1850     * @see IWorkspace#setDescription(IWorkspaceDescription)
1851     */

1852    public void setDescription(IWorkspaceDescription value) {
1853        // if both the old and new description's build orders are null, leave the
1854
// workspace's build order slot because it is caching the computed order.
1855
// Otherwise, set the slot to null to force recomputing or building from the description.
1856
WorkspaceDescription newDescription = (WorkspaceDescription) value;
1857        String JavaDoc[] newOrder = newDescription.getBuildOrder(false);
1858        if (description.getBuildOrder(false) != null || newOrder != null)
1859            buildOrder = null;
1860        description.copyFrom(newDescription);
1861        ResourcesPlugin.getPlugin().savePluginPreferences();
1862    }
1863
1864    public void setTreeLocked(boolean locked) {
1865        treeLocked = locked ? Thread.currentThread() : null;
1866    }
1867
1868    /**
1869     * @deprecated
1870     */

1871    public void setWorkspaceLock(WorkspaceLock lock) {
1872        // do nothing
1873
}
1874
1875    /**
1876     * Shuts down the workspace managers.
1877     */

1878    protected void shutdown(IProgressMonitor monitor) throws CoreException {
1879        monitor = Policy.monitorFor(monitor);
1880        try {
1881            IManager[] managers = {buildManager, propertyManager, pathVariableManager, charsetManager, fileSystemManager, markerManager, _workManager, aliasManager, refreshManager, contentDescriptionManager};
1882            monitor.beginTask("", managers.length); //$NON-NLS-1$
1883
String JavaDoc message = Messages.resources_shutdownProblems;
1884            MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
1885            // best effort to shutdown every object and free resources
1886
for (int i = 0; i < managers.length; i++) {
1887                IManager manager = managers[i];
1888                if (manager == null)
1889                    monitor.worked(1);
1890                else {
1891                    try {
1892                        manager.shutdown(Policy.subMonitorFor(monitor, 1));
1893                    } catch (Exception JavaDoc e) {
1894                        message = Messages.resources_shutdownProblems;
1895                        status.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, e));
1896                    }
1897                }
1898            }
1899            buildManager = null;
1900            notificationManager = null;
1901            propertyManager = null;
1902            pathVariableManager = null;
1903            fileSystemManager = null;
1904            markerManager = null;
1905            synchronizer = null;
1906            saveManager = null;
1907            _workManager = null;
1908            aliasManager = null;
1909            refreshManager = null;
1910            charsetManager = null;
1911            contentDescriptionManager = null;
1912            if (!status.isOK())
1913                throw new CoreException(status);
1914        } finally {
1915            monitor.done();
1916        }
1917    }
1918
1919    /* (non-Javadoc)
1920     * @see IWorkspace#sortNatureSet(String[])
1921     */

1922    public String JavaDoc[] sortNatureSet(String JavaDoc[] natureIds) {
1923        return natureManager.sortNatureSet(natureIds);
1924    }
1925
1926    /**
1927     * Starts all the workspace manager classes.
1928     */

1929    protected void startup(IProgressMonitor monitor) throws CoreException {
1930        // ensure the tree is locked during the startup notification
1931
try {
1932            _workManager = new WorkManager(this);
1933            _workManager.startup(null);
1934            fileSystemManager = new FileSystemResourceManager(this);
1935            fileSystemManager.startup(monitor);
1936            pathVariableManager = new PathVariableManager();
1937            pathVariableManager.startup(null);
1938            natureManager = new NatureManager();
1939            natureManager.startup(null);
1940            buildManager = new BuildManager(this, getWorkManager().getLock());
1941            buildManager.startup(null);
1942            notificationManager = new NotificationManager(this);
1943            notificationManager.startup(null);
1944            markerManager = new MarkerManager(this);
1945            markerManager.startup(null);
1946            synchronizer = new Synchronizer(this);
1947            refreshManager = new RefreshManager(this);
1948            saveManager = new SaveManager(this);
1949            saveManager.startup(null);
1950            //must start after save manager, because (read) access to tree is needed
1951
refreshManager.startup(null);
1952            aliasManager = new AliasManager(this);
1953            aliasManager.startup(null);
1954            propertyManager = ResourcesCompatibilityHelper.createPropertyManager();
1955            propertyManager.startup(monitor);
1956            charsetManager = new CharsetManager(this);
1957            charsetManager.startup(null);
1958            contentDescriptionManager = new ContentDescriptionManager();
1959            contentDescriptionManager.startup(null);
1960        } finally {
1961            //unlock tree even in case of failure, otherwise shutdown will also fail
1962
treeLocked = null;
1963        }
1964    }
1965
1966    /**
1967     * Returns a string representation of this working state's
1968     * structure suitable for debug purposes.
1969     */

1970    public String JavaDoc toDebugString() {
1971        final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("\nDump of " + toString() + ":\n"); //$NON-NLS-1$ //$NON-NLS-2$
1972
buffer.append(" parent: " + tree.getParent()); //$NON-NLS-1$
1973
IElementContentVisitor visitor = new IElementContentVisitor() {
1974            public boolean visitElement(ElementTree aTree, IPathRequestor requestor, Object JavaDoc elementContents) {
1975                buffer.append("\n " + requestor.requestPath() + ": " + elementContents); //$NON-NLS-1$ //$NON-NLS-2$
1976
return true;
1977            }
1978        };
1979        new ElementTreeIterator(tree, Path.ROOT).iterate(visitor);
1980        return buffer.toString();
1981    }
1982
1983    public void updateModificationStamp(ResourceInfo info) {
1984        info.incrementModificationStamp();
1985    }
1986
1987    /* (non-javadoc)
1988     * @see IWorkspace#validateEdit(IFile[], Object)
1989     */

1990    public IStatus validateEdit(final IFile[] files, final Object JavaDoc context) {
1991        // if validation is turned off then just return
1992
if (!shouldValidate) {
1993            String JavaDoc message = Messages.resources_readOnly2;
1994            MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.READ_ONLY_LOCAL, message, null);
1995            for (int i = 0; i < files.length; i++) {
1996                if (files[i].isReadOnly()) {
1997                    IPath filePath = files[i].getFullPath();
1998                    message = NLS.bind(Messages.resources_readOnly, filePath);
1999                    result.add(new ResourceStatus(IResourceStatus.READ_ONLY_LOCAL, filePath, message));
2000                }
2001            }
2002            return result.getChildren().length == 0 ? Status.OK_STATUS : (IStatus) result;
2003        }
2004        // first time through the validator hasn't been initialized so try and create it
2005
if (validator == null)
2006            initializeValidator();
2007        // we were unable to initialize the validator. Validation has been turned off and
2008
// a warning has already been logged so just return.
2009
if (validator == null)
2010            return Status.OK_STATUS;
2011        // otherwise call the API and throw an exception if appropriate
2012
final IStatus[] status = new IStatus[1];
2013        ISafeRunnable body = new ISafeRunnable() {
2014            public void handleException(Throwable JavaDoc exception) {
2015                status[0] = new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception);
2016            }
2017
2018            public void run() throws Exception JavaDoc {
2019                Object JavaDoc c = context;
2020                //must null any reference to FileModificationValidationContext for backwards compatibility
2021
if (!(validator instanceof FileModificationValidator))
2022                    if (c instanceof FileModificationValidationContext)
2023                        c = null;
2024                status[0] = validator.validateEdit(files, c);
2025            }
2026        };
2027        SafeRunner.run(body);
2028        return status[0];
2029    }
2030
2031    /* (non-Javadoc)
2032     * @see IWorkspace#validateLinkLocation(IResource, IPath)
2033     */

2034    public IStatus validateLinkLocation(IResource resource, IPath unresolvedLocation) {
2035        return locationValidator.validateLinkLocation(resource, unresolvedLocation);
2036    }
2037
2038    /* (non-Javadoc)
2039     * @see IWorkspace#validateLinkLocation(IResource, IPath)
2040     */

2041    public IStatus validateLinkLocationURI(IResource resource, URI JavaDoc unresolvedLocation) {
2042        return locationValidator.validateLinkLocationURI(resource, unresolvedLocation);
2043    }
2044
2045    /* (non-Javadoc)
2046     * @see IWorkspace#validateName(String, int)
2047     */

2048    public IStatus validateName(String JavaDoc segment, int type) {
2049        return locationValidator.validateName(segment, type);
2050    }
2051
2052    /* (non-Javadoc)
2053     * @see IWorkspace#validateNatureSet(String[])
2054     */

2055    public IStatus validateNatureSet(String JavaDoc[] natureIds) {
2056        return natureManager.validateNatureSet(natureIds);
2057    }
2058
2059    /* (non-Javadoc)
2060     * @see IWorkspace#validatePath(String, int)
2061     */

2062    public IStatus validatePath(String JavaDoc path, int type) {
2063        return locationValidator.validatePath(path, type);
2064    }
2065
2066    /* (non-Javadoc)
2067     * @see IWorkspace#validateProjectLocation(IProject, IPath)
2068     */

2069    public IStatus validateProjectLocation(IProject context, IPath location) {
2070        return locationValidator.validateProjectLocation(context, location);
2071    }
2072
2073    /*
2074     * (non-Javadoc)
2075     * @see org.eclipse.core.resources.IWorkspace#validateProjectLocation(org.eclipse.core.resources.IProject, java.net.URI)
2076     */

2077    public IStatus validateProjectLocationURI(IProject project, URI JavaDoc location) {
2078        return locationValidator.validateProjectLocationURI(project, location);
2079    }
2080
2081    /**
2082     * Internal method. To be called only from the following methods:
2083     * <ul>
2084     * <li><code>IFile#appendContents</code></li>
2085     * <li><code>IFile#setContents(InputStream, boolean, boolean, IProgressMonitor)</code></li>
2086     * <li><code>IFile#setContents(IFileState, boolean, boolean, IProgressMonitor)</code></li>
2087     * </ul>
2088     *
2089     * @see IFileModificationValidator#validateSave(IFile)
2090     */

2091    protected void validateSave(final IFile file) throws CoreException {
2092        // if validation is turned off then just return
2093
if (!shouldValidate)
2094            return;
2095        // first time through the validator hasn't been initialized so try and create it
2096
if (validator == null)
2097            initializeValidator();
2098        // we were unable to initialize the validator. Validation has been turned off and
2099
// a warning has already been logged so just return.
2100
if (validator == null)
2101            return;
2102        // otherwise call the API and throw an exception if appropriate
2103
final IStatus[] status = new IStatus[1];
2104        ISafeRunnable body = new ISafeRunnable() {
2105            public void handleException(Throwable JavaDoc exception) {
2106                status[0] = new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception);
2107            }
2108
2109            public void run() throws Exception JavaDoc {
2110                status[0] = validator.validateSave(file);
2111            }
2112        };
2113        SafeRunner.run(body);
2114        if (!status[0].isOK())
2115            throw new ResourceException(status[0]);
2116    }
2117
2118}
2119
Popular Tags