blob: 5115e44d860887ec81a04030ddcfe695474809b6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 University of Illinois at Urbana-Champaign 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:
* UIUC - Initial API and implementation
*******************************************************************************/
package org.eclipse.rephraserengine.core.vpg.eclipse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
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.IResourceVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
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.MultiRule;
import org.eclipse.rephraserengine.core.vpg.TokenRef;
import org.eclipse.rephraserengine.core.vpg.VPG;
import org.eclipse.rephraserengine.core.vpg.VPGDB;
/**
* A virtual program graph for use in an Eclipse environment.
* <a href="../../../../overview-summary.html#Eclipse">More Information</a>
* <p>
* This class is intended to be subclassed directly.
*
* @author Jeff Overbey
*/
public abstract class EclipseVPG<A, T, R extends TokenRef<T>, D extends VPGDB<A, T, R, L>, L extends EclipseVPGLog<T, R>>
extends VPG<A, T, R, D, L>
{
private String syncMessage;
public EclipseVPG(L log, D database, String syncMessage, int transientASTCacheSize)
{
super(log, database, transientASTCacheSize);
this.syncMessage = syncMessage;
}
public EclipseVPG(L log, D database, String syncMessage)
{
super(log, database);
this.syncMessage = syncMessage;
}
/** Enqueues a job to make sure the VPG is up-to-date with the
* workspace, and instructs it to automatically update itself
* when files are added, changed, or deleted in the workspace.
*/
public void start()
{
log.readLogFromFile();
// Now listen for changes to workspace resources
ResourcesPlugin.getWorkspace().addResourceChangeListener(new VPGResourceChangeListener(), IResourceChangeEvent.POST_CHANGE);
// The C/C++ Development Tool (see org.eclipse.cdt.internal.core.model.CModelManager) handles
// IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE
// and also executes
// Platform.getContentTypeManager().addContentTypeChangeListener(this);
queueJobToEnsureVPGIsUpToDate();
}
///////////////////////////////////////////////////////////////////////////
// Resource Visitor
///////////////////////////////////////////////////////////////////////////
public WorkspaceJob queueJobToEnsureVPGIsUpToDate()
{
WorkspaceJob job = new VPGInitialWorkspaceSyncJob(syncMessage);
job.setRule(MultiRule.combine(VPGSchedulingRule.getInstance(),
ResourcesPlugin.getWorkspace().getRoot()));
job.schedule();
return job;
}
private final class VPGInitialWorkspaceSyncJob extends VPGJob<A, T>
{
private VPGInitialWorkspaceSyncJob(String name)
{
super(name);
}
@Override public IStatus runInWorkspace(final IProgressMonitor monitor)
{
try
{
monitor.beginTask("Indexing", IProgressMonitor.UNKNOWN);
return ensureVPGIsUpToDate(monitor);
}
finally
{
monitor.done();
}
}
}
/** Ensures that the VPG database contains up-to-date information for all of
* the resources in the workspace.
*/
public IStatus ensureVPGIsUpToDate(IProgressMonitor monitor)
{
try
{
WorkspaceSyncResourceVisitor visitor = new WorkspaceSyncResourceVisitor();
collectFilesToIndex(visitor, monitor);
visitor.calculateDependencies(monitor);
visitor.index(monitor);
return Status.OK_STATUS;
}
catch (CoreException e)
{
return e.getStatus();
}
}
private void collectFilesToIndex(WorkspaceSyncResourceVisitor visitor, IProgressMonitor monitor) throws CoreException
{
monitor.subTask("Searching for workspace modifications...");
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
workspaceRoot.accept(visitor); // Collect list of files to index
}
private final class WorkspaceSyncResourceVisitor implements IResourceVisitor
{
private final ArrayList<String> files;
private WorkspaceSyncResourceVisitor()
{
this.files = new ArrayList<String>(1024);
}
public boolean visit(IResource resource)
{
try
{
if (!resource.isAccessible()) return false;
if (resource instanceof IProject && !shouldProcessProject((IProject)resource)) return false;
if (resource instanceof IFile && shouldProcessFile((IFile)resource))
files.add(EclipseVPG.getFilenameForIFile((IFile)resource));
}
catch (Exception e)
{
log.logError(e);
}
return !(resource instanceof IFile);
}
public void calculateDependencies(IProgressMonitor monitor)
{
ArrayList<String> queue = files;
int completed = 0, total = queue.size();
for (String filename : queue)
{
if (monitor.isCanceled()) throw new OperationCanceledException();
monitor.subTask(filename + " (calculating dependencies - file " + (++completed) + " of " + total + ")");
calculateDependenciesIfNotUpToDate(filename);
}
}
public void index(IProgressMonitor monitor)
{
List<String> queue = sortFilesAccordingToDependencies(files, monitor);
int completed = 0, total = countFilesInQueue(queue);
for (String filename : queue)
{
if (monitor.isCanceled()) throw new OperationCanceledException();
if (shouldListFileInIndexerProgressMessages(filename))
monitor.subTask(filename + " (" + (++completed) + " of " + total + ")");
indexIfNotUpToDate(filename);
}
}
}
private int countFilesInQueue(List<String> queue)
{
int total = 0;
for (String filename : queue)
if (shouldListFileInIndexerProgressMessages(filename))
total++;
return total;
}
protected boolean shouldListFileInIndexerProgressMessages(String filename) {
return !isVirtualFile(filename);
}
//public ArrayList<String> sortFilesAccordingToDependencies(final ArrayList<String> files, final IProgressMonitor monitor)
///////////////////////////////////////////////////////////////////////////
// Resource Change Listener
///////////////////////////////////////////////////////////////////////////
private final class VPGResourceChangeListener implements IResourceChangeListener
{
public void resourceChanged(IResourceChangeEvent event)
{
if (!(event.getSource() instanceof IWorkspace)) return;
if (event.getType() != IResourceChangeEvent.POST_CHANGE) return;
final IResourceDelta delta = event.getDelta();
if (delta == null) return;
new VPGResourceDeltaJob(syncMessage, delta).schedule();
}
}
private final class VPGResourceDeltaJob extends VPGJob<A, T>
{
private final IResourceDelta delta;
private VPGResourceDeltaJob(String name, IResourceDelta delta)
{
super(name);
this.delta = delta;
}
@Override public IStatus runInWorkspace(final IProgressMonitor monitor)
{
try
{
monitor.beginTask("Indexing", IProgressMonitor.UNKNOWN);
// Re-index or delete entries for files when they are added/changed or deleted, respectively
VPGResourceDeltaVisitor visitor = new VPGResourceDeltaVisitor();
monitor.subTask("Searching for workspace modifications...");
delta.accept(visitor); // Collect files to index
visitor.calculateDependencies(monitor);
visitor.index(monitor);
return Status.OK_STATUS;
}
catch (CoreException e)
{
return e.getStatus();
}
finally
{
monitor.done();
}
}
}
private final class VPGResourceDeltaVisitor implements IResourceDeltaVisitor
{
private ArrayList<String> files;
private HashMap<String, Boolean> forceReindex;
public VPGResourceDeltaVisitor()
{
this.files = new ArrayList<String>(256);
this.forceReindex = new HashMap<String, Boolean>(256);
}
public boolean visit(IResourceDelta delta)
{
try
{
IResource resource = delta.getResource();
if (!(resource instanceof IFile))
{
if (resource instanceof IProject)
return shouldProcessProject((IProject)resource);
else
return true;
}
IFile file = (IFile)resource;
if (!shouldProcessFile(file)) return true;
String filename = getFilenameForIFile(file);
switch (delta.getKind())
{
case IResourceDelta.ADDED:
debug("Resource Delta: Add", filename);
files.add(filename);
forceReindex.put(filename, true); // Was false
break;
case IResourceDelta.CHANGED:
debug("Resource Delta: Change", filename);
if ((delta.getFlags() & (IResourceDelta.CONTENT|IResourceDelta.REPLACED)) != 0)
{
files.add(filename);
forceReindex.put(filename, true);
}
break;
case IResourceDelta.REMOVED:
debug("Resource Delta: Remove", filename);
log.clearEntriesFor(filename);
db.deleteAllEntriesFor(filename);
break;
}
}
catch (Exception e)
{
log.logError(e);
}
return true;
}
public void calculateDependencies(IProgressMonitor monitor)
{
ArrayList<String> queue = files;
int completed = 0, total = queue.size();
for (String filename : queue)
{
if (monitor.isCanceled()) throw new OperationCanceledException();
monitor.subTask(filename + " (" + (++completed) + " of " + total + ")");
Boolean force = forceReindex.get(filename);
if (force == null || force)
forceRecomputationOfDependencies(filename);
else
calculateDependenciesIfNotUpToDate(filename);
}
}
public void index(IProgressMonitor monitor)
{
List<String> queue = sortFilesAccordingToDependencies(files, monitor);
int completed = 0, total = countFilesInQueue(queue);
for (String filename : queue)
{
if (monitor.isCanceled()) throw new OperationCanceledException();
if (shouldListFileInIndexerProgressMessages(filename))
monitor.subTask(filename + " (" + (++completed) + " of " + total + ")");
Boolean force = forceReindex.get(filename);
if (force == null || force)
forceRecomputationOfEdgesAndAnnotations(filename);
else
indexIfNotUpToDate(filename);
}
}
}
/** Updates the VPG database's dependency information for the given file
* if the stored information is out of date
* @param monitor */
protected void calculateDependenciesIfNotUpToDate(String filename)
{
if (db.isOutOfDate(filename))
{
debug("Indexing", filename);
forceRecomputationOfDependencies(filename);
}
else
{
debug("Index is up to date", filename);
}
}
/** Updates the VPG database information for the given file if the stored
* information is out of date
* @param monitor */
protected void indexIfNotUpToDate(String filename)
{
if (db.isOutOfDate(filename))
{
debug("Indexing", filename);
forceRecomputationOfEdgesAndAnnotations(filename);
}
else
{
debug("Index is up to date", filename);
}
}
///////////////////////////////////////////////////////////////////////////
// Abstract Methods (Resource Filtering)
///////////////////////////////////////////////////////////////////////////
/** @return true iff the given file should be parsed */
@Override protected boolean shouldProcessFile(String filename)
{
IFile file = getIFileForFilename(filename);
return file == null ? false : shouldProcessFile(file);
}
/** @return true if the given project should be indexed */
protected abstract boolean shouldProcessProject(IProject project);
/** @return true iff the given file should be indexed */
protected abstract boolean shouldProcessFile(IFile file);
///////////////////////////////////////////////////////////////////////////
// Utility Methods (IFile<->Filename Mapping)
///////////////////////////////////////////////////////////////////////////
public static IFile getIFileForFilename(String filename)
{
IResource resource = getIResourceForFilename(filename);
if (resource instanceof IFile)
return (IFile)resource;
else
return null;
}
public static String getFilenameForIFile(IFile file)
{
return getFilenameForIResource(file);
}
public static IResource getIResourceForFilename(String filename)
{
return ResourcesPlugin.getWorkspace().getRoot().findMember(filename);
}
public static String getFilenameForIResource(IResource resource)
{
if (resource == null)
return null;
else
return resource.getFullPath().toString();
}
///////////////////////////////////////////////////////////////////////////
// IFile-Based AST Acquisition & Release API
///////////////////////////////////////////////////////////////////////////
public A acquireTransientAST(IFile file)
{
return file == null ? null : acquireTransientAST(getFilenameForIFile(file));
}
public A acquirePermanentAST(IFile file)
{
return file == null ? null : acquirePermanentAST(getFilenameForIFile(file));
}
public A makeTransientASTPermanent(IFile file)
{
return file == null ? null : makeTransientASTPermanent(getFilenameForIFile(file));
}
public A makeTransientASTPermanent(IFile file, A ast)
{
return file == null ? null : makeTransientASTPermanent(getFilenameForIFile(file), ast);
}
public void releaseAST(IFile file)
{
if (file != null) releaseAST(getFilenameForIFile(file));
}
public IFile getIFileCorrespondingTo(A ast)
{
String filename = getFilenameCorrespondingTo(ast);
return filename == null ? null : getIFileForFilename(filename);
}
///////////////////////////////////////////////////////////////////////////
// VPG Overrides
///////////////////////////////////////////////////////////////////////////
@Override protected void processingDependent(String filename, String dependentFilename)
{
// if (progressMonitor != null)
// progressMonitor.subTask(filename
// + " - reindexing dependent file "
// + dependentFilename);
super.processingDependent(filename, dependentFilename);
}
/** Forces the database to be updated based on the current in-memory AST for the given file */
public void commitChangesFromInMemoryASTs(IProgressMonitor pm, int ticks, IFile file)
{
commitChangesFromInMemoryASTs(pm, ticks, getFilenameForIFile(file));
}
/** Forces the database to be updated based on the current in-memory ASTs for the given files */
public void commitChangesFromInMemoryASTs(IProgressMonitor pm, int ticks, Set<IFile> files)
{
String[] filenames = new String[files.size()];
int i = 0;
for (IFile file : files)
filenames[i++] = getFilenameForIFile(file);
commitChangesFromInMemoryASTs(pm, ticks, filenames);
}
}