KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > core > resources > SessionPropertySyncInfoCache


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  *******************************************************************************/

11 package org.eclipse.team.internal.ccvs.core.resources;
12
13 import java.util.Arrays JavaDoc;
14 import java.util.HashSet JavaDoc;
15 import java.util.Set JavaDoc;
16
17 import org.eclipse.core.resources.*;
18 import org.eclipse.core.runtime.*;
19 import org.eclipse.osgi.util.NLS;
20 import org.eclipse.team.core.RepositoryProvider;
21 import org.eclipse.team.internal.ccvs.core.*;
22 import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
23 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
24 import org.eclipse.team.internal.ccvs.core.util.FileNameMatcher;
25 import org.eclipse.team.internal.ccvs.core.util.SyncFileWriter;
26
27 /**
28  * This cache uses session properties to hold the bytes representing the sync
29  * info. In addition when the workbench closes or a project is closed, the dirty
30  * state for all cvs managed folders are persisted using the resource's plugin
31  * synchronizer.
32  */

33 /*package*/ class SessionPropertySyncInfoCache extends SyncInfoCache implements ISaveParticipant {
34     
35     // key used on a folder to indicate that the resource sync has been cahced for it's children
36
private static final QualifiedName RESOURCE_SYNC_CACHED_KEY = new QualifiedName(CVSProviderPlugin.ID, "resource-sync-cached"); //$NON-NLS-1$
37
private static final Object JavaDoc RESOURCE_SYNC_CACHED = new Object JavaDoc();
38     
39     /*package*/ static final FileNameMatcher NULL_IGNORES = new FileNameMatcher();
40     private static final FolderSyncInfo NULL_FOLDER_SYNC_INFO = new FolderSyncInfo("dummy-repo", "dummy-root", null, false); //$NON-NLS-1$ //$NON-NLS-2$
41

42     private QualifiedName FOLDER_DIRTY_STATE_KEY = new QualifiedName(CVSProviderPlugin.ID, "folder-dirty-state-cached"); //$NON-NLS-1$
43

44     // defer to the sychronizer if there is no sync info
45
// (i.e. for those cases where a deleted resource is recreated)
46
private SynchronizerSyncInfoCache synchronizerCache;
47     
48     /*package*/ SessionPropertySyncInfoCache(SynchronizerSyncInfoCache synchronizerCache) {
49         this.synchronizerCache = synchronizerCache;
50         try {
51             // this save participant is removed when the plugin is shutdown.
52
ResourcesPlugin.getWorkspace().addSaveParticipant(CVSProviderPlugin.getPlugin(), this);
53             ISynchronizer synchronizer = ResourcesPlugin.getWorkspace().getSynchronizer();
54             synchronizer.add(FOLDER_DIRTY_STATE_KEY);
55         } catch (CoreException e) {
56             CVSProviderPlugin.log(e);
57         }
58     }
59
60     /**
61      * If not already cached, loads and caches the folder ignores sync for the container.
62      * Folder must exist and must not be the workspace root.
63      *
64      * @param container the container
65      * @return the folder ignore patterns, or an empty array if none
66      */

67     /*package*/ FileNameMatcher getFolderIgnores(IContainer container, boolean threadSafeAccess) throws CVSException {
68         // don't try to load if the information is already cached
69
FileNameMatcher matcher = (FileNameMatcher)safeGetSessionProperty(container, IGNORE_SYNC_KEY);
70         if (threadSafeAccess && matcher == null) {
71             // read folder ignores and remember it
72
String JavaDoc[] ignores = SyncFileWriter.readCVSIgnoreEntries(container);
73             if (ignores == null) {
74                 matcher = NULL_IGNORES;
75             } else {
76                 matcher = new FileNameMatcher(ignores);
77             }
78             safeSetSessionProperty(container, IGNORE_SYNC_KEY, matcher);
79         }
80         return matcher;
81     }
82     
83     /* package */ boolean isIgnoresCached(IContainer container) throws CVSException {
84         return safeGetSessionProperty(container, IGNORE_SYNC_KEY) != null;
85     }
86
87     /*package*/ boolean isFolderSyncInfoCached(IContainer container) throws CVSException {
88         Object JavaDoc info = safeGetSessionProperty(container, FOLDER_SYNC_KEY);
89         if (info == null){
90             // Defer to the synchronizer in case the folder was recreated
91
info = synchronizerCache.getCachedFolderSync(container, true);
92         }
93         return info != null;
94     }
95
96     /*package*/ boolean isResourceSyncInfoCached(IContainer container) throws CVSException {
97         return safeGetSessionProperty(container, RESOURCE_SYNC_CACHED_KEY) != null;
98     }
99     
100     /*package*/ void setResourceSyncInfoCached(IContainer container) throws CVSException {
101         safeSetSessionProperty(container, RESOURCE_SYNC_CACHED_KEY, RESOURCE_SYNC_CACHED);
102     }
103
104     /**
105      * Returns the folder sync info for the container; null if none.
106      * Folder must exist and must not be the workspace root.
107      * The folder sync info for the container MUST ALREADY BE CACHED.
108      * @param container the container
109      * @param threadSafeAccess if false, the return value can only be used if not null
110      * @return the folder sync info for the folder, or null if none.
111      * @see #cacheFolderSync
112      */

113     FolderSyncInfo getCachedFolderSync(IContainer container, boolean threadSafeAccess) throws CVSException {
114         FolderSyncInfo info = (FolderSyncInfo)safeGetSessionProperty(container, FOLDER_SYNC_KEY);
115         // If we are not thread safe, just return whatever was found in the session property
116
if (!threadSafeAccess)
117             return info == NULL_FOLDER_SYNC_INFO ? null : info;
118         if (info == null) {
119             // Defer to the synchronizer in case the folder was recreated
120
info = synchronizerCache.getCachedFolderSync(container, true);
121             if (info != null) {
122                 safeSetSessionProperty(container, FOLDER_SYNC_KEY, info);
123             }
124         }
125         if (info == null) {
126             // There should be sync info but it was missing. Report the error.
127
// Only report the error is the folder is not derived (see bug 97023)
128
if (container.exists() && !container.isDerived()){
129                 IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.EclipseSynchronizer_folderSyncInfoMissing, new String JavaDoc[] { container.getFullPath().toString() }), container);
130                 throw new CVSException(status);
131             }
132         }
133         if (info == NULL_FOLDER_SYNC_INFO) return null;
134         return info;
135     }
136
137     /**
138      * Purges the cache recursively for all resources beneath the container.
139      * There must not be any pending uncommitted changes.
140      * @return the resources whose sync info was flushed
141      */

