diff --git a/.gitignore b/.gitignore
index 1cadbec..b5a5fe3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
target
debug.out
/nbactions.xml
-dependency-reduced-pom.xml
\ No newline at end of file
+dependency-reduced-pom.xml
+.DS_Store
diff --git a/injectfx-example/pom.xml b/injectfx-example/pom.xml
index 6c63b8b..48f7828 100644
--- a/injectfx-example/pom.xml
+++ b/injectfx-example/pom.xml
@@ -16,7 +16,7 @@
${project.artifactId}
${project.artifactId}
${project.artifactId}
- de.kneitzel.Main
+ de.neitzel.injectfx.example.Main
${project.artifactId}-${project.version}
diff --git a/injectfx-example/src/main/java/de/kneitzel/JavaFXApp.java b/injectfx-example/src/main/java/de/neitzel/injectfx/example/JavaFXApp.java
similarity index 94%
rename from injectfx-example/src/main/java/de/kneitzel/JavaFXApp.java
rename to injectfx-example/src/main/java/de/neitzel/injectfx/example/JavaFXApp.java
index b477580..2d1ca44 100644
--- a/injectfx-example/src/main/java/de/kneitzel/JavaFXApp.java
+++ b/injectfx-example/src/main/java/de/neitzel/injectfx/example/JavaFXApp.java
@@ -1,4 +1,4 @@
-package de.kneitzel;
+package de.neitzel.injectfx.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
diff --git a/injectfx-example/src/main/java/de/kneitzel/Main.java b/injectfx-example/src/main/java/de/neitzel/injectfx/example/Main.java
similarity index 91%
rename from injectfx-example/src/main/java/de/kneitzel/Main.java
rename to injectfx-example/src/main/java/de/neitzel/injectfx/example/Main.java
index 82917a6..2343191 100644
--- a/injectfx-example/src/main/java/de/kneitzel/Main.java
+++ b/injectfx-example/src/main/java/de/neitzel/injectfx/example/Main.java
@@ -1,4 +1,4 @@
-package de.kneitzel;
+package de.neitzel.injectfx.example;
/**
* Another Main class as workaround when the JavaFX Application ist started without
diff --git a/injectfx-example/src/main/java/de/kneitzel/MainWindow.java b/injectfx-example/src/main/java/de/neitzel/injectfx/example/MainWindow.java
similarity index 94%
rename from injectfx-example/src/main/java/de/kneitzel/MainWindow.java
rename to injectfx-example/src/main/java/de/neitzel/injectfx/example/MainWindow.java
index fd731f4..27802f7 100644
--- a/injectfx-example/src/main/java/de/kneitzel/MainWindow.java
+++ b/injectfx-example/src/main/java/de/neitzel/injectfx/example/MainWindow.java
@@ -1,4 +1,4 @@
-package de.kneitzel;
+package de.neitzel.injectfx.example;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
@@ -10,7 +10,6 @@ import java.util.ResourceBundle;
public class MainWindow implements Initializable {
-
private int counter = 0;
@FXML
diff --git a/injectfx-example/src/main/resources/de/kneitzel/MainWindow.fxml b/injectfx-example/src/main/resources/de/neitzel/injectfx/example/MainWindow.fxml
similarity index 89%
rename from injectfx-example/src/main/resources/de/kneitzel/MainWindow.fxml
rename to injectfx-example/src/main/resources/de/neitzel/injectfx/example/MainWindow.fxml
index 3b6f267..5e84972 100644
--- a/injectfx-example/src/main/resources/de/kneitzel/MainWindow.fxml
+++ b/injectfx-example/src/main/resources/de/neitzel/injectfx/example/MainWindow.fxml
@@ -3,7 +3,7 @@
-
+
diff --git a/injectfx-example/src/test/resources/testlevel.png b/injectfx-example/src/test/resources/testlevel.png
new file mode 100644
index 0000000..8b0538a
Binary files /dev/null and b/injectfx-example/src/test/resources/testlevel.png differ
diff --git a/injectfx-lib/pom.xml b/injectfx-lib/pom.xml
index 59edd87..649f2b5 100644
--- a/injectfx-lib/pom.xml
+++ b/injectfx-lib/pom.xml
@@ -17,10 +17,17 @@
${project.artifactId}
${project.artifactId}
${project.artifactId}
- de.kneitzel.Main
+ de.neitzel.injectfx.example.Main
${project.artifactId}-${project.version}
+
+
+ org.reflections
+ reflections
+ ${reflections.version}
+
+
${jar.filename}
diff --git a/injectfx-lib/src/main/java/de/neitzel/injectfx/FXMLComponentInstances.java b/injectfx-lib/src/main/java/de/neitzel/injectfx/FXMLComponentInstances.java
new file mode 100644
index 0000000..789ed7f
--- /dev/null
+++ b/injectfx-lib/src/main/java/de/neitzel/injectfx/FXMLComponentInstances.java
@@ -0,0 +1,138 @@
+package de.neitzel.injectfx;
+
+import java.lang.reflect.Constructor;
+import java.util.*;
+
+/**
+ * Manages the creation and storage of singleton instances for all @FXMLComponent classes.
+ * It ensures that each component is instantiated only once and resolves dependencies recursively.
+ */
+public class FXMLComponentInstances {
+
+ /** Map holding instances of all @FXMLComponent classes, indexed by class and its unique superclasses/interfaces. */
+ private final Map, Object> instanceMap = new HashMap<>();
+
+ /** The InjectableComponents instance that provides information about instantiable components. */
+ private final InjectableComponentScanner injectableScanner;
+
+ /**
+ * Constructs an FXMLComponentInstances manager and initializes all component instances.
+ *
+ * @param injectableComponents The InjectableComponents instance containing resolved component types.
+ */
+ public FXMLComponentInstances(InjectableComponentScanner injectableScanner) {
+ this.injectableScanner = injectableScanner;
+ createAllInstances();
+ }
+
+ /**
+ * Creates instances for all registered @FXMLComponent classes.
+ */
+ private void createAllInstances() {
+ for (Class> componentClass : injectableScanner.getInstantiableComponents().keySet()) {
+ getInstance(componentClass); // Ensures each component is instantiated once
+ }
+ }
+
+ /**
+ * Retrieves an instance of a given component class, creating it if necessary.
+ *
+ * @param componentClass The class for which an instance is needed.
+ * @return An instance of the requested class.
+ */
+ public Object getInstance(Class> componentClass) {
+ if (instanceMap.containsKey(componentClass)) {
+ return instanceMap.get(componentClass);
+ }
+
+ Class> concreteClass = injectableScanner.getInstantiableComponents().get(componentClass);
+ if (concreteClass == null) {
+ throw new IllegalStateException("No concrete implementation found for: " + componentClass.getName());
+ }
+
+ Object instance = createInstance(concreteClass);
+ registerInstance(concreteClass, instance);
+
+ return instance;
+ }
+
+ /**
+ * Creates an instance of the given class by selecting the best constructor.
+ *
+ * @param concreteClass The class to instantiate.
+ * @return A new instance of the class.
+ */
+ private Object createInstance(Class> concreteClass) {
+ try {
+ Constructor> bestConstructor = findBestConstructor(concreteClass);
+ if (bestConstructor == null) {
+ throw new IllegalStateException("No suitable constructor found for: " + concreteClass.getName());
+ }
+
+ Class>[] paramTypes = bestConstructor.getParameterTypes();
+ Object[] params = Arrays.stream(paramTypes)
+ .map(this::getInstance) // Recursively resolve dependencies
+ .toArray();
+
+ return bestConstructor.newInstance(params);
+
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to instantiate " + concreteClass.getName(), e);
+ }
+ }
+
+ /**
+ * Finds the best constructor for a given class by checking which one has resolvable parameters.
+ *
+ * @param concreteClass The class to analyze.
+ * @return The best constructor or {@code null} if none can be used.
+ */
+ private Constructor> findBestConstructor(Class> concreteClass) {
+ Constructor>[] constructors = concreteClass.getConstructors();
+
+ // Prefer constructors with all parameters resolvable
+ for (Constructor> constructor : constructors) {
+ if (canUseConstructor(constructor)) {
+ return constructor;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks whether a given constructor can be used by ensuring all parameters are known.
+ *
+ * @param constructor The constructor to check.
+ * @return {@code true} if all parameters can be resolved, otherwise {@code false}.
+ */
+ private boolean canUseConstructor(Constructor> constructor) {
+ return Arrays.stream(constructor.getParameterTypes())
+ .allMatch(injectableScanner.getInstantiableComponents()::containsKey);
+ }
+
+ /**
+ * Registers an instance in the map for all unique interfaces and superclasses it implements.
+ *
+ * @param concreteClass The concrete component class.
+ * @param instance The instance to store.
+ */
+ private void registerInstance(Class> concreteClass, Object instance) {
+ instanceMap.put(concreteClass, instance);
+
+ for (Map.Entry, Class>> entry : injectableScanner.getInstantiableComponents().entrySet()) {
+ if (entry.getValue().equals(concreteClass)) {
+ instanceMap.put(entry.getKey(), instance);
+ }
+ }
+ }
+
+ /**
+ * Retrieves the instance map containing all component instances.
+ *
+ * @return A map of class types to their instances.
+ */
+ public Map, Object> getInstanceMap() {
+ return instanceMap;
+ }
+}
\ No newline at end of file
diff --git a/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectableComponentScanner.java b/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectableComponentScanner.java
new file mode 100644
index 0000000..e09e0f6
--- /dev/null
+++ b/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectableComponentScanner.java
@@ -0,0 +1,245 @@
+package de.neitzel.injectfx;
+
+import de.neitzel.injectfx.annotation.FXMLComponent;
+import org.reflections.Reflections;
+
+import java.lang.reflect.Constructor;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * InjectableComponents scans a package for classes annotated with {@link FXMLComponent}.
+ * It determines which components can be instantiated and manages type mappings for dependency injection.
+ */
+public class InjectableComponentScanner {
+
+ /** Set of all detected @FXMLComponent classes within the given package. */
+ private final Set> fxmlComponents = new HashSet<>();
+
+ /** Set of all superclasses and interfaces that are implemented by multiple @FXMLComponent classes. */
+ private final Set> notUniqueTypes = new HashSet<>();
+
+ /** Map of unique superclasses/interfaces to a single corresponding @FXMLComponent class. */
+ private final Map, Class>> uniqueTypeToComponent = new HashMap<>();
+
+ /** Map of instantiable @FXMLComponent classes and their corresponding interfaces/superclasses (if unique). */
+ private final Map, Class>> instantiableComponents = new HashMap<>();
+
+ /** List of error messages generated when resolving component instantiability. */
+ private final List errors = new ArrayList<>();
+
+ /**
+ * Constructs an InjectableComponents instance and scans the given package for @FXMLComponent classes.
+ *
+ * @param basePackage The base package to scan for @FXMLComponent classes.
+ */
+ public InjectableComponentScanner(String basePackage) {
+ scanForComponents(basePackage);
+ analyzeComponentTypes();
+ resolveInstantiableComponents();
+ }
+
+ /**
+ * Scans the specified package for classes annotated with @FXMLComponent.
+ *
+ * @param basePackage The package to scan.
+ */
+ private void scanForComponents(String basePackage) {
+ Reflections reflections = new Reflections(basePackage);
+ fxmlComponents.addAll(reflections.getTypesAnnotatedWith(FXMLComponent.class));
+ }
+
+ /**
+ * Analyzes the collected @FXMLComponent classes to determine unique and non-unique superclasses/interfaces.
+ */
+ private void analyzeComponentTypes() {
+ Map, List>> superTypesMap = new HashMap<>();
+
+ for (Class> component : fxmlComponents) {
+ Set> allSuperTypes = getAllSuperTypes(component);
+
+ for (Class> superType : allSuperTypes) {
+ superTypesMap.computeIfAbsent(superType, k -> new ArrayList<>()).add(component);
+ }
+ }
+
+ for (Map.Entry, List>> entry : superTypesMap.entrySet()) {
+ Class> superType = entry.getKey();
+ List> implementations = entry.getValue();
+
+ if (implementations.size() > 1) {
+ notUniqueTypes.add(superType);
+ } else {
+ uniqueTypeToComponent.put(superType, implementations.get(0));
+ }
+ }
+ }
+
+ /**
+ * Determines which @FXMLComponent classes can be instantiated based on known dependencies.
+ * It registers valid components and collects errors for components that cannot be instantiated.
+ */
+ private void resolveInstantiableComponents() {
+ Set> resolved = new HashSet<>();
+ Set> unresolved = new HashSet<>(fxmlComponents);
+ Map, Class>> knownTypes = new HashMap<>();
+
+ boolean progress;
+ do {
+ progress = false;
+ Iterator> iterator = unresolved.iterator();
+
+ while (iterator.hasNext()) {
+ Class> component = iterator.next();
+ if (canInstantiate(component, knownTypes.keySet())) {
+ resolved.add(component);
+ registerComponentWithSuperTypes(component, knownTypes);
+ iterator.remove();
+ progress = true;
+ }
+ }
+ } while (progress);
+
+ instantiableComponents.putAll(knownTypes);
+
+ if (!unresolved.isEmpty()) {
+ collectInstantiationErrors(unresolved);
+ }
+ }
+
+ /**
+ * Registers a component along with its unique superclasses and interfaces in the known types map.
+ *
+ * @param component The component class.
+ * @param knownTypes The map of known instantiable types.
+ */
+ private void registerComponentWithSuperTypes(Class> component, Map, Class>> knownTypes) {
+ knownTypes.put(component, component);
+
+ for (Class> superType : getAllSuperTypes(component)) {
+ if (!notUniqueTypes.contains(superType)) {
+ knownTypes.put(superType, component);
+ }
+ }
+ }
+
+ /**
+ * Checks whether a given @FXMLComponent class can be instantiated based on known dependencies.
+ *
+ * @param component The component class to check.
+ * @param knownTypes Set of known instantiable types.
+ * @return {@code true} if the component can be instantiated, otherwise {@code false}.
+ */
+ private boolean canInstantiate(Class> component, Set> knownTypes) {
+ for (Constructor> constructor : component.getConstructors()) {
+ Class>[] paramTypes = constructor.getParameterTypes();
+ if (paramTypes.length == 0 || Arrays.stream(paramTypes).allMatch(knownTypes::contains)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Collects error messages for components that cannot be instantiated and adds them to the error list.
+ *
+ * @param unresolved Set of components that could not be instantiated.
+ */
+ private void collectInstantiationErrors(Set> unresolved) {
+ for (Class> component : unresolved) {
+ StringBuilder errorMsg = new StringBuilder("Component cannot be instantiated: " + component.getName());
+
+ boolean possibleWithUniqueTypes = false;
+
+ for (Constructor> constructor : component.getConstructors()) {
+ Class>[] paramTypes = constructor.getParameterTypes();
+ List> problematicTypes = Arrays.stream(paramTypes)
+ .filter(t -> !instantiableComponents.containsKey(t) && !notUniqueTypes.contains(t))
+ .collect(Collectors.toList());
+
+ if (problematicTypes.isEmpty()) {
+ possibleWithUniqueTypes = true;
+ } else {
+ errorMsg.append("\n ➤ Requires unknown types: ").append(problematicTypes);
+ }
+ }
+
+ if (possibleWithUniqueTypes) {
+ errorMsg.append("\n ➤ Could be instantiated if multiple implementations of ")
+ .append("interfaces/superclasses were resolved uniquely: ")
+ .append(getConflictingTypes(component));
+ }
+
+ errors.add(errorMsg.toString());
+ }
+ }
+
+ /**
+ * Returns a comma-separated list of conflicting types that prevent instantiation.
+ *
+ * @param component The component class.
+ * @return A string listing the conflicting types.
+ */
+ private String getConflictingTypes(Class> component) {
+ return Arrays.stream(component.getConstructors())
+ .flatMap(constructor -> Arrays.stream(constructor.getParameterTypes()))
+ .filter(notUniqueTypes::contains)
+ .map(Class::getName)
+ .collect(Collectors.joining(", "));
+ }
+
+ /**
+ * Retrieves all superclasses and interfaces of a given class.
+ *
+ * @param clazz The class to analyze.
+ * @return A set of all superclasses and interfaces of the given class.
+ */
+ private Set> getAllSuperTypes(Class> clazz) {
+ Set> result = new HashSet<>();
+ Class> superClass = clazz.getSuperclass();
+
+ while (superClass != null && superClass != Object.class) {
+ result.add(superClass);
+ superClass = superClass.getSuperclass();
+ }
+
+ result.addAll(Arrays.asList(clazz.getInterfaces()));
+ return result;
+ }
+
+ /**
+ * Returns a map of instantiable @FXMLComponent classes and their associated interfaces/superclasses.
+ *
+ * @return A map of instantiable components.
+ */
+ public Map, Class>> getInstantiableComponents() {
+ return instantiableComponents;
+ }
+
+ /**
+ * Returns the set of non-unique types (superclasses/interfaces with multiple implementations).
+ *
+ * @return A set of non-unique types.
+ */
+ public Set> getNotUniqueTypes() {
+ return notUniqueTypes;
+ }
+
+ /**
+ * Returns the map of unique types to corresponding @FXMLComponent implementations.
+ *
+ * @return A map of unique types.
+ */
+ public Map, Class>> getUniqueTypeToComponent() {
+ return uniqueTypeToComponent;
+ }
+
+ /**
+ * Returns a list of errors encountered during instantiation resolution.
+ *
+ * @return A list of error messages.
+ */
+ public List getErrors() {
+ return errors;
+ }
+}
\ No newline at end of file
diff --git a/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectingControllerFactory.java b/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectingControllerFactory.java
index 57fa2be..2467345 100644
--- a/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectingControllerFactory.java
+++ b/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectingControllerFactory.java
@@ -3,15 +3,16 @@ package de.neitzel.injectfx;
import javafx.util.Callback;
import java.lang.reflect.Constructor;
+import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
public class InjectingControllerFactory implements Callback, Object> {
- private final Map, Object> parameterMap;
+ private final Map, Object> parameterMap = new HashMap<>();
- public InjectingControllerFactory(Map, Object> parameterMap) {
- this.parameterMap = parameterMap;
+ public void addInjectingData(Class> clazz, Object object) {
+ parameterMap.put(clazz, object);
}
@Override
diff --git a/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectingFXMLLoader.java b/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectingFXMLLoader.java
new file mode 100644
index 0000000..90e4c79
--- /dev/null
+++ b/injectfx-lib/src/main/java/de/neitzel/injectfx/InjectingFXMLLoader.java
@@ -0,0 +1,44 @@
+package de.neitzel.injectfx;
+
+import javafx.fxml.FXMLLoader;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.net.URL;
+
+@Slf4j
+public class InjectingFXMLLoader {
+
+ private final InjectingControllerFactory controllerFactory;
+
+ public InjectingFXMLLoader() {
+ controllerFactory = new InjectingControllerFactory();
+ }
+
+ public InjectingFXMLLoader(InjectingControllerFactory controllerFactory) {
+ this.controllerFactory = controllerFactory;
+ }
+
+ public InjectingFXMLLoader(String packageName) {
+ controllerFactory = new InjectingControllerFactory();
+ InjectableComponentScanner scanner = new InjectableComponentScanner(packageName);
+ FXMLComponentInstances instances = new FXMLComponentInstances(scanner);
+ addInjectingData(instances);
+ }
+
+ private void addInjectingData(FXMLComponentInstances instances) {
+ for (var clazz: instances.getInstanceMap().keySet()) {
+ addInjectingData(clazz, instances.getInstance(clazz));
+ }
+ }
+
+ public void addInjectingData(Class> clazz, Object object) {
+ controllerFactory.addInjectingData(clazz, object);
+ }
+
+ public T load(URL url) throws IOException {
+ FXMLLoader loader = new FXMLLoader(url);
+ loader.setControllerFactory(controllerFactory);
+ return loader.load();
+ }
+}
diff --git a/injectfx-lib/src/main/java/de/neitzel/injectfx/annotation/FXMLComponent.java b/injectfx-lib/src/main/java/de/neitzel/injectfx/annotation/FXMLComponent.java
new file mode 100644
index 0000000..0d585e6
--- /dev/null
+++ b/injectfx-lib/src/main/java/de/neitzel/injectfx/annotation/FXMLComponent.java
@@ -0,0 +1,11 @@
+package de.neitzel.injectfx.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface FXMLComponent {
+}
diff --git a/injectfx-lib/src/main/java/de/neitzel/injectfx/annotation/FXMLConfig.java b/injectfx-lib/src/main/java/de/neitzel/injectfx/annotation/FXMLConfig.java
new file mode 100644
index 0000000..1294bd7
--- /dev/null
+++ b/injectfx-lib/src/main/java/de/neitzel/injectfx/annotation/FXMLConfig.java
@@ -0,0 +1,12 @@
+package de.neitzel.injectfx.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface FXMLConfig {
+ String value() default "";
+}
diff --git a/injectfx-lib/src/test/java/de/neitzel/injectfx/InjectableComponentScannerTest.java b/injectfx-lib/src/test/java/de/neitzel/injectfx/InjectableComponentScannerTest.java
new file mode 100644
index 0000000..4a39e69
--- /dev/null
+++ b/injectfx-lib/src/test/java/de/neitzel/injectfx/InjectableComponentScannerTest.java
@@ -0,0 +1,50 @@
+package de.neitzel.injectfx;
+
+import de.neitzel.injectfx.testcomponents.test1ok.SuperClass;
+import de.neitzel.injectfx.testcomponents.test1ok.TestComponent1_1;
+import de.neitzel.injectfx.testcomponents.test1ok.TestInterface1_1;
+import de.neitzel.injectfx.testcomponents.test1ok.TestInterface1_2;
+import de.neitzel.injectfx.testcomponents.test1ok.sub.TestComponent1_2;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class InjectableComponentScannerTest {
+
+ /**
+ * Tests loading of multiple FXMLComponents including sub packages.
+ */
+ @Test
+ void testLoadComponents() {
+ InjectableComponentScanner scanner = new InjectableComponentScanner("de.neitzel.injectfx.testcomponents.test1ok");
+ var instantiableComponents = scanner.getInstantiableComponents();
+ var nonUniqueTypes = scanner.getNotUniqueTypes();
+
+ assertAll(
+ () -> assertNotNull(instantiableComponents),
+ () -> assertEquals(3, instantiableComponents.size()),
+ () -> assertTrue(instantiableComponents.containsKey(TestComponent1_1.class)),
+ () -> assertTrue(instantiableComponents.containsKey(TestComponent1_2.class)),
+ () -> assertTrue(instantiableComponents.containsKey(TestInterface1_1.class)),
+ () -> assertTrue(nonUniqueTypes.contains(SuperClass.class)),
+ () -> assertTrue(nonUniqueTypes.contains(TestInterface1_2.class)),
+ () -> assertTrue(scanner.getErrors().isEmpty())
+ );
+ }
+
+ /**
+ * Tests failing to load a FXMLComponent which has an unknwown parameter.
+ */
+ @Test
+ void testComponentsFailWithUnknownParameters() {
+ InjectableComponentScanner scanner = new InjectableComponentScanner("de.neitzel.injectfx.testcomponents.test2fail");
+ var instantiableComponents = scanner.getInstantiableComponents();
+ var nonUniqueTypes = scanner.getNotUniqueTypes();
+
+ assertAll(
+ () -> assertNotNull(instantiableComponents),
+ () -> assertEquals(0, instantiableComponents.size()),
+ () -> assertFalse(scanner.getErrors().isEmpty())
+ );
+ }
+}
\ No newline at end of file
diff --git a/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/SuperClass.java b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/SuperClass.java
new file mode 100644
index 0000000..e38029a
--- /dev/null
+++ b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/SuperClass.java
@@ -0,0 +1,4 @@
+package de.neitzel.injectfx.testcomponents.test1ok;
+
+public class SuperClass {
+}
diff --git a/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestComponent1_1.java b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestComponent1_1.java
new file mode 100644
index 0000000..44dc8bf
--- /dev/null
+++ b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestComponent1_1.java
@@ -0,0 +1,9 @@
+package de.neitzel.injectfx.testcomponents.test1ok;
+
+import de.neitzel.injectfx.annotation.FXMLComponent;
+
+@FXMLComponent
+public class TestComponent1_1 extends SuperClass implements TestInterface1_2 {
+ public TestComponent1_1() {
+ }
+}
diff --git a/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestInterface1_1.java b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestInterface1_1.java
new file mode 100644
index 0000000..3d86e74
--- /dev/null
+++ b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestInterface1_1.java
@@ -0,0 +1,4 @@
+package de.neitzel.injectfx.testcomponents.test1ok;
+
+public interface TestInterface1_1 {
+}
diff --git a/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestInterface1_2.java b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestInterface1_2.java
new file mode 100644
index 0000000..b923839
--- /dev/null
+++ b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/TestInterface1_2.java
@@ -0,0 +1,4 @@
+package de.neitzel.injectfx.testcomponents.test1ok;
+
+public interface TestInterface1_2 {
+}
diff --git a/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/sub/TestComponent1_2.java b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/sub/TestComponent1_2.java
new file mode 100644
index 0000000..cd6a859
--- /dev/null
+++ b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test1ok/sub/TestComponent1_2.java
@@ -0,0 +1,12 @@
+package de.neitzel.injectfx.testcomponents.test1ok.sub;
+
+import de.neitzel.injectfx.annotation.FXMLComponent;
+import de.neitzel.injectfx.testcomponents.test1ok.SuperClass;
+import de.neitzel.injectfx.testcomponents.test1ok.TestInterface1_1;
+import de.neitzel.injectfx.testcomponents.test1ok.TestInterface1_2;
+
+@FXMLComponent
+public class TestComponent1_2 extends SuperClass implements TestInterface1_1, TestInterface1_2 {
+ public TestComponent1_2() {
+ }
+}
diff --git a/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test2fail/TestComponent2_1.java b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test2fail/TestComponent2_1.java
new file mode 100644
index 0000000..78f0e3e
--- /dev/null
+++ b/injectfx-lib/src/test/java/de/neitzel/injectfx/testcomponents/test2fail/TestComponent2_1.java
@@ -0,0 +1,12 @@
+package de.neitzel.injectfx.testcomponents.test2fail;
+
+import de.neitzel.injectfx.annotation.FXMLComponent;
+
+/**
+ * TestComponent1 that should fail.
+ */
+@FXMLComponent
+public class TestComponent2_1 {
+ public TestComponent2_1(String test) {
+ }
+}
diff --git a/pom.xml b/pom.xml
index 6d63ad7..07ed0e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,6 +25,8 @@
5.10.2
1.18.32
5.12.0
+ 0.10.2
+
2.16.2