blob: e8b88bfc488694143346ee6fa5dd1e70dbce86f2 [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;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.rephraserengine.core.util.Pair;
import org.eclipse.rephraserengine.core.vpg.eclipse.EclipseVPG;
/**
* Base class for a Virtual Program Graph. <a href="../../../overview-summary.html#VPG">More
* Information</a>
* <p>
* This class may be subclassed directly, although in an Eclipse environment, clients will usually
* subclass {@link EclipseVPG} instead.
* <p>
* <i>Requirements/assumptions:</i>
* <ul>
* <li>Transient ASTs <b>require</b> the AST to have bi-directional pointers. Otherwise, the portion
* of the tree above the acquired token can be garbage collected!
* </ul>
*
* @author Jeff Overbey
*
* @param <A> AST type
* @param <T> token type
* @param <R> {@link IVPGNode}/{@link NodeRef} type
*
* @since 1.0
*/
public abstract class VPG<A, T, R extends IVPGNode<T>>
{
/** The VPG component factory, which creates the database, log, etc. for this VPG. */
private final IVPGComponentFactory<A, T, R> factory;
/** The VPG writer, which populates the dependencies, edges, and annotations in this VPG. */
private final VPGWriter<A, T, R> vpgWriter;
/** The AST cache, which provides access to ASTs and determines which files' ASTs are in memory. */
private final ASTRepository<A> astCache;
/** The VPG database, which stores and persists the VPG's dependencies, edges, and annotations. */
private final DemandDB<A, T, R> db;
/** The VPG error/warning log. */
private final VPGLog<T, R> log;
///////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////
/** @since 3.0 */
protected VPG(IVPGComponentFactory<A, T, R> factory)
{
this(factory, 5);
}
/** @since 3.0 */
protected VPG(IVPGComponentFactory<A, T, R> factory, int transientASTCacheSize)
{
assert transientASTCacheSize > 0;
this.factory = factory;
this.log = factory.createLog();
this.db = new DemandDB<A,T,R>(factory.createDatabase(log));
this.vpgWriter = factory.createVPGWriter(db, log);
db.setContentProvider(vpgWriter);
this.astCache = new ASTRepository<A>(transientASTCacheSize);
}
////////////////////////////////////////////////////////////////////////////
// ACCESSORS
////////////////////////////////////////////////////////////////////////////
/** @since 3.0 */
@SuppressWarnings("unchecked")
public <W extends VPGWriter<A, T, R>> W getVPGWriter()
{
return (W)vpgWriter;
}
/** @since 3.0 */
public VPGLog<T, R> getLog()
{
return log;
}
/*package*/ VPGDB<A, T, R> getDB()
{
return db;
}
////////////////////////////////////////////////////////////////////////////
// CALLBACK: AST CONSTRUCTION
////////////////////////////////////////////////////////////////////////////
/**
* Parses the given file.
* @param filename (non-null)
* @return an AST for the given file, or <code>null</code> if an error was encountered
*/
protected abstract A parse(String filename);
////////////////////////////////////////////////////////////////////////////
// API: AST ACQUISITION/RELEASE
////////////////////////////////////////////////////////////////////////////
/** @return an AST for the given file which will be garbage collected after
* no pointers to any of its nodes remain.
*/
public final A acquireTransientAST(String filename)
{
return astCache.acquireTransientAST(filename, false, this);
}
/** @return an AST for the given file. The AST will remain in memory until it is
* explicitly released using {@link #releaseAST(String)} or {@link #releaseAllASTs()}.
*/
public final A acquirePermanentAST(String filename)
{
return astCache.acquirePermanentAST(filename, this);
}
/**
* Changes the AST for the given file from a transient AST to a permanent
* AST. The AST will remain in memory until it is explicitly released
* using {@link #releaseAST(String)} or {@link #releaseAllASTs()}.
*
* @since 2.0
*/
public final A makeTransientASTPermanent(String filename)
{
return astCache.makeTransientASTPermanent(filename, this);
}
/** Changes the AST for the given file from a transient AST to a permanent
* AST. The AST will remain in memory until it is explicitly released
* using {@link #releaseAST(String)} or {@link #releaseAllASTs()}.
*/
public final A makeTransientASTPermanent(String filename, A ast)
{
return astCache.makeTransientASTPermanent(filename, ast);
}
/** Releases the AST for the given file, regardless of whether it was
* acquired as a permanent or transient AST. */
public final void releaseAST(String filename)
{
astCache.releaseAST(filename);
}
/**
* Releases all ASTs, regardless of whether they were acquired as
* transient and permanent ASTs.
*
* @see #acquireTransientAST(String)
* @see #acquirePermanentAST(String)
* @see #makeTransientASTPermanent(String)
*/
public final void releaseAllASTs()
{
astCache.releaseAllASTs();
}
/**
* If the given AST was acquired using {@link #acquireTransientAST(String)} or
* {@link #acquirePermanentAST(String)}, returns the filename to which it
* corresponds. If it is not the root of an AST acquired from this VPG, returns
* <code>null</code>.
* <p>
* Note that this is not an O(1) operation: Internally, the filename is found
* by comparing the given argument with every AST in memory.
*
* @return filename or <code>null</code>
*
* @since 2.0
*/
public final String getFilenameCorrespondingTo(A ast)
{
return astCache.getFilenameCorrespondingTo(ast);
}
////////////////////////////////////////////////////////////////////////////
// API: VPG NODE ACCESS
////////////////////////////////////////////////////////////////////////////
/**
* @return a TokenRef referring to the token with the given position in the given file.
*
* @since 3.0
*/
public final R getVPGNode(String filename, int offset, int length)
{
return factory.getVPGNode(filename, offset, length);
}
////////////////////////////////////////////////////////////////////////////
// API: DEPENDENCIES
////////////////////////////////////////////////////////////////////////////
/**
* @return the name of every file on which at least one other file is dependent.
* @since 3.0
*/
public Iterable<String> listAllFilenamesWithDependents()
{
return db.listAllFilenamesWithDependents();
}
/**
* @return the name of every file which depends on at least one other file.
* @since 3.0
*/
public Iterable<String> listAllDependentFilenames()
{
return db.listAllDependentFilenames();
}
/**
* @return all of the files on which the given file depends
* @since 3.0
*/
public Iterable<String> getOutgoingDependenciesFrom(String filename)
{
return db.getOutgoingDependenciesFrom(filename);
}
/**
* @return all of the files dependent on the given file
* @since 3.0
*/
public Iterable<String> getIncomingDependenciesTo(String filename)
{
return db.getIncomingDependenciesTo(filename);
}
/** @since 3.0 */
public List<String> sortFilesAccordingToDependencies(List<String> files)
{
return db.sortFilesAccordingToDependencies(files);
}
// public boolean checkForCircularDependencies(String filename)
// {
// throw new UnsupportedOperationException();
// }
////////////////////////////////////////////////////////////////////////////
// API: EDGES
////////////////////////////////////////////////////////////////////////////
/**
* Returns a list of all of the edges with at least one endpoint in the given file.
* <p>
* Due to implementation details, some edges may be listed more than once.
*
* @since 3.0
*/
public Iterable<? extends VPGEdge<A, T, R>> getAllEdgesFor(String filename)
{
return db.getAllEdgesFor(filename);
}
////////////////////////////////////////////////////////////////////////////
// API: ANNOTATIONS
////////////////////////////////////////////////////////////////////////////
/**
* Returns a list of all of the annotations in the given file.
* <p>
* The first entry of each pair is a {@link IVPGNode}, and the second is an annotation type.
* The annotation can be retrieved using {@link VPGDB#getAnnotation(VPGNode, int)}.
* <p>
* Due to implementation details, some annotations may be listed more than once.
*
* @since 3.0
*/
public Iterable<Pair<R, Integer>> getAllAnnotationsFor(String filename)
{
return db.getAllAnnotationsFor(filename);
}
////////////////////////////////////////////////////////////////////////////
// PARSER/AST METHODS
////////////////////////////////////////////////////////////////////////////
/** Forces the database to be updated based on the current in-memory AST for the given file. */
public void commitChangesFromInMemoryASTs(IProgressMonitor pm, int ticks, String... filenames)
{
List<String> files = new ArrayList<String>(Arrays.asList(filenames));
files = sortFilesAccordingToDependencies(files); //, pm);
pm = new SubProgressMonitor(pm, ticks, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
pm.beginTask(Messages.VPG_PostTransformAnalysis, files.size());
for (String thisFile : files)
{
if (!isVirtualFile(thisFile))
{
pm.subTask(lastSegmentOfFilename(thisFile));
vpgWriter.computeEdgesAndAnnotationsFromModifiedAST(thisFile, acquireTransientAST(thisFile));
}
pm.worked(1);
}
pm.done();
}
public static String lastSegmentOfFilename(String filename)
{
if (filename == null) return ""; //$NON-NLS-1$
int lastSlash = filename.lastIndexOf('/');
int lastBackslash = filename.lastIndexOf('\\');
if (lastSlash < 0 && lastBackslash < 0)
return filename;
else
return filename.substring(Math.max(lastSlash + 1, lastBackslash + 1));
}
/**
* Returns the source code for the given AST (which may have been modified).
* <p>
* In preservation-based refactorings, if a transformation will cause an error
* in the modified source code, this is used in the refactoring wizard to display
* the modified source code and highlight the erroroneous region when the error
* list is displayed. If preservation-based refactorings are not being implemented,
* usually it is not necessary to override this method.
*
* @return source code for the given AST (which may have been modified), or <code>null</code>
* if this capability is not supported.
*/
public String getSourceCodeFromAST(A ast)
{
return null;
}
///////////////////////////////////////////////////////////////////////////
// Files & Resource Filtering
///////////////////////////////////////////////////////////////////////////
/**
* @return all filenames present in the VPG database.
* @since 3.0
*/
public Iterable<String> listAllFilenames()
{
return db.listAllFilenames();
}
/** @since 3.0 */
public boolean isOutOfDate(String filename)
{
return db.isOutOfDate(filename);
}
/**
* Returns <code>true</code> iff the given filename refers to a virtual file, i.e., a symbolic
* name that does not represent an actual file on disk.
* @param filename (non-null)
* @return <code>true</code> iff the given filename refers to a virtual file
*/
public boolean isVirtualFile(String filename)
{
return false;
}
/**
* @return true iff the given file should be parsed
* @since 3.0
*/
public boolean shouldProcessFile(String filename)
{
return !isVirtualFile(filename);
}
////////////////////////////////////////////////////////////////////////////
// HYPOTHETICAL UPDATING
////////////////////////////////////////////////////////////////////////////
/** @since 3.0 */
public void enterHypotheticalMode() throws IOException
{
db.enterHypotheticalMode();
}
/** @since 3.0 */
public void leaveHypotheticalMode() throws IOException
{
db.leaveHypotheticalMode();
}
/** @since 3.0 */
public boolean isInHypotheticalMode()
{
return db.isInHypotheticalMode();
}
////////////////////////////////////////////////////////////////////////////
// API: INDEXING
////////////////////////////////////////////////////////////////////////////
/** Recomputes the edges and annotations for the given file, regardless
* of whether or not the VPG database entries for that file are
* out of date.
*/
public final void forceRecomputationOfDependencies(String filename)
{
vpgWriter.computeDependencies(filename);
}
/** Recomputes the edges and annotations for the given file, regardless
* of whether or not the VPG database entries for that file are
* out of date.
*/
public final void forceRecomputationOfEdgesAndAnnotations(String filename)
{
releaseAST(filename);
astCache.acquireTransientAST(filename, true, this);
}
/**
* Callback method invoked by {@link EclipseVPG} when it detects that a file has been deleted
* from the filesystem.
* <p>
* Typically, implementors should respond by deleting all information (dependencies, edges, and
* annotations) for the file, since it no longer exists.
*
* @param filename path to the deleted file
*
* @since 3.0
*/
public void deleteAllEntriesFor(String filename)
{
log.clearEntriesFor(filename);
db.deleteAllEntriesFor(filename);
}
////////////////////////////////////////////////////////////////////////////
// UTILITY METHODS - DATABASE
////////////////////////////////////////////////////////////////////////////
/** @since 3.0 */
public void resetDatabaseStatistics()
{
db.resetStatistics();
}
/** @since 3.0 */
public void printDatabaseStatisticsOn(PrintStream out)
{
db.printStatisticsOn(out);
}
/** @since 3.0 */
public void printDatabaseOn(PrintStream out)
{
db.printOn(out);
}
/** @since 3.0 */
public void clearDatabase()
{
db.clearDatabase();
}
/**
* Forces any in-memory data to be flushed to disk
* @since 3.0
*/
public void flushDatabase()
{
db.flush();
}
/** Called when the database is no longer needed. Typically ensures that
* any data in memory is flushed to disk and any locks are released.
*/
public void close()
{
db.close();
}
////////////////////////////////////////////////////////////////////////////
// UTILITY METHODS - DESCRIPTION/DEBUGGING
////////////////////////////////////////////////////////////////////////////
public String describeEdgeType(int edgeType)
{
return Messages.bind(Messages.VPG_EdgeOfType, edgeType);
}
public String describeAnnotationType(int annotationType)
{
return Messages.bind(Messages.VPG_AnnotationOfType, annotationType);
}
/**
*
* @param message
* @param filename (possibly <code>null</code>
*
* @since 3.0
*/
public void debug(String message, String filename)
{
}
}