KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > core > util > SyncFileChangeListener


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.team.internal.ccvs.core.util;
12
13 import java.util.HashSet JavaDoc;
14 import java.util.Set JavaDoc;
15
16 import org.eclipse.core.resources.IContainer;
17 import org.eclipse.core.resources.IFile;
18 import org.eclipse.core.resources.IResource;
19 import org.eclipse.core.resources.IResourceChangeEvent;
20 import org.eclipse.core.resources.IResourceChangeListener;
21 import org.eclipse.core.resources.IResourceDelta;
22 import org.eclipse.core.resources.IResourceDeltaVisitor;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.Path;
25 import org.eclipse.team.internal.ccvs.core.*;
26 import org.eclipse.team.internal.ccvs.core.CVSException;
27 import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
28 import org.eclipse.team.internal.ccvs.core.ICVSFile;
29 import org.eclipse.team.internal.ccvs.core.Policy;
30 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
31 import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer;
32 import org.eclipse.team.internal.ccvs.core.syncinfo.DeferredResourceChangeHandler;
33
34 /*
35  * Listens to CVS meta-file changes and notifies the EclipseSynchronizer of changes made to sync files
36  * by 3rd parties.
37  *
38  * If CVS meta-directories are created outside of the CVS plugin their team-private state will be set
39  * by this listener however this change won't be known to other plugins because it does not generate
40  * a delta. As a result views, such as the navigator, may show CVS team-private directories. There
41  * are some common scenarios where a user may (depending on the order of delta traversal) see
42  * this behavior:
43  *
44  * 1. A user has an existing CVS project outside of Eclipse. By creating the project in Eclipse to point
45  * to the existing location the project's contents will be brought into Eclipse and the CVS folders
46  * will be marlked as team-private but other delta listeners that have handled the event already won't receive
47  * notification that the resource is now team-private. As a result, the user may have to close views or
48  * restart the workbench to have the CVS folders filtered.
49  *
50  * 2. A user performs CVS command line operations outside of Eclipse that result in new CVS folders.
51  * From Eclipse the refresh local will bring in the new folders and they will be marked as team-private.
52  * But as in 1, they may not appear in the UI.
53  *
54  * See: http://dev.eclipse.org/bugs/show_bug.cgi?id=12386
55  */

