/******************************************************************************* * Copyright (c) 2007, 2013 Spring IDE Developers * 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: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.core.internal.model.resources; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; 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.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; 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.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import org.springframework.ide.eclipse.core.SpringCore; import org.springframework.ide.eclipse.core.SpringCoreUtils; import org.springframework.ide.eclipse.core.java.JdtUtils; /** * Implementation of {@link IResourceChangeListener} which detects modifications to Spring projects (add/remove Spring * beans nature, open/close and delete). * <p> * An implementation of {@link ISpringResourceChangeEvents} has to be provided. Here are callbacks defined for the * different events. * @author Torsten Juergeleit * @author Christian Dupuis * @author Martin Lippert * @since 2.0 */ public class SpringResourceChangeListener implements IResourceChangeListener { public static final int LISTENER_FLAGS = IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_BUILD | IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.PRE_REFRESH; private static final int VISITOR_FLAGS = IResourceDelta.ADDED | IResourceDelta.CHANGED | IResourceDelta.REMOVED; private ISpringResourceChangeEvents events; public SpringResourceChangeListener(ISpringResourceChangeEvents events) { this.events = events; } public void resourceChanged(IResourceChangeEvent event) { if (event.getSource() instanceof IWorkspace) { int eventType = event.getType(); switch (eventType) { case IResourceChangeEvent.PRE_CLOSE: IProject closedProject = (IProject) event.getResource(); if (SpringCoreUtils.isSpringProject(closedProject)) { events.projectClosed(closedProject, eventType); } break; case IResourceChangeEvent.PRE_DELETE: IProject openedProject = (IProject) event.getResource(); if (SpringCoreUtils.isSpringProject(openedProject)) { events.projectDeleted(openedProject, eventType); } break; case IResourceChangeEvent.PRE_BUILD: case IResourceChangeEvent.POST_BUILD: case IResourceChangeEvent.PRE_REFRESH: IResourceDelta delta = event.getDelta(); if (delta != null) { try { delta.accept(getVisitor(eventType), VISITOR_FLAGS); } catch (CoreException e) { SpringCore.log("Error while traversing " + "resource change delta", e); } } break; } } else if (event.getSource() instanceof IProject) { int eventType = event.getType(); switch (eventType) { case IResourceChangeEvent.PRE_CLOSE: IProject closedProject = (IProject) event.getSource(); if (SpringCoreUtils.isSpringProject(closedProject)) { events.projectClosed(closedProject, eventType); } break; case IResourceChangeEvent.PRE_DELETE: IProject openedProject = (IProject) event.getSource(); if (SpringCoreUtils.isSpringProject(openedProject)) { events.projectDeleted(openedProject, eventType); } break; case IResourceChangeEvent.PRE_BUILD: case IResourceChangeEvent.POST_BUILD: IResourceDelta delta = event.getDelta(); if (delta != null) { try { delta.accept(getVisitor(eventType), VISITOR_FLAGS); } catch (CoreException e) { SpringCore.log("Error while traversing " + "resource change delta", e); } } break; } } } protected IResourceDeltaVisitor getVisitor(int eventType) { return new SpringResourceVisitor(eventType); } /** * Internal resource delta visitor. */ protected class SpringResourceVisitor implements IResourceDeltaVisitor { protected int eventType; public SpringResourceVisitor(int eventType) { this.eventType = eventType; } public final boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); switch (delta.getKind()) { case IResourceDelta.ADDED: return resourceAdded(resource); case IResourceDelta.OPEN: return resourceOpened(resource); case IResourceDelta.CHANGED: return resourceChanged(resource, delta.getFlags()); case IResourceDelta.REMOVED: return resourceRemoved(resource); } return true; } protected boolean resourceAdded(final IResource resource) { if (resource instanceof IProject) { if (SpringCoreUtils.isSpringProject(resource)) { // Check if project is already in sync with the file system. if not launch background job of later // refresh. if (resource.isSynchronized(IResource.DEPTH_INFINITE)) { events.projectAdded((IProject) resource, eventType); } else { Job projectAddJob = new Job("Importing project configuration for '" + resource.getName() + "'") { @Override protected IStatus run(IProgressMonitor monitor) { // Check again that project is in sync before initiating the new Spring projects if (!resource.isSynchronized(IResource.DEPTH_INFINITE)) { try { resource.refreshLocal(IResource.DEPTH_INFINITE, monitor); } catch (CoreException e) { SpringCore.log(e); } } events.projectAdded((IProject) resource, eventType); SpringCoreUtils.buildFullProject((IProject) resource); return Status.OK_STATUS; } }; projectAddJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().refreshRule(resource)); projectAddJob.setPriority(Job.INTERACTIVE); projectAddJob.setSystem(true); projectAddJob.schedule(); } } return false; } return true; } protected boolean resourceOpened(IResource resource) { if (resource instanceof IProject) { if (SpringCoreUtils.isSpringProject(resource)) { events.projectOpened((IProject) resource, eventType); } return false; } return true; } protected boolean resourceChanged(IResource resource, int flags) { if (resource instanceof IProject) { if ((flags & IResourceDelta.OPEN) != 0) { if (SpringCoreUtils.isSpringProject(resource)) { events.projectOpened((IProject) resource, eventType); } return false; } else if ((flags & IResourceDelta.DESCRIPTION) != 0) { IProject project = (IProject) resource; if (SpringCoreUtils.isSpringProject(project)) { if (!events.isSpringProject((IProject) resource, eventType)) { events.springNatureAdded(project, eventType); } } else if (events.isSpringProject(project, eventType)) { events.springNatureRemoved(project, eventType); } return false; } } else if (resource instanceof IFolder && JdtUtils.isJavaProject(resource)) { // Make sure we don't iterate into output locations/folders try { IJavaProject jp = JdtUtils.getJavaProject(resource); if (!checkPathForNonOutputLocation(jp.getOutputLocation(), resource)) { return false; } for (IClasspathEntry entry : jp.getRawClasspath()) { if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { if (!checkPathForNonOutputLocation(entry.getOutputLocation(), resource)) { return false; } } } } catch (JavaModelException e) { SpringCore.log(e); } } return true; } protected boolean resourceRemoved(IResource resource) { return true; } protected boolean checkPathForNonOutputLocation(IPath path, IResource resource) { if (path != null) { path = path.removeFirstSegments(1); if (path.isPrefixOf(resource.getProjectRelativePath())) { return false; } } return true; } } }