#932 Create class collector and use it where applicable
- Extract logic for walking through a directory and loading its classes into a separate class - Replace all implementations with the new ClassCollector
This commit is contained in:
@@ -1,23 +1,23 @@
|
||||
package tools;
|
||||
|
||||
import fr.xephi.authme.ClassCollector;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
import tools.utils.AutoToolTask;
|
||||
import tools.utils.ToolTask;
|
||||
import tools.utils.ToolsConstants;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Runner for executing tool tasks.
|
||||
*/
|
||||
public final class ToolsRunner {
|
||||
|
||||
private ToolsRunner() {
|
||||
private Map<String, ToolTask> tasks;
|
||||
|
||||
private ToolsRunner(Map<String, ToolTask> tasks) {
|
||||
this.tasks = tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -26,19 +26,23 @@ public final class ToolsRunner {
|
||||
* @param args .
|
||||
*/
|
||||
public static void main(String... args) {
|
||||
// Collect tasks and show them
|
||||
File toolsFolder = new File(ToolsConstants.TOOLS_SOURCE_ROOT);
|
||||
Map<String, ToolTask> tasks = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
collectTasksInDirectory(toolsFolder, tasks);
|
||||
// Note ljacqu 20151212: If the tools folder becomes a lot bigger, it will make sense to restrict the depth
|
||||
// of this recursive collector
|
||||
ClassCollector collector = new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, "tools");
|
||||
Map<String, ToolTask> tasks = new HashMap<>();
|
||||
for (ToolTask task : collector.getInstancesOfType(ToolTask.class)) {
|
||||
tasks.put(task.getTaskName(), task);
|
||||
}
|
||||
|
||||
ToolsRunner runner = new ToolsRunner(tasks);
|
||||
if (args == null || args.length == 0) {
|
||||
promptAndExecuteTask(tasks);
|
||||
runner.promptAndExecuteTask();
|
||||
} else {
|
||||
executeAutomaticTasks(tasks, args);
|
||||
runner.executeAutomaticTasks(args);
|
||||
}
|
||||
}
|
||||
|
||||
private static void promptAndExecuteTask(Map<String, ToolTask> tasks) {
|
||||
private void promptAndExecuteTask() {
|
||||
System.out.println("The following tasks are available:");
|
||||
for (String key : tasks.keySet()) {
|
||||
System.out.println("- " + key);
|
||||
@@ -57,7 +61,7 @@ public final class ToolsRunner {
|
||||
scanner.close();
|
||||
}
|
||||
|
||||
private static void executeAutomaticTasks(Map<String, ToolTask> tasks, String... requests) {
|
||||
private void executeAutomaticTasks(String... requests) {
|
||||
for (String taskName : requests) {
|
||||
ToolTask task = tasks.get(taskName);
|
||||
if (task == null) {
|
||||
@@ -69,80 +73,4 @@ public final class ToolsRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all implementations of {@link ToolTask} from the given folder to the provided collection.
|
||||
*
|
||||
* @param dir The directory to scan
|
||||
* @param taskCollection The collection to add results to
|
||||
*/
|
||||
// Note ljacqu 20151212: If the tools folder becomes a lot bigger, it will make sense to restrict the depth
|
||||
// of this recursive collector
|
||||
private static void collectTasksInDirectory(File dir, Map<String, ToolTask> taskCollection) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new IllegalStateException("Cannot read folder '" + dir + "'");
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
collectTasksInDirectory(file, taskCollection);
|
||||
} else if (file.isFile()) {
|
||||
ToolTask task = getTaskFromFile(file);
|
||||
if (task != null) {
|
||||
taskCollection.put(task.getTaskName(), task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ToolTask} instance defined by the given source file.
|
||||
*
|
||||
* @param file The file to load
|
||||
* @return ToolTask instance, or null if not applicable
|
||||
*/
|
||||
private static ToolTask getTaskFromFile(File file) {
|
||||
Class<? extends ToolTask> taskClass = loadTaskClassFromFile(file);
|
||||
if (taskClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Constructor<? extends ToolTask> constructor = taskClass.getConstructor();
|
||||
return constructor.newInstance();
|
||||
} catch (NoSuchMethodException | InvocationTargetException |
|
||||
IllegalAccessException | InstantiationException e) {
|
||||
throw new IllegalStateException("Cannot instantiate task '" + taskClass + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class the file defines if it implements {@link ToolTask}.
|
||||
*
|
||||
* @return The class instance, or null if not applicable
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends ToolTask> loadTaskClassFromFile(File file) {
|
||||
if (!file.getName().endsWith(".java")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String filePath = file.getPath();
|
||||
String className = "tools." + filePath
|
||||
.substring(ToolsConstants.TOOLS_SOURCE_ROOT.length(), filePath.length() - 5)
|
||||
.replace(File.separator, ".");
|
||||
try {
|
||||
Class<?> clazz = ToolsRunner.class.getClassLoader().loadClass(className);
|
||||
return ToolTask.class.isAssignableFrom(clazz) && isInstantiable(clazz)
|
||||
? (Class<? extends ToolTask>) clazz
|
||||
: null;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isInstantiable(Class<?> clazz) {
|
||||
return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package tools.checktestmocks;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Sets;
|
||||
import fr.xephi.authme.ClassCollector;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import org.mockito.Mock;
|
||||
import tools.utils.AutoToolTask;
|
||||
import tools.utils.InjectorUtils;
|
||||
import tools.utils.ToolsConstants;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -38,33 +37,13 @@ public class CheckTestMocks implements AutoToolTask {
|
||||
|
||||
@Override
|
||||
public void executeDefault() {
|
||||
readAndCheckFiles(new File(ToolsConstants.TEST_SOURCE_ROOT));
|
||||
ClassCollector collector = new ClassCollector(TestHelper.SOURCES_FOLDER, TestHelper.PROJECT_ROOT);
|
||||
for (Class<?> clazz : collector.collectClasses(c -> isTestClassWithMocks(c))) {
|
||||
checkClass(clazz);
|
||||
}
|
||||
System.out.println(StringUtils.join("\n", errors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively reads directories and checks the contained classes.
|
||||
*
|
||||
* @param dir the directory to read
|
||||
*/
|
||||
private void readAndCheckFiles(File dir) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new IllegalStateException("Cannot read folder '" + dir + "'");
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
readAndCheckFiles(file);
|
||||
} else if (file.isFile()) {
|
||||
Class<?> clazz = loadTestClass(file);
|
||||
if (clazz != null) {
|
||||
checkClass(clazz);
|
||||
}
|
||||
// else System.out.format("No @Mock fields found in class of file '%s'%n", file.getName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given test class' @Mock fields against the corresponding production class' @Inject fields.
|
||||
*
|
||||
@@ -91,20 +70,6 @@ public class CheckTestMocks implements AutoToolTask {
|
||||
errors.add(clazz.getSimpleName() + ": " + message);
|
||||
}
|
||||
|
||||
private static Class<?> loadTestClass(File file) {
|
||||
String fileName = file.getPath();
|
||||
String className = fileName
|
||||
// Strip source folders and .java ending
|
||||
.substring("src/test/java/".length(), fileName.length() - 5)
|
||||
.replace(File.separator, ".");
|
||||
try {
|
||||
Class<?> clazz = CheckTestMocks.class.getClassLoader().loadClass(className);
|
||||
return isTestClassWithMocks(clazz) ? clazz : null;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<Class<?>> getMocks(Class<?> clazz) {
|
||||
Set<Class<?>> result = new HashSet<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
@@ -147,12 +112,7 @@ public class CheckTestMocks implements AutoToolTask {
|
||||
}
|
||||
|
||||
private static String formatClassList(Collection<Class<?>> coll) {
|
||||
Collection<String> classNames = Collections2.transform(coll, new Function<Class<?>, String>() {
|
||||
@Override
|
||||
public String apply(Class<?> input) {
|
||||
return input.getSimpleName();
|
||||
}
|
||||
});
|
||||
Collection<String> classNames = Collections2.transform(coll, Class::getSimpleName);
|
||||
return StringUtils.join(", ", classNames);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import ch.jalu.injector.handlers.instantiation.Instantiation;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import fr.xephi.authme.ClassCollector;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.converter.Converter;
|
||||
import fr.xephi.authme.initialization.DataFolder;
|
||||
@@ -16,7 +18,6 @@ import tools.utils.InjectorUtils;
|
||||
import tools.utils.ToolTask;
|
||||
import tools.utils.ToolsConstants;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.nio.file.Files;
|
||||
@@ -34,15 +35,12 @@ import java.util.Scanner;
|
||||
public class DrawDependency implements ToolTask {
|
||||
|
||||
private static final String DOT_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "dependencygraph/graph.dot";
|
||||
// Package root
|
||||
private static final String ROOT_PACKAGE = "fr.xephi.authme";
|
||||
|
||||
private static final List<Class<?>> SUPER_TYPES = ImmutableList.of(ExecutableCommand.class,
|
||||
SynchronousProcess.class, AsynchronousProcess.class, EncryptionMethod.class, Converter.class, Listener.class);
|
||||
|
||||
/** Annotation types by which dependencies are identified. */
|
||||
private static final List<Class<? extends Annotation>> ANNOTATION_TYPES =
|
||||
ImmutableList.<Class<? extends Annotation>>of(DataFolder.class);
|
||||
private static final List<Class<? extends Annotation>> ANNOTATION_TYPES = ImmutableList.of(DataFolder.class);
|
||||
|
||||
private boolean mapToSupertype;
|
||||
// Map with the graph's nodes: value is one of the key's dependencies
|
||||
@@ -59,7 +57,10 @@ public class DrawDependency implements ToolTask {
|
||||
mapToSupertype = "y".equalsIgnoreCase(scanner.nextLine());
|
||||
|
||||
// Gather all connections
|
||||
readAndProcessFiles(new File(ToolsConstants.MAIN_SOURCE_ROOT));
|
||||
ClassCollector collector = new ClassCollector(TestHelper.SOURCES_FOLDER, TestHelper.PROJECT_ROOT);
|
||||
for (Class<?> clazz : collector.collectClasses()) {
|
||||
processClass(clazz);
|
||||
}
|
||||
|
||||
// Prompt user for simplification of graph
|
||||
System.out.println("Do you want to remove classes that are not used as dependency elsewhere?");
|
||||
@@ -93,28 +94,6 @@ public class DrawDependency implements ToolTask {
|
||||
System.out.format("Run 'dot -Tpng %s -o graph.png' to generate image (requires GraphViz)%n", DOT_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively reads the given directory and processes the files.
|
||||
*
|
||||
* @param dir the directory to read
|
||||
*/
|
||||
private void readAndProcessFiles(File dir) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new IllegalStateException("Cannot read folder '" + dir + "'");
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
readAndProcessFiles(file);
|
||||
} else if (file.isFile()) {
|
||||
Class<?> clazz = loadClass(file);
|
||||
if (clazz != null) {
|
||||
processClass(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processClass(Class<?> clazz) {
|
||||
List<String> dependencies = getDependencies(clazz);
|
||||
if (dependencies != null) {
|
||||
@@ -134,21 +113,6 @@ public class DrawDependency implements ToolTask {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
// Load Class object for the class in the given file
|
||||
private static Class<?> loadClass(File file) {
|
||||
final String fileName = file.getPath().replace(File.separator, ".");
|
||||
if (!fileName.endsWith(".java")) {
|
||||
return null;
|
||||
}
|
||||
final String className = fileName
|
||||
.substring(fileName.indexOf(ROOT_PACKAGE), fileName.length() - ".java".length());
|
||||
try {
|
||||
return DrawDependency.class.getClassLoader().loadClass(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getDependencies(Class<?> clazz) {
|
||||
Instantiation<?> instantiation = InjectorUtils.getInstantiationMethod(clazz);
|
||||
return instantiation == null ? null : formatInjectionDependencies(instantiation);
|
||||
|
||||
Reference in New Issue
Block a user