56 public class SyncFileChangeListener implements IResourceChangeListener {
57     
58     // consider the following changes types and ignore the others (e.g. marker and description changes are ignored)
59
protected int INTERESTING_CHANGES = IResourceDelta.CONTENT |
60                                                                     IResourceDelta.MOVED_FROM |
61                                                                     IResourceDelta.MOVED_TO |
62                                                                     IResourceDelta.OPEN |
63                                                                     IResourceDelta.REPLACED |
64                                                                     IResourceDelta.TYPE;
65     
66     protected boolean isProjectOpening = false;
67     
68     protected static DeferredResourceChangeHandler deferredHandler = new DeferredResourceChangeHandler();
69     
70     /**
71      * This accessor is for use by test cases only.
72      *
73      * @return Returns the deferredHandler.
74      */

75     public static DeferredResourceChangeHandler getDeferredHandler() {
76         return deferredHandler;
77     }
78     
79     /*
80      * When a resource changes this method will detect if the changed resources is a meta file that has changed
81      * by a 3rd party. For example, if the command line tool was run and then the user refreshed from local. To
82      * distinguish changes made by this class and thoses made by others a modification stamp is persisted with each
83      * metafile.
84      *
85      * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
86      */

87     public void resourceChanged(IResourceChangeEvent event) {
88         try {
89             final Set JavaDoc changedContainers = new HashSet JavaDoc();
90             final Set JavaDoc externalDeletions = new HashSet JavaDoc();
91             
92             setProjectOpening(false);
93             
94             event.getDelta().accept(new IResourceDeltaVisitor() {
95
96                 public boolean visit(IResourceDelta delta) {
97                     IResource resource = delta.getResource();
98                     
99                     if(resource.getType()==IResource.ROOT) {
100                         // continue with the delta
101
return true;
102                     }
103                     
104                     if (resource.getType() == IResource.PROJECT) {
105                         // If the project is not accessible, don't process it
106
if (!resource.isAccessible()) return false;
107                         setProjectOpening((delta.getFlags() & IResourceDelta.OPEN) != 0);
108                     }
109                                                             
110                     String JavaDoc name = resource.getName();
111                     int kind = delta.getKind();
112                     
113                     // if the file has changed but not in a way that we care
114
// then ignore the change (e.g. marker changes to files).
115
if(kind == IResourceDelta.CHANGED &&
116                         (delta.getFlags() & INTERESTING_CHANGES) == 0) {
117                             return true;
118                     }
119                                         
120                     if(name.equals(SyncFileWriter.CVS_DIRNAME)) {
121                         handleCVSDir((IContainer)resource, kind);
122                         // if the project is opening there is no need to notify about chagned CVs/ meta files
123
// they will all be read from disk.
124
if(isProjectOpening()) return false;
125                     } else {
126                         // Inform the synchronizer about folder creations
127
if(isProjectOpening()) return true;
128                     }
129                     
130                     if(isMetaFile(resource)) {
131                         IResource[] toBeNotified = handleChangedMetaFile(resource);
132                         if(toBeNotified.length>0 && isModifiedBy3rdParty(resource)) {
133                             for (int i = 0; i < toBeNotified.length; i++) {
134                                 changedContainers.add(toBeNotified[i]);
135                             }
136                             if(Policy.DEBUG_METAFILE_CHANGES) {
137                                 System.out.println("[cvs] metafile changed by 3rd party: " + resource.getFullPath()); //$NON-NLS-1$
138
}
139                             return false; /*don't visit any children we have all the information we need*/
140                         }
141                     } else if(isIgnoreFile(resource) && isModifiedBy3rdParty(resource)) {
142                         deferredHandler.ignoreFileChanged((IFile)resource);
143                     } else if (isExternalDeletion(resource, kind)) {
144                         externalDeletions.add(resource);
145                     } else if (kind == IResourceDelta.ADDED && isRecreation(resource)) {
146                         deferredHandler.recreated(resource);
147                     }
148                     return true;
149                 }
150             }, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
151                 
152             if(!changedContainers.isEmpty() || !externalDeletions.isEmpty()) {
153                 EclipseSynchronizer.getInstance().syncFilesChangedExternally(
154                     (IContainer[])changedContainers.toArray(new IContainer[changedContainers.size()]),
155                     (IFile[]) externalDeletions.toArray(new IFile[externalDeletions.size()]));
156             }
157         } catch(CoreException e) {
158             CVSProviderPlugin.log(e);
159         }
160     }
161
162     /**
163      * Treat a resource as an external deletion if
164      * - it is a file
165      * - the delta says the file was removed
166      * - the file is not managed but its parent is a CVS folder
167      *
168      * There will be some false positives but the reaction to this situation
169      * is to purge the cahced CVS meta-information so nothing bad will happen
170      * for the false positives.
171      *
172      * @param resource
173      * @param kind
174      * @return
175      */

176     protected boolean isExternalDeletion(IResource resource, int kind) {
177         if (kind != IResourceDelta.REMOVED) return false;
178         if (resource.getType() != IResource.FILE) return false;
179         ICVSFile file = CVSWorkspaceRoot.getCVSFileFor((IFile)resource);
180         try {
181             return (!file.isManaged() && file.getParent().isCVSFolder() && file.getParent().exists());
182         } catch (CVSException e) {
183             CVSProviderPlugin.log(e);
184             return false;
185         }
186     }
187
188     /*
189      * Consider non-existing resources as being recently deleted and thus modified, and resources
190      * with modification stamps that differ from when the CVS plugin last modified the meta-file.
191      */

192     protected boolean isModifiedBy3rdParty(IResource resource) {
193         if(!resource.exists()) return true;
194         long modStamp = resource.getModificationStamp();
195         Long JavaDoc whenWeWrote;
196         try {
197             whenWeWrote = (Long JavaDoc)resource.getSessionProperty(SyncFileWriter.MODSTAMP_KEY);
198         } catch(CoreException e) {
199             CVSProviderPlugin.log(e);
200             whenWeWrote = null;
201         }
202         return (whenWeWrote==null || whenWeWrote.longValue() != modStamp);
203     }
204     
205     /*
206      * If it's a new CVS directory with the canonical child metafiles then mark it as team-private. Otherwise
207      * if changed or deleted
208      */

209     protected void handleCVSDir(IContainer cvsDir, int kind) {
210         if((kind & IResourceDelta.ALL_WITH_PHANTOMS)!=0) {
211             if(kind==IResourceDelta.ADDED) {
212                 // should this dir be made team-private? If it contains CVS/Root and CVS/Repository then yes!
213
IFile rootFile = cvsDir.getFile(new Path(SyncFileWriter.ROOT));
214                 IFile repositoryFile = cvsDir.getFile(new Path(SyncFileWriter.REPOSITORY));
215                 if(rootFile.exists() && repositoryFile.exists() && !cvsDir.isTeamPrivateMember()) {
216                     try {
217                         // TODO: Is this considered a tree modification?
218
cvsDir.setTeamPrivateMember(true);
219                         if(Policy.DEBUG_METAFILE_CHANGES) {
220                             System.out.println("[cvs] found a new CVS meta folder, marking as team-private: " + cvsDir.getFullPath()); //$NON-NLS-1$
221
}
222                     } catch(CoreException e) {
223                         CVSProviderPlugin.log(CVSException.wrapException(cvsDir, CVSMessages.SyncFileChangeListener_errorSettingTeamPrivateFlag, e));
224                     }
225                 }
226             }
227         }
228     }
229     
230     protected boolean isIgnoreFile(IResource resource) {
231         return resource.getType() == IResource.FILE &&
232             resource.getName().equals(SyncFileWriter.IGNORE_FILE);
233     }
234     
235     private boolean isRecreation(IResource resource) {
236         return EclipseSynchronizer.getInstance().wasPhantom(resource);
237     }
238     
239     /*
240      * It's a meta file if it's parent is a team-private CVS folder.
241      */

242     protected boolean isMetaFile(IResource resource) {
243         IContainer parent = resource.getParent();
244         return resource.getType() == IResource.FILE &&
245                    parent!=null &&
246                    parent.getName().equals(SyncFileWriter.CVS_DIRNAME) &&
247                    (parent.isTeamPrivateMember() || !parent.exists());
248     }
249     
250     /*
251      * This is a meta file (e.g. folder/CVS/Entries), notify that 'folder' and it's immediate children
252      * may have their CVS sync state changed. If the 'folder' is deleted than no notification is
253      * required.
254      */

255     protected IContainer[] handleChangedMetaFile(IResource resource) {
256         IContainer changedContainer = resource.getParent().getParent();
257         if(changedContainer.exists()) {
258             return new IContainer[] {changedContainer};
259         } else {
260             return new IContainer[0];
261         }
262     }
263     
264     /**
265      * @return boolean
266      */

267     public boolean isProjectOpening() {
268         return isProjectOpening;
269     }
270
271     /**
272      * Sets the isProjectOpening.
273      * @param isProjectOpening The isProjectOpening to set
274      */

275     public void setProjectOpening(boolean isProjectOpening) {
276         this.isProjectOpening = isProjectOpening;
277     }
278 }
279
Popular Tags