All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refactoringminer.astDiff.matchers.ProjectASTDiffer Maven / Gradle / Ivy

package org.refactoringminer.astDiff.matchers;

import com.github.gumtreediff.tree.Tree;
import com.github.gumtreediff.tree.TreeContext;
import com.github.gumtreediff.utils.Pair;

import gr.uom.java.xmi.diff.ReplaceAnonymousWithClassRefactoring;
import gr.uom.java.xmi.diff.UMLAbstractClassDiff;
import gr.uom.java.xmi.diff.UMLModelDiff;
import org.refactoringminer.api.Refactoring;
import org.refactoringminer.api.RefactoringMinerTimedOutException;
import org.refactoringminer.api.RefactoringType;
import org.refactoringminer.astDiff.actions.editscript.SimplifiedExtendedChawatheScriptGenerator;
import org.refactoringminer.astDiff.models.ExtendedMultiMappingStore;
import org.refactoringminer.astDiff.models.OptimizationData;
import org.refactoringminer.astDiff.moved.AllSubTreesMovedASTDiffGenerator;
import org.refactoringminer.astDiff.moved.MovedASTDiffGenerator;
import org.refactoringminer.astDiff.models.ASTDiff;
import org.refactoringminer.astDiff.models.ProjectASTDiff;
import org.refactoringminer.astDiff.matchers.wrappers.*;
import org.refactoringminer.astDiff.matchers.vanilla.MissingIdenticalSubtree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static org.refactoringminer.astDiff.utils.Helpers.findAppends;
import static org.refactoringminer.astDiff.utils.Helpers.findTreeContexts;

/**
 * @author  Pourya Alikhani Fard [email protected]
 */
public class ProjectASTDiffer
{
	private final static Logger logger = LoggerFactory.getLogger(ProjectASTDiffer.class);
	private final UMLModelDiff modelDiff;
	private List modelDiffRefactorings;
	private final ProjectASTDiff projectASTDiff;
	private final MovedASTDiffGenerator movedDeclarationGenerator;
	private final Map optimizationDataMap = new HashMap<>();

	public ProjectASTDiffer(UMLModelDiff modelDiff, Map fileContentsBefore, Map fileContentsAfter) throws RefactoringMinerTimedOutException {
		this.modelDiff = modelDiff;
		this.projectASTDiff = new ProjectASTDiff(fileContentsBefore, fileContentsAfter);
//		movedDeclarationGenerator = new MovedDeclarationGenerator(modelDiff, projectASTDiff);
		movedDeclarationGenerator = new AllSubTreesMovedASTDiffGenerator(modelDiff, projectASTDiff);
		diff();
	}

	public ProjectASTDiff getProjectASTDiff() {
		return projectASTDiff;
	}

	//kept for backward compatibility, but getProjectASTDiff() should be used instead
	public Set getDiffSet() {
		return projectASTDiff.getDiffSet();
	}

