KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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  *******************************************************************************/

11 package org.eclipse.core.internal.resources;
12
13 import java.io.*;
14 import java.util.*;
15 import org.eclipse.core.internal.events.*;
16 import org.eclipse.core.internal.localstore.*;
17 import org.eclipse.core.internal.utils.*;
18 import org.eclipse.core.internal.watson.*;
19 import org.eclipse.core.resources.*;
20 import org.eclipse.core.runtime.*;
21 import org.eclipse.core.runtime.jobs.ISchedulingRule;
22 import org.eclipse.core.runtime.jobs.Job;
23 import org.eclipse.osgi.util.NLS;
24
25 public class SaveManager implements IElementInfoFlattener, IManager, IStringPoolParticipant {
26     protected static final String JavaDoc CLEAR_DELTA_PREFIX = "clearDelta_"; //$NON-NLS-1$
27
protected static final String JavaDoc DELTA_EXPIRATION_PREFIX = "deltaExpiration_"; //$NON-NLS-1$
28
protected static final int DONE_SAVING = 3;
29
30     /**
31      * The minimum delay, in milliseconds, between workspace snapshots
32      */

33     private static final long MIN_SNAPSHOT_DELAY = 1000 * 30L; //30 seconds
34

35     /**
36      * The number of empty operations that are equivalent to a single non-
37      * trivial operation.
38      */

39     protected static final int NO_OP_THRESHOLD = 20;
40
41     /** constants */
42     protected static final int PREPARE_TO_SAVE = 1;
43     protected static final int ROLLBACK = 4;
44     protected static final String JavaDoc SAVE_NUMBER_PREFIX = "saveNumber_"; //$NON-NLS-1$
45
protected static final int SAVING = 2;
46     protected ElementTree lastSnap;
47     protected Properties masterTable;
48
49     /**
50      * A flag indicating that a save operation is occurring. This is a signal
51      * that snapshot should not be scheduled if a nested operation occurs during
52      * save.
53      */

54     private boolean isSaving = false;
55
56     /**
57      * The number of empty (non-changing) operations since the last snapshot.
58      */

59     protected int noopCount = 0;
60     /**
61      * The number of non-trivial operations since the last snapshot.
62      */

63     protected int operationCount = 0;
64
65     // Count up the time taken for all saves/snaps on markers and sync info
66
protected long persistMarkers = 0l;
67     protected long persistSyncInfo = 0l;
68
69     /**
70      * In-memory representation of plugins saved state. Maps String (plugin id)-> SavedState.
71      * This map is accessed from API that is not synchronized, so it requires
72      * independent synchronization. This is accomplished using a synchronized
73      * wrapper map.
74      */

75     protected Map savedStates;
76
77     /**
78      * Plugins that participate on a workspace save. Maps (Plugin -> ISaveParticipant).
79      * This map is accessed from API that is not synchronized, so it requires
80      * independent synchronization. This is accomplished using a synchronized
81      * wrapper map.
82      */

83     protected Map saveParticipants;
84
85     protected final DelayedSnapshotJob snapshotJob;
86
87     protected boolean snapshotRequested;
88     protected Workspace workspace;
89     //declare debug messages as fields to get sharing
90
private static final String JavaDoc DEBUG_START = " starting..."; //$NON-NLS-1$
91
private static final String JavaDoc DEBUG_FULL_SAVE = "Full save on workspace: "; //$NON-NLS-1$
92
private static final String JavaDoc DEBUG_PROJECT_SAVE = "Save on project "; //$NON-NLS-1$
93
private static final String JavaDoc DEBUG_SNAPSHOT = "Snapshot: "; //$NON-NLS-1$
94
private static final int TREE_BUFFER_SIZE = 1024 * 64;//64KB buffer
95

96     public SaveManager(Workspace workspace) {
97         this.workspace = workspace;
98         this.snapshotJob = new DelayedSnapshotJob(this);
99         snapshotRequested = false;
100         saveParticipants = Collections.synchronizedMap(new HashMap(10));
101     }
102
103     public ISavedState addParticipant(Plugin plugin, ISaveParticipant participant) throws CoreException {
104         // If the plugin was already registered as a save participant we return null
105
if (saveParticipants.put(plugin, participant) != null)
106             return null;
107         String JavaDoc id = plugin.getBundle().getSymbolicName();
108         SavedState state = (SavedState) savedStates.get(id);
109         if (state != null) {
110             if (isDeltaCleared(id)) {
111                 // this plugin was marked not to receive deltas
112
state.forgetTrees();
113                 removeClearDeltaMarks(id);
114             } else {
115                 try {
116                     // thread safety: (we need to guarantee that the tree is immutable when computing deltas)
117
// so, the tree inside the saved state needs to be immutable
118
workspace.prepareOperation(null, null);
119                     workspace.beginOperation(true);
120                     state.newTree = workspace.getElementTree();
121                 } finally {
122                     workspace.endOperation(null, false, null);
123                 }
124                 return state;
125             }
126         }
127         // if the plug-in has a previous save number, we return a state, otherwise we return null
128
if (getSaveNumber(id) > 0)
129             return new SavedState(workspace, id, null, null);
130         return null;
131     }
132
133     protected void broadcastLifecycle(final int lifecycle, Map contexts, final MultiStatus warnings, IProgressMonitor monitor) {
134         monitor = Policy.monitorFor(monitor);
135         try {
136             monitor.beginTask("", contexts.size()); //$NON-NLS-1$
137
for (final Iterator it = contexts.entrySet().iterator(); it.hasNext();) {
138                 Map.Entry entry = (Map.Entry) it.next();
139                 Plugin plugin = (Plugin) entry.getKey();
140                 final ISaveParticipant participant = (ISaveParticipant) saveParticipants.get(plugin);
141                 //save participants can be removed concurrently
142
if (participant == null) {
143                     monitor.worked(1);
144                     continue;
145                 }
146                 final SaveContext context = (SaveContext) entry.getValue();
147                 /* Be extra careful when calling lifecycle method on arbitrary plugin */
148                 ISafeRunnable code = new ISafeRunnable() {
149
150                     public void handleException(Throwable JavaDoc e) {
151                         String JavaDoc message = Messages.resources_saveProblem;
152                         IStatus status = new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, e);
153                         warnings.add(status);
154
155                         /* Remove entry for defective plug-in from this save operation */
156                         it.remove();
157                     }
158
159                     public void run() throws Exception JavaDoc {
160                         executeLifecycle(lifecycle, participant, context);
161                     }
162                 };
163                 SafeRunner.run(code);
164                 monitor.worked(1);
165             }
166         } finally {
167             monitor.done();
168         }
169     }
170
171     /**
172      * Remove the delta expiration timestamp from the master table, either
173      * because the saved state has been processed, or the delta has expired.
174      */

175     protected void clearDeltaExpiration(String JavaDoc pluginId) {
176         masterTable.remove(DELTA_EXPIRATION_PREFIX + pluginId);
177     }
178
179     protected void cleanMasterTable() {
180         //remove tree file entries for everything except closed projects
181
for (Iterator it = masterTable.keySet().iterator(); it.hasNext();) {
182             String JavaDoc key = (String JavaDoc) it.next();
183             if (!key.endsWith(LocalMetaArea.F_TREE))
184                 continue;
185             String JavaDoc prefix = key.substring(0, key.length() - LocalMetaArea.F_TREE.length());
186             //always save the root tree entry
187
if (prefix.equals(Path.ROOT.toString()))
188                 continue;
189             IProject project = workspace.getRoot().getProject(prefix);
190             if (!project.exists() || project.isOpen())
191                 it.remove();
192         }
193         IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
194         IPath backup = workspace.getMetaArea().getBackupLocationFor(location);
195         try {
196             saveMasterTable(backup);
197         } catch (CoreException e) {
198             Policy.log(e.getStatus());
199             backup.toFile().delete();
200             return;
201         }
202         if (location.toFile().exists() && !location.toFile().delete())
203             return;
204         try {
205             saveMasterTable(location);
206         } catch (CoreException e) {
207             Policy.log(e.getStatus());
208             location.toFile().delete();
209             return;
210         }
211         backup.toFile().delete();
212     }
213
214     /**
215      * Marks the current participants to not receive deltas next time they are registered
216      * as save participants. This is done in order to maintain consistency if we crash
217      * after a snapshot. It would force plug-ins to rebuild their state.
218      */

219     protected void clearSavedDelta() {
220         synchronized (saveParticipants) {
221             for (Iterator i = saveParticipants.keySet().iterator(); i.hasNext();) {
222                 String JavaDoc pluginId = ((Plugin) i.next()).getBundle().getSymbolicName();
223                 masterTable.setProperty(CLEAR_DELTA_PREFIX + pluginId, "true"); //$NON-NLS-1$
224
}
225         }
226     }
227
228     /**
229      * Collects the set of ElementTrees we are still interested in,
230      * and removes references to any other trees.
231      */

