From 0331488b9109e836f7e7d4db4fa7706d19ff81ad Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Sat, 24 Mar 2018 16:57:32 +0300 Subject: [PATCH 01/15] number of public methods in class metric implemented --- gradle/wrapper/gradle-wrapper.properties | 4 +- .../org/ml_methods_group/algorithm/QOMMR.java | 63 +++++++++++++++++++ .../NumPublicMethodsClassCalculator.java | 42 +++++++++++++ .../NumPublicMethodsClassMetric.java | 52 +++++++++++++++ 4 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/ml_methods_group/algorithm/QOMMR.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumPublicMethodsClassMetric.java diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0b6241a9..e5fd9b1c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jul 12 12:29:38 MSK 2017 +#Fri Mar 09 09:33:20 MSK 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/src/main/java/org/ml_methods_group/algorithm/QOMMR.java b/src/main/java/org/ml_methods_group/algorithm/QOMMR.java new file mode 100644 index 00000000..d1f6605c --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/QOMMR.java @@ -0,0 +1,63 @@ +/* + * 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.Entity; +import org.ml_methods_group.algorithm.entity.MethodEntity; + +import java.util.List; +import java.util.stream.Collectors; + +public class QOMMR extends Algorithm { + private ExecutionContext context; + private List methodEntities; + QOMMR() { + super("Quality-orientated Move Method Refactoring", false); + } + + @Override + protected List calculateRefactorings( + ExecutionContext context, boolean enableFieldRefactorings) throws Exception { + methodEntities.clear(); + methodEntities = context.getEntities().getMethods(). + stream().filter(Entity::isMovable).map(MethodEntity::copy).collect(Collectors.toList()); + for(MethodEntity methodEntity : methodEntities){ + //if + } + return null; + } + + //number of classes -- projectMetrics.NumClassesProjectMetrics.java + //PolymorphismFactorProjectMetric + //NumInterfacesImplementedMetrics ?? super class + //NumPrivateAttributes / NumAttributes + //Number of classes where this class is used. + /* + Computes the relatedness among methods of the class based upon the + parameter list of the methods. The metrics is computed using the + summation of the intersection of parameters of a method with the + maximum independent set of all parameter types in the class. + */ + //Number of declarations with user classes + // + /* + MethodInheritanceFactorProjectMetrics + */ + //number of polymorphic methods ?? + //number of public methods + //number of methods +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java new file mode 100644 index 00000000..cd809e3d --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2005-2017 Sixth and Red River Software, Bas Leijdekkers + * + * 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 com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +import java.util.Arrays; + +public class NumPublicMethodsClassCalculator extends ClassCalculator { + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (!isConcreteClass(aClass)) { + return; + } + postMetric(aClass, Arrays.stream( + aClass.getMethods()).filter( + x -> x.hasModifierProperty( + PsiModifier.PUBLIC)).count()); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumPublicMethodsClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumPublicMethodsClassMetric.java new file mode 100644 index 00000000..5ab3d614 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumPublicMethodsClassMetric.java @@ -0,0 +1,52 @@ +/* + * Copyright 2005-2016 Sixth and Red River Software, Bas Leijdekkers + * + * 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 com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.NumConstructorsCalculator; +import com.sixrr.stockmetrics.classCalculators.NumPublicMethodsClassCalculator; +import com.sixrr.stockmetrics.classMetrics.ClassMetric; +import com.sixrr.stockmetrics.i18n.StockMetricsBundle; +import org.jetbrains.annotations.NotNull; + +public class NumPublicMethodsClassMetric extends ClassMetric { + + @NotNull + @Override + public String getDisplayName() { + return "Number of public methods"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "CIS"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new NumPublicMethodsClassCalculator(); + } +} From c6b418cfe5e0e5478359c32c6320498d378729dd Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Sat, 24 Mar 2018 18:31:27 +0300 Subject: [PATCH 02/15] Data Access Class Metric implemented --- .../DataAccessClassCalculator.java | 52 +++++++++++++++++++ .../classMetrics/DataAccessClassMetric.java | 48 +++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DataAccessClassMetric.java diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java new file mode 100644 index 00000000..c9febdca --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java @@ -0,0 +1,52 @@ +/* + * 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 com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +import java.util.Arrays; + +public class DataAccessClassCalculator extends ClassCalculator { + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (!isConcreteClass(aClass)) { + return; + } + PsiField[] fields = aClass.getFields(); + double numberOfPrivateFields = + Arrays.stream( + fields).filter( + x -> x.hasModifierProperty( + PsiModifier.PRIVATE)).count(); + PsiMethod[] methods = aClass.getMethods(); + double numberOfPrivateMethods = Arrays.stream( + methods).filter( + x -> x.hasModifierProperty( + PsiModifier.PRIVATE)).count(); + postMetric(aClass, (numberOfPrivateFields + + numberOfPrivateMethods) / (methods.length + fields.length)); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DataAccessClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DataAccessClassMetric.java new file mode 100644 index 00000000..ba855474 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DataAccessClassMetric.java @@ -0,0 +1,48 @@ +/* + * 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 com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.DataAccessClassCalculator; +import org.jetbrains.annotations.NotNull; + +public class DataAccessClassMetric extends ClassMetric { + @NotNull + @Override + public String getDisplayName() { + return "Data Access Class Metric"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "DAM"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Ratio; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new DataAccessClassCalculator(); + } +} From 82d9a0ea7172fee9f0c95265f4e48a34e2bd6ac0 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Sat, 24 Mar 2018 22:04:35 +0300 Subject: [PATCH 03/15] measure of functional abstraction metric implemented --- ...sureOfFunctionalAbstractionCalculator.java | 49 +++++++++++++++++++ .../MeasureOfFunctionalAbstractionMetric.java | 49 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfFunctionalAbstractionCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfFunctionalAbstractionMetric.java diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfFunctionalAbstractionCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfFunctionalAbstractionCalculator.java new file mode 100644 index 00000000..67ee6b74 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfFunctionalAbstractionCalculator.java @@ -0,0 +1,49 @@ +/* + * 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 com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +public class MeasureOfFunctionalAbstractionCalculator extends ClassCalculator { + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (isConcreteClass(aClass)) { + final PsiMethod[] allMethods = aClass.getAllMethods(); + double numInheritedMethods = 0; + for (final PsiMethod method : allMethods) { + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null && !containingClass.equals(aClass) && + !method.hasModifierProperty(PsiModifier.STATIC)) { + numInheritedMethods++; + } + } + + postMetric(aClass, numInheritedMethods / allMethods.length); + } + } + } +} + diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfFunctionalAbstractionMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfFunctionalAbstractionMetric.java new file mode 100644 index 00000000..bcfcfac3 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfFunctionalAbstractionMetric.java @@ -0,0 +1,49 @@ +/* + * 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 com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.MeasureOfFunctionalAbstractionCalculator; +import org.jetbrains.annotations.NotNull; + +public class MeasureOfFunctionalAbstractionMetric extends ClassMetric { + + @NotNull + @Override + public String getDisplayName() { + return "Measure of functional abstraction"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "MFA"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Ratio; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new MeasureOfFunctionalAbstractionCalculator(); + } +} From 48975f762aa83238f9279c013ff8f4dbb29e3feb Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Tue, 10 Apr 2018 17:37:36 +0300 Subject: [PATCH 04/15] all other metrics implemented --- ...CohesionAmongMethodsOfClassCalculator.java | 55 ++++++++++++++ .../CohesionAmongMethodsOfClassMetric.java | 68 +++++++++++++++++ ...verageNumOfAncestorsProjectCalculator.java | 48 ++++++++++++ .../DirectClassCouplingProjectCalculator.java | 74 +++++++++++++++++++ ...MeasureOfAggregationProjectCalculator.java | 72 ++++++++++++++++++ .../NumHierarchiesProjectCalculator.java | 27 +++++++ ...umPolymorphicMethodsProjectCalculator.java | 38 ++++++++++ .../AverageNumOfAncestorsProjectMetric.java | 68 +++++++++++++++++ .../DirectClassCouplingProjectMetric.java | 68 +++++++++++++++++ .../MeasureOfAggregationProjectMetric.java | 68 +++++++++++++++++ .../NumHierarchiesProjectMetric.java | 49 ++++++++++++ .../NumPolymorphicMethodsProjectMetric.java | 68 +++++++++++++++++ 12 files changed, 703 insertions(+) create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/CohesionAmongMethodsOfClassCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/CohesionAmongMethodsOfClassMetric.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/AverageNumOfAncestorsProjectCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/DirectClassCouplingProjectCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/MeasureOfAggregationProjectCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumHierarchiesProjectCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumPolymorphicMethodsProjectCalculator.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/AverageNumOfAncestorsProjectMetric.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/DirectClassCouplingProjectMetric.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/MeasureOfAggregationProjectMetric.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumHierarchiesProjectMetric.java create mode 100644 stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/CohesionAmongMethodsOfClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/CohesionAmongMethodsOfClassCalculator.java new file mode 100644 index 00000000..59d20d75 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/CohesionAmongMethodsOfClassCalculator.java @@ -0,0 +1,55 @@ +/* + * 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 com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class CohesionAmongMethodsOfClassCalculator extends ClassCalculator { + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + int sumIntersection = 0; + int numMethods = 0; + Set parameters = new HashSet<>(); + for(PsiMethod method : aClass.getMethods()){ + if(method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)){ + continue; + } + numMethods++; + Set parametersInMethod = Stream.of(method.getParameterList()). + flatMap(x -> Stream.of(x.getParameters())). + map(PsiVariable::getType).collect(Collectors.toSet()); + sumIntersection += parametersInMethod.size(); + parameters.addAll(parametersInMethod); + } + if(parameters.size() == 0){ + return; + } + postMetric(aClass, sumIntersection, numMethods * parameters.size()); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/CohesionAmongMethodsOfClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/CohesionAmongMethodsOfClassMetric.java new file mode 100644 index 00000000..25899d2e --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/CohesionAmongMethodsOfClassMetric.java @@ -0,0 +1,68 @@ +/* + * 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 com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.CohesionAmongMethodsOfClassCalculator; +import org.jetbrains.annotations.NotNull; + +public class CohesionAmongMethodsOfClassMetric extends ClassMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Cohesion Among Methods of Class"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "CAM"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Ratio; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new CohesionAmongMethodsOfClassCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/AverageNumOfAncestorsProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/AverageNumOfAncestorsProjectCalculator.java new file mode 100644 index 00000000..a06ef0f9 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/AverageNumOfAncestorsProjectCalculator.java @@ -0,0 +1,48 @@ +/* + * 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 com.sixrr.stockmetrics.projectCalculators; + +import com.intellij.psi.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiMethod; +import com.sixrr.metrics.utils.MethodUtils; +import com.sixrr.stockmetrics.utils.CyclomaticComplexityUtil; + +public class AverageNumOfAncestorsProjectCalculator extends ProjectCalculator { + private int totalNumOfAncestors = 0; + private int numOfClasses = 0; + + @Override + public void endMetricsRun() { + postMetric(totalNumOfAncestors, numOfClasses); + } + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + + @Override + public void visitClass(PsiClass aClass) { + totalNumOfAncestors += aClass.getExtendsListTypes().length; + numOfClasses++; + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/DirectClassCouplingProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/DirectClassCouplingProjectCalculator.java new file mode 100644 index 00000000..0b52eaeb --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/DirectClassCouplingProjectCalculator.java @@ -0,0 +1,74 @@ +/* + * 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 com.sixrr.stockmetrics.projectCalculators; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; + +import java.util.HashSet; +import java.util.Set; + +public class DirectClassCouplingProjectCalculator extends ProjectCalculator { + private int numClasses; + private int totalNumClassesDependencies; + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + numClasses++; + Set classes = new HashSet<>(); + PsiField[] fields = aClass.getFields(); + for (PsiField field : fields) { + if (!field.isPhysical()) { + continue; + } + PsiType type = field.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + classes.add(classInType); + } + PsiMethod[] methods = aClass.getMethods(); + for (PsiMethod method : methods){ + PsiParameter[] parameters = method.getParameterList().getParameters(); + for(PsiParameter parameter : parameters){ + PsiTypeElement typeElement = parameter.getTypeElement(); + if(typeElement == null){ + continue; + } + PsiType type = typeElement.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + classes.add(classInType); + } + } + totalNumClassesDependencies += classes.size(); + } + } + + @Override + public void endMetricsRun() { + postMetric(totalNumClassesDependencies, numClasses); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/MeasureOfAggregationProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/MeasureOfAggregationProjectCalculator.java new file mode 100644 index 00000000..3dce8e2b --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/MeasureOfAggregationProjectCalculator.java @@ -0,0 +1,72 @@ +/* + * 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 com.sixrr.stockmetrics.projectCalculators; + +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.FileTypeIndex; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.util.PsiTypesUtil; +import com.intellij.util.indexing.FileBasedIndex; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MeasureOfAggregationProjectCalculator extends ElementCountProjectCalculator { + private Set userDefinedClasses = new HashSet<>(); + + @Override + protected PsiElementVisitor createVisitor() { + collectUserDefinedClasses(); + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + incrementCount((int)Stream.of(aClass.getFields()).map(PsiVariable::getType). + map(PsiTypesUtil::getPsiClass).filter(Objects::nonNull). + filter(x -> userDefinedClasses.contains(x)).count()); + } + } + + private void collectUserDefinedClasses() { + final Runnable runnable = () -> { + Project project = executionContext.getProject(); + userDefinedClasses = FileBasedIndex.getInstance() + .getContainingFiles( + FileTypeIndex.NAME, + JavaFileType.INSTANCE, + ProjectScope.getProjectScope(project)).stream(). + map(x -> PsiManager.getInstance(project).findFile(x)). + filter(x -> x instanceof PsiJavaFile).map(x -> (PsiJavaFile)x). + flatMap(x -> Stream.of(x.getClasses())). + collect(Collectors.toSet()); + + + }; + final ProgressManager progressManager = ProgressManager.getInstance(); + progressManager.runProcess(runnable, null); + } + + +} \ No newline at end of file diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumHierarchiesProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumHierarchiesProjectCalculator.java new file mode 100644 index 00000000..efa14486 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumHierarchiesProjectCalculator.java @@ -0,0 +1,27 @@ +/* + * 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 com.sixrr.stockmetrics.projectCalculators; + +import com.intellij.psi.PsiClass; + +public class NumHierarchiesProjectCalculator extends ClassCountingProjectCalculator{ + @Override + protected boolean satisfies(PsiClass aClass) { + return aClass.getExtendsListTypes().length == 0 + && aClass.getImplementsListTypes().length > 0; + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumPolymorphicMethodsProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumPolymorphicMethodsProjectCalculator.java new file mode 100644 index 00000000..83cbf52b --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumPolymorphicMethodsProjectCalculator.java @@ -0,0 +1,38 @@ +/* + * 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 com.sixrr.stockmetrics.projectCalculators; + +import com.intellij.psi.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; + +import java.util.stream.Stream; + +public class NumPolymorphicMethodsProjectCalculator extends ElementCountProjectCalculator { + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + incrementCount((int) Stream.of(aClass.getMethods()). + filter(x -> x.findSuperMethods().length != 0).count()); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/AverageNumOfAncestorsProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/AverageNumOfAncestorsProjectMetric.java new file mode 100644 index 00000000..25b90d59 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/AverageNumOfAncestorsProjectMetric.java @@ -0,0 +1,68 @@ +/* + * 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 com.sixrr.stockmetrics.projectMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.projectCalculators.AverageNumOfAncestorsProjectCalculator; +import org.jetbrains.annotations.NotNull; + +public class AverageNumOfAncestorsProjectMetric extends ProjectMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Average number of ancestors"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "ANA"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Average; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new AverageNumOfAncestorsProjectCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/DirectClassCouplingProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/DirectClassCouplingProjectMetric.java new file mode 100644 index 00000000..9577e57d --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/DirectClassCouplingProjectMetric.java @@ -0,0 +1,68 @@ +/* + * 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 com.sixrr.stockmetrics.projectMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.projectCalculators.DirectClassCouplingProjectCalculator; +import org.jetbrains.annotations.NotNull; + +public class DirectClassCouplingProjectMetric extends ProjectMetric{ + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Direct Class Coupling"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "DCC"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Average; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new DirectClassCouplingProjectCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/MeasureOfAggregationProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/MeasureOfAggregationProjectMetric.java new file mode 100644 index 00000000..65825378 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/MeasureOfAggregationProjectMetric.java @@ -0,0 +1,68 @@ +/* + * 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 com.sixrr.stockmetrics.projectMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.projectCalculators.MeasureOfAggregationProjectCalculator; +import org.jetbrains.annotations.NotNull; + +public class MeasureOfAggregationProjectMetric extends ProjectMetric{ + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Measure of Aggregation"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "MOA"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new MeasureOfAggregationProjectCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumHierarchiesProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumHierarchiesProjectMetric.java new file mode 100644 index 00000000..6d56ed89 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumHierarchiesProjectMetric.java @@ -0,0 +1,49 @@ +/* + * 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 com.sixrr.stockmetrics.projectMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.projectCalculators.NumHierarchiesProjectCalculator; +import org.jetbrains.annotations.NotNull; + +public class NumHierarchiesProjectMetric extends ProjectMetric { + + @NotNull + @Override + public String getDisplayName() { + return "Number of hierarchies"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "NOH"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new NumHierarchiesProjectCalculator(); + } +} \ No newline at end of file diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java new file mode 100644 index 00000000..80b52a37 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java @@ -0,0 +1,68 @@ +/* + * 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 com.sixrr.stockmetrics.projectMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.projectCalculators.NumPolymorphicMethodsProjectCalculator; +import org.jetbrains.annotations.NotNull; + +public class NumPolymorphicMethodsProjectMetric extends ProjectMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Number of polymorphic methods"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "NOP"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new NumPolymorphicMethodsProjectCalculator(); + } +} From 1d70d02cf4b7cf0156fd492b0acd2690d82a5306 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Mon, 16 Apr 2018 23:46:07 +0300 Subject: [PATCH 05/15] created QMoveClassEntity, QMoveEntitySearcher and QMoveEntitySearchResult, added requested metrics to profile in AutomaticRefactoringAction --- .../algorithm/{QOMMR.java => QMove.java} | 33 ++++++++++++++----- .../algorithm/entity/QMoveClassEntity.java | 4 +++ .../entity/QMoveEntitySearchResult.java | 4 +++ .../algorithm/entity/QMoveEntitySearcher.java | 4 +++ 4 files changed, 37 insertions(+), 8 deletions(-) rename src/main/java/org/ml_methods_group/algorithm/{QOMMR.java => QMove.java} (67%) create mode 100644 src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java create mode 100644 src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java create mode 100644 src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java diff --git a/src/main/java/org/ml_methods_group/algorithm/QOMMR.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java similarity index 67% rename from src/main/java/org/ml_methods_group/algorithm/QOMMR.java rename to src/main/java/org/ml_methods_group/algorithm/QMove.java index d1f6605c..c9fe6e68 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QOMMR.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -16,15 +16,18 @@ package org.ml_methods_group.algorithm; -import org.ml_methods_group.algorithm.entity.Entity; -import org.ml_methods_group.algorithm.entity.MethodEntity; +import org.ml_methods_group.algorithm.entity.*; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.stream.Collectors; +import java.util.Map; +import java.util.stream.Stream; public class QOMMR extends Algorithm { - private ExecutionContext context; - private List methodEntities; + private List methodEntities = new ArrayList<>(); + private final List classes = new ArrayList<>(); + private final Map classesByName = new HashMap<>(); QOMMR() { super("Quality-orientated Move Method Refactoring", false); } @@ -32,15 +35,29 @@ public class QOMMR extends Algorithm { @Override protected List calculateRefactorings( ExecutionContext context, boolean enableFieldRefactorings) throws Exception { + + final EntitySearchResult searchResult = context.getEntities(); methodEntities.clear(); - methodEntities = context.getEntities().getMethods(). - stream().filter(Entity::isMovable).map(MethodEntity::copy).collect(Collectors.toList()); + classes.clear(); + Stream.of(searchResult.getMethods()) + .flatMap(List::stream) + .filter(Entity::isMovable) + .forEach(methodEntities::add); + + searchResult.getClasses() + .stream() + .map(ClassEntity::copy) // create local copies + .peek(entity -> classesByName.put(entity.getName(), entity)) + .forEach(classes::add); for(MethodEntity methodEntity : methodEntities){ - //if + //getTargets + + } return null; } + //number of classes -- projectMetrics.NumClassesProjectMetrics.java //PolymorphismFactorProjectMetric //NumInterfacesImplementedMetrics ?? super class diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java new file mode 100644 index 00000000..feaae0f5 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -0,0 +1,4 @@ +package org.ml_methods_group.algorithm.entity; + +public class QMoveClassEntity { +} diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java new file mode 100644 index 00000000..adcd4bec --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java @@ -0,0 +1,4 @@ +package org.ml_methods_group.algorithm.entity; + +public class QMoveEntitySearchResult { +} diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java new file mode 100644 index 00000000..3537c695 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -0,0 +1,4 @@ +package org.ml_methods_group.algorithm.entity; + +public class QMoveEntitySearcher { +} From 6d06cdc2a8b664dfe69f47685e3c7a8ce32ba7d0 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Mon, 16 Apr 2018 23:47:57 +0300 Subject: [PATCH 06/15] forgot to add some files in previous commit :( --- .../org/ml_methods_group/algorithm/QMove.java | 73 +++-- .../algorithm/entity/QMoveClassEntity.java | 148 ++++++++- .../entity/QMoveEntitySearchResult.java | 61 +++- .../algorithm/entity/QMoveEntitySearcher.java | 310 +++++++++++++++++- .../plugin/AutomaticRefactoringAction.java | 2 + .../RefactoringExecutionContext.java | 16 +- .../stockmetrics/JavaMetricProvider.java | 11 + .../DataAccessClassCalculator.java | 1 - 8 files changed, 584 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index c9fe6e68..f8cf9946 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -16,7 +16,12 @@ package org.ml_methods_group.algorithm; + import org.ml_methods_group.algorithm.entity.*; +import org.ml_methods_group.config.Logging; +import org.ml_methods_group.utils.AlgorithmsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; @@ -24,19 +29,21 @@ import java.util.Map; import java.util.stream.Stream; -public class QOMMR extends Algorithm { +public class QMove extends Algorithm { + private static final Logger LOGGER = LoggerFactory.getLogger(QMove.class); private List methodEntities = new ArrayList<>(); private final List classes = new ArrayList<>(); - private final Map classesByName = new HashMap<>(); - QOMMR() { - super("Quality-orientated Move Method Refactoring", false); + private final Map qMoveClassesByName = new HashMap<>(); + private final Map classesByName = new HashMap<>(); + public QMove() { + super("Quality-orientated Move Method Refactoring", true); } @Override protected List calculateRefactorings( ExecutionContext context, boolean enableFieldRefactorings) throws Exception { - - final EntitySearchResult searchResult = context.getEntities(); + System.err.println("algorithm started"); + final QMoveEntitySearchResult searchResult = (QMoveEntitySearchResult)context.getEntities(); methodEntities.clear(); classes.clear(); Stream.of(searchResult.getMethods()) @@ -44,37 +51,39 @@ protected List calculateRefactorings( .filter(Entity::isMovable) .forEach(methodEntities::add); - searchResult.getClasses() + searchResult.getqMoveClasses() .stream() - .map(ClassEntity::copy) // create local copies - .peek(entity -> classesByName.put(entity.getName(), entity)) + .peek(entity -> qMoveClassesByName.put(entity.getName(), entity)) .forEach(classes::add); - for(MethodEntity methodEntity : methodEntities){ - //getTargets - - } + runParallel(methodEntities, context, ArrayList::new, + this::findBestMoveForMethod, + AlgorithmsUtil::combineLists); return null; } - //number of classes -- projectMetrics.NumClassesProjectMetrics.java - //PolymorphismFactorProjectMetric - //NumInterfacesImplementedMetrics ?? super class - //NumPrivateAttributes / NumAttributes - //Number of classes where this class is used. - /* - Computes the relatedness among methods of the class based upon the - parameter list of the methods. The metrics is computed using the - summation of the intersection of parameters of a method with the - maximum independent set of all parameter types in the class. - */ - //Number of declarations with user classes - // - /* - MethodInheritanceFactorProjectMetrics - */ - //number of polymorphic methods ?? - //number of public methods - //number of methods + + private List findBestMoveForMethod(MethodEntity method, + List refactorings){ + System.err.printf("Find best move for %s\n", method.getName()); + double bestFitness = Double.NEGATIVE_INFINITY; + QMoveClassEntity targetForThisMethod = null; + for(QMoveClassEntity targetClass : classes){ + QMoveClassEntity containingClass = qMoveClassesByName.get( + method.getClassName()); + containingClass.removeFromClass(method.getName()); + targetClass.addToClass(method.getName()); + double newFitness = targetClass.fitness(); + //System.err.println(newFitness); + if(newFitness > bestFitness){ + bestFitness = newFitness; + targetForThisMethod = targetClass; + } + targetClass.removeFromClass(method.getName()); + containingClass.addToClass(method.getName()); + } + return refactorings; + } + } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index feaae0f5..4d55e49a 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -1,4 +1,150 @@ +/* + * 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.entity; -public class QMoveClassEntity { +import com.intellij.analysis.AnalysisScope; +import com.intellij.psi.PsiClass; +import com.sixrr.metrics.Metric; +import com.sixrr.metrics.MetricCategory; +import com.sixrr.metrics.metricModel.MetricsRun; +import com.sixrr.stockmetrics.classMetrics.*; +import com.sixrr.stockmetrics.projectMetrics.*; + +import java.util.HashSet; +import java.util.Set; + +public class QMoveClassEntity extends ClassEntity { + private double complexity; + private double size; + private double polymorphism; + private double abstraction; + private double hierarchies; + private double encapsulation; + private double coupling; + private double messaging; + private double cohesion; + private double composition; + private double inheritance; + + private static final VectorCalculator QMOOD_CALCULATOR = new VectorCalculator() + .addMetricDependence(NumMethodsClassMetric.class) //Complexity 0 + .addMetricDependence(NumClassesProjectMetric.class) //Size 1 + .addMetricDependence(NumPolymorphicMethodsProjectMetric.class) //Polymorphism 2 + .addMetricDependence(AverageNumOfAncestorsProjectMetric.class) //Abstraction 3 + .addMetricDependence(NumHierarchiesProjectMetric.class) //Hierarchies 4 + .addMetricDependence(DataAccessClassMetric.class) //Encapsulation 5 + .addMetricDependence(DirectClassCouplingProjectMetric.class) //Coupling 6 + .addMetricDependence(NumPublicMethodsClassMetric.class) //Messaging 7 + .addMetricDependence(CohesionAmongMethodsOfClassMetric.class) //Cohesion 8 + .addMetricDependence(MeasureOfAggregationProjectMetric.class) //Composition 9 + .addMetricDependence(MeasureOfFunctionalAbstractionMetric.class); //Inheritance 10 + + QMoveClassEntity(PsiClass psiClass) { + super(psiClass); + } + + + @Override + void calculateVector(MetricsRun metricsRun) { + System.err.println("Calculating vector"); + double[] vector = QMOOD_CALCULATOR.calculateVector(metricsRun, this); + for(int i = 0; i < 11; i++){ + System.err.printf("%f ", vector[i]); + } + System.err.println(); + complexity = vector[0]; + size = vector[1]; + polymorphism = vector[2]; + abstraction = vector[3]; + hierarchies = vector[4]; + encapsulation = vector[5]; + coupling = vector[6]; + messaging = vector[7]; + cohesion = vector[8]; + composition = vector[9]; + inheritance = vector[10]; + } + + @Override + public MetricCategory getCategory() { + return MetricCategory.Class; + } + + @Override + public String getClassName() { + return getName(); + } + + + @Override + public boolean isField() { + return false; + } + + public void removeFromClass(String method) { + getRelevantProperties().removeMethod(method); + } + + public void addToClass(String method) { + getRelevantProperties().addMethod(method); + } + + private double reusability() { + return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging + + 0.5 * size; + } + + private double flexibility() { + return 0.25 * encapsulation - 0.25 * coupling + 0.5 * composition + + 0.5 * polymorphism; + } + + private double understandability() { + return 0.33 * abstraction + 0.33 * encapsulation - 0.33 * coupling + + 0.33 * cohesion - 0.33 * polymorphism - 0.33 * complexity + - 0.33 * size; + } + + private double functionality() { + return +0.12 * cohesion + 0.22 * polymorphism + 0.22 * messaging + + 0.22 * size + 0.22 * hierarchies; + } + + private double extendibility() { + return 0.5 * abstraction - 0.5 * coupling + 0.5 * inheritance + + 0.5 * polymorphism; + } + + private double effectiveness() { + return 0.2 * abstraction + 0.2 * encapsulation + 0.2 * composition + + 0.2 * inheritance + 0.2 * polymorphism; + } + + public double fitness(){ + //calculateVector(); + return reusability() + flexibility() + understandability() + + functionality() + extendibility() + effectiveness(); + } + + + public static Set> getRequestedMetrics() { + return QMOOD_CALCULATOR.getRequestedMetrics(); + } + + + } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java index adcd4bec..95f96192 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java @@ -1,4 +1,63 @@ +/* + * 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.entity; -public class QMoveEntitySearchResult { +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +public class QMoveEntitySearchResult extends EntitySearchResult { + private final List methods; + private final List fields; + private final List qMoveClasses; + private final int propertiesCount; + private final long searchTime; + + public QMoveEntitySearchResult(List classes, List methods, List fields, + long searchTime, List qMoveClassEntities) { + super(classes, methods, fields, searchTime); + this.methods = methods; + this.fields = fields; + this.searchTime = searchTime; + qMoveClasses = qMoveClassEntities; + propertiesCount = Stream.of(classes, methods, fields) + .flatMap(List::stream) + .map(Entity::getRelevantProperties) + .mapToInt(RelevantProperties::size) + .sum(); + } + + public List getqMoveClasses() { + return Collections.unmodifiableList(qMoveClasses); + } + + public List getMethods() { + return Collections.unmodifiableList(methods); + } + + public List getFields() { + return Collections.unmodifiableList(fields); + } + + public int getPropertiesCount() { + return propertiesCount; + } + + public long getSearchTime() { + return searchTime; + } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java index 3537c695..5e33b1c7 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -1,4 +1,312 @@ +/* + * 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.entity; -public class QMoveEntitySearcher { +import com.intellij.analysis.AnalysisScope; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.sixrr.metrics.metricModel.MetricsRun; +import com.sixrr.metrics.utils.MethodUtils; +import org.apache.log4j.Logger; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; +import org.ml_methods_group.algorithm.properties.finder_strategy.FinderStrategy; +import org.ml_methods_group.algorithm.properties.finder_strategy.NewStrategy; +import org.ml_methods_group.config.Logging; +import org.ml_methods_group.utils.PSIUtil; + +import java.util.*; + +import static org.ml_methods_group.utils.PsiSearchUtil.getHumanReadableName; + +public class QMoveEntitySearcher { + private static final Logger LOGGER = Logging.getLogger(QMoveEntitySearcher.class); + + private final Map classForName = new HashMap<>(); + private final Map entities = new HashMap<>(); + private final AnalysisScope scope; + private final long startTime; + private final FinderStrategy strategy; + private final ProgressIndicator indicator; + + private QMoveEntitySearcher(AnalysisScope scope) { + System.err.println("In QMoveEntitySearcher"); + this.scope = scope; + strategy = NewStrategy.getInstance(); + startTime = System.currentTimeMillis(); + if (ProgressManager.getInstance().hasProgressIndicator()) { + indicator = ProgressManager.getInstance().getProgressIndicator(); + } else { + indicator = new EmptyProgressIndicator(); + } + } + + public static QMoveEntitySearchResult analyze(AnalysisScope scope, MetricsRun metricsRun) { + final QMoveEntitySearcher finder = new QMoveEntitySearcher(scope); + return finder.runCalculations(metricsRun); + } + + private QMoveEntitySearchResult runCalculations(MetricsRun metricsRun) { + indicator.pushState(); + indicator.setText("Searching entities"); + indicator.setIndeterminate(true); + LOGGER.info("Indexing entities..."); + scope.accept(new QMoveEntitySearcher.UnitsFinder()); + indicator.setIndeterminate(false); + LOGGER.info("Calculating properties..."); + indicator.setText("Calculating properties"); + scope.accept(new QMoveEntitySearcher.PropertiesCalculator()); + indicator.popState(); + return prepareResult(metricsRun); + } + + private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { + LOGGER.info("Preparing results..."); + final List classes = new ArrayList<>(); + final List methods = new ArrayList<>(); + final List fields = new ArrayList<>(); + final List qMoveClasses = new ArrayList<>(); + final List validEntities = new ArrayList<>(); + for (Entity entity : entities.values()) { + indicator.checkCanceled(); + try { + entity.calculateVector(metricsRun); + } catch (Exception e) { + LOGGER.warn("Failed to calculate vector for " + entity.getName()); + continue; + } + validEntities.add(entity); + switch (entity.getCategory()) { + case Class: + classes.add((ClassEntity) entity); + qMoveClasses.add((QMoveClassEntity) entity); + break; + case Method: + methods.add((MethodEntity) entity); + break; + default: + fields.add((FieldEntity) entity); + break; + } + } + LOGGER.info("Properties calculated"); + LOGGER.info("Generated " + classes.size() + " class entities"); + LOGGER.info("Generated " + methods.size() + " method entities"); + LOGGER.info("Generated " + fields.size() + " field entities"); + return new QMoveEntitySearchResult(classes, methods, fields, System.currentTimeMillis() - startTime, qMoveClasses); + } + + private class UnitsFinder extends JavaRecursiveElementVisitor { + + @Override + public void visitFile(PsiFile file) { + indicator.checkCanceled(); + if (strategy.acceptFile(file)) { + LOGGER.info("Indexing " + file.getName()); + super.visitFile(file); + } + } + + @Override + public void visitClass(PsiClass aClass) { + indicator.checkCanceled(); + classForName.put(getHumanReadableName(aClass), aClass); + if (!strategy.acceptClass(aClass)) { + return; + } + entities.put(aClass, new QMoveClassEntity(aClass)); + super.visitClass(aClass); + } + + @Override + public void visitField(PsiField field) { + if (!strategy.acceptField(field)) { + return; + } + indicator.checkCanceled(); + entities.put(field, new FieldEntity(field)); + super.visitField(field); + } + + @Override + public void visitMethod(PsiMethod method) { + if (!strategy.acceptMethod(method)) { + return; + } + indicator.checkCanceled(); + entities.put(method, new MethodEntity(method)); + super.visitMethod(method); + } + } + + private class PropertiesCalculator extends JavaRecursiveElementVisitor { + private int propertiesCalculated = 0; + + private PsiMethod currentMethod; + + @Override + public void visitFile(PsiFile file) { + indicator.checkCanceled(); + if (strategy.acceptFile(file)) { + super.visitFile(file); + } + } + + @Override + public void visitClass(PsiClass aClass) { + indicator.checkCanceled(); + final Entity entity = entities.get(aClass); + if (entity == null) { + super.visitClass(aClass); + return; + } + final RelevantProperties classProperties = entity.getRelevantProperties(); + classProperties.addClass(aClass, strategy.getWeight(aClass, aClass)); + if (strategy.processSupers()) { + for (PsiClass superClass : PSIUtil.getAllSupers(aClass)) { + if (superClass.isInterface()) { + classProperties.addClass(superClass, strategy.getWeight(aClass, superClass)); + } else { + propertiesFor(superClass).ifPresent(p -> p.addClass(aClass, strategy.getWeight(superClass, aClass))); + } + } + } + Arrays.stream(aClass.getMethods()) + .filter(m -> isProperty(aClass, m)) + .forEach(m -> classProperties.addMethod(m, strategy.getWeight(aClass, m))); + Arrays.stream(aClass.getFields()) + .filter(f -> isProperty(aClass, f)) + .forEach(f -> classProperties.addField(f, strategy.getWeight(aClass, f))); + reportPropertiesCalculated(); + super.visitClass(aClass); + } + + private boolean isProperty(PsiClass aClass, PsiMember member) { + return !(member instanceof PsiMethod && ((PsiMethod) member).isConstructor()) && (aClass.equals(member.getContainingClass()) || !MethodUtils.isPrivate(member)); + } + + @Contract("null -> false") + private boolean isClassInProject(final @Nullable PsiClass aClass) { + return aClass != null && classForName.containsKey(getHumanReadableName(aClass)); + } + + @Override + public void visitMethod(PsiMethod method) { + indicator.checkCanceled(); + final Entity entity = entities.get(method); + if (entity == null) { + super.visitMethod(method); + return; + + } + final RelevantProperties methodProperties = entity.getRelevantProperties(); + methodProperties.addMethod(method, strategy.getWeight(method, method)); + Optional.ofNullable(method.getContainingClass()) + .ifPresent(c -> methodProperties.addClass(c, strategy.getWeight(method, c))); + if (currentMethod == null) { + currentMethod = method; + } + if (strategy.processSupers()) { + PSIUtil.getAllSupers(method).stream() + .map(entities::get) + .filter(Objects::nonNull) + .forEach(superMethod -> superMethod + .getRelevantProperties() + .addOverrideMethod(method, strategy.getWeight(superMethod, method))); + } + reportPropertiesCalculated(); + super.visitMethod(method); + if (currentMethod == method) { + currentMethod = null; + } + } + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + indicator.checkCanceled(); + PsiElement element = expression.resolve(); + if (currentMethod != null && element instanceof PsiField + && isClassInProject(((PsiField) element).getContainingClass()) && strategy.isRelation(expression)) { + final PsiField field = (PsiField) element; + propertiesFor(currentMethod) + .ifPresent(p -> p.addField(field, strategy.getWeight(currentMethod, field))); +// propertiesFor(field) +// .ifPresent(p -> p.addMethod(currentMethod, strategy.getWeight(field, currentMethod))); + final PsiClass fieldClass = PsiUtil.resolveClassInType(field.getType()); + if (isClassInProject(fieldClass)) { + propertiesFor(currentMethod) + .ifPresent(p -> p.addClass(fieldClass, strategy.getWeight(currentMethod, fieldClass))); + } + } + super.visitReferenceExpression(expression); + } + + @Override + public void visitField(PsiField field) { + indicator.checkCanceled(); + final Entity entity = entities.get(field); + if (entity == null) { + super.visitField(field); + return; + } + RelevantProperties fieldProperties = entity.getRelevantProperties(); + fieldProperties.addField(field, strategy.getWeight(field, field)); + final PsiClass containingClass = field.getContainingClass(); + if (containingClass != null) { + fieldProperties.addClass(containingClass, strategy.getWeight(field, containingClass)); + final PsiClass fieldClass = PsiUtil.resolveClassInType(field.getType()); + if (isClassInProject(fieldClass)) { + entities.get(containingClass).getRelevantProperties().addClass(fieldClass, strategy.getWeight(containingClass, fieldClass)); + } + } + reportPropertiesCalculated(); + super.visitField(field); + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + indicator.checkCanceled(); + final PsiMethod called = expression.resolveMethod(); + final PsiClass usedClass = called != null ? called.getContainingClass() : null; + if (currentMethod != null && called != null && isClassInProject(usedClass) + && strategy.isRelation(expression)) { + propertiesFor(currentMethod) + .ifPresent(p -> { + p.addMethod(called, strategy.getWeight(currentMethod, called)); + p.addClass(usedClass, strategy.getWeight(currentMethod, usedClass)); + }); + } + super.visitMethodCallExpression(expression); + } + + private void reportPropertiesCalculated() { + propertiesCalculated++; + if (indicator != null) { + indicator.setFraction((double) propertiesCalculated / entities.size()); + } + } + } + + private Optional propertiesFor(PsiElement element) { + return Optional.ofNullable(entities.get(element)) + .map(Entity::getRelevantProperties); + } + } diff --git a/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java b/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java index 3dcb9a92..ad3de315 100644 --- a/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java +++ b/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java @@ -33,6 +33,7 @@ import org.jetbrains.annotations.Nullable; import org.ml_methods_group.algorithm.AlgorithmResult; import org.ml_methods_group.algorithm.entity.Entity; +import org.ml_methods_group.algorithm.entity.QMoveClassEntity; import org.ml_methods_group.config.ArchitectureReloadedConfig; import org.ml_methods_group.config.Logging; import org.ml_methods_group.refactoring.RefactoringExecutionContext; @@ -150,6 +151,7 @@ private void showDialogs(@NotNull RefactoringExecutionContext context) { private static void checkRefactoringProfile() { final Set> requestedSet = Entity.getRequestedMetrics(); + requestedSet.addAll(QMoveClassEntity.getRequestedMetrics()); final String profileName = ArchitectureReloadedBundle.message(REFACTORING_PROFILE_KEY); final MetricsProfileRepository repository = MetricsProfileRepository.getInstance(); if (MetricsProfilesUtil.checkMetricsList(profileName, requestedSet, repository)) { 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..eb3f4be4 100644 --- a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java +++ b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java @@ -33,6 +33,8 @@ import org.ml_methods_group.algorithm.*; import org.ml_methods_group.algorithm.entity.EntitySearchResult; import org.ml_methods_group.algorithm.entity.EntitySearcher; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearchResult; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearcher; import org.ml_methods_group.config.Logging; import java.util.*; @@ -44,7 +46,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, QMove.class); @NotNull private final MetricsRunImpl metricsRun = new MetricsRunImpl(); @@ -112,7 +114,17 @@ private void execute(ProgressIndicator indicator) { entitySearchResult = ApplicationManager.getApplication() .runReadAction((Computable) () -> EntitySearcher.analyze(scope, metricsRun)); for (String algorithm : requestedAlgorithms) { - calculateAlgorithmForName(algorithm); + if(algorithm.equals("QMove")){ + System.err.println("In QMove"); + QMoveEntitySearchResult qMoveEntitySearchResult = ApplicationManager.getApplication() + .runReadAction((Computable) () + -> QMoveEntitySearcher.analyze(scope, metricsRun)); + final Algorithm qMoveAlgorithm = new QMove(); + final AlgorithmResult result = qMoveAlgorithm.execute(qMoveEntitySearchResult, executorService, isFieldRefactoringAvailable); + } + else{ + calculateAlgorithmForName(algorithm); + } } indicator.setText("Finish refactorings search..."); } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java index adcbcb7d..0afc9ec8 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -116,6 +116,11 @@ private static void initializeClassMetrics(Collection metrics) { metrics.add(new FanInClassMetric()); metrics.add(new FanOutClassMetric()); metrics.add(new BlankLinesCountClassMetric()); + //mine + metrics.add(new NumPublicMethodsClassMetric()); + metrics.add(new MeasureOfFunctionalAbstractionMetric()); + metrics.add(new DataAccessClassMetric()); + metrics.add(new CohesionAmongMethodsOfClassMetric()); } private static void initializeInterfaceMetrics(Collection metrics) { @@ -349,6 +354,12 @@ private static void initializeProjectMetrics(Collection metrics) { metrics.add(new PercentMethodsJavadocedProjectMetric()); metrics.add(new PolymorphismFactorProjectMetric()); metrics.add(new TotalCyclomaticComplexityProjectMetric()); + //mine + metrics.add(new NumHierarchiesProjectMetric()); + metrics.add(new NumPolymorphicMethodsProjectMetric()); + metrics.add(new AverageCyclomaticComplexityProjectMetric()); + metrics.add(new MeasureOfFunctionalAbstractionMetric()); + metrics.add(new DirectClassCouplingProjectMetric()); } @NotNull diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java index c9febdca..107145cf 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java @@ -21,7 +21,6 @@ import java.util.Arrays; public class DataAccessClassCalculator extends ClassCalculator { - @Override protected PsiElementVisitor createVisitor() { return new Visitor(); From 5521dd5256722a5db9ce3d1b8d00fa38da2c767e Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Tue, 24 Apr 2018 09:56:22 +0300 Subject: [PATCH 07/15] changed all metrics to ClassMetrics, implemented add and remove method to QMoveClassEntity with recalculations of cohesion and coupling --- .../org/ml_methods_group/algorithm/QMove.java | 93 ++++++++++--- .../algorithm/entity/QMoveClassEntity.java | 124 ++++++++++++------ .../algorithm/entity/QMoveEntitySearcher.java | 4 +- .../RefactoringExecutionContext.java | 1 - .../stockmetrics/JavaMetricProvider.java | 2 +- .../DataAccessClassCalculator.java | 13 +- .../IsRootOfHierarchyClassCalculator.java} | 19 ++- .../MeasureOfAggregationClassCalculator.java} | 7 +- .../NumAncestorsClassCalculator.java} | 14 +- ...umPolymorphicMethodsProjectCalculator.java | 7 +- .../IsRootOfHierarchyClassMetric.java} | 8 +- .../MeasureOfAggregationClassMetric.java} | 8 +- .../NumAncestorsClassMetric.java} | 14 +- .../NumPolymorphicMethodsProjectMetric.java | 2 +- 14 files changed, 215 insertions(+), 101 deletions(-) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectCalculators/NumHierarchiesProjectCalculator.java => classCalculators/IsRootOfHierarchyClassCalculator.java} (56%) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectCalculators/MeasureOfAggregationProjectCalculator.java => classCalculators/MeasureOfAggregationClassCalculator.java} (89%) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectCalculators/AverageNumOfAncestorsProjectCalculator.java => classCalculators/NumAncestorsClassCalculator.java} (74%) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectCalculators => classCalculators}/NumPolymorphicMethodsProjectCalculator.java (80%) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectMetrics/NumHierarchiesProjectMetric.java => classMetrics/IsRootOfHierarchyClassMetric.java} (82%) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectMetrics/MeasureOfAggregationProjectMetric.java => classMetrics/MeasureOfAggregationClassMetric.java} (87%) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectMetrics/AverageNumOfAncestorsProjectMetric.java => classMetrics/NumAncestorsClassMetric.java} (83%) diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index f8cf9946..a01e3c72 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -20,8 +20,6 @@ import org.ml_methods_group.algorithm.entity.*; import org.ml_methods_group.config.Logging; import org.ml_methods_group.utils.AlgorithmsUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; @@ -30,11 +28,20 @@ import java.util.stream.Stream; public class QMove extends Algorithm { - private static final Logger LOGGER = LoggerFactory.getLogger(QMove.class); private List methodEntities = new ArrayList<>(); private final List classes = new ArrayList<>(); private final Map qMoveClassesByName = new HashMap<>(); - private final Map classesByName = new HashMap<>(); + private double complexity; + private double polymorphism; + private double abstraction; + private double hierarchies; + private double encapsulation; + private double coupling; + private double messaging; + private double cohesion; + private double composition; + private double inheritance; + private double size; public QMove() { super("Quality-orientated Move Method Refactoring", true); } @@ -42,7 +49,6 @@ public QMove() { @Override protected List calculateRefactorings( ExecutionContext context, boolean enableFieldRefactorings) throws Exception { - System.err.println("algorithm started"); final QMoveEntitySearchResult searchResult = (QMoveEntitySearchResult)context.getEntities(); methodEntities.clear(); classes.clear(); @@ -51,39 +57,94 @@ protected List calculateRefactorings( .filter(Entity::isMovable) .forEach(methodEntities::add); - searchResult.getqMoveClasses() + searchResult.getqMoveClasses() .stream() .peek(entity -> qMoveClassesByName.put(entity.getName(), entity)) .forEach(classes::add); - runParallel(methodEntities, context, ArrayList::new, - this::findBestMoveForMethod, - AlgorithmsUtil::combineLists); - return null; + ArrayList refactorings = new ArrayList<>(); + for(MethodEntity methodEntity : methodEntities){ + findBestMoveForMethod(methodEntity, refactorings); + } + return refactorings; } private List findBestMoveForMethod(MethodEntity method, - List refactorings){ - System.err.printf("Find best move for %s\n", method.getName()); - double bestFitness = Double.NEGATIVE_INFINITY; + List refactorings){ + double bestFitness = fitness(); QMoveClassEntity targetForThisMethod = null; for(QMoveClassEntity targetClass : classes){ QMoveClassEntity containingClass = qMoveClassesByName.get( method.getClassName()); containingClass.removeFromClass(method.getName()); - targetClass.addToClass(method.getName()); - double newFitness = targetClass.fitness(); - //System.err.println(newFitness); + targetClass.addMethod(method.getName()); + double newFitness = fitness(); if(newFitness > bestFitness){ bestFitness = newFitness; targetForThisMethod = targetClass; } targetClass.removeFromClass(method.getName()); containingClass.addToClass(method.getName()); + double fitness = fitness(); + } + if(targetForThisMethod != null){ + refactorings.add(new Refactoring(method.getName(), + targetForThisMethod.getName(), bestFitness / fitness() - 1, false)); } return refactorings; } + + private void calculateMetrics(){ + size = classes.size(); + complexity = methodEntities.size(); + polymorphism = classes.stream().mapToDouble(QMoveClassEntity::getPolymorphism).sum(); + abstraction = classes.stream().mapToDouble(QMoveClassEntity::getAbstraction).sum() / size; + hierarchies = classes.stream().mapToDouble(QMoveClassEntity::getHierarchies).sum(); + encapsulation = classes.stream().mapToDouble(QMoveClassEntity::getEncapsulation).sum(); + coupling = classes.stream().mapToDouble(QMoveClassEntity::getCoupling).sum(); + messaging = classes.stream().mapToDouble(QMoveClassEntity::getMessaging).sum(); + cohesion = classes.stream().mapToDouble(QMoveClassEntity::getCohesion).sum(); + composition = classes.stream().mapToDouble(QMoveClassEntity::getComposition).sum(); + inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); + } + + private double reusability() { + return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging + + 0.5 * size; + } + + private double flexibility() { + return 0.25 * encapsulation - 0.25 * coupling + 0.5 * composition + + 0.5 * polymorphism; + } + + private double understandability() { + return 0.33 * abstraction + 0.33 * encapsulation - 0.33 * coupling + + 0.33 * cohesion - 0.33 * polymorphism - 0.33 * complexity + - 0.33 * size; + } + + private double functionality() { + return +0.12 * cohesion + 0.22 * polymorphism + 0.22 * messaging + + 0.22 * size + 0.22 * hierarchies; + } + + private double extendibility() { + return 0.5 * abstraction - 0.5 * coupling + 0.5 * inheritance + + 0.5 * polymorphism; + } + + private double effectiveness() { + return 0.2 * abstraction + 0.2 * encapsulation + 0.2 * composition + + 0.2 * inheritance + 0.2 * polymorphism; + } + + private double fitness(){ + calculateMetrics(); + return reusability() + flexibility() + understandability() + + functionality() + extendibility() + effectiveness(); + } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index 4d55e49a..54f0ab6b 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -16,20 +16,22 @@ package org.ml_methods_group.algorithm.entity; -import com.intellij.analysis.AnalysisScope; import com.intellij.psi.PsiClass; import com.sixrr.metrics.Metric; import com.sixrr.metrics.MetricCategory; import com.sixrr.metrics.metricModel.MetricsRun; import com.sixrr.stockmetrics.classMetrics.*; import com.sixrr.stockmetrics.projectMetrics.*; +import org.jetbrains.annotations.NotNull; import java.util.HashSet; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class QMoveClassEntity extends ClassEntity { private double complexity; - private double size; private double polymorphism; private double abstraction; private double hierarchies; @@ -42,16 +44,15 @@ public class QMoveClassEntity extends ClassEntity { private static final VectorCalculator QMOOD_CALCULATOR = new VectorCalculator() .addMetricDependence(NumMethodsClassMetric.class) //Complexity 0 - .addMetricDependence(NumClassesProjectMetric.class) //Size 1 + .addMetricDependence(MeasureOfFunctionalAbstractionMetric.class) //Inheritance 1 .addMetricDependence(NumPolymorphicMethodsProjectMetric.class) //Polymorphism 2 - .addMetricDependence(AverageNumOfAncestorsProjectMetric.class) //Abstraction 3 - .addMetricDependence(NumHierarchiesProjectMetric.class) //Hierarchies 4 + .addMetricDependence(NumAncestorsClassMetric.class) //Abstraction 3 + .addMetricDependence(IsRootOfHierarchyClassMetric.class) //Hierarchies 4 .addMetricDependence(DataAccessClassMetric.class) //Encapsulation 5 .addMetricDependence(DirectClassCouplingProjectMetric.class) //Coupling 6 .addMetricDependence(NumPublicMethodsClassMetric.class) //Messaging 7 .addMetricDependence(CohesionAmongMethodsOfClassMetric.class) //Cohesion 8 - .addMetricDependence(MeasureOfAggregationProjectMetric.class) //Composition 9 - .addMetricDependence(MeasureOfFunctionalAbstractionMetric.class); //Inheritance 10 + .addMetricDependence(MeasureOfAggregationClassMetric.class); //Composition 9 QMoveClassEntity(PsiClass psiClass) { super(psiClass); @@ -60,14 +61,8 @@ public class QMoveClassEntity extends ClassEntity { @Override void calculateVector(MetricsRun metricsRun) { - System.err.println("Calculating vector"); double[] vector = QMOOD_CALCULATOR.calculateVector(metricsRun, this); - for(int i = 0; i < 11; i++){ - System.err.printf("%f ", vector[i]); - } - System.err.println(); complexity = vector[0]; - size = vector[1]; polymorphism = vector[2]; abstraction = vector[3]; hierarchies = vector[4]; @@ -76,7 +71,7 @@ void calculateVector(MetricsRun metricsRun) { messaging = vector[7]; cohesion = vector[8]; composition = vector[9]; - inheritance = vector[10]; + inheritance = vector[1]; } @Override @@ -95,6 +90,7 @@ public boolean isField() { return false; } + @Override public void removeFromClass(String method) { getRelevantProperties().removeMethod(method); } @@ -103,48 +99,102 @@ public void addToClass(String method) { getRelevantProperties().addMethod(method); } - private double reusability() { - return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging - + 0.5 * size; + public double getComplexity(){ + return complexity; } - private double flexibility() { - return 0.25 * encapsulation - 0.25 * coupling + 0.5 * composition - + 0.5 * polymorphism; + public double getAbstraction() { + return abstraction; } - private double understandability() { - return 0.33 * abstraction + 0.33 * encapsulation - 0.33 * coupling - + 0.33 * cohesion - 0.33 * polymorphism - 0.33 * complexity - - 0.33 * size; + public double getCohesion() { + return cohesion; } - private double functionality() { - return +0.12 * cohesion + 0.22 * polymorphism + 0.22 * messaging - + 0.22 * size + 0.22 * hierarchies; + public double getCoupling() { + return coupling; } - private double extendibility() { - return 0.5 * abstraction - 0.5 * coupling + 0.5 * inheritance - + 0.5 * polymorphism; + public double getHierarchies() { + return hierarchies; } - private double effectiveness() { - return 0.2 * abstraction + 0.2 * encapsulation + 0.2 * composition - + 0.2 * inheritance + 0.2 * polymorphism; + public double getComposition() { + return composition; } - public double fitness(){ - //calculateVector(); - return reusability() + flexibility() + understandability() + - functionality() + extendibility() + effectiveness(); + public double getEncapsulation() { + return encapsulation; } + public double getInheritance() { + return inheritance; + } + + public double getMessaging() { + return messaging; + } + + public double getPolymorphism() { + return polymorphism; + } public static Set> getRequestedMetrics() { return QMOOD_CALCULATOR.getRequestedMetrics(); } + public void addMethod(String method){ + getRelevantProperties().addMethod(method); + complexity++; + recalculateCoupling(); + recalculateCohesion(); + } + public void removeMethod(String method){ + getRelevantProperties().removeMethod(method); + complexity--; + recalculateCoupling(); + recalculateCohesion(); + } + + private void recalculateCoupling() { + Set relatedClasses = new HashSet<>(); + relatedClasses.addAll( + getRelevantProperties().getFields().stream().map( + this::extractClassnameFromField) + .filter(x -> !Objects.equals(x, "")). + collect(Collectors.toSet())); + relatedClasses.addAll( + getRelevantProperties().getMethods().stream() + .flatMap(this::extractParametersFromMethod) + .filter(x -> !Objects.equals(x, "")) + .collect(Collectors.toSet())); + coupling = relatedClasses.size(); + } + + private void recalculateCohesion(){ + int totalSize = + getRelevantProperties().getMethods().stream() + .flatMap(this::extractParametersFromMethod).collect( + Collectors.toSet() + ).size(); + double sum = getRelevantProperties().getMethods().stream() + .mapToDouble(x -> extractParametersFromMethod(x).collect( + Collectors.toSet() + ).size()).sum(); + cohesion = totalSize == 0 ? 0 : sum / (totalSize * + getRelevantProperties().getMethods().size()); + } + + private String extractClassnameFromField(String s){ + String[] strings = s.split("\\."); + assert strings.length > 1; + return strings[strings.length - 2]; + } + @NotNull + private Stream extractParametersFromMethod(String s){ + String params = s.substring(s.indexOf("(") + 1, s.indexOf(")")); + return Stream.of(params.split(",")); + } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java index 5e33b1c7..94aea807 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -47,7 +47,6 @@ public class QMoveEntitySearcher { private final ProgressIndicator indicator; private QMoveEntitySearcher(AnalysisScope scope) { - System.err.println("In QMoveEntitySearcher"); this.scope = scope; strategy = NewStrategy.getInstance(); startTime = System.currentTimeMillis(); @@ -108,6 +107,7 @@ private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { } LOGGER.info("Properties calculated"); LOGGER.info("Generated " + classes.size() + " class entities"); + LOGGER.info("Generated " + qMoveClasses.size() + "QMove class entities"); LOGGER.info("Generated " + methods.size() + " method entities"); LOGGER.info("Generated " + fields.size() + " field entities"); return new QMoveEntitySearchResult(classes, methods, fields, System.currentTimeMillis() - startTime, qMoveClasses); @@ -309,4 +309,4 @@ private Optional propertiesFor(PsiElement element) { .map(Entity::getRelevantProperties); } -} +} \ No newline at end of file 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 eb3f4be4..772f6779 100644 --- a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java +++ b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java @@ -115,7 +115,6 @@ private void execute(ProgressIndicator indicator) { .runReadAction((Computable) () -> EntitySearcher.analyze(scope, metricsRun)); for (String algorithm : requestedAlgorithms) { if(algorithm.equals("QMove")){ - System.err.println("In QMove"); QMoveEntitySearchResult qMoveEntitySearchResult = ApplicationManager.getApplication() .runReadAction((Computable) () -> QMoveEntitySearcher.analyze(scope, metricsRun)); diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java index 0afc9ec8..70c0bab5 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -355,7 +355,7 @@ private static void initializeProjectMetrics(Collection metrics) { metrics.add(new PolymorphismFactorProjectMetric()); metrics.add(new TotalCyclomaticComplexityProjectMetric()); //mine - metrics.add(new NumHierarchiesProjectMetric()); + metrics.add(new IsRootOfHierarchyClassMetric()); metrics.add(new NumPolymorphicMethodsProjectMetric()); metrics.add(new AverageCyclomaticComplexityProjectMetric()); metrics.add(new MeasureOfFunctionalAbstractionMetric()); diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java index 107145cf..c42b987d 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java @@ -34,18 +34,17 @@ public void visitClass(PsiClass aClass) { return; } PsiField[] fields = aClass.getFields(); + if(fields.length == 0){ + postMetric(aClass, 0); + return; + } double numberOfPrivateFields = Arrays.stream( fields).filter( x -> x.hasModifierProperty( PsiModifier.PRIVATE)).count(); - PsiMethod[] methods = aClass.getMethods(); - double numberOfPrivateMethods = Arrays.stream( - methods).filter( - x -> x.hasModifierProperty( - PsiModifier.PRIVATE)).count(); - postMetric(aClass, (numberOfPrivateFields - + numberOfPrivateMethods) / (methods.length + fields.length)); + postMetric(aClass, numberOfPrivateFields + / fields.length); } } } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumHierarchiesProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/IsRootOfHierarchyClassCalculator.java similarity index 56% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumHierarchiesProjectCalculator.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/IsRootOfHierarchyClassCalculator.java index efa14486..bf41718e 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumHierarchiesProjectCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/IsRootOfHierarchyClassCalculator.java @@ -14,14 +14,23 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectCalculators; +package com.sixrr.stockmetrics.classCalculators; +import com.intellij.psi.JavaRecursiveElementVisitor; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.sixrr.metrics.utils.ClassUtils; -public class NumHierarchiesProjectCalculator extends ClassCountingProjectCalculator{ +public class IsRootOfHierarchyClassCalculator extends ClassCalculator { @Override - protected boolean satisfies(PsiClass aClass) { - return aClass.getExtendsListTypes().length == 0 - && aClass.getImplementsListTypes().length > 0; + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + postMetric(aClass, ClassUtils.isRoot(aClass) ? 1 : 0); + } } } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/MeasureOfAggregationProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfAggregationClassCalculator.java similarity index 89% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/MeasureOfAggregationProjectCalculator.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfAggregationClassCalculator.java index 3dce8e2b..52e09006 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/MeasureOfAggregationProjectCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfAggregationClassCalculator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectCalculators; +package com.sixrr.stockmetrics.classCalculators; import com.intellij.ide.highlighter.JavaFileType; import com.intellij.openapi.progress.ProgressManager; @@ -24,6 +24,7 @@ import com.intellij.psi.search.ProjectScope; import com.intellij.psi.util.PsiTypesUtil; import com.intellij.util.indexing.FileBasedIndex; +import com.sixrr.stockmetrics.projectCalculators.ElementCountProjectCalculator; import java.util.HashSet; import java.util.Objects; @@ -31,7 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class MeasureOfAggregationProjectCalculator extends ElementCountProjectCalculator { +public class MeasureOfAggregationClassCalculator extends ClassCalculator { private Set userDefinedClasses = new HashSet<>(); @Override @@ -43,7 +44,7 @@ protected PsiElementVisitor createVisitor() { private class Visitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { - incrementCount((int)Stream.of(aClass.getFields()).map(PsiVariable::getType). + postMetric(aClass, (int)Stream.of(aClass.getFields()).map(PsiVariable::getType). map(PsiTypesUtil::getPsiClass).filter(Objects::nonNull). filter(x -> userDefinedClasses.contains(x)).count()); } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/AverageNumOfAncestorsProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumAncestorsClassCalculator.java similarity index 74% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/AverageNumOfAncestorsProjectCalculator.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumAncestorsClassCalculator.java index a06ef0f9..e8b9874d 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/AverageNumOfAncestorsProjectCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumAncestorsClassCalculator.java @@ -14,23 +14,18 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectCalculators; +package com.sixrr.stockmetrics.classCalculators; import com.intellij.psi.JavaRecursiveElementVisitor; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiMethod; import com.sixrr.metrics.utils.MethodUtils; +import com.sixrr.stockmetrics.projectCalculators.ProjectCalculator; import com.sixrr.stockmetrics.utils.CyclomaticComplexityUtil; -public class AverageNumOfAncestorsProjectCalculator extends ProjectCalculator { - private int totalNumOfAncestors = 0; - private int numOfClasses = 0; +public class NumAncestorsClassCalculator extends ClassCalculator { - @Override - public void endMetricsRun() { - postMetric(totalNumOfAncestors, numOfClasses); - } @Override protected PsiElementVisitor createVisitor() { @@ -41,8 +36,7 @@ private class Visitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { - totalNumOfAncestors += aClass.getExtendsListTypes().length; - numOfClasses++; + postMetric(aClass, aClass.getExtendsListTypes().length); } } } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumPolymorphicMethodsProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPolymorphicMethodsProjectCalculator.java similarity index 80% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumPolymorphicMethodsProjectCalculator.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPolymorphicMethodsProjectCalculator.java index 83cbf52b..9db3b59b 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/NumPolymorphicMethodsProjectCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPolymorphicMethodsProjectCalculator.java @@ -14,15 +14,16 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectCalculators; +package com.sixrr.stockmetrics.classCalculators; import com.intellij.psi.JavaRecursiveElementVisitor; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElementVisitor; +import com.sixrr.stockmetrics.projectCalculators.ElementCountProjectCalculator; import java.util.stream.Stream; -public class NumPolymorphicMethodsProjectCalculator extends ElementCountProjectCalculator { +public class NumPolymorphicMethodsProjectCalculator extends ClassCalculator { @Override protected PsiElementVisitor createVisitor() { return new Visitor(); @@ -31,7 +32,7 @@ protected PsiElementVisitor createVisitor() { private class Visitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { - incrementCount((int) Stream.of(aClass.getMethods()). + postMetric(aClass, (int) Stream.of(aClass.getMethods()). filter(x -> x.findSuperMethods().length != 0).count()); } } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumHierarchiesProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/IsRootOfHierarchyClassMetric.java similarity index 82% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumHierarchiesProjectMetric.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/IsRootOfHierarchyClassMetric.java index 6d56ed89..678cf8cc 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumHierarchiesProjectMetric.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/IsRootOfHierarchyClassMetric.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectMetrics; +package com.sixrr.stockmetrics.classMetrics; import com.sixrr.metrics.MetricCalculator; import com.sixrr.metrics.MetricType; -import com.sixrr.stockmetrics.projectCalculators.NumHierarchiesProjectCalculator; +import com.sixrr.stockmetrics.classCalculators.IsRootOfHierarchyClassCalculator; import org.jetbrains.annotations.NotNull; -public class NumHierarchiesProjectMetric extends ProjectMetric { +public class IsRootOfHierarchyClassMetric extends ClassMetric { @NotNull @Override @@ -44,6 +44,6 @@ public MetricType getType() { @NotNull @Override public MetricCalculator createCalculator() { - return new NumHierarchiesProjectCalculator(); + return new IsRootOfHierarchyClassCalculator(); } } \ No newline at end of file diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/MeasureOfAggregationProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfAggregationClassMetric.java similarity index 87% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/MeasureOfAggregationProjectMetric.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfAggregationClassMetric.java index 65825378..b25d3c52 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/MeasureOfAggregationProjectMetric.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfAggregationClassMetric.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectMetrics; +package com.sixrr.stockmetrics.classMetrics; import com.sixrr.metrics.MetricCalculator; import com.sixrr.metrics.MetricType; -import com.sixrr.stockmetrics.projectCalculators.MeasureOfAggregationProjectCalculator; +import com.sixrr.stockmetrics.classCalculators.MeasureOfAggregationClassCalculator; import org.jetbrains.annotations.NotNull; -public class MeasureOfAggregationProjectMetric extends ProjectMetric{ +public class MeasureOfAggregationClassMetric extends ClassMetric { /** * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category * @@ -63,6 +63,6 @@ public MetricType getType() { @NotNull @Override public MetricCalculator createCalculator() { - return new MeasureOfAggregationProjectCalculator(); + return new MeasureOfAggregationClassCalculator(); } } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/AverageNumOfAncestorsProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumAncestorsClassMetric.java similarity index 83% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/AverageNumOfAncestorsProjectMetric.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumAncestorsClassMetric.java index 25b90d59..b3bf3fc1 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/AverageNumOfAncestorsProjectMetric.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumAncestorsClassMetric.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectMetrics; +package com.sixrr.stockmetrics.classMetrics; import com.sixrr.metrics.MetricCalculator; import com.sixrr.metrics.MetricType; -import com.sixrr.stockmetrics.projectCalculators.AverageNumOfAncestorsProjectCalculator; +import com.sixrr.stockmetrics.classCalculators.NumAncestorsClassCalculator; import org.jetbrains.annotations.NotNull; -public class AverageNumOfAncestorsProjectMetric extends ProjectMetric { +public class NumAncestorsClassMetric extends ClassMetric { /** * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category * @@ -30,7 +30,7 @@ public class AverageNumOfAncestorsProjectMetric extends ProjectMetric { @NotNull @Override public String getDisplayName() { - return "Average number of ancestors"; + return "Number of ancestors"; } /** @@ -41,7 +41,7 @@ public String getDisplayName() { @NotNull @Override public String getAbbreviation() { - return "ANA"; + return "NOA"; } /** @@ -52,7 +52,7 @@ public String getAbbreviation() { @NotNull @Override public MetricType getType() { - return MetricType.Average; + return MetricType.Count; } /** @@ -63,6 +63,6 @@ public MetricType getType() { @NotNull @Override public MetricCalculator createCalculator() { - return new AverageNumOfAncestorsProjectCalculator(); + return new NumAncestorsClassCalculator(); } } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java index 80b52a37..e3fdba57 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java @@ -18,7 +18,7 @@ import com.sixrr.metrics.MetricCalculator; import com.sixrr.metrics.MetricType; -import com.sixrr.stockmetrics.projectCalculators.NumPolymorphicMethodsProjectCalculator; +import com.sixrr.stockmetrics.classCalculators.NumPolymorphicMethodsProjectCalculator; import org.jetbrains.annotations.NotNull; public class NumPolymorphicMethodsProjectMetric extends ProjectMetric { From 9f22feb7c954f87dc3c9415edbe81bb95a49913d Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Sat, 28 Apr 2018 00:50:34 +0300 Subject: [PATCH 08/15] changed DirectClassCouplingProjectMetric to DirectClassCouplingMetric (ClassMetric) --- .../algorithm/entity/QMoveClassEntity.java | 2 +- .../com/sixrr/stockmetrics/JavaMetricProvider.java | 2 +- .../DirectClassCouplingCalculator.java} | 14 ++++---------- .../DirectClassCouplingMetric.java} | 10 +++++----- 4 files changed, 11 insertions(+), 17 deletions(-) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectCalculators/DirectClassCouplingProjectCalculator.java => classCalculators/DirectClassCouplingCalculator.java} (85%) rename stockmetrics/src/main/java/com/sixrr/stockmetrics/{projectMetrics/DirectClassCouplingProjectMetric.java => classMetrics/DirectClassCouplingMetric.java} (86%) diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index 54f0ab6b..f1d36190 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -49,7 +49,7 @@ public class QMoveClassEntity extends ClassEntity { .addMetricDependence(NumAncestorsClassMetric.class) //Abstraction 3 .addMetricDependence(IsRootOfHierarchyClassMetric.class) //Hierarchies 4 .addMetricDependence(DataAccessClassMetric.class) //Encapsulation 5 - .addMetricDependence(DirectClassCouplingProjectMetric.class) //Coupling 6 + .addMetricDependence(DirectClassCouplingMetric.class) //Coupling 6 .addMetricDependence(NumPublicMethodsClassMetric.class) //Messaging 7 .addMetricDependence(CohesionAmongMethodsOfClassMetric.class) //Cohesion 8 .addMetricDependence(MeasureOfAggregationClassMetric.class); //Composition 9 diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java index 70c0bab5..8900df52 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -359,7 +359,7 @@ private static void initializeProjectMetrics(Collection metrics) { metrics.add(new NumPolymorphicMethodsProjectMetric()); metrics.add(new AverageCyclomaticComplexityProjectMetric()); metrics.add(new MeasureOfFunctionalAbstractionMetric()); - metrics.add(new DirectClassCouplingProjectMetric()); + metrics.add(new DirectClassCouplingMetric()); } @NotNull diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/DirectClassCouplingProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DirectClassCouplingCalculator.java similarity index 85% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/DirectClassCouplingProjectCalculator.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DirectClassCouplingCalculator.java index 0b52eaeb..4ad81426 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectCalculators/DirectClassCouplingProjectCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DirectClassCouplingCalculator.java @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectCalculators; +package com.sixrr.stockmetrics.classCalculators; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; +import com.sixrr.stockmetrics.projectCalculators.ProjectCalculator; import java.util.HashSet; import java.util.Set; -public class DirectClassCouplingProjectCalculator extends ProjectCalculator { - private int numClasses; - private int totalNumClassesDependencies; +public class DirectClassCouplingCalculator extends ClassCalculator { @Override protected PsiElementVisitor createVisitor() { return new Visitor(); @@ -33,7 +32,6 @@ protected PsiElementVisitor createVisitor() { private class Visitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { - numClasses++; Set classes = new HashSet<>(); PsiField[] fields = aClass.getFields(); for (PsiField field : fields) { @@ -63,12 +61,8 @@ public void visitClass(PsiClass aClass) { classes.add(classInType); } } - totalNumClassesDependencies += classes.size(); + postMetric(aClass, classes.size()); } } - @Override - public void endMetricsRun() { - postMetric(totalNumClassesDependencies, numClasses); - } } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/DirectClassCouplingProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DirectClassCouplingMetric.java similarity index 86% rename from stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/DirectClassCouplingProjectMetric.java rename to stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DirectClassCouplingMetric.java index 9577e57d..d33bca66 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/DirectClassCouplingProjectMetric.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DirectClassCouplingMetric.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.sixrr.stockmetrics.projectMetrics; +package com.sixrr.stockmetrics.classMetrics; import com.sixrr.metrics.MetricCalculator; import com.sixrr.metrics.MetricType; -import com.sixrr.stockmetrics.projectCalculators.DirectClassCouplingProjectCalculator; +import com.sixrr.stockmetrics.classCalculators.DirectClassCouplingCalculator; import org.jetbrains.annotations.NotNull; -public class DirectClassCouplingProjectMetric extends ProjectMetric{ +public class DirectClassCouplingMetric extends ClassMetric { /** * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category * @@ -52,7 +52,7 @@ public String getAbbreviation() { @NotNull @Override public MetricType getType() { - return MetricType.Average; + return MetricType.Count; } /** @@ -63,6 +63,6 @@ public MetricType getType() { @NotNull @Override public MetricCalculator createCalculator() { - return new DirectClassCouplingProjectCalculator(); + return new DirectClassCouplingCalculator(); } } From 64dc1c8d096958191efe1c3a2a50f9db8a6f4207 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Tue, 15 May 2018 23:28:15 +0300 Subject: [PATCH 09/15] trying to emulate method move from one class to another and recalculate metrics --- .../org/ml_methods_group/algorithm/QMove.java | 63 +++++++-- .../algorithm/entity/Entity.java | 4 +- .../algorithm/entity/QMoveClassEntity.java | 128 +++++++++++++----- .../entity/QMoveEntitySearchResult.java | 12 +- .../algorithm/entity/QMoveEntitySearcher.java | 8 +- .../algorithm/entity/QMoveMethodEntity.java | 70 ++++++++++ .../utils/RefactoringUtil.java | 4 +- .../DataAccessClassCalculator.java | 2 +- .../NumPublicMethodsClassCalculator.java | 2 +- 9 files changed, 231 insertions(+), 62 deletions(-) create mode 100644 src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index a01e3c72..e27f914b 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -17,18 +17,25 @@ package org.ml_methods_group.algorithm; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiVariable; +import com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodDialog; +import com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodHandler; +import com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodProcessor; +import com.intellij.util.VisibilityUtil; +import org.jetbrains.annotations.NotNull; import org.ml_methods_group.algorithm.entity.*; -import org.ml_methods_group.config.Logging; -import org.ml_methods_group.utils.AlgorithmsUtil; +import org.ml_methods_group.utils.RefactoringUtil; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Stream; +import static com.intellij.testFramework.LightPlatformTestCase.getProject; +import static org.ml_methods_group.utils.PsiSearchUtil.getHumanReadableName; + public class QMove extends Algorithm { - private List methodEntities = new ArrayList<>(); + private List methodEntities = new ArrayList<>(); private final List classes = new ArrayList<>(); private final Map qMoveClassesByName = new HashMap<>(); private double complexity; @@ -42,6 +49,7 @@ public class QMove extends Algorithm { private double composition; private double inheritance; private double size; + private QMoveEntitySearchResult searchResult; public QMove() { super("Quality-orientated Move Method Refactoring", true); } @@ -49,44 +57,52 @@ public QMove() { @Override protected List calculateRefactorings( ExecutionContext context, boolean enableFieldRefactorings) throws Exception { - final QMoveEntitySearchResult searchResult = (QMoveEntitySearchResult)context.getEntities(); + searchResult = (QMoveEntitySearchResult)context.getEntities(); methodEntities.clear(); classes.clear(); Stream.of(searchResult.getMethods()) .flatMap(List::stream) .filter(Entity::isMovable) + .map(x -> (QMoveMethodEntity)x) .forEach(methodEntities::add); - searchResult.getqMoveClasses() + searchResult.getClasses() .stream() + .map(x -> (QMoveClassEntity)x) .peek(entity -> qMoveClassesByName.put(entity.getName(), entity)) .forEach(classes::add); ArrayList refactorings = new ArrayList<>(); - for(MethodEntity methodEntity : methodEntities){ + + for(QMoveMethodEntity methodEntity : methodEntities){ findBestMoveForMethod(methodEntity, refactorings); } + for(Refactoring refactoring : refactorings){ + System.err.println(refactoring.getTarget() + + " " + refactoring.getUnit() + " " + refactoring.getAccuracy()); + } return refactorings; } - private List findBestMoveForMethod(MethodEntity method, + private List findBestMoveForMethod(QMoveMethodEntity method, List refactorings){ double bestFitness = fitness(); QMoveClassEntity targetForThisMethod = null; for(QMoveClassEntity targetClass : classes){ QMoveClassEntity containingClass = qMoveClassesByName.get( method.getClassName()); - containingClass.removeFromClass(method.getName()); - targetClass.addMethod(method.getName()); + if(containingClass.equals(targetClass)){ + continue; + } + moveMethod(method.getPsiMethod(), targetClass, containingClass); double newFitness = fitness(); if(newFitness > bestFitness){ bestFitness = newFitness; targetForThisMethod = targetClass; } - targetClass.removeFromClass(method.getName()); - containingClass.addToClass(method.getName()); + moveMethod(method.getPsiMethod(), containingClass, targetClass); double fitness = fitness(); } if(targetForThisMethod != null){ @@ -147,4 +163,21 @@ private double fitness(){ return reusability() + flexibility() + understandability() + functionality() + extendibility() + effectiveness(); } + + private void moveMethod(PsiMethod method, QMoveClassEntity target, QMoveClassEntity containingClass){ + containingClass.removeMethod(method); + target.addMethod(method); + for(QMoveClassEntity entity : classes){ + if(entity.getName().equals(target.getName()) || + entity.getName().equals(containingClass.getName())){ + continue; + } + if(entity.getPsiClass().isInheritor(target.getPsiClass(), true)){ + entity.recalculateInheritance(true); + } + if(entity.getPsiClass().isInheritor(containingClass.getPsiClass(), true)){ + entity.recalculateInheritance(false); + } + } + } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java b/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java index afa2eb8b..6ef8e9fe 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java @@ -58,8 +58,8 @@ public abstract class Entity { protected boolean isMovable = true; public Entity(PsiElement element) { - this.name = PsiSearchUtil.getHumanReadableName(element); - relevantProperties = new RelevantProperties(); + this.name = PsiSearchUtil.getHumanReadableName(element); + relevantProperties = new RelevantProperties(); } protected Entity(Entity original) { diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index f1d36190..c65c0443 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -16,21 +16,26 @@ package org.ml_methods_group.algorithm.entity; -import com.intellij.psi.PsiClass; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; import com.sixrr.metrics.Metric; import com.sixrr.metrics.MetricCategory; import com.sixrr.metrics.metricModel.MetricsRun; import com.sixrr.stockmetrics.classMetrics.*; import com.sixrr.stockmetrics.projectMetrics.*; import org.jetbrains.annotations.NotNull; +import org.ml_methods_group.utils.PsiSearchUtil; import java.util.HashSet; +import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; public class QMoveClassEntity extends ClassEntity { + static Set userDefinedClasses = new HashSet<>(); private double complexity; private double polymorphism; private double abstraction; @@ -42,10 +47,14 @@ public class QMoveClassEntity extends ClassEntity { private double composition; private double inheritance; + private int numOfAllMethods; + private PsiClass psiClass; + private Map methods; + private static final VectorCalculator QMOOD_CALCULATOR = new VectorCalculator() .addMetricDependence(NumMethodsClassMetric.class) //Complexity 0 - .addMetricDependence(MeasureOfFunctionalAbstractionMetric.class) //Inheritance 1 - .addMetricDependence(NumPolymorphicMethodsProjectMetric.class) //Polymorphism 2 + .addMetricDependence(NumOperationsInheritedMetric.class) //Inheritance 1 + .addMetricDependence(NumOperationsOverriddenMetric.class) //Polymorphism 2 .addMetricDependence(NumAncestorsClassMetric.class) //Abstraction 3 .addMetricDependence(IsRootOfHierarchyClassMetric.class) //Hierarchies 4 .addMetricDependence(DataAccessClassMetric.class) //Encapsulation 5 @@ -56,9 +65,11 @@ public class QMoveClassEntity extends ClassEntity { QMoveClassEntity(PsiClass psiClass) { super(psiClass); + this.psiClass = psiClass; + methods = Stream.of(psiClass.getMethods()) + .collect(Collectors.toMap(PsiSearchUtil::getHumanReadableName, Function.identity())); } - @Override void calculateVector(MetricsRun metricsRun) { double[] vector = QMOOD_CALCULATOR.calculateVector(metricsRun, this); @@ -72,6 +83,7 @@ void calculateVector(MetricsRun metricsRun) { cohesion = vector[8]; composition = vector[9]; inheritance = vector[1]; + numOfAllMethods = psiClass.getAllMethods().length; } @Override @@ -128,7 +140,10 @@ public double getEncapsulation() { } public double getInheritance() { - return inheritance; + if(numOfAllMethods == 0){ + return 0; + } + return inheritance / numOfAllMethods; } public double getMessaging() { @@ -143,47 +158,81 @@ public static Set> getRequestedMetrics() { return QMOOD_CALCULATOR.getRequestedMetrics(); } - public void addMethod(String method){ - getRelevantProperties().addMethod(method); + public void addMethod(PsiMethod method) { + methods.put(PsiSearchUtil.getHumanReadableName(method), method); complexity++; + if(method.hasModifierProperty(PsiModifier.PUBLIC)){ + messaging++; + } recalculateCoupling(); recalculateCohesion(); + numOfAllMethods++; } - public void removeMethod(String method){ - getRelevantProperties().removeMethod(method); + public void removeMethod(PsiMethod method){ + methods.remove(PsiSearchUtil.getHumanReadableName(method)); complexity--; + if(method.hasModifierProperty(PsiModifier.PUBLIC)){ + messaging--; + } recalculateCoupling(); recalculateCohesion(); + numOfAllMethods--; } private void recalculateCoupling() { - Set relatedClasses = new HashSet<>(); - relatedClasses.addAll( - getRelevantProperties().getFields().stream().map( - this::extractClassnameFromField) - .filter(x -> !Objects.equals(x, "")). - collect(Collectors.toSet())); - relatedClasses.addAll( - getRelevantProperties().getMethods().stream() - .flatMap(this::extractParametersFromMethod) - .filter(x -> !Objects.equals(x, "")) - .collect(Collectors.toSet())); - coupling = relatedClasses.size(); + Set classes = new HashSet<>(); + PsiField[] fields = psiClass.getFields(); + for (PsiField field : fields) { + if (!field.isPhysical()) { + continue; + } + PsiType type = field.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + classes.add(classInType); + } + for (Map.Entry method : methods.entrySet()) { + PsiParameter[] parameters = method.getValue().getParameterList().getParameters(); + for (PsiParameter parameter : parameters) { + PsiTypeElement typeElement = parameter.getTypeElement(); + if (typeElement == null) { + continue; + } + PsiType type = typeElement.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + classes.add(classInType); + } + } + coupling = classes.size(); } + private void recalculateCohesion(){ - int totalSize = - getRelevantProperties().getMethods().stream() - .flatMap(this::extractParametersFromMethod).collect( - Collectors.toSet() - ).size(); - double sum = getRelevantProperties().getMethods().stream() - .mapToDouble(x -> extractParametersFromMethod(x).collect( - Collectors.toSet() - ).size()).sum(); - cohesion = totalSize == 0 ? 0 : sum / (totalSize * - getRelevantProperties().getMethods().size()); + double sumIntersection = 0; + int numMethods = 0; + Set parameters = new HashSet<>(); + for(Map.Entry methodEntry : methods.entrySet()){ + PsiMethod method = methodEntry.getValue(); + if(method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)){ + continue; + } + numMethods++; + Set parametersInMethod = Stream.of(method.getParameterList()). + flatMap(x -> Stream.of(x.getParameters())). + map(PsiVariable::getType).collect(Collectors.toSet()); + sumIntersection += parametersInMethod.size(); + parameters.addAll(parametersInMethod); + } + if(parameters.size() == 0){ + return; + } + cohesion = sumIntersection / numMethods * parameters.size(); } private String extractClassnameFromField(String s){ @@ -197,4 +246,19 @@ private Stream extractParametersFromMethod(String s){ String params = s.substring(s.indexOf("(") + 1, s.indexOf(")")); return Stream.of(params.split(",")); } + + public void recalculateInheritance(boolean increment){ + if(increment){ + inheritance++; + numOfAllMethods++; + } + else{ + inheritance--; + numOfAllMethods--; + } + } + + public PsiClass getPsiClass() { + return psiClass; + } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java index 95f96192..4596a98c 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java @@ -16,6 +16,8 @@ package org.ml_methods_group.algorithm.entity; +import com.intellij.analysis.AnalysisScope; + import java.util.Collections; import java.util.List; import java.util.stream.Stream; @@ -23,17 +25,17 @@ public class QMoveEntitySearchResult extends EntitySearchResult { private final List methods; private final List fields; - private final List qMoveClasses; private final int propertiesCount; private final long searchTime; + private final AnalysisScope scope; public QMoveEntitySearchResult(List classes, List methods, List fields, - long searchTime, List qMoveClassEntities) { + long searchTime, AnalysisScope scope) { super(classes, methods, fields, searchTime); this.methods = methods; this.fields = fields; this.searchTime = searchTime; - qMoveClasses = qMoveClassEntities; + this.scope = scope; propertiesCount = Stream.of(classes, methods, fields) .flatMap(List::stream) .map(Entity::getRelevantProperties) @@ -41,8 +43,8 @@ public QMoveEntitySearchResult(List classes, List met .sum(); } - public List getqMoveClasses() { - return Collections.unmodifiableList(qMoveClasses); + public AnalysisScope getScope() { + return scope; } public List getMethods() { diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java index 94aea807..d7b3c75b 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -81,7 +81,6 @@ private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { final List classes = new ArrayList<>(); final List methods = new ArrayList<>(); final List fields = new ArrayList<>(); - final List qMoveClasses = new ArrayList<>(); final List validEntities = new ArrayList<>(); for (Entity entity : entities.values()) { indicator.checkCanceled(); @@ -95,7 +94,6 @@ private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { switch (entity.getCategory()) { case Class: classes.add((ClassEntity) entity); - qMoveClasses.add((QMoveClassEntity) entity); break; case Method: methods.add((MethodEntity) entity); @@ -107,10 +105,9 @@ private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { } LOGGER.info("Properties calculated"); LOGGER.info("Generated " + classes.size() + " class entities"); - LOGGER.info("Generated " + qMoveClasses.size() + "QMove class entities"); LOGGER.info("Generated " + methods.size() + " method entities"); LOGGER.info("Generated " + fields.size() + " field entities"); - return new QMoveEntitySearchResult(classes, methods, fields, System.currentTimeMillis() - startTime, qMoveClasses); + return new QMoveEntitySearchResult(classes, methods, fields, System.currentTimeMillis() - startTime, scope); } private class UnitsFinder extends JavaRecursiveElementVisitor { @@ -132,6 +129,7 @@ public void visitClass(PsiClass aClass) { return; } entities.put(aClass, new QMoveClassEntity(aClass)); + QMoveClassEntity.userDefinedClasses.add(aClass); super.visitClass(aClass); } @@ -151,7 +149,7 @@ public void visitMethod(PsiMethod method) { return; } indicator.checkCanceled(); - entities.put(method, new MethodEntity(method)); + entities.put(method, new QMoveMethodEntity(method)); super.visitMethod(method); } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java new file mode 100644 index 00000000..40844544 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java @@ -0,0 +1,70 @@ +/* + * 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.entity; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.sixrr.metrics.metricModel.MetricsRun; +import org.ml_methods_group.utils.PSIUtil; + +import java.util.HashSet; +import java.util.Set; + +public class QMoveMethodEntity extends MethodEntity { + private final PsiMethod psiMethod; + + QMoveMethodEntity(PsiMethod method) { + super(method); + this.psiMethod = method; + } + + @Override + void calculateVector(MetricsRun metricsRun) { } + + public PsiMethod getPsiMethod() { + return psiMethod; + } + + public boolean isValidMoveToClass(PsiClass targetClass){ + if(!isMovable){ + return false; + } + PsiClass containingClass = psiMethod.getContainingClass(); + if(getAllInnerClasses(targetClass, new HashSet<>()). + contains(containingClass) + || getAllInnerClasses(containingClass, new HashSet<>()). + contains(targetClass)){ + return true; + } + if(PSIUtil.getAllSupers(targetClass).contains(containingClass)){ + + } + else { + + } + return true; + } + + + private Set getAllInnerClasses(PsiClass aClass, Set innerClasses){ + innerClasses.add(aClass); + for(PsiClass psiClass : aClass.getInnerClasses()){ + getAllInnerClasses(psiClass, innerClasses); + } + return innerClasses; + } +} diff --git a/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java b/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java index 45057dcb..cbae9b9f 100755 --- a/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java +++ b/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java @@ -84,7 +84,9 @@ public static void moveRefactoring(@NotNull List refactorings, .filter(Objects::nonNull) .collect(Collectors.toList()); final Set accepted = moveMembersRefactoring(members, target, scope); - model.setAcceptedRefactorings(accepted.stream().map(m -> new Refactoring(m, PsiSearchUtil.getHumanReadableName(target), 0, true)).collect(Collectors.toSet())); + if (model != null) { + model.setAcceptedRefactorings(accepted.stream().map(m -> new Refactoring(m, PsiSearchUtil.getHumanReadableName(target), 0, true)).collect(Collectors.toSet())); + } } }); } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java index c42b987d..17dc7d78 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java @@ -34,7 +34,7 @@ public void visitClass(PsiClass aClass) { return; } PsiField[] fields = aClass.getFields(); - if(fields.length == 0){ + if (fields.length == 0) { postMetric(aClass, 0); return; } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java index cd809e3d..61b71af1 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java @@ -35,7 +35,7 @@ public void visitClass(PsiClass aClass) { } postMetric(aClass, Arrays.stream( aClass.getMethods()).filter( - x -> x.hasModifierProperty( + x -> !x.isConstructor() && x.hasModifierProperty( PsiModifier.PUBLIC)).count()); } } From 6ca0fd7bdbaf1bbf684bf45550087f6c5c7cc734 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Thu, 17 May 2018 16:50:33 +0300 Subject: [PATCH 10/15] speedified metrics recalculation --- .../org/ml_methods_group/algorithm/QMove.java | 32 ++++---- .../algorithm/entity/QMoveClassEntity.java | 77 ++++++++----------- .../algorithm/entity/QMoveMethodEntity.java | 19 ++++- .../RefactoringExecutionContext.java | 1 + .../org/ml_methods_group/utils/QMoveUtil.java | 64 +++++++++++++++ 5 files changed, 125 insertions(+), 68 deletions(-) create mode 100644 src/main/java/org/ml_methods_group/utils/QMoveUtil.java diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index e27f914b..ddc80acd 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -17,23 +17,17 @@ package org.ml_methods_group.algorithm; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiVariable; -import com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodDialog; -import com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodHandler; -import com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodProcessor; -import com.intellij.util.VisibilityUtil; -import org.jetbrains.annotations.NotNull; -import org.ml_methods_group.algorithm.entity.*; -import org.ml_methods_group.utils.RefactoringUtil; - -import java.util.*; +import org.ml_methods_group.algorithm.entity.Entity; +import org.ml_methods_group.algorithm.entity.QMoveClassEntity; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearchResult; +import org.ml_methods_group.algorithm.entity.QMoveMethodEntity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.stream.Stream; -import static com.intellij.testFramework.LightPlatformTestCase.getProject; -import static org.ml_methods_group.utils.PsiSearchUtil.getHumanReadableName; - public class QMove extends Algorithm { private List methodEntities = new ArrayList<>(); private final List classes = new ArrayList<>(); @@ -51,7 +45,7 @@ public class QMove extends Algorithm { private double size; private QMoveEntitySearchResult searchResult; public QMove() { - super("Quality-orientated Move Method Refactoring", true); + super("QMove", true); } @Override @@ -96,13 +90,13 @@ private List findBestMoveForMethod(QMoveMethodEntity method, if(containingClass.equals(targetClass)){ continue; } - moveMethod(method.getPsiMethod(), targetClass, containingClass); + moveMethod(method, targetClass, containingClass); double newFitness = fitness(); if(newFitness > bestFitness){ bestFitness = newFitness; targetForThisMethod = targetClass; } - moveMethod(method.getPsiMethod(), containingClass, targetClass); + moveMethod(method, containingClass, targetClass); double fitness = fitness(); } if(targetForThisMethod != null){ @@ -164,7 +158,7 @@ private double fitness(){ functionality() + extendibility() + effectiveness(); } - private void moveMethod(PsiMethod method, QMoveClassEntity target, QMoveClassEntity containingClass){ + private void moveMethod(QMoveMethodEntity method, QMoveClassEntity target, QMoveClassEntity containingClass){ containingClass.removeMethod(method); target.addMethod(method); for(QMoveClassEntity entity : classes){ diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index c65c0443..ee4fabbe 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -22,14 +22,10 @@ import com.sixrr.metrics.MetricCategory; import com.sixrr.metrics.metricModel.MetricsRun; import com.sixrr.stockmetrics.classMetrics.*; -import com.sixrr.stockmetrics.projectMetrics.*; -import org.jetbrains.annotations.NotNull; import org.ml_methods_group.utils.PsiSearchUtil; +import org.ml_methods_group.utils.QMoveUtil; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -50,6 +46,7 @@ public class QMoveClassEntity extends ClassEntity { private int numOfAllMethods; private PsiClass psiClass; private Map methods; + private Map relatedClasses = new HashMap<>(); private static final VectorCalculator QMOOD_CALCULATOR = new VectorCalculator() .addMetricDependence(NumMethodsClassMetric.class) //Complexity 0 @@ -58,7 +55,7 @@ public class QMoveClassEntity extends ClassEntity { .addMetricDependence(NumAncestorsClassMetric.class) //Abstraction 3 .addMetricDependence(IsRootOfHierarchyClassMetric.class) //Hierarchies 4 .addMetricDependence(DataAccessClassMetric.class) //Encapsulation 5 - .addMetricDependence(DirectClassCouplingMetric.class) //Coupling 6 + //.addMetricDependence(DirectClassCouplingMetric.class) //Coupling 6 .addMetricDependence(NumPublicMethodsClassMetric.class) //Messaging 7 .addMetricDependence(CohesionAmongMethodsOfClassMetric.class) //Cohesion 8 .addMetricDependence(MeasureOfAggregationClassMetric.class); //Composition 9 @@ -78,12 +75,12 @@ void calculateVector(MetricsRun metricsRun) { abstraction = vector[3]; hierarchies = vector[4]; encapsulation = vector[5]; - coupling = vector[6]; - messaging = vector[7]; - cohesion = vector[8]; - composition = vector[9]; + messaging = vector[6]; + cohesion = vector[7]; + composition = vector[8]; inheritance = vector[1]; numOfAllMethods = psiClass.getAllMethods().length; + coupling = calculateRelatedClasses(); } @Override @@ -158,30 +155,41 @@ public static Set> getRequestedMetrics() { return QMOOD_CALCULATOR.getRequestedMetrics(); } - public void addMethod(PsiMethod method) { - methods.put(PsiSearchUtil.getHumanReadableName(method), method); + public void addMethod(QMoveMethodEntity entity) { + PsiMethod method = entity.getPsiMethod(); + methods.put(entity.getName(), method); complexity++; if(method.hasModifierProperty(PsiModifier.PUBLIC)){ messaging++; } - recalculateCoupling(); + recalculateCoupling(entity, true); recalculateCohesion(); numOfAllMethods++; } - public void removeMethod(PsiMethod method){ - methods.remove(PsiSearchUtil.getHumanReadableName(method)); + public void removeMethod(QMoveMethodEntity methodEntity){ + methods.remove(methodEntity.getName()); complexity--; + PsiMethod method = methodEntity.getPsiMethod(); if(method.hasModifierProperty(PsiModifier.PUBLIC)){ messaging--; } - recalculateCoupling(); + recalculateCoupling(methodEntity, false); recalculateCohesion(); numOfAllMethods--; } - private void recalculateCoupling() { - Set classes = new HashSet<>(); + private void recalculateCoupling(QMoveMethodEntity entity, boolean add) { + if(add){ + QMoveUtil.addToUnion(relatedClasses, entity.getRelatedClasses()); + } + else { + QMoveUtil.removeFromUnion(relatedClasses, entity.getRelatedClasses()); + } + coupling = relatedClasses.size(); + } + + private int calculateRelatedClasses() { PsiField[] fields = psiClass.getFields(); for (PsiField field : fields) { if (!field.isPhysical()) { @@ -192,24 +200,13 @@ private void recalculateCoupling() { if (classInType == null) { continue; } - classes.add(classInType); + relatedClasses.put(classInType, + relatedClasses.getOrDefault(classInType, 0)); } for (Map.Entry method : methods.entrySet()) { - PsiParameter[] parameters = method.getValue().getParameterList().getParameters(); - for (PsiParameter parameter : parameters) { - PsiTypeElement typeElement = parameter.getTypeElement(); - if (typeElement == null) { - continue; - } - PsiType type = typeElement.getType().getDeepComponentType(); - PsiClass classInType = PsiUtil.resolveClassInType(type); - if (classInType == null) { - continue; - } - classes.add(classInType); - } + QMoveUtil.calculateRelatedClasses(method.getValue(), relatedClasses); } - coupling = classes.size(); + return relatedClasses.size(); } @@ -235,18 +232,6 @@ private void recalculateCohesion(){ cohesion = sumIntersection / numMethods * parameters.size(); } - private String extractClassnameFromField(String s){ - String[] strings = s.split("\\."); - assert strings.length > 1; - return strings[strings.length - 2]; - } - - @NotNull - private Stream extractParametersFromMethod(String s){ - String params = s.substring(s.indexOf("(") + 1, s.indexOf(")")); - return Stream.of(params.split(",")); - } - public void recalculateInheritance(boolean increment){ if(increment){ inheritance++; diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java index 40844544..143ac1d0 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java @@ -16,16 +16,23 @@ package org.ml_methods_group.algorithm.entity; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; import com.sixrr.metrics.metricModel.MetricsRun; import org.ml_methods_group.utils.PSIUtil; +import org.ml_methods_group.utils.QMoveUtil; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; public class QMoveMethodEntity extends MethodEntity { private final PsiMethod psiMethod; + private boolean containsPrivateCalls; + private boolean isContainsOnlyPublicCalls; + + private Map relatedClasses = new HashMap<>(); QMoveMethodEntity(PsiMethod method) { super(method); @@ -33,7 +40,9 @@ public class QMoveMethodEntity extends MethodEntity { } @Override - void calculateVector(MetricsRun metricsRun) { } + void calculateVector(MetricsRun metricsRun) { + QMoveUtil.calculateRelatedClasses(psiMethod, relatedClasses); + } public PsiMethod getPsiMethod() { return psiMethod; @@ -67,4 +76,8 @@ private Set getAllInnerClasses(PsiClass aClass, Set innerCla } return innerClasses; } + + Map getRelatedClasses() { + return relatedClasses; + } } 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 772f6779..ea7e9e3b 100644 --- a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java +++ b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java @@ -120,6 +120,7 @@ private void execute(ProgressIndicator indicator) { -> QMoveEntitySearcher.analyze(scope, metricsRun)); final Algorithm qMoveAlgorithm = new QMove(); final AlgorithmResult result = qMoveAlgorithm.execute(qMoveEntitySearchResult, executorService, isFieldRefactoringAvailable); + algorithmsResults.add(result); } else{ calculateAlgorithmForName(algorithm); diff --git a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java new file mode 100644 index 00000000..b9eabaa0 --- /dev/null +++ b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java @@ -0,0 +1,64 @@ +/* + * 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.utils; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; + +import java.util.Map; + +public class QMoveUtil { + public static void calculateRelatedClasses(PsiMethod method, Map relatedClasses){ + PsiParameter[] parameters = method.getParameterList().getParameters(); + for (PsiParameter parameter : parameters) { + PsiTypeElement typeElement = parameter.getTypeElement(); + if (typeElement == null) { + continue; + } + PsiType type = typeElement.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + relatedClasses.put(classInType, + relatedClasses.getOrDefault(classInType, 0) + 1); + } + } + + public static void removeFromUnion(Map union, Map set){ + for(Map.Entry item : set.entrySet()){ + int remove = item.getValue(); + T key = item.getKey(); + int present = union.get(key); + if(remove == present){ + union.remove(key); + } + else { + union.put(key, present - remove); + } + } + } + + public static void addToUnion(Map union, Map set){ + for(Map.Entry item : set.entrySet()){ + int add = item.getValue(); + T key = item.getKey(); + int present = union.getOrDefault(key, 0); + union.put(key, present + add); + } + } +} From ce8c9c166b578395864a3206b503cebb64d2b043 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Sun, 20 May 2018 01:40:18 +0300 Subject: [PATCH 11/15] cohesion recalculated faster --- .../org/ml_methods_group/algorithm/QMove.java | 21 ++++- .../algorithm/entity/QMoveClassEntity.java | 81 +++++++++++++------ .../algorithm/entity/QMoveEntitySearcher.java | 10 ++- .../algorithm/entity/QMoveMethodEntity.java | 10 ++- .../org/ml_methods_group/utils/QMoveUtil.java | 17 +++- 5 files changed, 107 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index ddc80acd..b2ac56b4 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -65,7 +65,7 @@ protected List calculateRefactorings( .map(x -> (QMoveClassEntity)x) .peek(entity -> qMoveClassesByName.put(entity.getName(), entity)) .forEach(classes::add); - + calculateMetrics(); ArrayList refactorings = new ArrayList<>(); for(QMoveMethodEntity methodEntity : methodEntities){ @@ -82,7 +82,8 @@ protected List calculateRefactorings( private List findBestMoveForMethod(QMoveMethodEntity method, List refactorings){ - double bestFitness = fitness(); + double initFitness = fitness(); + double bestFitness = initFitness; QMoveClassEntity targetForThisMethod = null; for(QMoveClassEntity targetClass : classes){ QMoveClassEntity containingClass = qMoveClassesByName.get( @@ -98,6 +99,9 @@ private List findBestMoveForMethod(QMoveMethodEntity method, } moveMethod(method, containingClass, targetClass); double fitness = fitness(); + if(fitness != initFitness){ + System.err.println("ERROR"); + } } if(targetForThisMethod != null){ refactorings.add(new Refactoring(method.getName(), @@ -121,6 +125,17 @@ private void calculateMetrics(){ inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); } + private void recalculateMetrics(){ + double coup = coupling; + coupling = classes.stream().mapToDouble(QMoveClassEntity::getCoupling).sum(); + double mess = messaging; + messaging = classes.stream().mapToDouble(QMoveClassEntity::getMessaging).sum(); + double coh = cohesion; + cohesion = classes.stream().mapToDouble(QMoveClassEntity::getCohesion).sum(); + double inh = inheritance; + inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); + } + private double reusability() { return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging + 0.5 * size; @@ -153,7 +168,7 @@ private double effectiveness() { } private double fitness(){ - calculateMetrics(); + recalculateMetrics(); return reusability() + flexibility() + understandability() + functionality() + extendibility() + effectiveness(); } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index ee4fabbe..d51235bd 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -31,7 +31,7 @@ import java.util.stream.Stream; public class QMoveClassEntity extends ClassEntity { - static Set userDefinedClasses = new HashSet<>(); + static Map> allNoneStaticMethods = new HashMap<>(); private double complexity; private double polymorphism; private double abstraction; @@ -43,10 +43,13 @@ public class QMoveClassEntity extends ClassEntity { private double composition; private double inheritance; - private int numOfAllMethods; + private double numOfAllMethods; + private int numOfNoneStaticMethods; private PsiClass psiClass; + private double sumIntersection; private Map methods; private Map relatedClasses = new HashMap<>(); + private Map parametersOfMethods = new HashMap<>(); private static final VectorCalculator QMOOD_CALCULATOR = new VectorCalculator() .addMetricDependence(NumMethodsClassMetric.class) //Complexity 0 @@ -63,8 +66,6 @@ public class QMoveClassEntity extends ClassEntity { QMoveClassEntity(PsiClass psiClass) { super(psiClass); this.psiClass = psiClass; - methods = Stream.of(psiClass.getMethods()) - .collect(Collectors.toMap(PsiSearchUtil::getHumanReadableName, Function.identity())); } @Override @@ -79,8 +80,14 @@ void calculateVector(MetricsRun metricsRun) { cohesion = vector[7]; composition = vector[8]; inheritance = vector[1]; - numOfAllMethods = psiClass.getAllMethods().length; - coupling = calculateRelatedClasses(); + + numOfAllMethods = inheritance + complexity; + + Stream.of(psiClass.getAllMethods()).forEach(x -> System.out.println(x.getName())); + methods = Stream.of(psiClass.getMethods()).collect( + Collectors.toMap(PsiSearchUtil::getHumanReadableName, Function.identity()) + ); + coupling = calculateCoupling(); } @Override @@ -117,6 +124,12 @@ public double getAbstraction() { } public double getCohesion() { + if(parametersOfMethods.isEmpty()){ + double coh = calculateCohesion(); + if(coh != cohesion){ + System.err.println("Wrong cohesion"); + } + } return cohesion; } @@ -163,10 +176,31 @@ public void addMethod(QMoveMethodEntity entity) { messaging++; } recalculateCoupling(entity, true); - recalculateCohesion(); + recalculateCohesion(entity, true); numOfAllMethods++; } + private void recalculateCohesion(QMoveMethodEntity entity, boolean add) { + if(entity.getPsiMethod().hasModifierProperty(PsiModifier.STATIC)){ + return; + } + if(add){ + numOfNoneStaticMethods++; + sumIntersection += entity.getParameters().size(); + QMoveUtil.addToUnion(parametersOfMethods, entity.getParameters()); + } + else { + numOfNoneStaticMethods--; + sumIntersection -= entity.getParameters().size(); + QMoveUtil.removeFromUnion(parametersOfMethods, entity.getParameters()); + } + if(parametersOfMethods.size() == 0){ + cohesion = 0; + return; + } + cohesion = sumIntersection / numOfNoneStaticMethods * parametersOfMethods.size(); + } + public void removeMethod(QMoveMethodEntity methodEntity){ methods.remove(methodEntity.getName()); complexity--; @@ -175,7 +209,7 @@ public void removeMethod(QMoveMethodEntity methodEntity){ messaging--; } recalculateCoupling(methodEntity, false); - recalculateCohesion(); + recalculateCohesion(methodEntity, false); numOfAllMethods--; } @@ -189,7 +223,8 @@ private void recalculateCoupling(QMoveMethodEntity entity, boolean add) { coupling = relatedClasses.size(); } - private int calculateRelatedClasses() { + private int calculateCoupling() { + QMoveUtil.incrementMapValue(psiClass, relatedClasses); PsiField[] fields = psiClass.getFields(); for (PsiField field : fields) { if (!field.isPhysical()) { @@ -200,8 +235,7 @@ private int calculateRelatedClasses() { if (classInType == null) { continue; } - relatedClasses.put(classInType, - relatedClasses.getOrDefault(classInType, 0)); + QMoveUtil.incrementMapValue(classInType, relatedClasses); } for (Map.Entry method : methods.entrySet()) { QMoveUtil.calculateRelatedClasses(method.getValue(), relatedClasses); @@ -210,26 +244,25 @@ private int calculateRelatedClasses() { } - private void recalculateCohesion(){ - double sumIntersection = 0; - int numMethods = 0; - Set parameters = new HashSet<>(); + private double calculateCohesion(){ + sumIntersection = allNoneStaticMethods.entrySet().stream() + .filter(x -> x.getKey().equals(psiClass)) + .map(Map.Entry::getValue) + .flatMap(List::stream) + .peek(x -> System.err.println(x.getName())) + .mapToInt(x -> x.getParameters().size()).sum(); for(Map.Entry methodEntry : methods.entrySet()){ PsiMethod method = methodEntry.getValue(); if(method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)){ continue; } - numMethods++; - Set parametersInMethod = Stream.of(method.getParameterList()). - flatMap(x -> Stream.of(x.getParameters())). - map(PsiVariable::getType).collect(Collectors.toSet()); - sumIntersection += parametersInMethod.size(); - parameters.addAll(parametersInMethod); + QMoveUtil.calculateMethodParameters(method, parametersOfMethods); + numOfNoneStaticMethods++; } - if(parameters.size() == 0){ - return; + if(parametersOfMethods.size() == 0){ + return 0; } - cohesion = sumIntersection / numMethods * parameters.size(); + return sumIntersection / numOfNoneStaticMethods * parametersOfMethods.size(); } public void recalculateInheritance(boolean increment){ diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java index d7b3c75b..4628e6d9 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -87,6 +87,7 @@ private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { try { entity.calculateVector(metricsRun); } catch (Exception e) { + e.printStackTrace(); LOGGER.warn("Failed to calculate vector for " + entity.getName()); continue; } @@ -97,6 +98,14 @@ private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { break; case Method: methods.add((MethodEntity) entity); + if(((QMoveMethodEntity)entity).getPsiMethod().hasModifierProperty(PsiModifier.STATIC)){ + break; + } + PsiClass containingClass = ((QMoveMethodEntity)entity).getPsiMethod().getContainingClass(); + if(!QMoveClassEntity.allNoneStaticMethods.containsKey(containingClass)){ + QMoveClassEntity.allNoneStaticMethods.put(containingClass, new ArrayList<>()); + } + QMoveClassEntity.allNoneStaticMethods.get(containingClass).add((QMoveMethodEntity) entity); break; default: fields.add((FieldEntity) entity); @@ -129,7 +138,6 @@ public void visitClass(PsiClass aClass) { return; } entities.put(aClass, new QMoveClassEntity(aClass)); - QMoveClassEntity.userDefinedClasses.add(aClass); super.visitClass(aClass); } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java index 143ac1d0..a87de2cf 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java @@ -33,6 +33,8 @@ public class QMoveMethodEntity extends MethodEntity { private boolean isContainsOnlyPublicCalls; private Map relatedClasses = new HashMap<>(); + private Map parameters = new HashMap<>(); + QMoveMethodEntity(PsiMethod method) { super(method); @@ -42,12 +44,18 @@ public class QMoveMethodEntity extends MethodEntity { @Override void calculateVector(MetricsRun metricsRun) { QMoveUtil.calculateRelatedClasses(psiMethod, relatedClasses); + QMoveUtil.calculateMethodParameters(psiMethod, parameters); + //assert res == parameters.size(); } - public PsiMethod getPsiMethod() { + PsiMethod getPsiMethod() { return psiMethod; } + Map getParameters(){ + return parameters; + } + public boolean isValidMoveToClass(PsiClass targetClass){ if(!isMovable){ return false; diff --git a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java index b9eabaa0..54453d7c 100644 --- a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java +++ b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java @@ -20,6 +20,7 @@ import com.intellij.psi.util.PsiUtil; import java.util.Map; +import java.util.stream.Stream; public class QMoveUtil { public static void calculateRelatedClasses(PsiMethod method, Map relatedClasses){ @@ -34,8 +35,7 @@ public static void calculateRelatedClasses(PsiMethod method, Map void removeFromUnion(Map union, Map s public static void addToUnion(Map union, Map set){ for(Map.Entry item : set.entrySet()){ int add = item.getValue(); - T key = item.getKey(); + T key = item.getKey(); int present = union.getOrDefault(key, 0); union.put(key, present + add); } } + + public static void incrementMapValue(T t, Map map){ + map.put(t, + map.getOrDefault(t, 0) + 1); + } + + public static void calculateMethodParameters(PsiMethod method, Map parameters){ + Stream.of(method.getParameterList()). + flatMap(x -> Stream.of(x.getParameters())). + map(PsiVariable::getType).forEach(x -> incrementMapValue(x, parameters)); + } } From e66e76742c304ca30452eca22bbc4c311c052062 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Tue, 22 May 2018 12:22:17 +0300 Subject: [PATCH 12/15] method is not moved now, metrics are just recalculted if it has been moved --- .../org/ml_methods_group/algorithm/QMove.java | 65 ++++---- .../algorithm/entity/QMoveClassEntity.java | 148 +++++++----------- .../algorithm/entity/QMoveEntitySearcher.java | 12 +- .../algorithm/entity/QMoveMethodEntity.java | 1 - .../RefactoringExecutionContext.java | 3 +- .../org/ml_methods_group/utils/QMoveUtil.java | 18 ++- 6 files changed, 112 insertions(+), 135 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index b2ac56b4..a716a181 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -43,7 +43,7 @@ public class QMove extends Algorithm { private double composition; private double inheritance; private double size; - private QMoveEntitySearchResult searchResult; + public QMove() { super("QMove", true); } @@ -51,7 +51,7 @@ public QMove() { @Override protected List calculateRefactorings( ExecutionContext context, boolean enableFieldRefactorings) throws Exception { - searchResult = (QMoveEntitySearchResult)context.getEntities(); + QMoveEntitySearchResult searchResult = (QMoveEntitySearchResult) context.getEntities(); methodEntities.clear(); classes.clear(); Stream.of(searchResult.getMethods()) @@ -63,6 +63,7 @@ protected List calculateRefactorings( searchResult.getClasses() .stream() .map(x -> (QMoveClassEntity)x) + .peek(x -> x.calculateCohesion(methodEntities)) .peek(entity -> qMoveClassesByName.put(entity.getName(), entity)) .forEach(classes::add); calculateMetrics(); @@ -82,8 +83,7 @@ protected List calculateRefactorings( private List findBestMoveForMethod(QMoveMethodEntity method, List refactorings){ - double initFitness = fitness(); - double bestFitness = initFitness; + double bestFitness = fitness(); QMoveClassEntity targetForThisMethod = null; for(QMoveClassEntity targetClass : classes){ QMoveClassEntity containingClass = qMoveClassesByName.get( @@ -91,17 +91,11 @@ private List findBestMoveForMethod(QMoveMethodEntity method, if(containingClass.equals(targetClass)){ continue; } - moveMethod(method, targetClass, containingClass); - double newFitness = fitness(); + double newFitness = moveMethod(method, targetClass, containingClass); if(newFitness > bestFitness){ bestFitness = newFitness; targetForThisMethod = targetClass; } - moveMethod(method, containingClass, targetClass); - double fitness = fitness(); - if(fitness != initFitness){ - System.err.println("ERROR"); - } } if(targetForThisMethod != null){ refactorings.add(new Refactoring(method.getName(), @@ -125,17 +119,6 @@ private void calculateMetrics(){ inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); } - private void recalculateMetrics(){ - double coup = coupling; - coupling = classes.stream().mapToDouble(QMoveClassEntity::getCoupling).sum(); - double mess = messaging; - messaging = classes.stream().mapToDouble(QMoveClassEntity::getMessaging).sum(); - double coh = cohesion; - cohesion = classes.stream().mapToDouble(QMoveClassEntity::getCohesion).sum(); - double inh = inheritance; - inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); - } - private double reusability() { return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging + 0.5 * size; @@ -168,25 +151,51 @@ private double effectiveness() { } private double fitness(){ - recalculateMetrics(); return reusability() + flexibility() + understandability() + functionality() + extendibility() + effectiveness(); } - private void moveMethod(QMoveMethodEntity method, QMoveClassEntity target, QMoveClassEntity containingClass){ - containingClass.removeMethod(method); - target.addMethod(method); + private double moveMethod(QMoveMethodEntity method, + QMoveClassEntity target, + QMoveClassEntity containingClass){ + //recalculating cohesion + double removeFromCohesion = target.getCohesion(); + removeFromCohesion += containingClass.getCohesion(); + double addToCohesion = target.recalculateCohesion(method, true); + addToCohesion += containingClass.recalculateCohesion(method, false); + double oldCohesion = cohesion; + cohesion -= removeFromCohesion; + cohesion += addToCohesion; + + //recalculating coupling + double removeFromCoupling = target.getCoupling(); + removeFromCoupling += containingClass.getCoupling(); + double addToCoupling = target.recalculateCoupling(method, true); + addToCoupling += containingClass.recalculateCoupling(method, false); + double oldCoupling = coupling; + coupling -= removeFromCoupling; + coupling += addToCoupling; + + //recalculating inheritance + double oldInheritance = inheritance; for(QMoveClassEntity entity : classes){ if(entity.getName().equals(target.getName()) || entity.getName().equals(containingClass.getName())){ continue; } if(entity.getPsiClass().isInheritor(target.getPsiClass(), true)){ - entity.recalculateInheritance(true); + inheritance -= entity.getInheritance(); + inheritance += entity.recalculateInheritance(true); } if(entity.getPsiClass().isInheritor(containingClass.getPsiClass(), true)){ - entity.recalculateInheritance(false); + inheritance -= entity.getInheritance(); + inheritance += entity.recalculateInheritance(false); } } + double fitness = fitness(); + cohesion = oldCohesion; + coupling = oldCoupling; + inheritance = oldInheritance; + return fitness; } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index d51235bd..87eec4d9 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -25,7 +25,10 @@ import org.ml_methods_group.utils.PsiSearchUtil; import org.ml_methods_group.utils.QMoveUtil; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -82,8 +85,6 @@ void calculateVector(MetricsRun metricsRun) { inheritance = vector[1]; numOfAllMethods = inheritance + complexity; - - Stream.of(psiClass.getAllMethods()).forEach(x -> System.out.println(x.getName())); methods = Stream.of(psiClass.getMethods()).collect( Collectors.toMap(PsiSearchUtil::getHumanReadableName, Function.identity()) ); @@ -115,7 +116,7 @@ public void addToClass(String method) { getRelevantProperties().addMethod(method); } - public double getComplexity(){ + public double getComplexity() { return complexity; } @@ -124,12 +125,6 @@ public double getAbstraction() { } public double getCohesion() { - if(parametersOfMethods.isEmpty()){ - double coh = calculateCohesion(); - if(coh != cohesion){ - System.err.println("Wrong cohesion"); - } - } return cohesion; } @@ -150,7 +145,7 @@ public double getEncapsulation() { } public double getInheritance() { - if(numOfAllMethods == 0){ + if (numOfAllMethods == 0) { return 0; } return inheritance / numOfAllMethods; @@ -168,111 +163,82 @@ public static Set> getRequestedMetrics() { return QMOOD_CALCULATOR.getRequestedMetrics(); } - public void addMethod(QMoveMethodEntity entity) { - PsiMethod method = entity.getPsiMethod(); - methods.put(entity.getName(), method); - complexity++; - if(method.hasModifierProperty(PsiModifier.PUBLIC)){ - messaging++; + public double recalculateCohesion(QMoveMethodEntity entity, boolean add) { + if (entity.getPsiMethod().hasModifierProperty(PsiModifier.STATIC)) { + return cohesion; } - recalculateCoupling(entity, true); - recalculateCohesion(entity, true); - numOfAllMethods++; - } - - private void recalculateCohesion(QMoveMethodEntity entity, boolean add) { - if(entity.getPsiMethod().hasModifierProperty(PsiModifier.STATIC)){ - return; + int newNumOfNoneStaticMethods = numOfNoneStaticMethods; + double newSumIntersection = sumIntersection; + int union; + if (add) { + newNumOfNoneStaticMethods++; + newSumIntersection += entity.getParameters().size(); + union = QMoveUtil.addToUnion(parametersOfMethods, entity.getParameters()); + } else { + newNumOfNoneStaticMethods--; + newSumIntersection -= entity.getParameters().size(); + union = QMoveUtil.removeFromUnion(parametersOfMethods, entity.getParameters()); } - if(add){ - numOfNoneStaticMethods++; - sumIntersection += entity.getParameters().size(); - QMoveUtil.addToUnion(parametersOfMethods, entity.getParameters()); - } - else { - numOfNoneStaticMethods--; - sumIntersection -= entity.getParameters().size(); - QMoveUtil.removeFromUnion(parametersOfMethods, entity.getParameters()); - } - if(parametersOfMethods.size() == 0){ - cohesion = 0; - return; + if (union == 0) { + return 0; } - cohesion = sumIntersection / numOfNoneStaticMethods * parametersOfMethods.size(); + return newSumIntersection / (newNumOfNoneStaticMethods * union); } - public void removeMethod(QMoveMethodEntity methodEntity){ - methods.remove(methodEntity.getName()); - complexity--; - PsiMethod method = methodEntity.getPsiMethod(); - if(method.hasModifierProperty(PsiModifier.PUBLIC)){ - messaging--; - } - recalculateCoupling(methodEntity, false); - recalculateCohesion(methodEntity, false); - numOfAllMethods--; - } - private void recalculateCoupling(QMoveMethodEntity entity, boolean add) { - if(add){ - QMoveUtil.addToUnion(relatedClasses, entity.getRelatedClasses()); + public double recalculateCoupling(QMoveMethodEntity entity, boolean add) { + if (add) { + return QMoveUtil.addToUnion(relatedClasses, entity.getRelatedClasses()); + } else { + return QMoveUtil.removeFromUnion(relatedClasses, entity.getRelatedClasses()); } - else { - QMoveUtil.removeFromUnion(relatedClasses, entity.getRelatedClasses()); - } - coupling = relatedClasses.size(); } private int calculateCoupling() { QMoveUtil.incrementMapValue(psiClass, relatedClasses); - PsiField[] fields = psiClass.getFields(); - for (PsiField field : fields) { - if (!field.isPhysical()) { - continue; - } - PsiType type = field.getType().getDeepComponentType(); - PsiClass classInType = PsiUtil.resolveClassInType(type); - if (classInType == null) { - continue; - } - QMoveUtil.incrementMapValue(classInType, relatedClasses); + PsiField[] fields = psiClass.getFields(); + for (PsiField field : fields) { + if (!field.isPhysical()) { + continue; } - for (Map.Entry method : methods.entrySet()) { - QMoveUtil.calculateRelatedClasses(method.getValue(), relatedClasses); + PsiType type = field.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; } - return relatedClasses.size(); + QMoveUtil.incrementMapValue(classInType, relatedClasses); + } + for (Map.Entry method : methods.entrySet()) { + QMoveUtil.calculateRelatedClasses(method.getValue(), relatedClasses); + } + return relatedClasses.size(); } - private double calculateCohesion(){ - sumIntersection = allNoneStaticMethods.entrySet().stream() - .filter(x -> x.getKey().equals(psiClass)) - .map(Map.Entry::getValue) - .flatMap(List::stream) - .peek(x -> System.err.println(x.getName())) - .mapToInt(x -> x.getParameters().size()).sum(); - for(Map.Entry methodEntry : methods.entrySet()){ - PsiMethod method = methodEntry.getValue(); - if(method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)){ + public double calculateCohesion(List methods) { + sumIntersection = methods.stream() + .filter(x -> x.getClassName().equals(getName())) + .map(QMoveMethodEntity::getPsiMethod) + .mapToInt(x -> x.getParameterList().getParameters().length).sum(); + for (QMoveMethodEntity methodEntry : methods) { + PsiMethod method = methodEntry.getPsiMethod(); + if (method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)) { continue; } QMoveUtil.calculateMethodParameters(method, parametersOfMethods); numOfNoneStaticMethods++; } - if(parametersOfMethods.size() == 0){ + if (parametersOfMethods.size() == 0) { return 0; } - return sumIntersection / numOfNoneStaticMethods * parametersOfMethods.size(); + return sumIntersection / (numOfNoneStaticMethods * parametersOfMethods.size()); } - public void recalculateInheritance(boolean increment){ - if(increment){ - inheritance++; - numOfAllMethods++; - } - else{ - inheritance--; - numOfAllMethods--; + public double recalculateInheritance(boolean increment) { + if (increment) { + return (inheritance + 1) / (numOfAllMethods + 1); + } else { + return (inheritance - 1) / (numOfAllMethods - 1); } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java index 4628e6d9..4e70bc39 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -183,7 +183,7 @@ public void visitClass(PsiClass aClass) { super.visitClass(aClass); return; } - final RelevantProperties classProperties = entity.getRelevantProperties(); + /* final RelevantProperties classProperties = entity.getRelevantProperties(); classProperties.addClass(aClass, strategy.getWeight(aClass, aClass)); if (strategy.processSupers()) { for (PsiClass superClass : PSIUtil.getAllSupers(aClass)) { @@ -199,7 +199,7 @@ public void visitClass(PsiClass aClass) { .forEach(m -> classProperties.addMethod(m, strategy.getWeight(aClass, m))); Arrays.stream(aClass.getFields()) .filter(f -> isProperty(aClass, f)) - .forEach(f -> classProperties.addField(f, strategy.getWeight(aClass, f))); + .forEach(f -> classProperties.addField(f, strategy.getWeight(aClass, f)));*/ reportPropertiesCalculated(); super.visitClass(aClass); } @@ -222,7 +222,7 @@ public void visitMethod(PsiMethod method) { return; } - final RelevantProperties methodProperties = entity.getRelevantProperties(); + /*final RelevantProperties methodProperties = entity.getRelevantProperties(); methodProperties.addMethod(method, strategy.getWeight(method, method)); Optional.ofNullable(method.getContainingClass()) .ifPresent(c -> methodProperties.addClass(c, strategy.getWeight(method, c))); @@ -236,7 +236,7 @@ public void visitMethod(PsiMethod method) { .forEach(superMethod -> superMethod .getRelevantProperties() .addOverrideMethod(method, strategy.getWeight(superMethod, method))); - } + }*/ reportPropertiesCalculated(); super.visitMethod(method); if (currentMethod == method) { @@ -264,7 +264,7 @@ && isClassInProject(((PsiField) element).getContainingClass()) && strategy.isRel super.visitReferenceExpression(expression); } - @Override + /*@Override public void visitField(PsiField field) { indicator.checkCanceled(); final Entity entity = entities.get(field); @@ -284,7 +284,7 @@ public void visitField(PsiField field) { } reportPropertiesCalculated(); super.visitField(field); - } + }*/ @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java index a87de2cf..b1a69fe9 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java @@ -45,7 +45,6 @@ public class QMoveMethodEntity extends MethodEntity { void calculateVector(MetricsRun metricsRun) { QMoveUtil.calculateRelatedClasses(psiMethod, relatedClasses); QMoveUtil.calculateMethodParameters(psiMethod, parameters); - //assert res == parameters.size(); } PsiMethod getPsiMethod() { 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 ea7e9e3b..eb498ad7 100644 --- a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java +++ b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java @@ -119,7 +119,8 @@ private void execute(ProgressIndicator indicator) { .runReadAction((Computable) () -> QMoveEntitySearcher.analyze(scope, metricsRun)); final Algorithm qMoveAlgorithm = new QMove(); - final AlgorithmResult result = qMoveAlgorithm.execute(qMoveEntitySearchResult, executorService, isFieldRefactoringAvailable); + final AlgorithmResult result = qMoveAlgorithm + .execute(qMoveEntitySearchResult, executorService, isFieldRefactoringAvailable); algorithmsResults.add(result); } else{ diff --git a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java index 54453d7c..f9cc9327 100644 --- a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java +++ b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java @@ -39,27 +39,29 @@ public static void calculateRelatedClasses(PsiMethod method, Map void removeFromUnion(Map union, Map set){ + public static int removeFromUnion(Map union, Map set){ + int res = union.size(); for(Map.Entry item : set.entrySet()){ int remove = item.getValue(); T key = item.getKey(); int present = union.get(key); if(remove == present){ - union.remove(key); - } - else { - union.put(key, present - remove); + res--; } } + return res; } - public static void addToUnion(Map union, Map set){ + public static int addToUnion(Map union, Map set){ + int res = union.size(); for(Map.Entry item : set.entrySet()){ int add = item.getValue(); T key = item.getKey(); - int present = union.getOrDefault(key, 0); - union.put(key, present + add); + if(!union.containsKey(key)){ + res++; + } } + return res; } public static void incrementMapValue(T t, Map map){ From 47c21b9ba01eb47f0d39f8f093d7668de3916fc4 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Sun, 27 May 2018 01:44:53 +0300 Subject: [PATCH 13/15] created QMoveRelevantProperties, all calculations in QMoveEntitySearcher, check ability to move method --- .../ml_methods_group/algorithm/Algorithm.java | 184 ------------ .../org/ml_methods_group/algorithm/QMove.java | 282 +++++++++++------- .../algorithm/entity/QMoveClassEntity.java | 133 +++------ .../algorithm/entity/QMoveEntitySearcher.java | 170 ++++++----- .../algorithm/entity/QMoveMethodEntity.java | 80 ++--- .../entity/QMoveRelevantProperties.java | 135 +++++++++ .../ml_methods_group/algorithm/QMoveTest.java | 134 +++++++++ 7 files changed, 620 insertions(+), 498 deletions(-) delete mode 100644 src/main/java/org/ml_methods_group/algorithm/Algorithm.java create mode 100644 src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java create mode 100644 src/test/java/org/ml_methods_group/algorithm/QMoveTest.java diff --git a/src/main/java/org/ml_methods_group/algorithm/Algorithm.java b/src/main/java/org/ml_methods_group/algorithm/Algorithm.java deleted file mode 100644 index 4b531cce..00000000 --- a/src/main/java/org/ml_methods_group/algorithm/Algorithm.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2017 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.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import org.apache.log4j.Logger; -import org.ml_methods_group.algorithm.entity.EntitySearchResult; -import org.ml_methods_group.config.Logging; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.function.BiFunction; -import java.util.function.BinaryOperator; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static java.util.Objects.requireNonNull; - -public abstract class Algorithm { - private static final Logger LOGGER = Logging.getLogger(Algorithm.class); - - private final String name; - private final boolean enableParallelExecution; - private final int preferredThreadsCount; - - Algorithm(String name, boolean enableParallelExecution) { - this.name = name; - this.enableParallelExecution = enableParallelExecution; - preferredThreadsCount = Runtime.getRuntime().availableProcessors(); - } - - public final AlgorithmResult execute(EntitySearchResult entities, ExecutorService service, boolean enableFieldRefactorings) { - LOGGER.info(name + " started"); - final long startTime = System.currentTimeMillis(); - final ProgressIndicator indicator; - if (ProgressManager.getInstance().hasProgressIndicator()) { - indicator = ProgressManager.getInstance().getProgressIndicator(); - } else { - indicator = new EmptyProgressIndicator(); - } - indicator.pushState(); - indicator.setText("Running " + name + "..."); - indicator.setFraction(0); - final ExecutionContext context = - new ExecutionContext(enableParallelExecution ? requireNonNull(service) : null, indicator, entities); - final List refactorings; - try { - refactorings = calculateRefactorings(context, enableFieldRefactorings); - } catch (ProcessCanceledException e) { - throw e; - } catch (Exception e) { - LOGGER.error(name + " finished with error: " + e); - return new AlgorithmResult(name, e); - } - final long time = System.currentTimeMillis() - startTime; - indicator.popState(); - final AlgorithmResult result = new AlgorithmResult(refactorings, name, time, context.usedThreads); - LOGGER.info(name + " successfully finished"); - LOGGER.info(result.getReport()); - return result; - } - - - protected abstract List calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) throws Exception; - - protected void reportProgress(double progress, ExecutionContext context) { - context.indicator.setFraction(progress); - } - - protected final A runParallel(List values, ExecutionContext context, Supplier accumulatorFactory, - BiFunction processor, BinaryOperator combiner) { - if (context.service == null) { - throw new UnsupportedOperationException("Parallel execution is disabled"); - } - final List> tasks = splitValues(values).stream() - .sequential() - .map(list -> new Task<>(list, accumulatorFactory, processor)) - .collect(Collectors.toList()); - context.reportAdditionalThreadsUsed(tasks.size()); - final List> results = new ArrayList<>(); - for (Callable task : tasks) { - results.add(context.service.submit(task)); - } - return results.stream() - .sequential() - .map(this::getResult) - .reduce(combiner) - .orElseGet(accumulatorFactory); - } - - private List> splitValues(List values) { - final List> lists = new ArrayList<>(); - final int valuesCount = values.size(); - final int blocksCount = Math.min(preferredThreadsCount, values.size()); - final int blockSize = (valuesCount - 1) / blocksCount + 1; // round up - - for (int blockStart = 0; blockStart < valuesCount; blockStart += blockSize) { - lists.add(values.subList(blockStart, Math.min(blockStart + blockSize, valuesCount))); - } - return lists; - } - - private T getResult(Future future) { - while (true) { - try { - return future.get(); - } catch (InterruptedException ignored) { - } catch (ExecutionException e) { - if (e.getCause() instanceof ProcessCanceledException) { - throw (ProcessCanceledException) e.getCause(); - } else { - throw new RuntimeException(e); // todo - } - } - } - } - - protected final class ExecutionContext { - private final ExecutorService service; - private final ProgressIndicator indicator; - private final EntitySearchResult entities; - private int usedThreads = 1; // default thread - - private ExecutionContext(ExecutorService service, ProgressIndicator indicator, - EntitySearchResult entities) { - this.service = service; - this.indicator = indicator; - this.entities = entities; - } - - public EntitySearchResult getEntities() { - return entities; - } - - public void checkCanceled() { - indicator.checkCanceled(); - } - - private void reportAdditionalThreadsUsed(int count) { - usedThreads = Math.max(usedThreads, 1 + count); - } - } - - private class Task implements Callable { - private final List values; - private final Supplier accumulatorFactory; - private final BiFunction processor; - - private Task(List values, Supplier accumulatorFactory, BiFunction processor) { - this.values = values; - this.accumulatorFactory = accumulatorFactory; - this.processor = processor; - } - - public A call() { - A accumulator = accumulatorFactory.get(); - for (V value : values) { - accumulator = processor.apply(value, accumulator); - } - return accumulator; - } - } -} diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index a716a181..02f1de3a 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -21,28 +21,20 @@ import org.ml_methods_group.algorithm.entity.QMoveClassEntity; import org.ml_methods_group.algorithm.entity.QMoveEntitySearchResult; import org.ml_methods_group.algorithm.entity.QMoveMethodEntity; +import org.ml_methods_group.utils.AlgorithmsUtil; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; public class QMove extends Algorithm { private List methodEntities = new ArrayList<>(); private final List classes = new ArrayList<>(); private final Map qMoveClassesByName = new HashMap<>(); - private double complexity; - private double polymorphism; - private double abstraction; - private double hierarchies; - private double encapsulation; - private double coupling; - private double messaging; - private double cohesion; - private double composition; - private double inheritance; - private double size; + private QMoveMetric metric = new QMoveMetric(); + private ExecutionContext context; + private AtomicInteger progress = new AtomicInteger(); + private long start; public QMove() { super("QMove", true); @@ -51,151 +43,213 @@ public QMove() { @Override protected List calculateRefactorings( ExecutionContext context, boolean enableFieldRefactorings) throws Exception { + this.context = context; QMoveEntitySearchResult searchResult = (QMoveEntitySearchResult) context.getEntities(); methodEntities.clear(); classes.clear(); + progress.set(0); Stream.of(searchResult.getMethods()) .flatMap(List::stream) .filter(Entity::isMovable) - .map(x -> (QMoveMethodEntity)x) + .map(x -> (QMoveMethodEntity) x) .forEach(methodEntities::add); - searchResult.getClasses() .stream() - .map(x -> (QMoveClassEntity)x) - .peek(x -> x.calculateCohesion(methodEntities)) + .map(x -> (QMoveClassEntity) x) .peek(entity -> qMoveClassesByName.put(entity.getName(), entity)) .forEach(classes::add); calculateMetrics(); - ArrayList refactorings = new ArrayList<>(); - - for(QMoveMethodEntity methodEntity : methodEntities){ + System.err.println("started"); + start = System.currentTimeMillis(); + /*for (QMoveMethodEntity methodEntity : methodEntities) { findBestMoveForMethod(methodEntity, refactorings); + }*/ + return runParallel(methodEntities, context, ArrayList::new, + this::findBestMoveForMethod, AlgorithmsUtil::combineLists); + } + + + private List findBestMoveForMethod(QMoveMethodEntity method, + List refactorings) { + reportProgress((double) progress.incrementAndGet() / methodEntities.size(), context); + QMoveMetric copy = new QMoveMetric(metric); + context.checkCanceled(); + ResultHolder resultHolder = new ResultHolder(null, 0); + start = System.currentTimeMillis(); + if (method.getMoveAbility() == QMoveMethodEntity.MoveAbility.ANY_CLASS) { + System.err.println(method.getName()); + resultHolder = checkMoveForClasses(method, classes, copy); + + } else { + for (Set targets : method.getTargets()) { + ResultHolder holder = checkMoveForClasses(method, targets, copy); + if (resultHolder.fitness < holder.fitness) { + resultHolder = holder; + } + } } - for(Refactoring refactoring : refactorings){ - System.err.println(refactoring.getTarget() + - " " + refactoring.getUnit() + " " + refactoring.getAccuracy()); + System.err.println(System.currentTimeMillis() - start); + if (resultHolder.target != null) { + refactorings.add(new Refactoring(method.getName(), + resultHolder.target.getName(), 0.5, + false)); } return refactorings; } - - - private List findBestMoveForMethod(QMoveMethodEntity method, - List refactorings){ - double bestFitness = fitness(); + private ResultHolder checkMoveForClasses(QMoveMethodEntity method, + Collection classes, QMoveMetric metricCopy) { + double bestFitness = 0; QMoveClassEntity targetForThisMethod = null; - for(QMoveClassEntity targetClass : classes){ + for (QMoveClassEntity targetClass : classes) { QMoveClassEntity containingClass = qMoveClassesByName.get( method.getClassName()); - if(containingClass.equals(targetClass)){ + if (containingClass.equals(targetClass) || + !method.isValidMoveToClass(targetClass)) { continue; } - double newFitness = moveMethod(method, targetClass, containingClass); - if(newFitness > bestFitness){ + double newFitness = moveMethod(method, targetClass, containingClass, metric); + if (newFitness > bestFitness) { bestFitness = newFitness; - targetForThisMethod = targetClass; + targetForThisMethod = targetClass; } } - if(targetForThisMethod != null){ - refactorings.add(new Refactoring(method.getName(), - targetForThisMethod.getName(), bestFitness / fitness() - 1, false)); - } - return refactorings; + return new ResultHolder(targetForThisMethod, bestFitness); } - private void calculateMetrics(){ - size = classes.size(); - complexity = methodEntities.size(); - polymorphism = classes.stream().mapToDouble(QMoveClassEntity::getPolymorphism).sum(); - abstraction = classes.stream().mapToDouble(QMoveClassEntity::getAbstraction).sum() / size; - hierarchies = classes.stream().mapToDouble(QMoveClassEntity::getHierarchies).sum(); - encapsulation = classes.stream().mapToDouble(QMoveClassEntity::getEncapsulation).sum(); - coupling = classes.stream().mapToDouble(QMoveClassEntity::getCoupling).sum(); - messaging = classes.stream().mapToDouble(QMoveClassEntity::getMessaging).sum(); - cohesion = classes.stream().mapToDouble(QMoveClassEntity::getCohesion).sum(); - composition = classes.stream().mapToDouble(QMoveClassEntity::getComposition).sum(); - inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); - } - - private double reusability() { - return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging - + 0.5 * size; - } - - private double flexibility() { - return 0.25 * encapsulation - 0.25 * coupling + 0.5 * composition - + 0.5 * polymorphism; - } - - private double understandability() { - return 0.33 * abstraction + 0.33 * encapsulation - 0.33 * coupling - + 0.33 * cohesion - 0.33 * polymorphism - 0.33 * complexity - - 0.33 * size; - } - - private double functionality() { - return +0.12 * cohesion + 0.22 * polymorphism + 0.22 * messaging - + 0.22 * size + 0.22 * hierarchies; - } - - private double extendibility() { - return 0.5 * abstraction - 0.5 * coupling + 0.5 * inheritance - + 0.5 * polymorphism; - } - - private double effectiveness() { - return 0.2 * abstraction + 0.2 * encapsulation + 0.2 * composition - + 0.2 * inheritance + 0.2 * polymorphism; - } - - private double fitness(){ - return reusability() + flexibility() + understandability() + - functionality() + extendibility() + effectiveness(); + private void calculateMetrics() { + metric.size = classes.size(); + metric.complexity = methodEntities.size(); + metric.polymorphism = classes.stream().mapToDouble(QMoveClassEntity::getPolymorphism).sum(); + metric.abstraction = classes.stream().mapToDouble(QMoveClassEntity::getAbstraction).sum() / metric.size; + metric.hierarchies = classes.stream().mapToDouble(QMoveClassEntity::getHierarchies).sum(); + metric.encapsulation = classes.stream().mapToDouble(QMoveClassEntity::getEncapsulation).sum(); + metric.coupling = classes.stream().mapToDouble(QMoveClassEntity::getCoupling).sum(); + metric.messaging = classes.stream().mapToDouble(QMoveClassEntity::getMessaging).sum(); + metric.cohesion = classes.stream().mapToDouble(QMoveClassEntity::getCohesion).sum(); + metric.composition = classes.stream().mapToDouble(QMoveClassEntity::getComposition).sum(); + metric.inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); } private double moveMethod(QMoveMethodEntity method, - QMoveClassEntity target, - QMoveClassEntity containingClass){ + QMoveClassEntity target, + QMoveClassEntity containingClass, QMoveMetric metric) { //recalculating cohesion double removeFromCohesion = target.getCohesion(); removeFromCohesion += containingClass.getCohesion(); double addToCohesion = target.recalculateCohesion(method, true); addToCohesion += containingClass.recalculateCohesion(method, false); - double oldCohesion = cohesion; - cohesion -= removeFromCohesion; - cohesion += addToCohesion; + double oldCohesion = metric.cohesion; + metric.cohesion -= removeFromCohesion; + metric.cohesion += addToCohesion; //recalculating coupling double removeFromCoupling = target.getCoupling(); removeFromCoupling += containingClass.getCoupling(); double addToCoupling = target.recalculateCoupling(method, true); addToCoupling += containingClass.recalculateCoupling(method, false); - double oldCoupling = coupling; - coupling -= removeFromCoupling; - coupling += addToCoupling; + double oldCoupling = metric.coupling; + metric.coupling -= removeFromCoupling; + metric.coupling += addToCoupling; //recalculating inheritance - double oldInheritance = inheritance; - for(QMoveClassEntity entity : classes){ - if(entity.getName().equals(target.getName()) || - entity.getName().equals(containingClass.getName())){ - continue; - } - if(entity.getPsiClass().isInheritor(target.getPsiClass(), true)){ - inheritance -= entity.getInheritance(); - inheritance += entity.recalculateInheritance(true); - } - if(entity.getPsiClass().isInheritor(containingClass.getPsiClass(), true)){ - inheritance -= entity.getInheritance(); - inheritance += entity.recalculateInheritance(false); - } + double oldInheritance = metric.inheritance; + for (QMoveClassEntity entity : target.getInheritors()) { + metric.inheritance -= entity.getInheritance(); + metric.inheritance += entity.recalculateInheritance(true); + } + for (QMoveClassEntity entity : containingClass.getInheritors()) { + metric.inheritance -= entity.getInheritance(); + metric.inheritance += entity.recalculateInheritance(false); } - double fitness = fitness(); - cohesion = oldCohesion; - coupling = oldCoupling; - inheritance = oldInheritance; + double fitness = metric.fitness(this.metric); + metric.cohesion = oldCohesion; + metric.coupling = oldCoupling; + metric.inheritance = oldInheritance; return fitness; } + + private static class QMoveMetric { + private QMoveMetric() { + } + + private QMoveMetric(QMoveMetric other) { + cohesion = other.cohesion; + complexity = other.complexity; + coupling = other.coupling; + encapsulation = other.encapsulation; + polymorphism = other.polymorphism; + abstraction = other.abstraction; + hierarchies = other.hierarchies; + messaging = other.messaging; + composition = other.composition; + inheritance = other.inheritance; + size = other.size; + } + + private double complexity; + private double polymorphism; + private double abstraction; + private double hierarchies; + private double encapsulation; + private double coupling; + private double messaging; + private double cohesion; + private double composition; + private double inheritance; + private double size; + + private double effectiveness() { + return 0.2 * abstraction + 0.2 * encapsulation + 0.2 * composition + + 0.2 * inheritance + 0.2 * polymorphism; + } + + private double extendibility() { + return 0.5 * abstraction - 0.5 * coupling + 0.5 * inheritance + + 0.5 * polymorphism; + } + + private double functionality() { + return +0.12 * cohesion + 0.22 * polymorphism + 0.22 * messaging + + 0.22 * size + 0.22 * hierarchies; + } + + private double understandability() { + return 0.33 * abstraction + 0.33 * encapsulation - 0.33 * coupling + + 0.33 * cohesion - 0.33 * polymorphism - 0.33 * complexity + - 0.33 * size; + } + + private double reusability() { + return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging + + 0.5 * size; + } + + private double flexibility() { + return 0.25 * encapsulation - 0.25 * coupling + 0.5 * composition + + 0.5 * polymorphism; + } + + private double fitness(QMoveMetric other) { + if (flexibility() < other.flexibility() || + understandability() < other.understandability() + || extendibility() < other.extendibility()) { + return Double.NEGATIVE_INFINITY; + } + return flexibility() + understandability() + extendibility() + - other.extendibility() - other.flexibility() - other.understandability(); + } + + } + + private static class ResultHolder { + private ResultHolder(QMoveClassEntity target, double fitness) { + this.fitness = fitness; + this.target = target; + } + + private QMoveClassEntity target; + private double fitness; + } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java index 87eec4d9..f94161c0 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -16,25 +16,17 @@ package org.ml_methods_group.algorithm.entity; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.PsiClass; import com.sixrr.metrics.Metric; import com.sixrr.metrics.MetricCategory; import com.sixrr.metrics.metricModel.MetricsRun; import com.sixrr.stockmetrics.classMetrics.*; -import org.ml_methods_group.utils.PsiSearchUtil; import org.ml_methods_group.utils.QMoveUtil; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; public class QMoveClassEntity extends ClassEntity { - static Map> allNoneStaticMethods = new HashMap<>(); + protected QMoveRelevantProperties properties = new QMoveRelevantProperties(); private double complexity; private double polymorphism; private double abstraction; @@ -47,12 +39,16 @@ public class QMoveClassEntity extends ClassEntity { private double inheritance; private double numOfAllMethods; - private int numOfNoneStaticMethods; private PsiClass psiClass; - private double sumIntersection; - private Map methods; - private Map relatedClasses = new HashMap<>(); - private Map parametersOfMethods = new HashMap<>(); + + + public Set getInheritors(){ + return properties.getInheritors(); + } + + QMoveRelevantProperties getProperties(){ + return properties; + } private static final VectorCalculator QMOOD_CALCULATOR = new VectorCalculator() .addMetricDependence(NumMethodsClassMetric.class) //Complexity 0 @@ -61,10 +57,11 @@ public class QMoveClassEntity extends ClassEntity { .addMetricDependence(NumAncestorsClassMetric.class) //Abstraction 3 .addMetricDependence(IsRootOfHierarchyClassMetric.class) //Hierarchies 4 .addMetricDependence(DataAccessClassMetric.class) //Encapsulation 5 - //.addMetricDependence(DirectClassCouplingMetric.class) //Coupling 6 - .addMetricDependence(NumPublicMethodsClassMetric.class) //Messaging 7 - .addMetricDependence(CohesionAmongMethodsOfClassMetric.class) //Cohesion 8 - .addMetricDependence(MeasureOfAggregationClassMetric.class); //Composition 9 + .addMetricDependence(NumPublicMethodsClassMetric.class); //Messaging 6 + // .addMetricDependence(CohesionAmongMethodsOfClassMetric.class) //Cohesion 8 + //.addMetricDependence(DirectClassCouplingMetric.class) //Coupling 9 + //.addMetricDependence(MeasureOfAggregationClassMetric.class); //Composition 10 + QMoveClassEntity(PsiClass psiClass) { super(psiClass); @@ -75,20 +72,21 @@ public class QMoveClassEntity extends ClassEntity { void calculateVector(MetricsRun metricsRun) { double[] vector = QMOOD_CALCULATOR.calculateVector(metricsRun, this); complexity = vector[0]; + inheritance = vector[1]; polymorphism = vector[2]; abstraction = vector[3]; hierarchies = vector[4]; encapsulation = vector[5]; messaging = vector[6]; - cohesion = vector[7]; - composition = vector[8]; - inheritance = vector[1]; numOfAllMethods = inheritance + complexity; - methods = Stream.of(psiClass.getMethods()).collect( - Collectors.toMap(PsiSearchUtil::getHumanReadableName, Function.identity()) - ); - coupling = calculateCoupling(); + + coupling = properties.getRelatedClasses().size(); + + cohesion = properties.getSumIntersection() != 0 ? properties.getSumIntersection() / + (properties.getParametersOfMethods().size() * properties.getNumOfMethods()) : 0; + + composition = properties.getNumUserDefinedClasses(); } @Override @@ -107,15 +105,6 @@ public boolean isField() { return false; } - @Override - public void removeFromClass(String method) { - getRelevantProperties().removeMethod(method); - } - - public void addToClass(String method) { - getRelevantProperties().addMethod(method); - } - public double getComplexity() { return complexity; } @@ -164,76 +153,36 @@ public static Set> getRequestedMetrics() { } public double recalculateCohesion(QMoveMethodEntity entity, boolean add) { - if (entity.getPsiMethod().hasModifierProperty(PsiModifier.STATIC)) { - return cohesion; - } - int newNumOfNoneStaticMethods = numOfNoneStaticMethods; - double newSumIntersection = sumIntersection; + int numMethods = properties.getNumOfMethods(); + double sumIntersection = properties.getSumIntersection(); int union; if (add) { - newNumOfNoneStaticMethods++; - newSumIntersection += entity.getParameters().size(); - union = QMoveUtil.addToUnion(parametersOfMethods, entity.getParameters()); + numMethods++; + sumIntersection += entity.getProperties().getSumIntersection(); + union = QMoveUtil.addToUnion(properties.getParametersOfMethods(), + entity.getProperties().getParametersOfMethods()); } else { - newNumOfNoneStaticMethods--; - newSumIntersection -= entity.getParameters().size(); - union = QMoveUtil.removeFromUnion(parametersOfMethods, entity.getParameters()); + numMethods--; + sumIntersection -= entity.getProperties().getSumIntersection(); + union = QMoveUtil.removeFromUnion(properties.getParametersOfMethods(), + entity.getProperties().getParametersOfMethods()); } if (union == 0) { return 0; } - return newSumIntersection / (newNumOfNoneStaticMethods * union); + return sumIntersection / (numMethods * union); } - public double recalculateCoupling(QMoveMethodEntity entity, boolean add) { if (add) { - return QMoveUtil.addToUnion(relatedClasses, entity.getRelatedClasses()); + return QMoveUtil.addToUnion(properties.getRelatedClasses(), + entity.getProperties().getRelatedClasses()); } else { - return QMoveUtil.removeFromUnion(relatedClasses, entity.getRelatedClasses()); + return QMoveUtil.removeFromUnion(properties.getRelatedClasses(), + entity.getProperties().getRelatedClasses()); } } - private int calculateCoupling() { - QMoveUtil.incrementMapValue(psiClass, relatedClasses); - PsiField[] fields = psiClass.getFields(); - for (PsiField field : fields) { - if (!field.isPhysical()) { - continue; - } - PsiType type = field.getType().getDeepComponentType(); - PsiClass classInType = PsiUtil.resolveClassInType(type); - if (classInType == null) { - continue; - } - QMoveUtil.incrementMapValue(classInType, relatedClasses); - } - for (Map.Entry method : methods.entrySet()) { - QMoveUtil.calculateRelatedClasses(method.getValue(), relatedClasses); - } - return relatedClasses.size(); - } - - - public double calculateCohesion(List methods) { - sumIntersection = methods.stream() - .filter(x -> x.getClassName().equals(getName())) - .map(QMoveMethodEntity::getPsiMethod) - .mapToInt(x -> x.getParameterList().getParameters().length).sum(); - for (QMoveMethodEntity methodEntry : methods) { - PsiMethod method = methodEntry.getPsiMethod(); - if (method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)) { - continue; - } - QMoveUtil.calculateMethodParameters(method, parametersOfMethods); - numOfNoneStaticMethods++; - } - if (parametersOfMethods.size() == 0) { - return 0; - } - return sumIntersection / (numOfNoneStaticMethods * parametersOfMethods.size()); - } - public double recalculateInheritance(boolean increment) { if (increment) { return (inheritance + 1) / (numOfAllMethods + 1); @@ -242,7 +191,5 @@ public double recalculateInheritance(boolean increment) { } } - public PsiClass getPsiClass() { - return psiClass; - } + } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java index 4e70bc39..4f8ecdfd 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -98,14 +98,6 @@ private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { break; case Method: methods.add((MethodEntity) entity); - if(((QMoveMethodEntity)entity).getPsiMethod().hasModifierProperty(PsiModifier.STATIC)){ - break; - } - PsiClass containingClass = ((QMoveMethodEntity)entity).getPsiMethod().getContainingClass(); - if(!QMoveClassEntity.allNoneStaticMethods.containsKey(containingClass)){ - QMoveClassEntity.allNoneStaticMethods.put(containingClass, new ArrayList<>()); - } - QMoveClassEntity.allNoneStaticMethods.get(containingClass).add((QMoveMethodEntity) entity); break; default: fields.add((FieldEntity) entity); @@ -178,28 +170,33 @@ public void visitFile(PsiFile file) { @Override public void visitClass(PsiClass aClass) { indicator.checkCanceled(); - final Entity entity = entities.get(aClass); + final QMoveClassEntity entity = (QMoveClassEntity) entities.get(aClass); if (entity == null) { super.visitClass(aClass); return; } - /* final RelevantProperties classProperties = entity.getRelevantProperties(); - classProperties.addClass(aClass, strategy.getWeight(aClass, aClass)); - if (strategy.processSupers()) { - for (PsiClass superClass : PSIUtil.getAllSupers(aClass)) { - if (superClass.isInterface()) { - classProperties.addClass(superClass, strategy.getWeight(aClass, superClass)); - } else { - propertiesFor(superClass).ifPresent(p -> p.addClass(aClass, strategy.getWeight(superClass, aClass))); + entity.getProperties().addToRelatedClasses(aClass); + for(PsiClass superClass : PSIUtil.getAllSupers(aClass)){ + if(isClassInProject(superClass)){ + QMoveClassEntity superClassEntity = (QMoveClassEntity) entities.get(superClass); + if(superClassEntity == null){ + continue; + } + superClassEntity.getProperties().getInheritors().add(entity); + entity.getProperties().getSupers().add(superClassEntity); + } + } + + for(PsiClass innerClass : aClass.getInnerClasses()){ + if(isClassInProject(innerClass)){ + QMoveClassEntity innerClassEntity = (QMoveClassEntity) entities.get(innerClass); + if(innerClassEntity == null){ + continue; } + entity.getProperties().getInnerClasses().add(innerClassEntity); + innerClassEntity.getProperties().getOuterClasses().add(entity); } } - Arrays.stream(aClass.getMethods()) - .filter(m -> isProperty(aClass, m)) - .forEach(m -> classProperties.addMethod(m, strategy.getWeight(aClass, m))); - Arrays.stream(aClass.getFields()) - .filter(f -> isProperty(aClass, f)) - .forEach(f -> classProperties.addField(f, strategy.getWeight(aClass, f)));*/ reportPropertiesCalculated(); super.visitClass(aClass); } @@ -216,27 +213,57 @@ private boolean isClassInProject(final @Nullable PsiClass aClass) { @Override public void visitMethod(PsiMethod method) { indicator.checkCanceled(); - final Entity entity = entities.get(method); + final QMoveMethodEntity entity = (QMoveMethodEntity) entities.get(method); if (entity == null) { super.visitMethod(method); return; } - /*final RelevantProperties methodProperties = entity.getRelevantProperties(); - methodProperties.addMethod(method, strategy.getWeight(method, method)); - Optional.ofNullable(method.getContainingClass()) - .ifPresent(c -> methodProperties.addClass(c, strategy.getWeight(method, c))); if (currentMethod == null) { currentMethod = method; } - if (strategy.processSupers()) { - PSIUtil.getAllSupers(method).stream() - .map(entities::get) - .filter(Objects::nonNull) - .forEach(superMethod -> superMethod - .getRelevantProperties() - .addOverrideMethod(method, strategy.getWeight(superMethod, method))); - }*/ + QMoveClassEntity containingClass = (QMoveClassEntity) entities.get(method.getContainingClass()); + if(containingClass != null){ + entity.setContainingClass(containingClass); + containingClass.getProperties().incrementNumOfMethods(); + for(PsiParameter parameter : method.getParameterList().getParameters()){ + PsiTypeElement typeElement = parameter.getTypeElement(); + if (typeElement == null) { + continue; + } + PsiType type = typeElement.getType().getDeepComponentType(); + //parameters of methods for cohesion + entity.getProperties().addToParameters(type); + containingClass.getProperties().addToParameters(type); + + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + //coupling + entity.getProperties().addToRelatedClasses(classInType); + containingClass.getProperties().addToRelatedClasses(classInType); + //composition + if(isClassInProject(classInType)){ + containingClass.getProperties().incrementUserDefinedClasses(); + } + } + //cohesion + int sumIntersection = entity.getProperties().setMethodSumIntersection(); + containingClass.getProperties().increaseSumIntersection(sumIntersection); + + entity.getProperties().setInnerClasses( + containingClass.getProperties().getInnerClasses()); + + entity.getProperties().setInheritors( + containingClass.getProperties().getInheritors()); + + entity.getProperties().setOuterClasses( + containingClass.getProperties().getOuterClasses()); + + entity.getProperties().setSupers( + containingClass.getProperties().getSupers()); + } reportPropertiesCalculated(); super.visitMethod(method); if (currentMethod == method) { @@ -245,26 +272,6 @@ public void visitMethod(PsiMethod method) { } @Override - public void visitReferenceExpression(PsiReferenceExpression expression) { - indicator.checkCanceled(); - PsiElement element = expression.resolve(); - if (currentMethod != null && element instanceof PsiField - && isClassInProject(((PsiField) element).getContainingClass()) && strategy.isRelation(expression)) { - final PsiField field = (PsiField) element; - propertiesFor(currentMethod) - .ifPresent(p -> p.addField(field, strategy.getWeight(currentMethod, field))); -// propertiesFor(field) -// .ifPresent(p -> p.addMethod(currentMethod, strategy.getWeight(field, currentMethod))); - final PsiClass fieldClass = PsiUtil.resolveClassInType(field.getType()); - if (isClassInProject(fieldClass)) { - propertiesFor(currentMethod) - .ifPresent(p -> p.addClass(fieldClass, strategy.getWeight(currentMethod, fieldClass))); - } - } - super.visitReferenceExpression(expression); - } - - /*@Override public void visitField(PsiField field) { indicator.checkCanceled(); final Entity entity = entities.get(field); @@ -272,32 +279,59 @@ public void visitField(PsiField field) { super.visitField(field); return; } - RelevantProperties fieldProperties = entity.getRelevantProperties(); - fieldProperties.addField(field, strategy.getWeight(field, field)); + final PsiClass containingClass = field.getContainingClass(); if (containingClass != null) { - fieldProperties.addClass(containingClass, strategy.getWeight(field, containingClass)); final PsiClass fieldClass = PsiUtil.resolveClassInType(field.getType()); + if(fieldClass != null){ + ((QMoveClassEntity)entities.get(containingClass)) + .getProperties().addToRelatedClasses(fieldClass); + } if (isClassInProject(fieldClass)) { - entities.get(containingClass).getRelevantProperties().addClass(fieldClass, strategy.getWeight(containingClass, fieldClass)); + ((QMoveClassEntity)entities.get(containingClass)) + .getProperties().incrementUserDefinedClasses(); } } reportPropertiesCalculated(); super.visitField(field); - }*/ + } + + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + indicator.checkCanceled(); + PsiElement element = expression.resolve(); + if (currentMethod != null && element instanceof PsiField) { + final PsiField field = (PsiField) element; + if(field.hasModifierProperty(PsiModifier.PRIVATE)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsPrivateCalls(true); + } + + if(field.hasModifierProperty(PsiModifier.PROTECTED)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsProtectedCalls(true); + } + } + super.visitReferenceExpression(expression); + } @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { indicator.checkCanceled(); final PsiMethod called = expression.resolveMethod(); final PsiClass usedClass = called != null ? called.getContainingClass() : null; - if (currentMethod != null && called != null && isClassInProject(usedClass) - && strategy.isRelation(expression)) { - propertiesFor(currentMethod) - .ifPresent(p -> { - p.addMethod(called, strategy.getWeight(currentMethod, called)); - p.addClass(usedClass, strategy.getWeight(currentMethod, usedClass)); - }); + if (currentMethod != null && called != null) { + if(called.hasModifierProperty(PsiModifier.PRIVATE)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsPrivateCalls(true); + } + + if(called.hasModifierProperty(PsiModifier.PROTECTED)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsProtectedCalls(true); + } + } super.visitMethodCallExpression(expression); } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java index b1a69fe9..1c415b98 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java @@ -22,69 +22,71 @@ import org.ml_methods_group.utils.PSIUtil; import org.ml_methods_group.utils.QMoveUtil; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class QMoveMethodEntity extends MethodEntity { - private final PsiMethod psiMethod; - private boolean containsPrivateCalls; - private boolean isContainsOnlyPublicCalls; - - private Map relatedClasses = new HashMap<>(); - private Map parameters = new HashMap<>(); - + private QMoveClassEntity containingClass; + private QMoveRelevantProperties properties = new QMoveRelevantProperties(); + private MoveAbility moveAbility; QMoveMethodEntity(PsiMethod method) { super(method); - this.psiMethod = method; } @Override void calculateVector(MetricsRun metricsRun) { - QMoveUtil.calculateRelatedClasses(psiMethod, relatedClasses); - QMoveUtil.calculateMethodParameters(psiMethod, parameters); + moveAbility = MoveAbility.ANY_CLASS; + if(properties.isContainsProtectedCalls()){ + moveAbility = MoveAbility.INHERITOR; + } + if(properties.isContainsPrivateCalls()){ + moveAbility = MoveAbility.INNER_OR_OUTER; + } } - PsiMethod getPsiMethod() { - return psiMethod; - } - Map getParameters(){ - return parameters; + public List> getTargets(){ + List> list = new ArrayList<>(); + list.add(properties.getInnerClasses()); + list.add(properties.getOuterClasses()); + if(moveAbility != MoveAbility.INNER_OR_OUTER){ + list.add(properties.getInheritors()); + } + return list; } - public boolean isValidMoveToClass(PsiClass targetClass){ - if(!isMovable){ + + public boolean isValidMoveToClass(QMoveClassEntity targetClass) { + if (!isMovable) { return false; } - PsiClass containingClass = psiMethod.getContainingClass(); - if(getAllInnerClasses(targetClass, new HashSet<>()). - contains(containingClass) - || getAllInnerClasses(containingClass, new HashSet<>()). - contains(targetClass)){ + if (properties.getInnerClasses().contains(targetClass) + || targetClass.getProperties() + .getInnerClasses().contains(containingClass)) { return true; } - if(PSIUtil.getAllSupers(targetClass).contains(containingClass)){ - + if (properties.isContainsPrivateCalls()) { + return false; } - else { + return !properties.isContainsProtectedCalls() + || targetClass.getProperties().getSupers().contains(containingClass); + } - } - return true; + public QMoveRelevantProperties getProperties() { + return properties; } + void setContainingClass(QMoveClassEntity containingClass) { + this.containingClass = containingClass; + } - private Set getAllInnerClasses(PsiClass aClass, Set innerClasses){ - innerClasses.add(aClass); - for(PsiClass psiClass : aClass.getInnerClasses()){ - getAllInnerClasses(psiClass, innerClasses); - } - return innerClasses; + public MoveAbility getMoveAbility() { + return moveAbility; } - Map getRelatedClasses() { - return relatedClasses; + public enum MoveAbility { + ANY_CLASS, + INHERITOR, + INNER_OR_OUTER } } diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java new file mode 100644 index 00000000..ecc2de78 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java @@ -0,0 +1,135 @@ +/* + * 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.entity; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiType; + +import java.util.*; + +import static org.ml_methods_group.utils.QMoveUtil.incrementMapValue; + +class QMoveRelevantProperties { + //cohesion + private double sumIntersection; + private Map parametersOfMethods = new HashMap<>(); + //coupling + private Map relatedClasses = new HashMap<>(); + + private Set outerClasses = new HashSet<>(); + private Set innerClasses = new HashSet<>(); + private Set supers = new HashSet<>(); + private Set inheritors = new HashSet<>(); + private int numUserDefinedClasses; + private boolean containsPrivateCalls; + private boolean containsProtectedCalls; + private int numOfMethods; + + void incrementNumOfMethods(){ + numOfMethods++; + } + + void addToParameters(PsiType psiType){ + incrementMapValue(psiType, parametersOfMethods); + } + + void addToRelatedClasses(PsiClass psiClass){ + incrementMapValue(psiClass, relatedClasses); + } + + void increaseSumIntersection(int val){ + sumIntersection += val; + } + + int setMethodSumIntersection(){ + sumIntersection = parametersOfMethods.size(); + return parametersOfMethods.size(); + } + Map getParametersOfMethods() { + return parametersOfMethods; + } + + Map getRelatedClasses() { + return relatedClasses; + } + + Set getInnerClasses() { + return innerClasses; + } + + void setInnerClasses(Set innerClasses) { + this.innerClasses = innerClasses; + } + + + void setSupers(Set supers) { + this.supers = supers; + } + + Set getInheritors() { + return inheritors; + } + + void incrementUserDefinedClasses(){ + numUserDefinedClasses++; + } + int getNumUserDefinedClasses() { + return numUserDefinedClasses; + } + + boolean isContainsPrivateCalls() { + return containsPrivateCalls; + } + + void setContainsPrivateCalls(boolean containsPrivateCalls) { + this.containsPrivateCalls = containsPrivateCalls; + } + + boolean isContainsProtectedCalls() { + return containsProtectedCalls; + } + + void setContainsProtectedCalls(boolean containsProtectedCalls) { + this.containsProtectedCalls = containsProtectedCalls; + } + + void setInheritors(Set inheritors) { + this.inheritors = inheritors; + } + + Set getSupers() { + return supers; + } + + + double getSumIntersection() { + return sumIntersection; + } + + int getNumOfMethods() { + return numOfMethods; + } + + + Set getOuterClasses() { + return outerClasses; + } + + void setOuterClasses(Set outerClasses) { + this.outerClasses = outerClasses; + } +} diff --git a/src/test/java/org/ml_methods_group/algorithm/QMoveTest.java b/src/test/java/org/ml_methods_group/algorithm/QMoveTest.java new file mode 100644 index 00000000..232e102f --- /dev/null +++ b/src/test/java/org/ml_methods_group/algorithm/QMoveTest.java @@ -0,0 +1,134 @@ +/* + * Copyright 2017 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.analysis.AnalysisScope; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import com.sixrr.metrics.metricModel.MetricsRun; +import com.sixrr.metrics.metricModel.MetricsRunImpl; +import com.sixrr.metrics.profile.MetricsProfile; +import org.ml_methods_group.algorithm.entity.Entity; +import org.ml_methods_group.algorithm.entity.QMoveClassEntity; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearchResult; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearcher; +import org.ml_methods_group.refactoring.RefactoringExecutionContext; +import org.ml_methods_group.utils.MetricsProfilesUtil; +import org.ml_methods_group.utils.RefactoringUtil; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.ml_methods_group.utils.RefactoringUtil.toMap; + +public class QMoveTest extends LightCodeInsightFixtureTestCase { + private Project project; + private AnalysisScope analysisScope; + private MetricsProfile profile; + private QMoveEntitySearchResult searchResult; + +// @Override +// public void setUp() throws Exception { +// super.setUp(); +// } + + @Override + protected String getTestDataPath() { + return "testdata/" + getTestName(true); + } + + public void testMoveMethod() throws IOException { + final VirtualFile file1 = myFixture.copyFileToProject("ClassA.java"); + final VirtualFile file2 = myFixture.copyFileToProject("ClassB.java"); + + project = myFixture.getProject(); + analysisScope = new AnalysisScope(project, Arrays.asList(file1, file2)); + profile = MetricsProfilesUtil.createProfile("test_profile", QMoveClassEntity.getRequestedMetrics()); + MetricsRunImpl metricsRun = new MetricsRunImpl(); + metricsRun.setProfileName(profile.getName()); + metricsRun.setContext(analysisScope); + + searchResult = QMoveEntitySearcher.analyze(analysisScope, metricsRun); + } + + private static void calculateMoveMethodRefactorings(RefactoringExecutionContext context) { + assertEquals(2, context.getClassCount()); + assertEquals(6, context.getMethodsCount()); + assertEquals(4, context.getFieldsCount()); + + final Map refactoringsCCDA = toMap(context.getResultForName("CCDA") + .getRefactorings()); + assertEquals(1, refactoringsCCDA.size()); + assertEquals("ClassB.methodB1()", refactoringsCCDA.keySet().toArray()[0]); + assertEquals("ClassA", refactoringsCCDA.get("ClassB.methodB1()")); + + final Map refactoringsMRI = toMap(context.getResultForName("MRI") + .getRefactorings()); + assertEquals(1, refactoringsMRI.size()); + assertEquals("ClassB.methodB1()", refactoringsMRI.keySet().toArray()[0]); + assertEquals("ClassA", refactoringsMRI.get("ClassB.methodB1()")); + + +// final Set common = new HashSet<>(refactoringsCCDA.keySet()); +// common.retainAll(refactoringsMRI.keySet()); +// System.out.println("Common for ARI and CCDA: "); +// for (String move : common) { +// System.out.print(move + " to "); +// System.out.print(refactoringsCCDA.get(move)); +// if (!refactoringsMRI.get(move).equals(refactoringsCCDA.get(move))) { +// System.out.print(" vs " + refactoringsMRI.get(move)); +// } +// System.out.println(); +// } +// System.out.println(); +// +// assertEquals(new HashSet(Collections.singletonList("ClassB.methodB1()")), common); + + final Map refactoringsAKMeans = toMap(context.getResultForName("AKMeans") + .getRefactorings()); + +// Set refactoringsARIEC = new HashSet<>(refactoringsAKMeans.keySet()); +// refactoringsARIEC.retainAll(refactoringsMRI.keySet()); +// System.out.println("Common for ARI and EC: "); +// for (String move : refactoringsARIEC) { +// System.out.print(move + " to "); +// System.out.print(refactoringsAKMeans.get(move)); +// if (!refactoringsMRI.get(move).equals(refactoringsAKMeans.get(move))) { +// System.out.print(" vs " + refactoringsMRI.get(move)); +// } +// System.out.println(); +// } +// System.out.println(); + + // TODO: make AKMeans more deterministic somehow and get some assertions here +// assertEquals(new HashSet(Collections.singletonList("ClassB.methodB1()")), refactoringsARIEC); + + final Map refactoringsHAC = toMap(context.getResultForName("HAC") + .getRefactorings()); + + final Map expectedHAC = new HashMap<>(); + expectedHAC.put("ClassB.methodB1()", "ClassA"); + assertEquals(expectedHAC, refactoringsHAC); + + final Map refactoringsARI = toMap(context.getResultForName("ARI") + .getRefactorings()); + assertEquals(expectedHAC, refactoringsARI); + } +} \ No newline at end of file From ad4b53fc861a8c951d833c93311ed8a8bdb5b768 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Sun, 27 May 2018 17:10:14 +0300 Subject: [PATCH 14/15] fixed bug in copying metric in QMove --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../ml_methods_group/algorithm/Algorithm.java | 184 ++++++++++++++++++ .../org/ml_methods_group/algorithm/QMove.java | 46 +++-- .../org/ml_methods_group/utils/QMoveUtil.java | 23 --- .../ml_methods_group/algorithm/QMoveTest.java | 134 ------------- 5 files changed, 210 insertions(+), 179 deletions(-) create mode 100644 src/main/java/org/ml_methods_group/algorithm/Algorithm.java delete mode 100644 src/test/java/org/ml_methods_group/algorithm/QMoveTest.java diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e5fd9b1c..fa6b6685 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip diff --git a/src/main/java/org/ml_methods_group/algorithm/Algorithm.java b/src/main/java/org/ml_methods_group/algorithm/Algorithm.java new file mode 100644 index 00000000..4b531cce --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/Algorithm.java @@ -0,0 +1,184 @@ +/* + * Copyright 2017 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.progress.EmptyProgressIndicator; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import org.apache.log4j.Logger; +import org.ml_methods_group.algorithm.entity.EntitySearchResult; +import org.ml_methods_group.config.Logging; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; + +public abstract class Algorithm { + private static final Logger LOGGER = Logging.getLogger(Algorithm.class); + + private final String name; + private final boolean enableParallelExecution; + private final int preferredThreadsCount; + + Algorithm(String name, boolean enableParallelExecution) { + this.name = name; + this.enableParallelExecution = enableParallelExecution; + preferredThreadsCount = Runtime.getRuntime().availableProcessors(); + } + + public final AlgorithmResult execute(EntitySearchResult entities, ExecutorService service, boolean enableFieldRefactorings) { + LOGGER.info(name + " started"); + final long startTime = System.currentTimeMillis(); + final ProgressIndicator indicator; + if (ProgressManager.getInstance().hasProgressIndicator()) { + indicator = ProgressManager.getInstance().getProgressIndicator(); + } else { + indicator = new EmptyProgressIndicator(); + } + indicator.pushState(); + indicator.setText("Running " + name + "..."); + indicator.setFraction(0); + final ExecutionContext context = + new ExecutionContext(enableParallelExecution ? requireNonNull(service) : null, indicator, entities); + final List refactorings; + try { + refactorings = calculateRefactorings(context, enableFieldRefactorings); + } catch (ProcessCanceledException e) { + throw e; + } catch (Exception e) { + LOGGER.error(name + " finished with error: " + e); + return new AlgorithmResult(name, e); + } + final long time = System.currentTimeMillis() - startTime; + indicator.popState(); + final AlgorithmResult result = new AlgorithmResult(refactorings, name, time, context.usedThreads); + LOGGER.info(name + " successfully finished"); + LOGGER.info(result.getReport()); + return result; + } + + + protected abstract List calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) throws Exception; + + protected void reportProgress(double progress, ExecutionContext context) { + context.indicator.setFraction(progress); + } + + protected final A runParallel(List values, ExecutionContext context, Supplier accumulatorFactory, + BiFunction processor, BinaryOperator combiner) { + if (context.service == null) { + throw new UnsupportedOperationException("Parallel execution is disabled"); + } + final List> tasks = splitValues(values).stream() + .sequential() + .map(list -> new Task<>(list, accumulatorFactory, processor)) + .collect(Collectors.toList()); + context.reportAdditionalThreadsUsed(tasks.size()); + final List> results = new ArrayList<>(); + for (Callable task : tasks) { + results.add(context.service.submit(task)); + } + return results.stream() + .sequential() + .map(this::getResult) + .reduce(combiner) + .orElseGet(accumulatorFactory); + } + + private List> splitValues(List values) { + final List> lists = new ArrayList<>(); + final int valuesCount = values.size(); + final int blocksCount = Math.min(preferredThreadsCount, values.size()); + final int blockSize = (valuesCount - 1) / blocksCount + 1; // round up + + for (int blockStart = 0; blockStart < valuesCount; blockStart += blockSize) { + lists.add(values.subList(blockStart, Math.min(blockStart + blockSize, valuesCount))); + } + return lists; + } + + private T getResult(Future future) { + while (true) { + try { + return future.get(); + } catch (InterruptedException ignored) { + } catch (ExecutionException e) { + if (e.getCause() instanceof ProcessCanceledException) { + throw (ProcessCanceledException) e.getCause(); + } else { + throw new RuntimeException(e); // todo + } + } + } + } + + protected final class ExecutionContext { + private final ExecutorService service; + private final ProgressIndicator indicator; + private final EntitySearchResult entities; + private int usedThreads = 1; // default thread + + private ExecutionContext(ExecutorService service, ProgressIndicator indicator, + EntitySearchResult entities) { + this.service = service; + this.indicator = indicator; + this.entities = entities; + } + + public EntitySearchResult getEntities() { + return entities; + } + + public void checkCanceled() { + indicator.checkCanceled(); + } + + private void reportAdditionalThreadsUsed(int count) { + usedThreads = Math.max(usedThreads, 1 + count); + } + } + + private class Task implements Callable { + private final List values; + private final Supplier accumulatorFactory; + private final BiFunction processor; + + private Task(List values, Supplier accumulatorFactory, BiFunction processor) { + this.values = values; + this.accumulatorFactory = accumulatorFactory; + this.processor = processor; + } + + public A call() { + A accumulator = accumulatorFactory.get(); + for (V value : values) { + accumulator = processor.apply(value, accumulator); + } + return accumulator; + } + } +} diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index 02f1de3a..5600a2bc 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -61,11 +61,13 @@ protected List calculateRefactorings( calculateMetrics(); System.err.println("started"); start = System.currentTimeMillis(); - /*for (QMoveMethodEntity methodEntity : methodEntities) { + /* List refactorings = new ArrayList<>(); + for (QMoveMethodEntity methodEntity : methodEntities) { findBestMoveForMethod(methodEntity, refactorings); }*/ return runParallel(methodEntities, context, ArrayList::new, - this::findBestMoveForMethod, AlgorithmsUtil::combineLists); + this::findBestMoveForMethod, AlgorithmsUtil::combineLists); + // return refactorings; } @@ -91,7 +93,7 @@ private List findBestMoveForMethod(QMoveMethodEntity method, System.err.println(System.currentTimeMillis() - start); if (resultHolder.target != null) { refactorings.add(new Refactoring(method.getName(), - resultHolder.target.getName(), 0.5, + resultHolder.target.getName(), resultHolder.fitness, false)); } return refactorings; @@ -108,7 +110,7 @@ private ResultHolder checkMoveForClasses(QMoveMethodEntity method, !method.isValidMoveToClass(targetClass)) { continue; } - double newFitness = moveMethod(method, targetClass, containingClass, metric); + double newFitness = moveMethod(method, targetClass, containingClass, metricCopy); if (newFitness > bestFitness) { bestFitness = newFitness; targetForThisMethod = targetClass; @@ -134,39 +136,41 @@ private void calculateMetrics() { private double moveMethod(QMoveMethodEntity method, QMoveClassEntity target, - QMoveClassEntity containingClass, QMoveMetric metric) { + QMoveClassEntity containingClass, QMoveMetric copyMetric) { //recalculating cohesion double removeFromCohesion = target.getCohesion(); removeFromCohesion += containingClass.getCohesion(); double addToCohesion = target.recalculateCohesion(method, true); addToCohesion += containingClass.recalculateCohesion(method, false); - double oldCohesion = metric.cohesion; - metric.cohesion -= removeFromCohesion; - metric.cohesion += addToCohesion; + double oldCohesion = copyMetric.cohesion; + copyMetric.cohesion -= removeFromCohesion; + copyMetric.cohesion += addToCohesion; //recalculating coupling double removeFromCoupling = target.getCoupling(); removeFromCoupling += containingClass.getCoupling(); double addToCoupling = target.recalculateCoupling(method, true); addToCoupling += containingClass.recalculateCoupling(method, false); - double oldCoupling = metric.coupling; - metric.coupling -= removeFromCoupling; - metric.coupling += addToCoupling; - + double oldCoupling = copyMetric.coupling; + copyMetric.coupling -= removeFromCoupling; + copyMetric.coupling += addToCoupling; + if(metric.coupling != oldCoupling){ + System.err.println("wtf"); + } //recalculating inheritance - double oldInheritance = metric.inheritance; + double oldInheritance = copyMetric.inheritance; for (QMoveClassEntity entity : target.getInheritors()) { - metric.inheritance -= entity.getInheritance(); - metric.inheritance += entity.recalculateInheritance(true); + copyMetric.inheritance -= entity.getInheritance(); + copyMetric.inheritance += entity.recalculateInheritance(true); } for (QMoveClassEntity entity : containingClass.getInheritors()) { - metric.inheritance -= entity.getInheritance(); - metric.inheritance += entity.recalculateInheritance(false); + copyMetric.inheritance -= entity.getInheritance(); + copyMetric.inheritance += entity.recalculateInheritance(false); } - double fitness = metric.fitness(this.metric); - metric.cohesion = oldCohesion; - metric.coupling = oldCoupling; - metric.inheritance = oldInheritance; + double fitness = copyMetric.fitness(this.metric); + copyMetric.cohesion = oldCohesion; + copyMetric.coupling = oldCoupling; + copyMetric.inheritance = oldInheritance; return fitness; } diff --git a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java index f9cc9327..958660b0 100644 --- a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java +++ b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java @@ -23,22 +23,6 @@ import java.util.stream.Stream; public class QMoveUtil { - public static void calculateRelatedClasses(PsiMethod method, Map relatedClasses){ - PsiParameter[] parameters = method.getParameterList().getParameters(); - for (PsiParameter parameter : parameters) { - PsiTypeElement typeElement = parameter.getTypeElement(); - if (typeElement == null) { - continue; - } - PsiType type = typeElement.getType().getDeepComponentType(); - PsiClass classInType = PsiUtil.resolveClassInType(type); - if (classInType == null) { - continue; - } - incrementMapValue(classInType, relatedClasses); - } - } - public static int removeFromUnion(Map union, Map set){ int res = union.size(); for(Map.Entry item : set.entrySet()){ @@ -55,7 +39,6 @@ public static int removeFromUnion(Map union, Map se public static int addToUnion(Map union, Map set){ int res = union.size(); for(Map.Entry item : set.entrySet()){ - int add = item.getValue(); T key = item.getKey(); if(!union.containsKey(key)){ res++; @@ -68,10 +51,4 @@ public static void incrementMapValue(T t, Map map){ map.put(t, map.getOrDefault(t, 0) + 1); } - - public static void calculateMethodParameters(PsiMethod method, Map parameters){ - Stream.of(method.getParameterList()). - flatMap(x -> Stream.of(x.getParameters())). - map(PsiVariable::getType).forEach(x -> incrementMapValue(x, parameters)); - } } diff --git a/src/test/java/org/ml_methods_group/algorithm/QMoveTest.java b/src/test/java/org/ml_methods_group/algorithm/QMoveTest.java deleted file mode 100644 index 232e102f..00000000 --- a/src/test/java/org/ml_methods_group/algorithm/QMoveTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2017 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.analysis.AnalysisScope; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; -import com.sixrr.metrics.metricModel.MetricsRun; -import com.sixrr.metrics.metricModel.MetricsRunImpl; -import com.sixrr.metrics.profile.MetricsProfile; -import org.ml_methods_group.algorithm.entity.Entity; -import org.ml_methods_group.algorithm.entity.QMoveClassEntity; -import org.ml_methods_group.algorithm.entity.QMoveEntitySearchResult; -import org.ml_methods_group.algorithm.entity.QMoveEntitySearcher; -import org.ml_methods_group.refactoring.RefactoringExecutionContext; -import org.ml_methods_group.utils.MetricsProfilesUtil; -import org.ml_methods_group.utils.RefactoringUtil; - -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import static org.ml_methods_group.utils.RefactoringUtil.toMap; - -public class QMoveTest extends LightCodeInsightFixtureTestCase { - private Project project; - private AnalysisScope analysisScope; - private MetricsProfile profile; - private QMoveEntitySearchResult searchResult; - -// @Override -// public void setUp() throws Exception { -// super.setUp(); -// } - - @Override - protected String getTestDataPath() { - return "testdata/" + getTestName(true); - } - - public void testMoveMethod() throws IOException { - final VirtualFile file1 = myFixture.copyFileToProject("ClassA.java"); - final VirtualFile file2 = myFixture.copyFileToProject("ClassB.java"); - - project = myFixture.getProject(); - analysisScope = new AnalysisScope(project, Arrays.asList(file1, file2)); - profile = MetricsProfilesUtil.createProfile("test_profile", QMoveClassEntity.getRequestedMetrics()); - MetricsRunImpl metricsRun = new MetricsRunImpl(); - metricsRun.setProfileName(profile.getName()); - metricsRun.setContext(analysisScope); - - searchResult = QMoveEntitySearcher.analyze(analysisScope, metricsRun); - } - - private static void calculateMoveMethodRefactorings(RefactoringExecutionContext context) { - assertEquals(2, context.getClassCount()); - assertEquals(6, context.getMethodsCount()); - assertEquals(4, context.getFieldsCount()); - - final Map refactoringsCCDA = toMap(context.getResultForName("CCDA") - .getRefactorings()); - assertEquals(1, refactoringsCCDA.size()); - assertEquals("ClassB.methodB1()", refactoringsCCDA.keySet().toArray()[0]); - assertEquals("ClassA", refactoringsCCDA.get("ClassB.methodB1()")); - - final Map refactoringsMRI = toMap(context.getResultForName("MRI") - .getRefactorings()); - assertEquals(1, refactoringsMRI.size()); - assertEquals("ClassB.methodB1()", refactoringsMRI.keySet().toArray()[0]); - assertEquals("ClassA", refactoringsMRI.get("ClassB.methodB1()")); - - -// final Set common = new HashSet<>(refactoringsCCDA.keySet()); -// common.retainAll(refactoringsMRI.keySet()); -// System.out.println("Common for ARI and CCDA: "); -// for (String move : common) { -// System.out.print(move + " to "); -// System.out.print(refactoringsCCDA.get(move)); -// if (!refactoringsMRI.get(move).equals(refactoringsCCDA.get(move))) { -// System.out.print(" vs " + refactoringsMRI.get(move)); -// } -// System.out.println(); -// } -// System.out.println(); -// -// assertEquals(new HashSet(Collections.singletonList("ClassB.methodB1()")), common); - - final Map refactoringsAKMeans = toMap(context.getResultForName("AKMeans") - .getRefactorings()); - -// Set refactoringsARIEC = new HashSet<>(refactoringsAKMeans.keySet()); -// refactoringsARIEC.retainAll(refactoringsMRI.keySet()); -// System.out.println("Common for ARI and EC: "); -// for (String move : refactoringsARIEC) { -// System.out.print(move + " to "); -// System.out.print(refactoringsAKMeans.get(move)); -// if (!refactoringsMRI.get(move).equals(refactoringsAKMeans.get(move))) { -// System.out.print(" vs " + refactoringsMRI.get(move)); -// } -// System.out.println(); -// } -// System.out.println(); - - // TODO: make AKMeans more deterministic somehow and get some assertions here -// assertEquals(new HashSet(Collections.singletonList("ClassB.methodB1()")), refactoringsARIEC); - - final Map refactoringsHAC = toMap(context.getResultForName("HAC") - .getRefactorings()); - - final Map expectedHAC = new HashMap<>(); - expectedHAC.put("ClassB.methodB1()", "ClassA"); - assertEquals(expectedHAC, refactoringsHAC); - - final Map refactoringsARI = toMap(context.getResultForName("ARI") - .getRefactorings()); - assertEquals(expectedHAC, refactoringsARI); - } -} \ No newline at end of file From b385a633ce9ef458ab11ce2fc71e0737ccadb4b8 Mon Sep 17 00:00:00 2001 From: Olga Lupuleac Date: Mon, 28 May 2018 13:54:12 +0300 Subject: [PATCH 15/15] changed PsiType and PsiClass in maps to String, speed increased --- .../java/org/ml_methods_group/algorithm/QMove.java | 13 +++++++------ .../algorithm/entity/QMoveRelevantProperties.java | 14 ++++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java index 5600a2bc..b8a2a7f2 100644 --- a/src/main/java/org/ml_methods_group/algorithm/QMove.java +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -25,6 +25,7 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; public class QMove extends Algorithm { @@ -34,7 +35,7 @@ public class QMove extends Algorithm { private QMoveMetric metric = new QMoveMetric(); private ExecutionContext context; private AtomicInteger progress = new AtomicInteger(); - private long start; + private AtomicLong start = new AtomicLong(); public QMove() { super("QMove", true); @@ -60,14 +61,14 @@ protected List calculateRefactorings( .forEach(classes::add); calculateMetrics(); System.err.println("started"); - start = System.currentTimeMillis(); - /* List refactorings = new ArrayList<>(); + start.set(System.currentTimeMillis()); + /*List refactorings = new ArrayList<>(); for (QMoveMethodEntity methodEntity : methodEntities) { findBestMoveForMethod(methodEntity, refactorings); }*/ return runParallel(methodEntities, context, ArrayList::new, this::findBestMoveForMethod, AlgorithmsUtil::combineLists); - // return refactorings; + //return refactorings; } @@ -77,7 +78,7 @@ private List findBestMoveForMethod(QMoveMethodEntity method, QMoveMetric copy = new QMoveMetric(metric); context.checkCanceled(); ResultHolder resultHolder = new ResultHolder(null, 0); - start = System.currentTimeMillis(); + start.set(System.currentTimeMillis()); if (method.getMoveAbility() == QMoveMethodEntity.MoveAbility.ANY_CLASS) { System.err.println(method.getName()); resultHolder = checkMoveForClasses(method, classes, copy); @@ -90,7 +91,7 @@ private List findBestMoveForMethod(QMoveMethodEntity method, } } } - System.err.println(System.currentTimeMillis() - start); + System.err.println(start.addAndGet(-System.currentTimeMillis())); if (resultHolder.target != null) { refactorings.add(new Refactoring(method.getName(), resultHolder.target.getName(), resultHolder.fitness, diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java index ecc2de78..4dbe7c15 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java @@ -26,9 +26,9 @@ class QMoveRelevantProperties { //cohesion private double sumIntersection; - private Map parametersOfMethods = new HashMap<>(); + private Map parametersOfMethods = new HashMap<>(); //coupling - private Map relatedClasses = new HashMap<>(); + private Map relatedClasses = new HashMap<>(); private Set outerClasses = new HashSet<>(); private Set innerClasses = new HashSet<>(); @@ -44,11 +44,13 @@ void incrementNumOfMethods(){ } void addToParameters(PsiType psiType){ - incrementMapValue(psiType, parametersOfMethods); + incrementMapValue(psiType.getPresentableText(), + parametersOfMethods); } void addToRelatedClasses(PsiClass psiClass){ - incrementMapValue(psiClass, relatedClasses); + incrementMapValue(psiClass.getQualifiedName(), + relatedClasses); } void increaseSumIntersection(int val){ @@ -59,11 +61,11 @@ int setMethodSumIntersection(){ sumIntersection = parametersOfMethods.size(); return parametersOfMethods.size(); } - Map getParametersOfMethods() { + Map getParametersOfMethods() { return parametersOfMethods; } - Map getRelatedClasses() { + Map getRelatedClasses() { return relatedClasses; }