blob: b4a1b815c98f5e0e007f332d9c1b0b4db70cbe57 [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.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.rephraserengine.core.util.Pair;
import org.eclipse.rephraserengine.core.vpg.db.profiling.ProfilingDB;
/**
* A VPG database, which stores dependencies, edges, and annotations.
*
* @author Jeff Overbey
*
* @param <A> AST type
* @param <T> token type
* @param <R> {@link IVPGNode}/{@link NodeRef} type
*/
public abstract class VPGDB<A, T, R extends IVPGNode<T>>
{
///////////////////////////////////////////////////////////////////////////
// Constants
///////////////////////////////////////////////////////////////////////////
/**
* Constant that can be passed to <code>getIncoming/OutgoingEdge...()</code> to
* indicate that edges of all types should be returned.
*/
public static final int ALL_EDGES = Integer.MIN_VALUE;
///////////////////////////////////////////////////////////////////////////
// Fields
///////////////////////////////////////////////////////////////////////////
protected final IVPGComponentFactory<A, T, R> factory;
///////////////////////////////////////////////////////////////////////////
// Constructors
///////////////////////////////////////////////////////////////////////////
public VPGDB(IVPGComponentFactory<A, T, R> factory)
{
this.factory = factory;
}
/**
* Constructor for use by subclasses that wrap another database (e.g., {@link ProfilingDB}.
* <p>
* Rather than providing an explicit {@link IVPGComponentFactory}, this uses the locator from the
* wrapped database.
*/
protected VPGDB(VPGDB<A, T, R> wrappedDB)
{
this.factory = wrappedDB.factory;
}
///////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////
/** Forces any in-memory data to be flushed to disk */
public abstract void 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 abstract void close();
/** Removes ALL data from the database; also clears the error/warning log. */
public abstract void clearDatabase();
// HYPOTHETICAL UPDATING ///////////////////////////////////////////////////
public abstract void enterHypotheticalMode() throws IOException;
public abstract void leaveHypotheticalMode() throws IOException;
public abstract boolean isInHypotheticalMode();
// FILES ///////////////////////////////////////////////////////////////////
/** Marks the VPG database entries for the given file as being up-to-date. */
public abstract void updateModificationStamp(String filename);
/** @return true iff the VPG entries for the given file are not up-to-date */
public abstract boolean isOutOfDate(String filename);
/** Removes all dependencies, edges, and annotations for the given file. */
public abstract void deleteAllEntriesFor(String filename);
/** Removes all edges and annotations (but not dependencies) for the given file. */
public abstract void deleteAllEdgesAndAnnotationsFor(String filename);
/** Removes all edges pointing inward to any token in the given file. */
public abstract void deleteAllIncomingDependenciesFor(String filename);
/** Removes all edges pointing outward from any token in the given file. */
public abstract void deleteAllOutgoingDependenciesFor(String filename);
/** Returns all filenames present in the VPG database. */
public abstract Iterable<String> listAllFilenames();
/** Returns the name of every file on which at least one other file is
* dependent. */
public abstract Iterable<String> listAllFilenamesWithDependents();
/** Returns the name of every file which depends on at least one other
* file. */
public abstract Iterable<String> listAllDependentFilenames();
// DEPENDENCIES ////////////////////////////////////////////////////////////
/** Adds the given dependency to the VPG database if a dependency between
* its files does not already exist. */
public abstract void ensure(VPGDependency<A, T, R> dependency);
/** Deletes the given dependency from the VPG database. */
public abstract void delete(VPGDependency<A, T, R> dependency);
/** @return all of the files on which the given file depends */
public abstract Iterable<String> getOutgoingDependenciesFrom(String filename);
/** @return all of the files dependent on the given file */
public abstract Iterable<String> getIncomingDependenciesTo(String filename);
public List<String> sortFilesAccordingToDependencies(final List<String> files)
{
// Enqueue the reflexive transitive closure of the dependencies
for (int i = 0; i < files.size(); i++)
enqueueNewDependents(files.get(i), files);
// Topological Sort -- from Cormen et al. pp. 550, 541
class DFS
{
final Integer WHITE = 0, GRAY = 1, BLACK = 2;
final int numFiles = files.size();
ArrayList<String> result = new ArrayList<String>(numFiles);
HashMap<String, Integer> color = new HashMap<String, Integer>();
int time;
DFS()
{
for (String filename : files)
color.put(filename, WHITE);
time = 0;
for (String filename : files)
if (color.get(filename) == WHITE)
dfsVisit(filename);
}
private void dfsVisit(String u)
{
color.put(u, GRAY);
time++;
for (String v : getIncomingDependenciesTo(u))
if (color.get(v) == WHITE)
dfsVisit(v);
color.put(u, BLACK);
result.add(0, u);
}
}
return new DFS().result;
}
private void enqueueNewDependents(String filename, List<String> dependents)
{
for (String f : findAllFilesDependentUpon(filename))
if (!dependents.contains(f))
dependents.add(f);
}
private Set<String> findAllFilesDependentUpon(String filename)
{
Set<String> dependents = new HashSet<String>();
for (String dependentFilename : getIncomingDependenciesTo(filename))
dependents.add(dependentFilename);
return dependents;
}
// EDGES ///////////////////////////////////////////////////////////////////
/** Adds the given edge to the VPG database if an edge of the given type
* between its tokens does not already exist. */
public abstract void ensure(VPGEdge<A, T, R> edge);
/** Deletes the given edge from the VPG database. */
public abstract void delete(VPGEdge<A, T, R> edge);
/**
* 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.
*/
public abstract Iterable<? extends VPGEdge<A, T, R>> getAllEdgesFor(String filename);
/**
* Returns a list of the edges extending from the given token.
* <p>
* To only return edges of a particular type, set the <code>edgeType</code>
* parameter to that type.
*
* @param edgeType the type of edge (an arbitrary non-negative integer), or
* {@link VPG#ALL_EDGES} to return all edges, regardless
* of type
*/
public abstract Iterable<? extends VPGEdge<A, T, R>> getOutgoingEdgesFrom(R tokenRef, int edgeType);
/**
* Returns a list of the edges pointing at the given token.
* <p>
* To only return edges of a particular type, set the <code>edgeType</code>
* parameter to that type.
*
* @param edgeType the type of edge (an arbitrary non-negative integer), or
* {@link VPG#ALL_EDGES} to return all edges, regardless
* of type
*/
public abstract Iterable<? extends VPGEdge<A, T, R>> getIncomingEdgesTo(R tokenRef, int edgeType);
/**
* Returns a list of the tokens pointed at by an edge extending from the given token.
* <p>
* To only return edges of a particular type, set the <code>edgeType</code>
* parameter to that type.
*
* @param edgeType the type of edge (an arbitrary non-negative integer), or
* {@link VPG#ALL_EDGES} to process all edges, regardless
* of type
*/
public Iterable<R> getOutgoingEdgeTargets(R tokenRef, int edgeType)
{
return new EdgeIterable<A, T, R>(getOutgoingEdgesFrom(tokenRef, edgeType), false);
}
/**
* Returns a list of the tokens which have an edges pointing at the given token.
* <p>
* To only return edges of a particular type, set the <code>edgeType</code>
* parameter to that type.
*
* @param edgeType the type of edge (an arbitrary non-negative integer), or
* {@link VPG#ALL_EDGES} to process all edges, regardless
* of type
*/
public Iterable<R> getIncomingEdgeSources(R tokenRef, int edgeType)
{
return new EdgeIterable<A, T, R>(getIncomingEdgesTo(tokenRef, edgeType), true);
}
// ANNOTATIONS /////////////////////////////////////////////////////////////
/**
* Annotates the given token with the given (serializable) object
* (which may be <code>null</code>).
* If an annotation for the given token with the given ID already exists,
* it will be replaced.
* <p>
* A token can have several annotations, but each annotation must be given
* a unique ID. For example, annotation 0 might describe
* the type of an identifier, while annotation 1 might hold documentation
* for that identifier.
*/
public abstract void setAnnotation(R token, int annotationID, Serializable annotation);
public void setAnnotation(R token, Enum<?> annotationID, Serializable annotation)
{
setAnnotation(token, annotationID.ordinal(), annotation);
}
/** Deletes the annotation with the given ID for the given token, if it exists. */
public abstract void deleteAnnotation(R token, int annotationID);
public void deleteAnnotation(R token, Enum<?> annotationID)
{
deleteAnnotation(token, annotationID.ordinal());
}
/** @return the annotation with the given ID for the given token, or <code>null</code>
* if it does not exist
*/
public abstract Serializable getAnnotation(R tokenRef, int annotationID);
public Serializable getAnnotation(R tokenRef, Enum<?> annotationID)
{
return getAnnotation(tokenRef, annotationID.ordinal());
}
/**
* 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.
*/
public abstract Iterable<Pair<R, Integer>> getAllAnnotationsFor(String filename);
// UTILITY METHODS /////////////////////////////////////////////////////////
public abstract void printOn(PrintStream out);
public abstract void printStatisticsOn(PrintStream out);
public abstract void resetStatistics();
public String describeEdgeType(int edgeType)
{
return Messages.bind(Messages.VPGDB_EdgeOfType, edgeType);
}
public String describeAnnotationType(int annotationType)
{
return Messages.bind(Messages.VPGDB_AnnotationOfType, annotationType);
}
public String describeToken(String filename, int offset, int length)
{
return Messages.bind(Messages.VPGDB_FilenameOffsetLength,
new Object[] { filename, offset, length });
}
////////////////////////////////////////////////////////////////////////////
// UTILITY CLASSES
////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("hiding")
private class EdgeIterable<A, T, R extends IVPGNode<T>> implements Iterable<R>
{
private Iterable<? extends VPGEdge<A, T, R>> edges;
private boolean returnSources;
EdgeIterable(Iterable<? extends VPGEdge<A, T, R>> edges, boolean returnSources)
{
this.edges = edges;
this.returnSources = returnSources;
}
public Iterator<R> iterator()
{
final Iterator<? extends VPGEdge<A, T, R>> edgeIterator = edges.iterator();
return new Iterator<R>()
{
public boolean hasNext()
{
return edgeIterator.hasNext();
}
public R next()
{
try
{
VPGEdge<A, T, R> edge = edgeIterator.next();
return returnSources ? edge.getSource() : edge.getSink();
}
catch (Exception e)
{
throw new Error(e);
}
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
}
}