Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/9A22t-SS)
Разработать standalone приложение, которое имеет следующие возможности:

Принимает на вход проект в виде .jar файла
Expand All @@ -16,4 +17,5 @@

Гайд по использованию ASM: https://asm.ow2.io/asm4-guide.pdf

Дополнительное (необязательное задание): сделайте агента для сбора покрытия по строчкам
Дополнительное (необязательное задание): сделайте агента для сбора покрытия по строчкам
TODO: подумать как это можно реализовать
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id("java")
id("io.freefair.lombok") version "8.10.2"
}

group = "org.example"
Expand Down
29 changes: 0 additions & 29 deletions src/main/java/org/example/Example.java

This file was deleted.

76 changes: 76 additions & 0 deletions src/main/java/org/example/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.example;

import org.example.metrics.GlobalMetrics;
import org.example.metrics.visitors.ClassAnalyzer;
import org.objectweb.asm.ClassReader;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class Main {
public static void main(String[] args) throws IOException {
String inputJar = "src/main/resources/guava.jar";
String outputFile = "src/main/resources/output.json";

if (args.length == 2) {
inputJar = args[0];
outputFile = args[1];
}

GlobalMetrics globalMetrics = new GlobalMetrics();

try (JarFile inputJarFile = new JarFile(inputJar)) {
Enumeration<JarEntry> enumeration = inputJarFile.entries();

while (enumeration.hasMoreElements()) {
JarEntry entry = enumeration.nextElement();
if (entry.getName().endsWith(".class")) {
ClassReader reader = new ClassReader(inputJarFile.getInputStream(entry));
ClassAnalyzer analyzer = new ClassAnalyzer(globalMetrics);
reader.accept(analyzer, 0);
}
}
}

globalMetrics.calculateFinalMetrics();

printMetricsToConsole(globalMetrics);

saveMetricsToJson(globalMetrics, outputFile);
}

private static void printMetricsToConsole(GlobalMetrics metrics) {
System.out.println("Максимальная глубина наследования: " + metrics.getMaxInheritanceDepth());
System.out.println("Средняя глубина наследования: " + metrics.getAvgInheritanceDepth());
System.out.println("Средняя метрика ABC: " + metrics.getAvgAbcMetric());
System.out.println("Среднее количество переопределенных методов: " + metrics.getAvgOverriddenMethods());
System.out.println("Среднее количество полей в классе: " + metrics.getAvgFieldCount());
}

private static void saveMetricsToJson(GlobalMetrics metrics, String outputFile) {
String json = String.format(
"{\n" +
" \"maxInheritanceDepth\": %d,\n" +
" \"avgInheritanceDepth\": %f,\n" +
" \"avgAbcMetric\": %f,\n" +
" \"avgOverriddenMethods\": %f,\n" +
" \"avgFieldCount\": %f\n" +
"}",
metrics.getMaxInheritanceDepth(),
metrics.getAvgInheritanceDepth(),
metrics.getAvgAbcMetric(),
metrics.getAvgOverriddenMethods(),
metrics.getAvgFieldCount()
);

try (FileWriter writer = new FileWriter(outputFile)) {
writer.write(json);
System.out.println("Метрики сохранены в файл: " + outputFile);
} catch (IOException e) {
System.err.println("Ошибка при записи в файл: " + e.getMessage());
}
}
}
29 changes: 0 additions & 29 deletions src/main/java/org/example/example/BubbleSort.java

This file was deleted.

20 changes: 20 additions & 0 deletions src/main/java/org/example/metrics/ClassMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.example.metrics;

import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Setter
@Getter
public class ClassMetrics {
private String className;
private String superClassName;
private int fieldCount = 0;
private List<MethodMetrics> methodsMetrics = new ArrayList<>();

public void addMethodMetrics(MethodMetrics methodMetrics) {
methodsMetrics.add(methodMetrics);
}
}
108 changes: 108 additions & 0 deletions src/main/java/org/example/metrics/GlobalMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.example.metrics;


import java.util.*;

