blob: f2a6525af6e2aa2886340be0db1455543e33f572 [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.preservation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.ltk.core.refactoring.FileStatusContext;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.rephraserengine.core.util.OffsetLength;
import org.eclipse.rephraserengine.core.vpg.eclipse.EclipseVPG;
import org.eclipse.rephraserengine.internal.core.preservation.Model;
import org.eclipse.rephraserengine.internal.core.preservation.ModelDiff;
import org.eclipse.rephraserengine.internal.core.preservation.PrimitiveOp;
import org.eclipse.rephraserengine.internal.core.preservation.PrimitiveOpList;
import org.eclipse.rephraserengine.internal.core.preservation.ModelDiff.EdgeAdded;
import org.eclipse.rephraserengine.internal.core.preservation.ModelDiff.EdgeDeleted;
import org.eclipse.rephraserengine.internal.core.preservation.ModelDiff.EdgeSinkChanged;
import org.eclipse.rephraserengine.internal.core.preservation.ModelDiff.ModelDiffProcessor;
import org.eclipse.rephraserengine.internal.core.preservation.PrimitiveOp.Alpha;
import org.eclipse.rephraserengine.internal.core.preservation.PrimitiveOp.Epsilon;
import org.eclipse.rephraserengine.internal.core.preservation.PrimitiveOp.Rho;
/**
* Checks for preservation of semantic edges in a program graph modulo a sequence of primitive
* transformations.
*
* @author Jeff Overbey
*
* @since 1.0
*/
@SuppressWarnings("unchecked")
public final class PreservationAnalysis
{
private IAdapterManager adapterManager;
private EclipseVPG vpg;
//private IProgressMonitor progressMonitor;
private Model initialModel;
private PrimitiveOpList primitiveOps;
private Set<PreservationRule> preserveEdgeTypes;
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
IFile file,
PreservationRule... edgeTypes)
{
this(vpg, progressMonitor, ticks, EclipseVPG.getFilenameForIFile(file), edgeTypes);
}
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
Collection<IFile> files,
PreservationRule... edgeTypes)
{
this(vpg, progressMonitor, ticks, getFilenames(files), edgeTypes);
}
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
String filename,
PreservationRule... edgeTypes)
{
this(vpg, progressMonitor, ticks, Collections.singletonList(filename), edgeTypes);
}
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
List<String> filenames,
PreservationRule... edgeTypes)
{
this.adapterManager = Platform.getAdapterManager();
this.vpg = vpg;
//this.progressMonitor = progressMonitor;
this.primitiveOps = new PrimitiveOpList();
this.preserveEdgeTypes = new HashSet<PreservationRule>(edgeTypes.length);
for (PreservationRule type : edgeTypes)
this.preserveEdgeTypes.add(type);
progressMonitor.subTask("Please wait; switching database to hypothetical mode");
ensureDatabaseIsInHypotheticalMode();
this.initialModel = new Model("initial model", progressMonitor, ticks, vpg, filenames);
}
private static List<String> getFilenames(Collection<IFile> files)
{
List<String> result = new ArrayList<String>(files.size());
for (IFile file : files)
result.add(EclipseVPG.getFilenameForIFile(file));
return result;
}
private void ensureDatabaseIsInHypotheticalMode() throws Error
{
if (!vpg.db.isInHypotheticalMode())
{
try
{
vpg.db.enterHypotheticalMode();
}
catch (IOException e)
{
throw new Error(e);
}
}
}
public void markAlpha(IFile file, Object node)
{
String filename = EclipseVPG.getFilenameForIFile(file);
OffsetLength offsetLength = (OffsetLength)adapterManager.getAdapter(node, OffsetLength.class);
if (offsetLength == null)
throw new Error("Unable to get OffsetLength adapter for " + node.getClass().getName());
Alpha alpha = PrimitiveOp.alpha(
filename,
offsetLength.getOffset(),
offsetLength.getPositionPastEnd());
primitiveOps.add(alpha);
adapterManager.getAdapter(node, ResetOffsetLength.class);
}
public void markEpsilon(IFile file, Object node)
{
String filename = EclipseVPG.getFilenameForIFile(file);
OffsetLength offsetLength = (OffsetLength)adapterManager.getAdapter(node, OffsetLength.class);
if (offsetLength == null)
throw new Error("Unable to get OffsetLength adapter for " + node.getClass().getName());
Epsilon epsilon = PrimitiveOp.epsilon(
filename,
offsetLength.getOffset(),
offsetLength.getPositionPastEnd());
primitiveOps.add(epsilon);
}
public void markRho(IFile file, Object node, int oldLength, int newLength)
{
String filename = EclipseVPG.getFilenameForIFile(file);
OffsetLength offsetLength = (OffsetLength)adapterManager.getAdapter(node, OffsetLength.class);
if (offsetLength == null)
throw new Error("Unable to get OffsetLength adapter for " + node.getClass().getName());
Rho rho = PrimitiveOp.rho(
filename,
offsetLength.getOffset(),
oldLength,
newLength);
primitiveOps.add(rho);
}
@Override public String toString()
{
return primitiveOps.toString();
}
public void checkForPreservation(
RefactoringStatus status,
IProgressMonitor progressMonitor, int ticks)
{
printDebug("INITIAL MODEL", initialModel);
printDebug("NORMALIZING RELATIVE TO", primitiveOps);
initialModel.inormalize(primitiveOps, preserveEdgeTypes, progressMonitor);
printDebug("NORMALIZED INITIAL MODEL", initialModel);
printDebug("File ordering:", initialModel.getFiles());
Model derivativeModel = new Model(
"derivative model",
progressMonitor, ticks+3,
vpg,
initialModel.getFiles());
printDebug("DERIVATIVE MODEL", derivativeModel);
derivativeModel.dnormalize(primitiveOps, preserveEdgeTypes, progressMonitor);
printDebug("NORMALIZED DERIVATIVE MODEL", derivativeModel);
ModelDiff diff = initialModel.compareAgainst(derivativeModel, progressMonitor);
describeDifferences(status, diff);
leaveHypotheticalMode(progressMonitor);
}
private void leaveHypotheticalMode(IProgressMonitor progressMonitor) throws Error
{
try
{
progressMonitor.subTask("Switching database out of hypothetical mode");
vpg.db.leaveHypotheticalMode();
}
catch (IOException e)
{
throw new Error(e);
}
}
private void printDebug(String string, Object object)
{
System.out.println();
System.out.println();
System.out.print(string); System.out.println(":");
System.out.println(object.toString());
}
private void describeDifferences(final RefactoringStatus status, ModelDiff diff)
{
final HashMap<IFile, String> modifiedSourceCode = new HashMap<IFile, String>();
diff.processUsing(new ModelDiffProcessor()
{
@Override
public void processAllEdgesDeleted(Set<String> filesWithAllEdgesDeleted)
{
String msg = "The following files will not compile if the transformation "
+ "is completed:";
status.addError(msg);
for (String filename : filesWithAllEdgesDeleted)
status.addError(" " + filename,
new FileStatusContext(EclipseVPG.getIFileForFilename(filename), null));
}
private String getCode(IFile file, HashMap<IFile, String> modifiedSourceCode)
{
if (file == null)
return null;
else if (modifiedSourceCode.containsKey(file))
return modifiedSourceCode.get(file);
else
{
Object ast = vpg.acquireTransientAST(EclipseVPG.getFilenameForIFile(file));
if (ast == null) return null;
String code = vpg.getSourceCodeFromAST(ast);
if (code == null) return null;
modifiedSourceCode.put(file, code);
return code;
}
}
@Override
public void processEdgeAdded(EdgeAdded addition)
{
String msg = "Completing this transformation will introduce an unexpected "
+ vpg.describeEdgeType(addition.edgeType).toLowerCase()
+ " (" + addition + ")"
;
status.addError(msg);
status.addError(" from here (click to view)",
new PostTransformationContext(
addition.getFileContainingSourceRegion(),
getCode(addition.getFileContainingSourceRegion(), modifiedSourceCode),
addition.getSourceRegion()));
status.addError(" to here (click to view)",
new PostTransformationContext(
addition.getFileContainingSinkRegion(),
getCode(addition.getFileContainingSinkRegion(), modifiedSourceCode),
addition.getSinkRegion()));
}
@Override
public void processEdgeDeleted(EdgeDeleted deletion)
{
String msg = "Completing this transformation will cause an existing "
+ vpg.describeEdgeType(deletion.edgeType).toLowerCase()
+ " to be eliminated"
+ " (" + deletion + ")"
;
status.addError(msg);
status.addError(" from here (click to view)",
new FileStatusContext(
deletion.getFileContainingSourceRegion(),
deletion.getSourceRegion()));
status.addError(" to here (click to view)",
new FileStatusContext(
deletion.getFileContainingSinkRegion(),
deletion.getSinkRegion()));
}
@Override
public void processEdgeSinkChanged(EdgeSinkChanged change)
{
String msg = "Completing this transformation will cause an existing "
+ vpg.describeEdgeType(change.edgeType).toLowerCase()
+ " to change"
+ " (" + change + ")"
;
status.addError(msg);
status.addError(" The "
+ vpg.describeEdgeType(change.edgeType).toLowerCase()
+ " from here (click to view)",
new PostTransformationContext(
change.getFileContainingSourceRegion(),
getCode(change.getFileContainingSourceRegion(), modifiedSourceCode),
change.getSourceRegion()));
status.addError(" to here (click to view)",
new PostTransformationContext(
change.getFileContainingSinkRegion(),
getCode(change.getFileContainingSinkRegion(), modifiedSourceCode),
change.getSinkRegion()));
status.addError(" will instead point here (click to view)",
new PostTransformationContext(
change.getFileContainingNewSinkRegion(),
getCode(change.getFileContainingNewSinkRegion(), modifiedSourceCode),
change.getNewSinkRegion()));
}
});
}
}