blob: 8a031e020886c39cfd51ccda486255d272aac4de [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.db.cdt;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.rephraserengine.core.vpg.TokenRef;
import org.eclipse.rephraserengine.core.vpg.VPG;
import org.eclipse.rephraserengine.core.vpg.VPGDB;
import org.eclipse.rephraserengine.core.vpg.VPGDependency;
import org.eclipse.rephraserengine.core.vpg.VPGEdge;
import org.eclipse.rephraserengine.core.vpg.VPGLog;
import org.eclipse.rephraserengine.internal.core.vpg.db.cdt.InternalCDTDB;
import org.eclipse.rephraserengine.internal.core.vpg.db.cdt.InternalCDTDB.IntVector;
/**
* Base class for a typical Virtual Program Graph with a database maintained on disk using the
* Eclipse C/C++ Development Tool's internal indexer database.
* <p>
* This class is intended to be subclassed directly, although Eclipse-based VPGs will subclass
* <code>EclipseVPG</code> instead.
* Subclasses must implement the language-specific (parser/AST) methods.
* <p>
* N.B. You <i>must</i> call {@link #close()} to flush the database to disk.
* <p>
* N.B. See several important notes in the JavaDoc for {@link VPG}.
*
* @author Jeff Overbey
*
* @param <A> AST type
* @param <T> token type
*/
public abstract class CDTDB<A, T, R extends TokenRef<T>, L extends VPGLog<T, R>> extends VPGDB<A, T, R, L>
{
private final InternalCDTDB db;
public CDTDB(String filename)
{
this(new File(filename));
}
public CDTDB(File file)
{
super();
Assert.isNotNull(file);
try
{
db = new InternalCDTDB(file);
}
catch (CoreException e)
{
throw new Error("Unable to create VPG database " + file.getName(), e);
}
}
////////////////////////////////////////////////////////////////////////////
// VPG DATABASE METHODS
////////////////////////////////////////////////////////////////////////////
public void flush()
{
try
{
db.flush();
}
catch (CoreException e)
{
throw new Error("Unable to flush VPG database to disk", e);
}
}
public void close()
{
try
{
db.close();
}
catch (CoreException e)
{
throw new Error("Unable to close VPG database", e);
}
}
public void clearDatabase()
{
getVPG().log.clear();
try
{
db.clear();
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
// FILES ///////////////////////////////////////////////////////////////////
private int ensureEntryForFile(String filename) throws CoreException
{
return db.files.ensure(filename);
}
@Override public void updateModificationStamp(String filename)
{
try
{
db.files.setModificationStamp(ensureEntryForFile(filename), getModificationStamp(filename));
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public boolean isOutOfDate(String filename)
{
try
{
long storedModificationStamp = db.files.getModificationStamp(ensureEntryForFile(filename));
//System.out.println((storedModificationStamp < getModificationStamp(filename)) + " - " + storedModificationStamp + " " + getModificationStamp(filename) + " - " + filename);
return storedModificationStamp < getModificationStamp(filename);
}
catch (CoreException e)
{
return true;
}
}
protected abstract long getModificationStamp(String filename);
/**
* Deletes all dependencies, edges, and token annotations that reference
* a token in the given file
*
* @param filename (non-null)
*/
@Override public void deleteAllEntriesFor(String filename)
{
try
{
db.dependencies.deleteAllIncomingDependenciesTo(filename);
db.dependencies.deleteAllOutgoingDependenciesFrom(filename);
db.edges.deleteAllIncomingEdgesTo(filename);
db.edges.deleteAllOutgoingEdgesFrom(filename);
db.annotations.deleteAllAnnotationsFor(filename);
db.files.delete(filename);
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public void deleteAllEdgesAndAnnotationsFor(String filename)
{
try
{
db.edges.deleteAllIncomingEdgesTo(filename);
db.edges.deleteAllOutgoingEdgesFrom(filename);
db.annotations.deleteAllAnnotationsFor(filename);
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public void deleteAllIncomingDependenciesFor(String filename)
{
try
{
db.dependencies.deleteAllIncomingDependenciesTo(filename);
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public void deleteAllOutgoingDependenciesFor(String filename)
{
try
{
db.dependencies.deleteAllOutgoingDependenciesFrom(filename);
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
private void cleanUp(String filename)
{
try
{
if (db.dependencies.hasIncomingDependencyRecords(filename)) return;
if (db.dependencies.hasOutgoingDependencyRecords(filename)) return;
if (db.edges.hasIncomingEdges(filename)) return;
if (db.edges.hasOutgoingEdges(filename)) return;
if (db.annotations.hasAnnotations(filename)) return;
db.files.delete(filename);
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public Iterable<String> listAllFilenames()
{
try
{
return db.files.getAllFilenames();
}
catch (CoreException e)
{
getVPG().log.logError(e);
return Collections.<String>emptyList();
}
}
@Override public Iterable<String> listAllFilenamesWithDependents()
{
try
{
return db.dependencies.listAllFilenamesWithDependents();
}
catch (CoreException e)
{
getVPG().log.logError(e);
return Collections.<String>emptyList();
}
}
@Override public Iterable<String> listAllDependentFilenames()
{
try
{
return db.dependencies.listAllDependentFilenames();
}
catch (CoreException e)
{
getVPG().log.logError(e);
return Collections.<String>emptyList();
}
}
// DEPENDENCIES ////////////////////////////////////////////////////////////
@Override public void ensure(VPGDependency<A, T, R> dependency)
{
try
{
db.dependencies.ensure(dependency.getDependentFile(), dependency.getDependsOnFile());
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public void delete(VPGDependency<A, T, R> dependency)
{
try
{
String dependentFile = dependency.getDependentFile();
String dependsOnFile = dependency.getDependsOnFile();
db.dependencies.delete(dependentFile, dependsOnFile);
cleanUp(dependentFile);
cleanUp(dependsOnFile);
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public Iterable<String> getOutgoingDependenciesFrom(String filename)
{
try
{
final IntVector records = db.dependencies.findAllOutgoingDependencyRecordsFrom(filename);
return new Iterable<String>()
{
public Iterator<String> iterator()
{
return new Iterator<String>()
{
private int nextRecord = 0;
private int numRecords = records.size();
public boolean hasNext()
{
return nextRecord < numRecords;
}
public String next()
{
try
{
return db.files.getFilename(db.dependencies.getDependsOnFileRecordPtr(records.get(nextRecord++))).getString();
}
catch (CoreException e)
{
getVPG().log.logError(e);
throw new Error(e);
}
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
catch (CoreException e)
{
getVPG().log.logError(e);
return new LinkedList<String>();
}
}
@Override public Iterable<String> getIncomingDependenciesTo(String filename)
{
try
{
final IntVector records = db.dependencies.findAllIncomingDependencyRecordsTo(filename);
return new Iterable<String>()
{
public Iterator<String> iterator()
{
return new Iterator<String>()
{
private int nextRecord = 0;
private int numRecords = records.size();
public boolean hasNext()
{
return nextRecord < numRecords;
}
public String next()
{
try
{
return db.files.getFilename(db.dependencies.getDependentFileRecordPtr(records.get(nextRecord++))).getString();
}
catch (CoreException e)
{
getVPG().log.logError(e);
throw new Error(e);
}
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
catch (CoreException e)
{
getVPG().log.logError(e);
return new LinkedList<String>();
}
}
// EDGES ///////////////////////////////////////////////////////////////////
@Override public void ensure(VPGEdge<A, T, R> edge)
{
try
{
R source = edge.getSource();
R sink = edge.getSink();
db.edges.ensure(source.getFilename(), source.getOffset(), source.getLength(), sink.getFilename(), sink.getOffset(), sink.getLength(), edge.getType());
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public void delete(VPGEdge<A, T, R> edge)
{
try
{
R source = edge.getSource();
R sink = edge.getSink();
db.edges.delete(source.getFilename(), source.getOffset(), source.getLength(), sink.getFilename(), sink.getOffset(), sink.getLength(), edge.getType());
cleanUp(source.getFilename());
cleanUp(sink.getFilename());
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public Iterable<? extends VPGEdge<A, T, R>> getAllEdgesFor(String filename)
{
try
{
final IntVector inRecords = db.edges.findAllIncomingEdgeRecordsTo(filename);
final IntVector outRecords = db.edges.findAllOutgoingEdgeRecordsFrom(filename);
return new Iterable<VPGEdge<A, T, R>>()
{
public Iterator<VPGEdge<A, T, R>> iterator()
{
return new Iterator<VPGEdge<A, T, R>>()
{
private int nextRecord = 0;
private int numRecords = inRecords.size() + outRecords.size();
public boolean hasNext()
{
return nextRecord < numRecords;
}
public VPGEdge<A, T, R> next()
{
try
{
IntVector records = (nextRecord < inRecords.size() ? inRecords : outRecords);
int recordNum = (nextRecord < inRecords.size() ? nextRecord : nextRecord-inRecords.size());
R fromRef = getVPG().createTokenRef(db.files.getFilename(db.edges.getFromFileRecordPtr(records.get(recordNum))).getString(), db.edges.getFromOffset(records.get(recordNum)), db.edges.getFromLength(records.get(recordNum)));
R toRef = getVPG().createTokenRef(db.files.getFilename(db.edges.getToFileRecordPtr(records.get(recordNum))).getString(), db.edges.getToOffset(records.get(recordNum)), db.edges.getToLength(records.get(recordNum)));
VPGEdge<A, T, R> result = getVPG().createEdge(fromRef, toRef, db.edges.getEdgeType(records.get(recordNum)));
nextRecord++;
return result;
}
catch (CoreException e)
{
getVPG().log.logError(e);
throw new Error(e);
}
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
catch (CoreException e)
{
getVPG().log.logError(e);
return new LinkedList<VPGEdge<A, T, R>>();
}
}
@Override public Iterable<VPGEdge<A, T, R>> getOutgoingEdgesFrom(R tokenRef, int edgeType)
{
try
{
final IntVector records =
edgeType == ALL_EDGES
? db.edges.findAllOutgoingEdgeRecordsFrom(tokenRef.getFilename(), tokenRef.getOffset(), tokenRef.getLength())
: db.edges.findAllOutgoingEdgeRecordsFrom(tokenRef.getFilename(), tokenRef.getOffset(), tokenRef.getLength(), edgeType);
return new Iterable<VPGEdge<A, T, R>>()
{
public Iterator<VPGEdge<A, T, R>> iterator()
{
return new Iterator<VPGEdge<A, T, R>>()
{
private int nextRecord = 0;
private int numRecords = records.size();
public boolean hasNext()
{
return nextRecord < numRecords;
}
public VPGEdge<A, T, R> next()
{
try
{
R fromRef = getVPG().createTokenRef(db.files.getFilename(db.edges.getFromFileRecordPtr(records.get(nextRecord))).getString(), db.edges.getFromOffset(records.get(nextRecord)), db.edges.getFromLength(records.get(nextRecord)));
R toRef = getVPG().createTokenRef(db.files.getFilename(db.edges.getToFileRecordPtr(records.get(nextRecord))).getString(), db.edges.getToOffset(records.get(nextRecord)), db.edges.getToLength(records.get(nextRecord)));
return getVPG().createEdge(fromRef, toRef, db.edges.getEdgeType(records.get(nextRecord++)));
}
catch (CoreException e)
{
getVPG().log.logError(e);
throw new Error(e);
}
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
catch (CoreException e)
{
getVPG().log.logError(e);
return new LinkedList<VPGEdge<A, T, R>>();
}
}
@Override public Iterable<VPGEdge<A, T, R>> getIncomingEdgesTo(R tokenRef, int edgeType)
{
try
{
final IntVector records =
edgeType == ALL_EDGES
? db.edges.findAllIncomingEdgeRecordsTo(tokenRef.getFilename(), tokenRef.getOffset(), tokenRef.getLength())
: db.edges.findAllIncomingEdgeRecordsTo(tokenRef.getFilename(), tokenRef.getOffset(), tokenRef.getLength(), edgeType);
return new Iterable<VPGEdge<A, T, R>>()
{
public Iterator<VPGEdge<A, T, R>> iterator()
{
return new Iterator<VPGEdge<A, T, R>>()
{
private int nextRecord = 0;
private int numRecords = records.size();
public boolean hasNext()
{
return nextRecord < numRecords;
}
public VPGEdge<A, T, R> next()
{
try
{
R fromRef = getVPG().createTokenRef(db.files.getFilename(db.edges.getFromFileRecordPtr(records.get(nextRecord))).getString(), db.edges.getFromOffset(records.get(nextRecord)), db.edges.getFromLength(records.get(nextRecord)));
R toRef = getVPG().createTokenRef(db.files.getFilename(db.edges.getToFileRecordPtr(records.get(nextRecord))).getString(), db.edges.getToOffset(records.get(nextRecord)), db.edges.getToLength(records.get(nextRecord)));
return getVPG().createEdge(fromRef, toRef, db.edges.getEdgeType(records.get(nextRecord++)));
}
catch (CoreException e)
{
getVPG().log.logError(e);
throw new Error(e);
}
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
catch (CoreException e)
{
getVPG().log.logError(e);
return new LinkedList<VPGEdge<A, T, R>>();
}
}
// ANNOTATIONS /////////////////////////////////////////////////////////////
@Override public void setAnnotation(R token, int annotationID, Serializable annotation)
{
try
{
db.annotations.set(token.getFilename(), token.getOffset(), token.getLength(), annotationID, serialize(annotation));
}
catch (Exception e)
{
getVPG().log.logError(e);
}
}
@Override public void deleteAnnotation(R token, int annotationID)
{
try
{
db.annotations.delete(token.getFilename(), token.getOffset(), token.getLength(), annotationID);
cleanUp(token.getFilename());
}
catch (CoreException e)
{
getVPG().log.logError(e);
}
}
@Override public Serializable getAnnotation(R token, int annotationID)
{
try
{
int record = db.annotations.findRecordFor(token.getFilename(), token.getOffset(), token.getLength(), annotationID);
if (record < 0)
return null;
else
return deserialize(db.annotations.getAnnotation(record));
}
catch (Exception e)
{
getVPG().log.logError(e);
return null;
}
}
/**
* Subclasses should implement this as
* <pre>
* ByteArrayOutputStream out = new ByteArrayOutputStream();
* new ObjectOutputStream(out).writeObject(annotation);
* return out.toByteArray();
* </pre>
* or a more efficient variant.
* <p>
* This is delegated to subclasses to complement <code>deserialize</code>.
*/
protected abstract byte[] serialize(Serializable annotation) throws IOException;
/**
* Subclasses should implement this as
* <pre>
* return (Serializable)new ObjectInputStream(binaryStream).readObject();
* </pre>
* <p>
* This must be implemented in the subclass, since the serialized (annotation) classes may not be in the
* classpath of the VPG plug-in, causing <code>ClassNotFoundException</code>s.
*/
protected abstract Serializable deserialize(InputStream binaryStream) throws IOException, ClassNotFoundException;
////////////////////////////////////////////////////////////////////////////
public void printOn(PrintStream out)
{
try
{
out.println("MODIFICATION STAMPS:");
out.println();
out.println(db.files.toString());
out.println();
out.println();
out.println("DEPENDENCIES:");
out.println();
out.println(db.dependencies.toString());
out.println();
out.println();
out.println("EDGES:");
out.println();
out.println(db.edges.toString());
out.println();
out.println();
out.println("ANNOTATIONS:");
out.println();
try
{
out.println(db.annotations.toString());
}
catch (Exception e)
{
getVPG().log.logError(e);
}
}
catch (Exception e)
{
out.print(e.getMessage());
e.printStackTrace(out);
}
}
public void printStatisticsOn(PrintStream out)
{
out.println("(No statistics available)");
}
public void resetStatistics()
{
// Nothing to do
}
}