blob: 0925f095ec3b0fd889aa8f8fb96650c7e4599076 [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2007 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.persistence.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
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.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.epf.persistence.PersistencePlugin;
import org.osgi.framework.Bundle;
/**
* Synchronizes the document with external resource changes.
*
* @author Phong Nguyen Le
*
* @since 1.2
*/
public class FileSynchronizer implements IResourceChangeListener {
/**
* Bundle of all required information to allow files as underlying document resources.
*/
public static class FileInfo //extends StorageInfo
{
/** The time stamp at which this provider changed the file. */
public long fModificationStamp= IResource.NULL_STAMP;
public boolean fCanBeSaved;
public IFile fFile;
/**
* Creates and returns a new file info.
*
* @param document the document
* @param model the annotation model
* @param fileSynchronizer the file synchronizer
*/
public FileInfo(IFile file) {
fFile = file;
}
}
/**
* A flag indicating whether this synchronizer is installed or not.
*
*
*/
protected boolean fIsInstalled= false;
/** Element information of all connected elements */
private Map<Object, FileInfo> fFileInfoMap= new HashMap<Object, FileInfo>();
/**
* Creates a new file synchronizer. Is not yet installed on a resource.
*/
public FileSynchronizer() {
install();
}
public boolean accept(IFile file) {
return false;
}
public boolean isInstalled() {
return fIsInstalled;
}
public void updateModificationStamp(IFile file) {
FileInfo info = getFileInfo(file);
if(info != null) {
info.fModificationStamp = computeModificationStamp(file);
}
}
/**
* Returns the element info object for the given element.
*
* @param element the element
* @return the element info object, or <code>null</code> if none
*/
public FileInfo getFileInfo(Object element) {
return (FileInfo) fFileInfoMap.get(element);
}
/**
* Installs the synchronizer on the input's file.
*/
public void install() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
fIsInstalled= true;
}
/**
* Uninstalls the synchronizer from the input's file.
*/
public void uninstall() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
fIsInstalled= false;
}
/*
* @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent e) {
IResourceDelta delta= e.getDelta();
try {
if (delta != null && fIsInstalled) {
ResourceDeltaVisistor visitor = new ResourceDeltaVisistor();
delta.accept(visitor);
Runnable runnable = visitor.getRunnable();
if(runnable != null) {
update(runnable);
}
}
} catch (CoreException x) {
handleCoreException(x, "Error handling resource change event."); //$NON-NLS-1$
}
}
/**
* Defines the standard procedure to handle <code>CoreExceptions</code>. Exceptions
* are written to the plug-in log.
*
* @param exception the exception to be logged
* @param message the message to be logged
*
*/
protected void handleCoreException(CoreException exception, String message) {
Bundle bundle = Platform.getBundle(PersistencePlugin.getDefault().getId());
ILog log= Platform.getLog(bundle);
if (message != null)
log.log(new Status(IStatus.ERROR, PersistencePlugin.getDefault().getId(), 0, message, exception));
else
log.log(exception.getStatus());
}
/**
* Computes the initial modification stamp for the given resource.
*
* @param resource the resource
* @return the modification stamp
*/
public static long computeModificationStamp(IResource resource) {
if(resource == null) {
System.err.println("FATAL ERROR: resource is null."); //$NON-NLS-1$
return 0;
}
long modificationStamp= resource.getModificationStamp();
IPath path= resource.getLocation();
if (path == null)
return modificationStamp;
modificationStamp= path.toFile().lastModified();
return modificationStamp;
}
private class ResourceDeltaVisistor implements IResourceDeltaVisitor {
private Collection<IFile> changedFiles = new ArrayList<IFile>();
private Map<IFile, IPath> movedFileToNewPathMap = new HashMap<IFile, IPath>();
private Collection<IFile> deletedFiles = new ArrayList<IFile>();
private Collection<IFile> addedFiles = new ArrayList<IFile>();
/*
* @see IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
*/
public boolean visit(IResourceDelta delta) throws CoreException {
if (delta == null)
return false;
if ((delta.getFlags() & IResourceDelta.MARKERS) != IResourceDelta.MARKERS
&& delta.getResource().getType() == IResource.FILE) {
switch (delta.getKind()) {
case IResourceDelta.CHANGED:
FileInfo info = (FileInfo) getFileInfo(delta.getResource());
if (info == null || info.fCanBeSaved)
break;
IFile file = (IFile) delta.getResource();
boolean isSynchronized = computeModificationStamp(file) == info.fModificationStamp;
if (((IResourceDelta.ENCODING & delta.getFlags()) != 0 && isSynchronized)
|| ((IResourceDelta.CONTENT & delta.getFlags()) != 0 && !isSynchronized)) {
changedFiles.add(file);
}
break;
case IResourceDelta.REMOVED:
if ((IResourceDelta.MOVED_TO & delta.getFlags()) != 0) {
info = (FileInfo) getFileInfo(delta.getResource());
if (info == null)
break;
final IPath path = delta.getMovedToPath();
movedFileToNewPathMap.put((IFile) delta.getResource(),
path);
} else {
info = (FileInfo) getFileInfo(delta.getResource());
if (info != null && !info.fCanBeSaved) {
deletedFiles.add((IFile) delta.getResource());
}
}
break;
case IResourceDelta.ADDED:
file = (IFile) delta.getResource();
if(accept(file)) {
addedFiles.add(file);
}
}
}
return true;
}
public Runnable getRunnable() {
// consider the file is newly added if it is accepted and not a moved file
//
if (!addedFiles.isEmpty() && !movedFileToNewPathMap.isEmpty()) {
for (Iterator<IFile> iter = addedFiles.iterator(); iter
.hasNext();) {
IFile file = iter.next();
if (movedFileToNewPathMap.containsValue(file)) {
iter.remove();
}
}
}
if(changedFiles.isEmpty() && movedFileToNewPathMap.isEmpty() && deletedFiles.isEmpty() && addedFiles.isEmpty()) {
return null;
}
return new Runnable() {
public void run() {
handleChanged(ResourceDeltaVisistor.this);
}
};
}
}
/**
* Posts the update code "behind" the running operation.
*
* @param runnable the update code
*/
protected void update(Runnable runnable) {
runnable.run();
}
public void monitor(IFile file) {
FileInfo info= (FileInfo) fFileInfoMap.get(file);
if (info == null) {
info = new FileInfo(file);
info.fModificationStamp = computeModificationStamp(file);
fFileInfoMap.put(file, info);
}
}
public void unmonitor(IFile file) {
fFileInfoMap.remove(file);
}
public void unmonitorAll() {
fFileInfoMap.clear();
}
protected void handleChanged(ResourceDeltaVisistor visitor) {
if(!visitor.changedFiles.isEmpty()) {
handleChangedFiles(visitor.changedFiles);
}
if(!visitor.movedFileToNewPathMap.isEmpty()) {
handleMovedFiles(visitor.movedFileToNewPathMap);
}
if(!visitor.deletedFiles.isEmpty()) {
handleDeletedFiles(visitor.deletedFiles);
}
if(!visitor.addedFiles.isEmpty()) {
handleAddedFiles(visitor.addedFiles);
}
}
protected Collection<IFile> handleDeletedFiles(Collection<IFile> deletedFiles) {
return Collections.emptyList();
}
protected Collection<IFile> handleMovedFiles(Map<IFile, IPath> movedFileToNewPathMap) {
return Collections.emptyList();
}
protected Collection<IFile> handleChangedFiles(Collection<IFile> changedFiles) {
return Collections.emptyList();
}
protected Collection<IFile> handleAddedFiles(Collection<IFile> addedFiles) {
return Collections.emptyList();
}
}