	private void diff() throws RefactoringMinerTimedOutException {
		long start = System.currentTimeMillis();
		this.modelDiffRefactorings = modelDiff.getRefactorings();
		long finish = System.currentTimeMillis();
		logger.info("ModelDiff.getRefactorings() execution time: " + (finish - start)/ 1000 + " seconds");
		projectASTDiff.setRefactorings(this.modelDiffRefactorings);
		projectASTDiff.setModelDiff(modelDiff);
		projectASTDiff.setParentContextMap(modelDiff.getParentModel().getTreeContextMap());
		projectASTDiff.setChildContextMap(modelDiff.getChildModel().getTreeContextMap());
		long diff_execution_started = System.currentTimeMillis();
		makeASTDiff(modelDiff.getCommonClassDiffList(),false);
		makeASTDiff(withCorrectOrder(modelDiff.getClassRenameDiffList()),false);
		makeASTDiff(withCorrectOrder(modelDiff.getClassMoveDiffList()),false);
		makeASTDiff(modelDiff.getInnerClassMoveDiffList(),true);
		makeASTDiff(getExtraDiffs(),true);
		//Process the ModelDiffRefactorings once at the end
		UnifiedModelDiffRefactoringsMatcher unifiedModelDiffRefactoringsMatcher = new UnifiedModelDiffRefactoringsMatcher(projectASTDiff.getDiffSet(), optimizationDataMap, modelDiff, modelDiffRefactorings);
		processAllOptimizations(unifiedModelDiffRefactoringsMatcher.getNewlyGeneratedDiffsOptimizationMap());
		for (ASTDiff diff : projectASTDiff.getDiffSet()) {
			new MissingIdenticalSubtree().match(diff.src.getRoot(), diff.dst.getRoot(), diff.getAllMappings());
		}
		long diff_execution_finished =  System.currentTimeMillis();
		logger.info("Diff execution: " + (diff_execution_finished - diff_execution_started)/ 1000 + " seconds");
		long movedDiff_execution_started =  System.currentTimeMillis();
		computeAllEditScripts();
		projectASTDiff.addMoveASTDiff(unifiedModelDiffRefactoringsMatcher.getNewlyGeneratedDiffsOptimizationMap().keySet());
		projectASTDiff.addMoveASTDiff(movedDeclarationGenerator.make());
		long movedDiff_execution_finished =  System.currentTimeMillis();
		logger.info("MovedDiff execution: " + (movedDiff_execution_finished - movedDiff_execution_started)/ 1000 + " seconds");
		computeMovedDiffsEditScripts();
	}

	private void processAllOptimizations(Map newlyGeneratedDiffMap) {
		for (ASTDiff diff : projectASTDiff.getDiffSet()) {
//			optimizationDataMap.get()
			new ASTDiffMappingOptimizer(optimizationDataMap.get(diff), diff, modelDiff.getParentModel().getTreeContextMap(), modelDiff.getChildModel().getTreeContextMap()).
					match(diff.src.getRoot(), diff.dst.getRoot(), diff.getAllMappings());
		}
		for (Map.Entry astDiffOptimizationDataEntry : newlyGeneratedDiffMap.entrySet()) {
			ASTDiff diff = astDiffOptimizationDataEntry.getKey();
			OptimizationData optimizationData = astDiffOptimizationDataEntry.getValue();
			new ASTDiffMappingOptimizer(optimizationData, diff, modelDiff.getParentModel().getTreeContextMap(), modelDiff.getChildModel().getTreeContextMap()).
					match(diff.src.getRoot(), diff.dst.getRoot(), diff.getAllMappings());
		}
	}

	private List withCorrectOrder(List umlDiffs) {
		ArrayList result = new ArrayList<>(umlDiffs);
		Set seen = new HashSet<>();
		for (UMLAbstractClassDiff umlDiff : umlDiffs) {
			UMLAbstractClassDiff found = findDiffWith(result, umlDiff.getOriginalClassName(), umlDiff.getNextClassName());
			if (found != null && !seen.contains(found))
			{
				seen.add(found);
				result.remove(found);
				result.add(0, found);
			}
		}
		return result;
	}

	private UMLAbstractClassDiff findDiffWith(ArrayList result, String originalClassName, String nextClassName) {
		for (UMLAbstractClassDiff umlAbstractClassDiff : result) {
			if (umlAbstractClassDiff.getOriginalClassName().equals(originalClassName)
				&&
				umlAbstractClassDiff.getNextClassName().equals(nextClassName))
				return umlAbstractClassDiff;
		}
		return null;
	}