142     /*package*/ IResource[] purgeCache(IContainer container, boolean deep) throws CVSException {
143         if (! container.exists()) return new IResource[0];
144         try {
145             Set JavaDoc flushed = new HashSet JavaDoc();
146             if (container.getType() != IResource.ROOT) {
147                 safeSetSessionProperty(container, IGNORE_SYNC_KEY, null);
148                 safeSetSessionProperty(container, FOLDER_SYNC_KEY, null);
149                 safeSetSessionProperty(container, RESOURCE_SYNC_CACHED_KEY, null);
150                 flushed.add(container);
151                 EclipseSynchronizer.getInstance().adjustDirtyStateRecursively(container, RECOMPUTE_INDICATOR);
152             }
153             IResource[] members = container.members();
154             for (int i = 0; i < members.length; i++) {
155                 IResource resource = members[i];
156                 purgeResourceSyncCache(resource);
157                 flushed.add(resource);
158                 if (deep && resource.getType() != IResource.FILE) {
159                     IResource[] flushedChildren = purgeCache((IContainer) resource, deep);
160                     flushed.addAll(Arrays.asList(flushedChildren));
161                 }
162             }
163             return (IResource[]) flushed.toArray(new IResource[flushed.size()]);
164         } catch (CoreException e) {
165             throw CVSException.wrapException(e);
166         }
167     }
168     
169     /* package*/ void purgeResourceSyncCache(IResource resource) throws CVSException {
170         safeSetSessionProperty(resource, RESOURCE_SYNC_KEY, null);
171         EclipseSynchronizer.getInstance().adjustDirtyStateRecursively(resource, RECOMPUTE_INDICATOR);
172     }
173     
174     /**
175      * Sets the array of folder ignore patterns for the container, must not be null.
176      * Folder must exist and must not be the workspace root.
177      *
178      * @param container the container
179      * @param ignores the array of ignore patterns
180      */