232     protected void collapseTrees() throws CoreException {
233         //collect trees we're interested in
234

235         //trees for plugin saved states
236
ArrayList trees = new ArrayList();
237         synchronized (savedStates) {
238             for (Iterator i = savedStates.values().iterator(); i.hasNext();) {
239                 SavedState state = (SavedState) i.next();
240                 if (state.oldTree != null) {
241                     trees.add(state.oldTree);
242                 }
243             }
244         }
245
246         //trees for builders
247
IProject[] projects = workspace.getRoot().getProjects();
248         for (int i = 0; i < projects.length; i++) {
249             IProject project = projects[i];
250             if (project.isOpen()) {
251                 ArrayList builderInfos = workspace.getBuildManager().createBuildersPersistentInfo(project);
252                 if (builderInfos != null) {
253                     for (Iterator it = builderInfos.iterator(); it.hasNext();) {
254                         BuilderPersistentInfo info = (BuilderPersistentInfo) it.next();
255                         trees.add(info.getLastBuiltTree());
256                     }
257                 }
258             }
259         }
260
261         //no need to collapse if there are no trees at this point
262
if (trees.isEmpty())
263             return;
264
265         //the complete tree
266
trees.add(workspace.getElementTree());
267
268         //collapse the trees
269
//sort trees in topological order, and set the parent of each
270
//tree to its parent in the topological ordering.
271
ElementTree[] treeArray = new ElementTree[trees.size()];
272         trees.toArray(treeArray);
273         ElementTree[] sorted = sortTrees(treeArray);
274         // if there was a problem sorting the tree, bail on trying to collapse.
275
// We will be able to GC the layers at a later time.
276
if (sorted == null)
277             return;
278         for (int i = 1; i < sorted.length; i++)
279             sorted[i].collapseTo(sorted[i - 1]);
280     }
281
282     protected void commit(Map contexts) throws CoreException {
283         for (Iterator i = contexts.values().iterator(); i.hasNext();)
284             ((SaveContext) i.next()).commit();
285     }
286
287     /**
288      * Given a collection of save participants, compute the collection of
289      * <code>SaveContexts</code> to use during the save lifecycle.
290      * The keys are plugins and values are SaveContext objects.
291      */

292     protected Map computeSaveContexts(Plugin[] plugins, int kind, IProject project) {
293         HashMap result = new HashMap(plugins.length);
294         for (int i = 0; i < plugins.length; i++) {
295             Plugin plugin = plugins[i];
296             try {
297                 SaveContext context = new SaveContext(plugin, kind, project);
298                 result.put(plugin, context);
299             } catch (CoreException e) {
300                 // FIXME: should return a status to the user and not just log it
301
Policy.log(e.getStatus());
302             }
303         }
304         return result;
305     }
306
307     /**
308      * Returns a table mapping having the plug-in id as the key and the old tree
309      * as the value.
310      * This table is based on the union of the current savedStates</code>
311      * and the given table of contexts. The specified tree is used as the tree for
312      * any newly created saved states. This method is used to compute the set of
313      * saved states to be written out.
314      */

315     protected Map computeStatesToSave(Map contexts, ElementTree current) {
316         HashMap result = new HashMap(savedStates.size() * 2);
317         synchronized (savedStates) {
318             for (Iterator i = savedStates.values().iterator(); i.hasNext();) {
319                 SavedState state = (SavedState) i.next();
320                 if (state.oldTree != null)
321                     result.put(state.pluginId, state.oldTree);
322             }
323         }
324         for (Iterator i = contexts.values().iterator(); i.hasNext();) {
325             SaveContext context = (SaveContext) i.next();
326             if (!context.isDeltaNeeded())
327                 continue;
328             String JavaDoc pluginId = context.getPlugin().getBundle().getSymbolicName();
329             result.put(pluginId, current);
330         }
331         return result;
332     }
333
334     protected void executeLifecycle(int lifecycle, ISaveParticipant participant, SaveContext context) throws CoreException {
335         switch (lifecycle) {
336             case PREPARE_TO_SAVE :
337                 participant.prepareToSave(context);
338                 break;
339             case SAVING :
340                 try {
341                     if (ResourceStats.TRACE_SAVE_PARTICIPANTS)
342                         ResourceStats.startSave(participant);
343                     participant.saving(context);
344                 } finally {
345                     if (ResourceStats.TRACE_SAVE_PARTICIPANTS)
346                         ResourceStats.endSave();
347                 }
348                 break;
349             case DONE_SAVING :
350                 participant.doneSaving(context);
351                 break;
352             case ROLLBACK :
353                 participant.rollback(context);
354                 break;
355             default :
356                 Assert.isTrue(false, "Invalid save lifecycle code"); //$NON-NLS-1$
357
}
358     }
359
360     public void forgetSavedTree(String JavaDoc pluginId) {
361         if (pluginId == null) {
362             synchronized (savedStates) {
363                 for (Iterator i = savedStates.values().iterator(); i.hasNext();)
364                     ((SavedState) i.next()).forgetTrees();
365             }
366         } else {
367             SavedState state = (SavedState) savedStates.get(pluginId);
368             if (state != null)
369                 state.forgetTrees();
370         }
371     }
372
373     /**
374      * Used in the policy for cleaning up tree's of plug-ins that are not often activated.
375      */

376     protected long getDeltaExpiration(String JavaDoc pluginId) {
377         String JavaDoc result = masterTable.getProperty(DELTA_EXPIRATION_PREFIX + pluginId);
378         return (result == null) ? System.currentTimeMillis() : new Long JavaDoc(result).longValue();
379     }
380
381     protected Properties getMasterTable() {
382         return masterTable;
383     }
384
385     public int getSaveNumber(String JavaDoc pluginId) {
386         String JavaDoc value = masterTable.getProperty(SAVE_NUMBER_PREFIX + pluginId);
387         return (value == null) ? 0 : new Integer JavaDoc(value).intValue();
388     }
389
390     protected Plugin[] getSaveParticipantPlugins() {
391         synchronized (saveParticipants) {
392             return (Plugin[]) saveParticipants.keySet().toArray(new Plugin[saveParticipants.size()]);
393         }
394     }
395
396     /**
397      * Hooks the end of a save operation, for debugging and performance
398      * monitoring purposes.
399      */

400     private void hookEndSave(int kind, IProject project, long start) {
401         if (ResourceStats.TRACE_SNAPSHOT && kind == ISaveContext.SNAPSHOT)
402             ResourceStats.endSnapshot();
403         if (Policy.DEBUG_SAVE) {
404             String JavaDoc endMessage = null;
405             switch (kind) {
406                 case ISaveContext.FULL_SAVE :
407                     endMessage = DEBUG_FULL_SAVE;
408                     break;
409                 case ISaveContext.SNAPSHOT :
410                     endMessage = DEBUG_SNAPSHOT;
411                     break;
412                 case ISaveContext.PROJECT_SAVE :
413                     endMessage = DEBUG_PROJECT_SAVE + project.getFullPath() + ": "; //$NON-NLS-1$
414
break;
415             }
416             if (endMessage != null)
417                 System.out.println(endMessage + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$
418
}
419     }
420
421     /**
422      * Hooks the start of a save operation, for debugging and performance
423      * monitoring purposes.
424      */

425     private void hookStartSave(int kind, Project project) {
426         if (ResourceStats.TRACE_SNAPSHOT && kind == ISaveContext.SNAPSHOT)
427             ResourceStats.startSnapshot();
428         if (Policy.DEBUG_SAVE) {
429             switch (kind) {
430                 case ISaveContext.FULL_SAVE :
431                     System.out.println(DEBUG_FULL_SAVE + DEBUG_START);
432                     break;
433                 case ISaveContext.SNAPSHOT :
434                     System.out.println(DEBUG_SNAPSHOT + DEBUG_START);
435                     break;
436                 case ISaveContext.PROJECT_SAVE :
437                     System.out.println(DEBUG_PROJECT_SAVE + project.getFullPath() + DEBUG_START);
438                     break;
439             }
440         }
441     }
442
443     /**
444      * Initializes the snapshot mechanism for this workspace.
445      */

446     protected void initSnap(IProgressMonitor monitor) throws CoreException {
447         //discard any pending snapshot request
448
snapshotJob.cancel();
449         //the "lastSnap" tree must be frozen as the exact tree obtained from startup,
450
// otherwise ensuing snapshot deltas may be based on an incorrect tree (see bug 12575)
451
lastSnap = workspace.getElementTree();
452         lastSnap.immutable();
453         workspace.newWorkingTree();
454         operationCount = 0;
455         // delete the snapshot file, if any
456
IPath snapPath = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
457         java.io.File JavaDoc file = snapPath.toFile();
458         if (file.exists())
459             file.delete();
460         if (file.exists()) {
461             String JavaDoc message = Messages.resources_snapInit;
462             throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, null, message, null);
463         }
464     }
465
466     protected boolean isDeltaCleared(String JavaDoc pluginId) {
467         String JavaDoc clearDelta = masterTable.getProperty(CLEAR_DELTA_PREFIX + pluginId);
468         return clearDelta != null && clearDelta.equals("true"); //$NON-NLS-1$
469
}
470
471     protected boolean isOldPluginTree(String JavaDoc pluginId) {
472         // first, check if this plug-ins was marked not to receive a delta
473
if (isDeltaCleared(pluginId))
474             return false;
475         //see if the plugin is still installed
476
if (Platform.getBundle(pluginId) == null)
477             return true;
478
479         //finally see if the delta has past its expiry date
480
long deltaAge = System.currentTimeMillis() - getDeltaExpiration(pluginId);
481         return deltaAge > workspace.internalGetDescription().getDeltaExpiration();
482     }
483
484     /**
485      * @see IElementInfoFlattener#readElement(IPath, DataInput)
486      */

