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
1 change: 1 addition & 0 deletions 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 Down
10 changes: 10 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@ dependencies {
implementation("org.ow2.asm:asm-analysis:9.5")
implementation("org.ow2.asm:asm-util:9.5")

implementation ("org.json:json:20240303")

testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
useJUnitPlatform()
}

tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}

tasks.withType<Javadoc> {
options.encoding = "UTF-8"
}
26 changes: 15 additions & 11 deletions src/main/java/org/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.example.visitor.ClassPrinter;
import org.objectweb.asm.ClassReader;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
Expand All @@ -13,17 +14,20 @@ public class Example {
public static void main(String[] args) throws IOException {
// var printer = new ByteCodePrinter();
// printer.printBubbleSortBytecode();
try (JarFile sampleJar = new JarFile("src/main/resources/sample.jar")) {
Enumeration<JarEntry> enumeration = sampleJar.entries();
// try (JarFile sampleJar = new JarFile("src/main/resources/sample.jar")) {
// Enumeration<JarEntry> enumeration = sampleJar.entries();
//
// while (enumeration.hasMoreElements()) {
// JarEntry entry = enumeration.nextElement();
// if (entry.getName().endsWith(".class")) {
// ClassPrinter cp = new ClassPrinter();
// ClassReader cr = new ClassReader(sampleJar.getInputStream(entry));
// cr.accept(cp, 0);
// }
// }
// }

while (enumeration.hasMoreElements()) {
JarEntry entry = enumeration.nextElement();
if (entry.getName().endsWith(".class")) {
ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader(sampleJar.getInputStream(entry));
cr.accept(cp, 0);
}
}
}
JarMetrics jarMetrics = new JarMetrics("src/main/resources/lab2-DenisStepanidenko.jar");
jarMetrics.getMetrics();
}
}
230 changes: 230 additions & 0 deletions src/main/java/org/example/JarMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package org.example;

import org.example.graph.Graph;
import org.example.graph.Node;
import org.example.visitor.ClassCalculateMetrics;
import org.objectweb.asm.ClassReader;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.json.JSONObject;

