#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:
ljacqu
2016-09-07 22:50:12 +02:00
parent f63871600a
commit 10493a3fa3
7 changed files with 210 additions and 250 deletions
+19 -91
View File
@@ -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());
}
}