From b8e93aca0c27c36e294bb873fc944f564535bb8c Mon Sep 17 00:00:00 2001 From: tsantalis Date: Fri, 22 Mar 2024 21:59:01 -0400 Subject: [PATCH] Implementation for issue #682 --- .../VariableReplacementAnalysis.java | 28 ++++ .../ReplaceGenericWithDiamondRefactoring.java | 150 ++++++++++++++++++ .../refactoringminer/api/RefactoringType.java | 6 +- .../test/RefactoringPopulator.java | 3 +- 4 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 src/main/java/gr/uom/java/xmi/diff/ReplaceGenericWithDiamondRefactoring.java diff --git a/src/main/java/gr/uom/java/xmi/decomposition/VariableReplacementAnalysis.java b/src/main/java/gr/uom/java/xmi/decomposition/VariableReplacementAnalysis.java index 6be30180c4..2ec1482118 100644 --- a/src/main/java/gr/uom/java/xmi/decomposition/VariableReplacementAnalysis.java +++ b/src/main/java/gr/uom/java/xmi/decomposition/VariableReplacementAnalysis.java @@ -50,6 +50,7 @@ import gr.uom.java.xmi.diff.RemoveVariableAnnotationRefactoring; import gr.uom.java.xmi.diff.RemoveVariableModifierRefactoring; import gr.uom.java.xmi.diff.RenameVariableRefactoring; +import gr.uom.java.xmi.diff.ReplaceGenericWithDiamondRefactoring; import gr.uom.java.xmi.diff.SplitVariableRefactoring; import gr.uom.java.xmi.diff.UMLAbstractClassDiff; import gr.uom.java.xmi.diff.UMLAnnotationDiff; @@ -890,6 +891,33 @@ private boolean callsInlinedMethod(VariableDeclaration removedVariable) { private void findTypeChanges() { for(AbstractCodeMapping mapping : mappings) { + for(Replacement r : mapping.getReplacements()) { + if(r.getType().equals(ReplacementType.TYPE)) { + if(r.getBefore().contains("<") && r.getBefore().contains(">") && + !r.getBefore().contains("<>") && + r.getAfter().contains("<>")) { + AbstractCall matchedCreation1 = null; + for(AbstractCall creation1 : mapping.getFragment1().getCreations()) { + if(creation1.actualString().contains(r.getBefore())) { + matchedCreation1 = creation1; + break; + } + } + AbstractCall matchedCreation2 = null; + for(AbstractCall creation2 : mapping.getFragment2().getCreations()) { + if(creation2.actualString().contains(r.getAfter())) { + matchedCreation2 = creation2; + break; + } + } + if(matchedCreation1 != null && matchedCreation2 != null) { + ReplaceGenericWithDiamondRefactoring refactoring = + new ReplaceGenericWithDiamondRefactoring(mapping.getFragment1(), mapping.getFragment2(), matchedCreation1, matchedCreation2, operation1, operation2); + refactorings.add(refactoring); + } + } + } + } AbstractCodeFragment fragment1 = mapping.getFragment1(); AbstractCodeFragment fragment2 = mapping.getFragment2(); List declarations1 = fragment1.getVariableDeclarations(); diff --git a/src/main/java/gr/uom/java/xmi/diff/ReplaceGenericWithDiamondRefactoring.java b/src/main/java/gr/uom/java/xmi/diff/ReplaceGenericWithDiamondRefactoring.java new file mode 100644 index 0000000000..a4f14c73f5 --- /dev/null +++ b/src/main/java/gr/uom/java/xmi/diff/ReplaceGenericWithDiamondRefactoring.java @@ -0,0 +1,150 @@ +package gr.uom.java.xmi.diff; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.refactoringminer.api.Refactoring; +import org.refactoringminer.api.RefactoringType; + +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.ObjectCreation; + +public class ReplaceGenericWithDiamondRefactoring implements Refactoring { + private AbstractCodeFragment statementBefore; + private AbstractCodeFragment statementAfter; + private AbstractCall creationBefore; + private AbstractCall creationAfter; + private VariableDeclarationContainer operationBefore; + private VariableDeclarationContainer operationAfter; + + public ReplaceGenericWithDiamondRefactoring(AbstractCodeFragment statementBefore, AbstractCodeFragment statementAfter, + AbstractCall creationBefore, AbstractCall creationAfter, + VariableDeclarationContainer operationBefore, VariableDeclarationContainer operationAfter) { + this.statementBefore = statementBefore; + this.statementAfter = statementAfter; + this.creationBefore = creationBefore; + this.creationAfter = creationAfter; + this.operationBefore = operationBefore; + this.operationAfter = operationAfter; + } + + public AbstractCall getCreationBefore() { + return creationBefore; + } + + public AbstractCall getCreationAfter() { + return creationAfter; + } + + public VariableDeclarationContainer getOperationBefore() { + return operationBefore; + } + + public VariableDeclarationContainer getOperationAfter() { + return operationAfter; + } + + @Override + public List leftSide() { + List ranges = new ArrayList(); + ranges.add(creationBefore.codeRange() + .setDescription("original creation") + .setCodeElement(creationBefore.toString())); + ranges.add(operationBefore.codeRange() + .setDescription("original method declaration") + .setCodeElement(operationBefore.toString())); + return ranges; + } + + @Override + public List rightSide() { + List ranges = new ArrayList(); + ranges.add(creationAfter.codeRange() + .setDescription("creation with diamond operator") + .setCodeElement(creationAfter.toString())); + ranges.add(operationAfter.codeRange() + .setDescription("method declaration with diamond operator") + .setCodeElement(operationAfter.toString())); + return ranges; + } + + @Override + public RefactoringType getRefactoringType() { + return RefactoringType.REPLACE_GENERIC_WITH_DIAMOND; + } + + @Override + public String getName() { + return getRefactoringType().getDisplayName(); + } + + @Override + public Set> getInvolvedClassesBeforeRefactoring() { + Set> pairs = new LinkedHashSet>(); + pairs.add(new ImmutablePair(getOperationBefore().getLocationInfo().getFilePath(), getOperationBefore().getClassName())); + return pairs; + } + + @Override + public Set> getInvolvedClassesAfterRefactoring() { + Set> pairs = new LinkedHashSet>(); + pairs.add(new ImmutablePair(getOperationAfter().getLocationInfo().getFilePath(), getOperationAfter().getClassName())); + return pairs; + } + + @Override + public int hashCode() { + return Objects.hash(statementAfter, statementBefore, operationAfter, operationBefore); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ReplaceGenericWithDiamondRefactoring other = (ReplaceGenericWithDiamondRefactoring) obj; + return Objects.equals(statementAfter, other.statementAfter) + && Objects.equals(statementBefore, other.statementBefore) + && Objects.equals(operationAfter, other.operationAfter) + && Objects.equals(operationBefore, other.operationBefore); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getName()).append("\t"); + sb.append(extractType(creationBefore)); + sb.append(" with "); + sb.append(extractType(creationAfter)); + sb.append(" in method "); + sb.append(operationAfter); + sb.append(" from class ").append(operationAfter.getClassName()); + return sb.toString(); + } + + private static String extractType(AbstractCall c) { + if(c instanceof ObjectCreation) { + ObjectCreation creation = (ObjectCreation)c; + return creation.getType().toString(); + } + else { + String s = c.actualString(); + if(s.startsWith("new ")) { + s = s.substring(4); + } + if(s.contains("(")) { + s = s.substring(0, s.indexOf("(")); + } + return s; + } + } +} diff --git a/src/main/java/org/refactoringminer/api/RefactoringType.java b/src/main/java/org/refactoringminer/api/RefactoringType.java index 24c090e373..6f6c32327a 100644 --- a/src/main/java/org/refactoringminer/api/RefactoringType.java +++ b/src/main/java/org/refactoringminer/api/RefactoringType.java @@ -109,7 +109,8 @@ public enum RefactoringType { SPLIT_OPERATION("Split Method", "Split Method (.+) to \\[(.+)\\] in class (.+)"), MOVE_CODE("Move Code", "Move Code from (.+) to (.+) in class (.+)"), PARAMETERIZE_TEST("Parameterize Test", "Parameterize Test (.+) to (.+) in class (.+)"), - ASSERT_THROWS("Assert Throws", "Assert Throws (.+) in method (.+) from class (.+)"); + ASSERT_THROWS("Assert Throws", "Assert Throws (.+) in method (.+) from class (.+)"), + REPLACE_GENERIC_WITH_DIAMOND("Replace Generic With Diamond", "Replace Generic With Diamond (.+) with (.+) in method (.+) from class (.+)"); private String displayName; private Pattern regex; @@ -214,7 +215,8 @@ public enum RefactoringType { MOVE_CODE, REPLACE_ANONYMOUS_WITH_CLASS, PARAMETERIZE_TEST, - ASSERT_THROWS + ASSERT_THROWS, + REPLACE_GENERIC_WITH_DIAMOND }; private RefactoringType(String displayName, String regex, int ... aggregateGroups) { diff --git a/src/test/java/org/refactoringminer/test/RefactoringPopulator.java b/src/test/java/org/refactoringminer/test/RefactoringPopulator.java index 4dff03a741..c7678808ec 100644 --- a/src/test/java/org/refactoringminer/test/RefactoringPopulator.java +++ b/src/test/java/org/refactoringminer/test/RefactoringPopulator.java @@ -131,7 +131,8 @@ public enum Refactorings { ReplaceAnonymousWithClass(new BigInteger("158456325028528675187087900672")), ParameterizeTest(new BigInteger("316912650057057350374175801344")), AssertThrows(new BigInteger("633825300114114700748351602688")), - All(new BigInteger("1267650600228229401496703205375")); + ReplaceGenericWithDiamond(new BigInteger("1267650600228229401496703205376")), + All(new BigInteger("2535301200456458802993406410751")); private BigInteger value;