blob: 1eda04cf638c818d06e2c439ef67bb2208eeb863 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v0.5
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v05.html
*
* Contributors:
* IBM - Initial implementation
******************************************************************************/
package org.eclipse.team.internal.ccvs.core.util;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer;
/*
* Listens to CVS meta-file changes and notifies the EclipseSynchronizer of changes made to sync files
* by 3rd parties.
*
* If CVS meta-directories are created outside of the CVS plugin their team-private state will be set
* by this listener however this change won't be known to other plugins because it does not generate
* a delta. As a result views, such as the navigator, may show CVS team-private directories. There
* are some common scenarios where a user may (depending on the order of delta traversal) see
* this behavior:
*
* 1. A user has an existing CVS project outside of Eclipse. By creating the project in Eclipse to point
* to the existing location the project's contents will be brought into Eclipse and the CVS folders
* will be marlked as team-private but other delta listeners that have handled the event already won't receive
* notification that the resource is now team-private. As a result, the user may have to close views or
* restart the workbench to have the CVS folders filtered.
*
* 2. A user performs CVS command line operations outside of Eclipse that result in new CVS folders.
* From Eclipse the refresh local will bring in the new folders and they will be marked as team-private.
* But as in 1, they may not appear in the UI.
*
* See: http://dev.eclipse.org/bugs/show_bug.cgi?id=12386
*/
public class SyncFileChangeListener implements IResourceChangeListener {
/*
* When a resource changes this method will detect if the changed resources is a meta file that has changed
* by a 3rd party. For example, if the command line tool was run and then the user refreshed from local. To
* distinguish changes made by this class and thoses made by others a modification stamp is persisted with each
* metafile.
*
* @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent event) {
try {
final Set changedContainers = new HashSet();
event.getDelta().accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
if(resource.getType()==IResource.ROOT) {
// continue with the delta
return true;
}
if (resource.getType() == IResource.PROJECT) {
// If the project is not accessible, don't process it
if (!resource.isAccessible()) return false;
if ((delta.getFlags() & IResourceDelta.OPEN) != 0) return false;
}
String name = resource.getName();
int kind = delta.getKind();
IResource[] toBeNotified = new IResource[0];
if(name.equals(SyncFileWriter.CVS_DIRNAME)) {
handleCVSDir((IContainer)resource, kind);
} else {
// Inform the synchronizer about folder creations
if (kind == IResourceDelta.ADDED) {
try {
CVSProviderPlugin.getPlugin().getFileModificationManager().created(resource);
} catch (CVSException e) {
throw new CoreException(e.getStatus());
}
}
}
if(isMetaFile(resource)) {
toBeNotified = handleChangedMetaFile(resource, kind);
} else if(name.equals(SyncFileWriter.IGNORE_FILE)) {
toBeNotified = handleChangedIgnoreFile(resource, kind);
}
if(toBeNotified.length>0 && isModifiedBy3rdParty(resource)) {
for (int i = 0; i < toBeNotified.length; i++) {
changedContainers.add(toBeNotified[i]);
}
if(Policy.DEBUG_METAFILE_CHANGES) {
System.out.println("[cvs] metafile changed by 3rd party: " + resource.getFullPath()); //$NON-NLS-1$
}
return false; /*don't visit any children we have all the information we need*/
} else {
return true;
}
}
}, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
if(!changedContainers.isEmpty()) {
EclipseSynchronizer.getInstance().syncFilesChanged((IContainer[])changedContainers.toArray(new IContainer[changedContainers.size()]));
}
} catch(CoreException e) {
CVSProviderPlugin.log(e.getStatus());
} catch(CVSException e) {
CVSProviderPlugin.log(e.getStatus());
}
}
/*
* Consider non-existing resources as being recently deleted and thus modified, and resources
* with modification stamps that differ from when the CVS plugin last modified the meta-file.
*/
protected boolean isModifiedBy3rdParty(IResource resource) {
if(!resource.exists()) return true;
long modStamp = resource.getModificationStamp();
Long whenWeWrote;
try {
whenWeWrote = (Long)resource.getSessionProperty(SyncFileWriter.MODSTAMP_KEY);
} catch(CoreException e) {
CVSProviderPlugin.log(e.getStatus());
whenWeWrote = null;
}
return (whenWeWrote==null || whenWeWrote.longValue() != modStamp);
}
/*
* If it's a new CVS directory with the canonical child metafiles then mark it as team-private. Otherwise
* if changed or deleted
*/
protected void handleCVSDir(IContainer cvsDir, int kind) {
if((kind & IResourceDelta.ALL_WITH_PHANTOMS)!=0) {
if(kind==IResourceDelta.ADDED) {
// should this dir be made team-private? If it contains CVS/Root and CVS/Repository then yes!
IFile rootFile = cvsDir.getFile(new Path(SyncFileWriter.ROOT));
IFile repositoryFile = cvsDir.getFile(new Path(SyncFileWriter.REPOSITORY));
if(rootFile.exists() && repositoryFile.exists() && !cvsDir.isTeamPrivateMember()) {
try {
cvsDir.setTeamPrivateMember(true);
if(Policy.DEBUG_METAFILE_CHANGES) {
System.out.println("[cvs] found a new CVS meta folder, marking as team-private: " + cvsDir.getFullPath()); //$NON-NLS-1$
}
} catch(CoreException e) {
CVSProviderPlugin.log(CVSException.wrapException(cvsDir, Policy.bind("SyncFileChangeListener.errorSettingTeamPrivateFlag"), e)); //$NON-NLS-1$
}
}
}
}
}
/*
* It's a meta file if it's parent is a team-private CVS folder.
*/
protected boolean isMetaFile(IResource resource) {
IContainer parent = resource.getParent();
return resource.getType() == IResource.FILE &&
parent!=null &&
parent.getName().equals(SyncFileWriter.CVS_DIRNAME) &&
(parent.isTeamPrivateMember() || !parent.exists());
}
/*
* This is a meta file (e.g. folder/CVS/Entries), notify that 'folder' and it's immediate children
* may have their CVS sync state changed. If the 'folder' is deleted than no notification is
* required.
*/
protected IContainer[] handleChangedMetaFile(IResource resource, int kind) {
IContainer changedContainer = resource.getParent().getParent();
if(changedContainer.exists()) {
return new IContainer[] {changedContainer};
} else {
return new IContainer[0];
}
}
/*
* This is an ignore file (e.g. folder/.cvsignore), notify that 'folder' and it's immediate children
* may have their CVS sync state changed.
*/
protected IContainer[] handleChangedIgnoreFile(IResource resource, int kind) {
IContainer changedContainer = resource.getParent();
if(changedContainer.exists()) {
return new IContainer[] {changedContainer};
} else {
return new IContainer[0];
}
}
}