181     /*package*/ void setCachedFolderIgnores(IContainer container, String JavaDoc[] ignores) throws CVSException {
182         safeSetSessionProperty(container, IGNORE_SYNC_KEY, new FileNameMatcher(ignores));
183     }
184
185
186     /**
187      * Sets the folder sync info for the container; if null, deletes it.
188      * Folder must exist and must not be the workspace root.
189      * The folder sync info for the container need not have previously been cached.
190      *
191      * @param container the container
192      * @param info the new folder sync info
193      */

194     void setCachedFolderSync(IContainer container, FolderSyncInfo info, boolean canModifyWorkspace) throws CVSException {
195         if (!container.exists()) return;
196         if (info == null) {
197             info = NULL_FOLDER_SYNC_INFO;
198         }
199         safeSetSessionProperty(container, FOLDER_SYNC_KEY, info);
200         // Ensure the synchronizer is clear for exiting resources
201
if (canModifyWorkspace && synchronizerCache.getCachedFolderSync(container, true) != null) {
202             synchronizerCache.setCachedFolderSync(container, null, true);
203         }
204     }
205
206     /*package*/ void setDirtyIndicator(IResource resource, String JavaDoc indicator) throws CVSException {
207         if (resource.getType() == IResource.FILE) {
208             internalSetDirtyIndicator((IFile)resource, indicator);
209         } else {
210             internalSetDirtyIndicator((IContainer)resource, indicator);
211         }
212     }
213     /*package*/ String JavaDoc getDirtyIndicator(IResource resource, boolean threadSafeAccess) throws CVSException {
214         if (resource.getType() == IResource.FILE) {
215             return internalGetDirtyIndicator((IFile)resource, threadSafeAccess);
216         } else {
217             return internalGetDirtyIndicator((IContainer)resource, threadSafeAccess);
218         }
219     }
220     
221     private void internalSetDirtyIndicator(IFile file, String JavaDoc indicator) throws CVSException {
222         safeSetSessionProperty(file, IS_DIRTY, indicator);
223     }
224     
225     private String JavaDoc internalGetDirtyIndicator(IFile file, boolean threadSafeAccess) throws CVSException {
226         String JavaDoc di = (String JavaDoc)safeGetSessionProperty(file, IS_DIRTY);
227         if(di == null) {
228             di = RECOMPUTE_INDICATOR;
229         }
230         return di;
231     }
232
233     private void internalSetDirtyIndicator(IContainer container, String JavaDoc indicator) throws CVSException {
234         safeSetSessionProperty(container, IS_DIRTY, indicator);
235     }
236     
237     private String JavaDoc internalGetDirtyIndicator(IContainer container, boolean threadSafeAccess) throws CVSException {
238         try {
239             String JavaDoc di = (String JavaDoc)safeGetSessionProperty(container, IS_DIRTY);
240             
241             // if the session property is not available then restore from persisted sync info. At this
242
// time the sync info is not flushed because we don't want the workspace to generate
243
// a delta.
244
if(di == null) {
245                 byte [] diBytes = ResourcesPlugin.getWorkspace().getSynchronizer().getSyncInfo(FOLDER_DIRTY_STATE_KEY, container);
246                 if(diBytes != null && !CVSProviderPlugin.getPlugin().crashOnLastRun()) {
247                     di = new String JavaDoc(diBytes);
248                     if(di.equals(NOT_DIRTY_INDICATOR)) {
249                         di = NOT_DIRTY_INDICATOR;
250                     } else if(di.equals(IS_DIRTY_INDICATOR)) {
251                         di = IS_DIRTY_INDICATOR;
252                     } else {
253                         di = RECOMPUTE_INDICATOR;
254                     }
255                 } else {
256                     di = RECOMPUTE_INDICATOR;
257                 }
258                 // Only set the session property if we are thread safe
259
if (threadSafeAccess) {
260                     setDirtyIndicator(container, di);
261                 }
262             }
263             return di;
264         } catch (CoreException e) {
265             throw CVSException.wrapException(e);
266         }
267     }
268         
269     /*
270      * Flush dirty cache for the resource
271      */

