blob: b64e4c38c231c24f8cbd8d9abefaf896d78d1dfd [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.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
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.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
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.PersistenceUtil;
import org.eclipse.epf.uma.ContentDescription;
import org.eclipse.epf.uma.MethodElement;
/**
* 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 addedResources = new UniqueEList();
private Collection changedResources = new UniqueEList();
private Collection removedResources = new UniqueEList();
private Collection movedResources = new UniqueEList();
private UniqueEList savedResources = new UniqueEList();
private Collection loadedBeforeRefreshResources = new ArrayList();
private IRefreshHandler refreshHandler;
private boolean enabled = true;
private Collection addedWorkspaceResources = new UniqueEList();
private RefreshJob() {
super("EPF Auto-Refresh"); //$NON-NLS-1$
}
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 void setRefreshHandler(IRefreshHandler handler) {
refreshHandler = handler;
}
/**
* Gets existing resources that reappear in workspaces
*
* @return the addedResources
*/
public Collection getAddedResources() {
removeResources(addedResources, savedResources);
return addedResources;
}
public Collection getAddedWorkspaceResources() {
return addedWorkspaceResources;
}
/**
* @return Returns the changedResources.
*/
public Collection 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 resources,
Collection resourcesToRemove) {
synchronized (resourcesToRemove) {
if (!resourcesToRemove.isEmpty()) {
for (Iterator iter = resourcesToRemove.iterator(); iter
.hasNext();) {
Object resource = iter.next();
boolean canRemove = true;
if (resource instanceof MultiFileXMIResourceImpl) {
MultiFileXMIResourceImpl mfResource = ((MultiFileXMIResourceImpl) resource);
long currentTime = new File(mfResource.getURI()
.toFileString()).lastModified();
canRemove = currentTime == mfResource
.getFileLastModified()
|| MultiFileSaveUtil.same(currentTime,
mfResource.getFileLastModified());
}
if (canRemove) {
if (resources.remove(resource)) {
iter.remove();
}
}
}
}
}
}
/**
* @return Returns the removedResources.
*/
public Collection getRemovedResources() {
return removedResources;
}
/**
* @return Returns the movedResources.
*/
public Collection getMovedResources() {
return movedResources;
}
public void resourceSaved(Resource resource) {
synchronized (savedResources) {
savedResources.add(resource);
}
}
/**
* @return the loadedBeforeRefreshResources
*/
public Collection getReloadedBeforeRefreshResources() {
return loadedBeforeRefreshResources;
}
public void reset() {
changedResources.clear();
removedResources.clear();
movedResources.clear();
savedResources.clear();
loadedBeforeRefreshResources.clear();
addedResources.clear();
addedWorkspaceResources.clear();
}
private void scheduleRefresh() {
if (getState() == Job.NONE) {
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();
}
//TODO: move this method to a utility class and make it static
public Resource getResource(String path) {
URI uri = URI.createFileURI(path);
for (Iterator iter = new ArrayList(resourceSet.getResources())
.iterator(); iter.hasNext();) {
Resource resource = (Resource) iter.next();
if (uri.equals(resource.getURI())) {
return resource;
}
}
return null;
}
//TODO: move this method to a utility class and make it static
public Resource getResource(IResource wsRes) {
return getResource(wsRes.getLocation().toString());
}
/**
* 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 changedResources = new ArrayList();
private Collection removedResources = new ArrayList();
private Collection movedResources = new ArrayList();
private Collection addedResources = new ArrayList();
private ArrayList addedWorkspaceResources = new ArrayList();
public boolean visit(IResourceDelta delta) throws CoreException {
Resource resource;
IPath path;
String loc;
if (delta.getFlags() != IResourceDelta.MARKERS
&& delta.getResource().getType() == IResource.FILE) {
switch (delta.getKind()) {
case IResourceDelta.ADDED:
// handle added resource
//
path = delta.getResource().getLocation();
if (path != null) {
loc = path.toString();
resource = getResource(loc);
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());
}
}
break;
case IResourceDelta.REMOVED:
if ((IResourceDelta.MOVED_TO & delta.getFlags()) != 0) {
// handle file move
path = delta.getResource().getLocation();
if(path != null) {
resource = getResource(path.toString());
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$
}
} else {
path = delta.getResource().getLocation();
if (path != null) {
resource = getResource(path.toString());
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) {
path = delta.getResource().getLocation();
if (path != null) {
loc = path.toString();
resource = getResource(loc);
if (resource != null
&& MultiFileSaveUtil
.checkSynchronized(resource) != 1) {
changedResources.add(resource);
}
}
}
break;
}
}
// watch for project move
else if (delta.getFlags() != IResourceDelta.MARKERS
&& delta.getResource().getType() == IResource.PROJECT) {
// tested using the Move.. command in the Resource Navigator view
// the REPLACED flag only seemed to occur when the library was moved
// outside of EPF and EPF was re-started, so make sure it's clear, otherwise
// we reload the library right after loading it when starting EPF
if (delta.getKind() == IResourceDelta.CHANGED &&
((delta.getFlags() & IResourceDelta.REPLACED) == 0) &&
((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0)) {
movedResources.add(delta.getResource());
}
return true;
}
return true;
}
public Collection getChangedResources() {
return changedResources;
}
public Collection getRemovedResources() {
return removedResources;
}
public Collection getMovedResources() {
return movedResources;
}
};
ResourceDeltaVisitor visitor = new ResourceDeltaVisitor();
delta.accept(visitor);
removedResources.addAll(visitor.getRemovedResources());
movedResources.addAll(visitor.getMovedResources());
changedResources.addAll(visitor.getChangedResources());
addedResources.addAll(visitor.addedResources);
addedWorkspaceResources.addAll(visitor.addedWorkspaceResources);
if (shouldRefresh()) {
scheduleRefresh();
}
} catch (CoreException e) {
CommonPlugin.INSTANCE.log(e);
}
}
/**
* Resolves the proxy and its containers
*
* TODO: move this method to a utility class and make it static
*
* @param proxy
* @return
*/
public EObject resolve(EObject proxy) {
EObject resolved;
try {
resolved = EcoreUtil.resolve(proxy, resourceSet);
} catch (Exception e) {
resolved = proxy;
}
EObject container = proxy.eContainer();
if (resolved.eContainer() == null && container != null) {
if (container.eIsProxy()) {
container = resolve(container);
}
EReference ref = proxy.eContainmentFeature();
if (ref.isMany()) {
List values = (List) container.eGet(ref);
for (Iterator iter = values.iterator(); iter.hasNext(); iter
.next())
;
} else {
container.eGet(ref);
}
}
return resolved;
}
public static RefreshJob getInstance() {
return instance;
}
private static RefreshJob instance = new RefreshJob();
}