Skip to content

Commit

Permalink
Support for indirect Extract and Move Method detection (issue #715)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsantalis committed May 22, 2024
1 parent f5b7f0d commit dd1aaf1
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 213 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,15 @@ public int statementCount() {
return count;
}

public int statementCountIncludingBlocks() {
int count = 0;
count++;
for(AbstractStatement statement : statementList) {
count += statement.statementCount();
}
return count;
}

public LocationInfo getLocationInfo() {
return locationInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ public int statementCount() {
return compositeStatement.statementCount();
}

public int statementCountIncludingBlocks() {
return compositeStatement.statementCountIncludingBlocks();
}

public CompositeStatementObject getCompositeStatement() {
return compositeStatement;
}
Expand Down
205 changes: 112 additions & 93 deletions src/main/java/gr/uom/java/xmi/decomposition/UMLOperationBodyMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import gr.uom.java.xmi.diff.CandidateAttributeRefactoring;
import gr.uom.java.xmi.diff.CandidateMergeVariableRefactoring;
import gr.uom.java.xmi.diff.CandidateSplitVariableRefactoring;
import gr.uom.java.xmi.diff.ExtractOperationDetection;
import gr.uom.java.xmi.diff.ExtractOperationRefactoring;
import gr.uom.java.xmi.diff.ExtractVariableRefactoring;
import gr.uom.java.xmi.diff.InlineOperationRefactoring;
Expand Down Expand Up @@ -112,6 +113,15 @@ public class UMLOperationBodyMapper implements Comparable<UMLOperationBodyMapper
private Set<CompositeStatementObjectMapping> ifBecomingElseIf = new HashSet<>();
private Set<CompositeStatementObjectMapping> ifAddingElseIf = new HashSet<>();
private Map<UMLOperation, Set<AbstractCodeFragment>> extractedStatements = new LinkedHashMap<>();
private List<AbstractCall> invocationsInSourceOperationAfterExtraction;


public List<AbstractCall> getInvocationsInSourceOperationAfterExtraction() {
if(invocationsInSourceOperationAfterExtraction == null) {
this.invocationsInSourceOperationAfterExtraction = ExtractOperationDetection.getInvocationsInSourceOperationAfterExtraction(this);
}
return invocationsInSourceOperationAfterExtraction;
}

public boolean isNested() {
return nested;
Expand Down Expand Up @@ -1991,7 +2001,7 @@ private boolean returnWithVariableReplacement(AbstractCodeMapping mapping) {
}

public UMLOperationBodyMapper(UMLOperationBodyMapper operationBodyMapper, UMLOperation addedOperation,
Map<String, String> parameterToArgumentMap1, Map<String, String> parameterToArgumentMap2, UMLAbstractClassDiff classDiff, AbstractCall operationInvocation, boolean nested) throws RefactoringMinerTimedOutException {
Map<String, String> parameterToArgumentMap1, Map<String, String> parameterToArgumentMap2, UMLAbstractClassDiff classDiff, AbstractCall operationInvocation, boolean nested, Optional<List<AbstractCodeFragment>> leaves1Sublist) throws RefactoringMinerTimedOutException {
this.parentMapper = operationBodyMapper;
this.operationInvocation = operationInvocation;
this.nested = nested;
Expand Down Expand Up @@ -2098,88 +2108,95 @@ public UMLOperationBodyMapper(UMLOperationBodyMapper operationBodyMapper, UMLOpe
return;
}
CompositeStatementObject composite2 = addedOperationBody.getCompositeStatement();
List<AbstractCodeFragment> leaves1 = operationBodyMapper.getNonMappedLeavesT1();
List<CompositeStatementObject> innerNodes1 = operationBodyMapper.getNonMappedInnerNodesT1();
List<AbstractCodeFragment> leaves1 = leaves1Sublist.isPresent() ? leaves1Sublist.get() : operationBodyMapper.getNonMappedLeavesT1();
List<CompositeStatementObject> innerNodes1 = leaves1Sublist.isPresent() ? new ArrayList<>() : operationBodyMapper.getNonMappedInnerNodesT1();
//adding leaves that were mapped with replacements
Set<AbstractCodeFragment> addedLeaves1 = new LinkedHashSet<AbstractCodeFragment>();
Set<CompositeStatementObject> addedInnerNodes1 = new LinkedHashSet<CompositeStatementObject>();
for(AbstractCodeFragment nonMappedLeaf1 : new ArrayList<>(operationBodyMapper.getNonMappedLeavesT1())) {
expandAnonymousAndLambdas(nonMappedLeaf1, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
}
List<AbstractCodeFragment> leaves2 = composite2.getLeaves();
for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) {
if(!returnWithVariableReplacement(mapping) && (!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2()) ||
!mapping.getFragment1().getClass().equals(mapping.getFragment2().getClass()))) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject comp = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(comp)) {
innerNodes1.add(comp);
addedInnerNodes1.add(comp);
}
handleBlocks(comp, innerNodes1, addedInnerNodes1);
}
if(leaves1Sublist.isPresent()) {
for(AbstractCodeFragment nonMappedLeaf1 : new ArrayList<>(leaves1Sublist.get())) {
expandAnonymousAndLambdas(nonMappedLeaf1, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
}
else if(mapping.getFragment1().getString().equals(mapping.getFragment2().getString())) {
for(AbstractCodeFragment leaf2 : leaves2) {
if(mapping.getFragment1().getString().equals(leaf2.getString())) {
CompositeStatementObject parent1 = mapping.getFragment1().getParent();
if(parent1.getParent() != null && (!operationBodyMapper.alreadyMatched1(parent1) || (parent1.isBlock() && !operationBodyMapper.alreadyMatched1(parent1.getParent())))) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
break;
}
else {
for(AbstractCodeFragment nonMappedLeaf1 : new ArrayList<>(operationBodyMapper.getNonMappedLeavesT1())) {
expandAnonymousAndLambdas(nonMappedLeaf1, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
}
for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) {
if(!returnWithVariableReplacement(mapping) && (!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2()) ||
!mapping.getFragment1().getClass().equals(mapping.getFragment2().getClass()))) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject comp = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(comp)) {
innerNodes1.add(comp);
addedInnerNodes1.add(comp);
}
handleBlocks(comp, innerNodes1, addedInnerNodes1);
}
}
}
}
if(nested && operationBodyMapper.getParentMapper() != null && operationBodyMapper.getParentMapper().getChildMappers().isEmpty()) {
for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) {
if(mapping.getFragment1().getString().equals(mapping.getFragment2().getString())) {
if(mapping.getFragment1().getString().equals(JAVA.RETURN_NULL)) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
else if(mapping.getFragment1().getString().equals(mapping.getFragment2().getString())) {
for(AbstractCodeFragment leaf2 : leaves2) {
if(mapping.getFragment1().getString().equals(leaf2.getString())) {
CompositeStatementObject parent1 = mapping.getFragment1().getParent();
if(parent1.getParent() != null && (!operationBodyMapper.alreadyMatched1(parent1) || (parent1.isBlock() && !operationBodyMapper.alreadyMatched1(parent1.getParent())))) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
break;
}
}
}
}
}
for(AbstractCodeMapping mapping : operationBodyMapper.getParentMapper().getMappings()) {
if(mapping.getFragment1().getString().equals(mapping.getFragment2().getString())) {
if((mapping.getFragment1().getString().equals(JAVA.RETURN_TRUE) || mapping.getFragment1().getString().equals(JAVA.RETURN_FALSE)) && addedOperation.getReturnParameter().getType().toString().equals("boolean")) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
if(nested && operationBodyMapper.getParentMapper() != null && operationBodyMapper.getParentMapper().getChildMappers().isEmpty()) {
for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) {
if(mapping.getFragment1().getString().equals(mapping.getFragment2().getString())) {
if(mapping.getFragment1().getString().equals(JAVA.RETURN_NULL)) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
}
}
}
else if(!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2())) {
AbstractCodeFragment fragment = mapping.getFragment1();
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject statement = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(statement)) {
innerNodes1.add(statement);
addedInnerNodes1.add(statement);
for(AbstractCodeMapping mapping : operationBodyMapper.getParentMapper().getMappings()) {
if(mapping.getFragment1().getString().equals(mapping.getFragment2().getString())) {
if((mapping.getFragment1().getString().equals(JAVA.RETURN_TRUE) || mapping.getFragment1().getString().equals(JAVA.RETURN_FALSE)) && addedOperation.getReturnParameter().getType().toString().equals("boolean")) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
}
}
}
}
}
for(UMLOperationBodyMapper childMapper : operationBodyMapper.childMappers) {
if(childMapper.container1.getClassName().equals(addedOperation.getClassName()) || classDiff instanceof UMLClassMoveDiff) {
for(AbstractCodeMapping mapping : childMapper.getMappings()) {
if(!returnWithVariableReplacement(mapping) && (!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2()))) {
else if(!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2())) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, childMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject comp = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(comp)) {
innerNodes1.add(comp);
addedInnerNodes1.add(comp);
CompositeStatementObject statement = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(statement)) {
innerNodes1.add(statement);
addedInnerNodes1.add(statement);
}
handleBlocks(comp, innerNodes1, addedInnerNodes1);
}
}
}
for(AbstractCodeFragment fragment : childMapper.getNonMappedLeavesT1()) {
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, childMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
}
for(UMLOperationBodyMapper childMapper : operationBodyMapper.childMappers) {
if(childMapper.container1.getClassName().equals(addedOperation.getClassName()) || classDiff instanceof UMLClassMoveDiff) {
for(AbstractCodeMapping mapping : childMapper.getMappings()) {
if(!returnWithVariableReplacement(mapping) && (!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2()))) {
AbstractCodeFragment fragment = mapping.getFragment1();
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, childMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject comp = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(comp)) {
innerNodes1.add(comp);
addedInnerNodes1.add(comp);
}
handleBlocks(comp, innerNodes1, addedInnerNodes1);
}
}
}
for(AbstractCodeFragment fragment : childMapper.getNonMappedLeavesT1()) {
expandAnonymousAndLambdas(fragment, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, childMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false);
}
}
}
}
Expand All @@ -2206,19 +2223,9 @@ else if(!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFra
}

