diff --git a/src/main/java/gr/uom/java/xmi/decomposition/CompositeStatementObject.java b/src/main/java/gr/uom/java/xmi/decomposition/CompositeStatementObject.java index eb9463321..7284bebe8 100644 --- a/src/main/java/gr/uom/java/xmi/decomposition/CompositeStatementObject.java +++ b/src/main/java/gr/uom/java/xmi/decomposition/CompositeStatementObject.java @@ -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; } diff --git a/src/main/java/gr/uom/java/xmi/decomposition/OperationBody.java b/src/main/java/gr/uom/java/xmi/decomposition/OperationBody.java index 9fdd5e8a2..ba7135436 100644 --- a/src/main/java/gr/uom/java/xmi/decomposition/OperationBody.java +++ b/src/main/java/gr/uom/java/xmi/decomposition/OperationBody.java @@ -111,6 +111,10 @@ public int statementCount() { return compositeStatement.statementCount(); } + public int statementCountIncludingBlocks() { + return compositeStatement.statementCountIncludingBlocks(); + } + public CompositeStatementObject getCompositeStatement() { return compositeStatement; } diff --git a/src/main/java/gr/uom/java/xmi/decomposition/UMLOperationBodyMapper.java b/src/main/java/gr/uom/java/xmi/decomposition/UMLOperationBodyMapper.java index 0353bf1da..92607d6e2 100644 --- a/src/main/java/gr/uom/java/xmi/decomposition/UMLOperationBodyMapper.java +++ b/src/main/java/gr/uom/java/xmi/decomposition/UMLOperationBodyMapper.java @@ -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; @@ -112,6 +113,15 @@ public class UMLOperationBodyMapper implements Comparable ifBecomingElseIf = new HashSet<>(); private Set ifAddingElseIf = new HashSet<>(); private Map> extractedStatements = new LinkedHashMap<>(); + private List invocationsInSourceOperationAfterExtraction; + + + public List getInvocationsInSourceOperationAfterExtraction() { + if(invocationsInSourceOperationAfterExtraction == null) { + this.invocationsInSourceOperationAfterExtraction = ExtractOperationDetection.getInvocationsInSourceOperationAfterExtraction(this); + } + return invocationsInSourceOperationAfterExtraction; + } public boolean isNested() { return nested; @@ -1991,7 +2001,7 @@ private boolean returnWithVariableReplacement(AbstractCodeMapping mapping) { } public UMLOperationBodyMapper(UMLOperationBodyMapper operationBodyMapper, UMLOperation addedOperation, - Map parameterToArgumentMap1, Map parameterToArgumentMap2, UMLAbstractClassDiff classDiff, AbstractCall operationInvocation, boolean nested) throws RefactoringMinerTimedOutException { + Map parameterToArgumentMap1, Map parameterToArgumentMap2, UMLAbstractClassDiff classDiff, AbstractCall operationInvocation, boolean nested, Optional> leaves1Sublist) throws RefactoringMinerTimedOutException { this.parentMapper = operationBodyMapper; this.operationInvocation = operationInvocation; this.nested = nested; @@ -2098,88 +2108,95 @@ public UMLOperationBodyMapper(UMLOperationBodyMapper operationBodyMapper, UMLOpe return; } CompositeStatementObject composite2 = addedOperationBody.getCompositeStatement(); - List leaves1 = operationBodyMapper.getNonMappedLeavesT1(); - List innerNodes1 = operationBodyMapper.getNonMappedInnerNodesT1(); + List leaves1 = leaves1Sublist.isPresent() ? leaves1Sublist.get() : operationBodyMapper.getNonMappedLeavesT1(); + List innerNodes1 = leaves1Sublist.isPresent() ? new ArrayList<>() : operationBodyMapper.getNonMappedInnerNodesT1(); //adding leaves that were mapped with replacements Set addedLeaves1 = new LinkedHashSet(); Set addedInnerNodes1 = new LinkedHashSet(); - for(AbstractCodeFragment nonMappedLeaf1 : new ArrayList<>(operationBodyMapper.getNonMappedLeavesT1())) { - expandAnonymousAndLambdas(nonMappedLeaf1, leaves1, innerNodes1, addedLeaves1, addedInnerNodes1, operationBodyMapper.anonymousClassList1(), codeFragmentOperationMap1, container1, false); - } List 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); + } } } } @@ -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; @@ -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; @@ -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); + } + } + } + } } } } diff --git a/src/main/java/gr/uom/java/xmi/diff/ExtractOperationDetection.java b/src/main/java/gr/uom/java/xmi/diff/ExtractOperationDetection.java index fc6cede60..56afee511 100644 --- a/src/main/java/gr/uom/java/xmi/diff/ExtractOperationDetection.java +++ b/src/main/java/gr/uom/java/xmi/diff/ExtractOperationDetection.java @@ -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; @@ -427,7 +428,7 @@ private UMLOperationBodyMapper createMapperForExtractedMethod(UMLOperationBodyMa UMLOperation delegateMethod = findDelegateMethod(originalOperation, addedOperation, addedOperationInvocation); return new UMLOperationBodyMapper(mapper, delegateMethod != null ? delegateMethod : addedOperation, - new LinkedHashMap(), parameterToArgumentMap, classDiff, addedOperationInvocation, nested); + new LinkedHashMap(), parameterToArgumentMap, classDiff, addedOperationInvocation, nested, Optional.empty()); } return null; } diff --git a/src/main/java/gr/uom/java/xmi/diff/UMLModelDiff.java b/src/main/java/gr/uom/java/xmi/diff/UMLModelDiff.java index 814f54daf..541e14d43 100644 --- a/src/main/java/gr/uom/java/xmi/diff/UMLModelDiff.java +++ b/src/main/java/gr/uom/java/xmi/diff/UMLModelDiff.java @@ -52,6 +52,7 @@ 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; @@ -1558,6 +1559,14 @@ private List getRemovedAttributesInCommonClasses() { 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) { @@ -2650,6 +2659,8 @@ else if(!diffs2.isEmpty()) { if(removedAndInlinedOperationsInCommonClasses.size() <= MAXIMUM_NUMBER_OF_COMPARED_METHODS) { checkForMovedAndInlinedOperations(getOperationBodyMappersInCommonClasses(), removedAndInlinedOperationsInCommonClasses); } + List allOperationsInAddedClasses = getOperationsInAddedClasses(); + checkForExtractedAndMovedLambdas(getOperationBodyMappersInMovedAndRenamedClasses(), allOperationsInAddedClasses); List moveAttributeRefactorings = new ArrayList(); moveAttributeRefactorings.addAll(checkForAttributeMovesBetweenCommonClasses(renameMap, refactorings)); moveAttributeRefactorings.addAll(checkForAttributeMovesIncludingAddedClasses(renameMap, refactorings)); @@ -3556,6 +3567,99 @@ else if(!addedClass.isTopLevel() && !classDiff.getNextClass().isTopLevel() && ad } } + private void checkForExtractedAndMovedLambdas(List mappers, List addedOperations) throws RefactoringMinerTimedOutException { + for(Iterator addedOperationIterator = addedOperations.iterator(); addedOperationIterator.hasNext();) { + UMLOperation addedOperation = addedOperationIterator.next(); + List addedOperationInvocations = new ArrayList(); + for(UMLOperationBodyMapper mapper : mappers) { + for(AbstractCall invocation : mapper.getInvocationsInSourceOperationAfterExtraction()) { + if(invocation.matchesOperation(addedOperation, mapper.getContainer2(), mapper.getClassDiff(), this)) { + addedOperationInvocations.add(invocation); + } + } + } + if(addedOperationInvocations.isEmpty()) { + for(UMLOperation operation : addedOperations) { + for(AbstractCall invocation : operation.getAllOperationInvocations()) { + if(invocation.matchesOperation(addedOperation, operation, null, this)) { + //check for indirect call + boolean indirectCallFound = false; + for(UMLOperationBodyMapper mapper : mappers) { + for(AbstractCall inv : mapper.getInvocationsInSourceOperationAfterExtraction()) { + if(inv.matchesOperation(operation, mapper.getContainer2(), mapper.getClassDiff(), this)) { + indirectCallFound = true; + break; + } + } + if(indirectCallFound) { + break; + } + } + if(indirectCallFound) { + addedOperationInvocations.add(invocation); + } + } + } + } + } + if(!addedOperationInvocations.isEmpty()) { + int addedOperationStatementCount = addedOperation.getBody() != null ? addedOperation.getBody().statementCountIncludingBlocks() : 0; + for(UMLOperationBodyMapper mapper : mappers) { + Pair pair = Pair.of(mapper.getContainer1(), addedOperation); + String className = mapper.getContainer2().getClassName(); + if(!className.equals(addedOperation.getClassName()) && (mapper.nonMappedElementsT1() > 0 || includesReplacementInvolvingAddedMethod(mapper.getReplacementsInvolvingMethodInvocation(), addedOperation, mapper.getContainer2(), mapper.getClassDiff())) && !mapper.containsExtractOperationRefactoring(addedOperation) && !processedOperationPairs.contains(pair)) { + processedOperationPairs.add(pair); + for(AbstractCodeFragment fragment : new ArrayList<>(mapper.getNonMappedLeavesT1())) { + if(fragment.getLambdas().size() > 0 && fragment.getLambdas().get(0).getBody() != null) { + int lambdaStatementCount = fragment.getLambdas().get(0).getBody().statementCountIncludingBlocks(); + if(addedOperationStatementCount == lambdaStatementCount) { + AbstractCall addedOperationInvocation = addedOperationInvocations.get(0); + ArrayList subList = new ArrayList(); + subList.add(fragment); + UMLOperationBodyMapper operationBodyMapper = createMapperForExtractAndMove(addedOperation, + mapper, className, addedOperationInvocation, Optional.of(subList)); + if(!anotherAddedMethodExistsWithBetterMatchingInvocationExpression(addedOperationInvocation, addedOperation, addedOperations) && + !conflictingExpression(addedOperationInvocation, addedOperation, mapper.getContainer2().variableDeclarationMap()) && + operationBodyMapper.getMappings().size() >= lambdaStatementCount && + extractAndMoveMatchCondition(operationBodyMapper, mapper, addedOperationInvocation)) { + if(className.equals(addedOperation.getClassName())) { + //extract inside moved or renamed class + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); + } + else if(isSubclassOf(className, addedOperation.getClassName())) { + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); + } + else if(isSubclassOf(addedOperation.getClassName(), className)) { + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); + } + else if(addedOperation.getClassName().startsWith(className + ".")) { + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); + } + else if(className.startsWith(addedOperation.getClassName() + ".")) { + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); + } + else if(sourceClassImportsTargetClass(className, addedOperation.getClassName()) || + sourceClassImportsSuperclassOfTargetClass(className, addedOperation.getClassName()) || + targetClassImportsSourceClass(className, addedOperation.getClassName())) { + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); + } + for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) { + if(fragment.getLambdas().get(0).getLocationInfo().subsumes(mapping.getFragment1().getLocationInfo())) { + mapper.getNonMappedLeavesT1().remove(fragment); + break; + } + } + break; + } + } + } + } + } + } + } + } + } + private void checkForExtractedAndMovedOperations(List mappers, List addedOperations) throws RefactoringMinerTimedOutException { for(Iterator addedOperationIterator = addedOperations.iterator(); addedOperationIterator.hasNext();) { UMLOperation addedOperation = addedOperationIterator.next(); @@ -3565,7 +3669,7 @@ private void checkForExtractedAndMovedOperations(List ma String className = mapper.getContainer2().getClassName(); if(!className.equals(addedOperation.getClassName()) && (mapper.nonMappedElementsT1() > 0 || includesReplacementInvolvingAddedMethod(mapper.getReplacementsInvolvingMethodInvocation(), addedOperation, mapper.getContainer2(), mapper.getClassDiff())) && !mapper.containsExtractOperationRefactoring(addedOperation) && !processedOperationPairs.contains(pair)) { processedOperationPairs.add(pair); - List operationInvocations = ExtractOperationDetection.getInvocationsInSourceOperationAfterExtraction(mapper); + List operationInvocations = mapper.getInvocationsInSourceOperationAfterExtraction(); List addedOperationInvocations = new ArrayList(); for(AbstractCall invocation : operationInvocations) { if(invocation.matchesOperation(addedOperation, mapper.getContainer2(), mapper.getClassDiff(), this)) { @@ -3574,141 +3678,31 @@ private void checkForExtractedAndMovedOperations(List ma } if(addedOperationInvocations.size() > 0) { AbstractCall addedOperationInvocation = addedOperationInvocations.get(0); - List arguments = addedOperationInvocation.arguments(); - List parameters = addedOperation.getParameterNameList(); - Map parameterToArgumentMap2 = new LinkedHashMap(); - //special handling for methods with varargs parameter for which no argument is passed in the matching invocation - int size = Math.min(arguments.size(), parameters.size()); - for(int i=0; i attributes = new ArrayList(); - if(className.contains(".") && isAnonymousClassName(className)) { - //add enclosing class fields + anonymous class fields - String[] tokens = className.split("\\."); - String anonymousID = ""; - for(int i=tokens.length-1; i>=0; i--) { - String token = tokens[i]; - if(StringDistance.isNumeric(token) || Character.isLowerCase(token.charAt(0))) { - anonymousID = "." + token + anonymousID; - } - else { - break; - } - } - UMLClassBaseDiff umlClassDiff = getUMLClassDiff(className); - if(umlClassDiff == null) { - String enclosingClassName = className.substring(0, className.indexOf(anonymousID)); - umlClassDiff = getUMLClassDiff(enclosingClassName); - } - attributes.addAll(umlClassDiff.originalClassAttributesOfType(addedOperation.getClassName())); - for(UMLAnonymousClass anonymousClass : umlClassDiff.getOriginalClass().getAnonymousClassList()) { - if(className.equals(anonymousClass.getCodePath())) { - attributes.addAll(anonymousClass.attributesOfType(addedOperation.getClassName())); - break; - } - } - } - else { - UMLClassBaseDiff umlClassDiff = getUMLClassDiff(className); - if(umlClassDiff == null) { - for(UMLClassDiff classDiff : commonClassDiffList) { - for(UMLAnonymousClass anonymousClass : classDiff.getAddedAnonymousClasses()) { - if(className.equals(anonymousClass.getCodePath())) { - umlClassDiff = classDiff; - attributes.addAll(anonymousClass.attributesOfType(addedOperation.getClassName())); - break; - } - } - if(umlClassDiff != null) { - break; - } - } - } - if(umlClassDiff != null) { - attributes.addAll(umlClassDiff.originalClassAttributesOfType(addedOperation.getClassName())); - } - } - Map parameterToArgumentMap1 = new LinkedHashMap(); - for(UMLAttribute attribute : attributes) { - parameterToArgumentMap1.put(attribute.getName() + ".", ""); - parameterToArgumentMap2.put(JAVA.THIS_DOT, ""); - } - if(addedOperationInvocation.getExpression() != null) { - parameterToArgumentMap1.put(addedOperationInvocation.getExpression() + ".", ""); - parameterToArgumentMap2.put(JAVA.THIS_DOT, ""); - } - UMLOperationBodyMapper operationBodyMapper = new UMLOperationBodyMapper(mapper, addedOperation, parameterToArgumentMap1, parameterToArgumentMap2, getUMLClassDiff(addedOperation.getClassName()), addedOperationInvocation, false); + UMLOperationBodyMapper operationBodyMapper = createMapperForExtractAndMove(addedOperation, + mapper, className, addedOperationInvocation, Optional.empty()); if(!anotherAddedMethodExistsWithBetterMatchingInvocationExpression(addedOperationInvocation, addedOperation, addedOperations) && !conflictingExpression(addedOperationInvocation, addedOperation, mapper.getContainer2().variableDeclarationMap()) && extractAndMoveMatchCondition(operationBodyMapper, mapper, addedOperationInvocation)) { if(className.equals(addedOperation.getClassName())) { //extract inside moved or renamed class - ExtractOperationRefactoring extractOperationRefactoring = - new ExtractOperationRefactoring(operationBodyMapper, mapper.getContainer2(), addedOperationInvocations); - refactorings.add(extractOperationRefactoring); - refactorings.addAll(operationBodyMapper.getRefactorings()); - deleteAddedOperation(addedOperation); - mapper.addChildMapper(operationBodyMapper); - MappingOptimizer optimizer = new MappingOptimizer(mapper.getClassDiff()); - optimizer.optimizeDuplicateMappingsForExtract(mapper, refactorings); + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); } else if(isSubclassOf(className, addedOperation.getClassName())) { - //extract and pull up method - ExtractOperationRefactoring extractOperationRefactoring = - new ExtractOperationRefactoring(operationBodyMapper, mapper.getContainer2(), addedOperationInvocations); - refactorings.add(extractOperationRefactoring); - refactorings.addAll(operationBodyMapper.getRefactorings()); - deleteAddedOperation(addedOperation); - mapper.addChildMapper(operationBodyMapper); - MappingOptimizer optimizer = new MappingOptimizer(mapper.getClassDiff()); - optimizer.optimizeDuplicateMappingsForExtract(mapper, refactorings); + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); } else if(isSubclassOf(addedOperation.getClassName(), className)) { - //extract and push down method - ExtractOperationRefactoring extractOperationRefactoring = - new ExtractOperationRefactoring(operationBodyMapper, mapper.getContainer2(), addedOperationInvocations); - refactorings.add(extractOperationRefactoring); - refactorings.addAll(operationBodyMapper.getRefactorings()); - deleteAddedOperation(addedOperation); - mapper.addChildMapper(operationBodyMapper); - MappingOptimizer optimizer = new MappingOptimizer(mapper.getClassDiff()); - optimizer.optimizeDuplicateMappingsForExtract(mapper, refactorings); + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); } else if(addedOperation.getClassName().startsWith(className + ".")) { - //extract and move to inner class - ExtractOperationRefactoring extractOperationRefactoring = - new ExtractOperationRefactoring(operationBodyMapper, mapper.getContainer2(), addedOperationInvocations); - refactorings.add(extractOperationRefactoring); - refactorings.addAll(operationBodyMapper.getRefactorings()); - deleteAddedOperation(addedOperation); - mapper.addChildMapper(operationBodyMapper); - MappingOptimizer optimizer = new MappingOptimizer(mapper.getClassDiff()); - optimizer.optimizeDuplicateMappingsForExtract(mapper, refactorings); + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); } else if(className.startsWith(addedOperation.getClassName() + ".")) { - //extract and move to outer class - ExtractOperationRefactoring extractOperationRefactoring = - new ExtractOperationRefactoring(operationBodyMapper, mapper.getContainer2(), addedOperationInvocations); - refactorings.add(extractOperationRefactoring); - refactorings.addAll(operationBodyMapper.getRefactorings()); - deleteAddedOperation(addedOperation); - mapper.addChildMapper(operationBodyMapper); - MappingOptimizer optimizer = new MappingOptimizer(mapper.getClassDiff()); - optimizer.optimizeDuplicateMappingsForExtract(mapper, refactorings); + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); } else if(sourceClassImportsTargetClass(className, addedOperation.getClassName()) || sourceClassImportsSuperclassOfTargetClass(className, addedOperation.getClassName()) || targetClassImportsSourceClass(className, addedOperation.getClassName())) { - //extract and move - ExtractOperationRefactoring extractOperationRefactoring = - new ExtractOperationRefactoring(operationBodyMapper, mapper.getContainer2(), addedOperationInvocations); - refactorings.add(extractOperationRefactoring); - refactorings.addAll(operationBodyMapper.getRefactorings()); - deleteAddedOperation(addedOperation); - mapper.addChildMapper(operationBodyMapper); - MappingOptimizer optimizer = new MappingOptimizer(mapper.getClassDiff()); - optimizer.optimizeDuplicateMappingsForExtract(mapper, refactorings); + createExtractAndMoveMethodRefactoring(addedOperation, mapper, addedOperationInvocations, operationBodyMapper); } for(CandidateAttributeRefactoring candidate : operationBodyMapper.getCandidateAttributeRenames()) { String before = PrefixSuffixUtils.normalize(candidate.getOriginalVariableName()); @@ -3750,6 +3744,90 @@ else if(mapping instanceof CompositeStatementObjectMapping && !mapper.getNonMapp } } + private void createExtractAndMoveMethodRefactoring(UMLOperation addedOperation, UMLOperationBodyMapper mapper, + List addedOperationInvocations, UMLOperationBodyMapper operationBodyMapper) + throws RefactoringMinerTimedOutException { + ExtractOperationRefactoring extractOperationRefactoring = + new ExtractOperationRefactoring(operationBodyMapper, mapper.getContainer2(), addedOperationInvocations); + refactorings.add(extractOperationRefactoring); + refactorings.addAll(operationBodyMapper.getRefactorings()); + deleteAddedOperation(addedOperation); + mapper.addChildMapper(operationBodyMapper); + MappingOptimizer optimizer = new MappingOptimizer(mapper.getClassDiff()); + optimizer.optimizeDuplicateMappingsForExtract(mapper, refactorings); + } + + private UMLOperationBodyMapper createMapperForExtractAndMove(UMLOperation addedOperation, + UMLOperationBodyMapper mapper, String className, AbstractCall addedOperationInvocation, Optional> leaves1Sublist) + throws RefactoringMinerTimedOutException { + List arguments = addedOperationInvocation.arguments(); + List parameters = addedOperation.getParameterNameList(); + Map parameterToArgumentMap2 = new LinkedHashMap(); + //special handling for methods with varargs parameter for which no argument is passed in the matching invocation + int size = Math.min(arguments.size(), parameters.size()); + for(int i=0; i attributes = new ArrayList(); + if(className.contains(".") && isAnonymousClassName(className)) { + //add enclosing class fields + anonymous class fields + String[] tokens = className.split("\\."); + String anonymousID = ""; + for(int i=tokens.length-1; i>=0; i--) { + String token = tokens[i]; + if(StringDistance.isNumeric(token) || Character.isLowerCase(token.charAt(0))) { + anonymousID = "." + token + anonymousID; + } + else { + break; + } + } + UMLClassBaseDiff umlClassDiff = getUMLClassDiff(className); + if(umlClassDiff == null) { + String enclosingClassName = className.substring(0, className.indexOf(anonymousID)); + umlClassDiff = getUMLClassDiff(enclosingClassName); + } + attributes.addAll(umlClassDiff.originalClassAttributesOfType(addedOperation.getClassName())); + for(UMLAnonymousClass anonymousClass : umlClassDiff.getOriginalClass().getAnonymousClassList()) { + if(className.equals(anonymousClass.getCodePath())) { + attributes.addAll(anonymousClass.attributesOfType(addedOperation.getClassName())); + break; + } + } + } + else { + UMLClassBaseDiff umlClassDiff = getUMLClassDiff(className); + if(umlClassDiff == null) { + for(UMLClassDiff classDiff : commonClassDiffList) { + for(UMLAnonymousClass anonymousClass : classDiff.getAddedAnonymousClasses()) { + if(className.equals(anonymousClass.getCodePath())) { + umlClassDiff = classDiff; + attributes.addAll(anonymousClass.attributesOfType(addedOperation.getClassName())); + break; + } + } + if(umlClassDiff != null) { + break; + } + } + } + if(umlClassDiff != null) { + attributes.addAll(umlClassDiff.originalClassAttributesOfType(addedOperation.getClassName())); + } + } + Map parameterToArgumentMap1 = new LinkedHashMap(); + for(UMLAttribute attribute : attributes) { + parameterToArgumentMap1.put(attribute.getName() + ".", ""); + parameterToArgumentMap2.put(JAVA.THIS_DOT, ""); + } + if(addedOperationInvocation.getExpression() != null) { + parameterToArgumentMap1.put(addedOperationInvocation.getExpression() + ".", ""); + parameterToArgumentMap2.put(JAVA.THIS_DOT, ""); + } + UMLOperationBodyMapper operationBodyMapper = new UMLOperationBodyMapper(mapper, addedOperation, parameterToArgumentMap1, parameterToArgumentMap2, getUMLClassDiff(addedOperation.getClassName()), addedOperationInvocation, false, leaves1Sublist); + return operationBodyMapper; + } + private boolean isAnonymousClassName(String className) { String anonymousID = className.substring(className.lastIndexOf(".")+1, className.length()); return StringDistance.isNumeric(anonymousID) || Character.isLowerCase(anonymousID.charAt(0));