/** * Copyright (c) 2013-2016 Angelo ZERR. * 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: * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation */ package tern.eclipse.ide.internal.core.resources; 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.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.jobs.Job; import tern.eclipse.ide.internal.core.Trace; import tern.resources.TernProject; /** * Tern project synchronizer used to : * * <ul> * <li>dispose the tern project when the Eclipse project is removed or closed.</li> * <li>synchronize tern file indexes</li> * </ul> */ public class IDETernProjectSynchronizer implements IResourceChangeListener, IResourceDeltaVisitor { private static final IDETernProjectSynchronizer INSTANCE = new IDETernProjectSynchronizer(); public static IDETernProjectSynchronizer getInstance() { return INSTANCE; } public IDETernProjectSynchronizer() { } public void initialize() { ResourcesPlugin.getWorkspace().addResourceChangeListener(this); } /** * Dispose the file manager. */ public void dispose() { ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } @Override public void resourceChanged(IResourceChangeEvent event) { try { IResource resource = event.getResource(); switch (event.getType()) { case IResourceChangeEvent.PRE_DELETE: // called when project is deleted. case IResourceChangeEvent.PRE_CLOSE: // called when project is closed. if (resource != null && resource.getType() == IResource.PROJECT) { IProject project = (IProject) resource; disposeTernProject(project); } break; case IResourceChangeEvent.POST_CHANGE: IResourceDelta delta = event.getDelta(); if (delta != null) { delta.accept(this); } break; } } catch (Throwable e) { Trace.trace(Trace.SEVERE, "Error while tern file synchronization", e); } } /** * Dispose the tern project linked to the given project if it is linked to a * tern project. * * @param project * @throws CoreException */ private void disposeTernProject(IProject project) throws CoreException { IDETernProject ternProject = IDETernProject.getTernProject(project); if (ternProject != null) { ternProject.dispose(); } } @Override public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); if (resource == null) { return false; } switch (resource.getType()) { case IResource.ROOT: return true; case IResource.PROJECT: IProject project = (IProject) resource; // if project is created, or removed stop the synchronization switch (delta.getKind()) { case IResourceDelta.ADDED: case IResourceDelta.REMOVED: return false; } // check if the current project has tern nature return IDETernProject.hasTernNature(project); case IResource.FOLDER: return true; case IResource.FILE: IDETernProject ternProject = IDETernProject.getTernProject(resource .getProject()); if (ternProject == null) { // tern project was not loaded, none synchronization is // required. return false; } if (isTernProjectFile(resource)) { switch (delta.getKind()) { case IResourceDelta.CHANGED: // refresh tern project outside the resource delta to avoid // having problem // "org.eclipse.core.internal.resources.ResourceException: The resource tree is locked for modifications" // See https://github.com/angelozerr/tern.java/issues/161 Job configJob = new RefreshTernProjectJob(ternProject); // We cannot use a project-based rule, because IProject.setDescription() uses a workspace-wide operation, // so a workspace-based rule is to be used here // See: https://github.com/angelozerr/tern.java/issues/251 // configJob.setRule(ternProject.getProject()); configJob.setRule(ternProject.getProject().getWorkspace().getRoot()); configJob.schedule(); break; case IResourceDelta.REMOVED: ternProject.dispose(); break; } } else { ternProject.getFileSynchronizer().refresh(resource); } return true; } return false; } private boolean isTernProjectFile(IResource resource) { // check if file name is .tern-project if (!TernProject.TERN_PROJECT_FILE.equals(resource.getName())) { return false; } // check if parent of file is a project if (resource.getParent() == null || resource.getParent().getType() != IResource.PROJECT) { return false; } return true; } }