//adding innerNodes that were mapped with replacements
for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) {
if(!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2())) {
AbstractCodeFragment fragment = mapping.getFragment1();
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject statement = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(statement)) {
innerNodes1.add(statement);
addedInnerNodes1.add(statement);
}
}
}
else if(mapping instanceof CompositeStatementObjectMapping) {
if(((CompositeStatementObjectMapping)mapping).getCompositeChildMatchingScore() <= 0.01) {
if(leaves1Sublist.isEmpty()) {
for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) {
if(!mapping.getReplacements().isEmpty() || !mapping.getFragment1().equalFragment(mapping.getFragment2())) {
AbstractCodeFragment fragment = mapping.getFragment1();
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject statement = (CompositeStatementObject)fragment;
Expand All @@ -2228,24 +2235,8 @@ else if(mapping instanceof CompositeStatementObjectMapping) {
}
}
}
else {
//search for leaf mappings being inexact
int subsumedLeafMappings = 0;
int inExactSubsumedLeafMappings = 0;
for(AbstractCodeMapping mapping2 : operationBodyMapper.getMappings()) {
if(mapping2.equals(mapping)) {
break;
}
if((mapping.getFragment1().getLocationInfo().subsumes(mapping2.getFragment1().getLocationInfo()) ||
mapping.getFragment2().getLocationInfo().subsumes(mapping2.getFragment2().getLocationInfo())) &&
!mapping.getFragment1().equals(mapping2.getFragment1()) && !mapping.getFragment2().equals(mapping2.getFragment2())) {
subsumedLeafMappings++;
if(!mapping2.getReplacements().isEmpty() || !mapping2.getFragment1().equalFragment(mapping2.getFragment2())) {
inExactSubsumedLeafMappings++;
}
}
}
if(inExactSubsumedLeafMappings == subsumedLeafMappings && subsumedLeafMappings > 0) {
else if(mapping instanceof CompositeStatementObjectMapping) {
if(((CompositeStatementObjectMapping)mapping).getCompositeChildMatchingScore() <= 0.01) {
AbstractCodeFragment fragment = mapping.getFragment1();
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject statement = (CompositeStatementObject)fragment;
Expand All @@ -2255,6 +2246,34 @@ else if(mapping instanceof CompositeStatementObjectMapping) {
}
}
}
else {
//search for leaf mappings being inexact
int subsumedLeafMappings = 0;
int inExactSubsumedLeafMappings = 0;
for(AbstractCodeMapping mapping2 : operationBodyMapper.getMappings()) {
if(mapping2.equals(mapping)) {
break;
}
if((mapping.getFragment1().getLocationInfo().subsumes(mapping2.getFragment1().getLocationInfo()) ||
mapping.getFragment2().getLocationInfo().subsumes(mapping2.getFragment2().getLocationInfo())) &&
!mapping.getFragment1().equals(mapping2.getFragment1()) && !mapping.getFragment2().equals(mapping2.getFragment2())) {
subsumedLeafMappings++;
if(!mapping2.getReplacements().isEmpty() || !mapping2.getFragment1().equalFragment(mapping2.getFragment2())) {
inExactSubsumedLeafMappings++;
}
}
}
if(inExactSubsumedLeafMappings == subsumedLeafMappings && subsumedLeafMappings > 0) {
AbstractCodeFragment fragment = mapping.getFragment1();
if(fragment instanceof CompositeStatementObject) {
CompositeStatementObject statement = (CompositeStatementObject)fragment;
if(!innerNodes1.contains(statement)) {
innerNodes1.add(statement);
addedInnerNodes1.add(statement);
}
}
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.refactoringminer.api.RefactoringMinerTimedOutException;
Expand Down Expand Up @@ -427,7 +428,7 @@ private UMLOperationBodyMapper createMapperForExtractedMethod(UMLOperationBodyMa
UMLOperation delegateMethod = findDelegateMethod(originalOperation, addedOperation, addedOperationInvocation);
return new UMLOperationBodyMapper(mapper,
delegateMethod != null ? delegateMethod : addedOperation,
new LinkedHashMap<String, String>(), parameterToArgumentMap, classDiff, addedOperationInvocation, nested);
new LinkedHashMap<String, String>(), parameterToArgumentMap, classDiff, addedOperationInvocation, nested, Optional.empty());
}
return null;
}
Expand Down
Loading

0 comments on commit dd1aaf1

Please sign in to comment.