-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMain.java
More file actions
211 lines (184 loc) · 10.1 KB
/
Main.java
File metadata and controls
211 lines (184 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
// Path for the output file and the number of top entries to include
private static String outputFilePath;
private static int topN;
private static List<Class<?>> classes;
// Maps to store counts of fields, methods, subtypes, and supertypes for each class
private static final Map<String, Integer> fieldsDeclared = new HashMap<>(),
fieldsAll = new HashMap<>(),
methodsAll = new HashMap<>(),
methodsDeclared = new HashMap<>(),
subtypesTotal = new HashMap<>(),
supertypesTotal = new HashMap<>();
public static void main(String[] args) {
// Handle command-line arguments to set file paths and topN limit
switch (args.length) {
case 1 -> {
// Case with 1 argument: Set topN, default output file path, and load all JDK classes
topN = Integer.parseInt(args[0]);
outputFilePath = "resources" + File.separator + "output.txt";
classes = ClassScanner.totalClasses();
}
case 3 -> {
// Case with 3 arguments: Set input and output file paths and topN, then load classes
String inputFilePath = args[0];
outputFilePath = args[1];
topN = Integer.parseInt(args[2]);
classes = inputClasses(new ArrayList<>(), inputFilePath);
}
default -> {
// Invalid usage; print instructions and exit
System.out.println("Invalid arguments. Usage:");
System.out.println("1 argument: java Main <value-of-N>");
System.out.println("3 arguments: java Main <input-file> <output-file> <value-of-N>");
return;
}
}
// If classes were successfully loaded, proceed with analysis
if (!classes.isEmpty()) {
System.out.println("Found " + classes.size() + " Classes");
classes.stream().forEach(clazz -> exploreHierarchy(clazz)); // Analyze each class's hierarchy
// Prepare output lines with results for fields, methods, subtypes, and supertypes
List<String> outputLines = new ArrayList<>();
outputLines.add("1a: " + sortMapByValueToString(fieldsDeclared, topN));
outputLines.add("1b: " + sortMapByValueToString(fieldsAll, topN));
outputLines.add("2a: " + sortMapByValueToString(methodsDeclared, topN));
outputLines.add("2b: " + sortMapByValueToString(methodsAll, topN));
outputLines.add("3: " + sortMapByValueToString(subtypesTotal, topN));
outputLines.add("4: " + sortMapByValueToString(supertypesTotal, topN));
// Write results to the specified output file
writeFile(outputFilePath, outputLines);
}
}
/**
* Reads class names from the specified input file, loads each class, and adds it to the classes list.
* @param classes List to hold the loaded classes
* @param inputFilePath Path to the file containing class names
* @return List of loaded classes
*/
private static List<Class<?>> inputClasses(List<Class<?>> classes, String inputFilePath) {
try (BufferedReader br = new BufferedReader(new FileReader(inputFilePath))) {
String typeName;
while ((typeName = br.readLine()) != null) {
// Skip entries that are not valid class names
if (typeName.endsWith("package-info") || typeName.endsWith("module-info") || typeName.contains("META-INF")) {
System.out.println("Skipped non-class entry: " + typeName);
continue;
}
try {
// Attempt to load the class by its name
Class<?> clazz = Class.forName(typeName);
classes.add(clazz);
System.out.println("Loaded: " + clazz.getName());
} catch (ClassNotFoundException e) {
System.out.println("Type not found: " + e.getMessage());
}
}
} catch (IOException e) {
System.out.println("An error occurred while reading the file: " + e.getMessage());
}
return classes;
}
/**
* Analyzes each class by counting its declared fields, methods, subtypes, and supertypes.
* @param clazz The class to be analyzed
*/
private static void exploreHierarchy(Class<?> clazz) {
// Sets to track unique field names, method names, and supertypes for each class
Set<String> uniqueFieldNames = new HashSet<>(),
uniqueMethodNames = new HashSet<>(),
supertypes = new HashSet<>();
// Count declared fields and methods
for (Field field : clazz.getDeclaredFields()) uniqueFieldNames.add(field.getName());
for (Method method : clazz.getDeclaredMethods()) uniqueMethodNames.add(method.getName());
// Store counts for declared fields and methods
fieldsDeclared.put(clazz.getName(), uniqueFieldNames.size());
methodsDeclared.put(clazz.getName(), uniqueMethodNames.size());
// Recursively explore superclass and interfaces to count inherited fields, methods, and supertypes
exploreRecursive(clazz, uniqueFieldNames, uniqueMethodNames, supertypes);
// Store counts for all (declared + inherited) fields and methods, and supertypes
fieldsAll.put(clazz.getName(), uniqueFieldNames.size());
methodsAll.put(clazz.getName(), uniqueMethodNames.size());
supertypesTotal.put(clazz.getName(), supertypes.size());
}
/**
* Recursively explores superclass and interfaces, adding inherited fields, methods, and supertypes.
* @param clazz The current class in the hierarchy
* @param uniqueFieldNames Set of unique field names to track inherited fields
* @param uniqueMethodNames Set of unique method names to track inherited methods
* @param supertypes Set of supertypes for the current class
*/
private static void exploreRecursive(Class<?> clazz, Set<String> uniqueFieldNames,
Set<String> uniqueMethodNames, Set<String> supertypes) {
// Process the superclass, if it exists
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
// Add non-private fields and methods from the superclass
for (Field field : superclass.getDeclaredFields()) {
if (!Modifier.isPrivate(field.getModifiers())) uniqueFieldNames.add(field.getName());
}
for (Method method : superclass.getDeclaredMethods()) {
if (!Modifier.isPrivate(method.getModifiers())) uniqueMethodNames.add(method.getName());
}
subtypesTotal.put(superclass.getName(), subtypesTotal.getOrDefault(superclass.getName(), 0) + 1);
supertypes.add(superclass.getName());
// Recursive call to explore superclass hierarchy
exploreRecursive(superclass, uniqueFieldNames, uniqueMethodNames, supertypes);
}
// Process each interface implemented by the class
for (Class<?> superInterface : clazz.getInterfaces()) {
for (Field field : superInterface.getDeclaredFields()) uniqueFieldNames.add(field.getName());
for (Method method : superInterface.getDeclaredMethods()) {
if (!Modifier.isPrivate(method.getModifiers())) uniqueMethodNames.add(method.getName());
}
subtypesTotal.put(superInterface.getName(), subtypesTotal.getOrDefault(superInterface.getName(), 0) + 1);
supertypes.add(superInterface.getName());
// Recursive call to explore interface hierarchy
exploreRecursive(superInterface, uniqueFieldNames, uniqueMethodNames, supertypes);
}
}
/**
* Writes the formatted output data to the specified output file.
* @param outputFilePath Path to the output file
* @param outputLines List of strings representing the formatted output data
*/
private static void writeFile(String outputFilePath, List<String> outputLines) {
File file = new File(outputFilePath);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath))) {
for (String line : outputLines) {
writer.write(line);
writer.newLine();
}
System.out.println("Output written to " + file.getAbsolutePath());
} catch (IOException e) {
System.err.println("Error writing to output file: " + e.getMessage());
}
}
/**
* Sorts a map by value in descending order and returns a formatted string of the top N entries.
* @param entries Map with class names as keys and counts as values
* @param topN The maximum number of top entries to include
* @return Formatted string of the top N entries
*/
private static String sortMapByValueToString(Map<String, Integer> entries, int topN) {
return entries.entrySet().stream()
.sorted((e1, e2) -> {
// Primary sorting by value in descending order
int valueComparison = Integer.compare(e2.getValue(), e1.getValue());
// If values are the same, apply secondary sorting by key in alphabetical order
return valueComparison != 0 ? valueComparison : e1.getKey().compareTo(e2.getKey());
})
.limit(topN) // Limit to the top N entries
.map(entry -> entry.getKey() + " (" + entry.getValue() + " occurrences)") // Format each entry
.collect(Collectors.joining(", ")); // Join formatted entries into a single string
}
}