/******************************************************************************* * Copyright (c) 2016 Zend Technologies 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: * Zend Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.core; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.CoreException; 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; /** * Cache that stores references to resources that are symbolic links in OS file * system. This cache is intended to be used whenever a quick check is needed if * some resource is a symbolic link. * * @author Bartlomiej Laczkowski */ public enum PHPSymbolicLinksCache { INSTANCE; private final class StartupJob extends Job { public StartupJob() { super(CoreMessages.getString("PHPSymbolicLinksCache_Initializing_symbolic_links_cache")); //$NON-NLS-1$ setSystem(true); setUser(false); } @Override protected IStatus run(IProgressMonitor monitor) { initialize(); return Status.OK_STATUS; } } private final class UpdaterJob extends Job { static final int ADDITION = 1; static final int REMOVAL = 2; private int kind; private List<IResource> resources; public UpdaterJob() { super(CoreMessages.getString("PHPSymbolicLinksCache_Updating_symbolic_links_cache")); //$NON-NLS-1$ setSystem(true); setUser(false); } @Override protected IStatus run(IProgressMonitor monitor) { switch (kind) { case ADDITION: { for (IResource resource : resources) { ResourceAttributes attributes = resource.getResourceAttributes(); if (attributes != null && attributes.isSymbolicLink()) { addResource(resource); } } break; } case REMOVAL: { for (IResource resource : resources) { if (isSymbolicLink(resource)) { removeResource(resource); } } break; } default: break; } return Status.OK_STATUS; } void perform(int kind, List<IResource> resources) { this.kind = kind; this.resources = resources; schedule(); } } private final class ResourceListener implements IResourceChangeListener { @Override public void resourceChanged(IResourceChangeEvent event) { IResourceDelta delta = event.getDelta(); final List<IResource> added = new ArrayList<IResource>(); final List<IResource> removed = new ArrayList<IResource>(); try { delta.accept(new IResourceDeltaVisitor() { @Override public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); if (resource != null) { if (delta.getKind() == IResourceDelta.ADDED) { added.add(resource); } else if (delta.getKind() == IResourceDelta.REMOVED) { removed.add(resource); } } return true; } }, IResourceDelta.ADDED | IResourceDelta.REMOVED); if (!added.isEmpty()) { // Update in a separate job (new UpdaterJob()).perform(UpdaterJob.ADDITION, added); } if (!removed.isEmpty()) { // Update in a separate job (new UpdaterJob()).perform(UpdaterJob.REMOVAL, removed); } } catch (CoreException e) { Logger.logException(e); } } } private final Set<IResource> cache = new HashSet<IResource>(); void startup() { // Perform initialization in a separate job. (new StartupJob()).schedule(); ResourcesPlugin.getWorkspace().addResourceChangeListener(new ResourceListener(), IResourceChangeEvent.POST_CHANGE); } private synchronized void initialize() { try { ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { ResourceAttributes attributes = resource.getResourceAttributes(); if (attributes != null && attributes.isSymbolicLink()) { cache.add(resource); } return true; } }); } catch (CoreException e) { Logger.logException(e); } } private synchronized void addResource(IResource resource) { cache.add(resource); } private synchronized void removeResource(IResource resource) { cache.remove(resource); } /** * Checks if given resource is a symbolic link in OS file system. * * @param resource * @return <code>true</code> if given resource is symbolic link, * <code>false</code> otherwise */ public synchronized boolean isSymbolicLink(IResource resource) { return cache.contains(resource); } }