Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
gr.uom.java.xmi.diff.UMLModelDiff Maven / Gradle / Ivy
package gr.uom.java.xmi.diff;
import gr.uom.java.xmi.LeafType;
import gr.uom.java.xmi.UMLAbstractClass;
import gr.uom.java.xmi.UMLAnonymousClass;
import gr.uom.java.xmi.UMLAttribute;
import gr.uom.java.xmi.UMLClass;
import gr.uom.java.xmi.UMLClassMatcher;
import gr.uom.java.xmi.UMLClassMatcher.MatchResult;
import gr.uom.java.xmi.UMLClassMatcher.Rename;
import gr.uom.java.xmi.UMLEnumConstant;
import gr.uom.java.xmi.UMLGeneralization;
import gr.uom.java.xmi.UMLImport;
import gr.uom.java.xmi.UMLModel;
import gr.uom.java.xmi.UMLOperation;
import gr.uom.java.xmi.UMLParameter;
import gr.uom.java.xmi.UMLRealization;
import gr.uom.java.xmi.UMLType;
import gr.uom.java.xmi.UMLTypeParameter;
import gr.uom.java.xmi.VariableDeclarationContainer;
import gr.uom.java.xmi.decomposition.AbstractCall;
import gr.uom.java.xmi.decomposition.AbstractCodeFragment;
import gr.uom.java.xmi.decomposition.AbstractCodeMapping;
import gr.uom.java.xmi.decomposition.AbstractExpression;
import gr.uom.java.xmi.decomposition.CompositeStatementObject;
import gr.uom.java.xmi.decomposition.CompositeStatementObjectMapping;
import gr.uom.java.xmi.decomposition.LeafExpression;
import gr.uom.java.xmi.decomposition.LeafMapping;
import gr.uom.java.xmi.decomposition.StatementObject;
import gr.uom.java.xmi.decomposition.UMLOperationBodyMapper;
import gr.uom.java.xmi.decomposition.UMLOperationBodyMapperComparator;
import gr.uom.java.xmi.decomposition.VariableDeclaration;
import gr.uom.java.xmi.decomposition.VariableReferenceExtractor;
import gr.uom.java.xmi.decomposition.replacement.ClassInstanceCreationWithMethodInvocationReplacement;
import gr.uom.java.xmi.decomposition.replacement.MergeVariableReplacement;
import gr.uom.java.xmi.decomposition.replacement.MethodInvocationReplacement;
import gr.uom.java.xmi.decomposition.replacement.MethodInvocationWithClassInstanceCreationReplacement;
import gr.uom.java.xmi.decomposition.replacement.Replacement;
import gr.uom.java.xmi.decomposition.replacement.Replacement.ReplacementType;
import gr.uom.java.xmi.decomposition.replacement.VariableReplacementWithMethodInvocation;
import gr.uom.java.xmi.decomposition.replacement.VariableReplacementWithMethodInvocation.Direction;
import static gr.uom.java.xmi.Constants.JAVA;
import static gr.uom.java.xmi.diff.UMLClassBaseDiff.BUILDER_STATEMENT_RATIO_THRESHOLD;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.commons.lang3.tuple.Pair;
import org.refactoringminer.api.Refactoring;
import org.refactoringminer.api.RefactoringMinerTimedOutException;
import org.refactoringminer.api.RefactoringType;
import org.refactoringminer.util.PrefixSuffixUtils;
public class UMLModelDiff {
private static final Pattern RETURN_NUMBER_LITERAL = Pattern.compile("return \\d+;\n");
private final int MAXIMUM_NUMBER_OF_COMPARED_METHODS;
private UMLModel parentModel;
private UMLModel childModel;
private List addedClasses;
private List removedClasses;
private List addedGeneralizations;
private List removedGeneralizations;
private List generalizationDiffList;
private List addedRealizations;
private List removedRealizations;
private List realizationDiffList;
private List commonClassDiffList;
private List classMoveDiffList;
private List innerClassMoveDiffList;
private List classRenameDiffList;
private List classMergeDiffList;
private List classSplitDiffList;
private List movedAttributeDiffList;
private Set refactorings;
private Set moveRenameClassRefactorings;
private Set deletedFolderPaths;
private Set> processedOperationPairs = new HashSet>();
private Set> processedClassPairs = new HashSet>();
private Map> renameMap = new LinkedHashMap>();
private Map> mergeMap = new LinkedHashMap>();
public UMLModelDiff(UMLModel parentModel, UMLModel childModel) {
this.parentModel = parentModel;
this.childModel = childModel;
if(partialModel()) {
MAXIMUM_NUMBER_OF_COMPARED_METHODS = 500;
}
else {
MAXIMUM_NUMBER_OF_COMPARED_METHODS = 200;
}
this.addedClasses = new ArrayList();
this.removedClasses = new ArrayList();
this.addedGeneralizations = new ArrayList();
this.removedGeneralizations = new ArrayList();
this.generalizationDiffList = new ArrayList();
this.realizationDiffList = new ArrayList();
this.addedRealizations = new ArrayList();
this.removedRealizations = new ArrayList();
this.commonClassDiffList = new ArrayList();
this.classMoveDiffList = new ArrayList();
this.innerClassMoveDiffList = new ArrayList();
this.classRenameDiffList = new ArrayList();
this.classMergeDiffList = new ArrayList();
this.classSplitDiffList = new ArrayList();
this.movedAttributeDiffList = new ArrayList();
this.refactorings = new LinkedHashSet();
this.deletedFolderPaths = new LinkedHashSet();
}
public UMLModel getParentModel() {
return parentModel;
}
public UMLModel getChildModel() {
return childModel;
}
public UMLAbstractClass findClassInParentModel(String className) {
for(UMLClass umlClass : parentModel.getClassList()) {
if(umlClass.getName().equals(className)) {
return umlClass;
}
}
for(UMLClass umlClass : parentModel.getClassList()) {
if(umlClass.getName().endsWith("." + className)) {
return umlClass;
}
}
return null;
}
public UMLAbstractClass findClassInChildModel(String className) {
for(UMLClass umlClass : childModel.getClassList()) {
if(umlClass.getName().equals(className)) {
return umlClass;
}
}
for(UMLClass umlClass : childModel.getClassList()) {
if(umlClass.getName().endsWith("." + className)) {
return umlClass;
}
}
return null;
}
public void reportAddedClass(UMLClass umlClass) {
if(!addedClasses.contains(umlClass))
this.addedClasses.add(umlClass);
}
public List getAddedClasses() {
return addedClasses;
}
public void reportRemovedClass(UMLClass umlClass) {
if(!removedClasses.contains(umlClass))
this.removedClasses.add(umlClass);
}
public List getRemovedClasses() {
return removedClasses;
}
public void reportAddedGeneralization(UMLGeneralization umlGeneralization) {
this.addedGeneralizations.add(umlGeneralization);
}
public void reportRemovedGeneralization(UMLGeneralization umlGeneralization) {
this.removedGeneralizations.add(umlGeneralization);
}
public void reportAddedRealization(UMLRealization umlRealization) {
this.addedRealizations.add(umlRealization);
}
public void reportRemovedRealization(UMLRealization umlRealization) {
this.removedRealizations.add(umlRealization);
}
public void addUMLClassDiff(UMLClassDiff classDiff) {
this.commonClassDiffList.add(classDiff);
}
public List getCommonClassDiffList() {
return commonClassDiffList;
}
public List getClassMoveDiffList() {
return classMoveDiffList;
}
public List getInnerClassMoveDiffList() {
return innerClassMoveDiffList;
}
public List getClassRenameDiffList() {
return classRenameDiffList;
}
public List getMovedAttributeDiffList() {
return movedAttributeDiffList;
}
public boolean commonlyImplementedOperations(UMLOperation operation1, UMLOperation operation2, UMLClassBaseDiff classDiff2) {
UMLClassBaseDiff classDiff1 = getUMLClassDiff(operation1.getClassName());
if(classDiff1 != null) {
Set commonInterfaces = classDiff1.nextClassCommonInterfaces(classDiff2);
for(UMLType commonInterface : commonInterfaces) {
UMLClassBaseDiff interfaceDiff = getUMLClassDiff(commonInterface);
if(interfaceDiff != null &&
interfaceDiff.containsOperationWithTheSameSignatureInOriginalClass(operation1) &&
interfaceDiff.containsOperationWithTheSameSignatureInNextClass(operation2)) {
return true;
}
}
}
return false;
}
public UMLClassBaseDiff getUMLClassDiff(String className) {
for(UMLClassDiff classDiff : commonClassDiffList) {
if(classDiff.matches(className))
return classDiff;
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
if(classDiff.matches(className))
return classDiff;
}
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
if(classDiff.matches(className))
return classDiff;
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
if(classDiff.matches(className))
return classDiff;
}
return null;
}
public UMLClassBaseDiff getUMLClassDiff(UMLType type) {
for(UMLClassDiff classDiff : commonClassDiffList) {
if(classDiff.matches(type))
return classDiff;
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
if(classDiff.matches(type))
return classDiff;
}
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
if(classDiff.matches(type))
return classDiff;
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
if(classDiff.matches(type))
return classDiff;
}
return null;
}
private UMLClassBaseDiff getUMLClassDiffWithAttribute(Replacement pattern) {
for(UMLClassDiff classDiff : commonClassDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getBefore()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
return classDiff;
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getBefore()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
return classDiff;
}
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getBefore()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
return classDiff;
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getBefore()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
return classDiff;
}
return null;
}
private List getUMLClassDiffWithExistingAttributeAfter(Replacement pattern) {
List classDiffs = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) != null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
return classDiffs;
}
private List getUMLClassDiffWithNewAttributeAfter(Replacement pattern) {
List classDiffs = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) == null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) == null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) == null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
if(classDiff.findAttributeInOriginalClass(pattern.getAfter()) == null &&
classDiff.findAttributeInNextClass(pattern.getAfter()) != null)
classDiffs.add(classDiff);
}
return classDiffs;
}
public boolean isSubclassOf(String subclass, String finalSuperclass) {
return isSubclassOf(subclass, finalSuperclass, new LinkedHashSet());
}
private boolean isSubclassOf(String subclass, String finalSuperclass, Set visitedClasses) {
if(visitedClasses.contains(subclass)) {
return false;
}
else {
visitedClasses.add(subclass);
}
UMLClassBaseDiff subclassDiff = getUMLClassDiff(subclass);
if(subclassDiff == null) {
subclassDiff = getUMLClassDiff(UMLType.extractTypeObject(subclass));
}
if(subclassDiff != null) {
UMLType superclass = subclassDiff.getSuperclass();
if(superclass != null) {
if(checkInheritanceRelationship(superclass, finalSuperclass, visitedClasses)) {
return true;
}
}
else if(subclassDiff.getOldSuperclass() != null && subclassDiff.getNewSuperclass() != null &&
!subclassDiff.getOldSuperclass().equals(subclassDiff.getNewSuperclass()) && looksLikeAddedClass(subclassDiff.getNewSuperclass()) != null) {
UMLClass addedClass = looksLikeAddedClass(subclassDiff.getNewSuperclass());
if(addedClass.getSuperclass() != null) {
return checkInheritanceRelationship(addedClass.getSuperclass(), finalSuperclass, visitedClasses);
}
}
else if(subclassDiff.getOldSuperclass() == null && subclassDiff.getNewSuperclass() != null && looksLikeAddedClass(subclassDiff.getNewSuperclass()) != null) {
UMLClass addedClass = looksLikeAddedClass(subclassDiff.getNewSuperclass());
return checkInheritanceRelationship(UMLType.extractTypeObject(addedClass.getName()), finalSuperclass, visitedClasses);
}
for(UMLType implementedInterface : subclassDiff.getAddedImplementedInterfaces()) {
if(checkInheritanceRelationship(implementedInterface, finalSuperclass, visitedClasses)) {
return true;
}
}
for(UMLType implementedInterface : subclassDiff.getNextClass().getImplementedInterfaces()) {
if(checkInheritanceRelationship(implementedInterface, finalSuperclass, visitedClasses)) {
return true;
}
}
}
UMLClass addedClass = getAddedClass(subclass);
if(addedClass == null) {
addedClass = looksLikeAddedClass(UMLType.extractTypeObject(subclass));
}
if(addedClass != null) {
UMLType superclass = addedClass.getSuperclass();
if(superclass != null) {
return checkInheritanceRelationship(superclass, finalSuperclass, visitedClasses);
}
for(UMLType implementedInterface : addedClass.getImplementedInterfaces()) {
if(checkInheritanceRelationship(implementedInterface, finalSuperclass, visitedClasses)) {
return true;
}
}
}
UMLClass removedClass = getRemovedClass(subclass);
if(removedClass == null) {
removedClass = looksLikeRemovedClass(UMLType.extractTypeObject(subclass));
}
if(removedClass != null) {
UMLType superclass = removedClass.getSuperclass();
if(superclass != null) {
return checkInheritanceRelationship(superclass, finalSuperclass, visitedClasses);
}
for(UMLType implementedInterface : removedClass.getImplementedInterfaces()) {
if(checkInheritanceRelationship(implementedInterface, finalSuperclass, visitedClasses)) {
return true;
}
}
}
return false;
}
private boolean checkInheritanceRelationship(UMLType superclass, String finalSuperclass, Set visitedClasses) {
if(looksLikeSameType(superclass.getClassType(), finalSuperclass))
return true;
else
return isSubclassOf(superclass.getClassType(), finalSuperclass, visitedClasses);
}
private UMLClass looksLikeAddedClass(UMLType type) {
for(UMLClass umlClass : addedClasses) {
if(umlClass.getName().endsWith("." + type.getClassType())) {
return umlClass;
}
}
return null;
}
private UMLClass looksLikeRemovedClass(UMLType type) {
for(UMLClass umlClass : removedClasses) {
if(umlClass.getName().endsWith("." + type.getClassType())) {
return umlClass;
}
}
return null;
}
public UMLOperation findOperationInAddedClasses(AbstractCall operationInvocation, VariableDeclarationContainer callerOperation, UMLAbstractClassDiff classDiff) {
for(UMLClass umlClass : addedClasses) {
String expression = operationInvocation.getExpression();
if(expression != null && umlClass.getNonQualifiedName().equalsIgnoreCase(expression)) {
for(UMLOperation operation : umlClass.getOperations()) {
if(operationInvocation.matchesOperation(operation, callerOperation, classDiff, this)) {
return operation;
}
}
}
}
return null;
}
public UMLClass getAddedClass(String className) {
for(UMLClass umlClass : addedClasses) {
if(umlClass.getName().equals(className))
return umlClass;
}
return null;
}
public UMLClass getRemovedClass(String className) {
for(UMLClass umlClass : removedClasses) {
if(umlClass.getName().equals(className))
return umlClass;
}
return null;
}
private String isRenamedClass(UMLClass umlClass) {
for(UMLClassRenameDiff renameDiff : classRenameDiffList) {
if(renameDiff.getOriginalClass().equals(umlClass))
return renameDiff.getRenamedClass().getName();
}
return null;
}
private String isMovedClass(UMLClass umlClass) {
for(UMLClassMoveDiff moveDiff : classMoveDiffList) {
if(moveDiff.getOriginalClass().equals(umlClass))
return moveDiff.getMovedClass().getName();
}
return null;
}
public void checkForGeneralizationChanges() {
for(Iterator removedGeneralizationIterator = removedGeneralizations.iterator(); removedGeneralizationIterator.hasNext();) {
UMLGeneralization removedGeneralization = removedGeneralizationIterator.next();
for(Iterator addedGeneralizationIterator = addedGeneralizations.iterator(); addedGeneralizationIterator.hasNext();) {
UMLGeneralization addedGeneralization = addedGeneralizationIterator.next();
String renamedChild = isRenamedClass(removedGeneralization.getChild());
String movedChild = isMovedClass(removedGeneralization.getChild());
if(removedGeneralization.getChild().equals(addedGeneralization.getChild())) {
UMLGeneralizationDiff generalizationDiff = new UMLGeneralizationDiff(removedGeneralization, addedGeneralization);
addedGeneralizationIterator.remove();
removedGeneralizationIterator.remove();
generalizationDiffList.add(generalizationDiff);
break;
}
if( (renamedChild != null && renamedChild.equals(addedGeneralization.getChild().getName())) ||
(movedChild != null && movedChild.equals(addedGeneralization.getChild().getName()))) {
UMLGeneralizationDiff generalizationDiff = new UMLGeneralizationDiff(removedGeneralization, addedGeneralization);
addedGeneralizationIterator.remove();
removedGeneralizationIterator.remove();
generalizationDiffList.add(generalizationDiff);
break;
}
}
}
}
public void checkForRealizationChanges() {
for(Iterator removedRealizationIterator = removedRealizations.iterator(); removedRealizationIterator.hasNext();) {
UMLRealization removedRealization = removedRealizationIterator.next();
for(Iterator addedRealizationIterator = addedRealizations.iterator(); addedRealizationIterator.hasNext();) {
UMLRealization addedRealization = addedRealizationIterator.next();
String renamedChild = isRenamedClass(removedRealization.getClient());
String movedChild = isMovedClass(removedRealization.getClient());
//String renamedParent = isRenamedClass(removedRealization.getSupplier());
//String movedParent = isMovedClass(removedRealization.getSupplier());
if( (renamedChild != null && renamedChild.equals(addedRealization.getClient().getName())) ||
(movedChild != null && movedChild.equals(addedRealization.getClient().getName()))) {
UMLRealizationDiff realizationDiff = new UMLRealizationDiff(removedRealization, addedRealization);
addedRealizationIterator.remove();
removedRealizationIterator.remove();
realizationDiffList.add(realizationDiff);
break;
}
}
}
}
public void checkForMovedClasses(Set repositoryDirectories, UMLClassMatcher matcher) throws RefactoringMinerTimedOutException {
if(removedClasses.size() <= addedClasses.size()) {
for(Iterator removedClassIterator = removedClasses.iterator(); removedClassIterator.hasNext();) {
UMLClass removedClass = removedClassIterator.next();
TreeSet diffSet = new TreeSet(new ClassMoveComparator());
for(Iterator addedClassIterator = addedClasses.iterator(); addedClassIterator.hasNext();) {
UMLClass addedClass = addedClassIterator.next();
String removedClassSourceFile = removedClass.getSourceFile();
String removedClassSourceFolder = "";
if(removedClassSourceFile.contains("/")) {
removedClassSourceFolder = removedClassSourceFile.substring(0, removedClassSourceFile.lastIndexOf("/"));
}
if(!repositoryDirectories.contains(removedClassSourceFolder)) {
deletedFolderPaths.add(removedClassSourceFolder);
//add deleted sub-directories
String subDirectory = new String(removedClassSourceFolder);
while(subDirectory.contains("/")) {
subDirectory = subDirectory.substring(0, subDirectory.lastIndexOf("/"));
if(!repositoryDirectories.contains(subDirectory)) {
deletedFolderPaths.add(subDirectory);
}
}
}
MatchResult matchResult = matcher.match(removedClass, addedClass);
if(matchResult.isMatch()) {
if(!conflictingMoveOfTopLevelClass(removedClass, addedClass)) {
UMLClassMoveDiff classMoveDiff = new UMLClassMoveDiff(removedClass, addedClass, this, matchResult);
diffSet.add(classMoveDiff);
}
}
}
if(!diffSet.isEmpty()) {
UMLClassMoveDiff minClassMoveDiff = diffSet.first();
TreeSet renameDiffSet = new TreeSet<>();
if(matcher instanceof UMLClassMatcher.RelaxedMove) {
renameDiffSet = findRenameMatchesForRemovedClass(removedClass, new UMLClassMatcher.RelaxedRename());
}
if(!renameDiffSet.isEmpty() && !(renameDiffSet.first().getOriginalClass().equals(minClassMoveDiff.getOriginalClass()) &&
renameDiffSet.first().getRenamedClass().equals(minClassMoveDiff.getMovedClass()))) {
UMLClassRenameDiff minClassRenameDiff = renameDiffSet.first();
int matchedMembers1 = minClassMoveDiff.getMatchResult().getMatchedOperations() + minClassMoveDiff.getMatchResult().getMatchedAttributes();
int matchedMembers2 = minClassRenameDiff.getMatchResult().getMatchedOperations() + minClassRenameDiff.getMatchResult().getMatchedAttributes();
if(matchedMembers2 > matchedMembers1) {
minClassRenameDiff.process();
classRenameDiffList.add(minClassRenameDiff);
addedClasses.remove(minClassRenameDiff.getRenamedClass());
removedClassIterator.remove();
}
else {
minClassMoveDiff.process();
classMoveDiffList.add(minClassMoveDiff);
addedClasses.remove(minClassMoveDiff.getMovedClass());
removedClassIterator.remove();
}
}
else {
minClassMoveDiff.process();
classMoveDiffList.add(minClassMoveDiff);
addedClasses.remove(minClassMoveDiff.getMovedClass());
removedClassIterator.remove();
}
}
}
}
else {
for(Iterator addedClassIterator = addedClasses.iterator(); addedClassIterator.hasNext();) {
UMLClass addedClass = addedClassIterator.next();
TreeSet diffSet = new TreeSet(new ClassMoveComparator());
for(Iterator removedClassIterator = removedClasses.iterator(); removedClassIterator.hasNext();) {
UMLClass removedClass = removedClassIterator.next();
String removedClassSourceFile = removedClass.getSourceFile();
String removedClassSourceFolder = "";
if(removedClassSourceFile.contains("/")) {
removedClassSourceFolder = removedClassSourceFile.substring(0, removedClassSourceFile.lastIndexOf("/"));
}
if(!repositoryDirectories.contains(removedClassSourceFolder)) {
deletedFolderPaths.add(removedClassSourceFolder);
//add deleted sub-directories
String subDirectory = new String(removedClassSourceFolder);
while(subDirectory.contains("/")) {
subDirectory = subDirectory.substring(0, subDirectory.lastIndexOf("/"));
if(!repositoryDirectories.contains(subDirectory)) {
deletedFolderPaths.add(subDirectory);
}
}
}
MatchResult matchResult = matcher.match(removedClass, addedClass);
if(matchResult.isMatch()) {
if(!conflictingMoveOfTopLevelClass(removedClass, addedClass)) {
UMLClassMoveDiff classMoveDiff = new UMLClassMoveDiff(removedClass, addedClass, this, matchResult);
diffSet.add(classMoveDiff);
}
}
}
if(!diffSet.isEmpty()) {
UMLClassMoveDiff minClassMoveDiff = diffSet.first();
TreeSet renameDiffSet = new TreeSet<>();
if(matcher instanceof UMLClassMatcher.RelaxedMove) {
renameDiffSet = findRenameMatchesForAddedClass(addedClass, new UMLClassMatcher.RelaxedRename());
}
if(!renameDiffSet.isEmpty() && !(renameDiffSet.first().getOriginalClass().equals(minClassMoveDiff.getOriginalClass()) &&
renameDiffSet.first().getRenamedClass().equals(minClassMoveDiff.getMovedClass()))) {
UMLClassRenameDiff minClassRenameDiff = renameDiffSet.first();
int matchedMembers1 = minClassMoveDiff.getMatchResult().getMatchedOperations() + minClassMoveDiff.getMatchResult().getMatchedAttributes();
int matchedMembers2 = minClassRenameDiff.getMatchResult().getMatchedOperations() + minClassRenameDiff.getMatchResult().getMatchedAttributes();
if(matchedMembers2 > matchedMembers1) {
minClassRenameDiff.process();
classRenameDiffList.add(minClassRenameDiff);
removedClasses.remove(minClassRenameDiff.getOriginalClass());
addedClassIterator.remove();
}
else {
minClassMoveDiff.process();
classMoveDiffList.add(minClassMoveDiff);
removedClasses.remove(minClassMoveDiff.getOriginalClass());
addedClassIterator.remove();
}
}
else {
minClassMoveDiff.process();
classMoveDiffList.add(minClassMoveDiff);
removedClasses.remove(minClassMoveDiff.getOriginalClass());
addedClassIterator.remove();
}
}
}
}
List allClassMoves = new ArrayList(this.classMoveDiffList);
Collections.sort(allClassMoves);
for(int i=0; i union) {
if(union.size() > 1) {
UMLClass renamedClass = null;
for(UMLClassRenameDiff renameDiff : union) {
if(renamedClass == null) {
renamedClass = renameDiff.getRenamedClass();
}
else if(!renamedClass.equals(renameDiff.getRenamedClass())) {
return false;
}
}
return true;
}
return false;
}
private boolean sameOriginalClass(Set union) {
if(union.size() > 1) {
UMLClass originalClass = null;
for(UMLClassRenameDiff renameDiff : union) {
if(originalClass == null) {
originalClass = renameDiff.getOriginalClass();
}
else if(!originalClass.equals(renameDiff.getOriginalClass())) {
return false;
}
}
return true;
}
return false;
}
private boolean inheritanceRelationshipBetweenMergedClasses(Set union) {
if(union.size() > 1) {
UMLClass originalClass = null;
for(UMLClassRenameDiff renameDiff : union) {
if(originalClass == null) {
originalClass = renameDiff.getOriginalClass();
}
else if(isSubclassOf(originalClass.getName(), renameDiff.getOriginalClass().getName()) ||
isSubclassOf(renameDiff.getOriginalClass().getName(), originalClass.getName())) {
return true;
}
}
}
return false;
}
private boolean inheritanceRelationshipBetweenSplitClasses(Set union) {
if(union.size() > 1) {
UMLClass renamedClass = null;
for(UMLClassRenameDiff renameDiff : union) {
if(renamedClass == null) {
renamedClass = renameDiff.getRenamedClass();
}
else if(isSubclassOf(renamedClass.getName(), renameDiff.getRenamedClass().getName()) ||
isSubclassOf(renameDiff.getRenamedClass().getName(), renamedClass.getName()) ||
renamedClass.hasCommonOperationWithTheSameSignature(renameDiff.getRenamedClass()).isMatch()) {
return true;
}
}
}
return false;
}
public void checkForRenamedClasses(UMLClassMatcher matcher) throws RefactoringMinerTimedOutException {
if(removedClasses.size() <= addedClasses.size()) {
Set mergedClassesToBeRemoved = new HashSet();
for(Iterator removedClassIterator = removedClasses.iterator(); removedClassIterator.hasNext();) {
UMLClass removedClass = removedClassIterator.next();
TreeSet diffSet = findRenameMatchesForRemovedClass(removedClass, matcher);
if(!diffSet.isEmpty()) {
UMLClassRenameDiff minClassRenameDiff = diffSet.first();
boolean mergeFound = false;
boolean splitFound = false;
boolean conflictFound = false;
TreeSet renameDiffSet = findRenameMatchesForAddedClass(minClassRenameDiff.getRenamedClass(), matcher);
TreeSet union = new TreeSet<>();
union.addAll(diffSet);
union.addAll(renameDiffSet);
if(matcher instanceof UMLClassMatcher.RelaxedRename) {
if(sameRenamedClass(union) && !inheritanceRelationshipBetweenMergedClasses(union) && !partialModel()) {
UMLClassMergeDiff mergeDiff = new UMLClassMergeDiff(union);
classMergeDiffList.add(mergeDiff);
for(UMLClassRenameDiff renameDiff : union) {
addedClasses.remove(renameDiff.getRenamedClass());
mergedClassesToBeRemoved.add(renameDiff.getOriginalClass());
}
removedClassIterator.remove();
mergeFound = true;
}
else if(sameOriginalClass(union) && !inheritanceRelationshipBetweenSplitClasses(union) && !partialModel()) {
UMLClassSplitDiff splitDiff = new UMLClassSplitDiff(union);
classSplitDiffList.add(splitDiff);
for(UMLClassRenameDiff renameDiff : union) {
addedClasses.remove(renameDiff.getRenamedClass());
mergedClassesToBeRemoved.add(renameDiff.getOriginalClass());
}
removedClassIterator.remove();
splitFound = true;
}
}
else if(matcher instanceof UMLClassMatcher.Rename) {
if((union.size() > 2 && sameRenamedClass(union)) || (renameDiffSet.size() > 2 && sameRenamedClass(renameDiffSet))) {
for(UMLClassRenameDiff renameDiff : union) {
addedClasses.remove(renameDiff.getRenamedClass());
mergedClassesToBeRemoved.add(renameDiff.getOriginalClass());
}
removedClassIterator.remove();
conflictFound = true;
}
}
if(!mergeFound && !splitFound && !conflictFound) {
if(!renameDiffSet.isEmpty() && !(renameDiffSet.first().getOriginalClass().equals(minClassRenameDiff.getOriginalClass()) &&
renameDiffSet.first().getRenamedClass().equals(minClassRenameDiff.getRenamedClass()))) {
UMLClassRenameDiff minClassRenameDiff2 = renameDiffSet.first();
int matchedMembers1 = minClassRenameDiff.getMatchResult().getMatchedOperations() + minClassRenameDiff.getMatchResult().getMatchedAttributes();
int matchedMembers2 = minClassRenameDiff2.getMatchResult().getMatchedOperations() + minClassRenameDiff2.getMatchResult().getMatchedAttributes();
if(matchedMembers2 > matchedMembers1) {
minClassRenameDiff2.process();
classRenameDiffList.add(minClassRenameDiff2);
addedClasses.remove(minClassRenameDiff2.getRenamedClass());
removedClassIterator.remove();
}
else {
minClassRenameDiff.process();
classRenameDiffList.add(minClassRenameDiff);
addedClasses.remove(minClassRenameDiff.getRenamedClass());
removedClassIterator.remove();
}
}
else {
minClassRenameDiff.process();
classRenameDiffList.add(minClassRenameDiff);
addedClasses.remove(minClassRenameDiff.getRenamedClass());
removedClassIterator.remove();
}
}
}
}
removedClasses.removeAll(mergedClassesToBeRemoved);
}
else {
for(Iterator addedClassIterator = addedClasses.iterator(); addedClassIterator.hasNext();) {
UMLClass addedClass = addedClassIterator.next();
TreeSet diffSet = findRenameMatchesForAddedClass(addedClass, matcher);
if(!diffSet.isEmpty()) {
UMLClassRenameDiff minClassRenameDiff = diffSet.first();
boolean mergeFound = false;
boolean splitFound = false;
boolean conflictFound = false;
TreeSet renameDiffSet = findRenameMatchesForRemovedClass(minClassRenameDiff.getOriginalClass(), matcher);
TreeSet union = new TreeSet<>();
union.addAll(diffSet);
union.addAll(renameDiffSet);
if(matcher instanceof UMLClassMatcher.RelaxedRename) {
if(sameRenamedClass(union) && !inheritanceRelationshipBetweenMergedClasses(union) && !partialModel()) {
UMLClassMergeDiff mergeDiff = new UMLClassMergeDiff(union);
classMergeDiffList.add(mergeDiff);
for(UMLClassRenameDiff renameDiff : union) {
removedClasses.remove(renameDiff.getOriginalClass());
}
addedClassIterator.remove();
mergeFound = true;
}
else if(sameOriginalClass(union) && !inheritanceRelationshipBetweenSplitClasses(union) && !partialModel()) {
UMLClassSplitDiff splitDiff = new UMLClassSplitDiff(union);
classSplitDiffList.add(splitDiff);
for(UMLClassRenameDiff renameDiff : union) {
removedClasses.remove(renameDiff.getOriginalClass());
}
addedClassIterator.remove();
splitFound = true;
}
}
else if(matcher instanceof UMLClassMatcher.Rename) {
if((union.size() > 2 && sameRenamedClass(union)) || (renameDiffSet.size() > 2 && sameRenamedClass(renameDiffSet))) {
for(UMLClassRenameDiff renameDiff : union) {
removedClasses.remove(renameDiff.getOriginalClass());
}
addedClassIterator.remove();
conflictFound = true;
}
}
if(!mergeFound && !splitFound && !conflictFound) {
if(!renameDiffSet.isEmpty() && !(renameDiffSet.first().getOriginalClass().equals(minClassRenameDiff.getOriginalClass()) &&
renameDiffSet.first().getRenamedClass().equals(minClassRenameDiff.getRenamedClass()))) {
UMLClassRenameDiff minClassRenameDiff2 = renameDiffSet.first();
int matchedMembers1 = minClassRenameDiff.getMatchResult().getMatchedOperations() + minClassRenameDiff.getMatchResult().getMatchedAttributes();
int matchedMembers2 = minClassRenameDiff2.getMatchResult().getMatchedOperations() + minClassRenameDiff2.getMatchResult().getMatchedAttributes();
if(matchedMembers2 > matchedMembers1) {
minClassRenameDiff2.process();
classRenameDiffList.add(minClassRenameDiff2);
removedClasses.remove(minClassRenameDiff2.getOriginalClass());
addedClassIterator.remove();
}
else {
minClassRenameDiff.process();
classRenameDiffList.add(minClassRenameDiff);
removedClasses.remove(minClassRenameDiff.getOriginalClass());
addedClassIterator.remove();
}
}
else {
minClassRenameDiff.process();
classRenameDiffList.add(minClassRenameDiff);
removedClasses.remove(minClassRenameDiff.getOriginalClass());
addedClassIterator.remove();
}
}
}
}
}
List allClassMoves = new ArrayList(this.classMoveDiffList);
Collections.sort(allClassMoves);
for(UMLClassRenameDiff classRename : classRenameDiffList) {
for(UMLClassMoveDiff classMove : allClassMoves) {
if(classRename.isInnerClassMove(classMove)) {
innerClassMoveDiffList.add(classMove);
}
}
}
this.classMoveDiffList.removeAll(innerClassMoveDiffList);
}
private TreeSet findRenameMatchesForRemovedClass(UMLClass removedClass, UMLClassMatcher matcher) {
TreeSet diffSet = new TreeSet(new ClassRenameComparator());
for(Iterator addedClassIterator = addedClasses.iterator(); addedClassIterator.hasNext();) {
UMLClass addedClass = addedClassIterator.next();
if(matcher instanceof UMLClassMatcher.RelaxedRename) {
Pair pair = Pair.of(removedClass, addedClass);
if(processedClassPairs.contains(pair)) {
continue;
}
else {
processedClassPairs.add(pair);
}
}
int matchingMovedInnerClasses = 0;
MatchResult matchResult = matcher.match(removedClass, addedClass);
if((addedClass.getAttributes().size() == 0 && addedClass.getOperations().size() == 0 &&
removedClass.getAttributes().size() == 0 && removedClass.getOperations().size() == 0) ||
matchResult.getMatchedOperations() >= 10) {
for(UMLClassMoveDiff classMoveDiff : classMoveDiffList) {
if(classMoveDiff.getOriginalClass().getName().startsWith(removedClass.getName() + ".") &&
(classMoveDiff.getMovedClass().getName().startsWith(addedClass.getName() + ".") ||
classMoveDiff.getMovedClass().getPackageName().equals(addedClass.getPackageName()))) {
matchingMovedInnerClasses++;
}
}
}
if(matchResult.isMatch() || matchingMovedInnerClasses > 0) {
if(!conflictingMoveOfTopLevelClass(removedClass, addedClass) && !innerClassWithTheSameName(removedClass, addedClass)) {
UMLClassRenameDiff classRenameDiff = new UMLClassRenameDiff(removedClass, addedClass, this, matchResult);
if(!classRenameDiff.getOriginalClass().getNonQualifiedName().equals(classRenameDiff.getRenamedClass().getNonQualifiedName())) {
diffSet.add(classRenameDiff);
}
}
}
}
return diffSet;
}
private TreeSet findRenameMatchesForAddedClass(UMLClass addedClass, UMLClassMatcher matcher) {
TreeSet diffSet = new TreeSet(new ClassRenameComparator());
for(Iterator removedClassIterator = removedClasses.iterator(); removedClassIterator.hasNext();) {
UMLClass removedClass = removedClassIterator.next();
if(matcher instanceof UMLClassMatcher.RelaxedRename) {
Pair pair = Pair.of(removedClass, addedClass);
if(processedClassPairs.contains(pair)) {
continue;
}
else {
processedClassPairs.add(pair);
}
}
int matchingMovedInnerClasses = 0;
MatchResult matchResult = matcher.match(removedClass, addedClass);
if((addedClass.getAttributes().size() == 0 && addedClass.getOperations().size() == 0 &&
removedClass.getAttributes().size() == 0 && removedClass.getOperations().size() == 0) ||
matchResult.getMatchedOperations() >= 10) {
for(UMLClassMoveDiff classMoveDiff : classMoveDiffList) {
if(classMoveDiff.getOriginalClass().getName().startsWith(removedClass.getName() + ".") &&
(classMoveDiff.getMovedClass().getName().startsWith(addedClass.getName() + ".") ||
classMoveDiff.getMovedClass().getPackageName().equals(addedClass.getPackageName()))) {
matchingMovedInnerClasses++;
}
}
}
if(matchResult.isMatch() || matchingMovedInnerClasses > 0) {
if(!conflictingMoveOfTopLevelClass(removedClass, addedClass) && !innerClassWithTheSameName(removedClass, addedClass)) {
UMLClassRenameDiff classRenameDiff = new UMLClassRenameDiff(removedClass, addedClass, this, matchResult);
if(!classRenameDiff.getOriginalClass().getNonQualifiedName().equals(classRenameDiff.getRenamedClass().getNonQualifiedName())) {
diffSet.add(classRenameDiff);
}
}
}
}
return diffSet;
}
private boolean innerClassWithTheSameName(UMLClass removedClass, UMLClass addedClass) {
if(!removedClass.isTopLevel() && !addedClass.isTopLevel()) {
String removedClassName = removedClass.getName();
String removedName = removedClassName.substring(removedClassName.lastIndexOf(".")+1, removedClassName.length());
String addedClassName = addedClass.getName();
String addedName = addedClassName.substring(addedClassName.lastIndexOf(".")+1, addedClassName.length());
if(removedName.equals(addedName)) {
return true;
}
}
return false;
}
public void inferClassRenameBasedOnFilePaths(UMLClassMatcher matcher) throws RefactoringMinerTimedOutException {
Set diffsToBeAdded = new LinkedHashSet();
Set addedClassesToBeRemoved = new LinkedHashSet();
Set removedClassesToBeRemoved = new LinkedHashSet();
for(Iterator removedClassIterator = removedClasses.iterator(); removedClassIterator.hasNext();) {
UMLClass removedClass = removedClassIterator.next();
String removedClassFilePath = removedClass.getSourceFile();
for(Iterator addedClassIterator = addedClasses.iterator(); addedClassIterator.hasNext();) {
UMLClass addedClass = addedClassIterator.next();
String addedClassFilePath = addedClass.getSourceFile();
for(UMLClassRenameDiff classRenameDiff : classRenameDiffList) {
if(classRenameDiff.getOriginalClass().getSourceFile().equals(removedClassFilePath) &&
classRenameDiff.getNextClass().getSourceFile().equals(addedClassFilePath)) {
MatchResult matchResult = matcher.match(removedClass, addedClass);
if(matchResult.getMatchedOperations() > 0 || matchResult.getMatchedAttributes() > 0) {
UMLClassRenameDiff newClassRenameDiff = new UMLClassRenameDiff(removedClass, addedClass, this, matchResult);
newClassRenameDiff.process();
diffsToBeAdded.add(newClassRenameDiff);
addedClassesToBeRemoved.add(addedClass);
removedClassesToBeRemoved.add(removedClass);
break;
}
}
}
}
}
classRenameDiffList.addAll(diffsToBeAdded);
removedClasses.removeAll(removedClassesToBeRemoved);
addedClasses.removeAll(addedClassesToBeRemoved);
}
public List getAddedGeneralizations() {
return addedGeneralizations;
}
public List getAddedRealizations() {
return addedRealizations;
}
private List checkForAttributeMovesIncludingRemovedClasses(Map> renameMap, Set refactorings) throws RefactoringMinerTimedOutException {
List addedAttributes = getAddedAttributesInCommonClasses();
/*for(UMLClass addedClass : addedClasses) {
addedAttributes.addAll(addedClass.getAttributes());
}*/
List removedAttributes = getRemovedAttributesInCommonClasses();
for(UMLClass removedClass : removedClasses) {
removedAttributes.addAll(removedClass.getAttributes());
}
return checkForAttributeMoves(addedAttributes, removedAttributes, renameMap, refactorings);
}
private List checkForAttributeMovesIncludingAddedClasses(Map> renameMap, Set refactorings) throws RefactoringMinerTimedOutException {
List addedAttributes = getAddedAttributesInCommonClasses();
for(UMLClass addedClass : addedClasses) {
addedAttributes.addAll(addedClass.getAttributes());
}
List removedAttributes = getRemovedAttributesInCommonClasses();
/*for(UMLClass removedClass : removedClasses) {
removedAttributes.addAll(removedClass.getAttributes());
}*/
return checkForAttributeMoves(addedAttributes, removedAttributes, renameMap, refactorings);
}
private List checkForAttributeMovesBetweenCommonClasses(Map> renameMap, Set refactorings) throws RefactoringMinerTimedOutException {
List addedAttributes = getAddedAttributesInCommonClasses();
List removedAttributes = getRemovedAttributesInCommonClasses();
return checkForAttributeMoves(addedAttributes, removedAttributes, renameMap, refactorings);
}
private List checkForAttributeMovesBetweenRemovedAndAddedClasses(Map> renameMap, Set refactorings) throws RefactoringMinerTimedOutException {
List addedAttributes = new ArrayList();
for(UMLClass addedClass : addedClasses) {
addedAttributes.addAll(addedClass.getAttributes());
}
List removedAttributes = new ArrayList();
for(UMLClass removedClass : removedClasses) {
removedAttributes.addAll(removedClass.getAttributes());
}
return checkForAttributeMoves(addedAttributes, removedAttributes, renameMap, refactorings);
}
private List checkForAttributeMoves(List addedAttributes, List removedAttributes,
Map> renameMap, Set pastRefactorings) throws RefactoringMinerTimedOutException {
List refactorings = new ArrayList();
if(addedAttributes.size() <= removedAttributes.size()) {
for(UMLAttribute addedAttribute : addedAttributes) {
List candidates = new ArrayList();
for(UMLAttribute removedAttribute : removedAttributes) {
MoveAttributeRefactoring candidate = processPairOfAttributes(addedAttribute, removedAttribute, renameMap, pastRefactorings);
if(candidate != null) {
candidates.add(candidate);
}
}
processCandidates(candidates, refactorings, pastRefactorings);
}
}
else {
for(UMLAttribute removedAttribute : removedAttributes) {
List candidates = new ArrayList();
for(UMLAttribute addedAttribute : addedAttributes) {
MoveAttributeRefactoring candidate = processPairOfAttributes(addedAttribute, removedAttribute, renameMap, pastRefactorings);
if(candidate != null) {
candidates.add(candidate);
}
}
processCandidates(candidates, refactorings, pastRefactorings);
}
}
return refactorings;
}
private List filterOutDuplicateRefactorings(Set refactorings) {
List filtered = new ArrayList();
Map> map = new LinkedHashMap>();
for(Refactoring ref : refactorings) {
if(map.containsKey(ref.toString())) {
map.get(ref.toString()).add(ref);
}
else {
List refs = new ArrayList();
refs.add(ref);
map.put(ref.toString(), refs);
}
}
for(String key : map.keySet()) {
List refs = map.get(key);
if(refs.size() == 1) {
filtered.addAll(refs);
}
else {
filtered.addAll(filterOutBasedOnFilePath(refs));
}
}
return filtered;
}
private List filterOutBasedOnFilePath(List refs) {
List filtered = new ArrayList();
Map> groupBySourceFilePath = new LinkedHashMap>();
for(Refactoring ref : refs) {
String sourceFilePath = ref.getInvolvedClassesBeforeRefactoring().iterator().next().getLeft();
if(groupBySourceFilePath.containsKey(sourceFilePath)) {
groupBySourceFilePath.get(sourceFilePath).add(ref);
}
else {
List refs2 = new ArrayList();
refs2.add(ref);
groupBySourceFilePath.put(sourceFilePath, refs2);
}
}
for(String sourceFilePath : groupBySourceFilePath.keySet()) {
List sourceFilePathGroup = groupBySourceFilePath.get(sourceFilePath);
TreeMap> groupByLongestCommonSourceFilePath = new TreeMap>();
for(Refactoring ref : sourceFilePathGroup) {
String longestCommonFilePathPrefix = PrefixSuffixUtils.longestCommonPrefix(ref.getInvolvedClassesBeforeRefactoring().iterator().next().getLeft(),
ref.getInvolvedClassesAfterRefactoring().iterator().next().getLeft());
int length = longestCommonFilePathPrefix.length();
if(groupByLongestCommonSourceFilePath.containsKey(length)) {
groupByLongestCommonSourceFilePath.get(length).add(ref);
}
else {
List refs2 = new ArrayList();
refs2.add(ref);
groupByLongestCommonSourceFilePath.put(length, refs2);
}
}
filtered.addAll(groupByLongestCommonSourceFilePath.lastEntry().getValue());
}
return filtered;
}
private void processCandidates(List candidates, List refactorings, Set pastRefactorings) throws RefactoringMinerTimedOutException {
if(candidates.size() > 1) {
TreeMap> map = new TreeMap>();
for(MoveAttributeRefactoring candidate : candidates) {
int compatibility = computeCompatibility(candidate);
if(map.containsKey(compatibility)) {
map.get(compatibility).add(candidate);
}
else {
List refs = new ArrayList();
refs.add(candidate);
map.put(compatibility, refs);
}
}
int maxCompatibility = map.lastKey();
if(maxCompatibility > 0) {
refactorings.addAll(map.get(maxCompatibility));
for(MoveAttributeRefactoring moveAttributeRefactoring : map.get(maxCompatibility)) {
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(moveAttributeRefactoring.getOriginalAttribute(), moveAttributeRefactoring.getMovedAttribute(), Collections.emptyList());
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
pastRefactorings.addAll(attributeDiff.getRefactorings());
}
}
}
else if(candidates.size() == 1) {
MoveAttributeRefactoring conflictingRefactoring = null;
for(Refactoring r : refactorings) {
if(r instanceof MoveAttributeRefactoring && !r.getRefactoringType().equals(RefactoringType.PUSH_DOWN_ATTRIBUTE)) {
MoveAttributeRefactoring old = (MoveAttributeRefactoring)r;
if(old.getOriginalAttribute().getVariableDeclaration().equals(candidates.get(0).getOriginalAttribute().getVariableDeclaration())) {
conflictingRefactoring = old;
break;
}
}
}
if(conflictingRefactoring == null) {
refactorings.addAll(candidates);
for(MoveAttributeRefactoring moveAttributeRefactoring : candidates) {
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(moveAttributeRefactoring.getOriginalAttribute(), moveAttributeRefactoring.getMovedAttribute(), Collections.emptyList());
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
pastRefactorings.addAll(attributeDiff.getRefactorings());
}
}
else if((conflictingRefactoring.getRefactoringType().equals(RefactoringType.MOVE_ATTRIBUTE) || conflictingRefactoring.getRefactoringType().equals(RefactoringType.MOVE_RENAME_ATTRIBUTE)) &&
candidates.get(0).getRefactoringType().equals(RefactoringType.PULL_UP_ATTRIBUTE)) {
refactorings.remove(conflictingRefactoring);
UMLAttributeDiff conflictingAttributeDiff = new UMLAttributeDiff(conflictingRefactoring.getOriginalAttribute(), conflictingRefactoring.getMovedAttribute(), Collections.emptyList());
pastRefactorings.removeAll(conflictingAttributeDiff.getRefactorings());
refactorings.addAll(candidates);
for(MoveAttributeRefactoring moveAttributeRefactoring : candidates) {
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(moveAttributeRefactoring.getOriginalAttribute(), moveAttributeRefactoring.getMovedAttribute(), Collections.emptyList());
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
pastRefactorings.addAll(attributeDiff.getRefactorings());
}
}
}
}
private MoveAttributeRefactoring processPairOfAttributes(UMLAttribute addedAttribute, UMLAttribute removedAttribute, Map> renameMap, Set pastRefactorings) throws RefactoringMinerTimedOutException {
if(!removedAttribute.getName().equals(addedAttribute.getName()) && movedAttributeRenamed(removedAttribute.getVariableDeclaration(), addedAttribute.getVariableDeclaration(), pastRefactorings).size() > 0) {
return null;
}
if(addedAttribute.getName().equals(removedAttribute.getName()) &&
addedAttribute.getType().equals(removedAttribute.getType())) {
if(isSubclassOf(removedAttribute.getClassName(), addedAttribute.getClassName())) {
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(removedAttribute, addedAttribute, Collections.emptyList());
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
PullUpAttributeRefactoring pullUpAttribute = new PullUpAttributeRefactoring(removedAttribute, addedAttribute);
return pullUpAttribute;
}
else if(isSubclassOf(addedAttribute.getClassName(), removedAttribute.getClassName())) {
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(removedAttribute, addedAttribute, Collections.emptyList());
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
PushDownAttributeRefactoring pushDownAttribute = new PushDownAttributeRefactoring(removedAttribute, addedAttribute);
return pushDownAttribute;
}
else if(sourceClassImportsTargetClass(removedAttribute.getClassName(), addedAttribute.getClassName()) ||
targetClassImportsSourceClass(removedAttribute.getClassName(), addedAttribute.getClassName())) {
if(!initializerContainsTypeLiteral(addedAttribute, removedAttribute)) {
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(removedAttribute, addedAttribute, Collections.emptyList());
boolean initializerWithMethodCallReplacement = false;
if(attributeDiff.getInitializerMapper().isPresent()) {
UMLOperationBodyMapper mapper = attributeDiff.getInitializerMapper().get();
for(AbstractCodeMapping mapping : mapper.getMappings()) {
for(Replacement r : mapping.getReplacements()) {
if(r instanceof MethodInvocationReplacement) {
MethodInvocationReplacement replacement = (MethodInvocationReplacement)r;
AbstractCall before = replacement.getInvokedOperationBefore();
AbstractCall after = replacement.getInvokedOperationAfter();
if(before.getExpression() != null && after.getExpression() != null &&
before.getExpression().contains(".") && after.getExpression().contains(".")) {
initializerWithMethodCallReplacement = true;
break;
}
}
}
}
}
if(!initializerWithMethodCallReplacement) {
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
MoveAttributeRefactoring moveAttribute = new MoveAttributeRefactoring(removedAttribute, addedAttribute);
return moveAttribute;
}
}
}
}
if(!removedAttribute.getClassName().equals(addedAttribute.getClassName()) &&
addedAttribute.getType().equals(removedAttribute.getType())) {
Replacement rename = new Replacement(removedAttribute.getName(), addedAttribute.getName(), ReplacementType.VARIABLE_NAME);
if(renameMap.containsKey(rename)) {
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(removedAttribute, addedAttribute, Collections.emptyList());
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
Set candidates = renameMap.get(rename);
MoveAndRenameAttributeRefactoring moveAttribute = new MoveAndRenameAttributeRefactoring(removedAttribute, addedAttribute, candidates);
return moveAttribute;
}
}
return null;
}
private boolean initializerContainsTypeLiteral(VariableDeclaration v1, VariableDeclaration v2) {
if(v1.getInitializer() != null && v2.getInitializer() != null) {
List typeLiterals1 = new ArrayList<>();
for(LeafExpression expression : v1.getInitializer().getTypeLiterals()) {
typeLiterals1.add(expression.getString());
}
List typeLiterals2 = new ArrayList<>();
for(LeafExpression expression : v2.getInitializer().getTypeLiterals()) {
typeLiterals2.add(expression.getString());
}
if(typeLiterals1.equals(typeLiterals2) && typeLiterals1.size() > 0) {
return true;
}
}
return false;
}
private boolean initializerContainsTypeLiteral(UMLAttribute addedAttribute, UMLAttribute removedAttribute) {
VariableDeclaration v1 = addedAttribute.getVariableDeclaration();
VariableDeclaration v2 = removedAttribute.getVariableDeclaration();
if(v1.getInitializer() != null && v2.getInitializer() != null) {
List typeLiterals1 = new ArrayList<>();
for(LeafExpression expression : v1.getInitializer().getTypeLiterals()) {
typeLiterals1.add(expression.getString());
}
List typeLiterals2 = new ArrayList<>();
for(LeafExpression expression : v2.getInitializer().getTypeLiterals()) {
typeLiterals2.add(expression.getString());
}
String className1 = addedAttribute.getNonQualifiedClassName();
String className2 = removedAttribute.getNonQualifiedClassName();
if(typeLiterals1.contains(className1 + ".class") && typeLiterals2.contains(className2 + ".class") &&
addedAttribute.getType().getClassType().endsWith("Logger") && removedAttribute.getType().getClassType().endsWith("Logger")) {
return true;
}
}
return false;
}
private int computeCompatibility(MoveAttributeRefactoring candidate) {
int count = 0;
for(Refactoring ref : refactorings) {
if(ref instanceof MoveOperationRefactoring) {
MoveOperationRefactoring moveRef = (MoveOperationRefactoring)ref;
if(moveRef.compatibleWith(candidate)) {
count++;
}
}
else if(ref.getRefactoringType().equals(RefactoringType.MOVE_AND_INLINE_OPERATION)) {
InlineOperationRefactoring inlineRef = (InlineOperationRefactoring)ref;
if(candidate.getMovedAttribute().getClassName().equals(inlineRef.getTargetOperationAfterInline().getClassName()) &&
candidate.getOriginalAttribute().getClassName().equals(inlineRef.getInlinedOperation().getClassName())) {
List originalOperationVariables = inlineRef.getTargetOperationAfterInline().getAllVariables();
List movedOperationVariables = inlineRef.getInlinedOperation().getAllVariables();
if(originalOperationVariables.contains(candidate.getOriginalAttribute().getName()) &&
movedOperationVariables.contains(candidate.getMovedAttribute().getName())) {
count++;
}
}
}
}
UMLClassBaseDiff sourceClassDiff = getUMLClassDiff(candidate.getSourceClassName());
UMLClassBaseDiff targetClassDiff = getUMLClassDiff(candidate.getTargetClassName());
if(sourceClassDiff != null) {
UMLType targetSuperclass = null;
if(targetClassDiff != null) {
targetSuperclass = targetClassDiff.getSuperclass();
}
List addedAttributes = sourceClassDiff.getAddedAttributes();
for(UMLAttribute addedAttribute : addedAttributes) {
if(looksLikeSameType(addedAttribute.getType().getClassType(), candidate.getTargetClassName())) {
count++;
}
if(targetSuperclass != null && looksLikeSameType(addedAttribute.getType().getClassType(), targetSuperclass.getClassType())) {
count++;
}
if(targetClassDiff != null) {
for(UMLType addedImplementedInterface : targetClassDiff.getAddedImplementedInterfaces()) {
if(looksLikeSameType(addedAttribute.getType().getClassType(), addedImplementedInterface.getClassType())) {
count++;
}
}
}
}
List originalAttributes = sourceClassDiff.originalClassAttributesOfType(candidate.getTargetClassName());
List nextAttributes = sourceClassDiff.nextClassAttributesOfType(candidate.getTargetClassName());
if(targetSuperclass != null) {
originalAttributes.addAll(sourceClassDiff.originalClassAttributesOfType(targetSuperclass.getClassType()));
nextAttributes.addAll(sourceClassDiff.nextClassAttributesOfType(targetSuperclass.getClassType()));
}
if(targetClassDiff != null) {
for(UMLType addedImplementedInterface : targetClassDiff.getAddedImplementedInterfaces()) {
originalAttributes.addAll(sourceClassDiff.originalClassAttributesOfType(addedImplementedInterface.getClassType()));
nextAttributes.addAll(sourceClassDiff.nextClassAttributesOfType(addedImplementedInterface.getClassType()));
}
}
Set intersection = new LinkedHashSet(originalAttributes);
intersection.retainAll(nextAttributes);
if(!intersection.isEmpty()) {
count++;
}
}
if(sourceClassDiff != null && targetClassDiff != null) {
boolean parameterDeletedFromConstructor = false;
for(UMLOperationBodyMapper mapper : sourceClassDiff.getOperationBodyMapperList()) {
if(mapper.getContainer1().isConstructor() && mapper.getContainer2().isConstructor()) {
if(mapper.getOperationSignatureDiff().isPresent()) {
UMLOperationDiff signatureDiff = mapper.getOperationSignatureDiff().get();
for(UMLParameter parameter : signatureDiff.getRemovedParameters()) {
if(parameter.getVariableDeclaration().toString().equals(candidate.getOriginalAttribute().getVariableDeclaration().toString())) {
parameterDeletedFromConstructor = true;
break;
}
}
}
}
}
boolean parameterAddedToConstructor = false;
for(UMLOperationBodyMapper mapper : targetClassDiff.getOperationBodyMapperList()) {
if(mapper.getContainer1().isConstructor() && mapper.getContainer2().isConstructor()) {
if(mapper.getOperationSignatureDiff().isPresent()) {
UMLOperationDiff signatureDiff = mapper.getOperationSignatureDiff().get();
for(UMLParameter parameter : signatureDiff.getAddedParameters()) {
if(parameter.getVariableDeclaration().toString().equals(candidate.getMovedAttribute().getVariableDeclaration().toString())) {
parameterAddedToConstructor = true;
break;
}
}
}
}
}
if(parameterDeletedFromConstructor && parameterAddedToConstructor) {
count++;
}
}
//moved from inner class
if(sourceClassDiff != null && targetClassDiff != null &&
targetClassDiff.getOriginalClass().isInnerClass(sourceClassDiff.getOriginalClass()) &&
targetClassDiff.getNextClass().isInnerClass(sourceClassDiff.getNextClass())) {
count++;
}
//moved to superclass
if(candidate.getRefactoringType().equals(RefactoringType.PULL_UP_ATTRIBUTE)) {
count++;
}
return count;
}
private boolean sourceClassImportsSuperclassOfTargetClass(String sourceClassName, String targetClassName) {
UMLClassBaseDiff targetClassDiff = getUMLClassDiff(targetClassName);
if(targetClassDiff != null && targetClassDiff.getSuperclass() != null) {
UMLClassBaseDiff superclassOfTargetClassDiff = getUMLClassDiff(targetClassDiff.getSuperclass());
if(superclassOfTargetClassDiff != null) {
return sourceClassImportsTargetClass(sourceClassName, superclassOfTargetClassDiff.getNextClassName());
}
}
return false;
}
private boolean sourceClassImportsTargetClass(String sourceClassName, String targetClassName) {
UMLClassBaseDiff classDiff = getUMLClassDiff(sourceClassName);
if(classDiff == null) {
classDiff = getUMLClassDiff(UMLType.extractTypeObject(sourceClassName));
}
if(classDiff != null) {
return classDiff.nextClassImportsType(targetClassName) || classDiff.originalClassImportsType(targetClassName);
}
UMLClass removedClass = getRemovedClass(sourceClassName);
if(removedClass == null) {
removedClass = looksLikeRemovedClass(UMLType.extractTypeObject(sourceClassName));
}
if(removedClass != null) {
return removedClass.importsType(targetClassName);
}
return false;
}
private boolean targetClassImportsSourceClass(String sourceClassName, String targetClassName) {
UMLClassBaseDiff classDiff = getUMLClassDiff(targetClassName);
if(classDiff == null) {
classDiff = getUMLClassDiff(UMLType.extractTypeObject(targetClassName));
}
if(classDiff != null) {
return classDiff.originalClassImportsType(sourceClassName) || classDiff.nextClassImportsType(sourceClassName);
}
UMLClass addedClass = getAddedClass(targetClassName);
if(addedClass == null) {
addedClass = looksLikeAddedClass(UMLType.extractTypeObject(targetClassName));
}
if(addedClass != null) {
return addedClass.importsType(sourceClassName);
}
return false;
}
private List getAddedAttributesInCommonClasses() {
List addedAttributes = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
addedAttributes.addAll(classDiff.getAddedAttributes());
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
addedAttributes.addAll(classDiff.getAddedAttributes());
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
addedAttributes.addAll(classDiff.getAddedAttributes());
}
return addedAttributes;
}
private List getRemovedAttributesInCommonClasses() {
List removedAttributes = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
removedAttributes.addAll(classDiff.getRemovedAttributes());
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
removedAttributes.addAll(classDiff.getRemovedAttributes());
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
removedAttributes.addAll(classDiff.getRemovedAttributes());
}
return removedAttributes;
}
private List getOperationsInAddedClasses() {
List addedOperations = new ArrayList();
for(UMLClass addedClass : addedClasses) {
addedOperations.addAll(addedClass.getOperations());
}
return addedOperations;
}
private List getAddedOperationsInCommonClasses() {
List addedOperations = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
addedOperations.addAll(classDiff.getAddedOperations());
}
return addedOperations;
}
private List getAddedAndExtractedOperationsInCommonClasses() throws RefactoringMinerTimedOutException {
List addedOperations = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
addedOperations.addAll(classDiff.getAddedOperations());
for(Refactoring ref : classDiff.getRefactoringsBeforePostProcessing()) {
if(ref instanceof ExtractOperationRefactoring) {
ExtractOperationRefactoring extractRef = (ExtractOperationRefactoring)ref;
addedOperations.add(extractRef.getExtractedOperation());
}
}
}
return addedOperations;
}
private List getAddedOperationsInMovedAndRenamedClasses() {
List addedOperations = new ArrayList();
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
addedOperations.addAll(classDiff.getAddedOperations());
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
addedOperations.addAll(classDiff.getAddedOperations());
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
addedOperations.addAll(classDiff.getAddedOperations());
}
return addedOperations;
}
private List getRemovedOperationsInCommonClasses() {
List removedOperations = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
removedOperations.addAll(classDiff.getRemovedOperations());
}
return removedOperations;
}
private List getRemovedOperationsInCommonMovedRenamedClasses() {
List removedOperations = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
removedOperations.addAll(classDiff.getRemovedOperations());
}
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
removedOperations.addAll(classDiff.getRemovedOperations());
}
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
removedOperations.addAll(classDiff.getRemovedOperations());
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
removedOperations.addAll(classDiff.getRemovedOperations());
}
return removedOperations;
}
private List getRemovedAndInlinedOperationsInCommonClasses() throws RefactoringMinerTimedOutException {
List removedOperations = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
removedOperations.addAll(classDiff.getRemovedOperations());
for(Refactoring ref : classDiff.getRefactoringsBeforePostProcessing()) {
if(ref instanceof InlineOperationRefactoring) {
InlineOperationRefactoring extractRef = (InlineOperationRefactoring)ref;
removedOperations.add(extractRef.getInlinedOperation());
}
}
}
return removedOperations;
}
private List getOperationBodyMappersInCommonClasses() {
List mappers = new ArrayList();
for(UMLClassDiff classDiff : commonClassDiffList) {
mappers.addAll(classDiff.getOperationBodyMapperList());
}
return mappers;
}
private List getOperationBodyMappersInMovedAndRenamedClasses() {
List mappers = new ArrayList();
for(UMLClassMoveDiff classDiff : classMoveDiffList) {
mappers.addAll(classDiff.getOperationBodyMapperList());
}
for(UMLClassMoveDiff classDiff : innerClassMoveDiffList) {
mappers.addAll(classDiff.getOperationBodyMapperList());
}
for(UMLClassRenameDiff classDiff : classRenameDiffList) {
mappers.addAll(classDiff.getOperationBodyMapperList());
}
return mappers;
}
private List identifyExtractClassRefactorings(List extends UMLClassBaseDiff> classDiffs) throws RefactoringMinerTimedOutException {
List refactorings = new ArrayList();
for(UMLClass addedClass : addedClasses) {
TreeSet candidates = new TreeSet();
UMLType addedClassSuperType = addedClass.getSuperclass();
if(!addedClass.isInterface()) {
for(UMLClassBaseDiff classDiff : classDiffs) {
UMLType classDiffSuperType = classDiff.getNewSuperclass();
boolean commonSuperType = addedClassSuperType != null && classDiffSuperType != null &&
addedClassSuperType.getClassType().equals(classDiffSuperType.getClassType());
boolean commonInterface = false;
for(UMLType addedClassInterface : addedClass.getImplementedInterfaces()) {
for(UMLType classDiffInterface : classDiff.getNextClass().getImplementedInterfaces()) {
if(addedClassInterface.getClassType().equals(classDiffInterface.getClassType())) {
commonInterface = true;
break;
}
}
if(commonInterface)
break;
}
boolean extendsAddedClass = classDiff.getNewSuperclass() != null &&
addedClass.getName().endsWith("." + classDiff.getNewSuperclass().getClassType());
UMLAttribute attributeOfExtractedClassType = attributeOfExtractedClassType(addedClass, classDiff);
boolean isTestClass = addedClass.isTestClass() && classDiff.getOriginalClass().isTestClass();
UMLImportListDiff importDiff = classDiff.getImportDiffList();
boolean foundInAddedImport = false;
if(importDiff != null) {
for(UMLImport addedImport : importDiff.getAddedImports()) {
if(addedImport.getName().contains(addedClass.getName())) {
foundInAddedImport = true;
}
}
}
if((!commonSuperType && !commonInterface && !extendsAddedClass) || attributeOfExtractedClassType != null || isTestClass || foundInAddedImport) {
ExtractClassRefactoring refactoring = atLeastOneCommonAttributeOrOperation(addedClass, classDiff, attributeOfExtractedClassType, foundInAddedImport);
if(refactoring != null) {
CandidateExtractClassRefactoring candidate = new CandidateExtractClassRefactoring(classDiff, refactoring, classMoveDiffList);
candidates.add(candidate);
}
}
}
}
if(!candidates.isEmpty()) {
CandidateExtractClassRefactoring firstCandidate = candidates.first();
if(firstCandidate.innerClassExtract() || firstCandidate.subclassExtract()) {
detectSubRefactorings(firstCandidate.getClassDiff(),
firstCandidate.getRefactoring().getExtractedClass(),
firstCandidate.getRefactoring().getRefactoringType());
refactorings.add(firstCandidate.getRefactoring());
}
else {
for(CandidateExtractClassRefactoring candidate : candidates) {
detectSubRefactorings(candidate.getClassDiff(),
candidate.getRefactoring().getExtractedClass(),
candidate.getRefactoring().getRefactoringType());
refactorings.add(candidate.getRefactoring());
}
}
}
}
return refactorings;
}
private UMLAttribute attributeOfExtractedClassType(UMLClass umlClass, UMLClassBaseDiff classDiff) {
List addedAttributes = classDiff.getAddedAttributes();
for(UMLAttribute addedAttribute : addedAttributes) {
if(umlClass.getName().endsWith("." + addedAttribute.getType().getClassType())) {
return addedAttribute;
}
}
return null;
}
private ExtractClassRefactoring atLeastOneCommonAttributeOrOperation(UMLClass umlClass, UMLClassBaseDiff classDiff, UMLAttribute attributeOfExtractedClassType, boolean addedClassFoundInAddedImport) {
Map commonOperations = new LinkedHashMap<>();
for(UMLOperation operation : classDiff.getRemovedOperations()) {
if(!operation.isConstructor() && !operation.overridesObject()) {
UMLOperation matchedOperation = umlClass.operationWithTheSameSignatureIgnoringChangedTypes(operation);
if(matchedOperation != null &&
operation.getAnonymousClassList().size() == matchedOperation.getAnonymousClassList().size() &&
operation.getSynchronizedStatements().size() == matchedOperation.getSynchronizedStatements().size()) {
commonOperations.put(operation, matchedOperation);
}
}
}
for(UMLOperation operation : classDiff.getRemovedOperations()) {
if(!operation.isConstructor() && !operation.overridesObject() && !commonOperations.containsKey(operation)) {
UMLOperation matchedOperation = umlClass.operationWithTheSameName(operation);
if(matchedOperation != null && !commonOperations.containsValue(matchedOperation)) {
boolean matchedOperationEmptyBody = matchedOperation.getBody() == null || matchedOperation.hasEmptyBody();
boolean operationEmptyBody = operation.getBody() == null || operation.hasEmptyBody();
Set commonParameters = operation.commonParameters(matchedOperation);
if(matchedOperationEmptyBody == operationEmptyBody && (commonParameters.size() > 0 || operation.getParameters().size() == matchedOperation.getParameters().size()) &&
operation.getAnonymousClassList().size() == matchedOperation.getAnonymousClassList().size() &&
operation.getSynchronizedStatements().size() == matchedOperation.getSynchronizedStatements().size()) {
commonOperations.put(operation, matchedOperation);
}
}
}
}
Map commonAttributes = new LinkedHashMap<>();
for(UMLAttribute attribute : classDiff.getRemovedAttributes()) {
UMLAttribute matchedAttribute = umlClass.attributeWithTheSameNameIgnoringChangedType(attribute);
if(matchedAttribute != null) {
commonAttributes.put(attribute, matchedAttribute);
}
}
int threshold = 1;
if(attributeOfExtractedClassType != null || classDiff.getNextClass().isInnerClass(umlClass) || addedClassFoundInAddedImport)
threshold = 0;
if(commonOperations.size() > threshold || commonAttributes.size() > threshold) {
ExtractClassRefactoring extractClassRefactoring = new ExtractClassRefactoring(umlClass, classDiff, commonOperations, commonAttributes, attributeOfExtractedClassType);
Set diffsToBeRemoved = new LinkedHashSet();
for(UMLAttributeDiff diff : classDiff.getAttributeDiffList()) {
if(diff.getAddedAttribute().equals(extractClassRefactoring.getAttributeOfExtractedClassTypeInOriginalClass()) &&
extractClassRefactoring.getExtractedAttributes().keySet().contains(diff.getRemovedAttribute())) {
diffsToBeRemoved.add(diff);
}
}
for(UMLAttributeDiff diff : diffsToBeRemoved) {
classDiff.getAttributeDiffList().remove(diff);
classDiff.getAddedAttributes().add(diff.getAddedAttribute());
classDiff.getRemovedAttributes().add(diff.getRemovedAttribute());
}
return extractClassRefactoring;
}
return null;
}
private List identifyCollapseHierarchyRefactorings() throws RefactoringMinerTimedOutException {
List refactorings = new ArrayList();
for(UMLClass removedClass : removedClasses) {
for(UMLRealization removedRealization : removedRealizations) {
UMLClass client = removedRealization.getClient();
UMLClassBaseDiff supplierClassDiff = getUMLClassDiff(UMLType.extractTypeObject(removedRealization.getSupplier()));
if(removedClass.equals(client) && supplierClassDiff != null) {
int commonOperations = 0;
for(UMLOperation operation : removedClass.getOperations()) {
if(supplierClassDiff.containsConcreteOperationWithTheSameSignatureInNextClass(operation)) {
commonOperations++;
}
}
if(commonOperations > 0) {
CollapseHierarchyRefactoring refactoring = new CollapseHierarchyRefactoring(removedClass, supplierClassDiff.getNextClass());
refactorings.add(refactoring);
for(UMLOperation removedOperation : removedClass.getOperations()) {
UMLOperation addedOperation = supplierClassDiff.containsAddedOperationWithTheSameSignature(removedOperation);
if(addedOperation != null) {
supplierClassDiff.getAddedOperations().remove(addedOperation);
UMLOperationBodyMapper mapper = new UMLOperationBodyMapper(removedOperation, addedOperation, supplierClassDiff);
Refactoring ref = new PullUpOperationRefactoring(mapper);
this.refactorings.add(ref);
this.refactorings.addAll(mapper.getRefactorings());
checkForExtractedOperationsWithinMovedMethod(mapper, supplierClassDiff.getRemovedOperations(), supplierClassDiff.getNextClass(), supplierClassDiff);
}
}
for(UMLAttribute removedAttribute : removedClass.getAttributes()) {
UMLAttribute addedAttribute = supplierClassDiff.containsAddedAttributeWithTheSameSignature(removedAttribute);
if(addedAttribute != null) {
supplierClassDiff.getAddedAttributes().remove(addedAttribute);
Refactoring ref = new PullUpAttributeRefactoring(removedAttribute, addedAttribute);
this.refactorings.add(ref);
}
}
}
}
}
}
return refactorings;
}
private List identifyExtractSuperclassRefactorings() throws RefactoringMinerTimedOutException {
List refactorings = new ArrayList();
for(UMLClass addedClass : addedClasses) {
Set subclassSetBefore = new LinkedHashSet();
Set subclassSetAfter = new LinkedHashSet();
String addedClassName = addedClass.getName();
for(UMLGeneralization addedGeneralization : addedGeneralizations) {
processAddedGeneralization(addedClass, subclassSetBefore, subclassSetAfter, addedGeneralization);
}
for(UMLGeneralizationDiff generalizationDiff : generalizationDiffList) {
UMLGeneralization addedGeneralization = generalizationDiff.getAddedGeneralization();
UMLGeneralization removedGeneralization = generalizationDiff.getRemovedGeneralization();
if(!addedGeneralization.getParent().equals(removedGeneralization.getParent())) {
processAddedGeneralization(addedClass, subclassSetBefore, subclassSetAfter, addedGeneralization);
}
}
for(UMLRealization addedRealization : addedRealizations) {
String supplier = addedRealization.getSupplier();
if(looksLikeSameType(supplier, addedClassName) && topLevelOrSameOuterClass(addedClass, addedRealization.getClient()) && getAddedClass(addedRealization.getClient().getName()) == null) {
UMLClassBaseDiff clientClassDiff = getUMLClassDiff(addedRealization.getClient().getName());
int implementedInterfaceOperations = 0;
boolean clientImplementsSupplier = false;
if(clientClassDiff != null) {
for(UMLOperation interfaceOperation : addedClass.getOperations()) {
if(clientClassDiff.containsOperationWithTheSameSignatureInOriginalClass(interfaceOperation)) {
implementedInterfaceOperations++;
}
}
clientImplementsSupplier = clientClassDiff.getOriginalClass().getImplementedInterfaces().contains(UMLType.extractTypeObject(supplier));
}
if((implementedInterfaceOperations > 0 || addedClass.getOperations().size() == 0) && !clientImplementsSupplier && clientClassDiff != null) {
subclassSetBefore.add(clientClassDiff.getOriginalClass());
subclassSetAfter.add(clientClassDiff.getNextClass());
}
}
}
if(subclassSetBefore.size() > 0) {
ExtractSuperclassRefactoring extractSuperclassRefactoring = new ExtractSuperclassRefactoring(addedClass, subclassSetBefore, subclassSetAfter);
refactorings.add(extractSuperclassRefactoring);
}
}
return refactorings;
}
private void processAddedGeneralization(UMLClass addedClass, Set subclassSetBefore, Set subclassSetAfter, UMLGeneralization addedGeneralization) throws RefactoringMinerTimedOutException {
String parent = addedGeneralization.getParent();
UMLClass subclass = addedGeneralization.getChild();
if(looksLikeSameType(parent, addedClass.getName()) && topLevelOrSameOuterClass(addedClass, subclass) && getAddedClass(subclass.getName()) == null) {
UMLClassBaseDiff subclassDiff = getUMLClassDiff(subclass.getName());
if(subclassDiff != null) {
detectSubRefactorings(subclassDiff, addedClass, RefactoringType.EXTRACT_SUPERCLASS);
subclassSetBefore.add(subclassDiff.getOriginalClass());
subclassSetAfter.add(subclassDiff.getNextClass());
}
}
}
private void detectSubRefactorings(UMLClassBaseDiff classDiff, UMLClass addedClass, RefactoringType parentType) throws RefactoringMinerTimedOutException {
for(UMLOperation addedOperation : addedClass.getOperations()) {
UMLOperation removedOperation = classDiff.containsRemovedOperationWithTheSameSignature(addedOperation);
if(parentType.equals(RefactoringType.MERGE_CLASS) && removedOperation == null) {
removedOperation = classDiff.getOriginalClass().operationWithTheSameSignature(addedOperation);
if(addedOperation.isConstructor() && removedOperation == null) {
RenamePattern renamePattern = new RenamePattern(classDiff.getOriginalClass().getNonQualifiedName(), classDiff.getNextClass().getNonQualifiedName());
removedOperation = classDiff.getOriginalClass().operationWithTheSameRenamePattern(addedOperation, renamePattern);
}
}
List removedOperationsWithSameName = classDiff.removedOperationWithTheSameName(addedOperation);
if(removedOperation == null && removedOperationsWithSameName.size() == 1) {
removedOperation = removedOperationsWithSameName.get(0);
}
if(removedOperation != null) {
UMLOperationBodyMapper mapper = new UMLOperationBodyMapper(removedOperation, addedOperation, classDiff);
int mappings = mapper.mappingsWithoutBlocks();
if(removedOperation.equalSignature(addedOperation) || (mappings > 0 && mappedElementsMoreThanNonMappedT1AndT2(mappings, mapper))) {
classDiff.getRemovedOperations().remove(removedOperation);
MoveOperationRefactoring ref = null;
if(parentType.equals(RefactoringType.EXTRACT_SUPERCLASS)) {
ref = new PullUpOperationRefactoring(mapper);
}
else if(parentType.equals(RefactoringType.EXTRACT_CLASS) || parentType.equals(RefactoringType.MERGE_CLASS)) {
ref = new MoveOperationRefactoring(mapper);
}
else if(parentType.equals(RefactoringType.EXTRACT_SUBCLASS)) {
ref = new PushDownOperationRefactoring(mapper);
}
this.refactorings.add(ref);
refactorings.addAll(mapper.getRefactorings());
for(CandidateAttributeRefactoring candidate : mapper.getCandidateAttributeRenames()) {
String before = PrefixSuffixUtils.normalize(candidate.getOriginalVariableName());
String after = PrefixSuffixUtils.normalize(candidate.getRenamedVariableName());
if(before.contains(".") && after.contains(".")) {
String prefix1 = before.substring(0, before.lastIndexOf(".") + 1);
String prefix2 = after.substring(0, after.lastIndexOf(".") + 1);
if(prefix1.equals(prefix2)) {
before = before.substring(prefix1.length(), before.length());
after = after.substring(prefix2.length(), after.length());
}
}
Replacement renamePattern = new Replacement(before, after, ReplacementType.VARIABLE_NAME);
if(renameMap.containsKey(renamePattern)) {
renameMap.get(renamePattern).add(candidate);
}
else {
Set set = new LinkedHashSet();
set.add(candidate);
renameMap.put(renamePattern, set);
}
}
}
}
}
for(Refactoring r : new ArrayList<>(this.refactorings)) {
if(r instanceof MoveOperationRefactoring) {
MoveOperationRefactoring moveRefactoring = (MoveOperationRefactoring)r;
checkForExtractedOperationsWithinMovedMethod(moveRefactoring.getBodyMapper(), classDiff.getRemovedOperations(), addedClass, classDiff);
}
}
for(UMLAttribute addedAttribute : addedClass.getAttributes()) {
UMLAttribute removedAttribute = classDiff.containsRemovedAttributeWithTheSameSignature(addedAttribute);
if(parentType.equals(RefactoringType.MERGE_CLASS) && removedAttribute == null) {
removedAttribute = classDiff.getOriginalClass().attributeWithTheSameSignature(addedAttribute);
}
if(removedAttribute == null) {
removedAttribute = classDiff.containsRemovedAttributeWithTheSameNameIgnoringChangedType(addedAttribute);
}
if(removedAttribute == null) {
boolean renameFound = false;
for(UMLAttributeDiff diff : classDiff.getAttributeDiffList()) {
if((diff.isRenamed() || diff.isTypeChanged()) && diff.getRemovedAttribute().equalsIgnoringChangedVisibility(addedAttribute)) {
renameFound = true;
break;
}
}
if(renameFound) {
removedAttribute = classDiff.getOriginalClass().containsAttributeWithTheSameSignature(addedAttribute);
}
}
if(removedAttribute != null) {
classDiff.getRemovedAttributes().remove(removedAttribute);
Refactoring ref = null;
if(parentType.equals(RefactoringType.EXTRACT_SUPERCLASS)) {
ref = new PullUpAttributeRefactoring(removedAttribute, addedAttribute);
}
else if(parentType.equals(RefactoringType.EXTRACT_CLASS) || parentType.equals(RefactoringType.MERGE_CLASS)) {
ref = new MoveAttributeRefactoring(removedAttribute, addedAttribute);
}
else if(parentType.equals(RefactoringType.EXTRACT_SUBCLASS)) {
ref = new PushDownAttributeRefactoring(removedAttribute, addedAttribute);
}
Set conflictingRefactorings = movedAttributeRenamed(removedAttribute.getVariableDeclaration(), addedAttribute.getVariableDeclaration(), new LinkedHashSet<>(classDiff.getRefactoringsBeforePostProcessing()));
if(!conflictingRefactorings.isEmpty()) {
classDiff.getRefactoringsBeforePostProcessing().removeAll(conflictingRefactorings);
this.refactorings.removeAll(conflictingRefactorings);
}
this.refactorings.add(ref);
UMLAttributeDiff attributeDiff = new UMLAttributeDiff(removedAttribute, addedAttribute, Collections.emptyList());
if(!movedAttributeDiffList.contains(attributeDiff)) {
movedAttributeDiffList.add(attributeDiff);
}
refactorings.addAll(attributeDiff.getRefactorings());
}
}
}
private void checkForExtractedOperationsWithinMovedMethod(UMLOperationBodyMapper movedMethodMapper, List potentiallyMovedOperations, UMLClass addedClass, UMLAbstractClassDiff classDiff) throws RefactoringMinerTimedOutException {
VariableDeclarationContainer removedOperation = movedMethodMapper.getContainer1();
VariableDeclarationContainer addedOperation = movedMethodMapper.getContainer2();
List removedInvocations = removedOperation.getAllOperationInvocations();
List