public class JarMetrics {

private String pathToJarFile;
private Node root;
private Map<String, Node> classMap;
private Integer countOfClass = 0;
private int a;
private int b;
private int c;
private int countOfFields;
private int countOfMethods;
int maxDepth;
double averageDepth;
int overrideMethods;
double averageOverrideMethods;
double averageCountOfFields;

private Graph graph = new Graph();

public JarMetrics(String pathToJarFile) {
this.pathToJarFile = pathToJarFile;
root = new Node("java/lang/Object", "", new HashSet<>(), List.of("hashCode()", "toString()", "equals(Ljava/lang/Object;)", "wait()", "notify()", "notifyAll()"));
classMap = new HashMap<>();
classMap.put(root.getClassName(), root);

}

public void getMetrics() {

try (JarFile sampleJar = new JarFile(pathToJarFile)) {
Enumeration<JarEntry> enumeration = sampleJar.entries();

while (enumeration.hasMoreElements()) {
JarEntry entry = enumeration.nextElement();
if (entry.getName().endsWith(".class")) {


ClassCalculateMetrics calculateMetrics = new ClassCalculateMetrics();
ClassReader cr = new ClassReader(sampleJar.getInputStream(entry));
cr.accept(calculateMetrics, 0);

countOfClass++;
a += calculateMetrics.getMetrics().getA();
b += calculateMetrics.getMetrics().getB();
c += calculateMetrics.getMetrics().getC();
countOfFields += calculateMetrics.getMetrics().getCountOfFields();
countOfMethods += calculateMetrics.getMetrics().getCountOfMethods();

Node classNode = new Node(calculateMetrics.getMetrics().getClassName(), calculateMetrics.getMetrics().getSuperClassName(), calculateMetrics.getMetrics().getNameOfInterfaces(), calculateMetrics.getMetrics().getNameOfMethods());


classMap.put(classNode.getClassName(), classNode);
}
}

createGraph();

// Запускаем dfs для подсчёта глубины наследования
graph.dfs(root, 0);

maxDepth = graph.getMaxDepth();
averageDepth = graph.averageDepth();

overrideMethods = calculateOverrideMethods();
averageOverrideMethods = (double) overrideMethods / countOfMethods;
averageCountOfFields = (double) countOfFields / countOfClass;


writeToJson(pathToJarFile, a, b, c, averageCountOfFields,
maxDepth, averageDepth, averageOverrideMethods, countOfMethods, overrideMethods);

} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static void writeToJson(String jarFileName, double aMetric, double bMetric, double cMetric,
double fieldsAverage, double maxDepth, double averageDepth, double averageOverridenMethods,
int methods, int overridenMethods) {
JSONObject metricJson = new JSONObject();
metricJson.put("jarName", jarFileName);
metricJson.put("fieldsAverage", fieldsAverage);
metricJson.put("A", aMetric);
metricJson.put("B", bMetric);
metricJson.put("C", cMetric);
metricJson.put("ABC", Math.sqrt(aMetric * aMetric + bMetric * bMetric + cMetric * cMetric));
metricJson.put("maxInheritanceDepth", maxDepth);
metricJson.put("averageInheritanceDepth", averageDepth);
metricJson.put("averageOverridenMethods", averageOverridenMethods);
metricJson.put("methods", methods);
metricJson.put("overridenMethods", overridenMethods);

try {
File file = new File("metrics.json");
file.createNewFile();
FileWriter fileWriter = new FileWriter(file);
System.out.print(metricJson);

fileWriter.write(metricJson.toString(4));
fileWriter.flush();
fileWriter.close();

} catch (IOException e) {
e.printStackTrace();
}

}

private int calculateOverrideMethods() {

// нужно посчитать переопределённые методы и у extends случаев и у implements
// superClass или интерфейсы

findAllSuperClasses();
findAllInterfaces();

return calculateOverrideMethodsInInterfaces() + calculateOverrideMethodsInSuperClasses();

}

private int calculateOverrideMethodsInSuperClasses() {

int result = 0;
for (Node node : classMap.values()) {

for (String method : node.getNameOfMethods()) {
for (Node parent : node.getSuperClasses()) {
if (parent != null && parent.getNameOfMethods().contains(method))
result++;
}
}
}

return result;
}

private int calculateOverrideMethodsInInterfaces() {
int result = 0;

for (Node node : classMap.values()) {

for (String method : node.getNameOfMethods()) {
for (String interfaceNode : node.getNameOfInterfaces()) {
Node parent = classMap.get(interfaceNode);
if (parent != null && parent.getNameOfMethods().contains(method))
result++;
}
}
}

return result;
}

private void findAllInterfaces() {

for (Node node : classMap.values()) {
findAllInterfaces(node, node);
}

}

private void findAllInterfaces(Node children, Node parent) {

Set<String> parentInterfaces = new HashSet<>(parent.getNameOfInterfaces());

for (String nameInterface : parentInterfaces) {
Node node = classMap.get(nameInterface);
if (node != null) {
children.getNameOfInterfaces().addAll(node.getNameOfInterfaces());
findAllInterfaces(children, node);
}
}

}

/**
* Здесь мы заполним всю цепочку наследования
*/
private void findAllSuperClasses() {

for (Node node : classMap.values()) {
node.getSuperClasses().add(classMap.get(node.getSuperClassName()));
findAllSuperClasses(node, node);
}
}

private void findAllSuperClasses(Node children, Node parent) {

Node nextParent = classMap.get(parent.getSuperClassName());
if (nextParent != null) {
children.getSuperClasses().add(nextParent);
findAllSuperClasses(children, nextParent);
}
}

private void createGraph() {

for (Node from : classMap.values()) {
Node to = classMap.get(from.getSuperClassName());
graph.addEdge(from, to);
}

for (Node from : classMap.values()) {

for (String interfacesName : from.getNameOfInterfaces()) {
Node to = classMap.get(interfacesName);
graph.addEdge(from, to);
}
}

}


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

import java.util.*;

public class Graph {

private Map<Node, List<Node>> adjacencyList = new HashMap<>();

public int maxDepth = -1;
public final List<Node> visited = new ArrayList<>();
private Map<String, Integer> depths = new HashMap<>();

public Map<Node, List<Node>> getAdjacencyList() {
return adjacencyList;
}

public void setAdjacencyList(Map<Node, List<Node>> adjacencyList) {
this.adjacencyList = adjacencyList;
}

public int getMaxDepth() {
return maxDepth;
}

public void setMaxDepth(int maxDepth) {
this.maxDepth = maxDepth;
}

public List<Node> getVisited() {
return visited;
}

public Map<String, Integer> getDepths() {
return depths;
}

public void setDepths(Map<String, Integer> depths) {
this.depths = depths;
}

public void addNode(Node node) {
adjacencyList.putIfAbsent(node, new ArrayList<>());
}


public void addEdge(Node node1, Node node2) {
addNode(node1);
addNode(node2);

adjacencyList.get(node2).add(node1);
}



public double averageDepth() {
return depths.values().stream().mapToInt(i -> i).average().orElse(0);
}

public int dfs(Node root, int count) {
visited.add(root);

for (Node node : adjacencyList.get(root)) {
if (!visited.contains(node)) {
int dfs = dfs(node, ++count);
if (maxDepth < dfs) {
maxDepth = dfs;
}

depths.put(node.getClassName(), count);
count--;
}
}

return count;
}
}
Loading