diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0b6241a9..fa6b6685 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Wed Jul 12 12:29:38 MSK 2017 +#Fri Mar 09 09:33:20 MSK 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/org/ml_methods_group/algorithm/QMove.java b/src/main/java/org/ml_methods_group/algorithm/QMove.java new file mode 100644 index 00000000..b8a2a7f2 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/QMove.java @@ -0,0 +1,260 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm; + + +import org.ml_methods_group.algorithm.entity.Entity; +import org.ml_methods_group.algorithm.entity.QMoveClassEntity; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearchResult; +import org.ml_methods_group.algorithm.entity.QMoveMethodEntity; +import org.ml_methods_group.utils.AlgorithmsUtil; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; + +public class QMove extends Algorithm { + private List methodEntities = new ArrayList<>(); + private final List classes = new ArrayList<>(); + private final Map qMoveClassesByName = new HashMap<>(); + private QMoveMetric metric = new QMoveMetric(); + private ExecutionContext context; + private AtomicInteger progress = new AtomicInteger(); + private AtomicLong start = new AtomicLong(); + + public QMove() { + super("QMove", true); + } + + @Override + protected List calculateRefactorings( + ExecutionContext context, boolean enableFieldRefactorings) throws Exception { + this.context = context; + QMoveEntitySearchResult searchResult = (QMoveEntitySearchResult) context.getEntities(); + methodEntities.clear(); + classes.clear(); + progress.set(0); + Stream.of(searchResult.getMethods()) + .flatMap(List::stream) + .filter(Entity::isMovable) + .map(x -> (QMoveMethodEntity) x) + .forEach(methodEntities::add); + searchResult.getClasses() + .stream() + .map(x -> (QMoveClassEntity) x) + .peek(entity -> qMoveClassesByName.put(entity.getName(), entity)) + .forEach(classes::add); + calculateMetrics(); + System.err.println("started"); + start.set(System.currentTimeMillis()); + /*List refactorings = new ArrayList<>(); + for (QMoveMethodEntity methodEntity : methodEntities) { + findBestMoveForMethod(methodEntity, refactorings); + }*/ + return runParallel(methodEntities, context, ArrayList::new, + this::findBestMoveForMethod, AlgorithmsUtil::combineLists); + //return refactorings; + } + + + private List findBestMoveForMethod(QMoveMethodEntity method, + List refactorings) { + reportProgress((double) progress.incrementAndGet() / methodEntities.size(), context); + QMoveMetric copy = new QMoveMetric(metric); + context.checkCanceled(); + ResultHolder resultHolder = new ResultHolder(null, 0); + start.set(System.currentTimeMillis()); + if (method.getMoveAbility() == QMoveMethodEntity.MoveAbility.ANY_CLASS) { + System.err.println(method.getName()); + resultHolder = checkMoveForClasses(method, classes, copy); + + } else { + for (Set targets : method.getTargets()) { + ResultHolder holder = checkMoveForClasses(method, targets, copy); + if (resultHolder.fitness < holder.fitness) { + resultHolder = holder; + } + } + } + System.err.println(start.addAndGet(-System.currentTimeMillis())); + if (resultHolder.target != null) { + refactorings.add(new Refactoring(method.getName(), + resultHolder.target.getName(), resultHolder.fitness, + false)); + } + return refactorings; + } + + private ResultHolder checkMoveForClasses(QMoveMethodEntity method, + Collection classes, QMoveMetric metricCopy) { + double bestFitness = 0; + QMoveClassEntity targetForThisMethod = null; + for (QMoveClassEntity targetClass : classes) { + QMoveClassEntity containingClass = qMoveClassesByName.get( + method.getClassName()); + if (containingClass.equals(targetClass) || + !method.isValidMoveToClass(targetClass)) { + continue; + } + double newFitness = moveMethod(method, targetClass, containingClass, metricCopy); + if (newFitness > bestFitness) { + bestFitness = newFitness; + targetForThisMethod = targetClass; + } + } + return new ResultHolder(targetForThisMethod, bestFitness); + } + + + private void calculateMetrics() { + metric.size = classes.size(); + metric.complexity = methodEntities.size(); + metric.polymorphism = classes.stream().mapToDouble(QMoveClassEntity::getPolymorphism).sum(); + metric.abstraction = classes.stream().mapToDouble(QMoveClassEntity::getAbstraction).sum() / metric.size; + metric.hierarchies = classes.stream().mapToDouble(QMoveClassEntity::getHierarchies).sum(); + metric.encapsulation = classes.stream().mapToDouble(QMoveClassEntity::getEncapsulation).sum(); + metric.coupling = classes.stream().mapToDouble(QMoveClassEntity::getCoupling).sum(); + metric.messaging = classes.stream().mapToDouble(QMoveClassEntity::getMessaging).sum(); + metric.cohesion = classes.stream().mapToDouble(QMoveClassEntity::getCohesion).sum(); + metric.composition = classes.stream().mapToDouble(QMoveClassEntity::getComposition).sum(); + metric.inheritance = classes.stream().mapToDouble(QMoveClassEntity::getInheritance).sum(); + } + + private double moveMethod(QMoveMethodEntity method, + QMoveClassEntity target, + QMoveClassEntity containingClass, QMoveMetric copyMetric) { + //recalculating cohesion + double removeFromCohesion = target.getCohesion(); + removeFromCohesion += containingClass.getCohesion(); + double addToCohesion = target.recalculateCohesion(method, true); + addToCohesion += containingClass.recalculateCohesion(method, false); + double oldCohesion = copyMetric.cohesion; + copyMetric.cohesion -= removeFromCohesion; + copyMetric.cohesion += addToCohesion; + + //recalculating coupling + double removeFromCoupling = target.getCoupling(); + removeFromCoupling += containingClass.getCoupling(); + double addToCoupling = target.recalculateCoupling(method, true); + addToCoupling += containingClass.recalculateCoupling(method, false); + double oldCoupling = copyMetric.coupling; + copyMetric.coupling -= removeFromCoupling; + copyMetric.coupling += addToCoupling; + if(metric.coupling != oldCoupling){ + System.err.println("wtf"); + } + //recalculating inheritance + double oldInheritance = copyMetric.inheritance; + for (QMoveClassEntity entity : target.getInheritors()) { + copyMetric.inheritance -= entity.getInheritance(); + copyMetric.inheritance += entity.recalculateInheritance(true); + } + for (QMoveClassEntity entity : containingClass.getInheritors()) { + copyMetric.inheritance -= entity.getInheritance(); + copyMetric.inheritance += entity.recalculateInheritance(false); + } + double fitness = copyMetric.fitness(this.metric); + copyMetric.cohesion = oldCohesion; + copyMetric.coupling = oldCoupling; + copyMetric.inheritance = oldInheritance; + return fitness; + } + + private static class QMoveMetric { + private QMoveMetric() { + } + + private QMoveMetric(QMoveMetric other) { + cohesion = other.cohesion; + complexity = other.complexity; + coupling = other.coupling; + encapsulation = other.encapsulation; + polymorphism = other.polymorphism; + abstraction = other.abstraction; + hierarchies = other.hierarchies; + messaging = other.messaging; + composition = other.composition; + inheritance = other.inheritance; + size = other.size; + } + + private double complexity; + private double polymorphism; + private double abstraction; + private double hierarchies; + private double encapsulation; + private double coupling; + private double messaging; + private double cohesion; + private double composition; + private double inheritance; + private double size; + + private double effectiveness() { + return 0.2 * abstraction + 0.2 * encapsulation + 0.2 * composition + + 0.2 * inheritance + 0.2 * polymorphism; + } + + private double extendibility() { + return 0.5 * abstraction - 0.5 * coupling + 0.5 * inheritance + + 0.5 * polymorphism; + } + + private double functionality() { + return +0.12 * cohesion + 0.22 * polymorphism + 0.22 * messaging + + 0.22 * size + 0.22 * hierarchies; + } + + private double understandability() { + return 0.33 * abstraction + 0.33 * encapsulation - 0.33 * coupling + + 0.33 * cohesion - 0.33 * polymorphism - 0.33 * complexity + - 0.33 * size; + } + + private double reusability() { + return -0.25 * coupling + 0.25 * cohesion + 0.5 * messaging + + 0.5 * size; + } + + private double flexibility() { + return 0.25 * encapsulation - 0.25 * coupling + 0.5 * composition + + 0.5 * polymorphism; + } + + private double fitness(QMoveMetric other) { + if (flexibility() < other.flexibility() || + understandability() < other.understandability() + || extendibility() < other.extendibility()) { + return Double.NEGATIVE_INFINITY; + } + return flexibility() + understandability() + extendibility() + - other.extendibility() - other.flexibility() - other.understandability(); + } + + } + + private static class ResultHolder { + private ResultHolder(QMoveClassEntity target, double fitness) { + this.fitness = fitness; + this.target = target; + } + + private QMoveClassEntity target; + private double fitness; + } +} diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java b/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java index afa2eb8b..6ef8e9fe 100644 --- a/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java +++ b/src/main/java/org/ml_methods_group/algorithm/entity/Entity.java @@ -58,8 +58,8 @@ public abstract class Entity { protected boolean isMovable = true; public Entity(PsiElement element) { - this.name = PsiSearchUtil.getHumanReadableName(element); - relevantProperties = new RelevantProperties(); + this.name = PsiSearchUtil.getHumanReadableName(element); + relevantProperties = new RelevantProperties(); } protected Entity(Entity original) { diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java new file mode 100644 index 00000000..f94161c0 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveClassEntity.java @@ -0,0 +1,195 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm.entity; + +import com.intellij.psi.PsiClass; +import com.sixrr.metrics.Metric; +import com.sixrr.metrics.MetricCategory; +import com.sixrr.metrics.metricModel.MetricsRun; +import com.sixrr.stockmetrics.classMetrics.*; +import org.ml_methods_group.utils.QMoveUtil; + +import java.util.Set; + +public class QMoveClassEntity extends ClassEntity { + protected QMoveRelevantProperties properties = new QMoveRelevantProperties(); + private double complexity; + private double polymorphism; + private double abstraction; + private double hierarchies; + private double encapsulation; + private double coupling; + private double messaging; + private double cohesion; + private double composition; + private double inheritance; + + private double numOfAllMethods; + private PsiClass psiClass; + + + public Set getInheritors(){ + return properties.getInheritors(); + } + + QMoveRelevantProperties getProperties(){ + return properties; + } + + private static final VectorCalculator QMOOD_CALCULATOR = new VectorCalculator() + .addMetricDependence(NumMethodsClassMetric.class) //Complexity 0 + .addMetricDependence(NumOperationsInheritedMetric.class) //Inheritance 1 + .addMetricDependence(NumOperationsOverriddenMetric.class) //Polymorphism 2 + .addMetricDependence(NumAncestorsClassMetric.class) //Abstraction 3 + .addMetricDependence(IsRootOfHierarchyClassMetric.class) //Hierarchies 4 + .addMetricDependence(DataAccessClassMetric.class) //Encapsulation 5 + .addMetricDependence(NumPublicMethodsClassMetric.class); //Messaging 6 + // .addMetricDependence(CohesionAmongMethodsOfClassMetric.class) //Cohesion 8 + //.addMetricDependence(DirectClassCouplingMetric.class) //Coupling 9 + //.addMetricDependence(MeasureOfAggregationClassMetric.class); //Composition 10 + + + QMoveClassEntity(PsiClass psiClass) { + super(psiClass); + this.psiClass = psiClass; + } + + @Override + void calculateVector(MetricsRun metricsRun) { + double[] vector = QMOOD_CALCULATOR.calculateVector(metricsRun, this); + complexity = vector[0]; + inheritance = vector[1]; + polymorphism = vector[2]; + abstraction = vector[3]; + hierarchies = vector[4]; + encapsulation = vector[5]; + messaging = vector[6]; + + numOfAllMethods = inheritance + complexity; + + coupling = properties.getRelatedClasses().size(); + + cohesion = properties.getSumIntersection() != 0 ? properties.getSumIntersection() / + (properties.getParametersOfMethods().size() * properties.getNumOfMethods()) : 0; + + composition = properties.getNumUserDefinedClasses(); + } + + @Override + public MetricCategory getCategory() { + return MetricCategory.Class; + } + + @Override + public String getClassName() { + return getName(); + } + + + @Override + public boolean isField() { + return false; + } + + public double getComplexity() { + return complexity; + } + + public double getAbstraction() { + return abstraction; + } + + public double getCohesion() { + return cohesion; + } + + public double getCoupling() { + return coupling; + } + + public double getHierarchies() { + return hierarchies; + } + + public double getComposition() { + return composition; + } + + public double getEncapsulation() { + return encapsulation; + } + + public double getInheritance() { + if (numOfAllMethods == 0) { + return 0; + } + return inheritance / numOfAllMethods; + } + + public double getMessaging() { + return messaging; + } + + public double getPolymorphism() { + return polymorphism; + } + + public static Set> getRequestedMetrics() { + return QMOOD_CALCULATOR.getRequestedMetrics(); + } + + public double recalculateCohesion(QMoveMethodEntity entity, boolean add) { + int numMethods = properties.getNumOfMethods(); + double sumIntersection = properties.getSumIntersection(); + int union; + if (add) { + numMethods++; + sumIntersection += entity.getProperties().getSumIntersection(); + union = QMoveUtil.addToUnion(properties.getParametersOfMethods(), + entity.getProperties().getParametersOfMethods()); + } else { + numMethods--; + sumIntersection -= entity.getProperties().getSumIntersection(); + union = QMoveUtil.removeFromUnion(properties.getParametersOfMethods(), + entity.getProperties().getParametersOfMethods()); + } + if (union == 0) { + return 0; + } + return sumIntersection / (numMethods * union); + } + + public double recalculateCoupling(QMoveMethodEntity entity, boolean add) { + if (add) { + return QMoveUtil.addToUnion(properties.getRelatedClasses(), + entity.getProperties().getRelatedClasses()); + } else { + return QMoveUtil.removeFromUnion(properties.getRelatedClasses(), + entity.getProperties().getRelatedClasses()); + } + } + + public double recalculateInheritance(boolean increment) { + if (increment) { + return (inheritance + 1) / (numOfAllMethods + 1); + } else { + return (inheritance - 1) / (numOfAllMethods - 1); + } + } + + +} diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java new file mode 100644 index 00000000..4596a98c --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearchResult.java @@ -0,0 +1,65 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm.entity; + +import com.intellij.analysis.AnalysisScope; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +public class QMoveEntitySearchResult extends EntitySearchResult { + private final List methods; + private final List fields; + private final int propertiesCount; + private final long searchTime; + private final AnalysisScope scope; + + public QMoveEntitySearchResult(List classes, List methods, List fields, + long searchTime, AnalysisScope scope) { + super(classes, methods, fields, searchTime); + this.methods = methods; + this.fields = fields; + this.searchTime = searchTime; + this.scope = scope; + propertiesCount = Stream.of(classes, methods, fields) + .flatMap(List::stream) + .map(Entity::getRelevantProperties) + .mapToInt(RelevantProperties::size) + .sum(); + } + + public AnalysisScope getScope() { + return scope; + } + + public List getMethods() { + return Collections.unmodifiableList(methods); + } + + public List getFields() { + return Collections.unmodifiableList(fields); + } + + public int getPropertiesCount() { + return propertiesCount; + } + + public long getSearchTime() { + return searchTime; + } +} diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java new file mode 100644 index 00000000..4f8ecdfd --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveEntitySearcher.java @@ -0,0 +1,352 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm.entity; + +import com.intellij.analysis.AnalysisScope; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.sixrr.metrics.metricModel.MetricsRun; +import com.sixrr.metrics.utils.MethodUtils; +import org.apache.log4j.Logger; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; +import org.ml_methods_group.algorithm.properties.finder_strategy.FinderStrategy; +import org.ml_methods_group.algorithm.properties.finder_strategy.NewStrategy; +import org.ml_methods_group.config.Logging; +import org.ml_methods_group.utils.PSIUtil; + +import java.util.*; + +import static org.ml_methods_group.utils.PsiSearchUtil.getHumanReadableName; + +public class QMoveEntitySearcher { + private static final Logger LOGGER = Logging.getLogger(QMoveEntitySearcher.class); + + private final Map classForName = new HashMap<>(); + private final Map entities = new HashMap<>(); + private final AnalysisScope scope; + private final long startTime; + private final FinderStrategy strategy; + private final ProgressIndicator indicator; + + private QMoveEntitySearcher(AnalysisScope scope) { + this.scope = scope; + strategy = NewStrategy.getInstance(); + startTime = System.currentTimeMillis(); + if (ProgressManager.getInstance().hasProgressIndicator()) { + indicator = ProgressManager.getInstance().getProgressIndicator(); + } else { + indicator = new EmptyProgressIndicator(); + } + } + + public static QMoveEntitySearchResult analyze(AnalysisScope scope, MetricsRun metricsRun) { + final QMoveEntitySearcher finder = new QMoveEntitySearcher(scope); + return finder.runCalculations(metricsRun); + } + + private QMoveEntitySearchResult runCalculations(MetricsRun metricsRun) { + indicator.pushState(); + indicator.setText("Searching entities"); + indicator.setIndeterminate(true); + LOGGER.info("Indexing entities..."); + scope.accept(new QMoveEntitySearcher.UnitsFinder()); + indicator.setIndeterminate(false); + LOGGER.info("Calculating properties..."); + indicator.setText("Calculating properties"); + scope.accept(new QMoveEntitySearcher.PropertiesCalculator()); + indicator.popState(); + return prepareResult(metricsRun); + } + + private QMoveEntitySearchResult prepareResult(MetricsRun metricsRun) { + LOGGER.info("Preparing results..."); + final List classes = new ArrayList<>(); + final List methods = new ArrayList<>(); + final List fields = new ArrayList<>(); + final List validEntities = new ArrayList<>(); + for (Entity entity : entities.values()) { + indicator.checkCanceled(); + try { + entity.calculateVector(metricsRun); + } catch (Exception e) { + e.printStackTrace(); + LOGGER.warn("Failed to calculate vector for " + entity.getName()); + continue; + } + validEntities.add(entity); + switch (entity.getCategory()) { + case Class: + classes.add((ClassEntity) entity); + break; + case Method: + methods.add((MethodEntity) entity); + break; + default: + fields.add((FieldEntity) entity); + break; + } + } + LOGGER.info("Properties calculated"); + LOGGER.info("Generated " + classes.size() + " class entities"); + LOGGER.info("Generated " + methods.size() + " method entities"); + LOGGER.info("Generated " + fields.size() + " field entities"); + return new QMoveEntitySearchResult(classes, methods, fields, System.currentTimeMillis() - startTime, scope); + } + + private class UnitsFinder extends JavaRecursiveElementVisitor { + + @Override + public void visitFile(PsiFile file) { + indicator.checkCanceled(); + if (strategy.acceptFile(file)) { + LOGGER.info("Indexing " + file.getName()); + super.visitFile(file); + } + } + + @Override + public void visitClass(PsiClass aClass) { + indicator.checkCanceled(); + classForName.put(getHumanReadableName(aClass), aClass); + if (!strategy.acceptClass(aClass)) { + return; + } + entities.put(aClass, new QMoveClassEntity(aClass)); + super.visitClass(aClass); + } + + @Override + public void visitField(PsiField field) { + if (!strategy.acceptField(field)) { + return; + } + indicator.checkCanceled(); + entities.put(field, new FieldEntity(field)); + super.visitField(field); + } + + @Override + public void visitMethod(PsiMethod method) { + if (!strategy.acceptMethod(method)) { + return; + } + indicator.checkCanceled(); + entities.put(method, new QMoveMethodEntity(method)); + super.visitMethod(method); + } + } + + private class PropertiesCalculator extends JavaRecursiveElementVisitor { + private int propertiesCalculated = 0; + + private PsiMethod currentMethod; + + @Override + public void visitFile(PsiFile file) { + indicator.checkCanceled(); + if (strategy.acceptFile(file)) { + super.visitFile(file); + } + } + + @Override + public void visitClass(PsiClass aClass) { + indicator.checkCanceled(); + final QMoveClassEntity entity = (QMoveClassEntity) entities.get(aClass); + if (entity == null) { + super.visitClass(aClass); + return; + } + entity.getProperties().addToRelatedClasses(aClass); + for(PsiClass superClass : PSIUtil.getAllSupers(aClass)){ + if(isClassInProject(superClass)){ + QMoveClassEntity superClassEntity = (QMoveClassEntity) entities.get(superClass); + if(superClassEntity == null){ + continue; + } + superClassEntity.getProperties().getInheritors().add(entity); + entity.getProperties().getSupers().add(superClassEntity); + } + } + + for(PsiClass innerClass : aClass.getInnerClasses()){ + if(isClassInProject(innerClass)){ + QMoveClassEntity innerClassEntity = (QMoveClassEntity) entities.get(innerClass); + if(innerClassEntity == null){ + continue; + } + entity.getProperties().getInnerClasses().add(innerClassEntity); + innerClassEntity.getProperties().getOuterClasses().add(entity); + } + } + reportPropertiesCalculated(); + super.visitClass(aClass); + } + + private boolean isProperty(PsiClass aClass, PsiMember member) { + return !(member instanceof PsiMethod && ((PsiMethod) member).isConstructor()) && (aClass.equals(member.getContainingClass()) || !MethodUtils.isPrivate(member)); + } + + @Contract("null -> false") + private boolean isClassInProject(final @Nullable PsiClass aClass) { + return aClass != null && classForName.containsKey(getHumanReadableName(aClass)); + } + + @Override + public void visitMethod(PsiMethod method) { + indicator.checkCanceled(); + final QMoveMethodEntity entity = (QMoveMethodEntity) entities.get(method); + if (entity == null) { + super.visitMethod(method); + return; + + } + if (currentMethod == null) { + currentMethod = method; + } + QMoveClassEntity containingClass = (QMoveClassEntity) entities.get(method.getContainingClass()); + if(containingClass != null){ + entity.setContainingClass(containingClass); + containingClass.getProperties().incrementNumOfMethods(); + for(PsiParameter parameter : method.getParameterList().getParameters()){ + PsiTypeElement typeElement = parameter.getTypeElement(); + if (typeElement == null) { + continue; + } + PsiType type = typeElement.getType().getDeepComponentType(); + //parameters of methods for cohesion + entity.getProperties().addToParameters(type); + containingClass.getProperties().addToParameters(type); + + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + //coupling + entity.getProperties().addToRelatedClasses(classInType); + containingClass.getProperties().addToRelatedClasses(classInType); + //composition + if(isClassInProject(classInType)){ + containingClass.getProperties().incrementUserDefinedClasses(); + } + } + //cohesion + int sumIntersection = entity.getProperties().setMethodSumIntersection(); + containingClass.getProperties().increaseSumIntersection(sumIntersection); + + entity.getProperties().setInnerClasses( + containingClass.getProperties().getInnerClasses()); + + entity.getProperties().setInheritors( + containingClass.getProperties().getInheritors()); + + entity.getProperties().setOuterClasses( + containingClass.getProperties().getOuterClasses()); + + entity.getProperties().setSupers( + containingClass.getProperties().getSupers()); + } + reportPropertiesCalculated(); + super.visitMethod(method); + if (currentMethod == method) { + currentMethod = null; + } + } + + @Override + public void visitField(PsiField field) { + indicator.checkCanceled(); + final Entity entity = entities.get(field); + if (entity == null) { + super.visitField(field); + return; + } + + final PsiClass containingClass = field.getContainingClass(); + if (containingClass != null) { + final PsiClass fieldClass = PsiUtil.resolveClassInType(field.getType()); + if(fieldClass != null){ + ((QMoveClassEntity)entities.get(containingClass)) + .getProperties().addToRelatedClasses(fieldClass); + } + if (isClassInProject(fieldClass)) { + ((QMoveClassEntity)entities.get(containingClass)) + .getProperties().incrementUserDefinedClasses(); + } + } + reportPropertiesCalculated(); + super.visitField(field); + } + + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + indicator.checkCanceled(); + PsiElement element = expression.resolve(); + if (currentMethod != null && element instanceof PsiField) { + final PsiField field = (PsiField) element; + if(field.hasModifierProperty(PsiModifier.PRIVATE)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsPrivateCalls(true); + } + + if(field.hasModifierProperty(PsiModifier.PROTECTED)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsProtectedCalls(true); + } + } + super.visitReferenceExpression(expression); + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + indicator.checkCanceled(); + final PsiMethod called = expression.resolveMethod(); + final PsiClass usedClass = called != null ? called.getContainingClass() : null; + if (currentMethod != null && called != null) { + if(called.hasModifierProperty(PsiModifier.PRIVATE)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsPrivateCalls(true); + } + + if(called.hasModifierProperty(PsiModifier.PROTECTED)){ + ((QMoveMethodEntity)entities.get(currentMethod)) + .getProperties().setContainsProtectedCalls(true); + } + + } + super.visitMethodCallExpression(expression); + } + + private void reportPropertiesCalculated() { + propertiesCalculated++; + if (indicator != null) { + indicator.setFraction((double) propertiesCalculated / entities.size()); + } + } + } + + private Optional propertiesFor(PsiElement element) { + return Optional.ofNullable(entities.get(element)) + .map(Entity::getRelevantProperties); + } + +} \ No newline at end of file diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java new file mode 100644 index 00000000..1c415b98 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveMethodEntity.java @@ -0,0 +1,92 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm.entity; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.sixrr.metrics.metricModel.MetricsRun; +import org.ml_methods_group.utils.PSIUtil; +import org.ml_methods_group.utils.QMoveUtil; + +import java.util.*; + +public class QMoveMethodEntity extends MethodEntity { + private QMoveClassEntity containingClass; + private QMoveRelevantProperties properties = new QMoveRelevantProperties(); + private MoveAbility moveAbility; + + QMoveMethodEntity(PsiMethod method) { + super(method); + } + + @Override + void calculateVector(MetricsRun metricsRun) { + moveAbility = MoveAbility.ANY_CLASS; + if(properties.isContainsProtectedCalls()){ + moveAbility = MoveAbility.INHERITOR; + } + if(properties.isContainsPrivateCalls()){ + moveAbility = MoveAbility.INNER_OR_OUTER; + } + } + + + public List> getTargets(){ + List> list = new ArrayList<>(); + list.add(properties.getInnerClasses()); + list.add(properties.getOuterClasses()); + if(moveAbility != MoveAbility.INNER_OR_OUTER){ + list.add(properties.getInheritors()); + } + return list; + } + + + public boolean isValidMoveToClass(QMoveClassEntity targetClass) { + if (!isMovable) { + return false; + } + if (properties.getInnerClasses().contains(targetClass) + || targetClass.getProperties() + .getInnerClasses().contains(containingClass)) { + return true; + } + if (properties.isContainsPrivateCalls()) { + return false; + } + return !properties.isContainsProtectedCalls() + || targetClass.getProperties().getSupers().contains(containingClass); + } + + public QMoveRelevantProperties getProperties() { + return properties; + } + + void setContainingClass(QMoveClassEntity containingClass) { + this.containingClass = containingClass; + } + + public MoveAbility getMoveAbility() { + return moveAbility; + } + + public enum MoveAbility { + ANY_CLASS, + INHERITOR, + INNER_OR_OUTER + } +} diff --git a/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java new file mode 100644 index 00000000..4dbe7c15 --- /dev/null +++ b/src/main/java/org/ml_methods_group/algorithm/entity/QMoveRelevantProperties.java @@ -0,0 +1,137 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.algorithm.entity; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiType; + +import java.util.*; + +import static org.ml_methods_group.utils.QMoveUtil.incrementMapValue; + +class QMoveRelevantProperties { + //cohesion + private double sumIntersection; + private Map parametersOfMethods = new HashMap<>(); + //coupling + private Map relatedClasses = new HashMap<>(); + + private Set outerClasses = new HashSet<>(); + private Set innerClasses = new HashSet<>(); + private Set supers = new HashSet<>(); + private Set inheritors = new HashSet<>(); + private int numUserDefinedClasses; + private boolean containsPrivateCalls; + private boolean containsProtectedCalls; + private int numOfMethods; + + void incrementNumOfMethods(){ + numOfMethods++; + } + + void addToParameters(PsiType psiType){ + incrementMapValue(psiType.getPresentableText(), + parametersOfMethods); + } + + void addToRelatedClasses(PsiClass psiClass){ + incrementMapValue(psiClass.getQualifiedName(), + relatedClasses); + } + + void increaseSumIntersection(int val){ + sumIntersection += val; + } + + int setMethodSumIntersection(){ + sumIntersection = parametersOfMethods.size(); + return parametersOfMethods.size(); + } + Map getParametersOfMethods() { + return parametersOfMethods; + } + + Map getRelatedClasses() { + return relatedClasses; + } + + Set getInnerClasses() { + return innerClasses; + } + + void setInnerClasses(Set innerClasses) { + this.innerClasses = innerClasses; + } + + + void setSupers(Set supers) { + this.supers = supers; + } + + Set getInheritors() { + return inheritors; + } + + void incrementUserDefinedClasses(){ + numUserDefinedClasses++; + } + int getNumUserDefinedClasses() { + return numUserDefinedClasses; + } + + boolean isContainsPrivateCalls() { + return containsPrivateCalls; + } + + void setContainsPrivateCalls(boolean containsPrivateCalls) { + this.containsPrivateCalls = containsPrivateCalls; + } + + boolean isContainsProtectedCalls() { + return containsProtectedCalls; + } + + void setContainsProtectedCalls(boolean containsProtectedCalls) { + this.containsProtectedCalls = containsProtectedCalls; + } + + void setInheritors(Set inheritors) { + this.inheritors = inheritors; + } + + Set getSupers() { + return supers; + } + + + double getSumIntersection() { + return sumIntersection; + } + + int getNumOfMethods() { + return numOfMethods; + } + + + Set getOuterClasses() { + return outerClasses; + } + + void setOuterClasses(Set outerClasses) { + this.outerClasses = outerClasses; + } +} diff --git a/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java b/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java index 3dcb9a92..ad3de315 100644 --- a/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java +++ b/src/main/java/org/ml_methods_group/plugin/AutomaticRefactoringAction.java @@ -33,6 +33,7 @@ import org.jetbrains.annotations.Nullable; import org.ml_methods_group.algorithm.AlgorithmResult; import org.ml_methods_group.algorithm.entity.Entity; +import org.ml_methods_group.algorithm.entity.QMoveClassEntity; import org.ml_methods_group.config.ArchitectureReloadedConfig; import org.ml_methods_group.config.Logging; import org.ml_methods_group.refactoring.RefactoringExecutionContext; @@ -150,6 +151,7 @@ private void showDialogs(@NotNull RefactoringExecutionContext context) { private static void checkRefactoringProfile() { final Set> requestedSet = Entity.getRequestedMetrics(); + requestedSet.addAll(QMoveClassEntity.getRequestedMetrics()); final String profileName = ArchitectureReloadedBundle.message(REFACTORING_PROFILE_KEY); final MetricsProfileRepository repository = MetricsProfileRepository.getInstance(); if (MetricsProfilesUtil.checkMetricsList(profileName, requestedSet, repository)) { diff --git a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java index 519c9ae6..eb498ad7 100644 --- a/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java +++ b/src/main/java/org/ml_methods_group/refactoring/RefactoringExecutionContext.java @@ -33,6 +33,8 @@ import org.ml_methods_group.algorithm.*; import org.ml_methods_group.algorithm.entity.EntitySearchResult; import org.ml_methods_group.algorithm.entity.EntitySearcher; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearchResult; +import org.ml_methods_group.algorithm.entity.QMoveEntitySearcher; import org.ml_methods_group.config.Logging; import java.util.*; @@ -44,7 +46,7 @@ public class RefactoringExecutionContext { private static final Logger LOGGER = Logging.getLogger(RefactoringExecutionContext.class); private static final List> ALGORITHMS = Arrays.asList(ARI.class, AKMeans.class, - CCDA.class, HAC.class, MRI.class); + CCDA.class, HAC.class, MRI.class, QMove.class); @NotNull private final MetricsRunImpl metricsRun = new MetricsRunImpl(); @@ -112,7 +114,18 @@ private void execute(ProgressIndicator indicator) { entitySearchResult = ApplicationManager.getApplication() .runReadAction((Computable) () -> EntitySearcher.analyze(scope, metricsRun)); for (String algorithm : requestedAlgorithms) { - calculateAlgorithmForName(algorithm); + if(algorithm.equals("QMove")){ + QMoveEntitySearchResult qMoveEntitySearchResult = ApplicationManager.getApplication() + .runReadAction((Computable) () + -> QMoveEntitySearcher.analyze(scope, metricsRun)); + final Algorithm qMoveAlgorithm = new QMove(); + final AlgorithmResult result = qMoveAlgorithm + .execute(qMoveEntitySearchResult, executorService, isFieldRefactoringAvailable); + algorithmsResults.add(result); + } + else{ + calculateAlgorithmForName(algorithm); + } } indicator.setText("Finish refactorings search..."); } diff --git a/src/main/java/org/ml_methods_group/utils/QMoveUtil.java b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java new file mode 100644 index 00000000..958660b0 --- /dev/null +++ b/src/main/java/org/ml_methods_group/utils/QMoveUtil.java @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ml_methods_group.utils; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; + +import java.util.Map; +import java.util.stream.Stream; + +public class QMoveUtil { + public static int removeFromUnion(Map union, Map set){ + int res = union.size(); + for(Map.Entry item : set.entrySet()){ + int remove = item.getValue(); + T key = item.getKey(); + int present = union.get(key); + if(remove == present){ + res--; + } + } + return res; + } + + public static int addToUnion(Map union, Map set){ + int res = union.size(); + for(Map.Entry item : set.entrySet()){ + T key = item.getKey(); + if(!union.containsKey(key)){ + res++; + } + } + return res; + } + + public static void incrementMapValue(T t, Map map){ + map.put(t, + map.getOrDefault(t, 0) + 1); + } +} diff --git a/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java b/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java index 45057dcb..cbae9b9f 100755 --- a/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java +++ b/src/main/java/org/ml_methods_group/utils/RefactoringUtil.java @@ -84,7 +84,9 @@ public static void moveRefactoring(@NotNull List refactorings, .filter(Objects::nonNull) .collect(Collectors.toList()); final Set accepted = moveMembersRefactoring(members, target, scope); - model.setAcceptedRefactorings(accepted.stream().map(m -> new Refactoring(m, PsiSearchUtil.getHumanReadableName(target), 0, true)).collect(Collectors.toSet())); + if (model != null) { + model.setAcceptedRefactorings(accepted.stream().map(m -> new Refactoring(m, PsiSearchUtil.getHumanReadableName(target), 0, true)).collect(Collectors.toSet())); + } } }); } diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java index adcbcb7d..8900df52 100644 --- a/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/JavaMetricProvider.java @@ -116,6 +116,11 @@ private static void initializeClassMetrics(Collection metrics) { metrics.add(new FanInClassMetric()); metrics.add(new FanOutClassMetric()); metrics.add(new BlankLinesCountClassMetric()); + //mine + metrics.add(new NumPublicMethodsClassMetric()); + metrics.add(new MeasureOfFunctionalAbstractionMetric()); + metrics.add(new DataAccessClassMetric()); + metrics.add(new CohesionAmongMethodsOfClassMetric()); } private static void initializeInterfaceMetrics(Collection metrics) { @@ -349,6 +354,12 @@ private static void initializeProjectMetrics(Collection metrics) { metrics.add(new PercentMethodsJavadocedProjectMetric()); metrics.add(new PolymorphismFactorProjectMetric()); metrics.add(new TotalCyclomaticComplexityProjectMetric()); + //mine + metrics.add(new IsRootOfHierarchyClassMetric()); + metrics.add(new NumPolymorphicMethodsProjectMetric()); + metrics.add(new AverageCyclomaticComplexityProjectMetric()); + metrics.add(new MeasureOfFunctionalAbstractionMetric()); + metrics.add(new DirectClassCouplingMetric()); } @NotNull diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/CohesionAmongMethodsOfClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/CohesionAmongMethodsOfClassCalculator.java new file mode 100644 index 00000000..59d20d75 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/CohesionAmongMethodsOfClassCalculator.java @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class CohesionAmongMethodsOfClassCalculator extends ClassCalculator { + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + int sumIntersection = 0; + int numMethods = 0; + Set parameters = new HashSet<>(); + for(PsiMethod method : aClass.getMethods()){ + if(method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)){ + continue; + } + numMethods++; + Set parametersInMethod = Stream.of(method.getParameterList()). + flatMap(x -> Stream.of(x.getParameters())). + map(PsiVariable::getType).collect(Collectors.toSet()); + sumIntersection += parametersInMethod.size(); + parameters.addAll(parametersInMethod); + } + if(parameters.size() == 0){ + return; + } + postMetric(aClass, sumIntersection, numMethods * parameters.size()); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java new file mode 100644 index 00000000..17dc7d78 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DataAccessClassCalculator.java @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +import java.util.Arrays; + +public class DataAccessClassCalculator extends ClassCalculator { + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (!isConcreteClass(aClass)) { + return; + } + PsiField[] fields = aClass.getFields(); + if (fields.length == 0) { + postMetric(aClass, 0); + return; + } + double numberOfPrivateFields = + Arrays.stream( + fields).filter( + x -> x.hasModifierProperty( + PsiModifier.PRIVATE)).count(); + postMetric(aClass, numberOfPrivateFields + / fields.length); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DirectClassCouplingCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DirectClassCouplingCalculator.java new file mode 100644 index 00000000..4ad81426 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/DirectClassCouplingCalculator.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.sixrr.stockmetrics.projectCalculators.ProjectCalculator; + +import java.util.HashSet; +import java.util.Set; + +public class DirectClassCouplingCalculator extends ClassCalculator { + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + Set classes = new HashSet<>(); + PsiField[] fields = aClass.getFields(); + for (PsiField field : fields) { + if (!field.isPhysical()) { + continue; + } + PsiType type = field.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + classes.add(classInType); + } + PsiMethod[] methods = aClass.getMethods(); + for (PsiMethod method : methods){ + PsiParameter[] parameters = method.getParameterList().getParameters(); + for(PsiParameter parameter : parameters){ + PsiTypeElement typeElement = parameter.getTypeElement(); + if(typeElement == null){ + continue; + } + PsiType type = typeElement.getType().getDeepComponentType(); + PsiClass classInType = PsiUtil.resolveClassInType(type); + if (classInType == null) { + continue; + } + classes.add(classInType); + } + } + postMetric(aClass, classes.size()); + } + } + +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/IsRootOfHierarchyClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/IsRootOfHierarchyClassCalculator.java new file mode 100644 index 00000000..bf41718e --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/IsRootOfHierarchyClassCalculator.java @@ -0,0 +1,36 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.sixrr.metrics.utils.ClassUtils; + +public class IsRootOfHierarchyClassCalculator extends ClassCalculator { + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + postMetric(aClass, ClassUtils.isRoot(aClass) ? 1 : 0); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfAggregationClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfAggregationClassCalculator.java new file mode 100644 index 00000000..52e09006 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfAggregationClassCalculator.java @@ -0,0 +1,73 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.FileTypeIndex; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.util.PsiTypesUtil; +import com.intellij.util.indexing.FileBasedIndex; +import com.sixrr.stockmetrics.projectCalculators.ElementCountProjectCalculator; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MeasureOfAggregationClassCalculator extends ClassCalculator { + private Set userDefinedClasses = new HashSet<>(); + + @Override + protected PsiElementVisitor createVisitor() { + collectUserDefinedClasses(); + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + postMetric(aClass, (int)Stream.of(aClass.getFields()).map(PsiVariable::getType). + map(PsiTypesUtil::getPsiClass).filter(Objects::nonNull). + filter(x -> userDefinedClasses.contains(x)).count()); + } + } + + private void collectUserDefinedClasses() { + final Runnable runnable = () -> { + Project project = executionContext.getProject(); + userDefinedClasses = FileBasedIndex.getInstance() + .getContainingFiles( + FileTypeIndex.NAME, + JavaFileType.INSTANCE, + ProjectScope.getProjectScope(project)).stream(). + map(x -> PsiManager.getInstance(project).findFile(x)). + filter(x -> x instanceof PsiJavaFile).map(x -> (PsiJavaFile)x). + flatMap(x -> Stream.of(x.getClasses())). + collect(Collectors.toSet()); + + + }; + final ProgressManager progressManager = ProgressManager.getInstance(); + progressManager.runProcess(runnable, null); + } + + +} \ No newline at end of file diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfFunctionalAbstractionCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfFunctionalAbstractionCalculator.java new file mode 100644 index 00000000..67ee6b74 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/MeasureOfFunctionalAbstractionCalculator.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +public class MeasureOfFunctionalAbstractionCalculator extends ClassCalculator { + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (isConcreteClass(aClass)) { + final PsiMethod[] allMethods = aClass.getAllMethods(); + double numInheritedMethods = 0; + for (final PsiMethod method : allMethods) { + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null && !containingClass.equals(aClass) && + !method.hasModifierProperty(PsiModifier.STATIC)) { + numInheritedMethods++; + } + } + + postMetric(aClass, numInheritedMethods / allMethods.length); + } + } + } +} + diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumAncestorsClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumAncestorsClassCalculator.java new file mode 100644 index 00000000..e8b9874d --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumAncestorsClassCalculator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiMethod; +import com.sixrr.metrics.utils.MethodUtils; +import com.sixrr.stockmetrics.projectCalculators.ProjectCalculator; +import com.sixrr.stockmetrics.utils.CyclomaticComplexityUtil; + +public class NumAncestorsClassCalculator extends ClassCalculator { + + + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + + @Override + public void visitClass(PsiClass aClass) { + postMetric(aClass, aClass.getExtendsListTypes().length); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPolymorphicMethodsProjectCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPolymorphicMethodsProjectCalculator.java new file mode 100644 index 00000000..9db3b59b --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPolymorphicMethodsProjectCalculator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.JavaRecursiveElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.sixrr.stockmetrics.projectCalculators.ElementCountProjectCalculator; + +import java.util.stream.Stream; + +public class NumPolymorphicMethodsProjectCalculator extends ClassCalculator { + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + postMetric(aClass, (int) Stream.of(aClass.getMethods()). + filter(x -> x.findSuperMethods().length != 0).count()); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java new file mode 100644 index 00000000..61b71af1 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classCalculators/NumPublicMethodsClassCalculator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2005-2017 Sixth and Red River Software, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classCalculators; + +import com.intellij.psi.*; + +import java.util.Arrays; + +public class NumPublicMethodsClassCalculator extends ClassCalculator { + @Override + protected PsiElementVisitor createVisitor() { + return new Visitor(); + } + + private class Visitor extends JavaRecursiveElementVisitor { + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (!isConcreteClass(aClass)) { + return; + } + postMetric(aClass, Arrays.stream( + aClass.getMethods()).filter( + x -> !x.isConstructor() && x.hasModifierProperty( + PsiModifier.PUBLIC)).count()); + } + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/CohesionAmongMethodsOfClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/CohesionAmongMethodsOfClassMetric.java new file mode 100644 index 00000000..25899d2e --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/CohesionAmongMethodsOfClassMetric.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.CohesionAmongMethodsOfClassCalculator; +import org.jetbrains.annotations.NotNull; + +public class CohesionAmongMethodsOfClassMetric extends ClassMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Cohesion Among Methods of Class"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "CAM"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Ratio; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new CohesionAmongMethodsOfClassCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DataAccessClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DataAccessClassMetric.java new file mode 100644 index 00000000..ba855474 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DataAccessClassMetric.java @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.DataAccessClassCalculator; +import org.jetbrains.annotations.NotNull; + +public class DataAccessClassMetric extends ClassMetric { + @NotNull + @Override + public String getDisplayName() { + return "Data Access Class Metric"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "DAM"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Ratio; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new DataAccessClassCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DirectClassCouplingMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DirectClassCouplingMetric.java new file mode 100644 index 00000000..d33bca66 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/DirectClassCouplingMetric.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.DirectClassCouplingCalculator; +import org.jetbrains.annotations.NotNull; + +public class DirectClassCouplingMetric extends ClassMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Direct Class Coupling"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "DCC"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new DirectClassCouplingCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/IsRootOfHierarchyClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/IsRootOfHierarchyClassMetric.java new file mode 100644 index 00000000..678cf8cc --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/IsRootOfHierarchyClassMetric.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.IsRootOfHierarchyClassCalculator; +import org.jetbrains.annotations.NotNull; + +public class IsRootOfHierarchyClassMetric extends ClassMetric { + + @NotNull + @Override + public String getDisplayName() { + return "Number of hierarchies"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "NOH"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new IsRootOfHierarchyClassCalculator(); + } +} \ No newline at end of file diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfAggregationClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfAggregationClassMetric.java new file mode 100644 index 00000000..b25d3c52 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfAggregationClassMetric.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.MeasureOfAggregationClassCalculator; +import org.jetbrains.annotations.NotNull; + +public class MeasureOfAggregationClassMetric extends ClassMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Measure of Aggregation"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "MOA"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new MeasureOfAggregationClassCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfFunctionalAbstractionMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfFunctionalAbstractionMetric.java new file mode 100644 index 00000000..bcfcfac3 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/MeasureOfFunctionalAbstractionMetric.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.MeasureOfFunctionalAbstractionCalculator; +import org.jetbrains.annotations.NotNull; + +public class MeasureOfFunctionalAbstractionMetric extends ClassMetric { + + @NotNull + @Override + public String getDisplayName() { + return "Measure of functional abstraction"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "MFA"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Ratio; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new MeasureOfFunctionalAbstractionCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumAncestorsClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumAncestorsClassMetric.java new file mode 100644 index 00000000..b3bf3fc1 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumAncestorsClassMetric.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.NumAncestorsClassCalculator; +import org.jetbrains.annotations.NotNull; + +public class NumAncestorsClassMetric extends ClassMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Number of ancestors"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "NOA"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new NumAncestorsClassCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumPublicMethodsClassMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumPublicMethodsClassMetric.java new file mode 100644 index 00000000..5ab3d614 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/classMetrics/NumPublicMethodsClassMetric.java @@ -0,0 +1,52 @@ +/* + * Copyright 2005-2016 Sixth and Red River Software, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.classMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.NumConstructorsCalculator; +import com.sixrr.stockmetrics.classCalculators.NumPublicMethodsClassCalculator; +import com.sixrr.stockmetrics.classMetrics.ClassMetric; +import com.sixrr.stockmetrics.i18n.StockMetricsBundle; +import org.jetbrains.annotations.NotNull; + +public class NumPublicMethodsClassMetric extends ClassMetric { + + @NotNull + @Override + public String getDisplayName() { + return "Number of public methods"; + } + + @NotNull + @Override + public String getAbbreviation() { + return "CIS"; + } + + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + @NotNull + @Override + public MetricCalculator createCalculator() { + return new NumPublicMethodsClassCalculator(); + } +} diff --git a/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java new file mode 100644 index 00000000..e3fdba57 --- /dev/null +++ b/stockmetrics/src/main/java/com/sixrr/stockmetrics/projectMetrics/NumPolymorphicMethodsProjectMetric.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Machine Learning Methods in Software Engineering Group of JetBrains Research + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sixrr.stockmetrics.projectMetrics; + +import com.sixrr.metrics.MetricCalculator; +import com.sixrr.metrics.MetricType; +import com.sixrr.stockmetrics.classCalculators.NumPolymorphicMethodsProjectCalculator; +import org.jetbrains.annotations.NotNull; + +public class NumPolymorphicMethodsProjectMetric extends ProjectMetric { + /** + * The user-visible name of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the display name for the metric. + */ + @NotNull + @Override + public String getDisplayName() { + return "Number of polymorphic methods"; + } + + /** + * The user-visible abbreviation of the metric. This need not be unique globally, but should be unique within a metric category + * + * @return the abbreviation for the metric. + */ + @NotNull + @Override + public String getAbbreviation() { + return "NOP"; + } + + /** + * The type of the metric, indicating whether the number returned is a score, a count, or an average. + * + * @return the metric type + */ + @NotNull + @Override + public MetricType getType() { + return MetricType.Count; + } + + /** + * Create a calculator for this method. The calculator returned is used for the duration of one entire metrics run. + * + * @return a calculator for this metric. + */ + @NotNull + @Override + public MetricCalculator createCalculator() { + return new NumPolymorphicMethodsProjectCalculator(); + } +}