blob: ef9bbf4a2d87c4c23172338c4914b4773223f19d [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 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.persistence.refresh;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
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.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.epf.persistence.FileManager;
import org.eclipse.epf.persistence.MultiFileResourceSetImpl;
import org.eclipse.epf.persistence.MultiFileSaveUtil;
import org.eclipse.epf.persistence.MultiFileXMIResourceImpl;
import org.eclipse.epf.persistence.PersistencePlugin;
import org.eclipse.epf.persistence.util.LibrarySchedulingRule;
import org.eclipse.epf.persistence.util.PersistenceResources;
import org.eclipse.epf.persistence.util.PersistenceUtil;
import org.eclipse.epf.uma.ContentDescription;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodLibrary;
/**
* Background job that keeps notifying refresh handlers about changes in
* resources
*
* @author Phong Nguyen Le
* @since 1.0
*/
public class RefreshJob extends WorkspaceJob implements IResourceChangeListener {
private static final long UPDATE_DELAY = 200;
private static final boolean DEBUG = PersistencePlugin.getDefault().isDebugging();
private static final String DEBUG_PREFIX = "EPF Auto-refresh:"; //$NON-NLS-1$
private ResourceSet resourceSet;
private Collection<Resource> addedResources = new UniqueEList<Resource>();
private Collection<Resource> changedResources = new UniqueEList<Resource>();
private Collection<Resource> removedResources = new UniqueEList<Resource>();
private Collection<Resource> movedResources = new UniqueEList<Resource>();
private UniqueEList<Resource> savedResources = new UniqueEList<Resource>();
private Collection<Resource> loadedBeforeRefreshResources = new ArrayList<Resource>();
private IRefreshHandler refreshHandler;
private boolean enabled = true;
private Collection<IResource> addedWorkspaceResources = new UniqueEList<IResource>();
private RefreshJob() {
super(PersistenceResources.resourceAutoRefreshJob_name);
}
public void setEnabled(boolean b) {
enabled = b;
}
public boolean isEnabled() {
return enabled;
}
/**
* @param resourceSet
* The resourceSet to set.
*/
public void setResourceSet(ResourceSet resourceSet) {
this.resourceSet = resourceSet;
}
public ResourceSet getResourceSet() {
return resourceSet;
}
public void setRefreshHandler(IRefreshHandler handler) {
refreshHandler = handler;
}
/**
* Gets existing resources that reappear in workspaces
*
* @return the addedResources
*/
public Collection<Resource> getAddedResources() {
removeResources(addedResources, savedResources);
return addedResources;
}
public Collection<IResource> getAddedWorkspaceResources() {
return addedWorkspaceResources;
}
/**
* @return Returns the changedResources.
*/
public Collection<Resource> getChangedResources() {
removeResources(changedResources, savedResources);
removeResources(changedResources, loadedBeforeRefreshResources);
return changedResources;
}
/**
* Removes <code>resourcesToRemove</code> from <code>resources</code> if
* the resources are synchronized with their storage.
*
* @param resourcesToRemove
*/
private static void removeResources(Collection<Resource> resources,
Collection<Resource> resourcesToRemove) {
synchronized (resourcesToRemove) {
if (!resourcesToRemove.isEmpty()) {
for (Iterator<Resource> iter = resourcesToRemove.iterator(); iter
.hasNext();) {
Object resource = iter.next();
boolean canRemove = true;
if (resource instanceof MultiFileXMIResourceImpl) {
MultiFileXMIResourceImpl mfResource = ((MultiFileXMIResourceImpl) resource);
canRemove = mfResource.isSynchronized();
}
if (canRemove) {
if (resources.remove(resource)) {
iter.remove();
}
}
}
}
}
}
/**
* @return Returns the removedResources.
*/
public Collection<Resource> getRemovedResources() {
return removedResources;
}
/**
* @return Returns the movedResources.
*/
public Collection<Resource> getMovedResources() {
return movedResources;
}
public void resourceSaved(Resource resource) {
if(resource.getResourceSet() == resourceSet) {
synchronized (savedResources) {
savedResources.add(resource);
}
}
}
/**
* @return the loadedBeforeRefreshResources
*/
public Collection<Resource> getReloadedBeforeRefreshResources() {
return loadedBeforeRefreshResources;
}
public void reset() {
changedResources.clear();
removedResources.clear();
movedResources.clear();
savedResources.clear();
loadedBeforeRefreshResources.clear();
addedResources.clear();
addedWorkspaceResources.clear();
}
private MethodLibrary getMethodLibrary() {
if(resourceSet instanceof MultiFileResourceSetImpl) {
return ((MultiFileResourceSetImpl)resourceSet).getMethodLibrary();
}
return null;
}
private void scheduleRefresh() {
if (getState() == Job.NONE) {
MethodLibrary lib = getMethodLibrary();
if(lib != null) {
setRule(new LibrarySchedulingRule(lib));
schedule(UPDATE_DELAY);
}
}
}
/*
* (non-Javadoc)
*
* @see WorkspaceJob#runInWorkspace
*/
public IStatus runInWorkspace(IProgressMonitor monitor) {
if (refreshHandler == null)
return Status.OK_STATUS;
long start = System.currentTimeMillis();
Throwable error = null;
try {
if (DEBUG)
System.out.println(DEBUG_PREFIX + " starting refresh job"); //$NON-NLS-1$
monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
if (monitor.isCanceled())
throw new OperationCanceledException();
try {
refreshHandler.refresh(monitor);
} catch (Throwable e) {
error = e;
}
} finally {
monitor.done();
if (DEBUG)
System.out
.println(DEBUG_PREFIX
+ " finished refresh job in: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (error != null)
return new Status(IStatus.ERROR, FileManager.PLUGIN_ID, 0,
"Refresh error", error); //$NON-NLS-1$
return Status.OK_STATUS;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.jobs.Job#shouldRun()
*/
public synchronized boolean shouldRun() {
return shouldRefresh();
}
private boolean shouldRefresh() {
return !removedResources.isEmpty() || !changedResources.isEmpty()
|| !addedResources.isEmpty() || !movedResources.isEmpty()
|| !loadedBeforeRefreshResources.isEmpty()
|| !addedWorkspaceResources.isEmpty();
}
/**
* Starts the refresh job
*/
public void start() {
if (DEBUG) {
System.out.println("RefreshJob.start()"); //$NON-NLS-1$
}
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}
/**
* Stops the refresh job
*/
public void stop() {
if (DEBUG) {
System.out.println("RefreshJob.stop()"); //$NON-NLS-1$
}
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
cancel();
}
public Resource getResource(IResource wsRes) {
if(resourceSet instanceof MultiFileResourceSetImpl) {
return ((MultiFileResourceSetImpl)resourceSet).getResource(wsRes);
}
IPath path = wsRes.getLocation();
return path != null ? PersistenceUtil.getResource(path, resourceSet) : null;
}
/**
* Checks if the given resource can be accepted as a new resource of
* resource set of this refresh job that needs to be loaded.
*
* @param resource
* @return
*/
private boolean accept(IResource resource) {
if (resourceSet instanceof MultiFileResourceSetImpl) {
return ((MultiFileResourceSetImpl) resourceSet)
.isNewResourceToLoad(resource);
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent event) {
if (!isEnabled() || resourceSet == null)
return;
IResourceDelta delta = event.getDelta();
if (delta == null)
return;
try {
class ResourceDeltaVisitor implements IResourceDeltaVisitor {
private Collection<Resource> changedResources = new ArrayList<Resource>();
private Collection<Resource> removedResources = new ArrayList<Resource>();
private Collection<Resource> movedResources = new ArrayList<Resource>();
private Collection<Resource> addedResources = new ArrayList<Resource>();
private ArrayList<IResource> addedWorkspaceResources = new ArrayList<IResource>();
public boolean visit(IResourceDelta delta) throws CoreException {
Resource resource;
if ((delta.getFlags() & IResourceDelta.MARKERS) != IResourceDelta.MARKERS
&& delta.getResource().getType() == IResource.FILE) {
switch (delta.getKind()) {
case IResourceDelta.ADDED:
// handle added resource
//
resource = getResource(delta.getResource());
if (resource != null) {
if (!resource.isLoaded()) {
// the resource was created but not
// loaded b/c the workspace resource
// was not added to the workspace at the
// time of loading
//
MethodElement me = PersistenceUtil
.getMethodElement(resource);
if (!(me instanceof ContentDescription)) {
// no auto-reload for content
// description
// MethodElementEditor will detect
// the change and load it
// when activated.
//
addedResources.add(resource);
}
}
} else if (accept(delta.getResource())) {
addedWorkspaceResources.add(delta
.getResource());
}
// handle file move
//
if ((IResourceDelta.MOVED_FROM & delta.getFlags()) != 0) {
// resource = getResource(delta.getResource());
// if (resource != null) {
// movedResources.add(resource);
// }
//
if (DEBUG) {
IPath movedFromPath = delta.getResource()
.getLocation();
IPath movedToPath = delta.getMovedToPath();
System.out
.println("Resource moved from '" + movedFromPath + "' to '" + movedToPath + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
break;
case IResourceDelta.REMOVED:
resource = getResource(delta.getResource());
if (resource != null) {
removedResources.add(resource);
}
break;
case IResourceDelta.CHANGED:
boolean encodingChanged = ((IResourceDelta.ENCODING & delta
.getFlags()) != 0);
boolean contentChanged = ((IResourceDelta.CONTENT & delta
.getFlags()) != 0);
if (encodingChanged || contentChanged) {
resource = getResource(delta.getResource());
if (resource != null
&& MultiFileSaveUtil
.checkSynchronized(resource) != 1) {
changedResources.add(resource);
}
}
break;
}
}
return true;
}
};
ResourceDeltaVisitor visitor = new ResourceDeltaVisitor();
delta.accept(visitor);
removedResources.addAll(visitor.removedResources);
movedResources.addAll(visitor.movedResources);
changedResources.addAll(visitor.changedResources);
addedResources.addAll(visitor.addedResources);
addedWorkspaceResources.addAll(visitor.addedWorkspaceResources);
if (shouldRefresh()) {
scheduleRefresh();
}
} catch (CoreException e) {
CommonPlugin.INSTANCE.log(e);
}
}
/**
* Resolves the proxy and its containers
*
* @param proxy
* @return
*/
public EObject resolve(EObject proxy) {
return PersistenceUtil.resolve(proxy, resourceSet);
}
public static RefreshJob getInstance() {
return instance;
}
private static RefreshJob instance = new RefreshJob();
}