272     /*package*/ void flushDirtyCache(IResource resource) throws CVSException {
273         if (resource.exists()) {
274             if (resource.getType() == IResource.FILE) {
275                 safeSetSessionProperty(resource, IS_DIRTY, null);
276             } else {
277                 safeSetSessionProperty(resource, IS_DIRTY, null);
278                 flushDirtyStateFromDisk((IContainer)resource);
279             }
280         }
281     }
282     
283     /**
284      * Method isSyncInfoLoaded returns true if all the sync info for the
285      * provided resources is loaded into the internal cache.
286      *
287      * @param resources
288      * @param i
289      * @return boolean
290      */

291     /*package*/ boolean isSyncInfoLoaded(IContainer parent) throws CVSException {
292         if (parent.getFolder(new Path(SyncFileWriter.CVS_DIRNAME)).exists()) {
293             if (safeGetSessionProperty(parent, RESOURCE_SYNC_CACHED_KEY) == null)
294                 return false;
295             if (safeGetSessionProperty(parent, FOLDER_SYNC_KEY) == null)
296                 return false;
297 // if (parent.getSessionProperty(IGNORE_SYNC_KEY) == null)
298
// return false;
299
}
300         return true;
301     }
302     
303     /**
304      * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#getCachedSyncBytes(org.eclipse.core.resources.IResource, boolean)
305      */

306     byte[] getCachedSyncBytes(IResource resource, boolean threadSafeAccess) throws CVSException {
307         byte[] bytes = (byte[])safeGetSessionProperty(resource, RESOURCE_SYNC_KEY);
308         // If we are not thread safe, just return whatever was found in the session property
309
if (!threadSafeAccess)
310             return bytes;
311         if (bytes == null) {
312             // Defer to the synchronizer in case the file was recreated
313
bytes = synchronizerCache.getCachedSyncBytes(resource, true);
314             if (bytes != null) {
315                 boolean genderChange = false;
316                 if (resource.getType() == IResource.FILE) {
317                     if (ResourceSyncInfo.isFolder(bytes)) {
318                         genderChange = true;
319                     }
320                 } else if (!ResourceSyncInfo.isFolder(bytes)) {
321                     genderChange = true;
322                 }
323                 if (genderChange) {
324                     // Return null if it is a gender change
325
bytes = null;
326                 } else {
327                     safeSetSessionProperty(resource, RESOURCE_SYNC_KEY, ResourceSyncInfo.convertFromDeletion(bytes));
328                 }
329             }
330         }
331         return bytes;
332     }
333
334     Object JavaDoc safeGetSessionProperty(IResource resource, QualifiedName key) throws CVSException {
335         try {
336             return resource.getSessionProperty(key);
337         } catch (CoreException e) {
338             IStatus status = e.getStatus();
339             if(status != null) {
340                 int code = e.getStatus().getCode();
341                 if(code != IResourceStatus.RESOURCE_NOT_LOCAL ||
342                     code != IResourceStatus.RESOURCE_NOT_FOUND) {
343                         // ignore error since a phantom would of been created
344
// and we can safely ignore these cases
345
return null;
346                 }
347             }
348             // some other error we did not expect
349
throw CVSException.wrapException(e);
350         }
351     }
352     
353     void safeSetSessionProperty(IResource resource, QualifiedName key, Object JavaDoc value) throws CVSException {
354             try {
355                 resource.setSessionProperty(key, value);
356             } catch (CoreException e) {
357                 throw CVSException.wrapException(e);
358             }
359         }
360     
361     /**
362      * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#setCachedSyncBytes(org.eclipse.core.resources.IResource, byte[])
363      */

364     void setCachedSyncBytes(IResource resource, byte[] syncBytes, boolean canModifyWorkspace) throws CVSException {
365         // Ensure that the sync bytes do not indicate a deletion
366
if (syncBytes != null && ResourceSyncInfo.isDeletion(syncBytes)) {
367             syncBytes = ResourceSyncInfo.convertFromDeletion(syncBytes);
368         }
369         // Put the sync bytes into the cache
370
safeSetSessionProperty(resource, RESOURCE_SYNC_KEY, syncBytes);
371         // Ensure the synchronizer is clear
372
if (canModifyWorkspace && synchronizerCache.getCachedSyncBytes(resource, true) != null) {
373             synchronizerCache.setCachedSyncBytes(resource, null, canModifyWorkspace);
374         }
375     }
376     
377     /* (non-Javadoc)
378      * @see org.eclipse.core.resources.ISaveParticipant#doneSaving(org.eclipse.core.resources.ISaveContext)
379      */