487     public Object JavaDoc readElement(IPath path, DataInput input) throws IOException {
488         Assert.isNotNull(path);
489         Assert.isNotNull(input);
490         // read the flags and pull out the type.
491
int flags = input.readInt();
492         int type = (flags & ICoreConstants.M_TYPE) >> ICoreConstants.M_TYPE_START;
493         ResourceInfo info = workspace.newElement(type);
494         info.readFrom(flags, input);
495         return info;
496     }
497
498     /**
499      * Remove marks from current save participants. This marks prevent them to receive their
500      * deltas when they register themselves as save participants.
501      */

502     protected void removeClearDeltaMarks() {
503         synchronized (saveParticipants) {
504             for (Iterator i = saveParticipants.keySet().iterator(); i.hasNext();) {
505                 String JavaDoc pluginId = ((Plugin) i.next()).getBundle().getSymbolicName();
506                 removeClearDeltaMarks(pluginId);
507             }
508         }
509     }
510
511     protected void removeClearDeltaMarks(String JavaDoc pluginId) {
512         masterTable.setProperty(CLEAR_DELTA_PREFIX + pluginId, "false"); //$NON-NLS-1$
513
}
514
515     protected void removeFiles(java.io.File JavaDoc root, String JavaDoc[] candidates, List exclude) {
516         for (int i = 0; i < candidates.length; i++) {
517             boolean delete = true;
518             for (ListIterator it = exclude.listIterator(); it.hasNext();) {
519                 String JavaDoc s = (String JavaDoc) it.next();
520                 if (s.equals(candidates[i])) {
521                     it.remove();
522                     delete = false;
523                     break;
524                 }
525             }
526             if (delete)
527                 new java.io.File JavaDoc(root, candidates[i]).delete();
528         }
529     }
530
531     private void removeGarbage(DataOutputStream output, IPath location, IPath tempLocation) throws IOException {
532         if (output.size() == 0) {
533             output.close();
534             location.toFile().delete();
535             tempLocation.toFile().delete();
536         }
537     }
538
539     public void removeParticipant(Plugin plugin) {
540         saveParticipants.remove(plugin);
541     }
542
543     protected void removeUnusedSafeTables() {
544         List valuables = new ArrayList(10);
545         IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
546         valuables.add(location.lastSegment()); // add master table
547
for (Enumeration e = masterTable.keys(); e.hasMoreElements();) {
548             String JavaDoc key = (String JavaDoc) e.nextElement();
549             if (key.startsWith(SAVE_NUMBER_PREFIX)) {
550                 String JavaDoc pluginId = key.substring(SAVE_NUMBER_PREFIX.length());
551                 valuables.add(workspace.getMetaArea().getSafeTableLocationFor(pluginId).lastSegment());
552             }
553         }
554         java.io.File JavaDoc target = location.toFile().getParentFile();
555         String JavaDoc[] candidates = target.list();
556         if (candidates == null)
557             return;
558         removeFiles(target, candidates, valuables);
559     }
560
561     protected void removeUnusedTreeFiles() {
562         // root resource
563
List valuables = new ArrayList(10);
564         IPath location = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), false);
565         valuables.add(location.lastSegment());
566         java.io.File JavaDoc target = location.toFile().getParentFile();
567         FilenameFilter filter = new FilenameFilter() {
568             public boolean accept(java.io.File JavaDoc dir, String JavaDoc name) {
569                 return name.endsWith(LocalMetaArea.F_TREE);
570             }
571         };
572         String JavaDoc[] candidates = target.list(filter);
573         if (candidates != null)
574             removeFiles(target, candidates, valuables);
575
576         // projects
577
IProject[] projects = workspace.getRoot().getProjects();
578         for (int i = 0; i < projects.length; i++) {
579             location = workspace.getMetaArea().getTreeLocationFor(projects[i], false);
580             valuables.add(location.lastSegment());
581             target = location.toFile().getParentFile();
582             candidates = target.list(filter);
583             if (candidates != null)
584                 removeFiles(target, candidates, valuables);
585         }
586     }
587
588     public void requestSnapshot() {
589         snapshotRequested = true;
590     }
591
592     /**
593      * Reset the snapshot mechanism for the non-workspace files. This
594      * includes the markers and sync info.
595      */

596     protected void resetSnapshots(IResource resource) throws CoreException {
597         Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
598         String JavaDoc message;
599
600         // delete the snapshot file, if any
601
java.io.File JavaDoc file = workspace.getMetaArea().getMarkersSnapshotLocationFor(resource).toFile();
602         if (file.exists())
603             file.delete();
604         if (file.exists()) {
605             message = Messages.resources_resetMarkers;
606             throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, resource.getFullPath(), message, null);
607         }
608
609         // delete the snapshot file, if any
610
file = workspace.getMetaArea().getSyncInfoSnapshotLocationFor(resource).toFile();
611         if (file.exists())
612             file.delete();
613         if (file.exists()) {
614             message = Messages.resources_resetSync;
615             throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, resource.getFullPath(), message, null);
616         }
617
618         // if we have the workspace root then recursive over the projects.
619
// only do open projects since closed ones are saved elsewhere
620
if (resource.getType() == IResource.PROJECT)
621             return;
622         IProject[] projects = ((IWorkspaceRoot) resource).getProjects();
623         for (int i = 0; i < projects.length; i++)
624             resetSnapshots(projects[i]);
625     }
626
627     /**
628      * Restores the state of this workspace by opening the projects
629      * which were open when it was last saved.
630      */

631     protected void restore(IProgressMonitor monitor) throws CoreException {
632         if (Policy.DEBUG_RESTORE)
633             System.out.println("Restore workspace: starting..."); //$NON-NLS-1$
634
long start = System.currentTimeMillis();
635         monitor = Policy.monitorFor(monitor);
636         try {
637             monitor.beginTask("", 50); //$NON-NLS-1$
638
// need to open the tree to restore, but since we're not
639
// inside an operation, be sure to close it afterwards
640
workspace.newWorkingTree();
641             try {
642                 String JavaDoc msg = Messages.resources_startupProblems;
643                 MultiStatus problems = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, msg, null);
644
645                 restoreMasterTable();
646                 // restore the saved tree and overlay the snapshots if any
647
restoreTree(Policy.subMonitorFor(monitor, 10));
648                 restoreSnapshots(Policy.subMonitorFor(monitor, 10));
649
650                 // tolerate failure for non-critical information
651
// if startup fails, the entire workspace is shot
652
try {
653                     restoreMarkers(workspace.getRoot(), false, Policy.subMonitorFor(monitor, 10));
654                 } catch (CoreException e) {
655                     problems.merge(e.getStatus());
656                 }
657                 try {
658                     restoreSyncInfo(workspace.getRoot(), Policy.subMonitorFor(monitor, 10));
659                 } catch (CoreException e) {
660                     problems.merge(e.getStatus());
661                 }
662                 // restore meta info last because it might close a project if its description is not readable
663
restoreMetaInfo(problems, Policy.subMonitorFor(monitor, 10));
664                 IProject[] roots = workspace.getRoot().getProjects();
665                 for (int i = 0; i < roots.length; i++)
666                     ((Project) roots[i]).startup();
667                 if (!problems.isOK())
668                     Policy.log(problems);
669             } finally {
670                 workspace.getElementTree().immutable();
671             }
672         } finally {
673             monitor.done();
674         }
675         if (Policy.DEBUG_RESTORE)
676             System.out.println("Restore workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
677
}
678
679     /**
680      * Restores the contents of this project. Throw
681      * an exception if the project could not be restored.
682      */

