blob: e2f644197e83c210ee6f450c3eb55f0f46707dc2 [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.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
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.preservation.ModelDiff.EdgeAdded;
import org.eclipse.rephraserengine.core.preservation.ModelDiff.EdgeDeleted;
import org.eclipse.rephraserengine.core.preservation.ModelDiff.EdgeSinkChanged;
import org.eclipse.rephraserengine.core.preservation.ModelDiff.ModelDiffProcessor;
import org.eclipse.rephraserengine.core.util.OffsetLength;
import org.eclipse.rephraserengine.core.vpg.eclipse.EclipseVPG;
/**
* Checks for preservation of semantic edges in a program graph modulo a sequence of primitive
* transformations.
*
* @author Jeff Overbey
*
* @since 1.0
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public final class PreservationAnalysis
{
private static final String SEPARATOR = ","; //$NON-NLS-1$
private IAdapterManager adapterManager;
private EclipseVPG vpg;
//private IProgressMonitor progressMonitor;
private Model initialModel;
private ReplacementList replacements;
private PreservationRuleset ruleset;
private String fields;
private String times;
/** @since 3.0 */
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
IFile file,
PreservationRuleset ruleset)
{
this(vpg, progressMonitor, ticks, EclipseVPG.getFilenameForIFile(file), ruleset);
}
/** @since 3.0 */
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
Collection<IFile> files,
PreservationRuleset ruleset)
{
this(vpg, progressMonitor, ticks, getFilenames(files), ruleset);
}
/** @since 3.0 */
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
String filename,
PreservationRuleset ruleset)
{
this(vpg, progressMonitor, ticks, Collections.singletonList(filename), ruleset);
}
/** @since 3.0 */
public PreservationAnalysis(
EclipseVPG vpg,
IProgressMonitor progressMonitor, int ticks,
List<String> filenames,
PreservationRuleset ruleset)
{
this.adapterManager = Platform.getAdapterManager();
this.vpg = vpg;
//this.progressMonitor = progressMonitor;
this.replacements = new ReplacementList();
this.ruleset = ruleset;
this.fields = "Filenames"; //$NON-NLS-1$
this.times = filenames.toString();
progressMonitor.subTask(Messages.PreservationAnalysis_EnteringHypotheticalMode);
long start = System.currentTimeMillis();
ensureDatabaseIsInHypotheticalMode();
this.fields += SEPARATOR + "Hypothetical"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
start = System.currentTimeMillis();
this.initialModel = new Model("initial model", progressMonitor, ticks, vpg, filenames); //$NON-NLS-1$
this.fields += SEPARATOR + "Init Model"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
}
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.isInHypotheticalMode())
{
try
{
vpg.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()); //$NON-NLS-1$
Replacement alpha = new Replacement(
filename,
offsetLength.getOffset(),
0,
offsetLength.getLength());
replacements.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()); //$NON-NLS-1$
Replacement epsilon = new Replacement(
filename,
offsetLength.getOffset(),
offsetLength.getLength(),
0);
replacements.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()); //$NON-NLS-1$
Replacement rho = new Replacement(
filename,
offsetLength.getOffset(),
oldLength,
newLength);
replacements.add(rho);
}
// /**
// * @since 3.0
// */
// public void markMu(IFile file, Object oldNode, Object newNode)
// {
// String filename = EclipseVPG.getFilenameForIFile(file);
//
// OffsetLength oldOffsetLength = (OffsetLength)adapterManager.getAdapter(oldNode, OffsetLength.class);
// if (oldOffsetLength == null)
// throw new Error("Unable to get OffsetLength adapter for " + oldNode.getClass().getName()); //$NON-NLS-1$
//
// OffsetLength newOffsetLength = (OffsetLength)adapterManager.getAdapter(newNode, OffsetLength.class);
// if (newOffsetLength == null)
// throw new Error("Unable to get OffsetLength adapter for " + newNode.getClass().getName()); //$NON-NLS-1$
//
// Mu mu = PrimitiveOp.mu(
// filename,
// oldOffsetLength,
// newOffsetLength);
//
// replacements.add(mu);
// }
//
// /**
// * @since 3.0
// */
// public void markMu(IFile file, OffsetLength oldOffsetLength, OffsetLength newOffsetLength)
// {
// String filename = EclipseVPG.getFilenameForIFile(file);
//
// Mu mu = PrimitiveOp.mu(
// filename,
// oldOffsetLength,
// newOffsetLength);
//
// replacements.add(mu);
// }
@Override public String toString()
{
return replacements.toString();
}
/** @since 3.0 */
public void logPostTransformTime(long milliseconds)
{
this.fields += SEPARATOR + "PostTransform"; //$NON-NLS-1$
this.times += SEPARATOR + milliseconds;
}
public void checkForPreservation(
RefactoringStatus status,
IProgressMonitor progressMonitor, int ticks)
{
printDebug("INITIAL MODEL", initialModel); //$NON-NLS-1$
printDebug("NORMALIZING RELATIVE TO", replacements); //$NON-NLS-1$
long start = System.currentTimeMillis();
initialModel.inormalize(replacements, progressMonitor);
this.fields += SEPARATOR + "I-Normalize"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
printDebug("NORMALIZED INITIAL MODEL", initialModel); //$NON-NLS-1$
printDebug("File ordering:", initialModel.getFiles()); //$NON-NLS-1$
start = System.currentTimeMillis();
Model derivativeModel = new Model(
"derivative model", //$NON-NLS-1$
progressMonitor, ticks+3,
vpg,
initialModel.getFiles());
this.fields += SEPARATOR + "D-Model"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
printDebug("DERIVATIVE MODEL", derivativeModel); //$NON-NLS-1$
start = System.currentTimeMillis();
derivativeModel.dnormalize(replacements, progressMonitor);
this.fields += SEPARATOR + "D-Normalize"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
printDebug("NORMALIZED DERIVATIVE MODEL", derivativeModel); //$NON-NLS-1$
start = System.currentTimeMillis();
ModelDiff diff = initialModel.checkPreservation(derivativeModel, ruleset, progressMonitor);
this.fields += SEPARATOR + "Preservation"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
start = System.currentTimeMillis();
describeDifferences(status, diff);
this.fields += SEPARATOR + "Desc Diff"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
start = System.currentTimeMillis();
leaveHypotheticalMode(progressMonitor);
this.fields += SEPARATOR + "Leave Hypoth"; //$NON-NLS-1$
this.times += SEPARATOR + (System.currentTimeMillis()-start);
logTimes();
}
private void logTimes()
{
try
{
FileWriter output = new FileWriter(
System.getProperty("user.home") + File.separator + //$NON-NLS-1$
"Desktop" + File.separator + //$NON-NLS-1$
"times.csv", true); //$NON-NLS-1$
output.write(fields + "\n"); //$NON-NLS-1$
output.write(times + "\n"); //$NON-NLS-1$
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
private void leaveHypotheticalMode(IProgressMonitor progressMonitor) throws Error
{
try
{
progressMonitor.subTask(Messages.PreservationAnalysis_ExitingHypotheticalMode);
vpg.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(":"); //$NON-NLS-1$
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 = Messages.PreservationAnalysis_TheFollowingFilesWillNotCompile;
status.addError(msg);
for (String filename : filesWithAllEdgesDeleted)
status.addError(" " + filename, //$NON-NLS-1$
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 =
Messages.bind(
Messages.PreservationAnalysis_TransformationWillIntroduce,
vpg.describeEdgeType(addition.edge.getType()).toLowerCase(),
addition);
status.addError(msg);
status.addError(Messages.PreservationAnalysis_FromHere,
new PostTransformationContext(
addition.getFileContainingSourceRegion(),
getCode(addition.getFileContainingSourceRegion(), modifiedSourceCode),
addition.getSourceRegion()));
status.addError(Messages.PreservationAnalysis_ToHere,
new PostTransformationContext(
addition.getFileContainingSinkRegion(),
getCode(addition.getFileContainingSinkRegion(), modifiedSourceCode),
addition.getSinkRegion()));
}
@Override
public void processEdgeDeleted(EdgeDeleted deletion)
{
String msg =
Messages.bind(
Messages.PreservationAnalysis_TransformationWillEliminate,
vpg.describeEdgeType(deletion.edge.getType()).toLowerCase(),
deletion);
status.addError(msg);
status.addError(Messages.PreservationAnalysis_FromHere,
new FileStatusContext(
deletion.getFileContainingSourceRegion(),
deletion.getSourceRegion()));
status.addError(Messages.PreservationAnalysis_ToHere,
new FileStatusContext(
deletion.getFileContainingSinkRegion(),
deletion.getSinkRegion()));
}
@Override
public void processEdgeSinkChanged(EdgeSinkChanged change)
{
String msg =
Messages.bind(
Messages.PreservationAnalysis_TransformationWillChange,
vpg.describeEdgeType(change.edge.getType()).toLowerCase(),
change);
status.addError(msg);
status.addError(
Messages.bind(
Messages.PreservationAnalysis_EdgeWillChange,
vpg.describeEdgeType(change.edge.getType()).toLowerCase()),
new PostTransformationContext(
change.getFileContainingSourceRegion(),
getCode(change.getFileContainingSourceRegion(), modifiedSourceCode),
change.getSourceRegion()));
status.addError(Messages.PreservationAnalysis_ToHere,
new PostTransformationContext(
change.getFileContainingSinkRegion(),
getCode(change.getFileContainingSinkRegion(), modifiedSourceCode),
change.getSinkRegion()));
status.addError(Messages.PreservationAnalysis_WillPointHereInstead,
new PostTransformationContext(
change.getFileContainingNewSinkRegion(),
getCode(change.getFileContainingNewSinkRegion(), modifiedSourceCode),
change.getNewSinkRegion()));
}
});
}
/** @since 3.0 */
public static boolean printModelOn(PrintStream ps, IFile file, EclipseVPG<?,?,?> vpg) throws UnsupportedEncodingException, IOException, CoreException
{
String filename = EclipseVPG.getFilenameForIFile(file);
if (filename == null) return false;
ArrayList<Integer> lineMap = new ArrayList<Integer>();
String fileContents = readStream(lineMap,
new BufferedReader(new InputStreamReader(file.getContents(true), file.getCharset())));
ps.println(filename);
ps.println();
Model model = new Model("edge model", new NullProgressMonitor(), 0, vpg, filename); //$NON-NLS-1$
ps.print(model.toString(filename, fileContents, lineMap));
return true;
}
private static String readStream(ArrayList<Integer> lineMap, Reader in) throws IOException
{
StringBuffer sb = new StringBuffer(4096);
for (int offset = 0, ch = in.read(); ch >= 0; ch = in.read())
{
sb.append((char)ch);
offset++;
if (ch == '\n' && lineMap != null)
{
//System.out.println("Line " + (lineMap.size()+1) + " starts at offset " + offset);
lineMap.add(offset);
}
}
in.close();
return sb.toString();
}
}