/******************************************************************************* * Copyright (c) 2013 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.springsource.ide.eclipse.commons.quicksearch.core; import java.util.PriorityQueue; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; 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; import org.springsource.ide.eclipse.commons.quicksearch.core.priority.DefaultPriorityFunction; import org.springsource.ide.eclipse.commons.quicksearch.core.priority.PriorityFunction; import org.springsource.ide.eclipse.commons.quicksearch.ui.QuickSearchActivator; /** * A Helper class that allows traversing all the resources in the workspace, assigning priorities * to the resources to decide the ordering and completely ignore some resources. * <p> * The walker can also be paused and resumed. * * @author Kris De Volder */ public abstract class ResourceWalker extends Job { private class QItem implements Comparable<QItem> { public final double priority; public final IResource resource; public QItem(double p, IResource r) { this.priority = p; this.resource = r; } public int compareTo(QItem other) { return Double.compare(other.priority, this.priority); } } public ResourceWalker() { super("QuickSearch"); init(); } protected void init() { queue = new PriorityQueue<ResourceWalker.QItem>(); queue.add(new QItem(0, ResourcesPlugin.getWorkspace().getRoot())); } /** * Queue of work to do. When all work is done this will be set to null. So it * can also be used to determine 'done' status. */ private PriorityQueue<QItem> queue = null; /** * Setting this to true will cause the ResourceWalker to stop walking. If the walker is running * as a scheduled job, then this Job will terminate. However it is possible to 'resume' the * later since pending list of workitems will be retained. */ private boolean suspend = false; private PriorityFunction prioritFun = new DefaultPriorityFunction(); public boolean isDone() { return queue==null; } /** * Request that the walker stops walking at the next reasonable opportunity. */ public void suspend() { this.suspend = true; } /** * Request the walker to be restarted... i.e. begin walking the resource tree from * the initial state. */ /** * Request that the walker be resumed. This clears the 'supsend' state if it is set * and ensures that the Job is scheduled. */ public void resume() { if (isDone()) { //Well... there's no work so don't bother with doing anything. return; } this.suspend = false; this.schedule(); } protected boolean ignore(IResource r) { String name = r.getName(); if (name.startsWith(".")) { return true; } if (name.endsWith(".jar") || name.endsWith(".zip")) { return true; } if (name.equals("bin")) { return true; } return false; } public IStatus run(IProgressMonitor monitor) { //TODO: progress reporting? while (!suspend && queue!=null) { if (monitor.isCanceled()) { queue = null; } else { IResource r = getWork(); if (r!=null) { if (!ignore(r)) { if (r instanceof IFile) { IFile f = (IFile) r; visit(f, monitor); } else if (r instanceof IContainer) { IContainer f = (IContainer) r; if (f.isAccessible()) { try { for (IResource child : f.members()) { enqueue(child); } } catch (CoreException e) { QuickSearchActivator.log(e); } } } } } else { queue = null; } } } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } else { return Status.OK_STATUS; } } /** * Add a resource to the work queue taking account the priority of the resource. */ private void enqueue(IResource child) { PriorityQueue<QItem> q = queue; if (q!=null) { double p = priority(child); if (p==PriorityFunction.PRIORITY_IGNORE) { return; } q.add(new QItem(p, child)); } } protected abstract void visit(IFile r, IProgressMonitor m); /** * Assigns a priority to a given resource. This priority will affect the order in which * resources get visited. Resources to be visited are tracked in a priority queue and * at any time the resource with highest priority number is visited first. * <p> * Note that if a resource is a folder then lowering its priority implicitly reduces * the priority of anything nested inside that folder because to visit the children * one has to first visit the parent to reach them. * <p> * If the priority returned is PRIORITY_IGNORE then the resource will be ignored * completely and not visited at all. * * @param r * @return */ final double priority(IResource r) { return prioritFun.priority(r); } /** * Set the priority function to use to determine walking order. For the function to * take effect, it should be set before walking has started as the function is * used when elements are added to the work queue during the walk. * <p> * Elements already in the work queue are not re-prioritized if a function is set * in 'mid-run'. */ public void setPriorityFun(PriorityFunction f) { Assert.isNotNull(f, "PriorityFunction should never be null"); this.prioritFun = f; } private IResource getWork() { PriorityQueue<QItem> q = queue; if (q!=null && !q.isEmpty()) { return q.remove().resource; } return null; } }