From 8ac7a967e0c4f21cb920a61e5c50f98bdf1ed7cb Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Tue, 28 Mar 2017 16:30:06 +0300 Subject: [PATCH 01/68] Added fan-in metric for classes --- .../stockmetrics/JavaMetricProvider.java | 10 ++- .../FanInClassCalculator.java | 66 +++++++++++++++++++ .../classMetrics/FanInClassMetric.java | 52 +++++++++++++++ .../i18n/StockMetricsBundle.properties | 5 +- 4 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanInClassMetric.java diff --git a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java index 6dfb4d5..3501786 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -104,6 +104,7 @@ private static void initializeClassMetrics(Collection metrics) { metrics.add(new TodoCommentCountClassMetric()); metrics.add(new TrueCommentRatioClassMetric()); metrics.add(new WeightedMethodComplexityMetric()); + metrics.add(new FanInClassMetric()); } private static void initializeInterfaceMetrics(Collection metrics) { @@ -329,7 +330,7 @@ private static void initializeProjectMetrics(Collection metrics) { @NotNull @Override public List getPrebuiltProfiles() { - final List out = new ArrayList(10); + final List out = new ArrayList(11); out.add(createChidamberKemererProfile()); out.add(createClassCountProfile()); out.add(createCodeSizeProfile()); @@ -340,9 +341,16 @@ public List getPrebuiltProfiles() { out.add(createMartinProfile()); out.add(createMoodProfile()); out.add(createTestProfile()); + out.add(createFanProfile()); return out; } + private static PrebuiltMetricProfile createFanProfile() { + final PrebuiltMetricProfile profile = new PrebuiltMetricProfile(StockMetricsBundle.message("fan.profile.name")); + profile.addMetric(FanInClassMetric.class); + return profile; + } + private static PrebuiltMetricProfile createChidamberKemererProfile() { final PrebuiltMetricProfile profile = new PrebuiltMetricProfile(StockMetricsBundle.message("chidamber.kemerer.metrics.profile.name")); diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java new file mode 100644 index 0000000..1f83626 --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java @@ -0,0 +1,66 @@ +/* + * 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.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiMethodCallExpression; + +import java.util.Stack; + +/** + * Created by Aleksandr Chudov on 27.03.2017. + */ +public class FanInClassCalculator extends ClassCalculator { + private final Stack fanInMetrics = new Stack(); + private int currentMetric = -1; + private final Stack classes = new Stack(); + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + + @Override + public void visitClass(PsiClass aClass) { + if (currentMetric != -1) { + fanInMetrics.push(Integer.valueOf(currentMetric)); + } + classes.push(aClass); + currentMetric = 0; + super.visitClass(aClass); + postMetric(aClass, currentMetric); + classes.pop(); + currentMetric = fanInMetrics.empty() ? -1 : fanInMetrics.pop().intValue(); + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (classes.empty()) { + return; + } + if (classes.peek().equals(expression.resolveMethod().getContainingClass())) { + return; + } + currentMetric++; + } + } +} diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanInClassMetric.java b/stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanInClassMetric.java new file mode 100644 index 0000000..e600352 --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanInClassMetric.java @@ -0,0 +1,52 @@ +/* + * 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.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.FanInClassCalculator; +import com.sixrr.stockmetrics.i18n.StockMetricsBundle; +import org.jetbrains.annotations.NotNull; + +/** + * Created by Aleksandr Chudov on 27.03.2017. + */ +public class FanInClassMetric extends ClassMetric { + @NotNull + @Override + public String getDisplayName() { + return StockMetricsBundle.message("fan.in.class.metric.display.name"); + } + + @NotNull + @Override + public String getAbbreviation() { + return StockMetricsBundle.message("fan.in.class.metric.abbreviation"); + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new FanInClassCalculator(); + } +} diff --git a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties index 5d0a934..89205e4 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties +++ b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties @@ -377,4 +377,7 @@ abstractness.display.name=Abstractness abstractness.abbreviation=A number.of.packages.abbreviation=P number.of.children.display.name=Number of children -number.of.children.abbreviation=NOC \ No newline at end of file +number.of.children.abbreviation=NOC +fan.profile.name=Fan metrics +fan.in.class.metric.display.name=Fan-In class metric +fan.in.class.metric.abbreviation=Fan-In \ No newline at end of file From 2393a18e376e78e9546f12d59d8754a7e5f32fc5 Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Tue, 28 Mar 2017 18:11:29 +0300 Subject: [PATCH 02/68] Added fan-out class metric --- .../stockmetrics/JavaMetricProvider.java | 2 + .../FanOutClassCalculator.java | 68 +++++++++++++++++++ .../classMetrics/FanOutClassMetric.java | 52 ++++++++++++++ .../i18n/StockMetricsBundle.properties | 4 +- 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanOutClassMetric.java diff --git a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java index 3501786..b29bc3c 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -105,6 +105,7 @@ private static void initializeClassMetrics(Collection metrics) { metrics.add(new TrueCommentRatioClassMetric()); metrics.add(new WeightedMethodComplexityMetric()); metrics.add(new FanInClassMetric()); + metrics.add(new FanOutClassMetric()); } private static void initializeInterfaceMetrics(Collection metrics) { @@ -348,6 +349,7 @@ public List getPrebuiltProfiles() { private static PrebuiltMetricProfile createFanProfile() { final PrebuiltMetricProfile profile = new PrebuiltMetricProfile(StockMetricsBundle.message("fan.profile.name")); profile.addMetric(FanInClassMetric.class); + profile.addMetric(FanOutClassMetric.class); return profile; } diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java new file mode 100644 index 0000000..a43b35c --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java @@ -0,0 +1,68 @@ +/* + * 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.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiMethodCallExpression; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * Created by Aleksandr Chudov on 28.03.2017. + */ +public class FanOutClassCalculator extends ClassCalculator { + private final Map metrics = new HashMap(); + private final Stack classes = new Stack(); + + @Override + public void endMetricsRun() { + for (Map.Entry e : metrics.entrySet()) { + postMetric(e.getKey(), e.getValue()); + } + super.endMetricsRun(); + } + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + classes.push(aClass); + if (!metrics.containsKey(aClass)) + metrics.put(aClass, 0); + super.visitClass(aClass); + classes.pop(); + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + final PsiClass aClass = expression.resolveMethod().getContainingClass(); + if (classes.empty() || classes.peek().equals(aClass)) { + return; + } + int metric = metrics.containsKey(aClass) ? metrics.get(aClass).intValue() : 0; + metrics.put(aClass, metric + 1); + } + } +} diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanOutClassMetric.java b/stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanOutClassMetric.java new file mode 100644 index 0000000..8ca0d33 --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/classMetrics/FanOutClassMetric.java @@ -0,0 +1,52 @@ +/* + * 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.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.FanOutClassCalculator; +import com.sixrr.stockmetrics.i18n.StockMetricsBundle; +import org.jetbrains.annotations.NotNull; + +/** + * Created by Aleksandr Chudov on 28.03.2017. + */ +public class FanOutClassMetric extends ClassMetric { + @NotNull + @Override + public String getDisplayName() { + return StockMetricsBundle.message("fan.out.class.metric.display.name"); + } + + @NotNull + @Override + public String getAbbreviation() { + return StockMetricsBundle.message("fan.out.class.metric.abbreviation"); + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new FanOutClassCalculator(); + } +} diff --git a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties index 89205e4..24c209a 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties +++ b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties @@ -380,4 +380,6 @@ number.of.children.display.name=Number of children number.of.children.abbreviation=NOC fan.profile.name=Fan metrics fan.in.class.metric.display.name=Fan-In class metric -fan.in.class.metric.abbreviation=Fan-In \ No newline at end of file +fan.in.class.metric.abbreviation=Fan-In +fan.out.class.metric.display.name=Fan-Out class metric +fan.out.class.metric.abbreviation=Fan-Out \ No newline at end of file From 65bad440efc07be7ed007035d8e02ccdb0298a1e Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Sun, 2 Apr 2017 16:42:15 +0300 Subject: [PATCH 03/68] Fixed NPE when calls method from unexisting class --- .../classCalculators/FanInClassCalculator.java | 8 +++----- .../classCalculators/FanOutClassCalculator.java | 12 +++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java index 1f83626..9791b84 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java @@ -16,10 +16,7 @@ package com.sixrr.stockmetrics.classCalculators; -import com.intellij.psi.JavaRecursiveElementVisitor; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiMethodCallExpression; +import com.intellij.psi.*; import java.util.Stack; @@ -57,7 +54,8 @@ public void visitMethodCallExpression(PsiMethodCallExpression expression) { if (classes.empty()) { return; } - if (classes.peek().equals(expression.resolveMethod().getContainingClass())) { + final PsiMethod method = expression.resolveMethod(); + if (method == null || classes.peek().equals(method.getContainingClass())) { return; } currentMetric++; diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java index a43b35c..9a6375f 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java @@ -16,10 +16,7 @@ package com.sixrr.stockmetrics.classCalculators; -import com.intellij.psi.JavaRecursiveElementVisitor; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiMethodCallExpression; +import com.intellij.psi.*; import java.util.HashMap; import java.util.Map; @@ -57,7 +54,12 @@ public void visitClass(PsiClass aClass) { @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { - final PsiClass aClass = expression.resolveMethod().getContainingClass(); + super.visitMethodCallExpression(expression); + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass aClass = method.getContainingClass(); if (classes.empty() || classes.peek().equals(aClass)) { return; } From fcfe7236008e058d8c66c31b2cbc76831bde4572 Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Sun, 2 Apr 2017 16:58:45 +0300 Subject: [PATCH 04/68] Post Fan-Out metric only for classes from scope --- .../classCalculators/FanOutClassCalculator.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java index 9a6375f..bad6b7e 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java @@ -18,21 +18,20 @@ import com.intellij.psi.*; -import java.util.HashMap; -import java.util.Map; -import java.util.Stack; +import java.util.*; /** * Created by Aleksandr Chudov on 28.03.2017. */ public class FanOutClassCalculator extends ClassCalculator { private final Map metrics = new HashMap(); + private final Collection visitClasses = new ArrayList(); private final Stack classes = new Stack(); @Override public void endMetricsRun() { - for (Map.Entry e : metrics.entrySet()) { - postMetric(e.getKey(), e.getValue()); + for (PsiClass aClass : visitClasses) { + postMetric(aClass, metrics.get(aClass)); } super.endMetricsRun(); } @@ -46,8 +45,10 @@ private class Visitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { classes.push(aClass); - if (!metrics.containsKey(aClass)) + visitClasses.add(aClass); + if (!metrics.containsKey(aClass)) { metrics.put(aClass, 0); + } super.visitClass(aClass); classes.pop(); } From 59fb53f3d3cb1d52a8d902e5cec2555cf11caea7 Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Sun, 2 Apr 2017 17:51:10 +0300 Subject: [PATCH 05/68] Added Fan-In method metric --- .../stockmetrics/JavaMetricProvider.java | 2 + .../i18n/StockMetricsBundle.properties | 4 +- .../FanInMethodCalculator.java | 63 +++++++++++++++++++ .../methodMetrics/FanInMethodMetric.java | 53 ++++++++++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanInMethodMetric.java diff --git a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java index b29bc3c..e291e22 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -183,6 +183,7 @@ private static void initializeMethodMetrics(Collection metrics) { metrics.add(new SourceLinesOfCodeMethodMetric()); metrics.add(new TodoCommentCountMethodMetric()); metrics.add(new TrueCommentRatioMethodMetric()); + metrics.add(new FanInMethodMetric()); } private static void initializeModuleMetrics(Collection metrics) { @@ -350,6 +351,7 @@ private static PrebuiltMetricProfile createFanProfile() { final PrebuiltMetricProfile profile = new PrebuiltMetricProfile(StockMetricsBundle.message("fan.profile.name")); profile.addMetric(FanInClassMetric.class); profile.addMetric(FanOutClassMetric.class); + profile.addMetric(FanInMethodMetric.class); return profile; } diff --git a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties index 24c209a..6e096d7 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties +++ b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties @@ -382,4 +382,6 @@ fan.profile.name=Fan metrics fan.in.class.metric.display.name=Fan-In class metric fan.in.class.metric.abbreviation=Fan-In fan.out.class.metric.display.name=Fan-Out class metric -fan.out.class.metric.abbreviation=Fan-Out \ No newline at end of file +fan.out.class.metric.abbreviation=Fan-Out +fan.in.method.metric.display.name=Fan-In method metric +fan.in.method.metric.abbreviation=FIM \ No newline at end of file diff --git a/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java new file mode 100644 index 0000000..20490af --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java @@ -0,0 +1,63 @@ +/* + * 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.methodCalculators; + +import com.intellij.psi.*; + +import java.util.Stack; + +/** + * Created by Aleksandr Chudov on 02.04.2017. + */ +public class FanInMethodCalculator extends MethodCalculator { + private final Stack fanInMetrics = new Stack(); + private int currentMetric = -1; + private final Stack methods = new Stack(); + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitMethod(PsiMethod method) { + if (currentMetric != -1) { + fanInMetrics.push(Integer.valueOf(currentMetric)); + } + methods.push(method); + currentMetric = 0; + super.visitMethod(method); + postMetric(method, currentMetric); + methods.pop(); + currentMetric = fanInMetrics.empty() ? -1 : fanInMetrics.pop().intValue(); + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (methods.empty()) { + return; + } + final PsiMethod method = expression.resolveMethod(); + if (method == null || methods.peek().equals(method)) { + return; + } + currentMetric++; + } + } +} diff --git a/stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanInMethodMetric.java b/stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanInMethodMetric.java new file mode 100644 index 0000000..31dea1d --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanInMethodMetric.java @@ -0,0 +1,53 @@ +/* + * 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.methodMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.i18n.StockMetricsBundle; +import com.sixrr.stockmetrics.methodCalculators.FanInMethodCalculator; +import com.sixrr.stockmetrics.methodCalculators.MethodCalculator; +import org.jetbrains.annotations.NotNull; + +/** + * Created by Aleksandr Chudov on 02.04.2017. + */ +public class FanInMethodMetric extends MethodMetric { + @NotNull + @Override + public String getDisplayName() { + return StockMetricsBundle.message("fan.in.method.metric.display.name"); + } + + @NotNull + @Override + public String getAbbreviation() { + return StockMetricsBundle.message("fan.in.method.metric.abbreviation"); + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new FanInMethodCalculator(); + } +} From 20790844e326473c1e696d0d334d5d315ad489b6 Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Sun, 2 Apr 2017 18:10:53 +0300 Subject: [PATCH 06/68] Adde Fan-Out method metric --- .../stockmetrics/JavaMetricProvider.java | 2 + .../i18n/StockMetricsBundle.properties | 4 +- .../FanOutMethodCalculator.java | 67 +++++++++++++++++++ .../methodMetrics/FanOutMethodMetric.java | 52 ++++++++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java create mode 100644 stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanOutMethodMetric.java diff --git a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java index e291e22..4fea181 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -184,6 +184,7 @@ private static void initializeMethodMetrics(Collection metrics) { metrics.add(new TodoCommentCountMethodMetric()); metrics.add(new TrueCommentRatioMethodMetric()); metrics.add(new FanInMethodMetric()); + metrics.add(new FanOutMethodMetric()); } private static void initializeModuleMetrics(Collection metrics) { @@ -352,6 +353,7 @@ private static PrebuiltMetricProfile createFanProfile() { profile.addMetric(FanInClassMetric.class); profile.addMetric(FanOutClassMetric.class); profile.addMetric(FanInMethodMetric.class); + profile.addMetric(FanOutMethodMetric.class); return profile; } diff --git a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties index 6e096d7..7b4bd48 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties +++ b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties @@ -384,4 +384,6 @@ fan.in.class.metric.abbreviation=Fan-In fan.out.class.metric.display.name=Fan-Out class metric fan.out.class.metric.abbreviation=Fan-Out fan.in.method.metric.display.name=Fan-In method metric -fan.in.method.metric.abbreviation=FIM \ No newline at end of file +fan.in.method.metric.abbreviation=FIM +fan.out.method.metric.display.name=Fan-Out method metric +fan.out.method.metric.abbreviation=FOM \ No newline at end of file diff --git a/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java new file mode 100644 index 0000000..eff8c1f --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java @@ -0,0 +1,67 @@ +/* + * 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.methodCalculators; + +import com.intellij.psi.*; + +import java.util.*; + +/** + * Created by Aleksandr Chudov on 02.04.2017. + */ +public class FanOutMethodCalculator extends MethodCalculator { + private final Map metrics = new HashMap(); + private final Collection visitMethods = new ArrayList(); + private final Stack methods = new Stack(); + + @Override + public void endMetricsRun() { + for (PsiMethod method : visitMethods) { + postMetric(method, metrics.get(method)); + } + super.endMetricsRun(); + } + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitMethod(PsiMethod method) { + methods.push(method); + visitMethods.add(method); + if (!metrics.containsKey(method)) { + metrics.put(method, 0); + } + super.visitMethod(method); + methods.pop(); + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiMethod method = expression.resolveMethod(); + if (method == null || methods.empty() || methods.peek().equals(method)) { + return; + } + int metric = metrics.containsKey(method) ? metrics.get(method).intValue() : 0; + metrics.put(method, metric + 1); + } + } +} diff --git a/stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanOutMethodMetric.java b/stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanOutMethodMetric.java new file mode 100644 index 0000000..acee8b9 --- /dev/null +++ b/stockmetrics/src/com/sixrr/stockmetrics/methodMetrics/FanOutMethodMetric.java @@ -0,0 +1,52 @@ +/* + * 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.methodMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.i18n.StockMetricsBundle; +import com.sixrr.stockmetrics.methodCalculators.FanOutMethodCalculator; +import org.jetbrains.annotations.NotNull; + +/** + * Created by Aleksandr Chudov on 02.04.2017. + */ +public class FanOutMethodMetric extends MethodMetric { + @NotNull + @Override + public String getDisplayName() { + return StockMetricsBundle.message("fan.out.method.metric.display.name"); + } + + @NotNull + @Override + public String getAbbreviation() { + return StockMetricsBundle.message("fan.out.method.metric.abbreviation"); + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new FanOutMethodCalculator(); + } +} From bc5c68a5918b067fb87d73a45be111c97a323eb5 Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Sun, 2 Apr 2017 18:11:45 +0300 Subject: [PATCH 07/68] Fix abbreviations for fan class metrics --- .../com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties index 7b4bd48..297d14e 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties +++ b/stockmetrics/src/com/sixrr/stockmetrics/i18n/StockMetricsBundle.properties @@ -380,9 +380,9 @@ number.of.children.display.name=Number of children number.of.children.abbreviation=NOC fan.profile.name=Fan metrics fan.in.class.metric.display.name=Fan-In class metric -fan.in.class.metric.abbreviation=Fan-In +fan.in.class.metric.abbreviation=FIC fan.out.class.metric.display.name=Fan-Out class metric -fan.out.class.metric.abbreviation=Fan-Out +fan.out.class.metric.abbreviation=FOC fan.in.method.metric.display.name=Fan-In method metric fan.in.method.metric.abbreviation=FIM fan.out.method.metric.display.name=Fan-Out method metric From d07dc502e703cb00efdb0ffe437c3f05e3b76bc7 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Sun, 2 Apr 2017 20:59:49 +0300 Subject: [PATCH 08/68] add metrics profile for refactoring --- src/META-INF/plugin.xml | 4 ++-- .../sixrr/stockmetrics/JavaMetricProvider.java | 16 +++++++++++++++- .../i18n/StockMetricsBundle.properties | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/META-INF/plugin.xml b/src/META-INF/plugin.xml index 5055230..05e8f51 100644 --- a/src/META-INF/plugin.xml +++ b/src/META-INF/plugin.xml @@ -68,8 +68,8 @@ - + " + refactorings.get(ent)); + } } }.execute(profile, metricsRun); } diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index 57f896f..4bb53e7 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -16,7 +16,6 @@ package vector.model; -import com.intellij.codeInspection.bytecodeAnalysis.Direction; import com.intellij.psi.PsiField; import com.intellij.psi.PsiMethod; import com.sixrr.metrics.MetricCategory; @@ -43,8 +42,7 @@ public CCDA(List entities) { } } - aCoefficients = new ArrayList(idCommunity.size()); - + aCoefficients = new ArrayList(Collections.nCopies(idCommunity.size() + 1, 0)); buildGraph(); } @@ -84,6 +82,14 @@ public void buildGraph() { graph.put(ent.getName(), neighbors); } + + System.out.println("Graph built:"); + for (String ent : graph.keySet()) { + System.out.println(ent); + for (String neighbor : graph.get(ent)) { + System.out.println(" -> " + neighbor); + } + } } public Map run() { @@ -91,19 +97,54 @@ public Map run() { q = calculateQualityIndex(); System.out.println(q); + Double dq = 1.0; + System.out.println("Running..."); + while (dq > 0) { + dq = 0.0; + int id = -1; + Integer community = -1; + for (int i = 0; i < nodes.size(); ++i) { + Entity ent = nodes.get(i); + for (int j = 1; j <= idCommunity.size(); ++j) { + if (j == communityId.get(ent.getName())) { + continue; + } + Double curdq = move(ent, j, true); + if (curdq > dq) { + dq = curdq; + id = i; + community = j; + } + } + } + + if (dq > 0) { + refactorings.put(nodes.get(id).getName(), idCommunity.get(community - 1)); + move(nodes.get(id), community, false); + System.out.println("move " + nodes.get(id).getName() + " to " + idCommunity.get(community - 1)); + System.out.println("quality index is now: " + q); + System.out.println(); + } + } + return refactorings; } - public void move(Entity ent, Integer from, Integer to) { + public Double move(Entity ent, Integer to, boolean rollback) { + String name = ent.getName(); + Integer from = communityId.get(name); Double dq = 0.0; dq += Math.pow(aCoefficients.get(from) * 1.0 / edges, 2); dq += Math.pow(aCoefficients.get(to) * 1.0 / edges, 2); - aCoefficients.add(from, 0); - aCoefficients.add(to, 0); + if (!rollback) { + aCoefficients.add(from, 0); + aCoefficients.add(to, 0); + } + Integer aFrom = 0; Integer aTo = 0; Integer de = 0; - String name = ent.getName(); + for (String neighbor : graph.get(name)) { if (communityId.get(neighbor).equals(from)) { de--; @@ -118,12 +159,23 @@ public void move(Entity ent, Integer from, Integer to) { } } - aCoefficients.add(from, aFrom); - aCoefficients.add(to, aTo); + aFrom = aCoefficients.get(from) - aFrom; + aTo = aCoefficients.get(to) + aTo; + + if (!rollback) { + aCoefficients.add(from, aFrom); + aCoefficients.add(to, aTo); + } + dq += de * 1.0 / edges; dq -= Math.pow(aFrom * 1.0 / edges, 2); dq -= Math.pow(aTo * 1.0 / edges, 2); - q += dq; + + if (!rollback) { + q += dq; + } + + return dq; } public Double calculateQualityIndex() { @@ -134,18 +186,12 @@ public Double calculateQualityIndex() { } edges /= 2; - - System.out.println(edges); - for (int i = 0; i < idCommunity.size(); ++i) { - String com = idCommunity.get(i); - System.out.println(com); + for (int i = 1; i <= idCommunity.size(); ++i) { + String com = idCommunity.get(i - 1); Integer e = 0; Integer a = 0; - System.out.println(graph.size()); for (String node : graph.keySet()) { - System.out.println(" " + node); - if (!communityId.containsKey(node)) { System.out.println("ERROR: unknown community"); } @@ -154,7 +200,6 @@ public Double calculateQualityIndex() { continue; } for (String neighbor : graph.get(node)) { - System.out.println(" -> " + neighbor); if (communityId.get(neighbor).equals(communityId.get(com))) { e++; } else { @@ -166,9 +211,7 @@ public Double calculateQualityIndex() { e /= 2; a += e; aCoefficients.add(i, a); - System.out.println(" " + e + " " + a); - qI += (e * 1.0 / edges) + Math.pow((a * 1.0 / edges), 2); - System.out.println("Q: " ); + qI += (e * 1.0 / edges) - Math.pow((a * 1.0 / edges), 2); } return qI; From 60c7e1a49e1014d6bc9e0b85402c90e8aca25de6 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 4 May 2017 20:18:36 +0300 Subject: [PATCH 30/68] add small example for testing CCDA --- .../model/examples/example1/Computer.java | 39 +++++++++++++++++++ .../model/examples/example1/Laptop.java | 31 +++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/vector/model/examples/example1/Computer.java create mode 100644 src/vector/model/examples/example1/Laptop.java diff --git a/src/vector/model/examples/example1/Computer.java b/src/vector/model/examples/example1/Computer.java new file mode 100644 index 0000000..9e2cf1f --- /dev/null +++ b/src/vector/model/examples/example1/Computer.java @@ -0,0 +1,39 @@ +/* + * 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 vector.model.examples.example1; + +/** + * Created by Kivi on 02.05.2017. + */ +public class Computer { + Computer() { + System.out.println("Constructor of Computer class."); + } + + void computer_method() { + System.out.println("Power gone! Shut down your PC soon..."); + } + + public static void main(String[] args) { + Computer my = new Computer(); + Laptop your = new Laptop(); + + my.computer_method(); + your.laptop_method(); + } +} + diff --git a/src/vector/model/examples/example1/Laptop.java b/src/vector/model/examples/example1/Laptop.java new file mode 100644 index 0000000..8f03b19 --- /dev/null +++ b/src/vector/model/examples/example1/Laptop.java @@ -0,0 +1,31 @@ +/* + * 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 vector.model.examples.example1; + +/** + * Created by Kivi on 02.05.2017. + */ +public class Laptop { + Laptop() { + System.out.println("Constructor of Laptop class."); + } + + void laptop_method() { + System.out.println("99% Battery available."); + } +} + From 72cf32017c68119bfd6ea0142158766ffcbce0ca Mon Sep 17 00:00:00 2001 From: kivi239 Date: Fri, 5 May 2017 19:17:35 +0300 Subject: [PATCH 31/68] fix: update the community after moving --- src/vector/model/CCDA.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index 4bb53e7..b0990cb 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -121,6 +121,7 @@ public Map run() { if (dq > 0) { refactorings.put(nodes.get(id).getName(), idCommunity.get(community - 1)); move(nodes.get(id), community, false); + communityId.put(nodes.get(id).getName(), community); System.out.println("move " + nodes.get(id).getName() + " to " + idCommunity.get(community - 1)); System.out.println("quality index is now: " + q); System.out.println(); From 75470bf9b996bc0a55c84b86fba9c0896430a4e8 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Fri, 5 May 2017 19:19:42 +0300 Subject: [PATCH 32/68] add another sample --- .../model/examples/example2/class_A.java | 46 +++++++++++++++++++ .../model/examples/example2/class_B.java | 46 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/vector/model/examples/example2/class_A.java create mode 100644 src/vector/model/examples/example2/class_B.java diff --git a/src/vector/model/examples/example2/class_A.java b/src/vector/model/examples/example2/class_A.java new file mode 100644 index 0000000..f9d3597 --- /dev/null +++ b/src/vector/model/examples/example2/class_A.java @@ -0,0 +1,46 @@ +/* + * 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 vector.model.examples.example2; + +/** + * Created by Kivi on 05.05.2017. + */ +class class_A +{ + static void methodA1() + { + attributeA1=0; + methodA2(); + } + + static void methodA2() + { + attributeA2=0; + attributeA1=0; + } + + static void methodA3() + { + attributeA1=0; + attributeA2=0; + methodA1(); + methodA2(); + } + + static int attributeA1; + static int attributeA2; +} diff --git a/src/vector/model/examples/example2/class_B.java b/src/vector/model/examples/example2/class_B.java new file mode 100644 index 0000000..12b5e0b --- /dev/null +++ b/src/vector/model/examples/example2/class_B.java @@ -0,0 +1,46 @@ +/* + * 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 vector.model.examples.example2; + +/** + * Created by Kivi on 05.05.2017. + */ +class class_B +{ + static void methodB1() + { + class_A.attributeA1=0; + class_A.attributeA2=0; + class_A.methodA1(); + } + + static void methodB2() + { + attributeB1=0; + attributeB2=0; + } + + static void methodB3() + { + attributeB1=0; + methodB1(); + methodB2(); + } + + static int attributeB1; + static int attributeB2; +} From 253b5996ae054c04552c023955ef2377d895e8b8 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 10 May 2017 15:56:23 +0300 Subject: [PATCH 33/68] add method for re-evaluating the partition after applying indetified refactorings --- src/vector/model/CCDA.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index b0990cb..90d8d6d 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -45,6 +45,13 @@ public CCDA(List entities) { aCoefficients = new ArrayList(Collections.nCopies(idCommunity.size() + 1, 0)); buildGraph(); } + + public void applyRefactorings(Map refactorings) { + for (String entity : refactorings.keySet()) { + String com = refactorings.get(entity); + communityId.put(entity, communityId.get(com)); + } + } public void buildGraph() { graph = new HashMap>(); From d4c9810b756f71d38cfe01cf1749680cdeb31385 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 10 May 2017 20:45:24 +0300 Subject: [PATCH 34/68] fix bug with anonymous classes --- .../plugin/AutomaticRefactoringAction.java | 15 +++++++++++- src/vector/model/PropertiesFinder.java | 24 +++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java index fe335e9..6750e4f 100644 --- a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java +++ b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java @@ -110,17 +110,25 @@ public void onFinish() { MetricsResult methodMetrics = metricsRun.getResultsForCategory(MetricCategory.Method); ArrayList entities = new ArrayList(); - + System.out.println("Classes: " + classMetrics.getMeasuredObjects().length); + System.out.println("Methods: " + methodMetrics.getMeasuredObjects().length); for (String obj : classMetrics.getMeasuredObjects()) { + if (obj.equals("null")) { + continue; + } Entity classEnt = new ClassEntity(obj, metricsRun, properties); entities.add(classEnt); } for (String obj : methodMetrics.getMeasuredObjects()) { + if (obj.substring(0, obj.indexOf('.')).equals("null")) { + continue; + } Entity methodEnt = new MethodEntity(obj, metricsRun, properties); entities.add(methodEnt); } Set fields = properties.getAllFields(); + System.out.println("Properties: " + fields.size()); for (String field : fields) { Entity fieldEnt = new FieldEntity(field, metricsRun, properties); entities.add(fieldEnt); @@ -146,6 +154,11 @@ public void onFinish() { for (String ent : refactorings.keySet()) { System.out.println(ent + " --> " + refactorings.get(ent)); } + + ARI alg2 = new ARI(entities); + System.out.println("Starting ARI..."); + refactorings = alg2.run(); + System.out.println("Finished ARI"); } }.execute(profile, metricsRun); } diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index 44bc0cc..1887db0 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -38,6 +38,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.java.AnonymousClassElement; import com.sixrr.metrics.utils.MethodUtils; import java.util.*; @@ -150,8 +151,13 @@ public void run() { private class EntityVisitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { + if (aClass instanceof AnonymousClassElement) { + return; + } + RelevantProperties rp = new RelevantProperties(); String fullName = aClass.getQualifiedName(); + classByName.put(fullName, aClass); rp.addClass(aClass); System.out.println(" !@! " + aClass.getName() + " : " + aClass.getQualifiedName()); @@ -196,6 +202,9 @@ public void visitReferenceExpression(PsiReferenceExpression expression) { System.out.println(" " + elem.getClass().getName()); if (elem instanceof PsiField) { PsiField field = (PsiField) elem; + if (methodStack.empty()) { + return; + } PsiMethod method = methodStack.peek(); String fullMethodName = MethodUtils.calculateSignature(method); String fullFieldName = field.getContainingClass().getQualifiedName() + "." + field.getName(); @@ -218,6 +227,9 @@ public void visitReferenceExpression(PsiReferenceExpression expression) { @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { PsiMethod element = expression.resolveMethod(); + if (methodStack.empty()) { + return; + } PsiMethod caller = methodStack.peek(); String callerName = MethodUtils.calculateSignature(caller); if (!methods.containsKey(callerName)) { @@ -228,9 +240,13 @@ public void visitMethodCallExpression(PsiMethodCallExpression expression) { @Override public void visitMethod(PsiMethod method) { + if (method.getContainingClass().equals(null)) { + return; + } String methodName = MethodUtils.calculateSignature(method); methodByName.put(methodName, method); - System.out.println(" !%! " + methodName); + System.out.println(" !%! " + methodName); + RelevantProperties rp = new RelevantProperties(); rp.addMethod(method); rp.addClass(method.getContainingClass()); @@ -251,8 +267,12 @@ public void visitMethod(PsiMethod method) { @Override public void visitField(PsiField field) { + if (field.getContainingClass().equals(null)) { + return; + } String name = field.getContainingClass().getQualifiedName() + "." + field.getName(); - System.out.println(" !$! " + name); + System.out.println(" !$! " + name); + if (!properties.containsKey(name)) { RelevantProperties rp = new RelevantProperties(); properties.put(name, rp); From 442bb81ad41493f1c2d6f2e23d74a675d1433b5d Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 11 May 2017 11:20:05 +0300 Subject: [PATCH 35/68] fix in applyRefactorings: new classes may appear in refactorings list --- src/vector/model/CCDA.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index 90d8d6d..fefa624 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -45,10 +45,14 @@ public CCDA(List entities) { aCoefficients = new ArrayList(Collections.nCopies(idCommunity.size() + 1, 0)); buildGraph(); } - + public void applyRefactorings(Map refactorings) { for (String entity : refactorings.keySet()) { String com = refactorings.get(entity); + if (!communityId.containsKey(com)) { + communityId.put(com, communityId.size() + 1); + idCommunity.add(com); + } communityId.put(entity, communityId.get(com)); } } From e4011e024fd57c691092beb30e408c0853d941da Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 11 May 2017 11:49:25 +0300 Subject: [PATCH 36/68] check vector components for null: bug with interfaces metrics --- src/vector/model/ClassEntity.java | 7 ++++++- src/vector/model/FieldEntity.java | 1 - src/vector/model/MethodEntity.java | 8 +++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vector/model/ClassEntity.java b/src/vector/model/ClassEntity.java index 9792243..c9b1556 100644 --- a/src/vector/model/ClassEntity.java +++ b/src/vector/model/ClassEntity.java @@ -44,13 +44,18 @@ public String getClassName() { protected Double[] initializeVector(MetricsRunImpl metricsRun) { Double[] vector = new Double[Dimension]; + for (int i = 0; i < Dimension; i++) { + vector[i] = 0.0; + } MetricCategory category = getCategory(); MetricsResult results = metricsRun.getResultsForCategory(category); for (Metric metric : metricsRun.getMetrics()) { if (metric.getCategory().equals(category)) { Integer id = components.get(metric.getAbbreviation()); - vector[id] = results.getValueForMetric(metric, getName()); + if (!results.getValueForMetric(metric, getName()).equals(null)) { + vector[id] = results.getValueForMetric(metric, getName()); + } } } diff --git a/src/vector/model/FieldEntity.java b/src/vector/model/FieldEntity.java index dfddd66..0b9b253 100644 --- a/src/vector/model/FieldEntity.java +++ b/src/vector/model/FieldEntity.java @@ -31,7 +31,6 @@ public FieldEntity(String entity_name, MetricsRunImpl metricsRun, PropertiesFind super(entity_name, metricsRun, propertiesFinder); RelevantProperties rp = propertiesFinder.getProperties(entity_name); double temp = rp.numberOfMethods(); - System.out.println(temp); vector[3] = Double.valueOf(temp); } diff --git a/src/vector/model/MethodEntity.java b/src/vector/model/MethodEntity.java index 6177454..cf03c44 100644 --- a/src/vector/model/MethodEntity.java +++ b/src/vector/model/MethodEntity.java @@ -38,6 +38,10 @@ public MetricCategory getCategory() { protected Double[] initializeVector(MetricsRunImpl metricsRun) { Double[] vector = new Double[Dimension]; + for (int i = 0; i < Dimension; i++) { + vector[i] = 0.0; + } + MetricCategory category = getCategory(); MetricsResult results = metricsRun.getResultsForCategory(category); MetricsResult classResults = metricsRun.getResultsForCategory(MetricCategory.Class); @@ -52,7 +56,9 @@ protected Double[] initializeVector(MetricsRunImpl metricsRun) { for (Metric metric : metricsRun.getMetrics()) { if (metric.getCategory().equals(category)) { Integer id = components.get(metric.getAbbreviation()); - vector[id] = results.getValueForMetric(metric, getName()); + if (!results.getValueForMetric(metric, getName()).equals(null)) { + vector[id] = results.getValueForMetric(metric, getName()); + } } } From 19c65d441776ed153346b248d66a2f2967ed3904 Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Thu, 11 May 2017 12:43:31 +0300 Subject: [PATCH 37/68] Now fan class metrics calculate metrics only for concrete classes --- .../stockmetrics/classCalculators/FanInClassCalculator.java | 3 +++ .../stockmetrics/classCalculators/FanOutClassCalculator.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java index dab6e75..1c61470 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java @@ -42,6 +42,9 @@ protected PsiElementVisitor createVisitor() { private class Visitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { + if (!isConcreteClass(aClass)) { + return; + } if (!metrics.containsKey(aClass)) { metrics.put(aClass, new HashSet()); } diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java index 1010407..46ac39b 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java @@ -50,6 +50,9 @@ private class Visitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { + if (!isConcreteClass(aClass)) { + return; + } if (!metrics.containsKey(aClass)) { metrics.put(aClass, new HashSet()); } From 71f85b288f4f54aa1c918ed48e9b5b8cec0c36e7 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 11 May 2017 12:47:05 +0300 Subject: [PATCH 38/68] comment out debug output --- src/vector/model/PropertiesFinder.java | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index 1887db0..a90c17f 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -160,46 +160,44 @@ public void visitClass(PsiClass aClass) { classByName.put(fullName, aClass); rp.addClass(aClass); - System.out.println(" !@! " + aClass.getName() + " : " + aClass.getQualifiedName()); + //System.out.println(" !@! " + aClass.getName() + " : " + aClass.getQualifiedName()); super.visitClass(aClass); PsiField[] fields = aClass.getAllFields(); for (PsiField field : fields) { - System.out.println(" " + field.getName()); + //System.out.println(" " + field.getName()); rp.addField(field); } - System.out.println(); + //System.out.println(); PsiMethod[] methods = aClass.getAllMethods(); for (PsiMethod method : methods) { rp.addMethod(method); - System.out.println(" " + method.getContainingClass().getName() + "." + method.getName()); + //System.out.println(" " + method.getContainingClass().getName() + "." + method.getName()); } - System.out.println(); + //System.out.println(); PsiClass[] supers = aClass.getSupers(); for (PsiClass sup : supers) { if (sup.isInterface()) { rp.addClass(sup); - System.out.println(" interface@ " + sup.getQualifiedName()); } else { if (!parents.containsKey(fullName)) { parents.put(fullName, new HashSet()); } parents.get(fullName).add(sup); - System.out.println(" superclass@ " + sup.getQualifiedName()); } } properties.put(fullName, rp); - System.out.println(); + //System.out.println(); } @Override public void visitReferenceExpression(PsiReferenceExpression expression) { - System.out.println(" !*! " + expression.getText()); + //System.out.println(" !*! " + expression.getText()); PsiElement elem = expression.resolve(); - System.out.println(" " + elem.getClass().getName()); + //System.out.println(" " + elem.getClass().getName()); if (elem instanceof PsiField) { PsiField field = (PsiField) elem; if (methodStack.empty()) { @@ -221,7 +219,7 @@ public void visitReferenceExpression(PsiReferenceExpression expression) { methods.get(fullFieldName).add(method); } - System.out.println(); + //System.out.println(); } @Override @@ -245,7 +243,7 @@ public void visitMethod(PsiMethod method) { } String methodName = MethodUtils.calculateSignature(method); methodByName.put(methodName, method); - System.out.println(" !%! " + methodName); + //System.out.println(" !%! " + methodName); RelevantProperties rp = new RelevantProperties(); rp.addMethod(method); @@ -259,10 +257,10 @@ public void visitMethod(PsiMethod method) { superMethods.put(methodName, new HashSet()); for (PsiMethod met : methods) { superMethods.get(methodName).add(met); - System.out.println(" " + met.getContainingClass().getQualifiedName() + "." + met.getName()); + //System.out.println(" " + met.getContainingClass().getQualifiedName() + "." + met.getName()); } - System.out.println(); + //System.out.println(); } @Override @@ -271,7 +269,7 @@ public void visitField(PsiField field) { return; } String name = field.getContainingClass().getQualifiedName() + "." + field.getName(); - System.out.println(" !$! " + name); + //System.out.println(" !$! " + name); if (!properties.containsKey(name)) { RelevantProperties rp = new RelevantProperties(); From 919dda18106a374f94889bb0c97b8dd626344d20 Mon Sep 17 00:00:00 2001 From: Aleksandr Chudov Date: Thu, 11 May 2017 13:27:15 +0300 Subject: [PATCH 39/68] Now fan metrics ignore lambda methods --- .../stockmetrics/classCalculators/FanInClassCalculator.java | 4 ++++ .../classCalculators/FanOutClassCalculator.java | 4 ++++ .../methodCalculators/FanInMethodCalculator.java | 4 ++++ .../methodCalculators/FanOutMethodCalculator.java | 6 ++++++ 4 files changed, 18 insertions(+) diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java index 1c61470..2b865b6 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanInClassCalculator.java @@ -68,6 +68,10 @@ public void visitClass(PsiClass aClass) { } } + @Override + public void visitLambdaExpression(PsiLambdaExpression expression) { + } + @Override public void visitCallExpression(PsiCallExpression callExpression) { super.visitCallExpression(callExpression); diff --git a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java index 46ac39b..e9493ad 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/classCalculators/FanOutClassCalculator.java @@ -77,6 +77,10 @@ public void visitClass(PsiClass aClass) { } } + @Override + public void visitLambdaExpression(PsiLambdaExpression expression) { + } + @Override public void visitCallExpression(PsiCallExpression callExpression) { super.visitCallExpression(callExpression); diff --git a/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java index 32aea4a..ef18dc6 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanInMethodCalculator.java @@ -53,6 +53,10 @@ public void visitMethod(PsiMethod method) { methods.pop(); } + @Override + public void visitLambdaExpression(PsiLambdaExpression expression) { + } + @Override public void visitCallExpression(PsiCallExpression callExpression) { super.visitCallExpression(callExpression); diff --git a/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java index 2706bab..e4b22d8 100644 --- a/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java +++ b/stockmetrics/src/com/sixrr/stockmetrics/methodCalculators/FanOutMethodCalculator.java @@ -17,7 +17,9 @@ package com.sixrr.stockmetrics.methodCalculators; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.sixrr.metrics.utils.BucketedCount; +import com.sixrr.metrics.utils.MethodUtils; import java.util.ArrayList; import java.util.Collection; @@ -53,6 +55,10 @@ public void visitMethod(PsiMethod method) { methods.pop(); } + @Override + public void visitLambdaExpression(PsiLambdaExpression expression) { + } + @Override public void visitCallExpression(PsiCallExpression callExpression) { super.visitCallExpression(callExpression); From 92212fe8cf2e6ad810707bd22fb6a50054958788 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 11 May 2017 13:30:31 +0300 Subject: [PATCH 40/68] check vector components for null: bug with interfaces metrics --- src/vector/model/ClassEntity.java | 2 +- src/vector/model/FieldEntity.java | 7 ++++++- src/vector/model/MethodEntity.java | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vector/model/ClassEntity.java b/src/vector/model/ClassEntity.java index c9b1556..1f91edb 100644 --- a/src/vector/model/ClassEntity.java +++ b/src/vector/model/ClassEntity.java @@ -53,7 +53,7 @@ protected Double[] initializeVector(MetricsRunImpl metricsRun) { for (Metric metric : metricsRun.getMetrics()) { if (metric.getCategory().equals(category)) { Integer id = components.get(metric.getAbbreviation()); - if (!results.getValueForMetric(metric, getName()).equals(null)) { + if (results.getValueForMetric(metric, getName()) != null) { vector[id] = results.getValueForMetric(metric, getName()); } } diff --git a/src/vector/model/FieldEntity.java b/src/vector/model/FieldEntity.java index 0b9b253..4ebfa38 100644 --- a/src/vector/model/FieldEntity.java +++ b/src/vector/model/FieldEntity.java @@ -40,12 +40,17 @@ public MetricCategory getCategory() { protected Double[] initializeVector(MetricsRunImpl metricsRun) { Double[] vector = new Double[Dimension]; + for (int i = 0; i < Dimension; i++) { + vector[i] = 0.0; + } MetricsResult classResults = metricsRun.getResultsForCategory(MetricCategory.Class); String className = getClassName(); for (Metric metric : metricsRun.getMetrics()) { if (metric.getCategory().equals(MetricCategory.Class)) { Integer id = components.get(metric.getAbbreviation()); - vector[id] = classResults.getValueForMetric(metric, className); + if (classResults.getValueForMetric(metric, className) != null) { + vector[id] = classResults.getValueForMetric(metric, className); + } } } diff --git a/src/vector/model/MethodEntity.java b/src/vector/model/MethodEntity.java index cf03c44..d8d7916 100644 --- a/src/vector/model/MethodEntity.java +++ b/src/vector/model/MethodEntity.java @@ -49,14 +49,16 @@ protected Double[] initializeVector(MetricsRunImpl metricsRun) { for (Metric metric : metricsRun.getMetrics()) { if (metric.getCategory().equals(MetricCategory.Class)) { Integer id = components.get(metric.getAbbreviation()); - vector[id] = classResults.getValueForMetric(metric, className); + if (classResults.getValueForMetric(metric, getName()) != null) { + vector[id] = classResults.getValueForMetric(metric, className); + } } } for (Metric metric : metricsRun.getMetrics()) { if (metric.getCategory().equals(category)) { Integer id = components.get(metric.getAbbreviation()); - if (!results.getValueForMetric(metric, getName()).equals(null)) { + if (results.getValueForMetric(metric, getName()) != null) { vector[id] = results.getValueForMetric(metric, getName()); } } From 64234b50b2ca480b45b0e71fbac12f0ff66c9a5d Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 11 May 2017 15:50:49 +0300 Subject: [PATCH 41/68] fixes for attributes and methods of anonymous classes or interfaces --- src/vector/model/CCDA.java | 14 ++++++++++++-- src/vector/model/PropertiesFinder.java | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index fefa624..cc4d2ac 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -37,7 +37,10 @@ public CCDA(List entities) { Integer id = communityId.size() + 1; communityId.put(ent.getName(), id); idCommunity.add(ent.getName()); - } else { + } + } + for (Entity ent : entities) { + if (!ent.getCategory().equals(MetricCategory.Class) && communityId.containsKey(ent.getClassName())) { nodes.add(ent); } } @@ -69,7 +72,7 @@ public void buildGraph() { Set methods = rp.getAllMethods(); for (PsiMethod method : methods) { String name = MethodUtils.calculateSignature(method); - if (name.equals(ent.getName())) { + if (name.equals(ent.getName()) || !communityId.containsKey(name)) { continue; } @@ -83,6 +86,9 @@ public void buildGraph() { for (PsiField field : rp.getAllFields()) { String name = field.getContainingClass().getQualifiedName() + "." + field.getName(); + if (name.equals(ent.getName()) || !communityId.containsKey(name)) { + continue; + } neighbors.add(name); if (!graph.containsKey(name)) { graph.put(name, new HashSet()); @@ -101,6 +107,7 @@ public void buildGraph() { System.out.println(" -> " + neighbor); } } + System.out.println("-----"); } public Map run() { @@ -191,6 +198,7 @@ public Double move(Entity ent, Integer to, boolean rollback) { } public Double calculateQualityIndex() { + System.out.println("Calculating Q..."); Double qI = 0.0; edges = 0; for (String node : graph.keySet()) { @@ -198,6 +206,7 @@ public Double calculateQualityIndex() { } edges /= 2; + System.out.println(edges); for (int i = 1; i <= idCommunity.size(); ++i) { String com = idCommunity.get(i - 1); Integer e = 0; @@ -206,6 +215,7 @@ public Double calculateQualityIndex() { for (String node : graph.keySet()) { if (!communityId.containsKey(node)) { System.out.println("ERROR: unknown community"); + System.out.println(node); } if (!communityId.get(node).equals(communityId.get(com))) { diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index a90c17f..525da69 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -238,7 +238,7 @@ public void visitMethodCallExpression(PsiMethodCallExpression expression) { @Override public void visitMethod(PsiMethod method) { - if (method.getContainingClass().equals(null)) { + if (method.getContainingClass() == null) { return; } String methodName = MethodUtils.calculateSignature(method); @@ -265,7 +265,7 @@ public void visitMethod(PsiMethod method) { @Override public void visitField(PsiField field) { - if (field.getContainingClass().equals(null)) { + if (field.getContainingClass() == null) { return; } String name = field.getContainingClass().getQualifiedName() + "." + field.getName(); From 7a11728ae5cc4e8d53c23cc66f03576aefe42da8 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 11 May 2017 16:33:03 +0300 Subject: [PATCH 42/68] fix: initialize communities for methods before building graph, add eps for Q --- src/vector/model/CCDA.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index cc4d2ac..a2c396e 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -42,6 +42,7 @@ public CCDA(List entities) { for (Entity ent : entities) { if (!ent.getCategory().equals(MetricCategory.Class) && communityId.containsKey(ent.getClassName())) { nodes.add(ent); + communityId.put(ent.getName(), communityId.get(ent.getClassName())); } } @@ -63,7 +64,6 @@ public void applyRefactorings(Map refactorings) { public void buildGraph() { graph = new HashMap>(); for (Entity ent : nodes) { - communityId.put(ent.getName(), communityId.get(ent.getClassName())); RelevantProperties rp = ent.getRelevantProperties(); HashSet neighbors = new HashSet(); if (graph.containsKey(ent.getName())) { @@ -117,7 +117,7 @@ public Map run() { Double dq = 1.0; System.out.println("Running..."); - while (dq > 0) { + while (dq > eps) { dq = 0.0; int id = -1; Integer community = -1; @@ -136,7 +136,7 @@ public Map run() { } } - if (dq > 0) { + if (dq > eps) { refactorings.put(nodes.get(id).getName(), idCommunity.get(community - 1)); move(nodes.get(id), community, false); communityId.put(nodes.get(id).getName(), community); @@ -248,4 +248,6 @@ public Double calculateQualityIndex() { private Double q; private Integer edges; + + private static Double eps = 1e-7; } From 8ad516cc9ae218ace415cfd86ec04a11cc546d3c Mon Sep 17 00:00:00 2001 From: kivi239 Date: Thu, 11 May 2017 16:55:23 +0300 Subject: [PATCH 43/68] fix bug: recalculating Q after moving --- src/vector/model/CCDA.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index a2c396e..fe3dda7 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -155,10 +155,6 @@ public Double move(Entity ent, Integer to, boolean rollback) { Double dq = 0.0; dq += Math.pow(aCoefficients.get(from) * 1.0 / edges, 2); dq += Math.pow(aCoefficients.get(to) * 1.0 / edges, 2); - if (!rollback) { - aCoefficients.add(from, 0); - aCoefficients.add(to, 0); - } Integer aFrom = 0; Integer aTo = 0; From 7bad1188abf48e87c1d7c70be2719e1749714e08 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 15 May 2017 18:48:03 +0300 Subject: [PATCH 44/68] change eps for dQ --- src/vector/model/CCDA.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index fe3dda7..05f3d3c 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -245,5 +245,5 @@ public Double calculateQualityIndex() { private Double q; private Integer edges; - private static Double eps = 1e-7; + private static Double eps = 1e-3; } From fed0185afe0d8b835c6232973ec593776f91a869 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 15 May 2017 18:48:24 +0300 Subject: [PATCH 45/68] fix bugs in distance function --- src/vector/model/Entity.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vector/model/Entity.java b/src/vector/model/Entity.java index 19b7082..70118c3 100644 --- a/src/vector/model/Entity.java +++ b/src/vector/model/Entity.java @@ -41,10 +41,14 @@ public double dist(Entity entity) { } int rpIntersect = entity.relevantProperties.sizeOfIntersect(relevantProperties); + if (rpIntersect == 0) { + return Double.MAX_VALUE; + } ans -= (rpIntersect) / (1.0 * relevantProperties.size() + entity.relevantProperties.size() - rpIntersect); - return ans; + ans /= (Dimension + 1); + return Math.sqrt(ans); } public static void normalize(ArrayList entities) { From da2a329b49c219a19e6cc4dd667319f8a976c403 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 15 May 2017 18:49:15 +0300 Subject: [PATCH 46/68] remove basic classes and methods from RP --- src/vector/model/PropertiesFinder.java | 6 ++++++ src/vector/model/RelevantProperties.java | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index 525da69..29996d8 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -145,6 +145,12 @@ public void run() { } } } + + for (String name : properties.keySet()) { + properties.get(name).retainClasses(classByName.values()); + properties.get(name).retainMethods(methodByName.values()); + } + } } diff --git a/src/vector/model/RelevantProperties.java b/src/vector/model/RelevantProperties.java index f1b2617..80ac7b4 100644 --- a/src/vector/model/RelevantProperties.java +++ b/src/vector/model/RelevantProperties.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiParameter; import com.sixrr.metrics.utils.MethodUtils; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -48,6 +49,14 @@ public void addField(PsiField field) { fields.add(field); } + public void retainMethods(Collection methodsSet) { + methods.retainAll(methodsSet); + } + + public void retainClasses(Collection classSet) { + classes.retainAll(classSet); + } + public void addOverrideMethod(PsiMethod method) { overrideMethods.add(method); } From db719c07a580b34b214e710f490a5e532ef989f7 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 15 May 2017 18:49:57 +0300 Subject: [PATCH 47/68] implemented ARI --- .../plugin/AutomaticRefactoringAction.java | 12 +- src/vector/model/ARI.java | 166 ++++++++++++++++++ 2 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 src/vector/model/ARI.java diff --git a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java index 6750e4f..683ff5b 100644 --- a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java +++ b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java @@ -134,17 +134,17 @@ public void onFinish() { entities.add(fieldEnt); } - for (Entity ent : entities) { + /*for (Entity ent : entities) { ent.print(); System.out.println(); - } + }*/ System.out.println("!!!\n"); Entity.normalize(entities); - for (Entity ent : entities) { + /*for (Entity ent : entities) { ent.print(); System.out.println(); - } + }*/ System.out.println("!!!\n"); @@ -157,8 +157,12 @@ public void onFinish() { ARI alg2 = new ARI(entities); System.out.println("Starting ARI..."); + //alg2.printTableDistances(); refactorings = alg2.run(); System.out.println("Finished ARI"); + for (String method : refactorings.keySet()) { + System.out.println(method + "--> " + refactorings.get(method)); + } } }.execute(profile, metricsRun); } diff --git a/src/vector/model/ARI.java b/src/vector/model/ARI.java new file mode 100644 index 0000000..62d0734 --- /dev/null +++ b/src/vector/model/ARI.java @@ -0,0 +1,166 @@ +/* + * 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 vector.model; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sixrr.metrics.MetricCategory; +import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansLloyd; +import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.initialization.RandomlyGeneratedInitialMeans; +import de.lmu.ifi.dbs.elki.data.Cluster; +import de.lmu.ifi.dbs.elki.data.Clustering; +import de.lmu.ifi.dbs.elki.data.NumberVector; +import de.lmu.ifi.dbs.elki.data.model.KMeansModel; +import de.lmu.ifi.dbs.elki.data.type.TypeUtil; +import de.lmu.ifi.dbs.elki.database.Database; +import de.lmu.ifi.dbs.elki.database.StaticArrayDatabase; +import de.lmu.ifi.dbs.elki.database.ids.*; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.datasource.ArrayAdapterDatabaseConnection; +import de.lmu.ifi.dbs.elki.datasource.DatabaseConnection; +import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractNumberVectorDistanceFunction; +import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; +import de.lmu.ifi.dbs.elki.algorithm.clustering.hierarchical.AnderbergHierarchicalClustering; +import de.lmu.ifi.dbs.elki.math.random.RandomFactory; + +/** + * Created by Kivi on 09.05.2017. + */ +public class ARI { + + public ARI(List entityList) { + entities = entityList; + } + + public Map run() { + Map refactorings = new HashMap(); + for (Entity entity : entities) { + if (entity.getCategory().equals(MetricCategory.Method)) { + String className = entity.getClassName(); + double minDist = Double.MAX_VALUE; + int idClass = -1; + for (int i = 0; i < entities.size(); ++i) { + Entity classEnt = entities.get(i); + if (classEnt.getCategory().equals(MetricCategory.Class)) { + double dist = entity.dist(classEnt); + if (dist < minDist) { + minDist = dist; + idClass = i; + } + } + } + + if (idClass == -1) { + System.out.println("WARNING: " + entity.getName() + " has no nearest class"); + } else { + if (!entities.get(idClass).getName().equals(entity.getClassName())) { + refactorings.put(entity.getName(), entities.get(idClass).getClassName()); + } + } + } + } + + return refactorings; + } + + public void printTableDistances() { + int maxLen = 0; + for (Entity ent : entities) { + maxLen = Math.max(maxLen, ent.getName().length() + 4); + } + + System.out.print(String.format("%1$" + maxLen + "s", "")); + for (Entity ent : entities) { + String name = String.format("%1$" + maxLen + "s", ent.getName()); + System.out.print(name); + } + System.out.println(); + + for (Entity ent : entities) { + String name = String.format("%1$" + maxLen + "s", ent.getName()); + System.out.print(name); + for (Entity entity : entities) { + double dist = ent.dist(entity); + String d = ""; + if (dist == Double.MAX_VALUE) { + d = String.format("%1$" + maxLen + "s", "inf"); + } else { + d = String.format(" %." + (maxLen - 4) + "f", dist); + } + System.out.print(d); + } + System.out.println(); + } + } + + /*public void ELKIrun() { + double[][] data = new double[entities.size()][1]; + for (int i = 0; i < entities.size(); ++i) { + data[i][0] = i; + } + + System.out.println(data.length); + DatabaseConnection dbc = new ArrayAdapterDatabaseConnection(data); + Database db = new StaticArrayDatabase(dbc, null); + db.initialize(); + + System.out.println("here 1"); + + RandomlyGeneratedInitialMeans init = new RandomlyGeneratedInitialMeans(RandomFactory.DEFAULT); + + KMeansLloyd km = new KMeansLloyd(new EntityDistance(), 5, 20, init); + System.out.println("here 2"); + Clustering c = km.run(db); + System.out.println("here 3"); + Relation rel = db.getRelation(TypeUtil.NUMBER_VECTOR_FIELD); + // We know that the ids must be a continuous range: + DBIDRange ids = (DBIDRange) rel.getDBIDs(); + + int i = 0; + for(Cluster clu : c.getAllClusters()) { + // K-means will name all clusters "Cluster" in lack of noise support: + System.out.println("#" + i + ": " + clu.getNameAutomatic()); + System.out.println("Size: " + clu.size()); + System.out.println("Center: " + clu.getModel().getPrototype().toString()); + // Iterate over objects: + System.out.print("Objects: "); + for(DBIDIter it = clu.getIDs().iter(); it.valid(); it.advance()) { + // To get the vector use: + NumberVector v = rel.get(it); + + // Offset within our DBID range: "line number" + final int offset = ids.getOffset(it); + System.out.print(" " + offset); + // Do NOT rely on using "internalGetIndex()" directly! + } + System.out.println(); + ++i; + } + } + + private class EntityDistance extends AbstractNumberVectorDistanceFunction { + @Override + public double distance(NumberVector v1, NumberVector v2) { + return entities.get(v1.intValue(0)).dist(entities.get(v2.intValue(0))); + } + } + */ + + List entities; +} From 9d452f12b252e1ab19cd0265a3f88142761a9a35 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 15 May 2017 20:49:18 +0300 Subject: [PATCH 48/68] fix bug with adding vector components for methods --- src/vector/model/MethodEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vector/model/MethodEntity.java b/src/vector/model/MethodEntity.java index d8d7916..2b2ebb0 100644 --- a/src/vector/model/MethodEntity.java +++ b/src/vector/model/MethodEntity.java @@ -49,7 +49,7 @@ protected Double[] initializeVector(MetricsRunImpl metricsRun) { for (Metric metric : metricsRun.getMetrics()) { if (metric.getCategory().equals(MetricCategory.Class)) { Integer id = components.get(metric.getAbbreviation()); - if (classResults.getValueForMetric(metric, getName()) != null) { + if (classResults.getValueForMetric(metric, className) != null) { vector[id] = classResults.getValueForMetric(metric, className); } } From 4e3b0a923ab2828f322d359f509812e94a0a4eef Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 15 May 2017 23:34:07 +0300 Subject: [PATCH 49/68] Update distance function --- src/vector/model/Entity.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/vector/model/Entity.java b/src/vector/model/Entity.java index 70118c3..0603afd 100644 --- a/src/vector/model/Entity.java +++ b/src/vector/model/Entity.java @@ -16,6 +16,7 @@ package vector.model; +import com.intellij.psi.PsiElement; import com.sixrr.metrics.MetricCategory; import com.sixrr.metrics.Metric; import com.sixrr.metrics.metricModel.MetricsResult; @@ -32,10 +33,11 @@ public Entity(String entity_name, MetricsRunImpl metricsRun, PropertiesFinder pr name = entity_name; vector = initializeVector(metricsRun); relevantProperties = propertiesFinder.getProperties(name); + psiEntity = propertiesFinder.getPsiElement(entity_name); } public double dist(Entity entity) { - double ans = 1; + double ans = 0.0; for (int i = 0; i < Dimension; i++) { ans += Math.pow(vector[i] - entity.vector[i], 2); } @@ -44,10 +46,10 @@ public double dist(Entity entity) { if (rpIntersect == 0) { return Double.MAX_VALUE; } - ans -= (rpIntersect) / (1.0 * relevantProperties.size() + entity.relevantProperties.size() - - rpIntersect); + ans += 2.0 * (1 - (rpIntersect) / (1.0 * relevantProperties.size() + entity.relevantProperties.size() - + rpIntersect)); - ans /= (Dimension + 1); + ans /= (Dimension + 2); return Math.sqrt(ans); } @@ -116,4 +118,10 @@ public void print() { relevantProperties.printAll(); } + + private PsiElement psiEntity; + + public PsiElement getPsiElement() { + return psiEntity; + } } From 649b930deead284a6f9ebbf7a3709cc9b3c9f7d4 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 15 May 2017 23:34:42 +0300 Subject: [PATCH 50/68] fix bug: swap fan-in & fan-out for fields --- src/vector/model/FieldEntity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vector/model/FieldEntity.java b/src/vector/model/FieldEntity.java index 4ebfa38..0ea0941 100644 --- a/src/vector/model/FieldEntity.java +++ b/src/vector/model/FieldEntity.java @@ -31,7 +31,7 @@ public FieldEntity(String entity_name, MetricsRunImpl metricsRun, PropertiesFind super(entity_name, metricsRun, propertiesFinder); RelevantProperties rp = propertiesFinder.getProperties(entity_name); double temp = rp.numberOfMethods(); - vector[3] = Double.valueOf(temp); + vector[2] = Double.valueOf(temp); } public MetricCategory getCategory() { @@ -54,7 +54,7 @@ protected Double[] initializeVector(MetricsRunImpl metricsRun) { } } - vector[2] = 0.0; + vector[3] = 0.0; return vector; } From bb528427d4de7da8d70520973d5c795420785e15 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 10:26:00 +0300 Subject: [PATCH 51/68] fix: method cannot move to parent class --- src/vector/model/ARI.java | 69 ++++++++++++++++++++++++-- src/vector/model/ClassEntity.java | 6 +++ src/vector/model/PSIUtil.java | 54 ++++++++++++++++++++ src/vector/model/PropertiesFinder.java | 31 +++++++++++- 4 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 src/vector/model/PSIUtil.java diff --git a/src/vector/model/ARI.java b/src/vector/model/ARI.java index 62d0734..869ea7d 100644 --- a/src/vector/model/ARI.java +++ b/src/vector/model/ARI.java @@ -16,10 +16,10 @@ package vector.model; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import com.sixrr.metrics.MetricCategory; import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansLloyd; import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.initialization.RandomlyGeneratedInitialMeans; @@ -44,8 +44,9 @@ */ public class ARI { - public ARI(List entityList) { + public ARI(List entityList, Set existingClasses) { entities = entityList; + allClasses = existingClasses; } public Map run() { @@ -69,7 +70,64 @@ public Map run() { if (idClass == -1) { System.out.println("WARNING: " + entity.getName() + " has no nearest class"); } else { - if (!entities.get(idClass).getName().equals(entity.getClassName())) { + if (entities.get(idClass).getName().equals(className)) { + continue; + } + + PsiMethod method = (PsiMethod) entity.getPsiElement(); + PsiClass moveFromClass = method.getContainingClass(); + PsiClass moveToClass = (PsiClass) entities.get(idClass).getPsiElement(); + + Set supersTo = PSIUtil.getAllSupers(moveToClass, allClasses);//new HashSet(Arrays.asList(moveToClass.getSupers())); + boolean isSuper = false; + + if (moveFromClass.getQualifiedName().equals("org.jhotdraw.draw.LineConnectionFigure") + && moveToClass.getQualifiedName().equals("org.jhotdraw.draw.LineFigure")) { + System.out.println("HERE!"); + } + + for (PsiClass sup : supersTo) { + if (sup.equals(moveFromClass)) { + isSuper = true; + break; + } + } + + Set supersFrom = PSIUtil.getAllSupers(moveFromClass, allClasses);//new HashSet(Arrays.asList(moveFromClass.getSupers())); + for (PsiClass sup : supersFrom) { + if (moveFromClass.getQualifiedName().equals("org.jhotdraw.draw.LineConnectionFigure") + && moveToClass.getQualifiedName().equals("org.jhotdraw.draw.LineFigure")) { + System.out.println(sup.getQualifiedName()); + } + if (sup.equals(moveToClass)) { + isSuper = true; + break; + } + } + supersFrom.retainAll(supersTo); + boolean isOverride = false; + + + + if (isSuper) { + continue; + } + + for (PsiClass sup : supersFrom) { + PsiMethod[] methods = sup.getMethods(); + for (PsiMethod m : methods) { + if (m.equals(method)) { + isOverride = true; + break; + } + } + + if (isOverride) { + break; + } + } + + if (!isOverride) { refactorings.put(entity.getName(), entities.get(idClass).getClassName()); } } @@ -163,4 +221,5 @@ public double distance(NumberVector v1, NumberVector v2) { */ List entities; + Set allClasses; } diff --git a/src/vector/model/ClassEntity.java b/src/vector/model/ClassEntity.java index 1f91edb..0429a92 100644 --- a/src/vector/model/ClassEntity.java +++ b/src/vector/model/ClassEntity.java @@ -16,6 +16,7 @@ package vector.model; +import com.intellij.psi.PsiClass; import com.sixrr.metrics.Metric; import com.sixrr.metrics.MetricCategory; import com.sixrr.metrics.metricModel.MetricsResult; @@ -24,6 +25,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Created by Kivi on 04.04.2017. @@ -68,4 +70,8 @@ protected HashSet findRelevantProperties() { return properties; } + + public Set getAllSupers(Set existing) { + return PSIUtil.getAllSupers((PsiClass) getPsiElement(), existing); + } } diff --git a/src/vector/model/PSIUtil.java b/src/vector/model/PSIUtil.java new file mode 100644 index 0000000..7c7b70e --- /dev/null +++ b/src/vector/model/PSIUtil.java @@ -0,0 +1,54 @@ +/* + * 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 vector.model; + +import com.intellij.psi.PsiClass; + +import java.util.HashSet; +import java.util.Set; + +/** + * Created by Kivi on 16.05.2017. + */ +public class PSIUtil { + public static Set getAllSupers(PsiClass aClass, Set existing) { + Set allSupers = new HashSet(); + if (!existing.contains(aClass)) { + return allSupers; + } + + PsiClass[] supers = aClass.getSupers(); + for (PsiClass sup : supers) { + allSupers.add(sup); + allSupers.addAll(getAllSupers(sup, existing)); + } + + return allSupers; + } + + public static Set getAllSupers(PsiClass aClass) { + Set allSupers = new HashSet(); + + PsiClass[] supers = aClass.getSupers(); + for (PsiClass sup : supers) { + allSupers.add(sup); + allSupers.addAll(getAllSupers(sup)); + } + + return allSupers; + } +} diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index 29996d8..bc4cc36 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -50,6 +50,10 @@ public class PropertiesFinder { public PsiElementVisitor createVisitor() {return new FileVisitor();} + public boolean hasElement(String name) { + return properties.containsKey(name); + } + public RelevantProperties getProperties(String name) { return properties.get(name); @@ -89,6 +93,26 @@ public Set getAllFields() { private Stack methodStack = new Stack(); + private Set allClasses = new HashSet(); + + public Set getAllClasses() { + return allClasses; + } + + public PsiElement getPsiElement(String name) { + if (methodByName.containsKey(name)) { + return methodByName.get(name); + } + if (classByName.containsKey(name)) { + return classByName.get(name); + } + if (fieldByName.containsKey(name)) { + return fieldByName.get(name); + } + + return null; + } + private class FileVisitor extends JavaElementVisitor { @Override @@ -161,6 +185,8 @@ public void visitClass(PsiClass aClass) { return; } + allClasses.add(aClass); + RelevantProperties rp = new RelevantProperties(); String fullName = aClass.getQualifiedName(); @@ -181,7 +207,7 @@ public void visitClass(PsiClass aClass) { } //System.out.println(); - PsiClass[] supers = aClass.getSupers(); + Set supers = PSIUtil.getAllSupers(aClass);//aClass.getSupers(); for (PsiClass sup : supers) { if (sup.isInterface()) { rp.addClass(sup); @@ -247,6 +273,9 @@ public void visitMethod(PsiMethod method) { if (method.getContainingClass() == null) { return; } + if (method.getContainingClass().isInterface()) { + return; + } String methodName = MethodUtils.calculateSignature(method); methodByName.put(methodName, method); //System.out.println(" !%! " + methodName); From c105cd6db40c450ef9a74653fe2613dab6c3f43f Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 13:06:20 +0300 Subject: [PATCH 52/68] added getAllSupers for methods --- src/vector/model/PSIUtil.java | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/vector/model/PSIUtil.java b/src/vector/model/PSIUtil.java index 7c7b70e..e838841 100644 --- a/src/vector/model/PSIUtil.java +++ b/src/vector/model/PSIUtil.java @@ -17,6 +17,7 @@ package vector.model; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import java.util.HashSet; import java.util.Set; @@ -33,6 +34,9 @@ public static Set getAllSupers(PsiClass aClass, Set existing PsiClass[] supers = aClass.getSupers(); for (PsiClass sup : supers) { + if (!existing.contains(sup)) { + continue; + } allSupers.add(sup); allSupers.addAll(getAllSupers(sup, existing)); } @@ -51,4 +55,34 @@ public static Set getAllSupers(PsiClass aClass) { return allSupers; } + + public static Set getAllSupers(PsiMethod method, Set existing) { + if (!existing.contains(method.getContainingClass())) { + return new HashSet(); + } + Set allSupers = new HashSet(); + + PsiMethod[] supers = method.findSuperMethods(); + for (PsiMethod m : supers) { + if (!existing.contains(m.getContainingClass())) { + continue; + } + allSupers.add(m); + allSupers.addAll(getAllSupers(m, existing)); + } + + return allSupers; + } + + public static Set getAllSupers(PsiMethod method) { + Set allSupers = new HashSet(); + + PsiMethod[] supers = method.findSuperMethods(); + for (PsiMethod m : supers) { + allSupers.add(m); + allSupers.addAll(getAllSupers(m)); + } + + return allSupers; + } } From a8e8a4962a73f4831f0b653fb34b226b40bd7903 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 13:07:59 +0300 Subject: [PATCH 53/68] remove debug output --- src/vector/model/ARI.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/vector/model/ARI.java b/src/vector/model/ARI.java index 869ea7d..3e102e0 100644 --- a/src/vector/model/ARI.java +++ b/src/vector/model/ARI.java @@ -81,11 +81,6 @@ public Map run() { Set supersTo = PSIUtil.getAllSupers(moveToClass, allClasses);//new HashSet(Arrays.asList(moveToClass.getSupers())); boolean isSuper = false; - if (moveFromClass.getQualifiedName().equals("org.jhotdraw.draw.LineConnectionFigure") - && moveToClass.getQualifiedName().equals("org.jhotdraw.draw.LineFigure")) { - System.out.println("HERE!"); - } - for (PsiClass sup : supersTo) { if (sup.equals(moveFromClass)) { isSuper = true; @@ -95,10 +90,6 @@ public Map run() { Set supersFrom = PSIUtil.getAllSupers(moveFromClass, allClasses);//new HashSet(Arrays.asList(moveFromClass.getSupers())); for (PsiClass sup : supersFrom) { - if (moveFromClass.getQualifiedName().equals("org.jhotdraw.draw.LineConnectionFigure") - && moveToClass.getQualifiedName().equals("org.jhotdraw.draw.LineFigure")) { - System.out.println(sup.getQualifiedName()); - } if (sup.equals(moveToClass)) { isSuper = true; break; @@ -107,8 +98,6 @@ public Map run() { supersFrom.retainAll(supersTo); boolean isOverride = false; - - if (isSuper) { continue; } From 33333689383af992f9ec1381d047a49df6cdf153 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 13:08:38 +0300 Subject: [PATCH 54/68] add another file visitor for finding all classes before building RPs --- src/vector/model/PropertiesFinder.java | 65 ++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index bc4cc36..26dd9dc 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -35,6 +35,7 @@ * limitations under the License. */ +import com.intellij.analysis.AnalysisScope; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.*; @@ -48,7 +49,17 @@ */ public class PropertiesFinder { - public PsiElementVisitor createVisitor() {return new FileVisitor();} + public PsiElementVisitor createVisitor(final AnalysisScope analysisScope) { + final PsiElementVisitor visitor = new FileClassesCounter(); + ProgressManager.getInstance().runProcess(new Runnable() { + @Override + public void run() { + analysisScope.accept(visitor);; + } + }, new EmptyProgressIndicator()); + + return new FileVisitor(); + } public boolean hasElement(String name) { return properties.containsKey(name); @@ -113,12 +124,54 @@ public PsiElement getPsiElement(String name) { return null; } + + private class ClassCounter extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + if (aClass instanceof AnonymousClassElement) { + return; + } + + if (aClass.getQualifiedName() == null) { + return; + } + allClasses.add(aClass); + super.visitClass(aClass); + } + } + + + private class FileClassesCounter extends JavaElementVisitor { + + @Override + public void visitFile(final PsiFile file) { + System.out.println("!#! " + file.getName()); + + final PsiElementVisitor counter = new ClassCounter(); + ProgressManager.getInstance().runProcess(new Runnable() { + @Override + public void run() { + file.accept(counter); + } + }, new EmptyProgressIndicator()); + + } + } + private class FileVisitor extends JavaElementVisitor { @Override public void visitFile(final PsiFile file) { System.out.println("!#! " + file.getName()); + final PsiElementVisitor counter = new ClassCounter(); + ProgressManager.getInstance().runProcess(new Runnable() { + @Override + public void run() { + file.accept(counter); + } + }, new EmptyProgressIndicator()); + final PsiElementVisitor visitor = new EntityVisitor(); ProgressManager.getInstance().runProcess(new Runnable() { @Override @@ -185,7 +238,10 @@ public void visitClass(PsiClass aClass) { return; } - allClasses.add(aClass); + if (aClass.getQualifiedName() == null) { + return; + } + //allClasses.add(aClass); RelevantProperties rp = new RelevantProperties(); String fullName = aClass.getQualifiedName(); @@ -288,9 +344,12 @@ public void visitMethod(PsiMethod method) { super.visitMethod(method); methodStack.pop(); - PsiMethod[] methods = method.findSuperMethods(); + Set methods = PSIUtil.getAllSupers(method, allClasses); superMethods.put(methodName, new HashSet()); for (PsiMethod met : methods) { + if (methodName.equals("org.jhotdraw.draw.AttributedFigure.clone()")) { + System.out.println("HERE: " + MethodUtils.calculateSignature(met)); + } superMethods.get(methodName).add(met); //System.out.println(" " + met.getContainingClass().getQualifiedName() + "." + met.getName()); } From 4d91db5d1cd1aeb20976f8711fcf351d3a7f964c Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 13:10:47 +0300 Subject: [PATCH 55/68] add another file visitor for finding all classes before building RPs --- .../plugin/AutomaticRefactoringAction.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java index 683ff5b..ed6c51a 100644 --- a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java +++ b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java @@ -28,6 +28,7 @@ import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElementVisitor; import com.sixrr.metrics.config.MetricsReloadedConfig; import com.sixrr.metrics.metricModel.*; @@ -82,7 +83,7 @@ protected void analyze(@NotNull final Project project, @NotNull final AnalysisSc final MetricsRunImpl metricsRun = new MetricsRunImpl(); final PropertiesFinder properties = new PropertiesFinder(); - final PsiElementVisitor visitor = properties.createVisitor(); + final PsiElementVisitor visitor = properties.createVisitor(analysisScope); ProgressManager.getInstance().runProcess(new Runnable() { @Override public void run() { @@ -123,15 +124,17 @@ public void onFinish() { if (obj.substring(0, obj.indexOf('.')).equals("null")) { continue; } - Entity methodEnt = new MethodEntity(obj, metricsRun, properties); - entities.add(methodEnt); + if (properties.hasElement(obj)) { + Entity methodEnt = new MethodEntity(obj, metricsRun, properties); + entities.add(methodEnt); + } } Set fields = properties.getAllFields(); System.out.println("Properties: " + fields.size()); for (String field : fields) { Entity fieldEnt = new FieldEntity(field, metricsRun, properties); - entities.add(fieldEnt); + //entities.add(fieldEnt); } /*for (Entity ent : entities) { @@ -155,13 +158,13 @@ public void onFinish() { System.out.println(ent + " --> " + refactorings.get(ent)); } - ARI alg2 = new ARI(entities); + ARI alg2 = new ARI(entities, properties.getAllClasses()); System.out.println("Starting ARI..."); //alg2.printTableDistances(); refactorings = alg2.run(); System.out.println("Finished ARI"); for (String method : refactorings.keySet()) { - System.out.println(method + "--> " + refactorings.get(method)); + System.out.println(method + " --> " + refactorings.get(method)); } } }.execute(profile, metricsRun); From 71d5a7afbdf3ba8348d51044fdafc690d24831dc Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 18:24:49 +0300 Subject: [PATCH 56/68] recalculate RPs after method moving --- src/vector/model/ARI.java | 5 ++++- src/vector/model/Entity.java | 20 ++++++++++++++++++++ src/vector/model/RelevantProperties.java | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/vector/model/ARI.java b/src/vector/model/ARI.java index 3e102e0..b83c23d 100644 --- a/src/vector/model/ARI.java +++ b/src/vector/model/ARI.java @@ -117,7 +117,10 @@ public Map run() { } if (!isOverride) { - refactorings.put(entity.getName(), entities.get(idClass).getClassName()); + Entity newClass = entities.get(idClass); + refactorings.put(entity.getName(), newClass.getClassName()); + entity.moveToClass((PsiClass) newClass.getPsiElement()); + newClass.removeFromClass((PsiMethod) entity.getPsiElement()); } } } diff --git a/src/vector/model/Entity.java b/src/vector/model/Entity.java index 0603afd..51d9a89 100644 --- a/src/vector/model/Entity.java +++ b/src/vector/model/Entity.java @@ -16,7 +16,10 @@ package vector.model; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiMethod; import com.sixrr.metrics.MetricCategory; import com.sixrr.metrics.Metric; import com.sixrr.metrics.metricModel.MetricsResult; @@ -70,6 +73,23 @@ public static void normalize(ArrayList entities) { } } + public void moveToClass(PsiClass newClass) { + Set oldClasses = relevantProperties.getAllClasses(); + for (PsiClass oldClass : oldClasses) { + relevantProperties.removeClass(oldClass); + } + relevantProperties.addClass(newClass); + } + + public void removeFromClass(PsiMethod method) { + relevantProperties.removeMethod(method); + } + + + public void removeFromClass(PsiField field) { + relevantProperties.removeField(field); + } + public static final int Dimension = 4; public RelevantProperties getRelevantProperties() { diff --git a/src/vector/model/RelevantProperties.java b/src/vector/model/RelevantProperties.java index 80ac7b4..e6afa3c 100644 --- a/src/vector/model/RelevantProperties.java +++ b/src/vector/model/RelevantProperties.java @@ -37,6 +37,18 @@ public RelevantProperties() { overrideMethods = new HashSet(); }; + public void removeMethod(PsiMethod method) { + methods.remove(method); + } + + public void removeField(PsiField field) { + fields.remove(field); + } + + public void removeClass(PsiClass aClass) { + classes.remove(aClass); + } + public void addMethod(PsiMethod method) { methods.add(method); } @@ -73,6 +85,10 @@ public Set getAllMethods() { return new HashSet(methods); } + public Set getAllClasses() { + return new HashSet(classes); + } + public int size() { return classes.size() + fields.size() + methods.size(); } From 0a0dddc8880849017e02b67a7c48948aa9451e56 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 18:25:01 +0300 Subject: [PATCH 57/68] implemeted HAC --- src/vector/model/HAC.java | 130 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/vector/model/HAC.java diff --git a/src/vector/model/HAC.java b/src/vector/model/HAC.java new file mode 100644 index 0000000..2004fca --- /dev/null +++ b/src/vector/model/HAC.java @@ -0,0 +1,130 @@ +/* + * 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 vector.model; + +import com.sixrr.metrics.MetricCategory; + +import java.util.*; + +/** + * Created by Kivi on 16.05.2017. + */ +public class HAC { + public HAC(List entityList) { + entities = entityList; + for (Entity entity : entities) { + String name = entity.getName(); + HashSet simpleCommunity = new HashSet(); + simpleCommunity.add(entity); + communities.put(name, simpleCommunity); + communityIds.put(entity, name); + entityByName.put(name, entity); + } + } + + public Map run() { + Map refactorings = new HashMap(); + + double minD = 0.0; + while (minD < 1.0) { + minD = Double.MAX_VALUE; + String id1 = ""; + String id2 = ""; + for (Entity ent1 : communityIds.keySet()) { + final String i = communityIds.get(ent1); + for (Entity ent2 : communityIds.keySet()) { + if (ent1.getCategory() == MetricCategory.Class && ent2.getCategory() == MetricCategory.Class) { + continue; + } + final String j = communityIds.get(ent2); + if (i.equals(j)) { + continue; + } + + final double d = ent1.dist(ent2); + if (d < minD) { + if (entityByName.get(i).getCategory() == MetricCategory.Class + && entityByName.get(j).getCategory() == MetricCategory.Class) { + continue; + } + + minD = d; + id1 = i; + id2 = j; + } + } + } + + if (minD > 1.0) { + continue; + } + + if (entityByName.get(id1).getCategory() == MetricCategory.Class + && entityByName.get(id2).getCategory() == MetricCategory.Class) { + minD = Double.MAX_VALUE; + continue; + } + + mergeCommunities(id1, id2); + System.out.println("Merge " + id1 + " and " + id2 + " to " + communityIds.get(entityByName.get(id1))); + } + + for (Entity ent : communityIds.keySet()) { + if (!ent.getClassName().equals(communityIds.get(ent))) { + refactorings.put(ent.getName(), communityIds.get(ent)); + + } + } + + return refactorings; + } + + private void mergeCommunities(String id1, String id2) { + String newName = id1; + Set merge = new HashSet(communities.get(id1)); + merge.addAll(communities.get(id2)); + int maxInClass = 0; + for (Entity ent : merge) { + if (ent.getCategory() == MetricCategory.Class) { + int inClass = 0; + for (Entity entity : merge) { + if (entity.getClassName().equals(ent.getName())) { + inClass++; + } + } + + if (inClass > maxInClass) { + maxInClass = inClass; + newName = ent.getName(); + } + } + } + + communities.remove(id1); + communities.remove(id2); + communities.put(newName, merge); + for (Entity ent : merge) { + communityIds.put(ent, newName); + } + } + + private HashMap> communities = new HashMap>(); + private HashMap communityIds = new HashMap(); + + private List entities; + private HashMap entityByName = new HashMap(); +} From f75ebff3ada6112ff948d04cda7cff6171d332af Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 22:18:50 +0300 Subject: [PATCH 58/68] renamed algorithm --- src/vector/model/{ARI.java => MMRI.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/vector/model/{ARI.java => MMRI.java} (98%) diff --git a/src/vector/model/ARI.java b/src/vector/model/MMRI.java similarity index 98% rename from src/vector/model/ARI.java rename to src/vector/model/MMRI.java index b83c23d..ad29426 100644 --- a/src/vector/model/ARI.java +++ b/src/vector/model/MMRI.java @@ -42,9 +42,9 @@ /** * Created by Kivi on 09.05.2017. */ -public class ARI { +public class MMRI { - public ARI(List entityList, Set existingClasses) { + public MMRI(List entityList, Set existingClasses) { entities = entityList; allClasses = existingClasses; } From 1f1a9d63d1f7106746ba95d3eea7482336588ce7 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 23:22:12 +0300 Subject: [PATCH 59/68] added ARI: same as MRI --- .../plugin/AutomaticRefactoringAction.java | 25 ++++- src/vector/model/ARI.java | 103 ++++++++++++++++++ src/vector/model/{MMRI.java => MRI.java} | 80 +++++++------- 3 files changed, 167 insertions(+), 41 deletions(-) create mode 100644 src/vector/model/ARI.java rename src/vector/model/{MMRI.java => MRI.java} (72%) diff --git a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java index ed6c51a..dbe0edf 100644 --- a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java +++ b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java @@ -134,7 +134,7 @@ public void onFinish() { System.out.println("Properties: " + fields.size()); for (String field : fields) { Entity fieldEnt = new FieldEntity(field, metricsRun, properties); - //entities.add(fieldEnt); + entities.add(fieldEnt); } /*for (Entity ent : entities) { @@ -152,16 +152,35 @@ public void onFinish() { System.out.println("!!!\n"); CCDA alg = new CCDA(entities); + System.out.println("Starting CCDA..."); System.out.println(alg.calculateQualityIndex()); Map refactorings = alg.run(); + System.out.println("Finished CCDA\n"); for (String ent : refactorings.keySet()) { System.out.println(ent + " --> " + refactorings.get(ent)); } - ARI alg2 = new ARI(entities, properties.getAllClasses()); - System.out.println("Starting ARI..."); + MRI alg2 = new MRI(entities, properties.getAllClasses()); + System.out.println("\nStarting MMRI..."); //alg2.printTableDistances(); refactorings = alg2.run(); + System.out.println("Finished MMRI"); + for (String method : refactorings.keySet()) { + System.out.println(method + " --> " + refactorings.get(method)); + } + + + HAC alg3 = new HAC(entities); + System.out.println("\nStarting HAC..."); + refactorings = alg3.run(); + System.out.println("Finished HAC"); + for (String method : refactorings.keySet()) { + System.out.println(method + " --> " + refactorings.get(method)); + } + + ARI alg4 = new ARI(entities); + System.out.println("\nStarting ARI..."); + refactorings = alg4.run(); System.out.println("Finished ARI"); for (String method : refactorings.keySet()) { System.out.println(method + " --> " + refactorings.get(method)); diff --git a/src/vector/model/ARI.java b/src/vector/model/ARI.java new file mode 100644 index 0000000..fcecc44 --- /dev/null +++ b/src/vector/model/ARI.java @@ -0,0 +1,103 @@ +/* + * 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 vector.model; + +import com.sixrr.metrics.MetricCategory; + +import java.util.*; + +/** + * Created by Kivi on 16.05.2017. + */ +public class ARI { + public ARI(List entityList) { + methodsAndFields = new ArrayList(); + classes = new ArrayList(); + for (Entity entity : entityList) { + String name = entity.getName(); + entityByName.put(name, entity); + if (entity.getCategory() == MetricCategory.Class) { + classes.add((ClassEntity) entity); + } else { + methodsAndFields.add(entity); + } + } + } + + public Map run() { + Map refactorings = new HashMap(); + + for (Entity method : methodsAndFields) { + double minD = Double.MAX_VALUE; + int classId = -1; + for (int i = 0; i < classes.size(); i++) { + ClassEntity cl = classes.get(i); + double d = method.dist(cl); + if (method.getName().equals("class_B.methodB1()")) { + System.out.println(cl.getName() + " " + d); + + } + if (d < minD) { + minD = d; + classId = i; + } + } + + if (classId == -1) { + System.out.println("HOW??? " + method.getName()); + } + + ClassEntity cl = classes.get(classId); + if (communityIds.containsKey(cl)) { + putMethodOrField(method, cl); + } else { + createCommunity(method, cl); + } + + System.out.println("Add " + method.getName() + " to " + cl.getName()); + } + + for (Entity ent : methodsAndFields) { + if (!ent.getClassName().equals(communityIds.get(ent))) { + refactorings.put(ent.getName(), communityIds.get(ent)); + } + } + + return refactorings; + } + + private void createCommunity(Entity method, Entity cl) { + communityIds.put(cl, cl.getName()); + communityIds.put(method, cl.getName()); + Set newCommunity = new HashSet(); + newCommunity.add(method); + newCommunity.add(cl); + communities.put(cl.getName(), newCommunity); + } + + private void putMethodOrField(Entity method, Entity cl) { + communityIds.put(method, cl.getName()); + communities.get(cl.getName()).add(method); + } + + private HashMap> communities = new HashMap>(); + private HashMap communityIds = new HashMap(); + + private List methodsAndFields; + private List classes; + private HashMap entityByName = new HashMap(); +} diff --git a/src/vector/model/MMRI.java b/src/vector/model/MRI.java similarity index 72% rename from src/vector/model/MMRI.java rename to src/vector/model/MRI.java index ad29426..58db3b6 100644 --- a/src/vector/model/MMRI.java +++ b/src/vector/model/MRI.java @@ -42,9 +42,9 @@ /** * Created by Kivi on 09.05.2017. */ -public class MMRI { +public class MRI { - public MMRI(List entityList, Set existingClasses) { + public MRI(List entityList, Set existingClasses) { entities = entityList; allClasses = existingClasses; } @@ -52,7 +52,7 @@ public MMRI(List entityList, Set existingClasses) { public Map run() { Map refactorings = new HashMap(); for (Entity entity : entities) { - if (entity.getCategory().equals(MetricCategory.Method)) { + if (entity.getCategory() != MetricCategory.Class) { String className = entity.getClassName(); double minDist = Double.MAX_VALUE; int idClass = -1; @@ -74,53 +74,57 @@ public Map run() { continue; } - PsiMethod method = (PsiMethod) entity.getPsiElement(); - PsiClass moveFromClass = method.getContainingClass(); - PsiClass moveToClass = (PsiClass) entities.get(idClass).getPsiElement(); + if (entity.getCategory() == MetricCategory.Method) { + PsiMethod method = (PsiMethod) entity.getPsiElement(); + PsiClass moveFromClass = method.getContainingClass(); + PsiClass moveToClass = (PsiClass) entities.get(idClass).getPsiElement(); - Set supersTo = PSIUtil.getAllSupers(moveToClass, allClasses);//new HashSet(Arrays.asList(moveToClass.getSupers())); - boolean isSuper = false; + Set supersTo = PSIUtil.getAllSupers(moveToClass, allClasses);//new HashSet(Arrays.asList(moveToClass.getSupers())); + boolean isSuper = false; - for (PsiClass sup : supersTo) { - if (sup.equals(moveFromClass)) { - isSuper = true; - break; + for (PsiClass sup : supersTo) { + if (sup.equals(moveFromClass)) { + isSuper = true; + break; + } } - } - Set supersFrom = PSIUtil.getAllSupers(moveFromClass, allClasses);//new HashSet(Arrays.asList(moveFromClass.getSupers())); - for (PsiClass sup : supersFrom) { - if (sup.equals(moveToClass)) { - isSuper = true; - break; + Set supersFrom = PSIUtil.getAllSupers(moveFromClass, allClasses);//new HashSet(Arrays.asList(moveFromClass.getSupers())); + for (PsiClass sup : supersFrom) { + if (sup.equals(moveToClass)) { + isSuper = true; + break; + } } - } - supersFrom.retainAll(supersTo); - boolean isOverride = false; + supersFrom.retainAll(supersTo); + boolean isOverride = false; - if (isSuper) { - continue; - } + if (isSuper) { + continue; + } + + for (PsiClass sup : supersFrom) { + PsiMethod[] methods = sup.getMethods(); + for (PsiMethod m : methods) { + if (m.equals(method)) { + isOverride = true; + break; + } + } - for (PsiClass sup : supersFrom) { - PsiMethod[] methods = sup.getMethods(); - for (PsiMethod m : methods) { - if (m.equals(method)) { - isOverride = true; + if (isOverride) { break; } } - if (isOverride) { - break; + if (!isOverride) { + Entity newClass = entities.get(idClass); + refactorings.put(entity.getName(), newClass.getClassName()); + entity.moveToClass((PsiClass) newClass.getPsiElement()); + newClass.removeFromClass((PsiMethod) entity.getPsiElement()); } - } - - if (!isOverride) { - Entity newClass = entities.get(idClass); - refactorings.put(entity.getName(), newClass.getClassName()); - entity.moveToClass((PsiClass) newClass.getPsiElement()); - newClass.removeFromClass((PsiMethod) entity.getPsiElement()); + } else { + refactorings.put(entity.getName(), entities.get(idClass).getName()); } } } From 9fbb17c8f80514435d383c55cfce02ba051e8c56 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Tue, 16 May 2017 23:51:16 +0300 Subject: [PATCH 60/68] fix: cannot move method to the parent class --- src/vector/model/ARI.java | 55 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/vector/model/ARI.java b/src/vector/model/ARI.java index fcecc44..1e9873a 100644 --- a/src/vector/model/ARI.java +++ b/src/vector/model/ARI.java @@ -16,6 +16,8 @@ package vector.model; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import com.sixrr.metrics.MetricCategory; import java.util.*; @@ -27,11 +29,13 @@ public class ARI { public ARI(List entityList) { methodsAndFields = new ArrayList(); classes = new ArrayList(); + allClasses = new HashSet(); for (Entity entity : entityList) { String name = entity.getName(); entityByName.put(name, entity); if (entity.getCategory() == MetricCategory.Class) { classes.add((ClassEntity) entity); + allClasses.add((PsiClass) entity.getPsiElement()); } else { methodsAndFields.add(entity); } @@ -46,11 +50,55 @@ public Map run() { int classId = -1; for (int i = 0; i < classes.size(); i++) { ClassEntity cl = classes.get(i); + if (method.getCategory() == MetricCategory.Method) { + PsiClass moveFromClass = ((PsiMethod) method.getPsiElement()).getContainingClass(); + PsiClass moveToClass = (PsiClass) cl.getPsiElement(); + + Set supersTo = PSIUtil.getAllSupers(moveToClass, allClasses);//new HashSet(Arrays.asList(moveToClass.getSupers())); + boolean isSuper = false; + + for (PsiClass sup : supersTo) { + if (sup.equals(moveFromClass)) { + isSuper = true; + break; + } + } + + Set supersFrom = PSIUtil.getAllSupers(moveFromClass, allClasses);//new HashSet(Arrays.asList(moveFromClass.getSupers())); + for (PsiClass sup : supersFrom) { + if (sup.equals(moveToClass)) { + isSuper = true; + break; + } + } + supersFrom.retainAll(supersTo); + boolean isOverride = false; + + if (isSuper) { + continue; + } + + for (PsiClass sup : supersFrom) { + PsiMethod[] methods = sup.getMethods(); + for (PsiMethod m : methods) { + if (m.equals(method)) { + isOverride = true; + break; + } + } + + if (isOverride) { + break; + } + } + + if (isOverride) { + continue; + } + } + double d = method.dist(cl); - if (method.getName().equals("class_B.methodB1()")) { - System.out.println(cl.getName() + " " + d); - } if (d < minD) { minD = d; classId = i; @@ -99,5 +147,6 @@ private void putMethodOrField(Entity method, Entity cl) { private List methodsAndFields; private List classes; + private Set allClasses; private HashMap entityByName = new HashMap(); } From 880172f13058cc31d92d0105b65b9dd5ecac5984 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 17 May 2017 10:59:09 +0300 Subject: [PATCH 61/68] remove debug output --- src/vector/model/PropertiesFinder.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index 26dd9dc..758d06a 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -347,9 +347,6 @@ public void visitMethod(PsiMethod method) { Set methods = PSIUtil.getAllSupers(method, allClasses); superMethods.put(methodName, new HashSet()); for (PsiMethod met : methods) { - if (methodName.equals("org.jhotdraw.draw.AttributedFigure.clone()")) { - System.out.println("HERE: " + MethodUtils.calculateSignature(met)); - } superMethods.get(methodName).add(met); //System.out.println(" " + met.getContainingClass().getQualifiedName() + "." + met.getName()); } From dbceacebb2169546da73768dbb4baaf7a476cac1 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 17 May 2017 11:00:01 +0300 Subject: [PATCH 62/68] change two nearest communities detection: using samples --- src/vector/model/HAC.java | 58 ++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/src/vector/model/HAC.java b/src/vector/model/HAC.java index 2004fca..ab4ee12 100644 --- a/src/vector/model/HAC.java +++ b/src/vector/model/HAC.java @@ -27,8 +27,8 @@ public class HAC { public HAC(List entityList) { entities = entityList; for (Entity entity : entities) { - String name = entity.getName(); - HashSet simpleCommunity = new HashSet(); + final String name = entity.getName(); + final HashSet simpleCommunity = new HashSet(); simpleCommunity.add(entity); communities.put(name, simpleCommunity); communityIds.put(entity, name); @@ -37,31 +37,24 @@ public HAC(List entityList) { } public Map run() { - Map refactorings = new HashMap(); + final Map refactorings = new HashMap(); double minD = 0.0; while (minD < 1.0) { minD = Double.MAX_VALUE; String id1 = ""; String id2 = ""; - for (Entity ent1 : communityIds.keySet()) { - final String i = communityIds.get(ent1); - for (Entity ent2 : communityIds.keySet()) { - if (ent1.getCategory() == MetricCategory.Class && ent2.getCategory() == MetricCategory.Class) { + for (String i : communities.keySet()) { + for (String j : communities.keySet()) { + if (i.compareTo(j) >= 0) { continue; } - final String j = communityIds.get(ent2); - if (i.equals(j)) { + if (entityByName.get(i).getCategory() == MetricCategory.Class && entityByName.get(j).getCategory() == MetricCategory.Class) { continue; } - final double d = ent1.dist(ent2); + final double d = distCommunities(i, j); if (d < minD) { - if (entityByName.get(i).getCategory() == MetricCategory.Class - && entityByName.get(j).getCategory() == MetricCategory.Class) { - continue; - } - minD = d; id1 = i; id2 = j; @@ -93,9 +86,41 @@ public Map run() { return refactorings; } + private double distCommunities(String id1, String id2) { + final Set s1 = buildSample(id1); + final Set s2 = buildSample(id2); + + double d = 0.0; + for (Entity e1 : s1) { + for (Entity e2 : s2) { + d += e1.dist(e2); + } + } + + d /= s1.size() * s2.size(); + + return d; + } + + private Set buildSample(String id) { + final Set sample = new HashSet(); + if (entityByName.get(id).getCategory() == MetricCategory.Class) { + sample.add(entityByName.get(id)); + } else { + final List s = new ArrayList(communities.get(id)); + final int len = Math.min(SampleSize, s.size()); + Collections.shuffle(s); + for (int i = 0; i < len; ++i) { + sample.add(s.get(i)); + } + } + + return sample; + } + private void mergeCommunities(String id1, String id2) { String newName = id1; - Set merge = new HashSet(communities.get(id1)); + final Set merge = new HashSet(communities.get(id1)); merge.addAll(communities.get(id2)); int maxInClass = 0; for (Entity ent : merge) { @@ -127,4 +152,5 @@ private void mergeCommunities(String id1, String id2) { private List entities; private HashMap entityByName = new HashMap(); + private static int SampleSize = 5; } From c1b88317304a03f3c6e129c67da3280bc558c14a Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 17 May 2017 15:08:06 +0300 Subject: [PATCH 63/68] fix HAC: change finding nearest clusters - complete-linkage --- src/vector/model/HAC.java | 47 ++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/vector/model/HAC.java b/src/vector/model/HAC.java index ab4ee12..03fa7d4 100644 --- a/src/vector/model/HAC.java +++ b/src/vector/model/HAC.java @@ -76,29 +76,65 @@ public Map run() { System.out.println("Merge " + id1 + " and " + id2 + " to " + communityIds.get(entityByName.get(id1))); } - for (Entity ent : communityIds.keySet()) { + /*for (Entity ent : communityIds.keySet()) { if (!ent.getClassName().equals(communityIds.get(ent))) { refactorings.put(ent.getName(), communityIds.get(ent)); } + }*/ + for (String center : communities.keySet()) { + String newName = receiveClassName(center); + for (Entity entity : communities.get(center)) { + if (!entity.getClassName().equals(newName)) { + refactorings.put(entity.getName(), newName); + } + } } return refactorings; } + private String receiveClassName(String center) { + String name = ""; + Integer maxClassCount = 0; + Map classCounts = new HashMap(); + for (Entity entity : communities.get(center)) { + String className = entity.getClassName(); + if (!classCounts.containsKey(className)) { + classCounts.put(className, 0); + } + + Integer count = classCounts.get(className); + count++; + classCounts.put(className, count); + } + + for (String className : classCounts.keySet()) { + if (maxClassCount < classCounts.get(className)) { + maxClassCount = classCounts.get(className); + name = className; + } + } + + if (name.equals("")) { + newClassCount++; + name = "NewClass" + newClassCount; + } + + return name; + } + private double distCommunities(String id1, String id2) { final Set s1 = buildSample(id1); final Set s2 = buildSample(id2); - double d = 0.0; + double d = 0; for (Entity e1 : s1) { for (Entity e2 : s2) { - d += e1.dist(e2); + d = Math.max(d, e1.dist(e2)); } } - d /= s1.size() * s2.size(); - return d; } @@ -153,4 +189,5 @@ private void mergeCommunities(String id1, String id2) { private List entities; private HashMap entityByName = new HashMap(); private static int SampleSize = 5; + private int newClassCount = 0; } From 17147e6f77b2a8960916ee43b23cf5d9c9a49189 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 17 May 2017 21:11:50 +0300 Subject: [PATCH 64/68] change eps for CCDA --- src/vector/model/CCDA.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vector/model/CCDA.java b/src/vector/model/CCDA.java index 05f3d3c..3f05bb2 100644 --- a/src/vector/model/CCDA.java +++ b/src/vector/model/CCDA.java @@ -245,5 +245,5 @@ public Double calculateQualityIndex() { private Double q; private Integer edges; - private static Double eps = 1e-3; + private static Double eps = 5e-4; } From 28a50d5e9fa8f90b72fd4036ee41af649b65a772 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 17 May 2017 21:12:10 +0300 Subject: [PATCH 65/68] remove enums from classes --- src/vector/model/PropertiesFinder.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vector/model/PropertiesFinder.java b/src/vector/model/PropertiesFinder.java index 758d06a..5822878 100644 --- a/src/vector/model/PropertiesFinder.java +++ b/src/vector/model/PropertiesFinder.java @@ -110,6 +110,15 @@ public Set getAllClasses() { return allClasses; } + public Set getAllClassesNames() { + Set classesNames = new HashSet(); + for (PsiClass c : allClasses) { + classesNames.add(c.getQualifiedName()); + } + + return classesNames; + } + public PsiElement getPsiElement(String name) { if (methodByName.containsKey(name)) { return methodByName.get(name); @@ -128,6 +137,9 @@ public PsiElement getPsiElement(String name) { private class ClassCounter extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { + if (aClass.isEnum()) { + return; + } if (aClass instanceof AnonymousClassElement) { return; } @@ -234,6 +246,9 @@ public void run() { private class EntityVisitor extends JavaRecursiveElementVisitor { @Override public void visitClass(PsiClass aClass) { + if (aClass.isEnum()) { + return; + } if (aClass instanceof AnonymousClassElement) { return; } From 80fb61d976f56a4c6112748e7a19db4a27130d59 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 17 May 2017 21:12:49 +0300 Subject: [PATCH 66/68] implemented AKMeans (FMC) algorithm --- src/vector/model/AKMeans.java | 195 ++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 src/vector/model/AKMeans.java diff --git a/src/vector/model/AKMeans.java b/src/vector/model/AKMeans.java new file mode 100644 index 0000000..5dcb173 --- /dev/null +++ b/src/vector/model/AKMeans.java @@ -0,0 +1,195 @@ +/* + * 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 vector.model; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.sixrr.metrics.MetricCategory; + +import java.util.*; + +/** + * Created by Kivi on 17.05.2017. + */ +public class AKMeans { + public AKMeans(List entityList, int steps) { + this.steps = steps; + for (Entity e : entityList) { + if (e.getCategory() != MetricCategory.Class) { + points.add(e); + communityIds.put(e, ""); + entityByName.put(e.getName(), e); + } else { + N++; + allClasses.add((PsiClass) e.getPsiElement()); + } + } + } + + private void initializeCenters() { + List entities = new ArrayList(points); + Collections.shuffle(entities); + + for (int i = 0; i < N; ++i) { + Entity center = entities.get(i); + Set simpleCommunity = new HashSet(); + simpleCommunity.add(center); + communities.put(center.getName(), simpleCommunity); + } + } + + public Map run() { + Map refactorings = new HashMap(); + + initializeCenters(); + for (int step = 0; step < steps; ++step) { + boolean moving = false; + Map newCommunities = new HashMap(); + for (Entity entity : points) { + String newCenter = findNearestCommunity(entity); + if (newCenter.equals("")) { + continue; + } + if (!newCenter.equals(communityIds.get(entity))) { + moving = true; + } + + newCommunities.put(entity, newCenter); + if (!communityIds.get(entity).equals(newCenter)) { + System.out.println("Move " + entity.getName() + " to " + newCenter + ", " + distToCommunity(entity, newCenter)); + } + //moveToCommunity(entity, newCenter); + } + + for (Entity entity : newCommunities.keySet()) { + moveToCommunity(entity, newCommunities.get(entity)); + } + System.out.println(); + + if (!moving) { + break; + } + } + + for (String center : communities.keySet()) { + String newName = receiveClassName(center); + for (Entity entity : communities.get(center)) { + if (!entity.getClassName().equals(newName)) { + refactorings.put(entity.getName(), newName); + } + } + } + + return refactorings; + } + + private String receiveClassName(String center) { + String name = ""; + Integer maxClassCount = 0; + Map classCounts = new HashMap(); + for (Entity entity : communities.get(center)) { + String className = entity.getClassName(); + if (!classCounts.containsKey(className)) { + classCounts.put(className, 0); + } + + Integer count = classCounts.get(className); + count++; + classCounts.put(className, count); + } + + for (String className : classCounts.keySet()) { + if (maxClassCount < classCounts.get(className)) { + maxClassCount = classCounts.get(className); + name = className; + } + } + + if (name.equals("")) { + newClassCount++; + name = "NewClass" + newClassCount; + } + + return name; + } + + private String findNearestCommunity(Entity entity) { + double minD = Double.MAX_VALUE; + String id = ""; + for (String center : communities.keySet()) { + double d = distToCommunity(entity, center); + if (!canMove(entity, center)) { + d = Double.MAX_VALUE; + } + if (d < minD) { + minD = d; + id = center; + } + } + + return id; + } + + private boolean canMove(Entity entity, String center) { + if (entity.getCategory() != MetricCategory.Method) { + return true; + } + + PsiMethod method = (PsiMethod) entity.getPsiElement(); + Set cluster = communities.get(center); + for (Entity e : cluster) { + if (e.getCategory() != MetricCategory.Method) { + continue; + } + + PsiMethod component = (PsiMethod) e.getPsiElement(); + + Set supers = PSIUtil.getAllSupers(component, allClasses); + supers.retainAll(PSIUtil.getAllSupers(method)); + if (!supers.isEmpty()) { + return false; + } + } + + return true; + } + + private double distToCommunity(Entity entity, String center) { + double minD = 0.0; + for (Entity point : communities.get(center)) { + double d = entity.dist(point); + minD = Math.max(d, minD); + } + + return minD; + } + + private void moveToCommunity(Entity entity, String id) { + communities.get(id).add(entity); + communityIds.put(entity, id); + } + + private List points = new ArrayList(); + //private Set<> + private Map> communities = new HashMap>(); + private Map communityIds = new HashMap(); + private Map entityByName = new HashMap(); + private Set allClasses = new HashSet(); + private int N = 0; + private int steps = 0; + private int newClassCount = 0; +} From 46a4ae9594b758dd53a66e1003d7959d3b974422 Mon Sep 17 00:00:00 2001 From: kivi239 Date: Wed, 17 May 2017 21:13:18 +0300 Subject: [PATCH 67/68] implemented AKMeans (FMC) algorithm --- .../plugin/AutomaticRefactoringAction.java | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java index dbe0edf..376c079 100644 --- a/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java +++ b/src/com/sixrr/metrics/plugin/AutomaticRefactoringAction.java @@ -117,6 +117,9 @@ public void onFinish() { if (obj.equals("null")) { continue; } + if (!properties.getAllClassesNames().contains(obj)) { + continue; + } Entity classEnt = new ClassEntity(obj, metricsRun, properties); entities.add(classEnt); } @@ -163,11 +166,45 @@ public void onFinish() { MRI alg2 = new MRI(entities, properties.getAllClasses()); System.out.println("\nStarting MMRI..."); //alg2.printTableDistances(); - refactorings = alg2.run(); + Map refactorings2 = alg2.run(); System.out.println("Finished MMRI"); - for (String method : refactorings.keySet()) { - System.out.println(method + " --> " + refactorings.get(method)); + for (String method : refactorings2.keySet()) { + System.out.println(method + " --> " + refactorings2.get(method)); + } + + Set common = new HashSet(refactorings.keySet()); + common.retainAll(refactorings2.keySet()); + System.out.println("Common for ARI and CCDA: "); + for (String move : common) { + System.out.print(move + " to "); + System.out.print(refactorings.get(move)); + if (!refactorings2.get(move).equals(refactorings.get(move))) { + System.out.print(" vs " + refactorings2.get(move)); + } + System.out.println(); } + System.out.println(); + + AKMeans alg5 = new AKMeans(entities, 50); + System.out.println("\nStarting AKMeans..."); + Map refactorings5 = alg5.run(); + System.out.println("Finished AKMeans"); + for (String method : refactorings5.keySet()) { + System.out.println(method + " --> " + refactorings5.get(method)); + } + + Set refactoringsARIEC = new HashSet(refactorings5.keySet()); + refactoringsARIEC.retainAll(refactorings2.keySet()); + System.out.println("Common for ARI and EC: "); + for (String move : refactoringsARIEC) { + System.out.print(move + " to "); + System.out.print(refactorings5.get(move)); + if (!refactorings2.get(move).equals(refactorings5.get(move))) { + System.out.print(" vs " + refactorings2.get(move)); + } + System.out.println(); + } + System.out.println(); HAC alg3 = new HAC(entities); @@ -185,6 +222,9 @@ public void onFinish() { for (String method : refactorings.keySet()) { System.out.println(method + " --> " + refactorings.get(method)); } + + + } }.execute(profile, metricsRun); } From f9ff149f891af25ed769b01f4ebdab760789ff7f Mon Sep 17 00:00:00 2001 From: kivi239 Date: Mon, 22 May 2017 20:23:58 +0300 Subject: [PATCH 68/68] implemented HAC (N^2 log N) --- src/vector/model/HAC.java | 164 ++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 60 deletions(-) diff --git a/src/vector/model/HAC.java b/src/vector/model/HAC.java index 03fa7d4..eb6da1f 100644 --- a/src/vector/model/HAC.java +++ b/src/vector/model/HAC.java @@ -24,6 +24,50 @@ * Created by Kivi on 16.05.2017. */ public class HAC { + + private class Triple implements Comparable { + + Triple(Double _d, String _c1, String _c2) { + d = _d; + c1 = _c1; + c2 = _c2; + } + + public int compareTo(Triple t) { + int compD = d.compareTo(t.d); + if (compD != 0) { + return compD; + } + + int compC1 = c1.compareTo(t.c1); + if (compC1 != 0) { + return compC1; + } + + int compC2 = c2.compareTo(t.c2); + if (compC2 != 0) { + return compC2; + } + + return 0; + } + + public Double getD() { + return d; + } + + public String getC1() { + return c1; + } + + public String getC2() { + return c2; + } + + private Double d; + + private String c1, c2; + } public HAC(List entityList) { entities = entityList; for (Entity entity : entities) { @@ -34,54 +78,45 @@ public HAC(List entityList) { communityIds.put(entity, name); entityByName.put(name, entity); } + + for (String com1 : entityByName.keySet()) { + dists.put(com1, new HashMap()); + for (String com2 : entityByName.keySet()) { + Double d = entityByName.get(com1).dist(entityByName.get(com2)); + dists.get(com1).put(com2, d); + if (com1.compareTo(com2) < 0) { + distances.add(new Triple(d, com1, com2)); + } + } + } + + System.out.println(distances.first().getD()); } public Map run() { final Map refactorings = new HashMap(); double minD = 0.0; - while (minD < 1.0) { - minD = Double.MAX_VALUE; - String id1 = ""; - String id2 = ""; - for (String i : communities.keySet()) { - for (String j : communities.keySet()) { - if (i.compareTo(j) >= 0) { - continue; - } - if (entityByName.get(i).getCategory() == MetricCategory.Class && entityByName.get(j).getCategory() == MetricCategory.Class) { - continue; - } - - final double d = distCommunities(i, j); - if (d < minD) { - minD = d; - id1 = i; - id2 = j; - } - } - } + while (minD < 1.0 && !distances.isEmpty()) { + Triple min = distances.first(); + minD = min.getD(); + String id1 = min.getC1(); + String id2 = min.getC2(); if (minD > 1.0) { continue; } - if (entityByName.get(id1).getCategory() == MetricCategory.Class + /*if (entityByName.get(id1).getCategory() == MetricCategory.Class && entityByName.get(id2).getCategory() == MetricCategory.Class) { minD = Double.MAX_VALUE; continue; - } + }*/ mergeCommunities(id1, id2); System.out.println("Merge " + id1 + " and " + id2 + " to " + communityIds.get(entityByName.get(id1))); } - /*for (Entity ent : communityIds.keySet()) { - if (!ent.getClassName().equals(communityIds.get(ent))) { - refactorings.put(ent.getName(), communityIds.get(ent)); - - } - }*/ for (String center : communities.keySet()) { String newName = receiveClassName(center); for (Entity entity : communities.get(center)) { @@ -94,7 +129,7 @@ public Map run() { return refactorings; } - private String receiveClassName(String center) { + private String calculateClassName(String center) { String name = ""; Integer maxClassCount = 0; Map classCounts = new HashMap(); @@ -116,42 +151,18 @@ private String receiveClassName(String center) { } } - if (name.equals("")) { - newClassCount++; - name = "NewClass" + newClassCount; - } - return name; } - private double distCommunities(String id1, String id2) { - final Set s1 = buildSample(id1); - final Set s2 = buildSample(id2); - - double d = 0; - for (Entity e1 : s1) { - for (Entity e2 : s2) { - d = Math.max(d, e1.dist(e2)); - } - } - - return d; - } + private String receiveClassName(String center) { + String name = calculateClassName(center); - private Set buildSample(String id) { - final Set sample = new HashSet(); - if (entityByName.get(id).getCategory() == MetricCategory.Class) { - sample.add(entityByName.get(id)); - } else { - final List s = new ArrayList(communities.get(id)); - final int len = Math.min(SampleSize, s.size()); - Collections.shuffle(s); - for (int i = 0; i < len; ++i) { - sample.add(s.get(i)); - } + if (name.equals("")) { + newClassCount++; + name = "NewClass" + newClassCount; } - return sample; + return name; } private void mergeCommunities(String id1, String id2) { @@ -181,13 +192,46 @@ private void mergeCommunities(String id1, String id2) { for (Entity ent : merge) { communityIds.put(ent, newName); } + + Double d = dists.get(id1).get(id2); + + dists.get(id1).remove(id2); + dists.get(id2).remove(id1); + + distances.remove(new Triple(d, id1, id2)); + distances.remove(new Triple(d, id2, id1)); + + for (String entity : communities.keySet()) { + if (entity.equals(id1) || entity.equals(id2)) { + continue; + } + Double d1 = dists.get(entity).get(id1); + Double d2 = dists.get(entity).get(id2); + Double newD = Math.max(d1, d2); + dists.get(id1).put(entity, newD); + dists.get(entity).put(id1, newD); + dists.get(entity).remove(id2); + dists.get(id2).remove(entity); + distances.remove(new Triple(d1, entity, id1)); + distances.remove(new Triple(d1, id1, entity)); + distances.remove(new Triple(d2, entity, id2)); + distances.remove(new Triple(d2, id2, entity)); + if (id1.compareTo(entity) < 0) { + distances.add(new Triple(newD, id1, entity)); + } else { + distances.add(new Triple(newD, entity, id1)); + } + } } private HashMap> communities = new HashMap>(); private HashMap communityIds = new HashMap(); + private Map> dists = new HashMap>(); private List entities; private HashMap entityByName = new HashMap(); + + private SortedSet distances = new TreeSet(); private static int SampleSize = 5; private int newClassCount = 0; }