	private List getExtraDiffs() {
		List extraDiffs = new ArrayList<>();
		for (Refactoring modelDiffRefactoring : modelDiffRefactorings) {
			if (modelDiffRefactoring.getRefactoringType() == RefactoringType.REPLACE_ANONYMOUS_WITH_CLASS)
			{
				ReplaceAnonymousWithClassRefactoring replaceAnonymousWithClassRefactoring = (ReplaceAnonymousWithClassRefactoring) modelDiffRefactoring;
				extraDiffs.add(replaceAnonymousWithClassRefactoring.getDiff());
			}
		}
		return extraDiffs;
	}

	private void computeAllEditScripts() {
		long editScript_start = System.currentTimeMillis();
		for (ASTDiff diff : projectASTDiff.getDiffSet()) {
			diff.computeEditScript(modelDiff.getParentModel().getTreeContextMap(), modelDiff.getChildModel().getTreeContextMap(), new SimplifiedExtendedChawatheScriptGenerator());
		}
		long editScript_end = System.currentTimeMillis();
		logger.info("EditScript execution: " + (editScript_end - editScript_start)/ 1000 + " seconds");
	}

	private void computeMovedDiffsEditScripts() {
		for (ASTDiff diff : projectASTDiff.getMoveDiffSet()) {
			Tree srcRoot = diff.src.getRoot();
			Tree dstRoot = diff.dst.getRoot();
			diff.getAllMappings().addMapping(srcRoot, dstRoot); //This helps Chawathe to generate the editscript properly, however the mapping is actually incorrect
			diff.computeEditScript(null, null, new SimplifiedExtendedChawatheScriptGenerator());
			diff.getAllMappings().removeMapping(srcRoot, dstRoot); //Removes the mapping that was added to help Chawathe
		}
	}

	private void makeASTDiff(List umlClassBaseDiffList, boolean mergeFlag){
		for (UMLAbstractClassDiff classDiff : umlClassBaseDiffList) {
			Collection appends = findAppends(projectASTDiff.getDiffSet(), classDiff.getOriginalClass().getSourceFile(), classDiff.getNextClass().getSourceFile());
			boolean decision = (!appends.isEmpty()) || mergeFlag;
			ASTDiff classASTDiff = process(classDiff, findTreeContexts(modelDiff, classDiff), decision, appends);
			if (!appends.isEmpty()) {
				for (ASTDiff append : appends) {
					append.getAllMappings().mergeMappings(classASTDiff.getAllMappings());
				}
			}
			else {
				projectASTDiff.addASTDiff(classASTDiff);
			}
		}
	}

	private ASTDiff process(UMLAbstractClassDiff classDiff, Pair treeContextPair, boolean mergeFlag, Collection appends){
		Tree srcTree = treeContextPair.first.getRoot();
		Tree dstTree = treeContextPair.second.getRoot();
		ExtendedMultiMappingStore mappingStore = new ExtendedMultiMappingStore(srcTree,dstTree);
		ASTDiff astDiff = new ASTDiff(classDiff.getOriginalClass().getLocationInfo().getFilePath(),
				classDiff.getNextClass().getLocationInfo().getFilePath(),
				treeContextPair.first,
				treeContextPair.second,
				mappingStore);

		OptimizationData optimizationData = optimizationDataMap.get(astDiff);
		if (optimizationData == null){
			if (!mergeFlag) {
				optimizationDataMap.putIfAbsent(astDiff,
						new OptimizationData(new ArrayList<>(), new ExtendedMultiMappingStore(srcTree, dstTree)));
				optimizationData = optimizationDataMap.get(astDiff);
			}
			else {
				try {
					optimizationData = optimizationDataMap.get(appends.iterator().next());
				}
				catch (Exception e)
				{
					optimizationDataMap.putIfAbsent(astDiff,
							new OptimizationData(new ArrayList<>(), new ExtendedMultiMappingStore(srcTree, dstTree)));
					optimizationData = optimizationDataMap.get(astDiff);
				}
			}
		}
		new ClassDiffMatcher(optimizationData, classDiff, mergeFlag, modelDiffRefactorings).match(srcTree, dstTree, mappingStore);
		return astDiff;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy