From 4a705b2f17fe305aae3cd50f9445679555535a3c Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Mon, 2 Apr 2018 13:27:28 +0300 Subject: [PATCH 01/14] first draft of an algorithm --- .../ml_methods_group/algorithm/JetMove.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/main/java/org/ml_methods_group/algorithm/JetMove.java diff --git a/src/main/java/org/ml_methods_group/algorithm/JetMove.java b/src/main/java/org/ml_methods_group/algorithm/JetMove.java new file mode 100644 index 00000000..1927651d --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/JetMove.java @@ -0,0 +1,106 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm; + +import org.ml_methods_group.algorithm.entity.ClassEntity; +import org.ml_methods_group.algorithm.entity.MethodEntity; + +import java.util.ArrayList; +import java.util.List; + +public class JetMove extends Algorithm { + JetMove() { + super("JetMove", false); + } + + @Override + protected List calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) { + List allMethods = context.getEntities().getMethods(); // get all methods ?? + List allClasses = context.getEntities().getClasses(); // get all classes ?? + List refactorings = new ArrayList<>(); + + for(MethodEntity curMethod : allMethods) { + ClassEntity curClass = curMethod.getClassEntity(); // the class of currMethod + double curSimilarity = calculateSimilarity(curMethod, curClass); + List potentialClasses = new ArrayList<>(); + for(ClassEntity potentialClass : allClasses) { + if(calculateSimilarity(curMethod, potentialClass) > curSimilarity) + potentialClasses.add(potentialClass); + } + ClassEntity bestClass = findBestClass(potentialClasses); + if(bestClass != null) refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), 0, false)); //accuracy ~ difference between coeff ?? + } + return refactorings; + } + + private double calculateSimilarity(MethodEntity methodEntity, ClassEntity classEntity) { + double similarity = 0; + List classMethods = classEntity.getMethodEntities(); //all methods of classEntity + for(MethodEntity curMethod : classMethods) { + if(methodEntity != curMethod) + similarity += methodSimilarity(methodEntity, curMethod); + } + if(methodEntity.getClassName() != classEntity.getName()) + return similarity / classMethods.size(); + else + return similarity / (classMethods.size() - 1); + } + + private double methodSimilarity (MethodEntity methodFst, MethodEntity methodSnd) { + Dependencies depFst = new Dependencies(methodFst); + Dependencies depSnd = new Dependencies(methodSnd); + + int depCardinalityFst = depFst.cardinality(); + int depCardinalitySnd = depSnd.cardinality(); + int depCardinalityIntersection = depFst.calculateIntersectionCardinality(depSnd); + return (double)depCardinalityIntersection/(depCardinalityFst + depCardinalitySnd - depCardinalityIntersection); // Jaccard Coefficient + } + + protected class Dependencies { + // here are lists that need to be made: + + //method calls + //field accesses + //object instantiations + //local declarations + //return types + //exceptions + //annotations + + //ignoring primitive types and types and annotations from java.lang and java.util + + public Dependencies(MethodEntity m) { + //find all these things + } + + + public int cardinality() { + + return 0; //todo + } + + public int calculateIntersectionCardinality(Dependencies depSnd) { + return 0; //todo + } + + } + + private ClassEntity findBestClass(List potentialClasses) { + //todo choose movable class with the biggest coefficient + return null; + } +} From 7cf22d0e26e34c978128da010d50f869b044811f Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Tue, 1 May 2018 13:06:26 +0300 Subject: [PATCH 02/14] naive approach --- .../org/ml_methods_group/algorithm/JMove.java | 168 ++++++++++++++++++ .../ml_methods_group/algorithm/JetMove.java | 106 ----------- .../algorithm/entity/ClassEntity.java | 7 + .../algorithm/entity/MethodEntity.java | 7 + 4 files changed, 182 insertions(+), 106 deletions(-) create mode 100644 src/main/java/org/ml_methods_group/algorithm/JMove.java delete mode 100644 src/main/java/org/ml_methods_group/algorithm/JetMove.java diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java new file mode 100644 index 00000000..423b98e4 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -0,0 +1,168 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm; + +import com.intellij.openapi.module.impl.scopes.LibraryScope; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiReferenceList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.ml_methods_group.algorithm.entity.ClassEntity; +import org.ml_methods_group.algorithm.entity.MethodEntity; + +import java.util.*; +import java.util.function.BinaryOperator; + +public class JMove extends Algorithm { + private final int MIN_NUMBER_OF_CANDIDATE_CLASSES = 3; + private final int MIN_NUMBER_OF_DEPENDENCIES = 4; + private final double MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS = 0.25; + + JMove() { + super("JMove", false); + } + + @Override + protected List calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) { + List allMethods = context.getEntities().getMethods(); // get all methods ?? + List allClasses = context.getEntities().getClasses(); // get all classes ?? + List refactorings = new ArrayList<>(); + + for(MethodEntity curMethod : allMethods) { + Dependencies curDependencies = new Dependencies(curMethod); + if(curDependencies.cardinality() < MIN_NUMBER_OF_DEPENDENCIES) + continue; + PsiClass curClass = curMethod.getPsiMethod().getContainingClass(); // the class of cф3цurMethod + double curSimilarity = calculateSimilarity(curMethod, curClass); + Map potentialClasses = new HashMap<>(); + for(ClassEntity potentialClass : allClasses) { + double potentialClassSimilarity = calculateSimilarity(curMethod, potentialClass.getPsiClass()); + if(potentialClassSimilarity > curSimilarity) { + potentialClasses.put(potentialClass, potentialClassSimilarity); + } + } + ClassEntity bestClass = findBestClass(potentialClasses); + if(bestClass != null) { + double diff = (potentialClasses.get(bestClass) - curSimilarity)/potentialClasses.get(bestClass); //may be + if(diff >= MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS) + refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), diff, false)); //accuracy ~ difference between coeff ?? + } + } + return refactorings; + } + + private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull PsiClass psiClass) { + double similarity = 0; + PsiMethod classMethods[] = psiClass.getMethods(); //all methods of psiClass + for(PsiMethod curMethod : classMethods) { + if(!methodEntity.getName().equals(curMethod.getName())) + similarity += methodSimilarity(methodEntity, new MethodEntity(curMethod)); //todo find out if creating new MethodEntity will fill RelevantProperties(probably not) + } + if(methodEntity.getClassName().equals(psiClass.getName())) + return similarity / classMethods.length; + else + return similarity / (classMethods.length - 1); + } + + private double methodSimilarity (MethodEntity methodFst, MethodEntity methodSnd) { + Dependencies depFst = new Dependencies(methodFst); + Dependencies depSnd = new Dependencies(methodSnd); + + int depCardinalityFst = depFst.cardinality(); + int depCardinalitySnd = depSnd.cardinality(); + int depCardinalityIntersection = depFst.calculateIntersectionCardinality(depSnd); + return (double)depCardinalityIntersection/(depCardinalityFst + depCardinalitySnd - depCardinalityIntersection); // Jaccard Coefficient + } + + protected class Dependencies { + // here are lists that need to be made: + + //method calls + private Set methodCalls; + + //field accesses + //object instantiations + //local declarations + private Set instances; + + //return types + private PsiType returnType; //use PsiMethod PsiType getReturnType() + + //exceptions + private PsiReferenceList exceptions; //use PsiMethod PsiReferenceList getThrowsList() + + //annotations + private List annotations; + + //todo ignore primitive types and types and annotations from java.lang and java.util + + private Dependencies(@NotNull MethodEntity m) { + methodCalls = m.getRelevantProperties().getMethods(); + instances = m.getRelevantProperties().getMethods(); + returnType = m.getPsiMethod().getReturnType(); + exceptions = m.getPsiMethod().getThrowsList(); + //todo initialize annotations + } + + + private int cardinality() { + + return methodCalls.size() + + instances.size() + + 1 //returnType + + exceptions.getReferencedTypes().length + + annotations.size(); + } + + private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { + int result = 0; + + Set methodCallIntersection = new HashSet<>(methodCalls); + methodCallIntersection.retainAll(depSnd.methodCalls); + result += methodCallIntersection.size(); + + Set instancesIntersection = new HashSet<>(instances); + instancesIntersection.retainAll(depSnd.instances); + result += instancesIntersection.size(); + + if(returnType.toString().equals(depSnd.returnType.toString())) + result++; + + //todo find intersection of exceptions and annotations + + return result; + } + + } + + @Nullable + private ClassEntity findBestClass(@NotNull Map potentialClasses) { //choose movable class with the biggest coefficient + if(potentialClasses.size() < MIN_NUMBER_OF_CANDIDATE_CLASSES) + return null; + ClassEntity bestClass = null; + Double bestCoefficient = 0.0; + for(Map.Entry entry : potentialClasses.entrySet()) { + if(entry.getValue() > bestCoefficient) { + bestCoefficient = entry.getValue(); + bestClass = entry.getKey(); + } + } + return bestClass; + } +} diff --git a/src/main/java/org/ml_methods_group/algorithm/JetMove.java b/src/main/java/org/ml_methods_group/algorithm/JetMove.java deleted file mode 100644 index 1927651d..00000000 --- a/src/main/java/org/ml_methods_group/algorithm/JetMove.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ml_methods_group.algorithm; - -import org.ml_methods_group.algorithm.entity.ClassEntity; -import org.ml_methods_group.algorithm.entity.MethodEntity; - -import java.util.ArrayList; -import java.util.List; - -public class JetMove extends Algorithm { - JetMove() { - super("JetMove", false); - } - - @Override - protected List calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) { - List allMethods = context.getEntities().getMethods(); // get all methods ?? - List allClasses = context.getEntities().getClasses(); // get all classes ?? - List refactorings = new ArrayList<>(); - - for(MethodEntity curMethod : allMethods) { - ClassEntity curClass = curMethod.getClassEntity(); // the class of currMethod - double curSimilarity = calculateSimilarity(curMethod, curClass); - List potentialClasses = new ArrayList<>(); - for(ClassEntity potentialClass : allClasses) { - if(calculateSimilarity(curMethod, potentialClass) > curSimilarity) - potentialClasses.add(potentialClass); - } - ClassEntity bestClass = findBestClass(potentialClasses); - if(bestClass != null) refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), 0, false)); //accuracy ~ difference between coeff ?? - } - return refactorings; - } - - private double calculateSimilarity(MethodEntity methodEntity, ClassEntity classEntity) { - double similarity = 0; - List classMethods = classEntity.getMethodEntities(); //all methods of classEntity - for(MethodEntity curMethod : classMethods) { - if(methodEntity != curMethod) - similarity += methodSimilarity(methodEntity, curMethod); - } - if(methodEntity.getClassName() != classEntity.getName()) - return similarity / classMethods.size(); - else - return similarity / (classMethods.size() - 1); - } - - private double methodSimilarity (MethodEntity methodFst, MethodEntity methodSnd) { - Dependencies depFst = new Dependencies(methodFst); - Dependencies depSnd = new Dependencies(methodSnd); - - int depCardinalityFst = depFst.cardinality(); - int depCardinalitySnd = depSnd.cardinality(); - int depCardinalityIntersection = depFst.calculateIntersectionCardinality(depSnd); - return (double)depCardinalityIntersection/(depCardinalityFst + depCardinalitySnd - depCardinalityIntersection); // Jaccard Coefficient - } - - protected class Dependencies { - // here are lists that need to be made: - - //method calls - //field accesses - //object instantiations - //local declarations - //return types - //exceptions - //annotations - - //ignoring primitive types and types and annotations from java.lang and java.util - - public Dependencies(MethodEntity m) { - //find all these things - } - - - public int cardinality() { - - return 0; //todo - } - - public int calculateIntersectionCardinality(Dependencies depSnd) { - return 0; //todo - } - - } - - private ClassEntity findBestClass(List potentialClasses) { - //todo choose movable class with the biggest coefficient - return null; - } -} diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/ClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/ClassEntity.java index c9fea859..eb7db95f 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/ClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/ClassEntity.java @@ -20,8 +20,11 @@ import com.sixrr.metrics.MetricCategory; public class ClassEntity extends Entity { + private PsiClass psiClass; + ClassEntity(PsiClass psiClass) { super(psiClass); + this.psiClass = psiClass; } private ClassEntity(ClassEntity original) { @@ -56,4 +59,8 @@ public ClassEntity copy() { public boolean isField() { return false; } + + public PsiClass getPsiClass() { + return psiClass; + } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/MethodEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/MethodEntity.java index d04621f6..92667309 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/MethodEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/MethodEntity.java @@ -23,8 +23,11 @@ public class MethodEntity extends Entity { + private PsiMethod psiMethod; + MethodEntity(PsiMethod method) { super(method); + psiMethod = method; isMovable = !PSIUtil.isOverriding(method) && !MethodUtils.isAbstract(method) && !method.isConstructor(); } @@ -54,4 +57,8 @@ public MethodEntity copy() { public boolean isField() { return false; } + + public PsiMethod getPsiMethod() { + return psiMethod; + } } From a6e0ddeb833ee8f6ff3743a2d69a9c7134b7d3d2 Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Tue, 1 May 2018 13:45:16 +0300 Subject: [PATCH 03/14] approach that should work (fixed the problem with finding ClassEntity from MethodEntity and other way around) --- .../org/ml_methods_group/algorithm/JMove.java | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index 423b98e4..f5f280ea 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -17,8 +17,6 @@ package org.ml_methods_group.algorithm; import com.intellij.openapi.module.impl.scopes.LibraryScope; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiType; import com.intellij.psi.PsiReferenceList; import org.jetbrains.annotations.NotNull; @@ -27,7 +25,6 @@ import org.ml_methods_group.algorithm.entity.MethodEntity; import java.util.*; -import java.util.function.BinaryOperator; public class JMove extends Algorithm { private final int MIN_NUMBER_OF_CANDIDATE_CLASSES = 3; @@ -44,19 +41,35 @@ protected List calculateRefactorings(ExecutionContext context, bool List allClasses = context.getEntities().getClasses(); // get all classes ?? List refactorings = new ArrayList<>(); + Map nameToClassEntity = new HashMap<>(); + Map nameToMethodEntity = new HashMap<>(); + + for(ClassEntity classEntity : allClasses) { + nameToClassEntity.put(classEntity.getName(), classEntity); //I see no other way to find containing ClassEntity for MethodEntity + } + + for(MethodEntity methodEntity : allMethods) { + nameToMethodEntity.put(methodEntity.getName(), methodEntity); //same + } + + for(MethodEntity curMethod : allMethods) { Dependencies curDependencies = new Dependencies(curMethod); if(curDependencies.cardinality() < MIN_NUMBER_OF_DEPENDENCIES) continue; - PsiClass curClass = curMethod.getPsiMethod().getContainingClass(); // the class of cф3цurMethod - double curSimilarity = calculateSimilarity(curMethod, curClass); + ClassEntity curClass = nameToClassEntity.get(curMethod.getClassName()); //ClassEntity of curMethod (potentially working) + double curSimilarity = calculateSimilarity(curMethod, curClass, nameToMethodEntity); Map potentialClasses = new HashMap<>(); for(ClassEntity potentialClass : allClasses) { - double potentialClassSimilarity = calculateSimilarity(curMethod, potentialClass.getPsiClass()); + double potentialClassSimilarity = calculateSimilarity(curMethod, potentialClass, nameToMethodEntity); if(potentialClassSimilarity > curSimilarity) { potentialClasses.put(potentialClass, potentialClassSimilarity); } } + + if(potentialClasses.size() < MIN_NUMBER_OF_CANDIDATE_CLASSES) + continue; + ClassEntity bestClass = findBestClass(potentialClasses); if(bestClass != null) { double diff = (potentialClasses.get(bestClass) - curSimilarity)/potentialClasses.get(bestClass); //may be @@ -67,17 +80,19 @@ protected List calculateRefactorings(ExecutionContext context, bool return refactorings; } - private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull PsiClass psiClass) { + private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull ClassEntity classEntity, Map nameToMethodEntity) { double similarity = 0; - PsiMethod classMethods[] = psiClass.getMethods(); //all methods of psiClass - for(PsiMethod curMethod : classMethods) { - if(!methodEntity.getName().equals(curMethod.getName())) - similarity += methodSimilarity(methodEntity, new MethodEntity(curMethod)); //todo find out if creating new MethodEntity will fill RelevantProperties(probably not) + Set methodNames = classEntity.getRelevantProperties().getMethods(); + + for(String curMethodName : methodNames) { + MethodEntity curMethod = nameToMethodEntity.get(curMethodName); + if(!methodEntity.getName().equals(curMethodName)) + similarity += methodSimilarity(methodEntity, curMethod); } - if(methodEntity.getClassName().equals(psiClass.getName())) - return similarity / classMethods.length; + if(methodEntity.getClassName().equals(classEntity.getName())) + return similarity / methodNames.size(); else - return similarity / (classMethods.length - 1); + return similarity / (methodNames.size() - 1); } private double methodSimilarity (MethodEntity methodFst, MethodEntity methodSnd) { @@ -90,7 +105,7 @@ private double methodSimilarity (MethodEntity methodFst, MethodEntity methodSnd) return (double)depCardinalityIntersection/(depCardinalityFst + depCardinalitySnd - depCardinalityIntersection); // Jaccard Coefficient } - protected class Dependencies { + private class Dependencies { // here are lists that need to be made: //method calls From 6be7cd41772497942074888d32f5a4a5a886e509 Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Tue, 1 May 2018 18:09:36 +0300 Subject: [PATCH 04/14] added JMove to ALGORITHMS array --- src/main/java/org/ml_methods_group/algorithm/JMove.java | 2 +- .../refactoring/RefactoringExecutionContext.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index f5f280ea..a6901c5a 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -16,7 +16,7 @@ package org.ml_methods_group.algorithm; -import com.intellij.openapi.module.impl.scopes.LibraryScope; + import com.intellij.psi.PsiType; import com.intellij.psi.PsiReferenceList; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java index 519c9ae6..08f6a87d 100644 --- a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java +++ b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java @@ -44,7 +44,7 @@ public class RefactoringExecutionContext { private static final Logger LOGGER = Logging.getLogger(RefactoringExecutionContext.class); private static final List> ALGORITHMS = Arrays.asList(ARI.class, AKMeans.class, - CCDA.class, HAC.class, MRI.class); + CCDA.class, HAC.class, MRI.class, JMove.class); @NotNull private final MetricsRunImpl metricsRun = new MetricsRunImpl(); From 3e63a26363b4f832064b01deaea43a1af78c5d2c Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Sun, 6 May 2018 16:03:59 +0300 Subject: [PATCH 05/14] Got annotations and exceptions in Dependencies done --- .../org/ml_methods_group/algorithm/JMove.java | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index a6901c5a..42f78785 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -18,7 +18,8 @@ import com.intellij.psi.PsiType; -import com.intellij.psi.PsiReferenceList; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiClassType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.ml_methods_group.algorithm.entity.ClassEntity; @@ -117,13 +118,13 @@ private class Dependencies { private Set instances; //return types - private PsiType returnType; //use PsiMethod PsiType getReturnType() + private PsiType returnType; //exceptions - private PsiReferenceList exceptions; //use PsiMethod PsiReferenceList getThrowsList() + private Set exceptions; //annotations - private List annotations; + private Set annotations; //todo ignore primitive types and types and annotations from java.lang and java.util @@ -131,8 +132,18 @@ private Dependencies(@NotNull MethodEntity m) { methodCalls = m.getRelevantProperties().getMethods(); instances = m.getRelevantProperties().getMethods(); returnType = m.getPsiMethod().getReturnType(); - exceptions = m.getPsiMethod().getThrowsList(); - //todo initialize annotations + + exceptions = new HashSet<>(); + PsiClassType[] referencedTypes = m.getPsiMethod().getThrowsList().getReferencedTypes(); + for(PsiClassType classType : referencedTypes) { + exceptions.add(classType.getClassName()); //may be not that name todo: check + } + + annotations = new HashSet<>(); + PsiAnnotation[] psiAnnotations = m.getPsiMethod().getModifierList().getAnnotations(); + for(PsiAnnotation psiAnnotation : psiAnnotations) { + annotations.add(psiAnnotation.getQualifiedName()); //may be not that name todo: check + } } @@ -141,27 +152,33 @@ private int cardinality() { return methodCalls.size() + instances.size() + 1 //returnType - + exceptions.getReferencedTypes().length + + exceptions.size() + annotations.size(); } private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { - int result = 0; + int intersectionCardinality = 0; Set methodCallIntersection = new HashSet<>(methodCalls); methodCallIntersection.retainAll(depSnd.methodCalls); - result += methodCallIntersection.size(); + intersectionCardinality += methodCallIntersection.size(); Set instancesIntersection = new HashSet<>(instances); instancesIntersection.retainAll(depSnd.instances); - result += instancesIntersection.size(); + intersectionCardinality += instancesIntersection.size(); if(returnType.toString().equals(depSnd.returnType.toString())) - result++; + intersectionCardinality++; + + Set exceptionsIntersection = new HashSet<>(exceptions); + exceptionsIntersection.retainAll(depSnd.exceptions); + intersectionCardinality += exceptionsIntersection.size(); - //todo find intersection of exceptions and annotations + Set annotationsIntersection = new HashSet<>(exceptions); + annotationsIntersection.retainAll(depSnd.exceptions); + intersectionCardinality += annotationsIntersection.size(); - return result; + return intersectionCardinality; } } From 33d2c3d1b11e9c140132067a64657d28f10b40af Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Tue, 8 May 2018 18:39:13 +0300 Subject: [PATCH 06/14] Added a bit of logger; now calculating all the Dependencies at once; added localVariables set into Dependencies --- .../org/ml_methods_group/algorithm/JMove.java | 84 ++++++++++++------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index 42f78785..27cbc21f 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -17,52 +17,62 @@ package org.ml_methods_group.algorithm; -import com.intellij.psi.PsiType; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClassType; +import com.intellij.psi.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiLocalVariable; +import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.ml_methods_group.algorithm.entity.ClassEntity; import org.ml_methods_group.algorithm.entity.MethodEntity; +import org.ml_methods_group.config.Logging; import java.util.*; public class JMove extends Algorithm { + private static final Logger LOGGER = Logging.getLogger(JMove.class); private final int MIN_NUMBER_OF_CANDIDATE_CLASSES = 3; private final int MIN_NUMBER_OF_DEPENDENCIES = 4; private final double MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS = 0.25; - JMove() { + public JMove() { super("JMove", false); } @Override protected List calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) { - List allMethods = context.getEntities().getMethods(); // get all methods ?? - List allClasses = context.getEntities().getClasses(); // get all classes ?? + LOGGER.info("Starting calculating refactorings"); + List allMethods = context.getEntities().getMethods(); + List allClasses = context.getEntities().getClasses(); + LOGGER.info("Found " + allMethods.size() + " methods and " + allClasses.size() + " classes"); List refactorings = new ArrayList<>(); Map nameToClassEntity = new HashMap<>(); - Map nameToMethodEntity = new HashMap<>(); + Map nameToDependencies = new HashMap<>(); for(ClassEntity classEntity : allClasses) { - nameToClassEntity.put(classEntity.getName(), classEntity); //I see no other way to find containing ClassEntity for MethodEntity + nameToClassEntity.put(classEntity.getName(), classEntity); } for(MethodEntity methodEntity : allMethods) { - nameToMethodEntity.put(methodEntity.getName(), methodEntity); //same + nameToDependencies.put(methodEntity.getName(), new Dependencies(methodEntity)); } for(MethodEntity curMethod : allMethods) { - Dependencies curDependencies = new Dependencies(curMethod); + if(!curMethod.isMovable()) + continue; + LOGGER.info("Checking " + curMethod.getName()); + + Dependencies curDependencies = nameToDependencies.get(curMethod.getName()); if(curDependencies.cardinality() < MIN_NUMBER_OF_DEPENDENCIES) continue; - ClassEntity curClass = nameToClassEntity.get(curMethod.getClassName()); //ClassEntity of curMethod (potentially working) - double curSimilarity = calculateSimilarity(curMethod, curClass, nameToMethodEntity); + ClassEntity curClass = nameToClassEntity.get(curMethod.getClassName()); + double curSimilarity = calculateSimilarity(curMethod, curClass, nameToDependencies); Map potentialClasses = new HashMap<>(); for(ClassEntity potentialClass : allClasses) { - double potentialClassSimilarity = calculateSimilarity(curMethod, potentialClass, nameToMethodEntity); + double potentialClassSimilarity = calculateSimilarity(curMethod, potentialClass, nameToDependencies); if(potentialClassSimilarity > curSimilarity) { potentialClasses.put(potentialClass, potentialClassSimilarity); } @@ -73,22 +83,21 @@ protected List calculateRefactorings(ExecutionContext context, bool ClassEntity bestClass = findBestClass(potentialClasses); if(bestClass != null) { - double diff = (potentialClasses.get(bestClass) - curSimilarity)/potentialClasses.get(bestClass); //may be + double diff = (potentialClasses.get(bestClass) - curSimilarity)/potentialClasses.get(bestClass); //may be idk if(diff >= MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS) - refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), diff, false)); //accuracy ~ difference between coeff ?? + refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), diff, false)); //accuracy ~ difference between coeff ?? idk } } return refactorings; } - private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull ClassEntity classEntity, Map nameToMethodEntity) { + private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull ClassEntity classEntity, Map nameToDependencies) { double similarity = 0; Set methodNames = classEntity.getRelevantProperties().getMethods(); for(String curMethodName : methodNames) { - MethodEntity curMethod = nameToMethodEntity.get(curMethodName); if(!methodEntity.getName().equals(curMethodName)) - similarity += methodSimilarity(methodEntity, curMethod); + similarity += methodSimilarity(nameToDependencies.get(methodEntity.getName()), nameToDependencies.get(curMethodName)); } if(methodEntity.getClassName().equals(classEntity.getName())) return similarity / methodNames.size(); @@ -96,10 +105,7 @@ private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull return similarity / (methodNames.size() - 1); } - private double methodSimilarity (MethodEntity methodFst, MethodEntity methodSnd) { - Dependencies depFst = new Dependencies(methodFst); - Dependencies depSnd = new Dependencies(methodSnd); - + private double methodSimilarity (@NotNull Dependencies depFst,@NotNull Dependencies depSnd) { int depCardinalityFst = depFst.cardinality(); int depCardinalitySnd = depSnd.cardinality(); int depCardinalityIntersection = depFst.calculateIntersectionCardinality(depSnd); @@ -113,12 +119,15 @@ private class Dependencies { private Set methodCalls; //field accesses + + private Set instances; + //object instantiations //local declarations - private Set instances; + private Set localVariables; //idk //return types - private PsiType returnType; + private String returnType; //exceptions private Set exceptions; @@ -128,22 +137,32 @@ private class Dependencies { //todo ignore primitive types and types and annotations from java.lang and java.util - private Dependencies(@NotNull MethodEntity m) { - methodCalls = m.getRelevantProperties().getMethods(); - instances = m.getRelevantProperties().getMethods(); - returnType = m.getPsiMethod().getReturnType(); + private Dependencies(@NotNull MethodEntity methodForDependencies) { + methodCalls = methodForDependencies.getRelevantProperties().getMethods(); + instances = methodForDependencies.getRelevantProperties().getFields(); + returnType = methodForDependencies.getPsiMethod().getReturnType().toString(); exceptions = new HashSet<>(); - PsiClassType[] referencedTypes = m.getPsiMethod().getThrowsList().getReferencedTypes(); + PsiClassType[] referencedTypes = methodForDependencies.getPsiMethod().getThrowsList().getReferencedTypes(); for(PsiClassType classType : referencedTypes) { exceptions.add(classType.getClassName()); //may be not that name todo: check } annotations = new HashSet<>(); - PsiAnnotation[] psiAnnotations = m.getPsiMethod().getModifierList().getAnnotations(); + PsiAnnotation[] psiAnnotations = methodForDependencies.getPsiMethod().getModifierList().getAnnotations(); for(PsiAnnotation psiAnnotation : psiAnnotations) { annotations.add(psiAnnotation.getQualifiedName()); //may be not that name todo: check } + + localVariables = new HashSet<>(); + methodForDependencies.getPsiMethod().accept(new JavaRecursiveElementVisitor() { + @Override + public void visitLocalVariable(PsiLocalVariable variable) { + super.visitLocalVariable(variable); + localVariables.add(variable.getType().getCanonicalText()); //idk + //System.out.println("Found a variable at offset " + variable.getTextRange().getStartOffset()); + } + }); } @@ -151,7 +170,8 @@ private int cardinality() { return methodCalls.size() + instances.size() - + 1 //returnType + + localVariables.size() + + 1 //returnType //fixme: may be null if I learn to ignore + exceptions.size() + annotations.size(); } @@ -167,7 +187,7 @@ private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { instancesIntersection.retainAll(depSnd.instances); intersectionCardinality += instancesIntersection.size(); - if(returnType.toString().equals(depSnd.returnType.toString())) + if(returnType.equals(depSnd.returnType)) intersectionCardinality++; Set exceptionsIntersection = new HashSet<>(exceptions); @@ -178,6 +198,10 @@ private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { annotationsIntersection.retainAll(depSnd.exceptions); intersectionCardinality += annotationsIntersection.size(); + Set localVariablesIntersection = new HashSet<>(localVariables); + localVariablesIntersection.retainAll(depSnd.localVariables); + intersectionCardinality += localVariablesIntersection.size(); + return intersectionCardinality; } From 0e3f03a03b931f007076d133214b39b72c23fa13 Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Wed, 9 May 2018 13:25:56 +0300 Subject: [PATCH 07/14] added finding of object instantiations for dependencies class by finiding new statements + a little renaming --- .../org/ml_methods_group/algorithm/JMove.java | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index 27cbc21f..11dddc6a 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -17,10 +17,7 @@ package org.ml_methods_group.algorithm; -import com.intellij.psi.PsiAnnotation; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.JavaRecursiveElementVisitor; -import com.intellij.psi.PsiLocalVariable; +import com.intellij.psi.*; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -73,7 +70,7 @@ protected List calculateRefactorings(ExecutionContext context, bool Map potentialClasses = new HashMap<>(); for(ClassEntity potentialClass : allClasses) { double potentialClassSimilarity = calculateSimilarity(curMethod, potentialClass, nameToDependencies); - if(potentialClassSimilarity > curSimilarity) { + if (potentialClassSimilarity > curSimilarity) { potentialClasses.put(potentialClass, potentialClassSimilarity); } } @@ -113,18 +110,18 @@ private double methodSimilarity (@NotNull Dependencies depFst,@NotNull Dependenc } private class Dependencies { - // here are lists that need to be made: //method calls private Set methodCalls; //field accesses - - private Set instances; + private Set fieldAccesses; //object instantiations + private Set objectInstantiations; //idk + //local declarations - private Set localVariables; //idk + private Set localDeclarations; //idk //return types private String returnType; @@ -139,7 +136,7 @@ private class Dependencies { private Dependencies(@NotNull MethodEntity methodForDependencies) { methodCalls = methodForDependencies.getRelevantProperties().getMethods(); - instances = methodForDependencies.getRelevantProperties().getFields(); + fieldAccesses = methodForDependencies.getRelevantProperties().getFields(); returnType = methodForDependencies.getPsiMethod().getReturnType().toString(); exceptions = new HashSet<>(); @@ -154,23 +151,39 @@ private Dependencies(@NotNull MethodEntity methodForDependencies) { annotations.add(psiAnnotation.getQualifiedName()); //may be not that name todo: check } - localVariables = new HashSet<>(); + localDeclarations = new HashSet<>(); + objectInstantiations = new HashSet<>(); methodForDependencies.getPsiMethod().accept(new JavaRecursiveElementVisitor() { @Override public void visitLocalVariable(PsiLocalVariable variable) { super.visitLocalVariable(variable); - localVariables.add(variable.getType().getCanonicalText()); //idk + localDeclarations.add(variable.getType().getCanonicalText()); //idk //System.out.println("Found a variable at offset " + variable.getTextRange().getStartOffset()); } + @Override + public void visitNewExpression(PsiNewExpression expression) { + super.visitNewExpression(expression); + objectInstantiations.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); + } }); + +// newStatements = new HashSet<>(); +// methodForDependencies.getPsiMethod().accept(new JavaRecursiveElementVisitor() { +// @Override +// public void visitNewExpression(PsiNewExpression expression) { +// super.visitNewExpression(expression); +// newStatements.add(expression.getText()); +// } +// }); } - private int cardinality() { + private int cardinality() { //idk may be I just need to do one whole set return methodCalls.size() - + instances.size() - + localVariables.size() + + fieldAccesses.size() + + objectInstantiations.size() + + localDeclarations.size() + 1 //returnType //fixme: may be null if I learn to ignore + exceptions.size() + annotations.size(); @@ -183,8 +196,8 @@ private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { methodCallIntersection.retainAll(depSnd.methodCalls); intersectionCardinality += methodCallIntersection.size(); - Set instancesIntersection = new HashSet<>(instances); - instancesIntersection.retainAll(depSnd.instances); + Set instancesIntersection = new HashSet<>(fieldAccesses); + instancesIntersection.retainAll(depSnd.fieldAccesses); intersectionCardinality += instancesIntersection.size(); if(returnType.equals(depSnd.returnType)) @@ -198,8 +211,8 @@ private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { annotationsIntersection.retainAll(depSnd.exceptions); intersectionCardinality += annotationsIntersection.size(); - Set localVariablesIntersection = new HashSet<>(localVariables); - localVariablesIntersection.retainAll(depSnd.localVariables); + Set localVariablesIntersection = new HashSet<>(localDeclarations); + localVariablesIntersection.retainAll(depSnd.localDeclarations); intersectionCardinality += localVariablesIntersection.size(); return intersectionCardinality; From f65c786434414970600a74ef230e71a010f902ef Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Sun, 13 May 2018 12:35:47 +0300 Subject: [PATCH 08/14] now ignoring primitive types and types and annotations from java.lang and java.util --- .../org/ml_methods_group/algorithm/JMove.java | 62 ++++++++++++------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index 11dddc6a..d7f2eba0 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -118,10 +118,10 @@ private class Dependencies { private Set fieldAccesses; //object instantiations - private Set objectInstantiations; //idk + private Set objectInstantiations; //local declarations - private Set localDeclarations; //idk + private Set localDeclarations; //return types private String returnType; @@ -132,23 +132,34 @@ private class Dependencies { //annotations private Set annotations; - //todo ignore primitive types and types and annotations from java.lang and java.util + //ignoring primitive types and types and annotations from java.lang and java.util - private Dependencies(@NotNull MethodEntity methodForDependencies) { - methodCalls = methodForDependencies.getRelevantProperties().getMethods(); - fieldAccesses = methodForDependencies.getRelevantProperties().getFields(); - returnType = methodForDependencies.getPsiMethod().getReturnType().toString(); + protected Dependencies(@NotNull MethodEntity methodForDependencies) { + methodCalls = methodForDependencies.getRelevantProperties().getMethods(); // todo delete the method itself, add classes not methods + fieldAccesses = methodForDependencies.getRelevantProperties().getFields(); //todo add classes not fields - exceptions = new HashSet<>(); + if(methodForDependencies.getPsiMethod().getReturnType() instanceof PsiPrimitiveType + || methodForDependencies.getPsiMethod().getReturnType().getCanonicalText().startsWith("java.lang.") + ||methodForDependencies.getPsiMethod().getReturnType().getCanonicalText().startsWith("java.util.") ) + returnType = null; + else + returnType = methodForDependencies.getPsiMethod().getReturnType().getCanonicalText(); + + + exceptions = new HashSet<>(); //todo check for internal exception handling may be PsiClassType[] referencedTypes = methodForDependencies.getPsiMethod().getThrowsList().getReferencedTypes(); for(PsiClassType classType : referencedTypes) { - exceptions.add(classType.getClassName()); //may be not that name todo: check + if(!(classType.getCanonicalText().startsWith("java.lang.") + || classType.getCanonicalText().startsWith("java.util."))) + exceptions.add(classType.getCanonicalText()); } annotations = new HashSet<>(); PsiAnnotation[] psiAnnotations = methodForDependencies.getPsiMethod().getModifierList().getAnnotations(); for(PsiAnnotation psiAnnotation : psiAnnotations) { - annotations.add(psiAnnotation.getQualifiedName()); //may be not that name todo: check + if(!psiAnnotation.getQualifiedName().startsWith("java.lang.") || + psiAnnotation.getQualifiedName().startsWith("java.util.")) + annotations.add(psiAnnotation.getQualifiedName()); } localDeclarations = new HashSet<>(); @@ -157,34 +168,37 @@ private Dependencies(@NotNull MethodEntity methodForDependencies) { @Override public void visitLocalVariable(PsiLocalVariable variable) { super.visitLocalVariable(variable); - localDeclarations.add(variable.getType().getCanonicalText()); //idk - //System.out.println("Found a variable at offset " + variable.getTextRange().getStartOffset()); + if(!(variable.getType() instanceof PsiPrimitiveType || + variable.getType().getCanonicalText().startsWith("java.lang.") || + variable.getType().getCanonicalText().startsWith("java.util."))) + localDeclarations.add(variable.getType().getCanonicalText()); //idk } @Override public void visitNewExpression(PsiNewExpression expression) { super.visitNewExpression(expression); - objectInstantiations.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); + if(!(expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.lang.") || + expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.util."))) + objectInstantiations.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); } - }); - -// newStatements = new HashSet<>(); -// methodForDependencies.getPsiMethod().accept(new JavaRecursiveElementVisitor() { -// @Override -// public void visitNewExpression(PsiNewExpression expression) { -// super.visitNewExpression(expression); -// newStatements.add(expression.getText()); +// @Override idk this way I will find all annotations may be I shouldn't +// public void visitAnnotation(PsiAnnotation annotation) { +// super.visitAnnotation(annotation); +// if(!annotation.getQualifiedName().startsWith("java.lang.") || +// annotation.getQualifiedName().startsWith("java.util.")) +// annotations.add(annotation.getQualifiedName()); // } -// }); - } + }); + } + private int cardinality() { //idk may be I just need to do one whole set return methodCalls.size() + fieldAccesses.size() + objectInstantiations.size() + localDeclarations.size() - + 1 //returnType //fixme: may be null if I learn to ignore + + 1 //returnType //fixme may be null if I learn to ignore + exceptions.size() + annotations.size(); } From 038d4e61dcaad4195c6171a4022141a5f266bbf9 Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Mon, 14 May 2018 22:33:26 +0300 Subject: [PATCH 09/14] added getter/setter check --- .../org/ml_methods_group/algorithm/JMove.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index d7f2eba0..fa1d8931 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -60,12 +60,15 @@ protected List calculateRefactorings(ExecutionContext context, bool for(MethodEntity curMethod : allMethods) { if(!curMethod.isMovable()) continue; - LOGGER.info("Checking " + curMethod.getName()); + LOGGER.info("Checking " + curMethod.getName()); // todo some logging Dependencies curDependencies = nameToDependencies.get(curMethod.getName()); - if(curDependencies.cardinality() < MIN_NUMBER_OF_DEPENDENCIES) - continue; ClassEntity curClass = nameToClassEntity.get(curMethod.getClassName()); + if(curDependencies.cardinality() < MIN_NUMBER_OF_DEPENDENCIES //experimental + || curClass.getRelevantProperties().getMethods().size() == 1 //because we calculate similarity between this method and all remaining in curClass + || isGetter(curMethod) //this methods are rarely implemented in the wrong classes + || isSetter(curMethod)) //todo: check if we need this check here and not in other place + continue; double curSimilarity = calculateSimilarity(curMethod, curClass, nameToDependencies); Map potentialClasses = new HashMap<>(); for(ClassEntity potentialClass : allClasses) { @@ -248,4 +251,20 @@ private ClassEntity findBestClass(@NotNull Map potentialCla } return bestClass; } + + private boolean isGetter (@NotNull MethodEntity methodEntity) { + PsiMethod psiMethod = methodEntity.getPsiMethod(); + int numSt = psiMethod.getBody().getStatements().length; + if(psiMethod.getParameterList().getParametersCount() == 0 && numSt == 1) + return true; + return false; + } + + private boolean isSetter(@NotNull MethodEntity methodEntity) { + PsiMethod psiMethod = methodEntity.getPsiMethod(); + int numSt = psiMethod.getBody().getStatements().length; + if(psiMethod.getParameterList().getParametersCount() == 1 && numSt == 1) + return true; + return false; + } } From 479cb9c6773dd8d811d80d19083e563b42b0077a Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Mon, 14 May 2018 22:36:00 +0300 Subject: [PATCH 10/14] made one set of dependencies instead of several + added formal parameters finding --- .../org/ml_methods_group/algorithm/JMove.java | 147 ++++++++++-------- 1 file changed, 80 insertions(+), 67 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index fa1d8931..cf6bb516 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -53,7 +53,7 @@ protected List calculateRefactorings(ExecutionContext context, bool } for(MethodEntity methodEntity : allMethods) { - nameToDependencies.put(methodEntity.getName(), new Dependencies(methodEntity)); + nameToDependencies.put(methodEntity.getName(), new Dependencies(methodEntity, nameToClassEntity)); } @@ -87,6 +87,7 @@ protected List calculateRefactorings(ExecutionContext context, bool if(diff >= MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS) refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), diff, false)); //accuracy ~ difference between coeff ?? idk } + //todo may be i should check weither it is possible to make this refactoring oops } return refactorings; } @@ -115,75 +116,91 @@ private double methodSimilarity (@NotNull Dependencies depFst,@NotNull Dependenc private class Dependencies { //method calls - private Set methodCalls; - //field accesses - private Set fieldAccesses; - //object instantiations - private Set objectInstantiations; - //local declarations - private Set localDeclarations; - - //return types - private String returnType; - + // formal parameters + //return type //exceptions - private Set exceptions; - //annotations - private Set annotations; + + private Set all; //ignoring primitive types and types and annotations from java.lang and java.util - protected Dependencies(@NotNull MethodEntity methodForDependencies) { - methodCalls = methodForDependencies.getRelevantProperties().getMethods(); // todo delete the method itself, add classes not methods - fieldAccesses = methodForDependencies.getRelevantProperties().getFields(); //todo add classes not fields + protected Dependencies(@NotNull MethodEntity methodForDependencies, @NotNull Map nameToClassEntity ) { + all = new HashSet<>(); //todo use + + //classes of methods that are called inside this method + for(String methodName : methodForDependencies.getRelevantProperties().getMethods()) { + if(!methodName.equals(methodForDependencies.getName())) //because it's somehow always there + all.add(methodName.substring(0, methodName.lastIndexOf('.'))); + } + + // classes of fields that the method accesses + for(String fieldName : methodForDependencies.getRelevantProperties().getFields()) { + PsiType pt = nameToClassEntity.get(fieldName.substring(0, fieldName.lastIndexOf('.'))).getPsiClass().findFieldByName(fieldName.substring(fieldName.lastIndexOf('.') + 1), false).getType(); + if(!(pt instanceof PsiPrimitiveType) + || pt.getCanonicalText().startsWith("java.lang.") + || pt.getCanonicalText().startsWith("java.util.")) + all.add(pt.getCanonicalText()); + } - if(methodForDependencies.getPsiMethod().getReturnType() instanceof PsiPrimitiveType + //return type + if(methodForDependencies.getPsiMethod().getReturnType() instanceof PsiPrimitiveType //return type || methodForDependencies.getPsiMethod().getReturnType().getCanonicalText().startsWith("java.lang.") ||methodForDependencies.getPsiMethod().getReturnType().getCanonicalText().startsWith("java.util.") ) - returnType = null; - else - returnType = methodForDependencies.getPsiMethod().getReturnType().getCanonicalText(); + all.add(methodForDependencies.getPsiMethod().getReturnType().getCanonicalText()); //idk what is going on here fixme - exceptions = new HashSet<>(); //todo check for internal exception handling may be + //exceptions + //idk check for internal exception handling may be PsiClassType[] referencedTypes = methodForDependencies.getPsiMethod().getThrowsList().getReferencedTypes(); for(PsiClassType classType : referencedTypes) { if(!(classType.getCanonicalText().startsWith("java.lang.") || classType.getCanonicalText().startsWith("java.util."))) - exceptions.add(classType.getCanonicalText()); + all.add(classType.getCanonicalText()); + } - annotations = new HashSet<>(); + //annotations PsiAnnotation[] psiAnnotations = methodForDependencies.getPsiMethod().getModifierList().getAnnotations(); for(PsiAnnotation psiAnnotation : psiAnnotations) { if(!psiAnnotation.getQualifiedName().startsWith("java.lang.") || psiAnnotation.getQualifiedName().startsWith("java.util.")) - annotations.add(psiAnnotation.getQualifiedName()); + all.add(psiAnnotation.getQualifiedName()); } - localDeclarations = new HashSet<>(); - objectInstantiations = new HashSet<>(); + //formal parameters + for(PsiParameter parameter: methodForDependencies.getPsiMethod().getParameterList().getParameters()) { + if(!((parameter.getType() instanceof PsiPrimitiveType) + || parameter.getType().getCanonicalText().startsWith("java.lang") + || parameter.getType().getCanonicalText().startsWith("java.util"))) + all.add(parameter.getType().getCanonicalText()); + + } + + methodForDependencies.getPsiMethod().accept(new JavaRecursiveElementVisitor() { @Override public void visitLocalVariable(PsiLocalVariable variable) { super.visitLocalVariable(variable); if(!(variable.getType() instanceof PsiPrimitiveType || variable.getType().getCanonicalText().startsWith("java.lang.") || - variable.getType().getCanonicalText().startsWith("java.util."))) - localDeclarations.add(variable.getType().getCanonicalText()); //idk + variable.getType().getCanonicalText().startsWith("java.util."))) { + all.add(variable.getType().getCanonicalText()); + } } @Override public void visitNewExpression(PsiNewExpression expression) { super.visitNewExpression(expression); - if(!(expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.lang.") || - expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.util."))) - objectInstantiations.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); + if (!(expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.lang.") || + expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.util."))) { + all.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); + } } -// @Override idk this way I will find all annotations may be I shouldn't + + // @Override idk this way I will find all annotations may be I shouldn't // public void visitAnnotation(PsiAnnotation annotation) { // super.visitAnnotation(annotation); // if(!annotation.getQualifiedName().startsWith("java.lang.") || @@ -195,44 +212,40 @@ public void visitNewExpression(PsiNewExpression expression) { }); } - private int cardinality() { //idk may be I just need to do one whole set + private int cardinality() { - return methodCalls.size() - + fieldAccesses.size() - + objectInstantiations.size() - + localDeclarations.size() - + 1 //returnType //fixme may be null if I learn to ignore - + exceptions.size() - + annotations.size(); + return all.size(); } private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { - int intersectionCardinality = 0; - - Set methodCallIntersection = new HashSet<>(methodCalls); - methodCallIntersection.retainAll(depSnd.methodCalls); - intersectionCardinality += methodCallIntersection.size(); - - Set instancesIntersection = new HashSet<>(fieldAccesses); - instancesIntersection.retainAll(depSnd.fieldAccesses); - intersectionCardinality += instancesIntersection.size(); - - if(returnType.equals(depSnd.returnType)) - intersectionCardinality++; - - Set exceptionsIntersection = new HashSet<>(exceptions); - exceptionsIntersection.retainAll(depSnd.exceptions); - intersectionCardinality += exceptionsIntersection.size(); - - Set annotationsIntersection = new HashSet<>(exceptions); - annotationsIntersection.retainAll(depSnd.exceptions); - intersectionCardinality += annotationsIntersection.size(); - - Set localVariablesIntersection = new HashSet<>(localDeclarations); - localVariablesIntersection.retainAll(depSnd.localDeclarations); - intersectionCardinality += localVariablesIntersection.size(); - return intersectionCardinality; + Set intersection = new HashSet<>(all); + intersection.retainAll(depSnd.all); + +// Set methodCallIntersection = new HashSet<>(methodCalls); +// methodCallIntersection.retainAll(depSnd.methodCalls); +// intersectionCardinality += methodCallIntersection.size(); +// +// Set instancesIntersection = new HashSet<>(fieldAccesses); +// instancesIntersection.retainAll(depSnd.fieldAccesses); +// intersectionCardinality += instancesIntersection.size(); +// +// if(returnType.equals(depSnd.returnType)) +// intersectionCardinality++; +// +// Set exceptionsIntersection = new HashSet<>(exceptions); +// exceptionsIntersection.retainAll(depSnd.exceptions); +// intersectionCardinality += exceptionsIntersection.size(); +// +// Set annotationsIntersection = new HashSet<>(exceptions); +// annotationsIntersection.retainAll(depSnd.exceptions); +// intersectionCardinality += annotationsIntersection.size(); +// +// Set localVariablesIntersection = new HashSet<>(localDeclarations); +// localVariablesIntersection.retainAll(depSnd.localDeclarations); +// intersectionCardinality += localVariablesIntersection.size(); + + return intersection.size(); } } From ee9f333139fc62779629288625e71955c0581a96 Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Mon, 14 May 2018 23:17:07 +0300 Subject: [PATCH 11/14] made function checking for util or lang packaging --- .../org/ml_methods_group/algorithm/JMove.java | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index cf6bb516..43c57413 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -139,45 +139,36 @@ protected Dependencies(@NotNull MethodEntity methodForDependencies, @NotNull Map // classes of fields that the method accesses for(String fieldName : methodForDependencies.getRelevantProperties().getFields()) { - PsiType pt = nameToClassEntity.get(fieldName.substring(0, fieldName.lastIndexOf('.'))).getPsiClass().findFieldByName(fieldName.substring(fieldName.lastIndexOf('.') + 1), false).getType(); - if(!(pt instanceof PsiPrimitiveType) - || pt.getCanonicalText().startsWith("java.lang.") - || pt.getCanonicalText().startsWith("java.util.")) - all.add(pt.getCanonicalText()); + PsiType psiType = nameToClassEntity.get(fieldName.substring(0, fieldName.lastIndexOf('.'))).getPsiClass().findFieldByName(fieldName.substring(fieldName.lastIndexOf('.') + 1), false).getType(); + if(!(psiType instanceof PsiPrimitiveType) || fromUtilOrLang(psiType.getCanonicalText())) + all.add(psiType.getCanonicalText()); } //return type - if(methodForDependencies.getPsiMethod().getReturnType() instanceof PsiPrimitiveType //return type - || methodForDependencies.getPsiMethod().getReturnType().getCanonicalText().startsWith("java.lang.") - ||methodForDependencies.getPsiMethod().getReturnType().getCanonicalText().startsWith("java.util.") ) - all.add(methodForDependencies.getPsiMethod().getReturnType().getCanonicalText()); //idk what is going on here fixme + PsiType returnType = methodForDependencies.getPsiMethod().getReturnType(); + if(!(returnType instanceof PsiPrimitiveType ||fromUtilOrLang(returnType.getCanonicalText()))) + all.add(returnType.getCanonicalText()); //exceptions //idk check for internal exception handling may be PsiClassType[] referencedTypes = methodForDependencies.getPsiMethod().getThrowsList().getReferencedTypes(); for(PsiClassType classType : referencedTypes) { - if(!(classType.getCanonicalText().startsWith("java.lang.") - || classType.getCanonicalText().startsWith("java.util."))) + if(!(fromUtilOrLang(classType.getCanonicalText()))) all.add(classType.getCanonicalText()); - } //annotations PsiAnnotation[] psiAnnotations = methodForDependencies.getPsiMethod().getModifierList().getAnnotations(); for(PsiAnnotation psiAnnotation : psiAnnotations) { - if(!psiAnnotation.getQualifiedName().startsWith("java.lang.") || - psiAnnotation.getQualifiedName().startsWith("java.util.")) + if(!fromUtilOrLang(psiAnnotation.getQualifiedName())) all.add(psiAnnotation.getQualifiedName()); } //formal parameters for(PsiParameter parameter: methodForDependencies.getPsiMethod().getParameterList().getParameters()) { - if(!((parameter.getType() instanceof PsiPrimitiveType) - || parameter.getType().getCanonicalText().startsWith("java.lang") - || parameter.getType().getCanonicalText().startsWith("java.util"))) + if(!((parameter.getType() instanceof PsiPrimitiveType) || fromUtilOrLang(parameter.getType().getCanonicalText()))) all.add(parameter.getType().getCanonicalText()); - } @@ -185,19 +176,14 @@ protected Dependencies(@NotNull MethodEntity methodForDependencies, @NotNull Map @Override public void visitLocalVariable(PsiLocalVariable variable) { super.visitLocalVariable(variable); - if(!(variable.getType() instanceof PsiPrimitiveType || - variable.getType().getCanonicalText().startsWith("java.lang.") || - variable.getType().getCanonicalText().startsWith("java.util."))) { + if(!(variable.getType() instanceof PsiPrimitiveType || fromUtilOrLang(variable.getType().getCanonicalText()))) all.add(variable.getType().getCanonicalText()); - } } @Override public void visitNewExpression(PsiNewExpression expression) { super.visitNewExpression(expression); - if (!(expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.lang.") || - expression.getClassOrAnonymousClassReference().getQualifiedName().startsWith("java.util."))) { + if (!(fromUtilOrLang(expression.getClassOrAnonymousClassReference().getQualifiedName()))) all.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); - } } // @Override idk this way I will find all annotations may be I shouldn't @@ -212,12 +198,12 @@ public void visitNewExpression(PsiNewExpression expression) { }); } - private int cardinality() { + public int cardinality() { return all.size(); } - private int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { + public int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { Set intersection = new HashSet<>(all); intersection.retainAll(depSnd.all); @@ -280,4 +266,11 @@ private boolean isSetter(@NotNull MethodEntity methodEntity) { return true; return false; } + + private boolean fromUtilOrLang (String fullName) { + if(fullName.startsWith("java.lang.") || fullName.startsWith("java.util.")) + return true; + else + return false; + } } From 9232fc05e4e64636dd2db76516ad80e396f7a00d Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Mon, 21 May 2018 23:21:41 +0300 Subject: [PATCH 12/14] added logging and error handeling --- .../org/ml_methods_group/algorithm/JMove.java | 105 ++++++++++++------ 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index 43c57413..96231434 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -29,7 +29,7 @@ public class JMove extends Algorithm { private static final Logger LOGGER = Logging.getLogger(JMove.class); - private final int MIN_NUMBER_OF_CANDIDATE_CLASSES = 3; + private final int MIN_NUMBER_OF_CANDIDATE_CLASSES = 3; //experimentally found thresholds private final int MIN_NUMBER_OF_DEPENDENCIES = 4; private final double MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS = 0.25; @@ -39,28 +39,33 @@ public JMove() { @Override protected List calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) { - LOGGER.info("Starting calculating refactorings"); List allMethods = context.getEntities().getMethods(); List allClasses = context.getEntities().getClasses(); - LOGGER.info("Found " + allMethods.size() + " methods and " + allClasses.size() + " classes"); List refactorings = new ArrayList<>(); Map nameToClassEntity = new HashMap<>(); Map nameToDependencies = new HashMap<>(); + int numberOfMethodInClasses + for(ClassEntity classEntity : allClasses) { nameToClassEntity.put(classEntity.getName(), classEntity); } + LOGGER.info("Calculating Dependencies for Method Entities..."); for(MethodEntity methodEntity : allMethods) { + if(methodEntity == null) { + LOGGER.warn("There is a null Method Entity"); + continue; + } nameToDependencies.put(methodEntity.getName(), new Dependencies(methodEntity, nameToClassEntity)); } - + LOGGER.info("Finding refactorings..."); for(MethodEntity curMethod : allMethods) { - if(!curMethod.isMovable()) + if(curMethod == null || !curMethod.isMovable()) continue; - LOGGER.info("Checking " + curMethod.getName()); // todo some logging + LOGGER.info("Checking " + curMethod.getName()); Dependencies curDependencies = nameToDependencies.get(curMethod.getName()); ClassEntity curClass = nameToClassEntity.get(curMethod.getClassName()); @@ -98,7 +103,14 @@ private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull for(String curMethodName : methodNames) { if(!methodEntity.getName().equals(curMethodName)) - similarity += methodSimilarity(nameToDependencies.get(methodEntity.getName()), nameToDependencies.get(curMethodName)); + try { + similarity += methodSimilarity(nameToDependencies.get(methodEntity.getName()), nameToDependencies.get(curMethodName)); + } + catch(IllegalArgumentException e) { + LOGGER.warn(e.getMessage() + + "\n Error happened when trying to calculate similarity between " + + methodEntity.getName() + " and " + curMethodName); + } } if(methodEntity.getClassName().equals(classEntity.getName())) return similarity / methodNames.size(); @@ -129,7 +141,7 @@ private class Dependencies { //ignoring primitive types and types and annotations from java.lang and java.util protected Dependencies(@NotNull MethodEntity methodForDependencies, @NotNull Map nameToClassEntity ) { - all = new HashSet<>(); //todo use + all = new HashSet<>(); //classes of methods that are called inside this method for(String methodName : methodForDependencies.getRelevantProperties().getMethods()) { @@ -139,15 +151,26 @@ protected Dependencies(@NotNull MethodEntity methodForDependencies, @NotNull Map // classes of fields that the method accesses for(String fieldName : methodForDependencies.getRelevantProperties().getFields()) { - PsiType psiType = nameToClassEntity.get(fieldName.substring(0, fieldName.lastIndexOf('.'))).getPsiClass().findFieldByName(fieldName.substring(fieldName.lastIndexOf('.') + 1), false).getType(); - if(!(psiType instanceof PsiPrimitiveType) || fromUtilOrLang(psiType.getCanonicalText())) - all.add(psiType.getCanonicalText()); + try { + PsiType psiType = nameToClassEntity.get(fieldName.substring(0, fieldName.lastIndexOf('.'))).getPsiClass().findFieldByName(fieldName.substring(fieldName.lastIndexOf('.') + 1), false).getType(); + if (!(psiType instanceof PsiPrimitiveType) || fromUtilOrLang(psiType.getCanonicalText())) + all.add(psiType.getCanonicalText()); + } + catch(NullPointerException e) { + LOGGER.warn("Trouble with field " + fieldName + " in method " + methodForDependencies.getName()); + } } //return type PsiType returnType = methodForDependencies.getPsiMethod().getReturnType(); - if(!(returnType instanceof PsiPrimitiveType ||fromUtilOrLang(returnType.getCanonicalText()))) - all.add(returnType.getCanonicalText()); + try { + if(!(returnType instanceof PsiPrimitiveType ||fromUtilOrLang(returnType.getCanonicalText()))) + all.add(returnType.getCanonicalText()); + } + catch (NullPointerException e) { + LOGGER.warn("Cannot get name of return type of method " + methodForDependencies.getName()); + } + //exceptions @@ -161,8 +184,13 @@ protected Dependencies(@NotNull MethodEntity methodForDependencies, @NotNull Map //annotations PsiAnnotation[] psiAnnotations = methodForDependencies.getPsiMethod().getModifierList().getAnnotations(); for(PsiAnnotation psiAnnotation : psiAnnotations) { - if(!fromUtilOrLang(psiAnnotation.getQualifiedName())) - all.add(psiAnnotation.getQualifiedName()); + try { + if (!fromUtilOrLang(psiAnnotation.getQualifiedName())) + all.add(psiAnnotation.getQualifiedName()); + } + catch(NullPointerException e) { + LOGGER.warn("Cannot get annotation name for method " + methodForDependencies.getName()); + } } //formal parameters @@ -176,14 +204,24 @@ protected Dependencies(@NotNull MethodEntity methodForDependencies, @NotNull Map @Override public void visitLocalVariable(PsiLocalVariable variable) { super.visitLocalVariable(variable); - if(!(variable.getType() instanceof PsiPrimitiveType || fromUtilOrLang(variable.getType().getCanonicalText()))) - all.add(variable.getType().getCanonicalText()); + try { + if (!(variable.getType() instanceof PsiPrimitiveType || fromUtilOrLang(variable.getType().getCanonicalText()))) + all.add(variable.getType().getCanonicalText()); + } + catch(NullPointerException e) { + LOGGER.warn("Cannot get name for local variable in method " + methodForDependencies.getName()); + } } @Override public void visitNewExpression(PsiNewExpression expression) { super.visitNewExpression(expression); - if (!(fromUtilOrLang(expression.getClassOrAnonymousClassReference().getQualifiedName()))) - all.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); + try { + if (!(fromUtilOrLang(expression.getClassOrAnonymousClassReference().getQualifiedName()))) + all.add(expression.getClassOrAnonymousClassReference().getQualifiedName()); + } + catch(NullPointerException e) { + LOGGER.warn("Cannot get name for class in new expression for method " + methodForDependencies.getName()); + } } // @Override idk this way I will find all annotations may be I shouldn't @@ -253,24 +291,29 @@ private ClassEntity findBestClass(@NotNull Map potentialCla private boolean isGetter (@NotNull MethodEntity methodEntity) { PsiMethod psiMethod = methodEntity.getPsiMethod(); - int numSt = psiMethod.getBody().getStatements().length; - if(psiMethod.getParameterList().getParametersCount() == 0 && numSt == 1) - return true; - return false; + try { + int numSt = psiMethod.getBody().getStatements().length; + return psiMethod.getParameterList().getParametersCount() == 0 && numSt == 1; + } + catch(NullPointerException e) { + LOGGER.warn("Cannot find out if method " + methodEntity.getName() + " is getter method. Assuming that it is not."); + return false; + } } private boolean isSetter(@NotNull MethodEntity methodEntity) { PsiMethod psiMethod = methodEntity.getPsiMethod(); - int numSt = psiMethod.getBody().getStatements().length; - if(psiMethod.getParameterList().getParametersCount() == 1 && numSt == 1) - return true; - return false; + try { + int numSt = psiMethod.getBody().getStatements().length; + return psiMethod.getParameterList().getParametersCount() == 1 && numSt == 1; + } + catch (NullPointerException e) { + LOGGER.warn("Cannot find out if method " + methodEntity.getName() + " is setter method. Assuming that it is not."); + return false; + } } private boolean fromUtilOrLang (String fullName) { - if(fullName.startsWith("java.lang.") || fullName.startsWith("java.util.")) - return true; - else - return false; + return fullName.startsWith("java.lang.") || fullName.startsWith("java.util."); } } From 5a9d6228a0eacecffb9ca1d82f9b111e6843760a Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Tue, 22 May 2018 03:44:53 +0300 Subject: [PATCH 13/14] improved finding of best class (plus there are a lot of commented lines for debuging) --- .../org/ml_methods_group/algorithm/JMove.java | 84 ++++++++----------- 1 file changed, 34 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index 96231434..d1f37e19 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -20,7 +20,6 @@ import com.intellij.psi.*; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.ml_methods_group.algorithm.entity.ClassEntity; import org.ml_methods_group.algorithm.entity.MethodEntity; import org.ml_methods_group.config.Logging; @@ -33,8 +32,11 @@ public class JMove extends Algorithm { private final int MIN_NUMBER_OF_DEPENDENCIES = 4; private final double MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS = 0.25; +// Set methodNamesWithNoDependencies; + public JMove() { super("JMove", false); +// methodNamesWithNoDependencies = new HashSet<>(); } @Override @@ -46,13 +48,15 @@ protected List calculateRefactorings(ExecutionContext context, bool Map nameToClassEntity = new HashMap<>(); Map nameToDependencies = new HashMap<>(); - int numberOfMethodInClasses +// int numberOfMethodInClasses = 0; // for debug for(ClassEntity classEntity : allClasses) { nameToClassEntity.put(classEntity.getName(), classEntity); +// numberOfMethodInClasses += classEntity.getRelevantProperties().getMethods().size(); } - +// LOGGER.info("The size of allMethods list: " + allMethods.size() + "\n The number of methods from allClasses: " + numberOfMethodInClasses ); LOGGER.info("Calculating Dependencies for Method Entities..."); + for(MethodEntity methodEntity : allMethods) { if(methodEntity == null) { LOGGER.warn("There is a null Method Entity"); @@ -69,31 +73,43 @@ protected List calculateRefactorings(ExecutionContext context, bool Dependencies curDependencies = nameToDependencies.get(curMethod.getName()); ClassEntity curClass = nameToClassEntity.get(curMethod.getClassName()); - if(curDependencies.cardinality() < MIN_NUMBER_OF_DEPENDENCIES //experimental + if(curDependencies.cardinality() < MIN_NUMBER_OF_DEPENDENCIES || curClass.getRelevantProperties().getMethods().size() == 1 //because we calculate similarity between this method and all remaining in curClass || isGetter(curMethod) //this methods are rarely implemented in the wrong classes || isSetter(curMethod)) //todo: check if we need this check here and not in other place continue; double curSimilarity = calculateSimilarity(curMethod, curClass, nameToDependencies); - Map potentialClasses = new HashMap<>(); + ClassEntity bestClass = null; + double bestClassSimilarity = curSimilarity; + int numberOfPotentialClasses = 0; for(ClassEntity potentialClass : allClasses) { double potentialClassSimilarity = calculateSimilarity(curMethod, potentialClass, nameToDependencies); if (potentialClassSimilarity > curSimilarity) { - potentialClasses.put(potentialClass, potentialClassSimilarity); + numberOfPotentialClasses++; + if(potentialClassSimilarity > bestClassSimilarity) { + bestClassSimilarity = potentialClassSimilarity; + bestClass = potentialClass; + } } } - if(potentialClasses.size() < MIN_NUMBER_OF_CANDIDATE_CLASSES) + if(numberOfPotentialClasses < MIN_NUMBER_OF_CANDIDATE_CLASSES) continue; - ClassEntity bestClass = findBestClass(potentialClasses); if(bestClass != null) { - double diff = (potentialClasses.get(bestClass) - curSimilarity)/potentialClasses.get(bestClass); //may be idk + double diff = (bestClassSimilarity - curSimilarity)/bestClassSimilarity; //may be idk if(diff >= MIN_DIFF_BETWEEN_SIMILARITY_COEFF_PERS) - refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), diff, false)); //accuracy ~ difference between coeff ?? idk + refactorings.add(new Refactoring(curMethod.getName(), bestClass.getName(), bestClassSimilarity, false)); //accuracy idk } //todo may be i should check weither it is possible to make this refactoring oops } +// LOGGER.info("The size of allMethods list: " + allMethods.size() + "\n The number of methods from allClasses: " + numberOfMethodInClasses ); +// LOGGER.info("Dependencies not found for " + methodNamesWithNoDependencies.size() + " methods"); +// LOGGER.info("Here they are:"); +// for(String methodName : methodNamesWithNoDependencies) { +// LOGGER.info(methodName); +// } +// methodNamesWithNoDependencies.clear(); return refactorings; } @@ -102,11 +118,18 @@ private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull Set methodNames = classEntity.getRelevantProperties().getMethods(); for(String curMethodName : methodNames) { - if(!methodEntity.getName().equals(curMethodName)) + if(!methodEntity.getPsiMethod().getModifierList().hasExplicitModifier("abstract") + && !methodEntity.getName().equals(curMethodName) ) try { similarity += methodSimilarity(nameToDependencies.get(methodEntity.getName()), nameToDependencies.get(curMethodName)); } catch(IllegalArgumentException e) { +// if(nameToDependencies.get(methodEntity.getName()) == null) { +// methodNamesWithNoDependencies.add(methodEntity.getName()); +// } +// if(nameToDependencies.get(curMethodName) == null) { +// methodNamesWithNoDependencies.add(curMethodName); +// } LOGGER.warn(e.getMessage() + "\n Error happened when trying to calculate similarity between " + methodEntity.getName() + " and " + curMethodName); @@ -245,50 +268,11 @@ public int calculateIntersectionCardinality(@NotNull Dependencies depSnd) { Set intersection = new HashSet<>(all); intersection.retainAll(depSnd.all); - -// Set methodCallIntersection = new HashSet<>(methodCalls); -// methodCallIntersection.retainAll(depSnd.methodCalls); -// intersectionCardinality += methodCallIntersection.size(); -// -// Set instancesIntersection = new HashSet<>(fieldAccesses); -// instancesIntersection.retainAll(depSnd.fieldAccesses); -// intersectionCardinality += instancesIntersection.size(); -// -// if(returnType.equals(depSnd.returnType)) -// intersectionCardinality++; -// -// Set exceptionsIntersection = new HashSet<>(exceptions); -// exceptionsIntersection.retainAll(depSnd.exceptions); -// intersectionCardinality += exceptionsIntersection.size(); -// -// Set annotationsIntersection = new HashSet<>(exceptions); -// annotationsIntersection.retainAll(depSnd.exceptions); -// intersectionCardinality += annotationsIntersection.size(); -// -// Set localVariablesIntersection = new HashSet<>(localDeclarations); -// localVariablesIntersection.retainAll(depSnd.localDeclarations); -// intersectionCardinality += localVariablesIntersection.size(); - return intersection.size(); } } - @Nullable - private ClassEntity findBestClass(@NotNull Map potentialClasses) { //choose movable class with the biggest coefficient - if(potentialClasses.size() < MIN_NUMBER_OF_CANDIDATE_CLASSES) - return null; - ClassEntity bestClass = null; - Double bestCoefficient = 0.0; - for(Map.Entry entry : potentialClasses.entrySet()) { - if(entry.getValue() > bestCoefficient) { - bestCoefficient = entry.getValue(); - bestClass = entry.getKey(); - } - } - return bestClass; - } - private boolean isGetter (@NotNull MethodEntity methodEntity) { PsiMethod psiMethod = methodEntity.getPsiMethod(); try { From 5df403b7e212dc4d46d9e01bb70df1bf06a66014 Mon Sep 17 00:00:00 2001 From: Elizaveta Mironovich Date: Tue, 22 May 2018 06:09:16 +0300 Subject: [PATCH 14/14] extremly important negation --- src/main/java/org/ml_methods_group/algorithm/JMove.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/JMove.java b/src/main/java/org/ml_methods_group/algorithm/JMove.java index d1f37e19..84766569 100644 --- a/src/main/java/org/ml_methods_group/algorithm/JMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/JMove.java @@ -135,7 +135,7 @@ private double calculateSimilarity(@NotNull MethodEntity methodEntity, @NotNull + methodEntity.getName() + " and " + curMethodName); } } - if(methodEntity.getClassName().equals(classEntity.getName())) + if(!methodEntity.getClassName().equals(classEntity.getName())) return similarity / methodNames.size(); else return similarity / (methodNames.size() - 1);