683     protected void restore(Project project, IProgressMonitor monitor) throws CoreException {
684         if (Policy.DEBUG_RESTORE)
685             System.out.println("Restore project " + project.getFullPath() + ": starting..."); //$NON-NLS-1$ //$NON-NLS-2$
686
long start = System.currentTimeMillis();
687         monitor = Policy.monitorFor(monitor);
688         try {
689             monitor.beginTask("", 40); //$NON-NLS-1$
690
if (project.isOpen()) {
691                 restoreTree(project, Policy.subMonitorFor(monitor, 10));
692             } else {
693                 monitor.worked(10);
694             }
695             restoreMarkers(project, true, Policy.subMonitorFor(monitor, 10));
696             restoreSyncInfo(project, Policy.subMonitorFor(monitor, 10));
697             // restore meta info last because it might close a project if its description is not found
698
restoreMetaInfo(project, Policy.subMonitorFor(monitor, 10));
699         } finally {
700             monitor.done();
701         }
702         if (Policy.DEBUG_RESTORE)
703             System.out.println("Restore project " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
704
}
705
706     /**
707      * Reads the markers which were originally saved
708      * for the tree rooted by the given resource.
709      */

710     protected void restoreMarkers(IResource resource, boolean generateDeltas, IProgressMonitor monitor) throws CoreException {
711         Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
712         long start = System.currentTimeMillis();
713         MarkerManager markerManager = workspace.getMarkerManager();
714         // when restoring a project, only load markers if it is open
715
if (resource.isAccessible())
716             markerManager.restore(resource, generateDeltas, monitor);
717
718         // if we have the workspace root then restore markers for its projects
719
if (resource.getType() == IResource.PROJECT) {
720             if (Policy.DEBUG_RESTORE_MARKERS) {
721                 System.out.println("Restore Markers for " + resource.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
722
}
723             return;
724         }
725         IProject[] projects = ((IWorkspaceRoot) resource).getProjects();
726         for (int i = 0; i < projects.length; i++)
727             if (projects[i].isAccessible())
728                 markerManager.restore(projects[i], generateDeltas, monitor);
729         if (Policy.DEBUG_RESTORE_MARKERS) {
730             System.out.println("Restore Markers for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
731
}
732     }
733
734     protected void restoreMasterTable() throws CoreException {
735         long start = System.currentTimeMillis();
736         masterTable = new Properties();
737         IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
738         java.io.File JavaDoc target = location.toFile();
739         if (!target.exists()) {
740             location = workspace.getMetaArea().getBackupLocationFor(location);
741             target = location.toFile();
742             if (!target.exists())
743                 return;
744         }
745         try {
746             SafeChunkyInputStream input = new SafeChunkyInputStream(target);
747             try {
748                 masterTable.load(input);
749             } finally {
750                 input.close();
751             }
752         } catch (IOException e) {
753             String JavaDoc message = Messages.resources_exMasterTable;
754             throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, message, e);
755         }
756         if (Policy.DEBUG_RESTORE_MASTERTABLE)
757             System.out.println("Restore master table for " + location + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
758
}
759
760     /**
761      * Restores the state of this workspace by opening the projects
762      * which were open when it was last saved.
763      */

764     protected void restoreMetaInfo(MultiStatus problems, IProgressMonitor monitor) {
765         if (Policy.DEBUG_RESTORE_METAINFO)
766             System.out.println("Restore workspace metainfo: starting..."); //$NON-NLS-1$
767
long start = System.currentTimeMillis();
768         IProject[] roots = workspace.getRoot().getProjects();
769         for (int i = 0; i < roots.length; i++) {
770             //fatal to throw exceptions during startup
771
try {
772                 restoreMetaInfo((Project) roots[i], monitor);
773             } catch (CoreException e) {
774                 String JavaDoc message = NLS.bind(Messages.resources_readMeta, roots[i].getName());
775                 problems.merge(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, roots[i].getFullPath(), message, e));
776             }
777         }
778         if (Policy.DEBUG_RESTORE_METAINFO)
779             System.out.println("Restore workspace metainfo: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
780
}
781
782     /**
783      * Restores the contents of this project. Throw an exception if the
784      * project description could not be restored.
785      */

786     protected void restoreMetaInfo(Project project, IProgressMonitor monitor) throws CoreException {
787         long start = System.currentTimeMillis();
788         ProjectDescription description = null;
789         CoreException failure = null;
790         try {
791             if (project.isOpen())
792                 description = workspace.getFileSystemManager().read(project, true);
793             else
794                 //for closed projects, just try to read the legacy .prj file,
795
//because the project location is stored there.
796
description = workspace.getMetaArea().readOldDescription(project);
797         } catch (CoreException e) {
798             failure = e;
799         }
800         // If we had an open project and there was an error reading the description
801
// from disk, close the project and give it a default description. If the project
802
// was already closed then just set a default description.
803
if (description == null) {
804             description = new ProjectDescription();
805             description.setName(project.getName());
806             //try to read private metadata and add to the description
807
workspace.getMetaArea().readPrivateDescription(project, description);
808         }
809         project.internalSetDescription(description, false);
810         if (failure != null) {
811             //close the project
812
project.internalClose();
813             throw failure;
814         }
815         if (Policy.DEBUG_RESTORE_METAINFO)
816             System.out.println("Restore metainfo for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
817
}
818
819     /**
820      * Restores the workspace tree from snapshot files in the event
821      * of a crash. The workspace tree must be open when this method
822      * is called, and will be open at the end of this method. In the
823      * event of a crash recovery, the snapshot file is not deleted until
824      * the next successful save.
825      */

826     protected void restoreSnapshots(IProgressMonitor monitor) throws CoreException {
827         long start = System.currentTimeMillis();
828         monitor = Policy.monitorFor(monitor);
829         String JavaDoc message;
830         try {
831             monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
832
IPath snapLocation = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
833             java.io.File JavaDoc localFile = snapLocation.toFile();
834
835             // If the snapshot file doesn't exist, there was no crash.
836
// Just initialize the snapshot file and return.
837
if (!localFile.exists()) {
838                 initSnap(Policy.subMonitorFor(monitor, Policy.totalWork / 2));
839                 return;
840             }
841             // If we have a snapshot file, the workspace was shutdown without being saved or crashed.
842
workspace.setCrashed(true);
843             try {
844                 /* Read each of the snapshots and lay them on top of the current tree.*/
845                 ElementTree complete = workspace.getElementTree();
846                 complete.immutable();
847                 DataInputStream input = new DataInputStream(new SafeChunkyInputStream(localFile));
848                 try {
849                     WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt());
850                     complete = reader.readSnapshotTree(input, complete, monitor);
851                 } finally {
852                     FileUtil.safeClose(input);
853                     //reader returned an immutable tree, but since we're inside
854
//an operation, we must return an open tree
855
lastSnap = complete;
856                     complete = complete.newEmptyDelta();
857                     workspace.tree = complete;
858                 }
859             } catch (Exception JavaDoc e) {
860                 // only log the exception, we should not fail restoring the snapshot
861
message = Messages.resources_snapRead;
862                 Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, null, message, e));
863             }
864         } finally {
865             monitor.done();
866         }
867         if (Policy.DEBUG_RESTORE_SNAPSHOTS)
868             System.out.println("Restore snapshots for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
869
}
870
871     /**
872      * Reads the sync info which was originally saved
873      * for the tree rooted by the given resource.
874      */

875     protected void restoreSyncInfo(IResource resource, IProgressMonitor monitor) throws CoreException {
876         Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
877         long start = System.currentTimeMillis();
878         Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
879         // when restoring a project, only load sync info if it is open
880
if (resource.isAccessible())
881             synchronizer.restore(resource, monitor);
882
883         // restore sync info for all projects if we were given the workspace root.
884
if (resource.getType() == IResource.PROJECT) {
885             if (Policy.DEBUG_RESTORE_SYNCINFO) {
886                 System.out.println("Restore SyncInfo for " + resource.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
887
}
888             return;
889         }
890         IProject[] projects = ((IWorkspaceRoot) resource).getProjects();
891         for (int i = 0; i < projects.length; i++)
892             if (projects[i].isAccessible())
893                 synchronizer.restore(projects[i], monitor);
894         if (Policy.DEBUG_RESTORE_SYNCINFO) {
895             System.out.println("Restore SyncInfo for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
896
}
897     }
898
899     /**
900      * Reads the contents of the tree rooted by the given resource from the
901      * file system. This method is used when restoring a complete workspace
902      * after workspace save/shutdown.
903      * @exception CoreException if the workspace could not be restored.
904      */

905     protected void restoreTree(IProgressMonitor monitor) throws CoreException {
906         long start = System.currentTimeMillis();
907         IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), false);
908         IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
909         if (!treeLocation.toFile().exists() && !tempLocation.toFile().exists()) {
910             savedStates = Collections.synchronizedMap(new HashMap(10));
911             return;
912         }
913         try {
914             DataInputStream input = new DataInputStream(new SafeFileInputStream(treeLocation.toOSString(), tempLocation.toOSString(), TREE_BUFFER_SIZE));
915             try {
916                 WorkspaceTreeReader.getReader(workspace, input.readInt()).readTree(input, monitor);
917             } finally {
918                 input.close();
919             }
920         } catch (IOException e) {
921             String JavaDoc msg = NLS.bind(Messages.resources_readMeta, treeLocation.toOSString());
922             throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, treeLocation, msg, e);
923         }
924         if (Policy.DEBUG_RESTORE_TREE) {
925             System.out.println("Restore Tree for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
926
}
927     }
928
929     /**
930      * Restores the trees for the builders of this project from the local disk.
931      * Does nothing if the tree file does not exist (this means the
932      * project has never been saved). This method is
933      * used when restoring a saved/closed project. restoreTree(Workspace) is
934      * used when restoring a complete workspace after workspace save/shutdown.
935      * @exception CoreException if the project could not be restored.
936      */

937     protected void restoreTree(Project project, IProgressMonitor monitor) throws CoreException {
938         long start = System.currentTimeMillis();
939         monitor = Policy.monitorFor(monitor);
940         String JavaDoc message;
941         try {
942             monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
943
IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(project, false);
944             IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
945             if (!treeLocation.toFile().exists() && !tempLocation.toFile().exists())
946                 return;
947             DataInputStream input = new DataInputStream(new SafeFileInputStream(treeLocation.toOSString(), tempLocation.toOSString()));
948             try {
949                 WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt());
950                 reader.readTree(project, input, Policy.subMonitorFor(monitor, Policy.totalWork));
951             } finally {
952                 input.close();
953             }
954         } catch (IOException e) {
955             message = NLS.bind(Messages.resources_readMeta, project.getFullPath());
956             throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, project.getFullPath(), message, e);
957         } finally {
958             monitor.done();
959         }
960         if (Policy.DEBUG_RESTORE_TREE) {
961             System.out.println("Restore Tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
962
}
963     }
964
965     public IStatus save(int kind, Project project, IProgressMonitor monitor) throws CoreException {
966         monitor = Policy.monitorFor(monitor);
967         try {
968             isSaving = true;
969             String JavaDoc message = Messages.resources_saving_0;
970             monitor.beginTask(message, 7);
971             message = Messages.resources_saveWarnings;
972             MultiStatus warnings = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.WARNING, message, null);
973             ISchedulingRule rule = project != null ? (IResource) project : workspace.getRoot();
974             try {
975                 workspace.prepareOperation(rule, monitor);
976                 workspace.beginOperation(false);
977                 hookStartSave(kind, project);
978                 long start = System.currentTimeMillis();
979                 Map contexts = computeSaveContexts(getSaveParticipantPlugins(), kind, project);
980                 broadcastLifecycle(PREPARE_TO_SAVE, contexts, warnings, Policy.subMonitorFor(monitor, 1));
981                 try {
982                     broadcastLifecycle(SAVING, contexts, warnings, Policy.subMonitorFor(monitor, 1));
983                     switch (kind) {
984                         case ISaveContext.FULL_SAVE :
985                             // save the complete tree and remember all of the required saved states
986
saveTree(contexts, Policy.subMonitorFor(monitor, 1));
987                             // reset the snapshot state.
988
initSnap(null);
989                             //save master table right after saving tree to ensure correct tree number is saved
990
cleanMasterTable();
991                             // save all of the markers and all sync info in the workspace
992
persistMarkers = 0l;
993                             persistSyncInfo = 0l;
994                             visitAndSave(workspace.getRoot());
995                             monitor.worked(1);
996                             if (Policy.DEBUG_SAVE) {
997                                 Policy.debug("Total Save Markers: " + persistMarkers + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
998
Policy.debug("Total Save Sync Info: " + persistSyncInfo + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
999
}
1000                            // reset the snap shot files
1001
resetSnapshots(workspace.getRoot());
1002                            //remove unused files
1003
removeUnusedSafeTables();
1004                            removeUnusedTreeFiles();
1005                            workspace.getFileSystemManager().getHistoryStore().clean(Policy.subMonitorFor(monitor, 1));
1006                            // write out all metainfo (e.g., workspace/project descriptions)
1007
saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
1008                            break;
1009                        case ISaveContext.SNAPSHOT :
1010                            snapTree(workspace.getElementTree(), Policy.subMonitorFor(monitor, 1));
1011                            // snapshot the markers and sync info for the workspace
1012
persistMarkers = 0l;
1013                            persistSyncInfo = 0l;
1014                            visitAndSnap(workspace.getRoot());
1015                            monitor.worked(1);
1016                            if (Policy.DEBUG_SAVE) {
1017                                Policy.debug("Total Snap Markers: " + persistMarkers + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1018
Policy.debug("Total Snap Sync Info: " + persistSyncInfo + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1019
}
1020                            collapseTrees();
1021                            clearSavedDelta();
1022                            // write out all metainfo (e.g., workspace/project descriptions)
1023
saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
1024                            break;
1025                        case ISaveContext.PROJECT_SAVE :
1026                            writeTree(project, IResource.DEPTH_INFINITE);
1027                            monitor.worked(1);
1028                            // save markers and sync info
1029
visitAndSave(project);
1030                            monitor.worked(1);
1031                            // reset the snapshot file
1032
resetSnapshots(project);
1033                            IStatus result = saveMetaInfo(project, null);
1034                            if (!result.isOK())
1035                                warnings.merge(result);
1036                            monitor.worked(1);
1037                            break;
1038                    }
1039                    // save contexts
1040
commit(contexts);
1041                    if (kind == ISaveContext.FULL_SAVE)
1042                        removeClearDeltaMarks();
1043                    //this must be done after committing save contexts to update participant save numbers
1044
saveMasterTable();
1045                    broadcastLifecycle(DONE_SAVING, contexts, warnings, Policy.subMonitorFor(monitor, 1));
1046                    hookEndSave(kind, project, start);
1047                    return warnings;
1048                } catch (CoreException e) {
1049                    broadcastLifecycle(ROLLBACK, contexts, warnings, Policy.subMonitorFor(monitor, 1));
1050                    // rollback ResourcesPlugin master table
1051
restoreMasterTable();
1052                    throw e; // re-throw
1053
}
1054            } catch (OperationCanceledException e) {
1055                workspace.getWorkManager().operationCanceled();
1056                throw e;
1057            } finally {
1058                workspace.endOperation(rule, false, Policy.monitorFor(null));
1059            }
1060        } finally {
1061            isSaving = false;
1062            monitor.done();
1063        }
1064    }
1065
1066    protected void saveMasterTable() throws CoreException {
1067        saveMasterTable(workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES));
1068    }
1069
1070    protected void saveMasterTable(IPath location) throws CoreException {
1071        long start = System.currentTimeMillis();
1072        java.io.File JavaDoc target = location.toFile();
1073        try {
1074            SafeChunkyOutputStream output = new SafeChunkyOutputStream(target);
1075            try {
1076                masterTable.store(output, "master table"); //$NON-NLS-1$
1077
output.succeed();
1078            } finally {
1079                output.close();
1080            }
1081        } catch (IOException e) {
1082            String JavaDoc message = Messages.resources_exSaveMaster;
1083            throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, message, e);
1084        }
1085        if (Policy.DEBUG_SAVE_MASTERTABLE)
1086            System.out.println("Save master table for " + location + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1087
}
1088
1089    /**
1090     * Writes the metainfo (e.g. descriptions) of the given workspace and
1091     * all projects to the local disk.
1092     */

1093    protected void saveMetaInfo(MultiStatus problems, IProgressMonitor monitor) throws CoreException {
1094        if (Policy.DEBUG_SAVE_METAINFO)
1095            System.out.println("Save workspace metainfo: starting..."); //$NON-NLS-1$
1096
long start = System.currentTimeMillis();
1097        // save preferences (workspace description, path variables, etc)
1098
ResourcesPlugin.getPlugin().savePluginPreferences();
1099        // save projects' meta info
1100
IProject[] roots = workspace.getRoot().getProjects();
1101        for (int i = 0; i < roots.length; i++)
1102            if (roots[i].isAccessible()) {
1103                IStatus result = saveMetaInfo((Project) roots[i], null);
1104                if (!result.isOK())
1105                    problems.merge(result);
1106            }
1107        if (Policy.DEBUG_SAVE_METAINFO)
1108            System.out.println("Save workspace metainfo: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1109
}
1110
1111    /**
1112     * Ensures that the project meta-info is saved. The project meta-info
1113     * is usually saved as soon as it changes, so this is just a sanity check
1114     * to make sure there is something on disk before we shutdown.
1115     *
1116     * @return Status object containing non-critical warnings, or an OK status.
1117     */

1118    protected IStatus saveMetaInfo(Project project, IProgressMonitor monitor) throws CoreException {
1119        long start = System.currentTimeMillis();
1120        //if there is nothing on disk, write the description
1121
if (!workspace.getFileSystemManager().hasSavedDescription(project)) {
1122            workspace.getFileSystemManager().writeSilently(project);
1123            String JavaDoc msg = NLS.bind(Messages.resources_missingProjectMetaRepaired, project.getName());
1124            return new ResourceStatus(IResourceStatus.MISSING_DESCRIPTION_REPAIRED, project.getFullPath(), msg);
1125        }
1126        if (Policy.DEBUG_SAVE_METAINFO)
1127            System.out.println("Save metainfo for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1128
return Status.OK_STATUS;
1129    }
1130
1131    /**
1132     * Writes the current state of the entire workspace tree to disk.
1133     * This is used during workspace save. saveTree(Project)
1134     * is used to save the state of an individual project.
1135     * @exception CoreException if there is a problem writing the tree to disk.
1136     */

1137    protected void saveTree(Map contexts, IProgressMonitor monitor) throws CoreException {
1138        long start = System.currentTimeMillis();
1139        IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), true);
1140        try {
1141            IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
1142            DataOutputStream output = new DataOutputStream(new SafeFileOutputStream(treeLocation.toOSString(), tempLocation.toOSString()));
1143            try {
1144                output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
1145                writeTree(computeStatesToSave(contexts, workspace.getElementTree()), output, monitor);
1146            } finally {
1147                output.close();
1148            }
1149        } catch (Exception JavaDoc e) {
1150            String JavaDoc msg = NLS.bind(Messages.resources_writeWorkspaceMeta, treeLocation);
1151            throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, Path.ROOT, msg, e);
1152        }
1153        if (Policy.DEBUG_SAVE_TREE)
1154            System.out.println("Save Workspace Tree: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1155
}
1156
1157    /**
1158     * Should only be used for read purposes.
1159     */

1160    void setPluginsSavedState(HashMap savedStates) {
1161        this.savedStates = Collections.synchronizedMap(savedStates);
1162    }
1163
1164    protected void setSaveNumber(String JavaDoc pluginId, int number) {
1165        masterTable.setProperty(SAVE_NUMBER_PREFIX + pluginId, new Integer JavaDoc(number).toString());
1166    }
1167
1168    /* (non-Javadoc)
1169     * Method declared on IStringPoolParticipant
1170     */

1171    public void shareStrings(StringPool pool) {
1172        lastSnap.shareStrings(pool);
1173    }
1174
1175    public void shutdown(final IProgressMonitor monitor) {
1176        // do a last snapshot if it was scheduled
1177
// we force it in the same thread because it would not
1178
// help if the job runs after we close the workspace
1179
int state = snapshotJob.getState();
1180        if (state == Job.WAITING || state == Job.SLEEPING)
1181            // we cannot pass null to Job#run
1182
snapshotJob.run(Policy.monitorFor(monitor));
1183        // cancel the snapshot job
1184
snapshotJob.cancel();
1185    }
1186
1187    /**
1188     * Performs a snapshot if one is deemed necessary.
1189     * Encapsulates rules for determining when a snapshot is needed.
1190     * This should be called at the end of every top level operation.
1191     */

1192    public void snapshotIfNeeded(boolean hasTreeChanges) {
1193        // never schedule a snapshot while save is occurring.
1194
if (isSaving)
1195            return;
1196        if (snapshotRequested || operationCount >= workspace.internalGetDescription().getOperationsPerSnapshot()) {
1197            if (snapshotJob.getState() == Job.NONE)
1198                snapshotJob.schedule();
1199            else
1200                snapshotJob.wakeUp();
1201        } else {
1202            if (hasTreeChanges) {
1203                operationCount++;
1204                if (snapshotJob.getState() == Job.NONE) {
1205                    if (Policy.DEBUG_SAVE)
1206                        System.out.println("Scheduling workspace snapshot"); //$NON-NLS-1$
1207
long interval = workspace.internalGetDescription().getSnapshotInterval();
1208                    snapshotJob.schedule(Math.max(interval, MIN_SNAPSHOT_DELAY));
1209                }
1210            } else {
1211                //only increment the operation count if we've had a sufficient number of no-ops
1212
if (++noopCount > NO_OP_THRESHOLD) {
1213                    operationCount++;
1214                    noopCount = 0;
1215                }
1216            }
1217        }
1218    }
1219
1220    /**
1221     * Performs a snapshot of the workspace tree.
1222     */

1223    protected void snapTree(ElementTree tree, IProgressMonitor monitor) throws CoreException {
1224        long start = System.currentTimeMillis();
1225        monitor = Policy.monitorFor(monitor);
1226        String JavaDoc message;
1227        try {
1228            monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
1229
//the tree must be immutable
1230
tree.immutable();
1231            // don't need to snapshot if there are no changes
1232
if (tree == lastSnap)
1233                return;
1234            operationCount = 0;
1235            IPath snapPath = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
1236            ElementTreeWriter writer = new ElementTreeWriter(this);
1237            java.io.File JavaDoc localFile = snapPath.toFile();
1238            try {
1239                SafeChunkyOutputStream safeStream = new SafeChunkyOutputStream(localFile);
1240                DataOutputStream out = new DataOutputStream(safeStream);
1241                try {
1242                    out.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
1243                    writeWorkspaceFields(out, monitor);
1244                    writer.writeDelta(tree, lastSnap, Path.ROOT, ElementTreeWriter.D_INFINITE, out, ResourceComparator.getSaveComparator());
1245                    safeStream.succeed();
1246                } finally {
1247                    out.close();
1248                }
1249            } catch (IOException e) {
1250                message = NLS.bind(Messages.resources_writeWorkspaceMeta, localFile.getAbsolutePath());
1251                throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, Path.ROOT, message, e);
1252            }
1253            lastSnap = tree;
1254        } finally {
1255            monitor.done();
1256        }
1257        if (Policy.DEBUG_SAVE_TREE)
1258            System.out.println("Snapshot Workspace Tree: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1259
}
1260
1261    /**
1262     * Sorts the given array of trees so that the following rules are true:
1263     * - The first tree has no parent
1264     * - No tree has an ancestor with a greater index in the array.
1265     * If there are no missing parents in the given trees array, this means
1266     * that in the resulting array, the i'th tree's parent will be tree i-1.
1267     * The input tree array may contain duplicate trees.
1268     */

1269    protected ElementTree[] sortTrees(ElementTree[] trees) {
1270        /* the sorted list */
1271        int numTrees = trees.length;
1272        ElementTree[] sorted = new ElementTree[numTrees];
1273
1274        /* first build a table of ElementTree -> List of Integers(indices in trees array) */
1275        Map table = new HashMap(numTrees * 2 + 1);
1276        for (int i = 0; i < trees.length; i++) {
1277            List indices = (List) table.get(trees[i]);
1278            if (indices == null) {
1279                indices = new ArrayList(10);
1280                table.put(trees[i], indices);
1281            }
1282            indices.add(new Integer JavaDoc(i));
1283        }
1284
1285        /* find the oldest tree (a descendent of all other trees) */
1286        ElementTree oldest = trees[ElementTree.findOldest(trees)];
1287
1288        /**
1289         * Walk through the chain of trees from oldest to newest,
1290         * adding them to the sorted list as we go.
1291         */

1292        int i = numTrees - 1;
1293        while (i >= 0) {
1294            /* add all instances of the current oldest tree to the sorted list */
1295            List indices = (List) table.remove(oldest);
1296            for (Enumeration e = Collections.enumeration(indices); e.hasMoreElements();) {
1297                e.nextElement();
1298                sorted[i] = oldest;
1299                i--;
1300            }
1301            if (i >= 0) {
1302                /* find the next tree in the list */
1303                ElementTree parent = oldest.getParent();
1304                while (parent != null && table.get(parent) == null) {
1305                    parent = parent.getParent();
1306                }
1307                if (parent == null) {
1308                    IStatus status = new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, "null parent found while collapsing trees", null); //$NON-NLS-1$
1309
Policy.log(status);
1310                    return null;
1311                }
1312                oldest = parent;
1313            }
1314        }
1315        return sorted;
1316    }
1317
1318    public void startup(IProgressMonitor monitor) throws CoreException {
1319        restore(monitor);
1320        java.io.File JavaDoc table = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES).toFile();
1321        if (!table.exists())
1322            table.getParentFile().mkdirs();
1323    }
1324
1325    /**
1326     * Update the expiration time for the given plug-in's tree. If the tree was never
1327     * loaded, use the current value in the master table. If the tree has been loaded,
1328     * use the provided new timestamp.
1329     *
1330     * The timestamp is used in the policy for cleaning up tree's of plug-ins that are
1331     * not often activated.
1332     */

1333    protected void updateDeltaExpiration(String JavaDoc pluginId) {
1334        String JavaDoc key = DELTA_EXPIRATION_PREFIX + pluginId;
1335        if (!masterTable.containsKey(key))
1336            masterTable.setProperty(key, Long.toString(System.currentTimeMillis()));
1337    }
1338
1339    /**
1340     * Visit the given resource (to depth infinite) and write out extra information
1341     * like markers and sync info. To be called during a full save and project save.
1342     *
1343     * FIXME: This method is ugly. Fix it up and look at merging with #visitAndSnap
1344     */

1345    public void visitAndSave(final IResource root) throws CoreException {
1346        // Ensure we have either a project or the workspace root
1347
Assert.isLegal(root.getType() == IResource.ROOT || root.getType() == IResource.PROJECT);
1348        // only write out info for accessible resources
1349
if (!root.isAccessible())
1350            return;
1351
1352        // Setup variables
1353
final Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
1354        final MarkerManager markerManager = workspace.getMarkerManager();
1355        IPath markersLocation = workspace.getMetaArea().getMarkersLocationFor(root);
1356        IPath markersTempLocation = workspace.getMetaArea().getBackupLocationFor(markersLocation);
1357        IPath syncInfoLocation = workspace.getMetaArea().getSyncInfoLocationFor(root);
1358        IPath syncInfoTempLocation = workspace.getMetaArea().getBackupLocationFor(syncInfoLocation);
1359        final List writtenTypes = new ArrayList(5);
1360        final List writtenPartners = new ArrayList(synchronizer.registry.size());
1361        DataOutputStream o1 = null;
1362        DataOutputStream o2 = null;
1363        String JavaDoc message;
1364
1365        // Create the output streams
1366
try {
1367            o1 = new DataOutputStream(new SafeFileOutputStream(markersLocation.toOSString(), markersTempLocation.toOSString()));
1368            // we don't store the sync info for the workspace root so don't create
1369
// an empty file
1370
if (root.getType() != IResource.ROOT)
1371                o2 = new DataOutputStream(new SafeFileOutputStream(syncInfoLocation.toOSString(), syncInfoTempLocation.toOSString()));
1372        } catch (IOException e) {
1373            if (o1 != null)
1374                try {
1375                    o1.close();
1376                } catch (IOException e2) {
1377                    // ignore
1378
}
1379            message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
1380            throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
1381        }
1382
1383        final DataOutputStream markersOutput = o1;
1384        final DataOutputStream syncInfoOutput = o2;
1385        // The following 2 piece array will hold a running total of the times
1386
// taken to save markers and syncInfo respectively. This will cut down
1387
// on the number of statements printed out as we would get 2 statements
1388
// for each resource otherwise.
1389
final long[] saveTimes = new long[2];
1390
1391        // Create the visitor
1392
IElementContentVisitor visitor = new IElementContentVisitor() {
1393            public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object JavaDoc elementContents) {
1394                ResourceInfo info = (ResourceInfo) elementContents;
1395                if (info != null) {
1396                    try {
1397                        // save the markers
1398
long start = System.currentTimeMillis();
1399                        markerManager.save(info, requestor, markersOutput, writtenTypes);
1400                        long markerSaveTime = System.currentTimeMillis() - start;
1401                        saveTimes[0] += markerSaveTime;
1402                        persistMarkers += markerSaveTime;
1403                        // save the sync info - if we have the workspace root then the output stream will be null
1404
if (syncInfoOutput != null) {
1405                            start = System.currentTimeMillis();
1406                            synchronizer.saveSyncInfo(info, requestor, syncInfoOutput, writtenPartners);
1407                            long syncInfoSaveTime = System.currentTimeMillis() - start;
1408                            saveTimes[1] += syncInfoSaveTime;
1409                            persistSyncInfo += syncInfoSaveTime;
1410                        }
1411                    } catch (IOException e) {
1412                        throw new WrappedRuntimeException(e);
1413                    }
1414                }
1415                // don't continue if the current resource is the workspace root, only continue for projects
1416
return root.getType() != IResource.ROOT;
1417            }
1418        };
1419
1420        // Call the visitor
1421
try {
1422            try {
1423                new ElementTreeIterator(workspace.getElementTree(), root.getFullPath()).iterate(visitor);
1424            } catch (WrappedRuntimeException e) {
1425                throw (IOException) e.getTargetException();
1426            }
1427            if (Policy.DEBUG_SAVE_MARKERS)
1428                System.out.println("Save Markers for " + root.getFullPath() + ": " + saveTimes[0] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1429
if (Policy.DEBUG_SAVE_SYNCINFO)
1430                System.out.println("Save SyncInfo for " + root.getFullPath() + ": " + saveTimes[1] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1431
removeGarbage(markersOutput, markersLocation, markersTempLocation);
1432            // if we have the workspace root the output stream will be null and we
1433
// don't have to perform cleanup code
1434
if (syncInfoOutput != null)
1435                removeGarbage(syncInfoOutput, syncInfoLocation, syncInfoTempLocation);
1436        } catch (IOException e) {
1437            message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
1438            throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
1439        } finally {
1440            FileUtil.safeClose(markersOutput);
1441            FileUtil.safeClose(syncInfoOutput);
1442        }
1443
1444        // recurse over the projects in the workspace if we were given the workspace root
1445
if (root.getType() == IResource.PROJECT)
1446            return;
1447        IProject[] projects = ((IWorkspaceRoot) root).getProjects();
1448        for (int i = 0; i < projects.length; i++)
1449            visitAndSave(projects[i]);
1450    }
1451
1452    /**
1453     * Visit the given resource (to depth infinite) and write out extra information
1454     * like markers and sync info. To be called during a snapshot
1455     *
1456     * FIXME: This method is ugly. Fix it up and look at merging with #visitAndSnap
1457     */

1458    public void visitAndSnap(final IResource root) throws CoreException {
1459        // Ensure we have either a project or the workspace root
1460
Assert.isLegal(root.getType() == IResource.ROOT || root.getType() == IResource.PROJECT);
1461        // only write out info for accessible resources
1462
if (!root.isAccessible())
1463            return;
1464
1465        // Setup variables
1466
final Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
1467        final MarkerManager markerManager = workspace.getMarkerManager();
1468        IPath markersLocation = workspace.getMetaArea().getMarkersSnapshotLocationFor(root);
1469        IPath syncInfoLocation = workspace.getMetaArea().getSyncInfoSnapshotLocationFor(root);
1470        SafeChunkyOutputStream safeMarkerStream = null;
1471        SafeChunkyOutputStream safeSyncInfoStream = null;
1472        DataOutputStream o1 = null;
1473        DataOutputStream o2 = null;
1474        String JavaDoc message;
1475
1476        // Create the output streams
1477
try {
1478            safeMarkerStream = new SafeChunkyOutputStream(markersLocation.toFile());
1479            o1 = new DataOutputStream(safeMarkerStream);
1480            // we don't store the sync info for the workspace root so don't create
1481
// an empty file
1482
if (root.getType() != IResource.ROOT) {
1483                safeSyncInfoStream = new SafeChunkyOutputStream(syncInfoLocation.toFile());
1484                o2 = new DataOutputStream(safeSyncInfoStream);
1485            }
1486        } catch (IOException e) {
1487            FileUtil.safeClose(o1);
1488            message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
1489            throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
1490        }
1491
1492        final DataOutputStream markersOutput = o1;
1493        final DataOutputStream syncInfoOutput = o2;
1494        int markerFileSize = markersOutput.size();
1495        int syncInfoFileSize = safeSyncInfoStream == null ? -1 : syncInfoOutput.size();
1496        // The following 2 piece array will hold a running total of the times
1497
// taken to save markers and syncInfo respectively. This will cut down
1498
// on the number of statements printed out as we would get 2 statements
1499
// for each resource otherwise.
1500
final long[] snapTimes = new long[2];
1501
1502        IElementContentVisitor visitor = new IElementContentVisitor() {
1503            public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object JavaDoc elementContents) {
1504                ResourceInfo info = (ResourceInfo) elementContents;
1505                if (info != null) {
1506                    try {
1507                        // save the markers
1508
long start = System.currentTimeMillis();
1509                        markerManager.snap(info, requestor, markersOutput);
1510                        long markerSnapTime = System.currentTimeMillis() - start;
1511                        snapTimes[0] += markerSnapTime;
1512                        persistMarkers += markerSnapTime;
1513                        // save the sync info - if we have the workspace root then the output stream will be null
1514
if (syncInfoOutput != null) {
1515                            start = System.currentTimeMillis();
1516                            synchronizer.snapSyncInfo(info, requestor, syncInfoOutput);
1517                            long syncInfoSnapTime = System.currentTimeMillis() - start;
1518                            snapTimes[1] += syncInfoSnapTime;
1519                            persistSyncInfo += syncInfoSnapTime;
1520                        }
1521                    } catch (IOException e) {
1522                        throw new WrappedRuntimeException(e);
1523                    }
1524                }
1525                // don't continue if the current resource is the workspace root, only continue for projects
1526
return root.getType() != IResource.ROOT;
1527            }
1528        };
1529
1530        try {
1531            // Call the visitor
1532
try {
1533                new ElementTreeIterator(workspace.getElementTree(), root.getFullPath()).iterate(visitor);
1534            } catch (WrappedRuntimeException e) {
1535                throw (IOException) e.getTargetException();
1536            }
1537            if (Policy.DEBUG_SAVE_MARKERS)
1538                System.out.println("Snap Markers for " + root.getFullPath() + ": " + snapTimes[0] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1539
if (Policy.DEBUG_SAVE_SYNCINFO)
1540                System.out.println("Snap SyncInfo for " + root.getFullPath() + ": " + snapTimes[1] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1541
if (markerFileSize != markersOutput.size())
1542                safeMarkerStream.succeed();
1543            if (safeSyncInfoStream != null && syncInfoFileSize != syncInfoOutput.size())
1544                safeSyncInfoStream.succeed();
1545        } catch (IOException e) {
1546            message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
1547            throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
1548        } finally {
1549            FileUtil.safeClose(markersOutput);
1550            FileUtil.safeClose(syncInfoOutput);
1551        }
1552
1553        // recurse over the projects in the workspace if we were given the workspace root
1554
if (root.getType() == IResource.PROJECT)
1555            return;
1556        IProject[] projects = ((IWorkspaceRoot) root).getProjects();
1557        for (int i = 0; i < projects.length; i++)
1558            visitAndSnap(projects[i]);
1559    }
1560
1561    /**
1562     * Writes out persistent information about all builders for which a last built
1563     * tree is available. File format is:
1564     * int - number of builders
1565     * for each builder:
1566     * UTF - project name
1567     * UTF - fully qualified builder extension name
1568     * int - number of interesting projects for builder
1569     * For each interesting project:
1570     * UTF - interesting project name
1571     */

1572    protected void writeBuilderPersistentInfo(DataOutputStream output, List builders, List trees, IProgressMonitor monitor) throws IOException {
1573        monitor = Policy.monitorFor(monitor);
1574        try {
1575            // write the number of builders we are saving
1576
int numBuilders = builders.size();
1577            output.writeInt(numBuilders);
1578            for (int i = 0; i < numBuilders; i++) {
1579                BuilderPersistentInfo info = (BuilderPersistentInfo) builders.get(i);
1580                output.writeUTF(info.getProjectName());
1581                output.writeUTF(info.getBuilderName());
1582                // write interesting projects
1583
IProject[] interestingProjects = info.getInterestingProjects();
1584                output.writeInt(interestingProjects.length);
1585                for (int j = 0; j < interestingProjects.length; j++)
1586                    output.writeUTF(interestingProjects[j].getName());
1587                ElementTree last = info.getLastBuiltTree();
1588                //it is not unusual for a builder to have no last built tree (for example after a clean)
1589
if (last == null)
1590                    last = workspace.getElementTree();
1591                trees.add(last);
1592            }
1593        } finally {
1594            monitor.done();
1595        }
1596    }
1597
1598    /**
1599     * @see IElementInfoFlattener#writeElement(IPath, Object, DataOutput)
1600     */

1601    public void writeElement(IPath path, Object JavaDoc element, DataOutput output) throws IOException {
1602        Assert.isNotNull(path);
1603        Assert.isNotNull(element);
1604        Assert.isNotNull(output);
1605        ResourceInfo info = (ResourceInfo) element;
1606        output.writeInt(info.getFlags());
1607        info.writeTo(output);
1608    }
1609
1610    protected void writeTree(Map statesToSave, DataOutputStream output, IProgressMonitor monitor) throws IOException, CoreException {
1611        monitor = Policy.monitorFor(monitor);
1612        try {
1613            monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
1614
boolean wasImmutable = false;
1615            try {
1616                // Create an array of trees to save. Ensure that the current one is in the list
1617
ElementTree current = workspace.getElementTree();
1618                wasImmutable = current.isImmutable();
1619                current.immutable();
1620                ArrayList trees = new ArrayList(statesToSave.size() * 2); // pick a number
1621
monitor.worked(Policy.totalWork * 10 / 100);
1622
1623                // write out the workspace fields
1624
writeWorkspaceFields(output, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
1625
1626                // save plugin info
1627
output.writeInt(statesToSave.size()); // write the number of plugins we are saving
1628
for (Iterator i = statesToSave.entrySet().iterator(); i.hasNext();) {
1629                    Map.Entry entry = (Map.Entry) i.next();
1630                    String JavaDoc pluginId = (String JavaDoc) entry.getKey();
1631                    output.writeUTF(pluginId);
1632                    trees.add(entry.getValue()); // tree
1633
updateDeltaExpiration(pluginId);
1634                }
1635                monitor.worked(Policy.totalWork * 10 / 100);
1636
1637                // add builders' trees
1638
IProject[] projects = workspace.getRoot().getProjects();
1639                List builders = new ArrayList(projects.length * 2);
1640                for (int i = 0; i < projects.length; i++) {
1641                    IProject project = projects[i];
1642                    if (project.isOpen()) {
1643                        ArrayList infos = workspace.getBuildManager().createBuildersPersistentInfo(project);
1644                        if (infos != null)
1645                            builders.addAll(infos);
1646                    }
1647                }
1648                writeBuilderPersistentInfo(output, builders, trees, Policy.subMonitorFor(monitor, Policy.totalWork * 10 / 100));
1649
1650                // add the current tree in the list as the last element
1651
trees.add(current);
1652
1653                /* save the forest! */
1654                ElementTreeWriter writer = new ElementTreeWriter(this);
1655                ElementTree[] treesToSave = (ElementTree[]) trees.toArray(new ElementTree[trees.size()]);
1656                writer.writeDeltaChain(treesToSave, Path.ROOT, ElementTreeWriter.D_INFINITE, output, ResourceComparator.getSaveComparator());
1657                monitor.worked(Policy.totalWork * 50 / 100);
1658            } finally {
1659                if (!wasImmutable)
1660                    workspace.newWorkingTree();
1661            }
1662        } finally {
1663            monitor.done();
1664        }
1665    }
1666
1667    /**
1668     * Attempts to save all the trees for this project (the current tree
1669     * plus a tree for each builder with a previously built state). Throws
1670     * an IOException if anything went wrong during save. Attempts to close
1671     * the provided stream at all costs.
1672     */

1673    protected void writeTree(Project project, DataOutputStream output, IProgressMonitor monitor) throws IOException, CoreException {
1674        monitor = Policy.monitorFor(monitor);
1675        try {
1676            monitor.beginTask("", 10); //$NON-NLS-1$
1677
boolean wasImmutable = false;
1678            try {
1679                /**
1680                 * Obtain a list of BuilderPersistentInfo.
1681                 * This list includes builders that have never been instantiated
1682                 * but already had a last built state.
1683                 */

1684                ArrayList builderInfos = workspace.getBuildManager().createBuildersPersistentInfo(project);
1685                if (builderInfos == null)
1686                    builderInfos = new ArrayList(5);
1687                List trees = new ArrayList(builderInfos.size() + 1);
1688                monitor.worked(1);
1689
1690                /* Make sure the most recent tree is in the array */
1691                ElementTree current = workspace.getElementTree();
1692                wasImmutable = current.isImmutable();
1693                current.immutable();
1694
1695                /* add the tree for each builder to the array */
1696                writeBuilderPersistentInfo(output, builderInfos, trees, Policy.subMonitorFor(monitor, 1));
1697                trees.add(current);
1698
1699                /* save the forest! */
1700                ElementTreeWriter writer = new ElementTreeWriter(this);
1701                ElementTree[] treesToSave = (ElementTree[]) trees.toArray(new ElementTree[trees.size()]);
1702                writer.writeDeltaChain(treesToSave, project.getFullPath(), ElementTreeWriter.D_INFINITE, output, ResourceComparator.getSaveComparator());
1703                monitor.worked(8);
1704            } finally {
1705                if (output != null)
1706                    output.close();
1707                if (!wasImmutable)
1708                    workspace.newWorkingTree();
1709            }
1710        } finally {
1711            monitor.done();
1712        }
1713    }
1714
1715    protected void writeTree(Project project, int depth) throws CoreException {
1716        long start = System.currentTimeMillis();
1717        IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(project, true);
1718        IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
1719        try {
1720            SafeFileOutputStream safe = new SafeFileOutputStream(treeLocation.toOSString(), tempLocation.toOSString());
1721            try {
1722                DataOutputStream output = new DataOutputStream(safe);
1723                output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
1724                writeTree(project, output, null);
1725            } finally {
1726                safe.close();
1727            }
1728        } catch (IOException e) {
1729            String JavaDoc msg = NLS.bind(Messages.resources_writeMeta, project.getFullPath());
1730            throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, treeLocation, msg, e);
1731        }
1732        if (Policy.DEBUG_SAVE_TREE)
1733            System.out.println("Save tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1734
}
1735
1736    protected void writeWorkspaceFields(DataOutputStream output, IProgressMonitor monitor) throws IOException {
1737        monitor = Policy.monitorFor(monitor);
1738        try {
1739            // save the next node id
1740
output.writeLong(workspace.nextNodeId);
1741            // save the modification stamp (no longer used)
1742
output.writeLong(0L);
1743            // save the marker id counter
1744
output.writeLong(workspace.nextMarkerId);
1745            // save the registered sync partners in the synchronizer
1746
((Synchronizer) workspace.getSynchronizer()).savePartners(output);
1747        } finally {
1748            monitor.done();
1749        }
1750    }
1751}
1752
Popular Tags