| //------------------------------------------------------------------------------ |
| // 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(); |
| |
| } |