public class GlobalMetrics {
Map<String, String> parents = new HashMap<>();
Map<String, List<String>> methodSignatures = new HashMap<>();
Map<String, Integer> depths = new HashMap<>();
Map<String, Integer> overriddenMethodsCounter = new HashMap<>();
Map<String, ClassMetrics> classMetricsMap = new HashMap<>();

public void addClassHierarchy(String className, String superClassName) {
parents.put(className, superClassName);
}

public void addClassMethods(String className, List<String> methodSignatures) {
this.methodSignatures.put(className, methodSignatures);
}

public void addClassMetrics(ClassMetrics classMetrics) {
this.classMetricsMap.put(classMetrics.getClassName(), classMetrics);
}

private void calculateInheritanceDepth(String className) {
int depth = 0;
String currentClassName = className;

while (parents.containsKey(currentClassName)) {
depth++;
currentClassName = parents.get(currentClassName);
}

depths.put(className, depth);
}

private void countOverriddenMethods(String className, List<String> methods) {
if (!parents.containsKey(className)) {
overriddenMethodsCounter.put(className, 0);
return;
}

String parentClassName = parents.get(className);
Set<String> allParentMethods = new HashSet<>();

while (parentClassName != null && parents.containsKey(parentClassName)) {
if (methodSignatures.containsKey(parentClassName)) {
allParentMethods.addAll(methodSignatures.get(parentClassName));
}

parentClassName = parents.get(parentClassName);
}

int counter = 0;

for (String method : methods) {
if (allParentMethods.contains(method)) {
counter++;
}
}

overriddenMethodsCounter.put(className, counter);
}

public void calculateFinalMetrics() {
for (String className : parents.keySet()) {
calculateInheritanceDepth(className);
}

for (String className : methodSignatures.keySet()) {
List<String> methods = methodSignatures.get(className);
countOverriddenMethods(className, methods);
}
}

public int getMaxInheritanceDepth() {
return depths.values().stream().max(Integer::compareTo).orElse(0);
}

public double getAvgInheritanceDepth() {
return depths.values().stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
}

public double getAvgAbcMetric() {
return classMetricsMap.values().stream()
.flatMap(classMetrics -> classMetrics.getMethodsMetrics().stream())
.mapToDouble(MethodMetrics::getAbc)
.average()
.orElse(0.0);
}

public double getAvgOverriddenMethods() {
return overriddenMethodsCounter.values().stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
}

public double getAvgFieldCount() {
return classMetricsMap.values().stream()
.mapToInt(ClassMetrics::getFieldCount)
.average()
.orElse(0.0);
}
}
16 changes: 16 additions & 0 deletions src/main/java/org/example/metrics/MethodMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.example.metrics;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class MethodMetrics {
private String signature;

private int assignments = 0;
private int branches = 0;
private int conditions = 0;

private double abc = 0;
}
56 changes: 56 additions & 0 deletions src/main/java/org/example/metrics/visitors/ClassAnalyzer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.example.metrics.visitors;

import org.example.metrics.ClassMetrics;
import org.example.metrics.GlobalMetrics;
import org.example.metrics.MethodMetrics;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class ClassAnalyzer extends ClassVisitor {
GlobalMetrics globalMetrics;
ClassMetrics classMetrics = new ClassMetrics();

public ClassAnalyzer(GlobalMetrics globalMetrics) {
super(Opcodes.ASM9);
this.globalMetrics = globalMetrics;
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
classMetrics.setClassName(name);
classMetrics.setSuperClassName(superName);
}

@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
classMetrics.setFieldCount(classMetrics.getFieldCount() + 1);
return super.visitField(access, name, descriptor, signature, value);
}

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (!name.equals("<init>") && !name.equals("<clinit>")) {
MethodMetrics methodMetrics = new MethodMetrics();
methodMetrics.setSignature(signature);
classMetrics.addMethodMetrics(methodMetrics);
return new MethodAnalyzer(methodMetrics);
}

return super.visitMethod(access, name, descriptor, signature, exceptions);
}

@Override
public void visitEnd() {
globalMetrics.addClassHierarchy(classMetrics.getClassName(), classMetrics.getSuperClassName());
globalMetrics.addClassMethods(
classMetrics.getClassName(),
classMetrics.getMethodsMetrics().stream().map(MethodMetrics::getSignature).toList()
);
globalMetrics.addClassMetrics(classMetrics);

super.visitEnd();
}
}
Loading