380     public void doneSaving(ISaveContext context) {
381     }
382
383     /* (non-Javadoc)
384      * @see org.eclipse.core.resources.ISaveParticipant#prepareToSave(org.eclipse.core.resources.ISaveContext)
385      */

386     public void prepareToSave(ISaveContext context) throws CoreException {
387     }
388
389     /* (non-Javadoc)
390      * @see org.eclipse.core.resources.ISaveParticipant#rollback(org.eclipse.core.resources.ISaveContext)
391      */

392     public void rollback(ISaveContext context) {
393     }
394
395     /* Called when the workbench is shutdown or projects are closed. The dirty state
396      * of folders is persisted, using sync info, so that at startup or project open
397      * the folder state can be quickly calculated. This is mainly for improving decorator
398      * performance.
399      * @see org.eclipse.core.resources.ISaveParticipant#saving(org.eclipse.core.resources.ISaveContext)
400      */

401     public void saving(ISaveContext context) throws CoreException {
402         boolean fullSave = (context.getKind() == ISaveContext.FULL_SAVE);
403         boolean projectSave = (context.getKind() == ISaveContext.PROJECT_SAVE);
404         
405         if((projectSave || fullSave)) {
406             // persist all session properties for folders into sync info.
407
final ISynchronizer synchronizer = ResourcesPlugin.getWorkspace().getSynchronizer();
408         
409             // traverse the workspace looking for CVS managed projects or just the
410
// specific projects being closed
411
IProject[] projects;
412             if(projectSave) {
413                 projects = new IProject[1];
414                 projects[0] = context.getProject();
415             } else {
416                 projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
417             }
418             for (int i = 0; i < projects.length; i++) {
419                 IProject project = projects[i];
420                 RepositoryProvider provider = RepositoryProvider.getProvider(
421                                                         project,
422                                                         CVSProviderPlugin.getTypeId());
423                                                         
424                 // found a project managed by CVS, convert each session property on a
425
// folder to a sync object.
426
if (provider != null) {
427                     project.accept(new IResourceVisitor() {
428                         public boolean visit(IResource resource) throws CoreException {
429                             if(resource.getType() != IResource.FILE) {
430                                 String JavaDoc di = null;
431                                 try {
432                                     di = getDirtyIndicator(resource, true);
433                                 } catch (CVSException e) {
434                                     // continue traversal
435
CVSProviderPlugin.log(e);
436                                 }
437                                 if(di != null) {
438                                     synchronizer.setSyncInfo(FOLDER_DIRTY_STATE_KEY, resource, di.getBytes());
439                                 }
440                             }
441                             return true;
442                         }
443                     });
444                 }
445             }
446         }
447     }
448         
449     /*
450      * Called to clear the folder dirty state from the resource sync tree and stop persisting
451      * these values to disk.
452      */

453     private void flushDirtyStateFromDisk(IContainer container) {
454         final ISynchronizer synchronizer = ResourcesPlugin.getWorkspace().getSynchronizer();
455         try {
456             synchronizer.flushSyncInfo(FOLDER_DIRTY_STATE_KEY, container, IResource.DEPTH_INFINITE);
457         } catch (CoreException e) {
458             CVSProviderPlugin.log(e);
459         }
460     }
461
462     /*
463      * Flush all the cahced dirty state for the resource and its members.
464      */

465     /* package*/ void purgeDirtyCache(IResource resource) throws CVSException {
466         if (! resource.exists()) return;
467         try {
468             if (resource.getType() != IResource.ROOT) {
469                 safeSetSessionProperty(resource, IS_DIRTY, null);
470             }
471             if (resource.getType() != IResource.FILE) {
472                 ResourcesPlugin.getWorkspace().getSynchronizer().flushSyncInfo(FOLDER_DIRTY_STATE_KEY, resource, IResource.DEPTH_INFINITE);
473                 IResource[] members = ((IContainer)resource).members();
474                 for (int i = 0; i < members.length; i++) {
475                     purgeDirtyCache(members[i]);
476                 }
477             }
478         } catch (CoreException e) {
479             throw CVSException.wrapException(e);
480         }
481     }
482
483     /* (non-Javadoc)
484      * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#cachesDirtyState()
485      */

486     public boolean cachesDirtyState() {
487         return true;
488     }
489 }
490
Popular Tags