- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMain.java
More file actions
Latest commit
211 lines (184 loc) · 10.1 KB
/
Copy pathMain.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
importjava.io.BufferedReader;
importjava.io.BufferedWriter;
importjava.io.File;
importjava.io.FileReader;
importjava.io.FileWriter;
importjava.io.IOException;
importjava.lang.reflect.*;
importjava.util.*;
importjava.util.stream.Collectors;
publicclassMain {
// Path for the output file and the number of top entries to include
privatestaticStringoutputFilePath;
privatestaticinttopN;
privatestaticList<Class<?>> classes;
// Maps to store counts of fields, methods, subtypes, and supertypes for each class
privatestaticfinalMap<String, Integer> fieldsDeclared = newHashMap<>(),
fieldsAll = newHashMap<>(),
methodsAll = newHashMap<>(),
methodsDeclared = newHashMap<>(),
subtypesTotal = newHashMap<>(),
supertypesTotal = newHashMap<>();
publicstaticvoidmain(String[] args) {
// Handle command-line arguments to set file paths and topN limit
switch (args.length) {
case1 -> {
// 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();
}
case3 -> {
// Case with 3 arguments: Set input and output file paths and topN, then load classes
StringinputFilePath = args[0];
outputFilePath = args[1];
topN = Integer.parseInt(args[2]);
classes = inputClasses(newArrayList<>(), 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 = newArrayList<>();
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
*/
privatestaticList<Class<?>> inputClasses(List<Class<?>> classes, StringinputFilePath) {
try (BufferedReaderbr = newBufferedReader(newFileReader(inputFilePath))) {
StringtypeName;
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 (ClassNotFoundExceptione) {
System.out.println("Type not found: " + e.getMessage());
}
}
} catch (IOExceptione) {
System.out.println("An error occurred while reading the file: " + e.getMessage());
}
returnclasses;
}
/**
* Analyzes each class by counting its declared fields, methods, subtypes, and supertypes.
* @param clazz The class to be analyzed
*/
privatestaticvoidexploreHierarchy(Class<?> clazz) {
// Sets to track unique field names, method names, and supertypes for each class
Set<String> uniqueFieldNames = newHashSet<>(),
uniqueMethodNames = newHashSet<>(),
supertypes = newHashSet<>();
// Count declared fields and methods
for (Fieldfield : clazz.getDeclaredFields()) uniqueFieldNames.add(field.getName());
for (Methodmethod : 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
*/
privatestaticvoidexploreRecursive(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 (Fieldfield : superclass.getDeclaredFields()) {
if (!Modifier.isPrivate(field.getModifiers())) uniqueFieldNames.add(field.getName());
}
for (Methodmethod : 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 (Fieldfield : superInterface.getDeclaredFields()) uniqueFieldNames.add(field.getName());
for (Methodmethod : 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
*/
privatestaticvoidwriteFile(StringoutputFilePath, List<String> outputLines) {
Filefile = newFile(outputFilePath);
try (BufferedWriterwriter = newBufferedWriter(newFileWriter(outputFilePath))) {
for (Stringline : outputLines) {
writer.write(line);
writer.newLine();
}
System.out.println("Output written to " + file.getAbsolutePath());
} catch (IOExceptione) {
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
*/
privatestaticStringsortMapByValueToString(Map<String, Integer> entries, inttopN) {
returnentries.entrySet().stream()
.sorted((e1, e2) -> {
// Primary sorting by value in descending order
intvalueComparison = Integer.compare(e2.getValue(), e1.getValue());
// If values are the same, apply secondary sorting by key in alphabetical order
returnvalueComparison != 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
}
}