diff --git a/README.md b/README.md
index 120e8a5..a8e9b12 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,275 @@
-# FXML Compiler
\ No newline at end of file
+# FXML Compiler
+
+## Introduction
+
+This projects aims at generating Java code from FXML files.
+
+## Requirements
+
+- Java 21 (at least for the plugin, the generated code can be compatible with older Java versions)
+- Maven 3.8.0
+
+## Installation
+
+Add the plugin to your project:
+
+```xml
+
+
+
+
+ com.github.gtache
+ fxml-compiler-maven-plugin
+ 1.0.0
+
+
+
+ compile
+
+
+
+
+
+
+```
+
+Optionally add dependencies to the plugin (e.g. when using MediaView and controlsfx):
+
+```xml
+
+
+
+
+ com.github.gtache
+ fxml-compiler-maven-plugin
+ 1.0.0
+
+
+
+ compile
+
+
+
+
+
+ org.openjfx
+ javafx-media
+ ${javafx.version}
+
+
+ org.controlsfx
+ controlsfx
+ ${controlsfx.version}
+
+
+
+
+
+```
+
+## Advantages
+
+- Compile-time validation
+- Faster startup speed for the application
+- Possibility to use controller factories to instantiate controllers with final fields
+- Easier time with JPMS
+ - No need to open the controllers packages to javafx.fxml
+ - No need to open the resources packages when using use-image-inputstream-constructor (if images or resource bundles
+ are in the project resources)
+
+## Disadvantages
+
+- Possible bugs (file an issue if you see one)
+- Expression binding is limited
+- Probably not fully compatible with all FXML features (file an issue if you need one in specific)
+
+## Parameters
+
+### Field injection
+
+There are four ways to inject fields into a controller:
+
+- `REFLECTION`: Inject fields using reflection (like FXMLLoader)
+-
+ - ```java
+ try {
+ final var field = controller.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(controller, object);
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException("Error using reflection on " + fieldName, e);
+ }
+ ```
+-
+ - Slowest method
+-
+ - Fully compatible with FXMLLoader, so this allows easy switching between the two.
+-
+ - This is the default injection method (for compatibility reasons).
+- `ASSIGN`: variable assignment
+-
+ - `controller.field = value`
+-
+ - This means that the field must be accessible from the view (e.g. package-private).
+- `SETTERS`: controller setters methods
+-
+ - `controller.setField(value)`
+- `FACTORY`: controller factory
+-
+ - `controller = factory.create(fieldMap)`
+-
+ - `factory` is a `ControllerFactory` instance that is created at runtime and passed to the view.
+-
+ - `fieldMap` is a map of field name (String) to value (Object) that is computed during the view `load` method.
+-
+ - This allows the controller to have final fields.
+
+### Method injections
+
+There are two ways to inject methods (meaning use them as event handlers) into a controller:
+
+- `REFLECTION`: Inject methods using reflection (like FXMLLoader)
+-
+ - ```java
+ try {
+ final java.lang.reflect.Method method;
+ final var methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
+ .filter(m -> m.getName().equals(methodName)).toList();
+ if (methods.size() > 1) {
+ final var eventMethods = methods.stream().filter(m ->
+ m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0])).toList();
+ if (eventMethods.size() == 1) {
+ method = eventMethods.getFirst();
+ } else {
+ final var emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList();
+ if (emptyMethods.size() == 1) {
+ method = emptyMethods.getFirst();
+ } else {
+ throw new IllegalArgumentException("Multiple matching methods for " + methodName);
+ }
+ }
+ } else if (methods.size() == 1) {
+ method = methods.getFirst();
+ } else {
+ throw new IllegalArgumentException("No matching method for " + methodName);
+ }
+ method.setAccessible(true);
+ if (method.getParameterCount() == 0) {
+ method.invoke(controller);
+ } else {
+ method.invoke(controller, event);
+ }
+ } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
+ throw new RuntimeException("Error using reflection on " + methodName, ex);
+ }
+ ```
+-
+ - Slowest method
+-
+ - Fully compatible with FXMLLoader, so this allows easy switching between the two.
+-
+ - This is the default injection method (for compatibility reasons).
+- `REFERENCE`: Directly reference the method
+-
+ - `controller.method(event)`
+-
+ - This means that the method must be accessible from the view (e.g. package-private).
+
+### Resource bundle injection
+
+There are three ways to inject resource bundles into a controller:
+
+- `CONSTRUCTOR`: Inject resource bundle in the view constructor
+-
+ - ```java
+ view = new View(controller, resourceBundle);
+ ```
+-
+ - This is the default injection method because it is the most similar to FXMLLoader (
+ `FXMLLoader.setResources(resourceBundle)`).
+- `CONSTRUCTOR_FUNCTION`: Injects a function in the view constructor
+-
+ - `bundleFunction.apply(key)`
+-
+ - The function takes a string (the key) and returns a string (the value)
+ - This allows using another object than a resource bundle for example
+- `GETTER`: Retrieves the resource bundle using a controller getter method
+-
+ - `controller.resources()`
+-
+ - The method name (resources) was chosen because it matches the name of the field injected by FXMLLoader.
+-
+ - The method must be accessible from the view (e.g. package-private).
+- `GET-BUNDLE`: Injects the bundle name in the view constructor and retrieves it using
+ `ResourceBundle.getBundle(bundleName)`
+-
+ - `ResourceBundle.getBundle(bundleName)`
+ - Also used when fx:include specifies a resource attribute to pass it to the included view.
+
+## View creation
+
+The views are generated in the same packages as the FXML files.
+The name of the class is generated from the name of the FXML file.
+
+The constructor of the view is generated depending on the parameters of the plugin.
+The constructor will have as many arguments as the number of controllers in the FXML tree (recursive fx:include) +
+potentially the resource bundle if necessary. If no resource reference (`%key.to.resource`) is found in the FXML tree or
+if all the includes using references specify a resources attribute, the argument is not created.
+
+The type of the constructor arguments will either be the controller instance or the controller factory (a function of
+fields map -> controller).
+The resource bundle argument will either be the resource bundle instance, the resource bundle name or a function of
+string ->
+string.
+
+The smallest constructor will have only one argument: The controller (or controller factory).
+
+## Maven Plugin
+
+### Parameters
+
+- output-directory
+-
+ - The output directory of the generated classes
+-
+ - default: `${project.build.directory}/generated-sources/java`)
+- target-version
+-
+ - The target Java version for the generated code
+ - default: `21`
+ - minimum: `8`
+ - File an issue if the generated code is not compatible with the target version
+- use-image-inputstream-constructor
+-
+ - Use the InputStream constructor for Image instead of the String (URL) one.
+-
+ - default: `true`
+ - Disables background loading
+- field-injection
+-
+ - The type of field injections to use (see [Field injection](#field-injection))
+ - default: `REFLECTION`
+- method-injection
+-
+ - The type of method injections to use (see [Method injection](#method-injection))
+ - default: `REFLECTION`
+- bundle-injection
+-
+ - The type of resource bundle injection to use (see [Resource bundle injection](#resource-bundle-injection))
+ - default: `CONSTRUCTOR`
+- bundle-map
+-
+ - A map of resource bundle name to resource bundle path
+ - Used with `GET-BUNDLE` injection
+ - default: `{}`
+
+### Limitations
+
+- Given that the plugin operates during the `generate-sources` phase, it doesn't have access to the classes of the
+ application.
+-
+ - The controller info (fields, methods) is obtained from the source file and may therefore be inaccurate.
+-
+ - Custom classes instantiated in the FXML files are not available during generation and may therefore cause it to
+ fail.
+- If the application uses e.g. WebView, the javafx-web dependency must be added to the plugin dependencies.
\ No newline at end of file
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFactory.java b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFactory.java
deleted file mode 100644
index d49d0ed..0000000
--- a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFactory.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.github.gtache.fxml.compiler;
-
-import java.util.Map;
-
-/**
- * Factory for creating controllers
- *
- * @param The type of the controller
- */
-@FunctionalInterface
-public interface ControllerFactory {
-
- /**
- * Creates a controller
- *
- * @param fieldMap The assignment of field name to value
- * @return The created controller
- */
- T create(final Map fieldMap);
-}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInfo.java b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInfo.java
index 0596bee..1a324be 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInfo.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInfo.java
@@ -28,5 +28,5 @@ public interface ControllerFieldInfo {
*
* @return The generic types as a list, empty if not generic or raw
*/
- List genericTypes();
+ List genericTypes();
}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInfo.java b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInfo.java
index 442073b..6841f81 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInfo.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInfo.java
@@ -7,10 +7,17 @@ import java.util.Map;
*/
public interface ControllerInfo {
+ /**
+ * Returns the controller class name
+ *
+ * @return The name
+ */
+ String className();
+
/**
* Returns a mapping of event handler method name -> boolean
*
- * @return A mapping of method name to true if the method has an argument
+ * @return A mapping of method name to true if the method has an argument (event handler may not have an argument)
*/
Map handlerHasArgument();
@@ -18,7 +25,7 @@ public interface ControllerInfo {
* Returns whether the given event handler method has an argument
*
* @param methodName The method name
- * @return A mapping of method name to true if the method has an event
+ * @return True if the method has an argument
*/
default boolean handlerHasArgument(final String methodName) {
return handlerHasArgument().getOrDefault(methodName, true);
@@ -40,4 +47,11 @@ public interface ControllerInfo {
default ControllerFieldInfo fieldInfo(final String property) {
return fieldInfo().get(property);
}
+
+ /**
+ * Returns whether the controller has an initialize method
+ *
+ * @return True if the controller has an initialize method
+ */
+ boolean hasInitialize();
}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInjection.java b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInjection.java
deleted file mode 100644
index 211fc17..0000000
--- a/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInjection.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.github.gtache.fxml.compiler;
-
-/**
- * Represents a controller injection to use for generated code
- */
-public interface ControllerInjection {
-
- /**
- * Returns the injection type of class fields
- *
- * @return The injection type for fields
- */
- InjectionType fieldInjectionType();
-
- /**
- * Returns the injection type for event handlers methods
- *
- * @return The injection type for event handlers
- */
- InjectionType methodInjectionType();
-
- /**
- * The name of the controller class
- *
- * @return The class
- */
- String injectionClass();
-}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationException.java b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationException.java
index 1250b95..07c4597 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationException.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationException.java
@@ -6,7 +6,7 @@ package com.github.gtache.fxml.compiler;
public class GenerationException extends Exception {
/**
- * Instantiates a new GenerationException
+ * Instantiates a new exception
*
* @param message The message
*/
@@ -15,7 +15,7 @@ public class GenerationException extends Exception {
}
/**
- * Instantiates a new GenerationException
+ * Instantiates a new exception
*
* @param message The message
* @param cause The cause
@@ -25,7 +25,7 @@ public class GenerationException extends Exception {
}
/**
- * Instantiates a new GenerationException
+ * Instantiates a new exception
*
* @param cause The cause
*/
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java
index 85ae115..b874302 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java
@@ -1,5 +1,7 @@
package com.github.gtache.fxml.compiler;
+import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
+
import java.util.Map;
/**
@@ -8,31 +10,52 @@ import java.util.Map;
public interface GenerationParameters {
/**
- * Returns the mapping of controller class name to controller injection
+ * Returns the compatibility information
*
- * @return The mapping
+ * @return The compatibility
*/
- Map controllerInjections();
+ GenerationCompatibility compatibility();
/**
- * Returns the mapping of fx:include source to generated class name
+ * Returns whether to use Image InputStream constructor instead of the String (url) one.
+ * This allows avoiding opening some packages with JPMS
*
- * @return The mapping
+ * @return True if the constructor should be used
*/
- Map sourceToGeneratedClassName();
-
+ boolean useImageInputStreamConstructor();
/**
- * Returns the mapping of fx:include source to controller class name
+ * Returns the mapping of controller class to resource bundle path (in case of GET-BUNDLE injection)
*
- * @return The mapping
+ * @return The map
*/
- Map sourceToControllerName();
+ Map bundleMap();
/**
- * Returns the resource bundle injection to use
+ * Returns the controller injection to use
*
* @return The injection
*/
- ResourceBundleInjection resourceBundleInjection();
+ InjectionType controllerInjectionType();
+
+ /**
+ * Returns the field injection to use
+ *
+ * @return The injection
+ */
+ InjectionType fieldInjectionType();
+
+ /**
+ * Returns the method injection to use
+ *
+ * @return The injection
+ */
+ InjectionType methodInjectionType();
+
+ /**
+ * Returns the resource injection to use
+ *
+ * @return The injection
+ */
+ InjectionType resourceInjectionType();
}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationRequest.java b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationRequest.java
index 7d982a1..46967d8 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationRequest.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationRequest.java
@@ -21,6 +21,13 @@ public interface GenerationRequest {
*/
GenerationParameters parameters();
+ /**
+ * Returns the info about the main source file
+ *
+ * @return The info
+ */
+ SourceInfo sourceInfo();
+
/**
* Returns the object to generate code for
*
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/GenericTypes.java b/api/src/main/java/com/github/gtache/fxml/compiler/GenericTypes.java
new file mode 100644
index 0000000..447fc4b
--- /dev/null
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/GenericTypes.java
@@ -0,0 +1,23 @@
+package com.github.gtache.fxml.compiler;
+
+import java.util.List;
+
+/**
+ * Represents generic types for a field
+ */
+public interface GenericTypes {
+
+ /**
+ * Returns the name of the type
+ *
+ * @return The name
+ */
+ String name();
+
+ /**
+ * Returns the possible subtypes of the type
+ *
+ * @return The list of subtypes, empty if no subtypes
+ */
+ List subTypes();
+}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/ResourceBundleInjection.java b/api/src/main/java/com/github/gtache/fxml/compiler/ResourceBundleInjection.java
deleted file mode 100644
index b0a00fe..0000000
--- a/api/src/main/java/com/github/gtache/fxml/compiler/ResourceBundleInjection.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.github.gtache.fxml.compiler;
-
-/**
- * Represents a controller injection to use for generated code
- */
-public interface ResourceBundleInjection {
-
- /**
- * Returns the injection type for the resource bundle
- *
- * @return The injection type
- */
- InjectionType injectionType();
-
- /**
- * Returns the resource bundle name
- *
- * @return The path
- */
- String bundleName();
-}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/SourceInfo.java b/api/src/main/java/com/github/gtache/fxml/compiler/SourceInfo.java
new file mode 100644
index 0000000..741042f
--- /dev/null
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/SourceInfo.java
@@ -0,0 +1,54 @@
+package com.github.gtache.fxml.compiler;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Info about a source file
+ */
+public interface SourceInfo {
+
+ /**
+ * Returns the generated view class name
+ *
+ * @return The class name
+ */
+ String generatedClassName();
+
+ /**
+ * Returns the controller class name
+ *
+ * @return The class name
+ */
+ String controllerClassName();
+
+ /**
+ * Returns the source file
+ *
+ * @return The file
+ */
+ Path sourceFile();
+
+ /**
+ * Returns the included sources.
+ * Note that there can be multiple times the same source.
+ *
+ * @return The sources
+ */
+ List includedSources();
+
+ /**
+ * Returns the mapping of source value to source info
+ *
+ * @return The mapping
+ */
+ Map sourceToSourceInfo();
+
+ /**
+ * Returns whether the source or its children requires a resource bundle
+ *
+ * @return True if the subtree requires a resource bundle
+ */
+ boolean requiresResourceBundle();
+}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/compatibility/GenerationCompatibility.java b/api/src/main/java/com/github/gtache/fxml/compiler/compatibility/GenerationCompatibility.java
new file mode 100644
index 0000000..a31190c
--- /dev/null
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/compatibility/GenerationCompatibility.java
@@ -0,0 +1,57 @@
+package com.github.gtache.fxml.compiler.compatibility;
+
+/**
+ * Compatibility information for generated code
+ */
+@FunctionalInterface
+public interface GenerationCompatibility {
+
+ /**
+ * Returns the minimum supported Java version
+ *
+ * @return The version
+ */
+ int javaVersion();
+
+ /**
+ * Returns whether to use var for object declaration
+ *
+ * @return True if var should be used
+ */
+ default boolean useVar() {
+ return javaVersion() >= 10;
+ }
+
+ /**
+ * Returns the type of list collector to use
+ *
+ * @return The collector
+ */
+ default ListCollector listCollector() {
+ if (javaVersion() >= 16) {
+ return ListCollector.TO_LIST;
+ } else if (javaVersion() >= 10) {
+ return ListCollector.COLLECT_TO_UNMODIFIABLE_LIST;
+ } else {
+ return ListCollector.COLLECT_TO_LIST;
+ }
+ }
+
+ /**
+ * Returns whether to use List.of() (or Set.of() etc.) instead of Arrays.asList()
+ *
+ * @return True if List.of() should be used
+ */
+ default boolean useCollectionsOf() {
+ return javaVersion() >= 9;
+ }
+
+ /**
+ * Returns whether to use getFirst() or get(0)
+ *
+ * @return True if getFirst() should be used
+ */
+ default boolean useGetFirst() {
+ return javaVersion() >= 21;
+ }
+}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/compatibility/ListCollector.java b/api/src/main/java/com/github/gtache/fxml/compiler/compatibility/ListCollector.java
new file mode 100644
index 0000000..0ca25cd
--- /dev/null
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/compatibility/ListCollector.java
@@ -0,0 +1,22 @@
+package com.github.gtache.fxml.compiler.compatibility;
+
+/**
+ * Type of list collector to use for generated code
+ */
+public enum ListCollector {
+
+ /**
+ * Use .toList()
+ */
+ TO_LIST,
+
+ /**
+ * Use .collect(Collectors.toUnmodifiableList())
+ */
+ COLLECT_TO_UNMODIFIABLE_LIST,
+
+ /**
+ * Use .collect(Collectors.toList())
+ */
+ COLLECT_TO_LIST
+}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParseException.java b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParseException.java
index 6b059b1..136935d 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParseException.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParseException.java
@@ -6,7 +6,7 @@ package com.github.gtache.fxml.compiler.parsing;
public class ParseException extends Exception {
/**
- * Instantiates a new ParseException
+ * Instantiates a new exception
*
* @param message The message
*/
@@ -15,7 +15,7 @@ public class ParseException extends Exception {
}
/**
- * Instantiates a new ParseException
+ * Instantiates a new exception
*
* @param message The message
* @param cause The cause
@@ -25,7 +25,7 @@ public class ParseException extends Exception {
}
/**
- * Instantiates a new ParseException
+ * Instantiates a new exception
*
* @param cause The cause
*/
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedDefine.java b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedDefine.java
index 6d2005c..960798a 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedDefine.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedDefine.java
@@ -1,5 +1,6 @@
package com.github.gtache.fxml.compiler.parsing;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedCollection;
import java.util.SequencedMap;
@@ -10,30 +11,18 @@ import java.util.SequencedMap;
@FunctionalInterface
public interface ParsedDefine extends ParsedObject {
- /**
- * Returns the object defined by this fx:define
- *
- * @return The object
- */
- ParsedObject object();
-
@Override
default String className() {
- return object().className();
+ return ParsedDefine.class.getName();
}
@Override
default Map attributes() {
- return object().attributes();
+ return Map.of();
}
@Override
default SequencedMap> properties() {
- return object().properties();
- }
-
- @Override
- default SequencedCollection children() {
- return object().children();
+ return new LinkedHashMap<>();
}
}
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedFactory.java b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedFactory.java
index e753dc1..5b875d3 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedFactory.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedFactory.java
@@ -29,7 +29,8 @@ public interface ParsedFactory extends ParsedObject {
}
/**
- * Returns the arguments for the factory
+ * Returns the arguments for the factory.
+ * Different from {@link ParsedObject#children()} (in practice, children should only contain fx:define)
*
* @return The arguments
*/
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedObject.java b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedObject.java
index 78ef8a7..12c257b 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedObject.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedObject.java
@@ -10,7 +10,7 @@ import java.util.SequencedMap;
public interface ParsedObject {
/**
- * The type of the object
+ * Returns the type of the object
*
* @return The class name
*/
diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedText.java b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedText.java
index dba2ff4..c895bb9 100644
--- a/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedText.java
+++ b/api/src/main/java/com/github/gtache/fxml/compiler/parsing/ParsedText.java
@@ -21,7 +21,7 @@ public interface ParsedText extends ParsedObject {
@Override
default String className() {
- return "java.lang.String";
+ return String.class.getName();
}
@Override
diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java
index 7560e1f..3802d1b 100644
--- a/api/src/main/java/module-info.java
+++ b/api/src/main/java/module-info.java
@@ -3,5 +3,6 @@
*/
module com.github.gtache.fxml.compiler.api {
exports com.github.gtache.fxml.compiler;
+ exports com.github.gtache.fxml.compiler.compatibility;
exports com.github.gtache.fxml.compiler.parsing;
}
\ No newline at end of file
diff --git a/api/src/test/java/com/github/gtache/fxml/compiler/TestControllerFieldInfo.java b/api/src/test/java/com/github/gtache/fxml/compiler/TestControllerFieldInfo.java
index e9e0728..6097b51 100644
--- a/api/src/test/java/com/github/gtache/fxml/compiler/TestControllerFieldInfo.java
+++ b/api/src/test/java/com/github/gtache/fxml/compiler/TestControllerFieldInfo.java
@@ -6,8 +6,7 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
class TestControllerFieldInfo {
@@ -25,7 +24,7 @@ class TestControllerFieldInfo {
@Test
void testIsGenericTrue() {
- when(info.genericTypes()).thenReturn(List.of("A", "B", "C"));
+ when(info.genericTypes()).thenReturn(List.of(mock(GenericTypes.class)));
assertTrue(info.isGeneric());
}
}
diff --git a/api/src/test/java/com/github/gtache/fxml/compiler/compatibility/TestGenerationCompatibility.java b/api/src/test/java/com/github/gtache/fxml/compiler/compatibility/TestGenerationCompatibility.java
new file mode 100644
index 0000000..ea6c0d1
--- /dev/null
+++ b/api/src/test/java/com/github/gtache/fxml/compiler/compatibility/TestGenerationCompatibility.java
@@ -0,0 +1,72 @@
+package com.github.gtache.fxml.compiler.compatibility;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+class TestGenerationCompatibility {
+
+ private final GenerationCompatibility compatibility;
+
+ TestGenerationCompatibility() {
+ this.compatibility = spy(GenerationCompatibility.class);
+ }
+
+ @Test
+ void testUseVar() {
+ when(compatibility.javaVersion()).thenReturn(10);
+ assertTrue(compatibility.useVar());
+ }
+
+ @Test
+ void testDontUseVar() {
+ when(compatibility.javaVersion()).thenReturn(9);
+ assertFalse(compatibility.useVar());
+ }
+
+ @Test
+ void testToListCollector() {
+ when(compatibility.javaVersion()).thenReturn(16);
+ assertEquals(ListCollector.TO_LIST, compatibility.listCollector());
+ }
+
+ @Test
+ void testCollectToUnmodifiableListCollector() {
+ when(compatibility.javaVersion()).thenReturn(15);
+ assertEquals(ListCollector.COLLECT_TO_UNMODIFIABLE_LIST, compatibility.listCollector());
+ when(compatibility.javaVersion()).thenReturn(10);
+ assertEquals(ListCollector.COLLECT_TO_UNMODIFIABLE_LIST, compatibility.listCollector());
+ }
+
+ @Test
+ void testCollectToListCollector() {
+ when(compatibility.javaVersion()).thenReturn(9);
+ assertEquals(ListCollector.COLLECT_TO_LIST, compatibility.listCollector());
+ }
+
+ @Test
+ void testUseCollectionsOf() {
+ when(compatibility.javaVersion()).thenReturn(9);
+ assertTrue(compatibility.useCollectionsOf());
+ }
+
+ @Test
+ void testDontUseCollectionsOf() {
+ when(compatibility.javaVersion()).thenReturn(8);
+ assertFalse(compatibility.useCollectionsOf());
+ }
+
+ @Test
+ void testUseGetFirst() {
+ when(compatibility.javaVersion()).thenReturn(21);
+ assertTrue(compatibility.useGetFirst());
+ }
+
+ @Test
+ void testDontUseGetFirst() {
+ when(compatibility.javaVersion()).thenReturn(20);
+ assertFalse(compatibility.useGetFirst());
+ }
+}
diff --git a/api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParsedDefine.java b/api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParsedDefine.java
index b585935..0337d17 100644
--- a/api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParsedDefine.java
+++ b/api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParsedDefine.java
@@ -1,80 +1,34 @@
package com.github.gtache.fxml.compiler.parsing;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
-import java.util.SequencedCollection;
-import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.spy;
-@ExtendWith(MockitoExtension.class)
class TestParsedDefine {
- private final ParsedProperty property;
- private final ParsedObject object;
- private final String string;
private final ParsedDefine define;
- TestParsedDefine(@Mock final ParsedProperty property, @Mock final ParsedObject object) {
- this.property = requireNonNull(property);
- this.object = requireNonNull(object);
- this.string = "str/ing";
+ TestParsedDefine() {
this.define = spy(ParsedDefine.class);
}
- @BeforeEach
- void beforeEach() {
- when(property.value()).thenReturn(string);
- when(define.object()).thenReturn(object);
- when(object.className()).thenReturn(string);
- when(object.children()).thenReturn(List.of(define));
- final var map = new LinkedHashMap>();
- map.put(property, List.of(object));
- when(object.properties()).thenReturn(map);
- when(object.attributes()).thenReturn(Map.of(string, property));
- }
-
- @Test
- void testObject() {
- assertEquals(object, define.object());
- }
-
@Test
void testClassName() {
- assertEquals(string, define.className());
- verify(define).object();
- verify(object).className();
+ assertEquals(ParsedDefine.class.getName(), define.className());
}
@Test
void testAttributes() {
- assertEquals(Map.of(string, property), define.attributes());
- verify(define).object();
- verify(object).attributes();
+ assertEquals(Map.of(), define.attributes());
}
@Test
void testProperties() {
- final var map = new LinkedHashMap>();
- map.put(property, List.of(object));
- assertEquals(map, define.properties());
- verify(define).object();
- verify(object).properties();
- }
-
- @Test
- void testChildren() {
- assertEquals(List.of(define), define.children());
- verify(define).object();
- verify(object).children();
+ assertEquals(new LinkedHashMap<>(), define.properties());
}
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/compatibility/impl/GenerationCompatibilityImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/compatibility/impl/GenerationCompatibilityImpl.java
new file mode 100644
index 0000000..d7b798c
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/compatibility/impl/GenerationCompatibilityImpl.java
@@ -0,0 +1,22 @@
+package com.github.gtache.fxml.compiler.compatibility.impl;
+
+import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
+
+/**
+ * Implementation of {@link GenerationCompatibility}
+ *
+ * @param javaVersion The minimum supported Java version
+ */
+public record GenerationCompatibilityImpl(int javaVersion) implements GenerationCompatibility {
+
+ /**
+ * Instantiates a new compatibility
+ *
+ * @param javaVersion The minimum supported Java version
+ */
+ public GenerationCompatibilityImpl {
+ if (javaVersion < 8) {
+ throw new IllegalArgumentException("Java version must be at least 8");
+ }
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInfoImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInfoImpl.java
index c790d8e..fd3946a 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInfoImpl.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInfoImpl.java
@@ -1,6 +1,7 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
+import com.github.gtache.fxml.compiler.GenericTypes;
import java.util.List;
import java.util.Objects;
@@ -11,7 +12,7 @@ import java.util.Objects;
* @param name The field name
* @param genericTypes The generic types
*/
-public record ControllerFieldInfoImpl(String name, List genericTypes) implements ControllerFieldInfo {
+public record ControllerFieldInfoImpl(String name, List genericTypes) implements ControllerFieldInfo {
/**
* Instantiates a new info
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java
index 4183e62..1b21a7f 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java
@@ -1,10 +1,9 @@
package com.github.gtache.fxml.compiler.impl;
-import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.InjectionType;
/**
- * Base field {@link InjectionType}s for {@link ControllerInjection}
+ * Base field {@link InjectionType}s
*/
public enum ControllerFieldInjectionTypes implements InjectionType {
/**
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java
index 9835ef7..486e47e 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java
@@ -4,17 +4,22 @@ import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import java.util.Map;
+import java.util.Objects;
/**
* Implementation of {@link ControllerInfo}
*
+ * @param className The controller class name
* @param handlerHasArgument The mapping of method name to true if the method has an argument
* @param fieldInfo The mapping of property name to controller field info
+ * @param hasInitialize True if the controller has an initialize method
*/
-public record ControllerInfoImpl(Map handlerHasArgument,
- Map fieldInfo) implements ControllerInfo {
+public record ControllerInfoImpl(String className, Map handlerHasArgument,
+ Map fieldInfo,
+ boolean hasInitialize) implements ControllerInfo {
public ControllerInfoImpl {
+ Objects.requireNonNull(className);
handlerHasArgument = Map.copyOf(handlerHasArgument);
fieldInfo = Map.copyOf(fieldInfo);
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionImpl.java
deleted file mode 100644
index 5b94367..0000000
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionImpl.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.github.gtache.fxml.compiler.impl;
-
-import com.github.gtache.fxml.compiler.ControllerInjection;
-import com.github.gtache.fxml.compiler.InjectionType;
-
-import java.util.Objects;
-
-/**
- * Implementation of {@link ControllerInjection}
- *
- * @param fieldInjectionType The field injection type
- * @param methodInjectionType The method injection type
- * @param injectionClass The injection class name
- */
-public record ControllerInjectionImpl(InjectionType fieldInjectionType, InjectionType methodInjectionType,
- String injectionClass) implements ControllerInjection {
- public ControllerInjectionImpl {
- Objects.requireNonNull(fieldInjectionType);
- Objects.requireNonNull(methodInjectionType);
- Objects.requireNonNull(injectionClass);
- }
-}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionTypes.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionTypes.java
new file mode 100644
index 0000000..4c28607
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionTypes.java
@@ -0,0 +1,17 @@
+package com.github.gtache.fxml.compiler.impl;
+
+import com.github.gtache.fxml.compiler.InjectionType;
+
+/**
+ * Base controller {@link InjectionType}s
+ */
+public enum ControllerInjectionTypes implements InjectionType {
+ /**
+ * Inject the controller instance
+ */
+ INSTANCE,
+ /**
+ * Inject a controller factory
+ */
+ FACTORY
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerMethodsInjectionType.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerMethodsInjectionType.java
index 1870c1d..7d2e50f 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerMethodsInjectionType.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerMethodsInjectionType.java
@@ -1,10 +1,9 @@
package com.github.gtache.fxml.compiler.impl;
-import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.InjectionType;
/**
- * Base methods {@link InjectionType}s for {@link ControllerInjection}
+ * Base methods {@link InjectionType}s
*/
public enum ControllerMethodsInjectionType implements InjectionType {
/**
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java
index fb5739d..53da964 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java
@@ -1,30 +1,38 @@
package com.github.gtache.fxml.compiler.impl;
-import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.GenerationParameters;
-import com.github.gtache.fxml.compiler.ResourceBundleInjection;
+import com.github.gtache.fxml.compiler.InjectionType;
+import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import java.util.Map;
-import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link GenerationParameters}
*
- * @param controllerInjections The mapping of controller class name to controller injection
- * @param sourceToGeneratedClassName The mapping of fx:include source to generated class name
- * @param sourceToControllerName The mapping of fx:include source to controller class name
- * @param resourceBundleInjection The resource bundle injection
+ * @param compatibility The compatibility info
+ * @param useImageInputStreamConstructor True if the InputStream constructor should be used
+ * @param bundleMap The mapping of controller class to resource bundle path
+ * @param controllerInjectionType The controller injection type
+ * @param fieldInjectionType The field injection type
+ * @param methodInjectionType The method injection type
+ * @param resourceInjectionType The resource injection type
*/
-public record GenerationParametersImpl(Map controllerInjections,
- Map sourceToGeneratedClassName,
- Map sourceToControllerName,
- ResourceBundleInjection resourceBundleInjection) implements GenerationParameters {
+public record GenerationParametersImpl(GenerationCompatibility compatibility, boolean useImageInputStreamConstructor,
+ Map bundleMap,
+ InjectionType controllerInjectionType,
+ InjectionType fieldInjectionType,
+ InjectionType methodInjectionType,
+ InjectionType resourceInjectionType) implements GenerationParameters {
public GenerationParametersImpl {
- controllerInjections = Map.copyOf(controllerInjections);
- sourceToGeneratedClassName = Map.copyOf(sourceToGeneratedClassName);
- sourceToControllerName = Map.copyOf(sourceToControllerName);
- Objects.requireNonNull(resourceBundleInjection);
+ requireNonNull(compatibility);
+ bundleMap = Map.copyOf(bundleMap);
+ requireNonNull(controllerInjectionType);
+ requireNonNull(fieldInjectionType);
+ requireNonNull(methodInjectionType);
+ requireNonNull(resourceInjectionType);
}
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java
index 5b935b9..7db390a 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java
@@ -3,6 +3,7 @@ package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
+import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import java.util.Objects;
@@ -12,15 +13,17 @@ import java.util.Objects;
*
* @param parameters The generation parameters
* @param controllerInfo The controller info
+ * @param sourceInfo The source info
* @param rootObject The root object
* @param outputClassName The output class name
*/
public record GenerationRequestImpl(GenerationParameters parameters, ControllerInfo controllerInfo,
- ParsedObject rootObject,
+ SourceInfo sourceInfo, ParsedObject rootObject,
String outputClassName) implements GenerationRequest {
public GenerationRequestImpl {
Objects.requireNonNull(parameters);
Objects.requireNonNull(controllerInfo);
+ Objects.requireNonNull(sourceInfo);
Objects.requireNonNull(rootObject);
Objects.requireNonNull(outputClassName);
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java
index d9f8a20..c554e98 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java
@@ -3,12 +3,10 @@ package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.Generator;
+import com.github.gtache.fxml.compiler.impl.internal.ConstructorFormatter;
import com.github.gtache.fxml.compiler.impl.internal.GenerationProgress;
-import com.github.gtache.fxml.compiler.impl.internal.HelperMethodsProvider;
-
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getControllerInjection;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getVariablePrefix;
-import static com.github.gtache.fxml.compiler.impl.internal.ObjectFormatter.format;
+import com.github.gtache.fxml.compiler.impl.internal.HelperMethodsFormatter;
+import com.github.gtache.fxml.compiler.impl.internal.LoadMethodFormatter;
//TODO handle binding (${})
@@ -16,7 +14,7 @@ import static com.github.gtache.fxml.compiler.impl.internal.ObjectFormatter.form
* Implementation of {@link Generator}
*/
public class GeneratorImpl implements Generator {
-
+
@Override
public String generate(final GenerationRequest request) throws GenerationException {
@@ -24,143 +22,37 @@ public class GeneratorImpl implements Generator {
final var className = request.outputClassName();
final var pkgName = className.substring(0, className.lastIndexOf('.'));
final var simpleClassName = className.substring(className.lastIndexOf('.') + 1);
- final var loadMethod = getLoadMethod(progress);
- final var controllerInjection = getControllerInjection(progress);
- final var controllerInjectionType = controllerInjection.fieldInjectionType();
- final var controllerInjectionClass = controllerInjection.injectionClass();
- final String constructorArgument;
- final String constructorControllerJavadoc;
- final String controllerArgumentType;
- final String controllerMapType;
- if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
- constructorArgument = "controllerFactory";
- constructorControllerJavadoc = "controller factory";
- controllerArgumentType = "com.github.gtache.fxml.compiler.ControllerFactory<" + controllerInjectionClass + ">";
- controllerMapType = "com.github.gtache.fxml.compiler.ControllerFactory>";
- } else {
- constructorArgument = "controller";
- constructorControllerJavadoc = "controller";
- controllerArgumentType = controllerInjectionClass;
- controllerMapType = "Object";
- }
- final var helperMethods = HelperMethodsProvider.getHelperMethods(progress);
- return """
- package %1$s;
-
- /**
- * Generated code, not thread-safe
- */
- public final class %2$s {
-
- private final java.util.Map, %7$s> controllersMap;
- private final java.util.Map, java.util.ResourceBundle> resourceBundlesMap;
- private boolean loaded;
- private %3$s controller;
-
- /**
- * Instantiates a new %2$s with no nested controllers and no resource bundle
- * @param %4$s The %5$s
- */
- public %2$s(final %8$s %4$s) {
- this(java.util.Map.of(%3$s.class, %4$s), java.util.Map.of());
- }
-
- /**
- * Instantiates a new %2$s with no nested controllers
- * @param %4$s The %5$s
- * @param resourceBundle The resource bundle
- */
- public %2$s(final %8$s %4$s, final java.util.ResourceBundle resourceBundle) {
- this(java.util.Map.of(%3$s.class, %4$s), java.util.Map.of(%3$s.class, resourceBundle));
- }
-
- /**
- * Instantiates a new %2$s with nested controllers
- * @param controllersMap The map of controller class to %5$s
- * @param resourceBundlesMap The map of controller class to resource bundle
- */
- public %2$s(final java.util.Map, %7$s> controllersMap, final java.util.Map, java.util.ResourceBundle> resourceBundlesMap) {
- this.controllersMap = java.util.Map.copyOf(controllersMap);
- this.resourceBundlesMap = java.util.Map.copyOf(resourceBundlesMap);
- }
-
- /**
- * Loads the view. Can only be called once.
- *
- * @return The view parent
- */
- %6$s
-
- %9$s
-
- /**
- * @return The controller
- */
- public %3$s controller() {
- if (loaded) {
- return controller;
- } else {
- throw new IllegalStateException("Not loaded");
- }
- }
- }
- """.formatted(pkgName, simpleClassName, controllerInjectionClass, constructorArgument, constructorControllerJavadoc,
- loadMethod, controllerMapType, controllerArgumentType, helperMethods);
- }
-
-
- /**
- * Computes the load method
- *
- * @param progress The generation progress
- * @return The load method
- */
- private static String getLoadMethod(final GenerationProgress progress) throws GenerationException {
- final var request = progress.request();
- final var rootObject = request.rootObject();
- final var controllerInjection = getControllerInjection(progress);
- final var controllerInjectionType = controllerInjection.fieldInjectionType();
- final var controllerClass = controllerInjection.injectionClass();
+ final var controllerInjectionClass = request.controllerInfo().className();
final var sb = progress.stringBuilder();
- sb.append("public T load() {\n");
- sb.append(" if (loaded) {\n");
- sb.append(" throw new IllegalStateException(\"Already loaded\");\n");
- sb.append(" }\n");
- final var resourceBundleInjection = request.parameters().resourceBundleInjection();
- if (resourceBundleInjection.injectionType() == ResourceBundleInjectionTypes.GET_BUNDLE) {
- sb.append(" final var bundle = java.util.ResourceBundle.getBundle(\"").append(resourceBundleInjection.bundleName()).append("\");\n");
- } else if (resourceBundleInjection.injectionType() == ResourceBundleInjectionTypes.CONSTRUCTOR) {
- sb.append(" final var bundle = resourceBundlesMap.get(").append(controllerClass).append(".class);\n");
- }
- if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
- sb.append(" final var fieldMap = new HashMap();\n");
- } else {
- sb.append(" controller = (").append(controllerClass).append(") controllersMap.get(").append(controllerClass).append(".class);\n");
- }
- final var variableName = progress.getNextVariableName(getVariablePrefix(rootObject));
- format(progress, rootObject, variableName);
- if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
- sb.append(" final var controllerFactory = controllersMap.get(").append(controllerClass).append(".class);\n");
- sb.append(" controller = (").append(controllerClass).append(") controllerFactory.create(fieldMap);\n");
- progress.controllerFactoryPostAction().forEach(sb::append);
- }
- if (controllerInjection.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) {
- sb.append(" try {\n");
- sb.append(" final var initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n");
- sb.append(" initialize.setAccessible(true);\n");
- sb.append(" initialize.invoke(controller);\n");
- sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException e) {\n");
- sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n");
- sb.append(" } catch (final NoSuchMethodException ignored) {\n");
- sb.append(" }\n");
- } else {
- sb.append(" controller.initialize();\n");
- }
- sb.append(" loaded = true;\n");
- sb.append(" return (T) ").append(variableName).append(";\n");
- sb.append("}");
+ sb.append("package ").append(pkgName).append(";\n");
+ sb.append("\n");
+ sb.append("/**\n");
+ sb.append(" * Generated code, not thread-safe\n");
+ sb.append(" */\n");
+ sb.append("public final class ").append(simpleClassName).append(" {\n");
+ sb.append("\n");
+ ConstructorFormatter.formatFieldsAndConstructor(progress);
+ sb.append("\n");
+ LoadMethodFormatter.formatLoadMethod(progress);
+ sb.append("\n");
+ HelperMethodsFormatter.formatHelperMethods(progress);
+ sb.append("\n");
+ formatControllerMethod(progress, controllerInjectionClass);
+ sb.append("}\n");
return sb.toString();
}
-
+ private static void formatControllerMethod(final GenerationProgress progress, final String controllerInjectionClass) {
+ final var sb = progress.stringBuilder();
+ sb.append(" /**\n");
+ sb.append(" * @return The controller\n");
+ sb.append(" */\n");
+ sb.append(" public ").append(controllerInjectionClass).append(" controller() {\n");
+ sb.append(" if (loaded) {\n");
+ sb.append(" return controller;\n");
+ sb.append(" } else {\n");
+ sb.append(" throw new IllegalStateException(\"Not loaded\");\n");
+ sb.append(" }\n");
+ sb.append(" }\n");
+ }
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenericTypesImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenericTypesImpl.java
new file mode 100644
index 0000000..e7ce8d4
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenericTypesImpl.java
@@ -0,0 +1,27 @@
+package com.github.gtache.fxml.compiler.impl;
+
+import com.github.gtache.fxml.compiler.GenericTypes;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Implementation of {@link GenericTypes}
+ *
+ * @param name The name
+ * @param subTypes The subtypes
+ */
+public record GenericTypesImpl(String name, List subTypes) implements GenericTypes {
+
+ /**
+ * Instantiates a new generic types
+ *
+ * @param name The name
+ * @param subTypes The subtypes
+ * @throws NullPointerException if any parameter is null
+ */
+ public GenericTypesImpl {
+ Objects.requireNonNull(name);
+ subTypes = List.copyOf(subTypes);
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionImpl.java
deleted file mode 100644
index af66fe5..0000000
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionImpl.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.github.gtache.fxml.compiler.impl;
-
-import com.github.gtache.fxml.compiler.InjectionType;
-import com.github.gtache.fxml.compiler.ResourceBundleInjection;
-
-import java.util.Objects;
-
-/**
- * Implementation of {@link ResourceBundleInjection}
- *
- * @param injectionType The injection type
- * @param bundleName The bundle name
- */
-public record ResourceBundleInjectionImpl(InjectionType injectionType,
- String bundleName) implements ResourceBundleInjection {
-
- public ResourceBundleInjectionImpl {
- Objects.requireNonNull(injectionType);
- Objects.requireNonNull(bundleName);
- }
-}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java
index b129ed1..95b634f 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java
@@ -1,16 +1,23 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
-import com.github.gtache.fxml.compiler.ResourceBundleInjection;
/**
- * Base {@link InjectionType}s for {@link ResourceBundleInjection}
+ * Base {@link InjectionType}s for resource bundles
*/
public enum ResourceBundleInjectionTypes implements InjectionType {
/**
* Resource bundle is injected in the constructor
*/
CONSTRUCTOR,
+ /**
+ * Resource bundle is injected as a function in the constructor
+ */
+ CONSTRUCTOR_FUNCTION,
+ /**
+ * Resource bundle name is injected in the constructor
+ */
+ CONSTRUCTOR_NAME,
/**
* Resource bundle is loaded using getBundle
*/
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/SourceInfoImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/SourceInfoImpl.java
new file mode 100644
index 0000000..ab20e88
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/SourceInfoImpl.java
@@ -0,0 +1,32 @@
+package com.github.gtache.fxml.compiler.impl;
+
+import com.github.gtache.fxml.compiler.SourceInfo;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Implementation of {@link SourceInfo}
+ *
+ * @param generatedClassName The generated class name
+ * @param controllerClassName The controller class name
+ * @param sourceFile The source file
+ * @param includedSources The included sources
+ * @param sourceToSourceInfo The mapping of source value to source info
+ * @param requiresResourceBundle True if the subtree requires a resource bundle
+ */
+public record SourceInfoImpl(String generatedClassName, String controllerClassName, Path sourceFile,
+ List includedSources,
+ Map sourceToSourceInfo,
+ boolean requiresResourceBundle) implements SourceInfo {
+
+ public SourceInfoImpl {
+ Objects.requireNonNull(generatedClassName);
+ Objects.requireNonNull(controllerClassName);
+ Objects.requireNonNull(sourceFile);
+ includedSources = List.copyOf(includedSources);
+ sourceToSourceInfo = Map.copyOf(sourceToSourceInfo);
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorFormatter.java
new file mode 100644
index 0000000..54f19a8
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorFormatter.java
@@ -0,0 +1,193 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.SourceInfo;
+import com.github.gtache.fxml.compiler.impl.ControllerInjectionTypes;
+import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
+import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Utility class to provide the view's constructor and fields
+ */
+public final class ConstructorFormatter {
+
+ private ConstructorFormatter() {
+ }
+
+ public static void formatFieldsAndConstructor(final GenerationProgress progress) throws GenerationException {
+ final var className = progress.request().outputClassName();
+ final var simpleClassName = className.substring(className.lastIndexOf('.') + 1);
+ final var mainControllerClass = progress.request().controllerInfo().className();
+ final var isFactory = progress.request().parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY;
+ if (hasDuplicateControllerClass(progress) && !isFactory) {
+ throw new GenerationException("Some controllers in the view tree have the same class ; Factory field injection is required");
+ }
+ fillControllers(progress);
+ final var sb = progress.stringBuilder();
+ final var controllerMap = progress.controllerClassToVariable();
+ controllerMap.forEach((c, v) -> sb.append(" private final ").append(c).append(" ").append(v).append(isFactory ? "Factory" : "").append(";\n"));
+ final var controllerArg = getVariableName("controller", isFactory);
+ final var controllerArgClass = getType(mainControllerClass, isFactory);
+ final var resourceBundleInfo = getResourceBundleInfo(progress);
+ final var resourceBundleType = resourceBundleInfo.type();
+ final var resourceBundleArg = resourceBundleInfo.variableName();
+ if (isFactory) {
+ sb.append(" private final ").append(controllerArgClass).append(" ").append(controllerArg).append(";\n");
+ sb.append(" private ").append(mainControllerClass).append(" controller;\n");
+ } else {
+ sb.append(" private final ").append(mainControllerClass).append(" controller;\n");
+ }
+ if (resourceBundleType != null) {
+ sb.append(" private final ").append(resourceBundleType).append(" ").append(resourceBundleArg).append(";\n");
+ }
+ sb.append(" private boolean loaded;\n");
+ sb.append("\n");
+ sb.append(" /**\n");
+ sb.append(" * Instantiates a new ").append(simpleClassName).append("\n");
+ sb.append(" * @param ").append(controllerArg).append(" The controller ").append(isFactory ? "factory" : "instance").append("\n");
+ controllerMap.forEach((c, s) -> sb.append(" * @param ").append(getVariableName(s, isFactory))
+ .append(" The subcontroller ").append(isFactory ? "factory" : "instance").append(" for ").append(c).append("\n"));
+
+ if (resourceBundleType != null) {
+ sb.append(" * @param ").append(resourceBundleArg).append(" The resource bundle\n");
+ }
+ sb.append(" */\n");
+ final var arguments = "final " + controllerArgClass + " " + controllerArg +
+ ((controllerMap.isEmpty()) ? "" : ", ") +
+ controllerMap.entrySet().stream().map(e -> "final " + getType(e.getKey(), isFactory) + " " + getVariableName(e.getValue(), isFactory))
+ .sorted().collect(Collectors.joining(", "))
+ + (resourceBundleType == null ? "" : ", final " + resourceBundleType + " " + resourceBundleArg);
+ sb.append(" public ").append(simpleClassName).append("(").append(arguments).append(") {\n");
+ sb.append(" this.").append(controllerArg).append(" = java.util.Objects.requireNonNull(").append(controllerArg).append(");\n");
+ controllerMap.values().forEach(s -> sb.append(" this.").append(getVariableName(s, isFactory)).append(" = java.util.Objects.requireNonNull(").append(getVariableName(s, isFactory)).append(");\n"));
+ if (resourceBundleType != null) {
+ sb.append(" this.").append(resourceBundleArg).append(" = java.util.Objects.requireNonNull(").append(resourceBundleArg).append(");\n");
+ }
+ sb.append(" }\n");
+ }
+
+ private static ResourceBundleInfo getResourceBundleInfo(final GenerationProgress progress) throws GenerationException {
+ final var injectionType = progress.request().parameters().resourceInjectionType();
+ if (injectionType instanceof final ResourceBundleInjectionTypes types) {
+ return switch (types) {
+ case CONSTRUCTOR -> new ResourceBundleInfo("java.util.ResourceBundle", "resourceBundle");
+ case CONSTRUCTOR_FUNCTION ->
+ new ResourceBundleInfo("java.util.function.Function", "resourceBundleFunction");
+ case CONSTRUCTOR_NAME -> new ResourceBundleInfo("String", "resourceBundleName");
+ case GETTER -> new ResourceBundleInfo(null, null);
+ case GET_BUNDLE -> new ResourceBundleInfo(null, null);
+ };
+ } else {
+ throw new GenerationException("Unknown resource injection type : " + injectionType);
+ }
+ }
+
+ private record ResourceBundleInfo(String type, String variableName) {
+ }
+
+ private static String getType(final String controllerClass, final boolean isFactory) {
+ if (isFactory) {
+ return "java.util.function.Function, " + controllerClass + ">";
+ } else {
+ return controllerClass;
+ }
+ }
+
+ private static String getVariableName(final String variableName, final boolean isFactory) {
+ if (isFactory) {
+ return getVariableName(variableName, "Factory");
+ } else {
+ return variableName;
+ }
+ }
+
+ private static String getVariableName(final String variableName, final String suffix) {
+ return variableName + suffix;
+ }
+
+ private static boolean hasDuplicateControllerClass(final GenerationProgress progress) {
+ final var set = new HashSet();
+ return hasDuplicateControllerClass(progress.request().sourceInfo(), set);
+ }
+
+ private static boolean hasDuplicateControllerClass(final SourceInfo info, final Set controllers) {
+ final var controllerClass = info.controllerClassName();
+ if (controllers.contains(controllerClass)) {
+ return true;
+ }
+ return info.includedSources().stream().anyMatch(s -> hasDuplicateControllerClass(s, controllers));
+ }
+
+ private static void fillControllers(final GenerationProgress progress) {
+ progress.request().sourceInfo().includedSources().forEach(s -> fillControllers(progress, s));
+ }
+
+ private static void fillControllers(final GenerationProgress progress, final SourceInfo info) {
+ progress.controllerClassToVariable().put(info.controllerClassName(), progress.getNextVariableName(GenerationHelper.getVariablePrefix(info.controllerClassName())));
+ info.includedSources().forEach(s -> fillControllers(progress, s));
+ }
+
+ private static void fillControllers(final SourceInfo info, final Set super String> controllers) {
+ controllers.add(info.controllerClassName());
+ info.includedSources().forEach(s -> fillControllers(s, controllers));
+ }
+
+ static String formatSubViewConstructorCall(final GenerationProgress progress, final ParsedInclude include) throws GenerationException {
+ final var request = progress.request();
+ final var info = request.sourceInfo();
+ final var subInfo = info.sourceToSourceInfo().get(include.source());
+ if (subInfo == null) {
+ throw new GenerationException("Unknown include source : " + include.source());
+ } else {
+ final var isFactory = request.parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY;
+ final var subClassName = subInfo.controllerClassName();
+ final var subControllerVariable = getVariableName(progress.controllerClassToVariable().get(subClassName), isFactory);
+ final var subControllers = new HashSet();
+ subInfo.includedSources().forEach(s -> fillControllers(s, subControllers));
+ final var arguments = subControllers.stream().sorted().map(c -> getVariableName(progress.controllerClassToVariable().get(c), isFactory)).collect(Collectors.joining(", "));
+ final var bundleVariable = subInfo.requiresResourceBundle() ? getBundleVariable(progress, include) : null;
+ final var argumentList = subControllerVariable + (arguments.isEmpty() ? "" : ", " + arguments) + (bundleVariable == null ? "" : ", " + bundleVariable);
+ final var subViewName = subInfo.generatedClassName();
+ final var variable = progress.getNextVariableName(GenerationHelper.getVariablePrefix(subViewName));
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, subViewName)).append(variable).append(" = new ").append(subViewName).append("(").append(argumentList).append(");\n");
+ return variable;
+ }
+ }
+
+ private static String getBundleVariable(final GenerationProgress progress, final ParsedInclude include) throws GenerationException {
+ final var info = getResourceBundleInfo(progress);
+ if (info.type() == null) {
+ return null;
+ } else if (include.resources() == null) {
+ return info.variableName();
+ } else {
+ final var sb = progress.stringBuilder();
+ if (progress.request().parameters().resourceInjectionType() instanceof final ResourceBundleInjectionTypes types) {
+ return switch (types) {
+ case GETTER, GET_BUNDLE -> null;
+ case CONSTRUCTOR_NAME -> {
+ final var bundleVariable = progress.getNextVariableName("resourceBundleName");
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "String")).append(bundleVariable).append(" = \"").append(include.resources()).append("\";\n");
+ yield bundleVariable;
+ }
+ case CONSTRUCTOR_FUNCTION -> {
+ final var bundleVariable = progress.getNextVariableName("resourceBundleFunction");
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.function.Function")).append(bundleVariable).append(" = (java.util.function.Function) s -> \"").append(include.resources()).append("\";\n");
+ yield bundleVariable;
+ }
+ case CONSTRUCTOR -> {
+ final var bundleVariable = progress.getNextVariableName("resourceBundle");
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.ResourceBundle")).append(bundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(include.resources()).append("\");\n");
+ yield bundleVariable;
+ }
+ };
+ } else {
+ throw new GenerationException("Unknown resource injection type : " + progress.request().parameters().resourceInjectionType());
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java
index e13e5e9..cbdf1b3 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java
@@ -11,12 +11,11 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
-import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.getConstructorArgs;
-
/**
- * Helper methods for {@link GeneratorImpl} to handle constructors
+ * Helper methods for {@link GeneratorImpl} to handle objects constructors
*/
final class ConstructorHelper {
+
private ConstructorHelper() {
}
@@ -58,7 +57,7 @@ final class ConstructorHelper {
static ConstructorArgs getMatchingConstructorArgs(final Constructor>[] constructors, final Set allPropertyNames) {
ConstructorArgs matchingConstructorArgs = null;
for (final var constructor : constructors) {
- final var constructorArgs = getConstructorArgs(constructor);
+ final var constructorArgs = ReflectionHelper.getConstructorArgs(constructor);
final var matchingArgsCount = getMatchingArgsCount(constructorArgs, allPropertyNames);
if (matchingConstructorArgs == null ? matchingArgsCount > 0 : matchingArgsCount > getMatchingArgsCount(matchingConstructorArgs, allPropertyNames)) {
matchingConstructorArgs = constructorArgs;
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java
index cca0008..dc2208e 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java
@@ -7,15 +7,12 @@ import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getControllerInjection;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSetMethod;
-
/**
* Various methods to help {@link GeneratorImpl} for injecting controllers
*/
final class ControllerInjector {
- private ControllerInjector() {
+ private ControllerInjector() {
}
/**
@@ -27,23 +24,22 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
static void injectControllerField(final GenerationProgress progress, final String id, final String variable) throws GenerationException {
- final var controllerInjection = getControllerInjection(progress);
- final var controllerInjectionType = controllerInjection.fieldInjectionType();
- if (controllerInjectionType instanceof final ControllerFieldInjectionTypes types) {
+ final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
+ if (fieldInjectionType instanceof final ControllerFieldInjectionTypes types) {
final var sb = progress.stringBuilder();
switch (types) {
case FACTORY ->
- sb.append(" fieldMap.put(\"").append(id).append("\", ").append(variable).append(");\n");
- case ASSIGN -> sb.append(" controller.").append(id).append(" = ").append(variable).append(";\n");
+ sb.append(" fieldMap.put(\"").append(id).append("\", ").append(variable).append(");\n");
+ case ASSIGN -> sb.append(" controller.").append(id).append(" = ").append(variable).append(";\n");
case SETTERS -> {
- final var setMethod = getSetMethod(id);
- sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
+ final var setMethod = GenerationHelper.getSetMethod(id);
+ sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
}
case REFLECTION ->
- sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
+ sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
}
} else {
- throw new GenerationException("Unknown controller injection type : " + controllerInjectionType);
+ throw new GenerationException("Unknown controller injection type : " + fieldInjectionType);
}
}
@@ -80,14 +76,14 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
private static void injectControllerMethod(final GenerationProgress progress, final String methodInjection) throws GenerationException {
- final var injection = getControllerInjection(progress);
- if (injection.fieldInjectionType() instanceof final ControllerFieldInjectionTypes fieldTypes) {
+ final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
+ if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) {
switch (fieldTypes) {
case FACTORY -> progress.controllerFactoryPostAction().add(methodInjection);
case ASSIGN, SETTERS, REFLECTION -> progress.stringBuilder().append(methodInjection);
}
} else {
- throw getUnknownInjectionException(injection.fieldInjectionType());
+ throw getUnknownInjectionException(fieldInjectionType);
}
}
@@ -101,24 +97,24 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
private static String getEventHandlerMethodInjection(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) throws GenerationException {
- final var setMethod = getSetMethod(property.name());
- final var injection = getControllerInjection(progress);
+ final var setMethod = GenerationHelper.getSetMethod(property.name());
+ final var methodInjectionType = progress.request().parameters().methodInjectionType();
final var controllerMethod = property.value().replace("#", "");
- if (injection.methodInjectionType() instanceof final ControllerMethodsInjectionType methodTypes) {
+ if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) {
return switch (methodTypes) {
case REFERENCE -> {
final var hasArgument = progress.request().controllerInfo().handlerHasArgument(controllerMethod);
if (hasArgument) {
- yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
+ yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
} else {
- yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
+ yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
}
}
case REFLECTION ->
- " " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n";
+ " " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n";
};
} else {
- throw getUnknownInjectionException(injection.methodInjectionType());
+ throw getUnknownInjectionException(methodInjectionType);
}
}
@@ -133,18 +129,18 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
private static String getCallbackMethodInjection(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String argumentClazz) throws GenerationException {
- final var setMethod = getSetMethod(property.name());
- final var injection = getControllerInjection(progress);
+ final var setMethod = GenerationHelper.getSetMethod(property.name());
+ final var methodInjectionType = progress.request().parameters().methodInjectionType();
final var controllerMethod = property.value().replace("#", "");
- if (injection.methodInjectionType() instanceof final ControllerMethodsInjectionType methodTypes) {
+ if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) {
return switch (methodTypes) {
case REFERENCE ->
- " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
+ " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
case REFLECTION ->
- " " + parentVariable + "." + setMethod + "(e -> callCallbackMethod(\"" + controllerMethod + "\", e, " + argumentClazz + "));\n";
+ " " + parentVariable + "." + setMethod + "(e -> callCallbackMethod(\"" + controllerMethod + "\", e, " + argumentClazz + "));\n";
};
} else {
- throw getUnknownInjectionException(injection.methodInjectionType());
+ throw getUnknownInjectionException(methodInjectionType);
}
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java
index 018d61c..79abb60 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java
@@ -5,7 +5,7 @@ import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.EXPRESSION_PREFIX;
/**
* Helper methods for {@link GeneratorImpl} to set fields
@@ -13,7 +13,6 @@ import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
final class FieldSetter {
private FieldSetter() {
-
}
/**
@@ -39,8 +38,8 @@ final class FieldSetter {
* @throws GenerationException if an error occurs
*/
static void setField(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String fieldType) throws GenerationException {
- final var injection = getControllerInjection(progress);
- if (injection.fieldInjectionType() instanceof final ControllerFieldInjectionTypes fieldTypes) {
+ final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
+ if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) {
switch (fieldTypes) {
case ASSIGN -> setAssign(progress, property, parentVariable);
case FACTORY -> setFactory(progress, property, parentVariable);
@@ -48,14 +47,14 @@ final class FieldSetter {
case REFLECTION -> setReflection(progress, property, parentVariable, fieldType);
}
} else {
- throw new GenerationException("Unknown injection type : " + injection.fieldInjectionType());
+ throw new GenerationException("Unknown injection type : " + fieldInjectionType);
}
}
private static void setAssign(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) {
- final var methodName = getSetMethod(property);
- final var value = property.value().replace("$", "");
- progress.stringBuilder().append(" ").append(parentVariable).append(".").append(methodName).append("(").append(value).append(");\n");
+ final var methodName = GenerationHelper.getSetMethod(property);
+ final var value = property.value().replace(EXPRESSION_PREFIX, "");
+ progress.stringBuilder().append(" ").append(parentVariable).append(".").append(methodName).append("(").append(value).append(");\n");
}
private static void setFactory(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) {
@@ -67,28 +66,27 @@ final class FieldSetter {
}
private static String getSetString(final ParsedProperty property, final String parentVariable) {
- final var methodName = getSetMethod(property);
- final var value = property.value().replace("$", "");
+ final var methodName = GenerationHelper.getSetMethod(property);
+ final var value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\.");
- final var getterName = getGetMethod(split[1]);
- return " " + parentVariable + "." + methodName + "(" + split[0] + "." + getterName + ");\n";
+ final var getterName = GenerationHelper.getGetMethod(split[1]);
+ return " " + parentVariable + "." + methodName + "(" + split[0] + "." + getterName + ");\n";
}
private static void setReflection(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String fieldType) {
- final var methodName = getSetMethod(property);
- final var value = property.value().replace("$", "");
+ final var methodName = GenerationHelper.getSetMethod(property);
+ final var value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\.");
final var fieldName = split[1];
- progress.stringBuilder().append("""
- try {
- final var field = controller.getClass().getDeclaredField("%s");
- field.setAccessible(true);
- final var value = (%s) field.get(controller);
- %s.%s(value);
- } catch (NoSuchFieldException | IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- """.formatted(fieldName, fieldType, parentVariable, methodName));
+ final var sb = progress.stringBuilder();
+ sb.append(" try {\n");
+ sb.append(" ").append(GenerationCompatibilityHelper.getStartVar(progress, "java.lang.reflect.Field", 0)).append("field = controller.getClass().getDeclaredField(\"").append(fieldName).append("\");\n");
+ sb.append(" field.setAccessible(true);\n");
+ sb.append(" final var value = (").append(fieldType).append(") field.get(controller);\n");
+ sb.append(" ").append(parentVariable).append(".").append(methodName).append("(value);\n");
+ sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n");
+ sb.append(" throw new RuntimeException(e);\n");
+ sb.append(" }\n");
}
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java
index 7a617ae..8d25ff0 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java
@@ -12,7 +12,8 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format fonts
@@ -23,6 +24,10 @@ final class FontFormatter {
}
+ private static String getStartFont(final GenerationProgress progress) {
+ return GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.text.Font");
+ }
+
static void formatFont(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
final var value = parseFontValue(parsedObject);
@@ -38,32 +43,31 @@ final class FontFormatter {
} else {
formatStyle(progress, fw, fp, size, name, variableName);
}
- handleId(progress, parsedObject, variableName);
+ GenerationHelper.handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("Font cannot have children or properties : " + parsedObject);
}
}
private static void formatURL(final GenerationProgress progress, final URL url, final double size, final String variableName) {
- final var urlVariableName = URLBuilder.formatURL(progress, url.toString());
- progress.stringBuilder().append("""
- Font %1$s;
- try (final var in = %2$s.openStream()) {
- %1$s = Font.loadFont(in, %3$s);
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
- """.formatted(variableName, urlVariableName, size));
+ final var urlVariableName = URLFormatter.formatURL(progress, url.toString());
+ final var sb = progress.stringBuilder();
+ sb.append(" final javafx.scene.text.Font ").append(variableName).append(";\n");
+ sb.append(" try (").append(GenerationCompatibilityHelper.getStartVar(progress, "java.io.InputStream", 0)).append(" in = ").append(urlVariableName).append(".openStream()) {\n");
+ sb.append(" ").append(variableName).append(" = javafx.scene.text.Font.loadFont(in, ").append(size).append(");\n");
+ sb.append(" } catch (final java.io.IOException e) {\n");
+ sb.append(" throw new RuntimeException(e);\n");
+ sb.append(" }\n");
}
private static void formatNoStyle(final GenerationProgress progress, final String name, final double size, final String variableName) {
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name).append("\", ").append(size).append(");\n");
+ progress.stringBuilder().append(getStartFont(progress)).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name).append("\", ").append(size).append(");\n");
}
private static void formatStyle(final GenerationProgress progress, final FontWeight fw, final FontPosture fp, final double size, final String name, final String variableName) {
final var finalFW = fw == null ? FontWeight.NORMAL : fw;
final var finalFP = fp == null ? FontPosture.REGULAR : fp;
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name)
+ progress.stringBuilder().append(getStartFont(progress)).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name)
.append("\", javafx.scene.text.FontWeight.").append(finalFW.name()).append(", javafx.scene.text.FontPosture.")
.append(finalFP.name()).append(", ").append(size).append(");\n");
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationCompatibilityHelper.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationCompatibilityHelper.java
new file mode 100644
index 0000000..f04ad49
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationCompatibilityHelper.java
@@ -0,0 +1,55 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+
+/**
+ * Various helper methods for {@link GeneratorImpl} to handle compatibility with older java versions
+ */
+final class GenerationCompatibilityHelper {
+
+ private GenerationCompatibilityHelper() {
+
+ }
+
+ static String getStartVar(final GenerationProgress progress, final ParsedObject parsedObject) throws GenerationException {
+ return getStartVar(progress, parsedObject.className() + ReflectionHelper.getGenericTypes(progress, parsedObject));
+ }
+
+ static String getStartVar(final GenerationProgress progress, final String className) {
+ return getStartVar(progress, className, 8);
+ }
+
+ static String getStartVar(final GenerationProgress progress, final String className, final int indent) {
+ if (progress.request().parameters().compatibility().useVar()) {
+ return " ".repeat(indent) + "final var ";
+ } else {
+ return " ".repeat(indent) + "final " + className + " ";
+ }
+ }
+
+ static String getToList(final GenerationProgress progress) {
+ return switch (progress.request().parameters().compatibility().listCollector()) {
+ case TO_LIST -> ".toList()";
+ case COLLECT_TO_UNMODIFIABLE_LIST -> ".collect(java.util.stream.Collectors.toUnmodifiableList())";
+ case COLLECT_TO_LIST -> ".collect(java.util.stream.Collectors.toList())";
+ };
+ }
+
+ static String getGetFirst(final GenerationProgress progress) {
+ if (progress.request().parameters().compatibility().useGetFirst()) {
+ return ".getFirst()";
+ } else {
+ return ".get(0)";
+ }
+ }
+
+ static String getListOf(final GenerationProgress progress) {
+ if (progress.request().parameters().compatibility().useCollectionsOf()) {
+ return "java.util.List.of(";
+ } else {
+ return "java.util.Arrays.asList(";
+ }
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java
index 264ee0e..83466bf 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java
@@ -1,6 +1,5 @@
package com.github.gtache.fxml.compiler.impl.internal;
-import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
@@ -11,21 +10,54 @@ import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Map;
-import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectControllerField;
-
/**
* Various helper methods for {@link GeneratorImpl}
*/
-public final class GenerationHelper {
+final class GenerationHelper {
private static final Logger logger = LogManager.getLogger(GenerationHelper.class);
static final String FX_ID = "fx:id";
static final String FX_VALUE = "fx:value";
static final String VALUE = "value";
- static final String START_VAR = " final var ";
+
+ //Taken from FXMLLoader
+ static final String ESCAPE_PREFIX = "\\";
+ static final String RELATIVE_PATH_PREFIX = "@";
+ static final String RESOURCE_KEY_PREFIX = "%";
+ static final String EXPRESSION_PREFIX = "$";
+ static final String BINDING_EXPRESSION_PREFIX = "${";
+ static final String BI_DIRECTIONAL_BINDING_PREFIX = "#{";
+
private GenerationHelper() {
+ }
+ /**
+ * Handles the fx:id attribute of an object
+ *
+ * @param progress The generation progress
+ * @param parsedObject The parsed object
+ * @param variableName The variable name
+ * @throws GenerationException if an error occurs
+ */
+ static void handleId(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
+ final var id = parsedObject.attributes().get(FX_ID);
+ if (id != null) {
+ final var idValue = id.value();
+ final String className;
+ if (progress.request().controllerInfo().fieldInfo(idValue) == null) {
+ className = parsedObject.className();
+ logger.debug("Not injecting {} because it is not found in controller", idValue);
+ } else {
+ if (ReflectionHelper.isGeneric(ReflectionHelper.getClass(parsedObject.className()))) {
+ className = parsedObject.className() + ReflectionHelper.getGenericTypes(progress, parsedObject);
+ } else {
+ className = parsedObject.className();
+ }
+ ControllerInjector.injectControllerField(progress, idValue, variableName);
+ }
+ progress.idToVariableInfo().put(idValue, new VariableInfo(idValue, parsedObject, variableName, className));
+ }
}
/**
@@ -34,27 +66,18 @@ public final class GenerationHelper {
* @param object The object
* @return The variable prefix
*/
- public static String getVariablePrefix(final ParsedObject object) {
- final var className = object.className();
- return className.substring(className.lastIndexOf('.') + 1).toLowerCase();
+ static String getVariablePrefix(final ParsedObject object) {
+ return getVariablePrefix(object.className());
}
/**
- * Gets the controller injection object from the generation request
+ * Returns the variable prefix for the given class name
*
- * @param progress The generation progress
- * @return The controller injection
- * @throws GenerationException If the controller is not found
+ * @param className The class name
+ * @return The variable prefix
*/
- public static ControllerInjection getControllerInjection(final GenerationProgress progress) throws GenerationException {
- final var request = progress.request();
- final var property = request.rootObject().attributes().get("fx:controller");
- if (property == null) {
- throw new GenerationException("Root object must have a controller property");
- } else {
- final var id = property.value();
- return request.parameters().controllerInjections().get(id);
- }
+ static String getVariablePrefix(final String className) {
+ return className.substring(className.lastIndexOf('.') + 1).toLowerCase();
}
/**
@@ -97,27 +120,6 @@ public final class GenerationHelper {
return "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
}
- /**
- * Handles the fx:id attribute of an object
- *
- * @param progress The generation progress
- * @param parsedObject The parsed object
- * @param variableName The variable name
- * @throws GenerationException if an error occurs
- */
- static void handleId(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
- final var id = parsedObject.attributes().get(FX_ID);
- if (id != null) {
- progress.idToVariableName().put(id.value(), variableName);
- progress.idToObject().put(id.value(), parsedObject);
- if (progress.request().controllerInfo().fieldInfo(id.value()) == null) {
- logger.debug("Not injecting {} because it is not found in controller", id.value());
- } else {
- injectControllerField(progress, id.value(), variableName);
- }
- }
- }
-
/**
* Returns the sorted attributes of the given object
*
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java
index 8fea449..7d9ad3c 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java
@@ -2,28 +2,29 @@ package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
-import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.SequencedCollection;
+import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Used by {@link GeneratorImpl} to track the generation progress
*
* @param request The generation request
- * @param idToVariableName The id to variable name mapping
- * @param idToObject The id to parsed object mapping
+ * @param idToVariableInfo The id to variable info
* @param variableNameCounters The variable name counters for variable name generation
+ * @param controllerClassToVariable The controller class to variable mapping
* @param controllerFactoryPostAction The controller factory post action for factory injection
* @param stringBuilder The string builder
*/
-public record GenerationProgress(GenerationRequest request, Map idToVariableName,
- Map idToObject,
+public record GenerationProgress(GenerationRequest request, Map idToVariableInfo,
Map variableNameCounters,
+ SequencedMap controllerClassToVariable,
SequencedCollection controllerFactoryPostAction,
StringBuilder stringBuilder) {
@@ -31,18 +32,18 @@ public record GenerationProgress(GenerationRequest request, Map
* Instantiates a new GenerationProgress
*
* @param request The generation request
- * @param idToVariableName The id to variable name mapping
- * @param idToObject The id to parsed object mapping
+ * @param idToVariableInfo The id to variable info mapping
* @param variableNameCounters The variable name counters
+ * @param controllerClassToVariable The controller class to variable mapping
* @param controllerFactoryPostAction The controller factory post action
* @param stringBuilder The string builder
* @throws NullPointerException if any parameter is null
*/
public GenerationProgress {
Objects.requireNonNull(request);
- Objects.requireNonNull(idToVariableName);
- Objects.requireNonNull(idToObject);
+ Objects.requireNonNull(idToVariableInfo);
Objects.requireNonNull(variableNameCounters);
+ Objects.requireNonNull(controllerClassToVariable);
Objects.requireNonNull(controllerFactoryPostAction);
Objects.requireNonNull(stringBuilder);
}
@@ -54,7 +55,7 @@ public record GenerationProgress(GenerationRequest request, Map
* @throws NullPointerException if request is null
*/
public GenerationProgress(final GenerationRequest request) {
- this(request, new HashMap<>(), new HashMap<>(), new HashMap<>(), new ArrayList<>(), new StringBuilder());
+ this(request, new HashMap<>(), new HashMap<>(), new LinkedHashMap<>(), new ArrayList<>(), new StringBuilder());
}
/**
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsFormatter.java
new file mode 100644
index 0000000..9b80366
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsFormatter.java
@@ -0,0 +1,99 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
+import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
+
+/**
+ * Formats the helper methods for the generated code
+ */
+public final class HelperMethodsFormatter {
+
+
+ private HelperMethodsFormatter() {
+ }
+
+ /**
+ * Formats the helper methods for the given generation progress
+ *
+ * @param progress The generation progress
+ */
+ public static void formatHelperMethods(final GenerationProgress progress) {
+ final var parameters = progress.request().parameters();
+ final var methodInjectionType = parameters.methodInjectionType();
+ final var sb = progress.stringBuilder();
+ if (methodInjectionType == ControllerMethodsInjectionType.REFLECTION) {
+ final var toList = GenerationCompatibilityHelper.getToList(progress);
+ final var getFirst = GenerationCompatibilityHelper.getGetFirst(progress);
+ final var startVariableMethodList = GenerationCompatibilityHelper.getStartVar(progress, "java.util.List", 0);
+ sb.append(" private void callEventHandlerMethod(final String methodName, final T event) {\n");
+ sb.append(" try {\n");
+ sb.append(" final java.lang.reflect.Method method;\n");
+ sb.append(" ").append(startVariableMethodList).append("methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())\n");
+ sb.append(" .filter(m -> m.getName().equals(methodName))").append(toList).append(";\n");
+ sb.append(" if (methods.size() > 1) {\n");
+ sb.append(" ").append(startVariableMethodList).append("eventMethods = methods.stream().filter(m ->\n");
+ sb.append(" m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0]))").append(toList).append(";\n");
+ sb.append(" if (eventMethods.size() == 1) {\n");
+ sb.append(" method = eventMethods").append(getFirst).append(";\n");
+ sb.append(" } else {\n");
+ sb.append(" ").append(startVariableMethodList).append("emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0)").append(toList).append(";\n");
+ sb.append(" if (emptyMethods.size() == 1) {\n");
+ sb.append(" method = emptyMethods").append(getFirst).append(";\n");
+ sb.append(" } else {\n");
+ sb.append(" throw new IllegalArgumentException(\"Multiple matching methods for \" + methodName);\n");
+ sb.append(" }\n");
+ sb.append(" }\n");
+ sb.append(" } else if (methods.size() == 1) {\n");
+ sb.append(" method = methods").append(getFirst).append(";\n");
+ sb.append(" } else {\n");
+ sb.append(" throw new IllegalArgumentException(\"No matching method for \" + methodName);\n");
+ sb.append(" }\n");
+ sb.append(" method.setAccessible(true);\n");
+ sb.append(" if (method.getParameterCount() == 0) {\n");
+ sb.append(" method.invoke(controller);\n");
+ sb.append(" } else {\n");
+ sb.append(" method.invoke(controller, event);\n");
+ sb.append(" }\n");
+ sb.append(" } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {\n");
+ sb.append(" throw new RuntimeException(\"Error using reflection on \" + methodName, ex);\n");
+ sb.append(" }\n");
+ sb.append(" }\n");
+ sb.append("\n");
+ sb.append(" private U callCallbackMethod(final String methodName, final T value, final Class clazz) {\n");
+ sb.append(" try {\n");
+ sb.append(" final java.lang.reflect.Method method;\n");
+ sb.append(" ").append(startVariableMethodList).append("methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())\n");
+ sb.append(" .filter(m -> m.getName().equals(methodName))").append(toList).append(";\n");
+ sb.append(" if (methods.size() > 1) {\n");
+ sb.append(" ").append(startVariableMethodList).append("eventMethods = methods.stream().filter(m ->\n");
+ sb.append(" m.getParameterCount() == 1 && clazz.isAssignableFrom(m.getParameterTypes()[0]))").append(toList).append(";\n");
+ sb.append(" if (eventMethods.size() == 1) {\n");
+ sb.append(" method = eventMethods").append(getFirst).append(";\n");
+ sb.append(" } else {\n");
+ sb.append(" throw new IllegalArgumentException(\"Multiple matching methods for \" + methodName);\n");
+ sb.append(" }\n");
+ sb.append(" } else if (methods.size() == 1) {\n");
+ sb.append(" method = methods").append(getFirst).append(";\n");
+ sb.append(" } else {\n");
+ sb.append(" throw new IllegalArgumentException(\"No matching method for \" + methodName);\n");
+ sb.append(" }\n");
+ sb.append(" method.setAccessible(true);\n");
+ sb.append(" return (U) method.invoke(controller, value);\n");
+ sb.append(" } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {\n");
+ sb.append(" throw new RuntimeException(\"Error using reflection on \" + methodName, ex);\n");
+ sb.append(" }\n");
+ sb.append(" }\n");
+ }
+ if (parameters.fieldInjectionType() == ControllerFieldInjectionTypes.REFLECTION) {
+ sb.append(" private void injectField(final String fieldName, final T object) {\n");
+ sb.append(" try {\n");
+ sb.append(" ").append(GenerationCompatibilityHelper.getStartVar(progress, "java.lang.reflect.Field", 0)).append("field = controller.getClass().getDeclaredField(fieldName);\n");
+ sb.append(" field.setAccessible(true);\n");
+ sb.append(" field.set(controller, object);\n");
+ sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n");
+ sb.append(" throw new RuntimeException(\"Error using reflection on \" + fieldName, e);\n");
+ sb.append(" }\n");
+ sb.append(" }\n");
+ }
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsProvider.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsProvider.java
deleted file mode 100644
index 4175e47..0000000
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsProvider.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.github.gtache.fxml.compiler.impl.internal;
-
-import com.github.gtache.fxml.compiler.GenerationException;
-import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
-import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
-
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getControllerInjection;
-
-/**
- * Provides the helper methods for the generated code
- */
-public final class HelperMethodsProvider {
-
- private HelperMethodsProvider() {
-
- }
-
- /**
- * Gets helper methods string for the given generation progress
- *
- * @param progress The generation progress
- * @return The helper methods
- * @throws GenerationException if an error occurs
- */
- public static String getHelperMethods(final GenerationProgress progress) throws GenerationException {
- final var injection = getControllerInjection(progress);
- final var methodInjectionType = injection.methodInjectionType();
- final var sb = new StringBuilder();
- if (methodInjectionType == ControllerMethodsInjectionType.REFLECTION) {
- sb.append("""
- private void callEventHandlerMethod(final String methodName, final T event) {
- try {
- final java.lang.reflect.Method method;
- final var methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
- .filter(m -> m.getName().equals(methodName)).toList();
- if (methods.size() > 1) {
- final var eventMethods = methods.stream().filter(m ->
- m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0])).toList();
- if (eventMethods.size() == 1) {
- method = eventMethods.getFirst();
- } else {
- final var emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList();
- if (emptyMethods.size() == 1) {
- method = emptyMethods.getFirst();
- } else {
- throw new IllegalArgumentException("Multiple matching methods for " + methodName);
- }
- }
- } else if (methods.size() == 1) {
- method = methods.getFirst();
- } else {
- throw new IllegalArgumentException("No matching method for " + methodName);
- }
- method.setAccessible(true);
- if (method.getParameterCount() == 0) {
- method.invoke(controller);
- } else {
- method.invoke(controller, event);
- }
- } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
- throw new RuntimeException("Error using reflection on " + methodName, ex);
- }
- }
-
- private U callCallbackMethod(final String methodName, final T value, final Class clazz) {
- try {
- final java.lang.reflect.Method method;
- final var methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
- .filter(m -> m.getName().equals(methodName)).toList();
- if (methods.size() > 1) {
- final var eventMethods = methods.stream().filter(m ->
- m.getParameterCount() == 1 && clazz.isAssignableFrom(m.getParameterTypes()[0])).toList();
- if (eventMethods.size() == 1) {
- method = eventMethods.getFirst();
- } else {
- throw new IllegalArgumentException("Multiple matching methods for " + methodName);
- }
- } else if (methods.size() == 1) {
- method = methods.getFirst();
- } else {
- throw new IllegalArgumentException("No matching method for " + methodName);
- }
- method.setAccessible(true);
- return (U) method.invoke(controller, value);
- } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
- throw new RuntimeException("Error using reflection on " + methodName, ex);
- }
- }
- """);
- }
- if (injection.fieldInjectionType() == ControllerFieldInjectionTypes.REFLECTION) {
- sb.append("""
- private void injectField(final String fieldName, final T object) {
- try {
- final var field = controller.getClass().getDeclaredField(fieldName);
- field.setAccessible(true);
- field.set(controller, object);
- } catch (final NoSuchFieldException | IllegalAccessException e) {
- throw new RuntimeException("Error using reflection on " + fieldName, e);
- }
- }
- """);
- }
- return sb.toString();
- }
-}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageBuilder.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageBuilder.java
deleted file mode 100644
index 4984251..0000000
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageBuilder.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.github.gtache.fxml.compiler.impl.internal;
-
-import com.github.gtache.fxml.compiler.GenerationException;
-import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
-import com.github.gtache.fxml.compiler.parsing.ParsedObject;
-
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
-import static com.github.gtache.fxml.compiler.impl.internal.URLBuilder.formatURL;
-
-/**
- * Helper methods for {@link GeneratorImpl} to format Images
- */
-final class ImageBuilder {
-
- private ImageBuilder() {
-
- }
-
- static void formatImage(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
- if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
- final var sortedAttributes = getSortedAttributes(parsedObject);
- String url = null;
- var requestedWidth = 0.0;
- var requestedHeight = 0.0;
- var preserveRatio = false;
- var smooth = false;
- var backgroundLoading = false;
- for (final var property : sortedAttributes) {
- switch (property.name()) {
- case FX_ID -> {
- //Do nothing
- }
- case "url" -> url = formatURL(progress, property.value());
- case "requestedWidth" -> requestedWidth = Double.parseDouble(property.value());
- case "requestedHeight" -> requestedHeight = Double.parseDouble(property.value());
- case "preserveRatio" -> preserveRatio = Boolean.parseBoolean(property.value());
- case "smooth" -> smooth = Boolean.parseBoolean(property.value());
- case "backgroundLoading" -> backgroundLoading = Boolean.parseBoolean(property.value());
- default -> throw new GenerationException("Unknown image attribute : " + property.name());
- }
- }
- final var urlString = progress.getNextVariableName("urlStr");
- progress.stringBuilder().append(START_VAR).append(urlString).append(" = ").append(url).append(".toString();\n");
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = new javafx.scene.image.Image(").append(urlString)
- .append(", ").append(requestedWidth).append(", ").append(requestedHeight).append(", ")
- .append(preserveRatio).append(", ").append(smooth).append(", ").append(backgroundLoading).append(");\n");
- handleId(progress, parsedObject, variableName);
- } else {
- throw new GenerationException("Image cannot have children or properties : " + parsedObject);
- }
- }
-}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageFormatter.java
new file mode 100644
index 0000000..fa0cd3e
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageFormatter.java
@@ -0,0 +1,81 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
+
+/**
+ * Helper methods for {@link GeneratorImpl} to format Images
+ */
+final class ImageFormatter {
+
+ private ImageFormatter() {
+
+ }
+
+ static void formatImage(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
+ if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
+ doFormatImage(progress, parsedObject, variableName);
+ } else {
+ throw new GenerationException("Image cannot have children or properties : " + parsedObject);
+ }
+ }
+
+ private static void formatInputStream(final GenerationProgress progress, final String url, final double requestedWidth,
+ final double requestedHeight, final boolean preserveRatio, final boolean smooth, final String variableName) {
+ final var inputStream = progress.getNextVariableName("inputStream");
+ final var sb = progress.stringBuilder();
+ sb.append(" final javafx.scene.image.Image ").append(variableName).append(";\n");
+ sb.append(" try (").append(GenerationCompatibilityHelper.getStartVar(progress, "java.io.InputStream", 0)).append(inputStream).append(" = ").append(url).append(".openStream()) {\n");
+ sb.append(" ").append(variableName).append(" = new javafx.scene.image.Image(").append(inputStream);
+ sb.append(", ").append(requestedWidth).append(", ").append(requestedHeight).append(", ").append(preserveRatio).append(", ").append(smooth).append(");\n");
+ sb.append(" } catch (final java.io.IOException e) {\n");
+ sb.append(" throw new RuntimeException(e);\n");
+ sb.append(" }\n");
+ }
+
+ private static void doFormatImage(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
+ final var sortedAttributes = getSortedAttributes(parsedObject);
+ String url = null;
+ var requestedWidth = 0.0;
+ var requestedHeight = 0.0;
+ var preserveRatio = false;
+ var smooth = false;
+ var backgroundLoading = false;
+ for (final var property : sortedAttributes) {
+ switch (property.name()) {
+ case FX_ID -> {
+ //Do nothing
+ }
+ case "url" -> url = URLFormatter.formatURL(progress, property.value());
+ case "requestedWidth" -> requestedWidth = Double.parseDouble(property.value());
+ case "requestedHeight" -> requestedHeight = Double.parseDouble(property.value());
+ case "preserveRatio" -> preserveRatio = Boolean.parseBoolean(property.value());
+ case "smooth" -> smooth = Boolean.parseBoolean(property.value());
+ case "backgroundLoading" -> backgroundLoading = Boolean.parseBoolean(property.value());
+ default -> throw new GenerationException("Unknown image attribute : " + property.name());
+ }
+ }
+
+ if (progress.request().parameters().useImageInputStreamConstructor()) {
+ formatInputStream(progress, url, requestedWidth, requestedHeight, preserveRatio, smooth, variableName);
+ } else {
+ formatURL(progress, url, requestedWidth, requestedHeight, preserveRatio, smooth, backgroundLoading, variableName);
+ }
+ GenerationHelper.handleId(progress, parsedObject, variableName);
+ }
+
+ private static void formatURL(final GenerationProgress progress, final String url, final double requestedWidth,
+ final double requestedHeight, final boolean preserveRatio, final boolean smooth,
+ final boolean backgroundLoading, final String variableName) {
+ final var urlString = progress.getNextVariableName("urlStr");
+ final var sb = progress.stringBuilder();
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "String")).append(urlString).append(" = ").append(url).append(".toString();\n");
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.image.Image")).append(variableName).append(" = new javafx.scene.image.Image(").append(urlString)
+ .append(", ").append(requestedWidth).append(", ").append(requestedHeight).append(", ")
+ .append(preserveRatio).append(", ").append(smooth).append(", ").append(backgroundLoading).append(");\n");
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/LoadMethodFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/LoadMethodFormatter.java
new file mode 100644
index 0000000..6606903
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/LoadMethodFormatter.java
@@ -0,0 +1,70 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
+import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
+import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
+
+/**
+ * Formats the load method for the generated code
+ */
+public final class LoadMethodFormatter {
+
+ private LoadMethodFormatter() {
+ }
+
+ /**
+ * Formats the load method
+ *
+ * @param progress The generation progress
+ * @throws GenerationException if an error occurs
+ */
+ public static void formatLoadMethod(final GenerationProgress progress) throws GenerationException {
+ final var request = progress.request();
+ final var rootObject = request.rootObject();
+ final var parameters = progress.request().parameters();
+ final var controllerInjectionType = parameters.fieldInjectionType();
+ final var controllerClass = progress.request().controllerInfo().className();
+ final var sb = progress.stringBuilder();
+ sb.append(" /**\n");
+ sb.append(" * Loads the view. Can only be called once.\n");
+ sb.append(" *\n");
+ sb.append(" * @return The view parent\n");
+ sb.append(" */\n");
+ sb.append(" public T load() {\n");
+ sb.append(" if (loaded) {\n");
+ sb.append(" throw new IllegalStateException(\"Already loaded\");\n");
+ sb.append(" }\n");
+ final var resourceBundleInjection = parameters.resourceInjectionType();
+ if (resourceBundleInjection == ResourceBundleInjectionTypes.CONSTRUCTOR_NAME) {
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"resourceBundleName\");\n");
+ } else if (resourceBundleInjection == ResourceBundleInjectionTypes.GET_BUNDLE && parameters.bundleMap().containsKey(controllerClass)) {
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"").append(parameters.bundleMap().get(controllerClass)).append("\");\n");
+ }
+ if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.Map")).append("fieldMap = new java.util.HashMap();\n");
+ }
+ final var variableName = progress.getNextVariableName(GenerationHelper.getVariablePrefix(rootObject));
+ ObjectFormatter.format(progress, rootObject, variableName);
+ if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
+ sb.append(" controller = (").append(controllerClass).append(") controllerFactory.create(fieldMap);\n");
+ progress.controllerFactoryPostAction().forEach(sb::append);
+ }
+ if (parameters.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) {
+ sb.append(" try {\n");
+ sb.append(" ").append(GenerationCompatibilityHelper.getStartVar(progress, "java.lang.reflect.Method", 0)).append("initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n");
+ sb.append(" initialize.setAccessible(true);\n");
+ sb.append(" initialize.invoke(controller);\n");
+ sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException e) {\n");
+ sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n");
+ sb.append(" } catch (final NoSuchMethodException ignored) {\n");
+ sb.append(" }\n");
+ } else {
+ sb.append(" controller.initialize();\n");
+ }
+ sb.append(" loaded = true;\n");
+ sb.append(" return (T) ").append(variableName).append(";\n");
+ sb.append(" }\n");
+ }
+
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java
index 8f1b971..cdd0796 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java
@@ -15,18 +15,7 @@ import java.util.SequencedCollection;
import java.util.Set;
import java.util.stream.Collectors;
-import static com.github.gtache.fxml.compiler.impl.internal.ConstructorHelper.getListConstructorArgs;
-import static com.github.gtache.fxml.compiler.impl.internal.ConstructorHelper.getMatchingConstructorArgs;
-import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectControllerField;
-import static com.github.gtache.fxml.compiler.impl.internal.FontFormatter.formatFont;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
-import static com.github.gtache.fxml.compiler.impl.internal.ImageBuilder.formatImage;
-import static com.github.gtache.fxml.compiler.impl.internal.PropertyFormatter.formatProperty;
-import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.*;
-import static com.github.gtache.fxml.compiler.impl.internal.SceneBuilder.formatScene;
-import static com.github.gtache.fxml.compiler.impl.internal.TriangleMeshBuilder.formatTriangleMesh;
-import static com.github.gtache.fxml.compiler.impl.internal.URLBuilder.formatURL;
-import static com.github.gtache.fxml.compiler.impl.internal.WebViewBuilder.formatWebView;
/**
* Helper methods for {@link GeneratorImpl} to format properties
@@ -34,7 +23,7 @@ import static com.github.gtache.fxml.compiler.impl.internal.WebViewBuilder.forma
public final class ObjectFormatter {
private static final Logger logger = LogManager.getLogger(ObjectFormatter.class);
-
+
private static final String NEW_ASSIGN = " = new ";
private static final Set BUILDER_CLASSES = Set.of(
@@ -57,7 +46,6 @@ public final class ObjectFormatter {
);
private ObjectFormatter() {
-
}
/**
@@ -72,7 +60,7 @@ public final class ObjectFormatter {
switch (parsedObject) {
case final ParsedConstant constant -> formatConstant(progress, constant, variableName);
case final ParsedCopy copy -> formatCopy(progress, copy, variableName);
- case final ParsedDefine define -> formatDefine(progress, define, variableName);
+ case final ParsedDefine define -> formatDefine(progress, define);
case final ParsedFactory factory -> formatFactory(progress, factory, variableName);
case final ParsedInclude include -> formatInclude(progress, include, variableName);
case final ParsedReference reference -> formatReference(progress, reference, variableName);
@@ -90,7 +78,7 @@ public final class ObjectFormatter {
* @param variableName The variable name
*/
private static void formatText(final GenerationProgress progress, final ParsedText text, final String variableName) {
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = \"").append(text.text()).append("\";\n");
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, "String")).append(variableName).append(" = \"").append(text.text()).append("\";\n");
}
/**
@@ -118,12 +106,13 @@ public final class ObjectFormatter {
private static void formatBuilderObject(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var className = parsedObject.className();
switch (className) {
- case "javafx.scene.Scene" -> formatScene(progress, parsedObject, variableName);
- case "javafx.scene.text.Font" -> formatFont(progress, parsedObject, variableName);
- case "javafx.scene.image.Image" -> formatImage(progress, parsedObject, variableName);
- case "java.net.URL" -> formatURL(progress, parsedObject, variableName);
- case "javafx.scene.shape.TriangleMesh" -> formatTriangleMesh(progress, parsedObject, variableName);
- case "javafx.scene.web.WebView" -> formatWebView(progress, parsedObject, variableName);
+ case "javafx.scene.Scene" -> SceneFormatter.formatScene(progress, parsedObject, variableName);
+ case "javafx.scene.text.Font" -> FontFormatter.formatFont(progress, parsedObject, variableName);
+ case "javafx.scene.image.Image" -> ImageFormatter.formatImage(progress, parsedObject, variableName);
+ case "java.net.URL" -> URLFormatter.formatURL(progress, parsedObject, variableName);
+ case "javafx.scene.shape.TriangleMesh" ->
+ TriangleMeshFormatter.formatTriangleMesh(progress, parsedObject, variableName);
+ case "javafx.scene.web.WebView" -> WebViewFormatter.formatWebView(progress, parsedObject, variableName);
default -> throw new IllegalArgumentException("Unknown builder class : " + className);
}
}
@@ -145,8 +134,8 @@ public final class ObjectFormatter {
}
final var value = getSimpleValue(progress, parsedObject);
final var valueStr = ValueFormatter.toString(value, ReflectionHelper.getClass(parsedObject.className()));
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(valueStr).append(";\n");
- handleId(progress, parsedObject, variableName);
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, parsedObject)).append(variableName).append(" = ").append(valueStr).append(";\n");
+ GenerationHelper.handleId(progress, parsedObject, variableName);
}
private static String getSimpleValue(final GenerationProgress progress, final ParsedObject parsedObject) throws GenerationException {
@@ -217,12 +206,12 @@ public final class ObjectFormatter {
}
}
if (!notDefinedChildren.isEmpty()) {
- final var defaultProperty = getDefaultProperty(parsedObject.className());
+ final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
if (defaultProperty != null) {
allPropertyNames.add(defaultProperty);
}
}
- final var constructorArgs = getMatchingConstructorArgs(constructors, allPropertyNames);
+ final var constructorArgs = ConstructorHelper.getMatchingConstructorArgs(constructors, allPropertyNames);
if (constructorArgs == null) {
formatNoConstructor(progress, parsedObject, variableName, allPropertyNames);
} else {
@@ -234,21 +223,21 @@ public final class ObjectFormatter {
final var clazz = ReflectionHelper.getClass(parsedObject.className());
if (allPropertyNames.size() == 1 && allPropertyNames.iterator().next().equals("fx:constant")) {
final var property = parsedObject.attributes().get("fx:constant");
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(clazz.getCanonicalName()).append(".").append(property.value()).append(";\n");
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, parsedObject)).append(variableName).append(" = ").append(clazz.getCanonicalName()).append(".").append(property.value()).append(";\n");
} else {
throw new GenerationException("Cannot find constructor for " + clazz.getCanonicalName());
}
}
private static void formatConstructor(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName, final ConstructorArgs constructorArgs) throws GenerationException {
- final var args = getListConstructorArgs(constructorArgs, parsedObject);
- final var genericTypes = getGenericTypes(progress, parsedObject);
- progress.stringBuilder().append(START_VAR).append(variableName).append(NEW_ASSIGN).append(parsedObject.className())
+ final var args = ConstructorHelper.getListConstructorArgs(constructorArgs, parsedObject);
+ final var genericTypes = ReflectionHelper.getGenericTypes(progress, parsedObject);
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, parsedObject)).append(variableName).append(NEW_ASSIGN).append(parsedObject.className())
.append(genericTypes).append("(").append(String.join(", ", args)).append(");\n");
final var sortedAttributes = getSortedAttributes(parsedObject);
for (final var value : sortedAttributes) {
if (!constructorArgs.namedArgs().containsKey(value.name())) {
- formatProperty(progress, value, parsedObject, variableName);
+ PropertyFormatter.formatProperty(progress, value, parsedObject, variableName);
}
}
final var sortedProperties = parsedObject.properties().entrySet().stream().sorted(Comparator.comparing(e -> e.getKey().name())).toList();
@@ -261,7 +250,7 @@ public final class ObjectFormatter {
}
final var notDefinedChildren = parsedObject.children().stream().filter(c -> !(c instanceof ParsedDefine)).toList();
if (!notDefinedChildren.isEmpty()) {
- final var defaultProperty = getDefaultProperty(parsedObject.className());
+ final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
if (!constructorArgs.namedArgs().containsKey(defaultProperty)) {
final var property = new ParsedPropertyImpl(defaultProperty, null, null);
formatChild(progress, parsedObject, property, notDefinedChildren, variableName);
@@ -278,26 +267,8 @@ public final class ObjectFormatter {
*/
private static void formatInclude(final GenerationProgress progress, final ParsedInclude include, final String subNodeName) throws GenerationException {
final var subViewVariable = progress.getNextVariableName("view");
- final var source = include.source();
- final var resources = include.resources();
- final var request = progress.request();
- final var subControllerClass = request.parameters().sourceToControllerName().get(source);
- final var subClassName = request.parameters().sourceToGeneratedClassName().get(source);
- if (subClassName == null) {
- throw new GenerationException("Unknown include source : " + source);
- }
- final var sb = progress.stringBuilder();
- if (resources == null) {
- sb.append(START_VAR).append(subViewVariable).append(NEW_ASSIGN).append(subClassName).append("(controllersMap, resourceBundlesMap);\n");
- } else {
- final var subResourceBundlesMapVariable = progress.getNextVariableName("map");
- final var subBundleVariable = progress.getNextVariableName("bundle");
- sb.append(START_VAR).append(subResourceBundlesMapVariable).append(" = new HashMap<>(resourceBundlesMap);\n");
- sb.append(START_VAR).append(subBundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(resources).append("\");\n");
- sb.append(" ").append(subResourceBundlesMapVariable).append(".put(").append(subControllerClass).append(", ").append(subBundleVariable).append(");\n");
- sb.append(START_VAR).append(subViewVariable).append(NEW_ASSIGN).append(subClassName).append("(controllersMap, ").append(subResourceBundlesMapVariable).append(");\n");
- }
- sb.append(" final javafx.scene.Parent ").append(subNodeName).append(" = ").append(subViewVariable).append(".load();\n");
+ final var viewVariable = ConstructorFormatter.formatSubViewConstructorCall(progress, include);
+ progress.stringBuilder().append(" final javafx.scene.Parent ").append(subNodeName).append(" = ").append(viewVariable).append(".load();\n");
injectSubController(progress, include, subViewVariable);
}
@@ -305,13 +276,13 @@ public final class ObjectFormatter {
final var id = include.controllerId();
if (id != null) {
final var subControllerVariable = progress.getNextVariableName("controller");
- progress.stringBuilder().append(START_VAR).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
- progress.idToVariableName().put(id, subControllerVariable);
- progress.idToObject().put(id, include);
+ final var controllerClass = progress.request().sourceInfo().sourceToSourceInfo().get(include.source()).controllerClassName();
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, controllerClass)).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
+ progress.idToVariableInfo().put(id, new VariableInfo(id, include, subControllerVariable, controllerClass));
if (progress.request().controllerInfo().fieldInfo(id) == null) {
logger.debug("Not injecting {} because it is not found in controller", id);
} else {
- injectControllerField(progress, id, subControllerVariable);
+ ControllerInjector.injectControllerField(progress, id, subControllerVariable);
}
}
}
@@ -319,13 +290,14 @@ public final class ObjectFormatter {
/**
* Formats a fx:define
*
- * @param progress The generation progress
- * @param define The parsed define
- * @param variableName The variable name
+ * @param progress The generation progress
+ * @param define The parsed define
* @throws GenerationException if an error occurs
*/
- private static void formatDefine(final GenerationProgress progress, final ParsedDefine define, final String variableName) throws GenerationException {
- formatObject(progress, define.object(), variableName);
+ private static void formatDefine(final GenerationProgress progress, final ParsedObject define) throws GenerationException {
+ for (final var child : define.children()) {
+ format(progress, child, progress.getNextVariableName("definedObject"));
+ }
}
/**
@@ -337,11 +309,12 @@ public final class ObjectFormatter {
*/
private static void formatReference(final GenerationProgress progress, final ParsedReference reference, final String variableName) throws GenerationException {
final var id = reference.source();
- final var variable = progress.idToVariableName().get(id);
- if (variable == null) {
+ final var variableInfo = progress.idToVariableInfo().get(id);
+ if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id);
}
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(variable).append(";\n");
+ final var referenceName = variableInfo.variableName();
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, variableInfo.className())).append(variableName).append(" = ").append(referenceName).append(";\n");
}
/**
@@ -354,12 +327,12 @@ public final class ObjectFormatter {
*/
private static void formatCopy(final GenerationProgress progress, final ParsedCopy copy, final String variableName) throws GenerationException {
final var id = copy.source();
- final var variable = progress.idToVariableName().get(id);
- final var object = progress.idToObject().get(id);
- if (variable == null || object == null) {
+ final var variableInfo = progress.idToVariableInfo().get(id);
+ if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id);
}
- progress.stringBuilder().append(START_VAR).append(variableName).append(NEW_ASSIGN).append(object.className()).append("(").append(variable).append(");\n");
+ final var copyVariable = variableInfo.variableName();
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, variableInfo.className())).append(variableName).append(NEW_ASSIGN).append(variableInfo.className()).append("(").append(copyVariable).append(");\n");
}
/**
@@ -370,7 +343,7 @@ public final class ObjectFormatter {
* @param variableName The variable name
*/
private static void formatConstant(final GenerationProgress progress, final ParsedConstant constant, final String variableName) {
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(constant.className()).append(".").append(constant.constant()).append(";\n");
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, constant.className())).append(variableName).append(" = ").append(constant.className()).append(".").append(constant.constant()).append(";\n");
}
/**
@@ -381,7 +354,7 @@ public final class ObjectFormatter {
* @param variableName The variable name
*/
private static void formatValue(final GenerationProgress progress, final ParsedValue value, final String variableName) {
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(value.className()).append(".valueOf(\"").append(value.value()).append("\");\n");
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, value.className())).append(variableName).append(" = ").append(value.className()).append(".valueOf(\"").append(value.value()).append("\");\n");
}
/**
@@ -398,8 +371,14 @@ public final class ObjectFormatter {
variables.add(argumentVariable);
format(progress, argument, argumentVariable);
}
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(factory.className())
- .append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
+ if (progress.request().parameters().compatibility().useVar()) {
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, factory.className())).append(variableName).append(" = ").append(factory.className())
+ .append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
+ } else {
+ final var returnType = ReflectionHelper.getReturnType(factory.className(), factory.factory());
+ progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, returnType)).append(variableName).append(" = ").append(factory.className())
+ .append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
+ }
}
/**
@@ -416,7 +395,7 @@ public final class ObjectFormatter {
final var propertyName = property.name();
final var variables = new ArrayList();
for (final var object : objects) {
- final var vn = progress.getNextVariableName(getVariablePrefix(object));
+ final var vn = progress.getNextVariableName(GenerationHelper.getVariablePrefix(object));
format(progress, object, vn);
if (!(object instanceof ParsedDefine)) {
variables.add(vn);
@@ -441,9 +420,9 @@ public final class ObjectFormatter {
*/
private static void formatMultipleChildren(final GenerationProgress progress, final Iterable variables, final String propertyName, final ParsedObject parent,
final String parentVariable) throws GenerationException {
- final var getMethod = getGetMethod(propertyName);
- if (hasMethod(ReflectionHelper.getClass(parent.className()), getMethod)) {
- progress.stringBuilder().append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(java.util.List.of(").append(String.join(", ", variables)).append("));\n");
+ final var getMethod = GenerationHelper.getGetMethod(propertyName);
+ if (ReflectionHelper.hasMethod(ReflectionHelper.getClass(parent.className()), getMethod)) {
+ progress.stringBuilder().append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(").append(GenerationCompatibilityHelper.getListOf(progress)).append(String.join(", ", variables)).append("));\n");
} else {
throw getCannotSetException(propertyName, parent.className());
}
@@ -479,15 +458,15 @@ public final class ObjectFormatter {
private static void formatSingleChildInstance(final GenerationProgress progress, final String variableName,
final ParsedProperty property, final ParsedObject parent,
final String parentVariable) throws GenerationException {
- final var setMethod = getSetMethod(property);
- final var getMethod = getGetMethod(property);
+ final var setMethod = GenerationHelper.getSetMethod(property);
+ final var getMethod = GenerationHelper.getGetMethod(property);
final var parentClass = ReflectionHelper.getClass(parent.className());
final var sb = progress.stringBuilder();
- if (hasMethod(parentClass, setMethod)) {
- sb.append(" ").append(parentVariable).append(".").append(setMethod).append("(").append(variableName).append(");\n");
- } else if (hasMethod(parentClass, getMethod)) {
+ if (ReflectionHelper.hasMethod(parentClass, setMethod)) {
+ sb.append(" ").append(parentVariable).append(".").append(setMethod).append("(").append(variableName).append(");\n");
+ } else if (ReflectionHelper.hasMethod(parentClass, getMethod)) {
//Probably a list method that has only one element
- sb.append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(java.util.List.of(").append(variableName).append("));\n");
+ sb.append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(").append(GenerationCompatibilityHelper.getListOf(progress)).append(variableName).append("));\n");
} else {
throw getCannotSetException(property.name(), parent.className());
}
@@ -503,9 +482,9 @@ public final class ObjectFormatter {
*/
private static void formatSingleChildStatic(final GenerationProgress progress, final String variableName,
final ParsedProperty property, final String parentVariable) throws GenerationException {
- final var setMethod = getSetMethod(property);
- if (hasStaticMethod(ReflectionHelper.getClass(property.sourceType()), setMethod)) {
- progress.stringBuilder().append(" ").append(property.sourceType()).append(".").append(setMethod)
+ final var setMethod = GenerationHelper.getSetMethod(property);
+ if (ReflectionHelper.hasStaticMethod(ReflectionHelper.getClass(property.sourceType()), setMethod)) {
+ progress.stringBuilder().append(" ").append(property.sourceType()).append(".").append(setMethod)
.append("(").append(parentVariable).append(", ").append(variableName).append(");\n");
} else {
throw getCannotSetException(property.name(), property.sourceType());
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/Parameter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/Parameter.java
index eca8634..e81cfc1 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/Parameter.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/Parameter.java
@@ -19,7 +19,7 @@ record Parameter(String name, Class> type, String defaultValue) {
* @param defaultValue The parameter default value
* @throws NullPointerException if any parameter is null
*/
- public Parameter {
+ Parameter {
requireNonNull(name);
requireNonNull(type);
requireNonNull(defaultValue);
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java
index 3dd6d63..411bacd 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java
@@ -10,18 +10,15 @@ import javafx.event.EventHandler;
import java.util.Objects;
-import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectEventHandlerControllerMethod;
-import static com.github.gtache.fxml.compiler.impl.internal.FieldSetter.setEventHandler;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
-import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.*;
-import static com.github.gtache.fxml.compiler.impl.internal.ValueFormatter.getArg;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.RESOURCE_KEY_PREFIX;
/**
* Helper methods for {@link GeneratorImpl} to format properties
*/
final class PropertyFormatter {
- private PropertyFormatter() {
+ private PropertyFormatter() {
}
/**
@@ -36,7 +33,7 @@ final class PropertyFormatter {
static void formatProperty(final GenerationProgress progress, final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException {
final var propertyName = property.name();
if (propertyName.equals(FX_ID)) {
- handleId(progress, parent, parentVariable);
+ GenerationHelper.handleId(progress, parent, parentVariable);
} else if (propertyName.equals("fx:controller")) {
checkDuplicateController(progress, parent);
} else if (Objects.equals(property.sourceType(), EventHandler.class.getName())) {
@@ -56,20 +53,20 @@ final class PropertyFormatter {
private static void handleEventHandler(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) throws GenerationException {
if (property.value().startsWith("#")) {
- injectEventHandlerControllerMethod(progress, property, parentVariable);
+ ControllerInjector.injectEventHandlerControllerMethod(progress, property, parentVariable);
} else {
- setEventHandler(progress, property, parentVariable);
+ FieldSetter.setEventHandler(progress, property, parentVariable);
}
}
private static void handleStaticProperty(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String propertyName) throws GenerationException {
- final var setMethod = getSetMethod(propertyName);
+ final var setMethod = GenerationHelper.getSetMethod(propertyName);
final var propertySourceTypeClass = ReflectionHelper.getClass(property.sourceType());
- if (hasStaticMethod(propertySourceTypeClass, setMethod)) {
- final var method = getStaticMethod(propertySourceTypeClass, setMethod);
+ if (ReflectionHelper.hasStaticMethod(propertySourceTypeClass, setMethod)) {
+ final var method = ReflectionHelper.getStaticMethod(propertySourceTypeClass, setMethod);
final var parameterType = method.getParameterTypes()[1];
- final var arg = getArg(progress, property.value(), parameterType);
- setLaterIfNeeded(progress, property, parameterType, " " + property.sourceType() + "." + setMethod + "(" + parentVariable + ", " + arg + ");\n");
+ final var arg = ValueFormatter.getArg(progress, property.value(), parameterType);
+ setLaterIfNeeded(progress, property, parameterType, " " + property.sourceType() + "." + setMethod + "(" + parentVariable + ", " + arg + ");\n");
} else {
throw new GenerationException("Cannot set " + propertyName + " on " + property.sourceType());
}
@@ -77,12 +74,12 @@ final class PropertyFormatter {
private static void handleProperty(final GenerationProgress progress, final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException {
final var propertyName = property.name();
- final var setMethod = getSetMethod(propertyName);
- final var getMethod = getGetMethod(propertyName);
+ final var setMethod = GenerationHelper.getSetMethod(propertyName);
+ final var getMethod = GenerationHelper.getGetMethod(propertyName);
final var parentClass = ReflectionHelper.getClass(parent.className());
- if (hasMethod(parentClass, setMethod)) {
+ if (ReflectionHelper.hasMethod(parentClass, setMethod)) {
handleSetProperty(progress, property, parentClass, parentVariable);
- } else if (hasMethod(parentClass, getMethod)) {
+ } else if (ReflectionHelper.hasMethod(parentClass, getMethod)) {
handleGetProperty(progress, property, parentClass, parentVariable);
} else {
throw new GenerationException("Cannot set " + propertyName + " on " + parent.className());
@@ -90,20 +87,20 @@ final class PropertyFormatter {
}
private static void handleSetProperty(final GenerationProgress progress, final ParsedProperty property, final Class> parentClass, final String parentVariable) throws GenerationException {
- final var setMethod = getSetMethod(property.name());
- final var method = getMethod(parentClass, setMethod);
+ final var setMethod = GenerationHelper.getSetMethod(property.name());
+ final var method = ReflectionHelper.getMethod(parentClass, setMethod);
final var parameterType = method.getParameterTypes()[0];
- final var arg = getArg(progress, property.value(), parameterType);
- setLaterIfNeeded(progress, property, parameterType, " " + parentVariable + "." + setMethod + "(" + arg + ");\n");
+ final var arg = ValueFormatter.getArg(progress, property.value(), parameterType);
+ setLaterIfNeeded(progress, property, parameterType, " " + parentVariable + "." + setMethod + "(" + arg + ");\n");
}
private static void handleGetProperty(final GenerationProgress progress, final ParsedProperty property, final Class> parentClass, final String parentVariable) throws GenerationException {
- final var getMethod = getGetMethod(property.name());
- final var method = getMethod(parentClass, getMethod);
+ final var getMethod = GenerationHelper.getGetMethod(property.name());
+ final var method = ReflectionHelper.getMethod(parentClass, getMethod);
final var returnType = method.getReturnType();
- if (hasMethod(returnType, "addAll")) {
- final var arg = getArg(progress, property.value(), String.class);
- setLaterIfNeeded(progress, property, String.class, " " + parentVariable + "." + getMethod + "().addAll(java.util.List.of(" + arg + "));\n");
+ if (ReflectionHelper.hasMethod(returnType, "addAll")) {
+ final var arg = ValueFormatter.getArg(progress, property.value(), String.class);
+ setLaterIfNeeded(progress, property, String.class, " " + parentVariable + "." + getMethod + "().addAll(" + GenerationCompatibilityHelper.getListOf(progress) + arg + "));\n");
}
}
@@ -114,11 +111,11 @@ final class PropertyFormatter {
* @param property The property
* @param type The type
* @param arg The argument
- * @throws GenerationException if an error occurs
*/
- private static void setLaterIfNeeded(final GenerationProgress progress, final ParsedProperty property, final Class> type, final String arg) throws GenerationException {
- if (type == String.class && property.value().startsWith("%") && progress.request().parameters().resourceBundleInjection().injectionType() == ResourceBundleInjectionTypes.GETTER
- && getControllerInjection(progress).fieldInjectionType() == ControllerFieldInjectionTypes.FACTORY) {
+ private static void setLaterIfNeeded(final GenerationProgress progress, final ParsedProperty property, final Class> type, final String arg) {
+ final var parameters = progress.request().parameters();
+ if (type == String.class && property.value().startsWith(RESOURCE_KEY_PREFIX) && parameters.resourceInjectionType() == ResourceBundleInjectionTypes.GETTER
+ && parameters.fieldInjectionType() == ControllerFieldInjectionTypes.FACTORY) {
progress.controllerFactoryPostAction().add(arg);
} else {
progress.stringBuilder().append(arg);
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ReflectionHelper.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ReflectionHelper.java
index 0782625..1ad8f87 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ReflectionHelper.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ReflectionHelper.java
@@ -1,6 +1,7 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.GenericTypes;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import javafx.beans.DefaultProperty;
import javafx.beans.NamedArg;
@@ -8,11 +9,14 @@ import javafx.scene.Node;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -24,11 +28,23 @@ import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_
*/
final class ReflectionHelper {
private static final Logger logger = LogManager.getLogger(ReflectionHelper.class);
- private static final Map, Boolean> HAS_VALUE_OF = new ConcurrentHashMap<>();
- private static final Map, Boolean> IS_GENERIC = new ConcurrentHashMap<>();
- private static final Map DEFAULT_PROPERTY = new ConcurrentHashMap<>();
- private static final Map, Map> METHODS = new ConcurrentHashMap<>();
- private static final Map, Map> STATIC_METHODS = new ConcurrentHashMap<>();
+ private static final Map> classMap = new HashMap<>();
+ private static final Map, Boolean> hasValueOf = new HashMap<>();
+ private static final Map, Boolean> isGeneric = new HashMap<>();
+ private static final Map defaultProperty = new HashMap<>();
+ private static final Map, Map> methods = new HashMap<>();
+ private static final Map, Map> staticMethods = new HashMap<>();
+
+ private static final Map> PRIMITIVE_TYPES = Map.of(
+ "boolean", boolean.class,
+ "byte", byte.class,
+ "char", char.class,
+ "short", short.class,
+ "int", int.class,
+ "long", long.class,
+ "float", float.class,
+ "double", double.class
+ );
private ReflectionHelper() {
}
@@ -41,7 +57,7 @@ final class ReflectionHelper {
* @return True if the class is generic
*/
static boolean isGeneric(final Class> clazz) {
- return IS_GENERIC.computeIfAbsent(clazz, c -> c.getTypeParameters().length > 0);
+ return isGeneric.computeIfAbsent(clazz, c -> c.getTypeParameters().length > 0);
}
/**
@@ -53,7 +69,7 @@ final class ReflectionHelper {
* @return True if the class has a method with the given name
*/
static boolean hasMethod(final Class> clazz, final String methodName) {
- final var methodMap = METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
+ final var methodMap = methods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var method = methodMap.computeIfAbsent(methodName, m -> computeMethod(clazz, m));
return method != null;
}
@@ -67,7 +83,7 @@ final class ReflectionHelper {
* @return The method
*/
static Method getMethod(final Class> clazz, final String methodName) {
- final var methodMap = METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
+ final var methodMap = methods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
return methodMap.computeIfAbsent(methodName, m -> computeMethod(clazz, m));
}
@@ -110,7 +126,7 @@ final class ReflectionHelper {
* @return True if the class has a static method with the given name
*/
static boolean hasStaticMethod(final Class> clazz, final String methodName) {
- final var methodMap = STATIC_METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
+ final var methodMap = staticMethods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var method = methodMap.computeIfAbsent(methodName, m -> computeStaticMethod(clazz, m));
return method != null;
}
@@ -124,7 +140,7 @@ final class ReflectionHelper {
* @return The method
*/
static Method getStaticMethod(final Class> clazz, final String methodName) {
- final var methodMap = STATIC_METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
+ final var methodMap = staticMethods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
return methodMap.computeIfAbsent(methodName, m -> computeStaticMethod(clazz, m));
}
@@ -161,7 +177,7 @@ final class ReflectionHelper {
* @return True if the class has a valueOf(String)
*/
static boolean hasValueOf(final Class> clazz) {
- return HAS_VALUE_OF.computeIfAbsent(clazz, ReflectionHelper::computeHasValueOf);
+ return hasValueOf.computeIfAbsent(clazz, ReflectionHelper::computeHasValueOf);
}
/**
@@ -216,14 +232,14 @@ final class ReflectionHelper {
* @throws GenerationException If the class is not found or no default property is found
*/
static String getDefaultProperty(final String className) throws GenerationException {
- if (DEFAULT_PROPERTY.containsKey(className)) {
- return DEFAULT_PROPERTY.get(className);
+ if (defaultProperty.containsKey(className)) {
+ return defaultProperty.get(className);
} else {
- final var defaultProperty = computeDefaultProperty(className);
- if (defaultProperty != null) {
- DEFAULT_PROPERTY.put(className, defaultProperty);
+ final var property = computeDefaultProperty(className);
+ if (property != null) {
+ defaultProperty.put(className, property);
}
- return defaultProperty;
+ return property;
}
}
@@ -238,7 +254,7 @@ final class ReflectionHelper {
if (name.contains(".") || Character.isUpperCase(name.charAt(0))) {
return name;
} else {
- return name.substring(0, 1).toUpperCase() + name.substring(1);
+ return MethodType.methodType(clazz).wrap().returnType().getName();
}
}
@@ -249,11 +265,25 @@ final class ReflectionHelper {
* @return The class
* @throws GenerationException If the class is not found
*/
- public static Class> getClass(final String className) throws GenerationException {
- try {
- return Class.forName(className, false, Thread.currentThread().getContextClassLoader());
- } catch (final ClassNotFoundException e) {
- throw new GenerationException("Cannot find class " + className + " ; Is a dependency missing for the plugin?", e);
+ static Class> getClass(final String className) throws GenerationException {
+ if (classMap.containsKey(className)) {
+ return classMap.get(className);
+ } else {
+ final var clazz = computeClass(className);
+ classMap.put(className, clazz);
+ return clazz;
+ }
+ }
+
+ private static Class> computeClass(final String className) throws GenerationException {
+ if (PRIMITIVE_TYPES.containsKey(className)) {
+ return PRIMITIVE_TYPES.get(className);
+ } else {
+ try {
+ return Class.forName(className, false, Thread.currentThread().getContextClassLoader());
+ } catch (final ClassNotFoundException e) {
+ throw new GenerationException("Cannot find class " + className + " ; Is a dependency missing for the plugin?", e);
+ }
}
}
@@ -312,10 +342,55 @@ final class ReflectionHelper {
logger.warn("No field found for generic class {} (id={}) ; Using raw", clazz.getName(), id);
return "";
} else if (fieldInfo.isGeneric()) {
- return "<" + String.join(", ", fieldInfo.genericTypes()) + ">";
+ return formatGenerics(fieldInfo.genericTypes());
}
}
}
return "";
}
+
+ private static String formatGenerics(final List extends GenericTypes> genericTypes) {
+ final var sb = new StringBuilder();
+ sb.append("<");
+ for (var i = 0; i < genericTypes.size(); i++) {
+ final var genericType = genericTypes.get(i);
+ sb.append(genericType.name());
+ formatGenerics(genericType.subTypes(), sb);
+ if (i < genericTypes.size() - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append(">");
+ return sb.toString();
+ }
+
+ private static void formatGenerics(final List extends GenericTypes> genericTypes, final StringBuilder sb) {
+ if (!genericTypes.isEmpty()) {
+ sb.append("<");
+ for (var i = 0; i < genericTypes.size(); i++) {
+ final var genericType = genericTypes.get(i);
+ sb.append(genericType.name());
+ formatGenerics(genericType.subTypes(), sb);
+ if (i < genericTypes.size() - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append(">");
+ }
+ }
+
+ /**
+ * Gets the return type of the given method for the given class
+ *
+ * @param className The class
+ * @param methodName The method
+ * @return The return type
+ * @throws GenerationException if an error occurs
+ */
+ static String getReturnType(final String className, final String methodName) throws GenerationException {
+ final var clazz = getClass(className);
+ final var method = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals(methodName))
+ .findFirst().orElseThrow(() -> new GenerationException("Method " + methodName + " not found in class " + className));
+ return method.getReturnType().getName();
+ }
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneBuilder.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneFormatter.java
similarity index 75%
rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneBuilder.java
rename to core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneFormatter.java
index 76fc74f..544d955 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneBuilder.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneFormatter.java
@@ -8,23 +8,21 @@ import javafx.scene.paint.Color;
import java.util.ArrayList;
import java.util.Collection;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
-import static com.github.gtache.fxml.compiler.impl.internal.ObjectFormatter.format;
-import static com.github.gtache.fxml.compiler.impl.internal.URLBuilder.formatURL;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format Scenes
*/
-final class SceneBuilder {
-
- private SceneBuilder() {
+final class SceneFormatter {
+ private SceneFormatter() {
}
static void formatScene(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var root = findRoot(parsedObject);
final var rootVariableName = progress.getNextVariableName("root");
- format(progress, root, rootVariableName);
+ ObjectFormatter.format(progress, root, rootVariableName);
final var sortedAttributes = getSortedAttributes(parsedObject);
double width = -1;
double height = -1;
@@ -43,10 +41,10 @@ final class SceneBuilder {
}
}
final var sb = progress.stringBuilder();
- sb.append(START_VAR).append(variableName).append(" = new javafx.scene.Scene(").append(rootVariableName).append(", ")
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.Scene")).append(variableName).append(" = new javafx.scene.Scene(").append(rootVariableName).append(", ")
.append(width).append(", ").append(height).append(", javafx.scene.paint.Color.valueOf(\"").append(paint).append("\"));\n");
addStylesheets(progress, variableName, stylesheets);
- handleId(progress, parsedObject, variableName);
+ GenerationHelper.handleId(progress, parsedObject, variableName);
}
private static ParsedObject findRoot(final ParsedObject parsedObject) throws GenerationException {
@@ -64,14 +62,11 @@ final class SceneBuilder {
private static void addStylesheets(final GenerationProgress progress, final String variableName, final Collection stylesheets) {
if (!stylesheets.isEmpty()) {
- final var urlVariables = formatURL(progress, stylesheets);
+ final var urlVariables = URLFormatter.formatURL(progress, stylesheets);
final var tmpVariable = progress.getNextVariableName("stylesheets");
final var sb = progress.stringBuilder();
- sb.append("""
- final var %1$s = %2$s.getStylesheets();
- %1$s.addAll(java.util.List.of(%3$s));
- """.formatted(tmpVariable, variableName, String.join(", ", urlVariables)));
- stylesheets.forEach(s -> sb.append(" ").append(variableName).append(".getStyleSheets().add(\"").append(s).append("\");\n"));
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.List")).append(tmpVariable).append(" = ").append(variableName).append(".getStyleSheets();\n");
+ sb.append(" ").append(tmpVariable).append(".addAll(").append(GenerationCompatibilityHelper.getListOf(progress)).append(String.join(", ", urlVariables)).append("));\n");
}
}
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshBuilder.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshFormatter.java
similarity index 78%
rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshBuilder.java
rename to core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshFormatter.java
index cedd1e2..d904079 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshBuilder.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshFormatter.java
@@ -14,15 +14,15 @@ import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format TriangleMeshes
*/
-final class TriangleMeshBuilder {
-
- private TriangleMeshBuilder() {
+final class TriangleMeshFormatter {
+ private TriangleMeshFormatter() {
}
static void formatTriangleMesh(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
@@ -64,14 +64,14 @@ final class TriangleMeshBuilder {
}
}
final var sb = progress.stringBuilder();
- sb.append(START_VAR).append(variableName).append(" = new javafx.scene.shape.TriangleMesh();\n");
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.shape.TriangleMesh")).append(variableName).append(" = new javafx.scene.shape.TriangleMesh();\n");
setPoints(progress, variableName, points);
setTexCoords(progress, variableName, texCoords);
setNormals(progress, variableName, normals);
setFaces(progress, variableName, faces);
setFaceSmoothingGroups(progress, variableName, faceSmoothingGroups);
setVertexFormat(progress, variableName, vertexFormat);
- handleId(progress, parsedObject, variableName);
+ GenerationHelper.handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("Image cannot have children or properties : " + parsedObject);
}
@@ -89,37 +89,37 @@ final class TriangleMeshBuilder {
private static void setPoints(final GenerationProgress progress, final String variableName, final Collection points) {
if (!points.isEmpty()) {
- progress.stringBuilder().append(" ").append(variableName).append(".getPoints().setAll(new float[]{").append(formatList(points)).append("});\n");
+ progress.stringBuilder().append(" ").append(variableName).append(".getPoints().setAll(new float[]{").append(formatList(points)).append("});\n");
}
}
private static void setTexCoords(final GenerationProgress progress, final String variableName, final Collection texCoords) {
if (!texCoords.isEmpty()) {
- progress.stringBuilder().append(" ").append(variableName).append(".getTexCoords().setAll(new float[]{").append(formatList(texCoords)).append("});\n");
+ progress.stringBuilder().append(" ").append(variableName).append(".getTexCoords().setAll(new float[]{").append(formatList(texCoords)).append("});\n");
}
}
private static void setNormals(final GenerationProgress progress, final String variableName, final Collection normals) {
if (!normals.isEmpty()) {
- progress.stringBuilder().append(" ").append(variableName).append(".getNormals().setAll(new float[]{").append(formatList(normals)).append("});\n");
+ progress.stringBuilder().append(" ").append(variableName).append(".getNormals().setAll(new float[]{").append(formatList(normals)).append("});\n");
}
}
private static void setFaces(final GenerationProgress progress, final String variableName, final Collection faces) {
if (!faces.isEmpty()) {
- progress.stringBuilder().append(" ").append(variableName).append(".getFaces().setAll(new int[]{").append(formatList(faces)).append("});\n");
+ progress.stringBuilder().append(" ").append(variableName).append(".getFaces().setAll(new int[]{").append(formatList(faces)).append("});\n");
}
}
private static void setFaceSmoothingGroups(final GenerationProgress progress, final String variableName, final Collection faceSmoothingGroups) {
if (!faceSmoothingGroups.isEmpty()) {
- progress.stringBuilder().append(" ").append(variableName).append(".getFaceSmoothingGroups().setAll(new int[]{").append(formatList(faceSmoothingGroups)).append("});\n");
+ progress.stringBuilder().append(" ").append(variableName).append(".getFaceSmoothingGroups().setAll(new int[]{").append(formatList(faceSmoothingGroups)).append("});\n");
}
}
private static void setVertexFormat(final GenerationProgress progress, final String variableName, final VertexFormat vertexFormat) {
if (vertexFormat != null) {
- progress.stringBuilder().append(" ").append(variableName).append(".setVertexFormat(javafx.scene.shape.VertexFormat.").append(vertexFormat).append(");\n");
+ progress.stringBuilder().append(" ").append(variableName).append(".setVertexFormat(javafx.scene.shape.VertexFormat.").append(vertexFormat).append(");\n");
}
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLBuilder.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLFormatter.java
similarity index 62%
rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLBuilder.java
rename to core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLFormatter.java
index e59b9a9..342ca63 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLBuilder.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLFormatter.java
@@ -9,13 +9,13 @@ import java.util.List;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
+
/**
* Helper methods for {@link GeneratorImpl} to format URLs
*/
-final class URLBuilder {
-
- private URLBuilder() {
+final class URLFormatter {
+ private URLFormatter() {
}
static List formatURL(final GenerationProgress progress, final Collection stylesheets) {
@@ -25,17 +25,15 @@ final class URLBuilder {
static String formatURL(final GenerationProgress progress, final String url) {
final var variableName = progress.getNextVariableName("url");
final var sb = progress.stringBuilder();
- if (url.startsWith("@")) {
- sb.append(START_VAR).append(variableName).append(" = getClass().getResource(\"").append(url.substring(1)).append("\");\n");
+ if (url.startsWith(RELATIVE_PATH_PREFIX)) {
+ sb.append(getStartURL(progress)).append(variableName).append(" = getClass().getResource(\"").append(url.substring(1)).append("\");\n");
} else {
- sb.append("""
- final java.net.URL %1$s;
- try {
- %1$s = new java.net.URI("%2$s").toURL();
- } catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {
- throw new RuntimeException("Couldn't parse url : %2$s", e);
- }
- """.formatted(variableName, url));
+ sb.append(" final java.net.URL ").append(variableName).append(";\n");
+ sb.append(" try {\n");
+ sb.append(" ").append(variableName).append(" = new java.net.URI(\"").append(url).append("\").toURL();\n");
+ sb.append(" } catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {\n");
+ sb.append(" throw new RuntimeException(\"Couldn't parse url : ").append(url).append("\", e);\n");
+ sb.append(" }\n");
}
return variableName;
}
@@ -53,10 +51,14 @@ final class URLBuilder {
default -> throw new GenerationException("Unknown URL attribute : " + property.name());
}
}
- progress.stringBuilder().append(START_VAR).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n");
+ progress.stringBuilder().append(getStartURL(progress)).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n");
handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("URL cannot have children or properties : " + parsedObject);
}
}
+
+ private static String getStartURL(final GenerationProgress progress) {
+ return GenerationCompatibilityHelper.getStartVar(progress, "java.net.URL");
+ }
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java
index 1f1c714..d8945cf 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java
@@ -6,8 +6,7 @@ import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import java.util.regex.Pattern;
-import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.getWrapperClass;
-import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.hasValueOf;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
/**
* Helper methods for {@link GeneratorImpl} to format values
@@ -31,19 +30,19 @@ final class ValueFormatter {
* @throws GenerationException if an error occurs
*/
static String getArg(final GenerationProgress progress, final String value, final Class> parameterType) throws GenerationException {
- if (parameterType == String.class && value.startsWith("%")) {
+ if (parameterType == String.class && value.startsWith(RESOURCE_KEY_PREFIX)) {
return getBundleValue(progress, value.substring(1));
- } else if (value.startsWith("@")) {
+ } else if (value.startsWith(RELATIVE_PATH_PREFIX)) {
final var subpath = value.substring(1);
return getResourceValue(subpath);
- } else if (value.startsWith("${")) {
+ } else if (value.startsWith(BINDING_EXPRESSION_PREFIX)) {
throw new UnsupportedOperationException("Not implemented yet");
- } else if (value.startsWith("$")) {
- final var variable = progress.idToVariableName().get(value.substring(1));
+ } else if (value.startsWith(EXPRESSION_PREFIX)) {
+ final var variable = progress.idToVariableInfo().get(value.substring(1));
if (variable == null) {
throw new GenerationException("Unknown variable : " + value.substring(1));
}
- return variable;
+ return variable.variableName();
} else {
return toString(value, parameterType);
}
@@ -62,11 +61,12 @@ final class ValueFormatter {
* @throws GenerationException if an error occurs
*/
private static String getBundleValue(final GenerationProgress progress, final String value) throws GenerationException {
- final var resourceBundleInjectionType = progress.request().parameters().resourceBundleInjection().injectionType();
+ final var resourceBundleInjectionType = progress.request().parameters().resourceInjectionType();
if (resourceBundleInjectionType instanceof final ResourceBundleInjectionTypes types) {
return switch (types) {
- case CONSTRUCTOR, GET_BUNDLE -> "bundle.getString(\"" + value + "\")";
+ case CONSTRUCTOR, GET_BUNDLE, CONSTRUCTOR_NAME -> "resourceBundle.getString(\"" + value + "\")";
case GETTER -> "controller.resources().getString(\"" + value + "\")";
+ case CONSTRUCTOR_FUNCTION -> "resourceBundleFunction.apply(\"" + value + "\")";
};
} else {
throw new GenerationException("Unknown resource bundle injection type : " + resourceBundleInjectionType);
@@ -90,28 +90,40 @@ final class ValueFormatter {
return value;
} else if (clazz == byte.class || clazz == Byte.class || clazz == short.class || clazz == Short.class ||
clazz == int.class || clazz == Integer.class || clazz == long.class || clazz == Long.class) {
- if (INT_PATTERN.matcher(value).matches()) {
- return value;
- } else {
- return getValueOf(getWrapperClass(clazz), value);
- }
+ return intToString(value, clazz);
} else if (clazz == float.class || clazz == Float.class || clazz == double.class || clazz == Double.class) {
- if (DECIMAL_PATTERN.matcher(value).matches()) {
- return value;
- } else {
- return getValueOf(getWrapperClass(clazz), value);
- }
- } else if (hasValueOf(clazz)) {
- if (clazz.isEnum()) {
- return clazz.getCanonicalName() + "." + value;
- } else {
- return getValueOf(clazz.getCanonicalName(), value);
- }
+ return decimalToString(value, clazz);
+ } else if (ReflectionHelper.hasValueOf(clazz)) {
+ return valueOfToString(value, clazz);
} else {
return value;
}
}
+ private static String intToString(final String value, final Class> clazz) {
+ if (INT_PATTERN.matcher(value).matches()) {
+ return value;
+ } else {
+ return getValueOf(ReflectionHelper.getWrapperClass(clazz), value);
+ }
+ }
+
+ private static String decimalToString(final String value, final Class> clazz) {
+ if (DECIMAL_PATTERN.matcher(value).matches()) {
+ return value;
+ } else {
+ return getValueOf(ReflectionHelper.getWrapperClass(clazz), value);
+ }
+ }
+
+ private static String valueOfToString(final String value, final Class> clazz) {
+ if (clazz.isEnum()) {
+ return clazz.getCanonicalName() + "." + value;
+ } else {
+ return getValueOf(clazz.getCanonicalName(), value);
+ }
+ }
+
private static String getValueOf(final String clazz, final String value) {
return clazz + ".valueOf(\"" + value + "\")";
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableInfo.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableInfo.java
new file mode 100644
index 0000000..eace47e
--- /dev/null
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableInfo.java
@@ -0,0 +1,23 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+
+import java.util.Objects;
+
+/**
+ * Info about a variable
+ *
+ * @param id The fx:id of the variable
+ * @param parsedObject The parsed object of the variable
+ * @param variableName The variable name
+ * @param className The class name of the variable
+ */
+record VariableInfo(String id, ParsedObject parsedObject, String variableName, String className) {
+
+ VariableInfo {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(parsedObject);
+ Objects.requireNonNull(variableName);
+ Objects.requireNonNull(className);
+ }
+}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewBuilder.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewFormatter.java
similarity index 71%
rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewBuilder.java
rename to core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewFormatter.java
index cdf8216..37aa30c 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewBuilder.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewFormatter.java
@@ -5,20 +5,15 @@ import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
-import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectCallbackControllerMethod;
-import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectEventHandlerControllerMethod;
-import static com.github.gtache.fxml.compiler.impl.internal.FieldSetter.setEventHandler;
-import static com.github.gtache.fxml.compiler.impl.internal.FieldSetter.setField;
-import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
-import static com.github.gtache.fxml.compiler.impl.internal.PropertyFormatter.formatProperty;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
+import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format WebViews
*/
-final class WebViewBuilder {
-
- private WebViewBuilder() {
+final class WebViewFormatter {
+ private WebViewFormatter() {
}
/**
@@ -33,13 +28,13 @@ final class WebViewBuilder {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
final var sortedAttributes = getSortedAttributes(parsedObject);
final var sb = progress.stringBuilder();
- sb.append(START_VAR).append(variableName).append(" = new javafx.scene.web.WebView();\n");
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.web.WebView")).append(variableName).append(" = new javafx.scene.web.WebView();\n");
final var engineVariable = progress.getNextVariableName("engine");
- sb.append(START_VAR).append(engineVariable).append(" = ").append(variableName).append(".getEngine();\n");
+ sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.web.WebEngine")).append(engineVariable).append(" = ").append(variableName).append(".getEngine();\n");
for (final var value : sortedAttributes) {
formatAttribute(progress, value, parsedObject, variableName, engineVariable);
}
- handleId(progress, parsedObject, variableName);
+ GenerationHelper.handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("WebView cannot have children or properties : " + parsedObject);
}
@@ -57,13 +52,13 @@ final class WebViewBuilder {
injectEventHandler(progress, value, engineVariable);
case "promptHandler" -> injectPromptHandler(progress, value, engineVariable);
case "location" -> injectLocation(progress, value, engineVariable);
- default -> formatProperty(progress, value, parsedObject, variableName);
+ default -> PropertyFormatter.formatProperty(progress, value, parsedObject, variableName);
}
}
private static void injectConfirmHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
- injectCallbackControllerMethod(progress, value, engineVariable, "String.class");
+ ControllerInjector.injectCallbackControllerMethod(progress, value, engineVariable, "String.class");
} else {
setCallback(progress, value, engineVariable);
}
@@ -71,7 +66,7 @@ final class WebViewBuilder {
private static void injectCreatePopupHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
- injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PopupFeatures.class");
+ ControllerInjector.injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PopupFeatures.class");
} else {
setCallback(progress, value, engineVariable);
}
@@ -79,22 +74,22 @@ final class WebViewBuilder {
private static void injectEventHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
- injectEventHandlerControllerMethod(progress, value, engineVariable);
+ ControllerInjector.injectEventHandlerControllerMethod(progress, value, engineVariable);
} else {
- setEventHandler(progress, value, engineVariable);
+ FieldSetter.setEventHandler(progress, value, engineVariable);
}
}
private static void injectPromptHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
- injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PromptData.class");
+ ControllerInjector.injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PromptData.class");
} else {
setCallback(progress, value, engineVariable);
}
}
private static void injectLocation(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) {
- progress.stringBuilder().append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n");
+ progress.stringBuilder().append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n");
}
@@ -106,6 +101,6 @@ final class WebViewBuilder {
* @param parentVariable The parent variable
*/
private static void setCallback(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) throws GenerationException {
- setField(progress, property, parentVariable, "javafx.util.Callback");
+ FieldSetter.setField(progress, property, parentVariable, "javafx.util.Callback");
}
}
diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java
index 2ccccab..b841971 100644
--- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java
+++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java
@@ -3,7 +3,7 @@ package com.github.gtache.fxml.compiler.parsing.impl;
import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
-import java.util.Objects;
+import java.util.List;
/**
* Implementation of {@link ParsedObject}
@@ -12,9 +12,9 @@ import java.util.Objects;
* @param attributes The object properties
* @param properties The object children (complex properties)
*/
-public record ParsedDefineImpl(ParsedObject object) implements ParsedDefine {
+public record ParsedDefineImpl(List children) implements ParsedDefine {
public ParsedDefineImpl {
- Objects.requireNonNull(object);
+ children = List.copyOf(children);
}
}
diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java
index 19c7051..b55eddf 100644
--- a/core/src/main/java/module-info.java
+++ b/core/src/main/java/module-info.java
@@ -5,7 +5,9 @@ module com.github.gtache.fxml.compiler.core {
requires transitive com.github.gtache.fxml.compiler.api;
requires transitive javafx.graphics;
requires org.apache.logging.log4j;
+ requires java.sql;
exports com.github.gtache.fxml.compiler.impl;
exports com.github.gtache.fxml.compiler.parsing.impl;
+ exports com.github.gtache.fxml.compiler.compatibility.impl;
}
\ No newline at end of file
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/compatibility/impl/TestGenerationCompatibilityImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/compatibility/impl/TestGenerationCompatibilityImpl.java
new file mode 100644
index 0000000..e40d66c
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/compatibility/impl/TestGenerationCompatibilityImpl.java
@@ -0,0 +1,28 @@
+package com.github.gtache.fxml.compiler.compatibility.impl;
+
+import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class TestGenerationCompatibilityImpl {
+
+ private final int javaVersion;
+ private final GenerationCompatibility compatibility;
+
+ TestGenerationCompatibilityImpl() {
+ this.javaVersion = 8;
+ this.compatibility = new GenerationCompatibilityImpl(javaVersion);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(javaVersion, compatibility.javaVersion());
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(IllegalArgumentException.class, () -> new GenerationCompatibilityImpl(7));
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestClassesFinder.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestClassesFinder.java
index 039c1fd..c91756c 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestClassesFinder.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestClassesFinder.java
@@ -14,6 +14,7 @@ class TestClassesFinder {
final var expected = Set.of(
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedConstantImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedCopyImpl",
+ "com.github.gtache.fxml.compiler.parsing.impl.TestParsedDefineImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedFactoryImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedIncludeImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedObjectImpl",
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerFieldInfoImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerFieldInfoImpl.java
index 77c03dd..bc8a3e6 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerFieldInfoImpl.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerFieldInfoImpl.java
@@ -1,6 +1,7 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
+import com.github.gtache.fxml.compiler.GenericTypes;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
@@ -8,16 +9,17 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
class TestControllerFieldInfoImpl {
private final String name;
- private final List genericTypes;
+ private final List genericTypes;
private final ControllerFieldInfo info;
TestControllerFieldInfoImpl() {
this.name = "name";
- this.genericTypes = new ArrayList<>(List.of("A", "B", "C"));
+ this.genericTypes = new ArrayList<>(List.of(mock(GenericTypes.class)));
this.info = new ControllerFieldInfoImpl(name, genericTypes);
}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerInfoImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerInfoImpl.java
index 7b66fae..7ef9803 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerInfoImpl.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerInfoImpl.java
@@ -16,29 +16,39 @@ import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class TestControllerInfoImpl {
+ private final String className;
private final Map handlerHasArgument;
private final ControllerFieldInfo fieldInfo;
private final Map fieldInfoMap;
+ private final boolean hasInitialize;
private final ControllerInfo info;
TestControllerInfoImpl(@Mock final ControllerFieldInfo fieldInfo) {
+ this.className = "controllerName";
this.handlerHasArgument = new HashMap<>(Map.of("one", true, "two", false));
this.fieldInfo = Objects.requireNonNull(fieldInfo);
this.fieldInfoMap = new HashMap<>(Map.of("one", fieldInfo));
- this.info = new ControllerInfoImpl(handlerHasArgument, fieldInfoMap);
+ this.hasInitialize = true;
+ this.info = new ControllerInfoImpl(className, handlerHasArgument, fieldInfoMap, hasInitialize);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(className, info.className());
+ assertEquals(handlerHasArgument, info.handlerHasArgument());
+ assertEquals(fieldInfoMap, info.fieldInfo());
+ assertEquals(hasInitialize, info.hasInitialize());
}
@Test
void testHandlerHasArgument() {
- assertEquals(handlerHasArgument, info.handlerHasArgument());
assertTrue(info.handlerHasArgument("one"));
assertFalse(info.handlerHasArgument("two"));
assertTrue(info.handlerHasArgument("three"));
}
@Test
- void testPropertyGenericTypes() {
- assertEquals(fieldInfoMap, info.fieldInfo());
+ void testFieldInfo() {
assertEquals(fieldInfo, info.fieldInfo("one"));
}
@@ -65,7 +75,8 @@ class TestControllerInfoImpl {
@Test
void testIllegal() {
- assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(null, fieldInfoMap));
- assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(handlerHasArgument, null));
+ assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(null, handlerHasArgument, fieldInfoMap, hasInitialize));
+ assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(className, null, fieldInfoMap, hasInitialize));
+ assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(className, handlerHasArgument, null, hasInitialize));
}
}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerInjectionImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerInjectionImpl.java
deleted file mode 100644
index 3545032..0000000
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestControllerInjectionImpl.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.github.gtache.fxml.compiler.impl;
-
-import com.github.gtache.fxml.compiler.ControllerInjection;
-import com.github.gtache.fxml.compiler.InjectionType;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.Objects;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-@ExtendWith(MockitoExtension.class)
-class TestControllerInjectionImpl {
-
- private final InjectionType fieldInjectionType;
- private final InjectionType methodInjectionType;
- private final String injectionClass;
- private final ControllerInjection controllerInjection;
-
- TestControllerInjectionImpl(@Mock final InjectionType fieldInjectionType, @Mock final InjectionType methodInjectionType) {
- this.fieldInjectionType = Objects.requireNonNull(fieldInjectionType);
- this.methodInjectionType = Objects.requireNonNull(methodInjectionType);
- this.injectionClass = "class";
- this.controllerInjection = new ControllerInjectionImpl(fieldInjectionType, methodInjectionType, injectionClass);
- }
-
- @Test
- void testGetters() {
- assertEquals(fieldInjectionType, controllerInjection.fieldInjectionType());
- assertEquals(methodInjectionType, controllerInjection.methodInjectionType());
- assertEquals(injectionClass, controllerInjection.injectionClass());
- }
-
- @Test
- void testIllegal() {
- assertThrows(NullPointerException.class, () -> new ControllerInjectionImpl(null, methodInjectionType, injectionClass));
- assertThrows(NullPointerException.class, () -> new ControllerInjectionImpl(fieldInjectionType, null, injectionClass));
- assertThrows(NullPointerException.class, () -> new ControllerInjectionImpl(fieldInjectionType, methodInjectionType, null));
- }
-}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java
index cc6ef8a..b8efa0e 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java
@@ -1,49 +1,62 @@
package com.github.gtache.fxml.compiler.impl;
-import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.GenerationParameters;
-import com.github.gtache.fxml.compiler.ResourceBundleInjection;
+import com.github.gtache.fxml.compiler.InjectionType;
+import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Map;
-import java.util.Objects;
+import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestGenerationParametersImpl {
- private final Map controllerInjections;
- private final Map sourceToGeneratedClassName;
- private final Map sourceToControllerName;
- private final ResourceBundleInjection resourceBundleInjection;
+ private final GenerationCompatibility compatibility;
+ private final boolean useImageInputStreamConstructor;
+ private final Map bundleMap;
+ private final InjectionType controllerInjectionType;
+ private final InjectionType fieldInjectionType;
+ private final InjectionType methodInjectionType;
+ private final InjectionType resourceInjectionType;
private final GenerationParameters parameters;
- TestGenerationParametersImpl(@Mock final ControllerInjection controllerInjection, @Mock final ResourceBundleInjection resourceBundleInjection) {
- this.controllerInjections = Map.of("class", controllerInjection);
- this.sourceToGeneratedClassName = Map.of("source", "generated");
- this.sourceToControllerName = Map.of("source", "class");
- this.resourceBundleInjection = Objects.requireNonNull(resourceBundleInjection);
- this.parameters = new GenerationParametersImpl(controllerInjections, sourceToGeneratedClassName, sourceToControllerName, resourceBundleInjection);
+ TestGenerationParametersImpl(@Mock final GenerationCompatibility compatibility, @Mock final InjectionType controllerInjectionType,
+ @Mock final InjectionType fieldInjectionType, @Mock final InjectionType methodInjectionType,
+ @Mock final InjectionType resourceInjectionType) {
+ this.compatibility = requireNonNull(compatibility);
+ this.useImageInputStreamConstructor = true;
+ this.controllerInjectionType = requireNonNull(controllerInjectionType);
+ this.fieldInjectionType = requireNonNull(fieldInjectionType);
+ this.methodInjectionType = requireNonNull(methodInjectionType);
+ this.resourceInjectionType = requireNonNull(resourceInjectionType);
+ this.bundleMap = Map.of("source", "generated");
+ this.parameters = new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType);
}
@Test
void testGetters() {
- assertEquals(controllerInjections, parameters.controllerInjections());
- assertEquals(sourceToGeneratedClassName, parameters.sourceToGeneratedClassName());
- assertEquals(sourceToControllerName, parameters.sourceToControllerName());
- assertEquals(resourceBundleInjection, parameters.resourceBundleInjection());
+ assertEquals(compatibility, parameters.compatibility());
+ assertEquals(useImageInputStreamConstructor, parameters.useImageInputStreamConstructor());
+ assertEquals(bundleMap, parameters.bundleMap());
+ assertEquals(controllerInjectionType, parameters.controllerInjectionType());
+ assertEquals(fieldInjectionType, parameters.fieldInjectionType());
+ assertEquals(methodInjectionType, parameters.methodInjectionType());
+ assertEquals(resourceInjectionType, parameters.resourceInjectionType());
}
@Test
void testIllegal() {
- assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(null, sourceToGeneratedClassName, sourceToControllerName, resourceBundleInjection));
- assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(controllerInjections, null, sourceToControllerName, resourceBundleInjection));
- assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(controllerInjections, sourceToGeneratedClassName, null, resourceBundleInjection));
- assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(controllerInjections, sourceToGeneratedClassName, sourceToControllerName, null));
+ assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(null, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType));
+ assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, null, controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType));
+ assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, null, fieldInjectionType, methodInjectionType, resourceInjectionType));
+ assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, null, methodInjectionType, resourceInjectionType));
+ assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, null, resourceInjectionType));
+ assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, methodInjectionType, null));
}
}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationRequestImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationRequestImpl.java
index f57cf79..111a33c 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationRequestImpl.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationRequestImpl.java
@@ -3,6 +3,7 @@ package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
+import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -19,31 +20,36 @@ class TestGenerationRequestImpl {
private final GenerationParameters parameters;
private final ControllerInfo controllerInfo;
+ private final SourceInfo sourceInfo;
private final ParsedObject rootObject;
private final String outputClassName;
private final GenerationRequest request;
- TestGenerationRequestImpl(@Mock final GenerationParameters parameters, @Mock final ControllerInfo controllerInfo, @Mock final ParsedObject rootObject) {
+ TestGenerationRequestImpl(@Mock final GenerationParameters parameters, @Mock final ControllerInfo controllerInfo,
+ @Mock final SourceInfo sourceInfo, @Mock final ParsedObject rootObject) {
this.parameters = Objects.requireNonNull(parameters);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
+ this.sourceInfo = Objects.requireNonNull(sourceInfo);
this.rootObject = Objects.requireNonNull(rootObject);
this.outputClassName = "class";
- this.request = new GenerationRequestImpl(parameters, controllerInfo, rootObject, outputClassName);
+ this.request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, rootObject, outputClassName);
}
@Test
void testGetters() {
assertEquals(parameters, request.parameters());
assertEquals(controllerInfo, request.controllerInfo());
+ assertEquals(sourceInfo, request.sourceInfo());
assertEquals(rootObject, request.rootObject());
assertEquals(outputClassName, request.outputClassName());
}
@Test
void testIllegal() {
- assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(null, controllerInfo, rootObject, outputClassName));
- assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, null, rootObject, outputClassName));
- assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, null, outputClassName));
- assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, rootObject, null));
+ assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(null, controllerInfo, sourceInfo, rootObject, outputClassName));
+ assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, null, sourceInfo, rootObject, outputClassName));
+ assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, null, rootObject, outputClassName));
+ assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, null, outputClassName));
+ assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, rootObject, null));
}
}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenericTypesImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenericTypesImpl.java
new file mode 100644
index 0000000..8680b18
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenericTypesImpl.java
@@ -0,0 +1,49 @@
+package com.github.gtache.fxml.compiler.impl;
+
+import com.github.gtache.fxml.compiler.GenericTypes;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+
+class TestGenericTypesImpl {
+
+ private final String name;
+ private final List subTypes;
+ private final GenericTypes types;
+
+ TestGenericTypesImpl() {
+ this.name = "name";
+ this.subTypes = new ArrayList<>(List.of(mock(GenericTypes.class)));
+ this.types = new GenericTypesImpl(name, subTypes);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(name, types.name());
+ assertEquals(subTypes, types.subTypes());
+ }
+
+ @Test
+ void testCopyList() {
+ final var originalGenericTypes = types.subTypes();
+ subTypes.clear();
+ assertEquals(originalGenericTypes, types.subTypes());
+ }
+
+ @Test
+ void testUnmodifiable() {
+ final var infoList = types.subTypes();
+ assertThrows(UnsupportedOperationException.class, infoList::clear);
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(NullPointerException.class, () -> new ControllerFieldInfoImpl(null, subTypes));
+ assertThrows(NullPointerException.class, () -> new ControllerFieldInfoImpl(name, null));
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestResourceBundleInjectionImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestResourceBundleInjectionImpl.java
deleted file mode 100644
index 9c500de..0000000
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestResourceBundleInjectionImpl.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.github.gtache.fxml.compiler.impl;
-
-import com.github.gtache.fxml.compiler.InjectionType;
-import com.github.gtache.fxml.compiler.ResourceBundleInjection;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.Objects;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-@ExtendWith(MockitoExtension.class)
-class TestResourceBundleInjectionImpl {
-
- private final InjectionType injectionType;
- private final String bundleName;
- private final ResourceBundleInjection resourceBundleInjection;
-
- TestResourceBundleInjectionImpl(@Mock final InjectionType injectionType) {
- this.injectionType = Objects.requireNonNull(injectionType);
- this.bundleName = "bundle";
- this.resourceBundleInjection = new ResourceBundleInjectionImpl(injectionType, bundleName);
- }
-
- @Test
- void testGetters() {
- assertEquals(injectionType, resourceBundleInjection.injectionType());
- assertEquals(bundleName, resourceBundleInjection.bundleName());
- }
-
- @Test
- void testIllegal() {
- assertThrows(NullPointerException.class, () -> new ResourceBundleInjectionImpl(null, bundleName));
- assertThrows(NullPointerException.class, () -> new ResourceBundleInjectionImpl(injectionType, null));
- }
-}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestSourceInfoImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestSourceInfoImpl.java
new file mode 100644
index 0000000..727396a
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestSourceInfoImpl.java
@@ -0,0 +1,80 @@
+package com.github.gtache.fxml.compiler.impl;
+
+import com.github.gtache.fxml.compiler.SourceInfo;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(MockitoExtension.class)
+class TestSourceInfoImpl {
+
+ private final String generatedClassName;
+ private final String controllerClassName;
+ private final Path sourceFile;
+ private final List includedSources;
+ private final Map sourceToSourceInfo;
+ private final boolean requiresResourceBundle;
+ private final SourceInfo info;
+
+ TestSourceInfoImpl(@Mock final SourceInfo subInfo) {
+ this.generatedClassName = "class";
+ this.controllerClassName = "controller";
+ this.sourceFile = Paths.get("path");
+ this.includedSources = new ArrayList<>(List.of(subInfo));
+ this.sourceToSourceInfo = new HashMap<>(Map.of("source", subInfo));
+ this.requiresResourceBundle = false;
+ this.info = new SourceInfoImpl(generatedClassName, controllerClassName, sourceFile, includedSources, sourceToSourceInfo, requiresResourceBundle);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(generatedClassName, info.generatedClassName());
+ assertEquals(controllerClassName, info.controllerClassName());
+ assertEquals(sourceFile, info.sourceFile());
+ assertEquals(includedSources, info.includedSources());
+ assertEquals(sourceToSourceInfo, info.sourceToSourceInfo());
+ assertEquals(requiresResourceBundle, info.requiresResourceBundle());
+ }
+
+ @Test
+ void testCopyList() {
+ final var originalIncludedSources = info.includedSources();
+ includedSources.clear();
+ assertEquals(originalIncludedSources, info.includedSources());
+ }
+
+ @Test
+ void testCopyMap() {
+ final var originalSourceToSourceInfo = info.sourceToSourceInfo();
+ sourceToSourceInfo.clear();
+ assertEquals(originalSourceToSourceInfo, info.sourceToSourceInfo());
+ }
+
+ @Test
+ void testUnmodifiable() {
+ final var infoIncludedSources = info.includedSources();
+ final var infoSourceToSourceInfo = info.sourceToSourceInfo();
+ assertThrows(UnsupportedOperationException.class, infoIncludedSources::clear);
+ assertThrows(UnsupportedOperationException.class, infoSourceToSourceInfo::clear);
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(NullPointerException.class, () -> new SourceInfoImpl(null, controllerClassName, sourceFile, includedSources, sourceToSourceInfo, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, null, sourceFile, includedSources, sourceToSourceInfo, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, controllerClassName, null, includedSources, sourceToSourceInfo, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, controllerClassName, sourceFile, null, sourceToSourceInfo, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, controllerClassName, sourceFile, includedSources, null, requiresResourceBundle));
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorFormatter.java
new file mode 100644
index 0000000..3106c94
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorFormatter.java
@@ -0,0 +1,58 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.GenerationParameters;
+import com.github.gtache.fxml.compiler.GenerationRequest;
+import com.github.gtache.fxml.compiler.SourceInfo;
+import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class TestConstructorFormatter {
+
+ private final GenerationProgress progress;
+ private final GenerationRequest request;
+ private final GenerationParameters parameters;
+ private final StringBuilder stringBuilder;
+ private final SourceInfo sourceInfo;
+ private final ParsedInclude include;
+ private final Map sourceToSourceInfo;
+
+
+ TestConstructorFormatter(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
+ @Mock final GenerationParameters parameters, @Mock final StringBuilder stringBuilder,
+ @Mock final SourceInfo sourceInfo, @Mock final ParsedInclude include) {
+ this.progress = Objects.requireNonNull(progress);
+ this.request = Objects.requireNonNull(request);
+ this.parameters = Objects.requireNonNull(parameters);
+ this.stringBuilder = Objects.requireNonNull(stringBuilder);
+ this.sourceInfo = Objects.requireNonNull(sourceInfo);
+ this.include = Objects.requireNonNull(include);
+ this.sourceToSourceInfo = new HashMap<>();
+ }
+
+ @BeforeEach
+ void beforeEach() {
+ when(request.parameters()).thenReturn(parameters);
+ when(progress.request()).thenReturn(request);
+ when(progress.stringBuilder()).thenReturn(stringBuilder);
+ when(request.sourceInfo()).thenReturn(sourceInfo);
+ when(sourceInfo.sourceToSourceInfo()).thenReturn(sourceToSourceInfo);
+ }
+
+ @Test
+ void testFormatSubViewConstructorCallNullSubInfo() {
+ assertThrows(GenerationException.class, () -> ConstructorFormatter.formatSubViewConstructorCall(progress, include));
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorHelper.java
new file mode 100644
index 0000000..336a5ce
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorHelper.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestConstructorHelper {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestControllerInjector.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestControllerInjector.java
new file mode 100644
index 0000000..f004167
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestControllerInjector.java
@@ -0,0 +1,172 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.ControllerInfo;
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.GenerationParameters;
+import com.github.gtache.fxml.compiler.GenerationRequest;
+import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
+import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
+import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class TestControllerInjector {
+
+ private final GenerationProgress progress;
+ private final GenerationRequest request;
+ private final GenerationParameters parameters;
+ private final ControllerInfo controllerInfo;
+ private final List controllerFactoryPostAction;
+ private final String id;
+ private final String variable;
+ private final ParsedProperty property;
+ private final String propertyValue;
+ private final StringBuilder sb;
+
+ TestControllerInjector(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
+ @Mock final GenerationParameters parameters, @Mock final ControllerInfo controllerInfo,
+ @Mock final ParsedProperty property) {
+ this.progress = Objects.requireNonNull(progress);
+ this.request = Objects.requireNonNull(request);
+ this.parameters = Objects.requireNonNull(parameters);
+ this.controllerInfo = Objects.requireNonNull(controllerInfo);
+ this.controllerFactoryPostAction = new ArrayList<>();
+ this.id = "id";
+ this.variable = "variable";
+ this.propertyValue = "#property";
+ this.property = Objects.requireNonNull(property);
+ this.sb = new StringBuilder();
+ }
+
+ @BeforeEach
+ void beforeEach() {
+ when(progress.request()).thenReturn(request);
+ when(request.parameters()).thenReturn(parameters);
+ when(request.controllerInfo()).thenReturn(controllerInfo);
+ when(progress.controllerFactoryPostAction()).thenReturn(controllerFactoryPostAction);
+ when(progress.stringBuilder()).thenReturn(sb);
+ when(property.name()).thenReturn("name");
+ when(property.value()).thenReturn(propertyValue);
+ }
+
+ @Test
+ void testInjectControllerFieldFactory() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
+ ControllerInjector.injectControllerField(progress, id, variable);
+ final var expected = " fieldMap.put(\"" + id + "\", " + variable + ");\n";
+ assertEquals(expected, sb.toString());
+ assertTrue(controllerFactoryPostAction.isEmpty());
+ }
+
+ @Test
+ void testInjectControllerFieldAssign() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
+ ControllerInjector.injectControllerField(progress, id, variable);
+ final var expected = " controller." + id + " = " + variable + ";\n";
+ assertEquals(expected, sb.toString());
+ assertTrue(controllerFactoryPostAction.isEmpty());
+ }
+
+ @Test
+ void testInjectControllerFieldSetters() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.SETTERS);
+ ControllerInjector.injectControllerField(progress, id, variable);
+ final var expected = " controller." + GenerationHelper.getSetMethod(id) + "(" + variable + ");\n";
+ assertEquals(expected, sb.toString());
+ assertTrue(controllerFactoryPostAction.isEmpty());
+ }
+
+ @Test
+ void testInjectControllerFieldReflection() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.REFLECTION);
+ ControllerInjector.injectControllerField(progress, id, variable);
+ final var expected = " injectField(\"" + id + "\", " + variable + ");\n";
+ assertEquals(expected, sb.toString());
+ assertTrue(controllerFactoryPostAction.isEmpty());
+ }
+
+ @Test
+ void testInjectControllerFieldUnknown() {
+ when(parameters.fieldInjectionType()).thenReturn(null);
+ assertThrows(GenerationException.class, () -> ControllerInjector.injectControllerField(progress, id, variable));
+ }
+
+ @Test
+ void testInjectEventHandlerReferenceFactoryNoArgument() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
+ when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFERENCE);
+ ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable);
+ final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> controller." + property.value().replace("#", "") + "());\n";
+ assertEquals(1, controllerFactoryPostAction.size());
+ assertEquals(expected, controllerFactoryPostAction.getFirst());
+ assertEquals("", sb.toString());
+ }
+
+ @Test
+ void testInjectEventHandlerReferenceFactoryWithArgument() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
+ when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFERENCE);
+ when(controllerInfo.handlerHasArgument(propertyValue.replace("#", ""))).thenReturn(true);
+ ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable);
+ final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
+ assertEquals(1, controllerFactoryPostAction.size());
+ assertEquals(expected, controllerFactoryPostAction.getFirst());
+ assertEquals("", sb.toString());
+ }
+
+ @Test
+ void testInjectEventHandlerReflectionAssign() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
+ when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFLECTION);
+ ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable);
+ final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callEventHandlerMethod(\"" + propertyValue.replace("#", "") + "\", e));\n";
+ assertEquals(expected, sb.toString());
+ assertTrue(controllerFactoryPostAction.isEmpty());
+ }
+
+ @Test
+ void testInjectEventHandlerUnknownMethod() {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
+ when(parameters.methodInjectionType()).thenReturn(null);
+ assertThrows(GenerationException.class, () -> ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable));
+ }
+
+ @Test
+ void testInjectEventHandlerUnknownField() {
+ when(parameters.fieldInjectionType()).thenReturn(null);
+ when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFLECTION);
+ assertThrows(GenerationException.class, () -> ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable));
+ }
+
+ @Test
+ void testInjectCallbackReflectionSetters() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
+ when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFLECTION);
+ ControllerInjector.injectCallbackControllerMethod(progress, property, variable, "clazz");
+ final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callCallbackMethod(\"" + propertyValue.replace("#", "") + "\", e, clazz));\n";
+ assertEquals(expected, sb.toString());
+ assertTrue(controllerFactoryPostAction.isEmpty());
+ }
+
+ @Test
+ void testInjectCallbackReferenceFactory() throws GenerationException {
+ when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
+ when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFERENCE);
+ ControllerInjector.injectCallbackControllerMethod(progress, property, variable, "clazz");
+ final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
+ assertEquals(1, controllerFactoryPostAction.size());
+ assertEquals(expected, controllerFactoryPostAction.getFirst());
+ assertEquals("", sb.toString());
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFieldSetter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFieldSetter.java
new file mode 100644
index 0000000..9c55b49
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFieldSetter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestFieldSetter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFontFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFontFormatter.java
new file mode 100644
index 0000000..e011032
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFontFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestFontFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationCompatibilityHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationCompatibilityHelper.java
new file mode 100644
index 0000000..4e1254e
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationCompatibilityHelper.java
@@ -0,0 +1,141 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.ControllerFieldInfo;
+import com.github.gtache.fxml.compiler.ControllerInfo;
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.GenerationParameters;
+import com.github.gtache.fxml.compiler.GenerationRequest;
+import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
+import com.github.gtache.fxml.compiler.compatibility.ListCollector;
+import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class TestGenerationCompatibilityHelper {
+
+ private final GenerationProgress progress;
+ private final GenerationRequest request;
+ private final GenerationParameters parameters;
+ private final GenerationCompatibility compatibility;
+ private final ParsedObject parsedObject;
+
+ TestGenerationCompatibilityHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
+ @Mock final GenerationParameters parameters, @Mock final GenerationCompatibility compatibility,
+ @Mock final ParsedObject parsedObject) {
+ this.progress = Objects.requireNonNull(progress);
+ this.request = Objects.requireNonNull(request);
+ this.parameters = Objects.requireNonNull(parameters);
+ this.compatibility = Objects.requireNonNull(compatibility);
+ this.parsedObject = Objects.requireNonNull(parsedObject);
+ }
+
+ @BeforeEach
+ void beforeEach() {
+ when(progress.request()).thenReturn(request);
+ when(request.parameters()).thenReturn(parameters);
+ when(parameters.compatibility()).thenReturn(compatibility);
+ when(parsedObject.className()).thenReturn("java.lang.String");
+ }
+
+ @Test
+ void testGetStartVarUseVar() {
+ when(compatibility.useVar()).thenReturn(true);
+ assertEquals(" final var ", GenerationCompatibilityHelper.getStartVar(progress, "class", 2));
+ }
+
+ @Test
+ void testGetStartVarUseVarDefaultIndent() {
+ when(compatibility.useVar()).thenReturn(true);
+ assertEquals(" final var ", GenerationCompatibilityHelper.getStartVar(progress, "class"));
+ }
+
+ @Test
+ void testGetStartVarUseVarObject() throws GenerationException {
+ when(compatibility.useVar()).thenReturn(true);
+ assertEquals(" final var ", GenerationCompatibilityHelper.getStartVar(progress, parsedObject));
+ }
+
+ @Test
+ void testGetStartVarDontUseVar() {
+ when(compatibility.useVar()).thenReturn(false);
+ assertEquals(" final javafx.scene.control.Label ", GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.control.Label", 2));
+ }
+
+ @Test
+ void testGetStartVarDontUseVarObject() throws GenerationException {
+ when(compatibility.useVar()).thenReturn(false);
+ when(parsedObject.className()).thenReturn("javafx.scene.control.Label");
+ assertEquals(" final javafx.scene.control.Label ", GenerationCompatibilityHelper.getStartVar(progress, parsedObject));
+ }
+
+ @Test
+ void testGetStartVarDontUseVarGenericObject() throws GenerationException {
+ when(compatibility.useVar()).thenReturn(false);
+ when(parsedObject.className()).thenReturn("javafx.scene.control.TableView");
+ final var id = "tableView";
+ when(parsedObject.attributes()).thenReturn(Map.of("fx:id", new ParsedPropertyImpl("fx:id", null, id)));
+ final var info = mock(ControllerInfo.class);
+ final var fieldInfo = mock(ControllerFieldInfo.class);
+ when(info.fieldInfo(id)).thenReturn(fieldInfo);
+ when(request.controllerInfo()).thenReturn(info);
+ when(fieldInfo.isGeneric()).thenReturn(true);
+ when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("java.lang.Integer", List.of())));
+ assertEquals(" final javafx.scene.control.TableView ", GenerationCompatibilityHelper.getStartVar(progress, parsedObject));
+ }
+
+ @Test
+ void testGetToListToList() {
+ when(compatibility.listCollector()).thenReturn(ListCollector.TO_LIST);
+ assertEquals(".toList()", GenerationCompatibilityHelper.getToList(progress));
+ }
+
+ @Test
+ void testGetToListCollectToUnmodifiableList() {
+ when(compatibility.listCollector()).thenReturn(ListCollector.COLLECT_TO_UNMODIFIABLE_LIST);
+ assertEquals(".collect(java.util.stream.Collectors.toUnmodifiableList())", GenerationCompatibilityHelper.getToList(progress));
+ }
+
+ @Test
+ void testGetToListCollectToList() {
+ when(compatibility.listCollector()).thenReturn(ListCollector.COLLECT_TO_LIST);
+ assertEquals(".collect(java.util.stream.Collectors.toList())", GenerationCompatibilityHelper.getToList(progress));
+ }
+
+ @Test
+ void testGetFirstUse() {
+ when(compatibility.useGetFirst()).thenReturn(true);
+ assertEquals(".getFirst()", GenerationCompatibilityHelper.getGetFirst(progress));
+ }
+
+ @Test
+ void testGetFirstDontUse() {
+ when(compatibility.useGetFirst()).thenReturn(false);
+ assertEquals(".get(0)", GenerationCompatibilityHelper.getGetFirst(progress));
+ }
+
+ @Test
+ void testGetListOfUse() {
+ when(compatibility.useCollectionsOf()).thenReturn(true);
+ assertEquals("java.util.List.of(", GenerationCompatibilityHelper.getListOf(progress));
+ }
+
+ @Test
+ void testGetListOfDontUse() {
+ when(compatibility.useCollectionsOf()).thenReturn(false);
+ assertEquals("java.util.Arrays.asList(", GenerationCompatibilityHelper.getListOf(progress));
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationHelper.java
new file mode 100644
index 0000000..549ce32
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationHelper.java
@@ -0,0 +1,102 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.ControllerFieldInfo;
+import com.github.gtache.fxml.compiler.ControllerInfo;
+import com.github.gtache.fxml.compiler.GenerationRequest;
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
+import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class TestGenerationHelper {
+
+ private final GenerationProgress progress;
+ private final GenerationRequest request;
+ private final ControllerInfo controllerInfo;
+ private final ControllerFieldInfo fieldInfo;
+ private final Map idToVariableInfo;
+ private final String variableName;
+ private final ParsedObject object;
+ private final Map attributes;
+ private final String className;
+ private final ParsedProperty property;
+ private final String propertyName;
+
+ TestGenerationHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
+ @Mock final ControllerInfo controllerInfo, @Mock final ControllerFieldInfo fieldInfo,
+ @Mock final ParsedObject object, @Mock final ParsedProperty property) {
+ this.progress = Objects.requireNonNull(progress);
+ this.request = Objects.requireNonNull(request);
+ this.controllerInfo = Objects.requireNonNull(controllerInfo);
+ this.fieldInfo = Objects.requireNonNull(fieldInfo);
+ this.object = Objects.requireNonNull(object);
+ this.property = Objects.requireNonNull(property);
+ this.idToVariableInfo = new HashMap<>();
+ this.variableName = "variable";
+ this.attributes = new HashMap<>();
+ this.className = "java.lang.String";
+ this.propertyName = "property";
+ }
+
+ @BeforeEach
+ void beforeEach() {
+ when(progress.request()).thenReturn(request);
+ when(request.controllerInfo()).thenReturn(controllerInfo);
+ when(object.attributes()).thenReturn(attributes);
+ when(object.className()).thenReturn(className);
+ when(property.name()).thenReturn(propertyName);
+ when(progress.idToVariableInfo()).thenReturn(idToVariableInfo);
+ }
+
+ @Test
+ void testGetVariablePrefixObject() {
+ assertEquals("string", GenerationHelper.getVariablePrefix(object));
+ }
+
+ @Test
+ void testGetVariablePrefix() {
+ assertEquals("int", GenerationHelper.getVariablePrefix("int"));
+ }
+
+ @Test
+ void testGetGetMethodProperty() {
+ assertEquals("getProperty", GenerationHelper.getGetMethod(property));
+ }
+
+ @Test
+ void testGetGetMethod() {
+ assertEquals("getSomething", GenerationHelper.getGetMethod("Something"));
+ }
+
+ @Test
+ void testGetSetMethodProperty() {
+ assertEquals("setProperty", GenerationHelper.getSetMethod(property));
+ }
+
+ @Test
+ void testGetSetMethod() {
+ assertEquals("setSomething", GenerationHelper.getSetMethod("Something"));
+ }
+
+ @Test
+ void testGetSortedAttributes() {
+ attributes.put("a", new ParsedPropertyImpl("a", null, "valueA"));
+ attributes.put("b", new ParsedPropertyImpl("b", null, "valueB"));
+ attributes.put("c", new ParsedPropertyImpl("c", null, "valueC"));
+ final var expected = List.of(attributes.get("a"), attributes.get("b"), attributes.get("c"));
+ assertEquals(expected, GenerationHelper.getSortedAttributes(object));
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationProgress.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationProgress.java
similarity index 60%
rename from core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationProgress.java
rename to core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationProgress.java
index 66b6073..c8f8137 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationProgress.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationProgress.java
@@ -1,8 +1,6 @@
-package com.github.gtache.fxml.compiler.impl;
+package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationRequest;
-import com.github.gtache.fxml.compiler.impl.internal.GenerationProgress;
-import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@@ -10,61 +8,66 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.SequencedCollection;
+import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
+import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
@ExtendWith(MockitoExtension.class)
class TestGenerationProgress {
private final GenerationRequest request;
- private final Map idToVariableName;
- private final Map idToObject;
+ private final VariableInfo variableInfo;
+ private final Map idToVariableInfo;
private final Map variableNameCounters;
+ private final SequencedMap controllerClassToVariable;
private final SequencedCollection controllerFactoryPostAction;
private final StringBuilder sb;
private final GenerationProgress progress;
- TestGenerationProgress(@Mock final GenerationRequest request, @Mock final ParsedObject object) {
- this.request = Objects.requireNonNull(request);
- this.idToVariableName = new HashMap<>();
- idToVariableName.put("var1", "var2");
- this.idToObject = new HashMap<>();
- idToObject.put("var1", object);
+ TestGenerationProgress(@Mock final GenerationRequest request, @Mock final VariableInfo variableInfo) {
+ this.request = requireNonNull(request);
+ this.variableInfo = requireNonNull(variableInfo);
+ this.idToVariableInfo = new HashMap<>();
+ idToVariableInfo.put("var1", variableInfo);
+ this.controllerClassToVariable = new LinkedHashMap();
+ controllerClassToVariable.put("bla", "var1");
+ controllerClassToVariable.put("bla2", "var2");
this.variableNameCounters = new HashMap<>();
variableNameCounters.put("var", new AtomicInteger(0));
this.controllerFactoryPostAction = new ArrayList<>();
controllerFactoryPostAction.add("bla");
this.sb = new StringBuilder("test");
- this.progress = new GenerationProgress(request, idToVariableName, idToObject, variableNameCounters, controllerFactoryPostAction, sb);
+ this.progress = new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb);
}
@Test
void testGetters() {
assertEquals(request, progress.request());
- assertEquals(idToVariableName, progress.idToVariableName());
+ assertEquals(idToVariableInfo, progress.idToVariableInfo());
assertEquals(variableNameCounters, progress.variableNameCounters());
+ assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
assertEquals(sb, progress.stringBuilder());
}
@Test
void testConstructorDoesntCopy() {
- idToVariableName.clear();
- assertEquals(idToVariableName, progress.idToVariableName());
-
- idToObject.clear();
- assertEquals(idToObject, progress.idToObject());
+ idToVariableInfo.clear();
+ assertEquals(idToVariableInfo, progress.idToVariableInfo());
variableNameCounters.clear();
assertEquals(variableNameCounters, progress.variableNameCounters());
+ controllerClassToVariable.clear();
+ assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
+
controllerFactoryPostAction.clear();
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -74,15 +77,15 @@ class TestGenerationProgress {
@Test
void testCanModify() {
- progress.idToVariableName().put("var3", "var4");
- assertEquals(idToVariableName, progress.idToVariableName());
-
- progress.idToObject().put("var3", mock(ParsedObject.class));
- assertEquals(idToObject, progress.idToObject());
+ progress.idToVariableInfo().put("var3", variableInfo);
+ assertEquals(idToVariableInfo, progress.idToVariableInfo());
progress.variableNameCounters().put("var5", new AtomicInteger(0));
assertEquals(variableNameCounters, progress.variableNameCounters());
+ progress.controllerClassToVariable().put("bla3", "var3");
+ assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
+
progress.controllerFactoryPostAction().add("bla2");
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -94,8 +97,9 @@ class TestGenerationProgress {
void testOtherConstructor() {
final var progress2 = new GenerationProgress(request);
assertEquals(request, progress2.request());
- assertEquals(Map.of(), progress2.idToVariableName());
+ assertEquals(Map.of(), progress2.idToVariableInfo());
assertEquals(Map.of(), progress2.variableNameCounters());
+ assertEquals(Map.of(), progress2.controllerClassToVariable());
assertEquals(List.of(), progress2.controllerFactoryPostAction());
assertEquals("", progress2.stringBuilder().toString());
}
@@ -110,12 +114,12 @@ class TestGenerationProgress {
@Test
void testIllegal() {
- assertThrows(NullPointerException.class, () -> new GenerationProgress(null, idToVariableName, idToObject, variableNameCounters, controllerFactoryPostAction, sb));
- assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, idToObject, variableNameCounters, controllerFactoryPostAction, sb));
- assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, null, variableNameCounters, controllerFactoryPostAction, sb));
- assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, idToObject, null, controllerFactoryPostAction, sb));
- assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, idToObject, variableNameCounters, null, sb));
- assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, idToObject, variableNameCounters, controllerFactoryPostAction, null));
+ assertThrows(NullPointerException.class, () -> new GenerationProgress(null, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb));
+ assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb));
+ assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, null, controllerClassToVariable, controllerFactoryPostAction, sb));
+ assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, null, controllerFactoryPostAction, sb));
+ assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, null, sb));
+ assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, null));
}
}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperMethodsFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperMethodsFormatter.java
new file mode 100644
index 0000000..90826d8
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperMethodsFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestHelperMethodsFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestImageFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestImageFormatter.java
new file mode 100644
index 0000000..e906702
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestImageFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestImageFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestLoadMethodFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestLoadMethodFormatter.java
new file mode 100644
index 0000000..32a1e9b
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestLoadMethodFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestLoadMethodFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestObjectFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestObjectFormatter.java
new file mode 100644
index 0000000..e7d8454
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestObjectFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestObjectFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestPropertyFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestPropertyFormatter.java
new file mode 100644
index 0000000..f32fe85
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestPropertyFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestPropertyFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java
index f780f51..5a24daa 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java
@@ -1,7 +1,13 @@
package com.github.gtache.fxml.compiler.impl.internal;
+import com.github.gtache.fxml.compiler.ControllerFieldInfo;
+import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationException;
-import com.github.gtache.fxml.compiler.impl.WholeConstructorArgs;
+import com.github.gtache.fxml.compiler.GenerationRequest;
+import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
+import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
@@ -9,15 +15,54 @@ import javafx.scene.control.TableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.when;
+@ExtendWith(MockitoExtension.class)
class TestReflectionHelper {
+ private final GenerationProgress progress;
+ private final GenerationRequest request;
+ private final Map attributes;
+ private final ControllerInfo controllerInfo;
+ private final ControllerFieldInfo fieldInfo;
+ private final ParsedObject parsedObject;
+
+ TestReflectionHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
+ @Mock final ControllerInfo controllerInfo, @Mock final ControllerFieldInfo fieldInfo,
+ @Mock final ParsedObject parsedObject) {
+ this.progress = Objects.requireNonNull(progress);
+ this.request = Objects.requireNonNull(request);
+ this.controllerInfo = Objects.requireNonNull(controllerInfo);
+ this.fieldInfo = Objects.requireNonNull(fieldInfo);
+ this.parsedObject = Objects.requireNonNull(parsedObject);
+ this.attributes = new HashMap<>();
+ }
+
+ @BeforeEach
+ void beforeEach() {
+ when(progress.request()).thenReturn(request);
+ when(request.controllerInfo()).thenReturn(controllerInfo);
+ when(controllerInfo.fieldInfo("id")).thenReturn(fieldInfo);
+ when(parsedObject.attributes()).thenReturn(attributes);
+ when(parsedObject.className()).thenReturn("javafx.scene.control.ComboBox");
+ attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
+ when(fieldInfo.isGeneric()).thenReturn(true);
+ }
+
@Test
void testIsGeneric() {
assertFalse(ReflectionHelper.isGeneric(String.class));
@@ -127,4 +172,50 @@ class TestReflectionHelper {
void testGetDefaultProperty() throws GenerationException {
assertEquals("items", ReflectionHelper.getDefaultProperty("javafx.scene.control.ListView"));
}
+
+ @Test
+ void testGetWrapperClass() {
+ assertEquals(Integer.class.getName(), ReflectionHelper.getWrapperClass(int.class));
+ assertEquals(String.class.getName(), ReflectionHelper.getWrapperClass(String.class));
+ assertEquals(Object.class.getName(), ReflectionHelper.getWrapperClass(Object.class));
+ assertEquals(Double.class.getName(), ReflectionHelper.getWrapperClass(double.class));
+ }
+
+ @Test
+ void testGetClass() throws GenerationException {
+ assertEquals(String.class, ReflectionHelper.getClass("java.lang.String"));
+ assertEquals(Object.class, ReflectionHelper.getClass("java.lang.Object"));
+ assertEquals(int.class, ReflectionHelper.getClass("int"));
+ }
+
+ @Test
+ void testGetGenericTypesNotGeneric() throws GenerationException {
+ when(parsedObject.className()).thenReturn("java.lang.String");
+ assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
+ }
+
+ @Test
+ void testGetGenericTypesNullProperty() throws GenerationException {
+ attributes.clear();
+ assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
+ }
+
+ @Test
+ void testGetGenericTypesFieldNotFound() throws GenerationException {
+ when(controllerInfo.fieldInfo("id")).thenReturn(null);
+ assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
+ }
+
+ @Test
+ void testGetGenericTypesFieldNotGeneric() throws GenerationException {
+ when(fieldInfo.isGeneric()).thenReturn(false);
+ when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("java.lang.Integer", List.of())));
+ assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
+ }
+
+ @Test
+ void testGetGenericTypes() throws GenerationException {
+ when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("java.lang.Integer", List.of())));
+ assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
+ }
}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestSceneFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestSceneFormatter.java
new file mode 100644
index 0000000..61f5021
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestSceneFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestSceneFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestTriangleMeshFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestTriangleMeshFormatter.java
new file mode 100644
index 0000000..aac331f
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestTriangleMeshFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestTriangleMeshFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestURLFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestURLFormatter.java
new file mode 100644
index 0000000..d85a27e
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestURLFormatter.java
@@ -0,0 +1,9 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestURLFormatter {
+
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestValueFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestValueFormatter.java
new file mode 100644
index 0000000..f81e0d5
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestValueFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestValueFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestVariableInfo.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestVariableInfo.java
new file mode 100644
index 0000000..5062ec9
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestVariableInfo.java
@@ -0,0 +1,46 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(MockitoExtension.class)
+class TestVariableInfo {
+
+ private final String id;
+ private final ParsedObject parsedObject;
+ private final String variableName;
+ private final String className;
+ private final VariableInfo info;
+
+ TestVariableInfo(@Mock final ParsedObject parsedObject) {
+ this.id = "id";
+ this.parsedObject = Objects.requireNonNull(parsedObject);
+ this.variableName = "variableName";
+ this.className = "className";
+ this.info = new VariableInfo(id, parsedObject, variableName, className);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(id, info.id());
+ assertEquals(parsedObject, info.parsedObject());
+ assertEquals(variableName, info.variableName());
+ assertEquals(className, info.className());
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(NullPointerException.class, () -> new VariableInfo(null, parsedObject, variableName, className));
+ assertThrows(NullPointerException.class, () -> new VariableInfo(id, null, variableName, className));
+ assertThrows(NullPointerException.class, () -> new VariableInfo(id, parsedObject, null, className));
+ assertThrows(NullPointerException.class, () -> new VariableInfo(id, parsedObject, variableName, null));
+ }
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestWebViewFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestWebViewFormatter.java
new file mode 100644
index 0000000..ce85da0
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestWebViewFormatter.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.impl.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestWebViewFormatter {
+}
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/WholeConstructorArgs.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/WholeConstructorArgs.java
similarity index 97%
rename from core/src/test/java/com/github/gtache/fxml/compiler/impl/WholeConstructorArgs.java
rename to core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/WholeConstructorArgs.java
index bae93b2..cf0f75c 100644
--- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/WholeConstructorArgs.java
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/WholeConstructorArgs.java
@@ -1,4 +1,4 @@
-package com.github.gtache.fxml.compiler.impl;
+package com.github.gtache.fxml.compiler.impl.internal;
import javafx.beans.NamedArg;
diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedDefineImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedDefineImpl.java
new file mode 100644
index 0000000..c40e831
--- /dev/null
+++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedDefineImpl.java
@@ -0,0 +1,49 @@
+package com.github.gtache.fxml.compiler.parsing.impl;
+
+import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(MockitoExtension.class)
+class TestParsedDefineImpl {
+
+ private final List children;
+ private final ParsedDefine parsedDefine;
+
+ TestParsedDefineImpl(@Mock ParsedObject parsedObject1, @Mock ParsedObject parsedObject2) {
+ this.children = new ArrayList<>(List.of(parsedObject1, parsedObject2));
+ this.parsedDefine = new ParsedDefineImpl(children);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(children, parsedDefine.children());
+ }
+
+ @Test
+ void testCopy() {
+ final var originalChildren = parsedDefine.children();
+ children.clear();
+ assertEquals(originalChildren, parsedDefine.children());
+ }
+
+ @Test
+ void testUnmodifiable() {
+ final var objectChildren = parsedDefine.children();
+ assertThrows(UnsupportedOperationException.class, objectChildren::clear);
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(NullPointerException.class, () -> new ParsedDefineImpl(null));
+ }
+}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java
index 87bd106..10a28e3 100644
--- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java
@@ -1,32 +1,24 @@
package com.github.gtache.fxml.compiler.maven;
-import com.github.gtache.fxml.compiler.ControllerInjection;
-import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.compatibility.impl.GenerationCompatibilityImpl;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
-import com.github.gtache.fxml.compiler.impl.ControllerInjectionImpl;
+import com.github.gtache.fxml.compiler.impl.ControllerInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.GenerationParametersImpl;
-import com.github.gtache.fxml.compiler.impl.GenerationRequestImpl;
-import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
-import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionImpl;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
-import com.github.gtache.fxml.compiler.parsing.ParseException;
-import com.github.gtache.fxml.compiler.parsing.xml.DOMFXMLParser;
+import com.github.gtache.fxml.compiler.maven.internal.CompilationInfo;
+import com.github.gtache.fxml.compiler.maven.internal.CompilationInfoProvider;
+import com.github.gtache.fxml.compiler.maven.internal.Compiler;
+import com.github.gtache.fxml.compiler.maven.internal.ControllerProvider;
+import com.github.gtache.fxml.compiler.maven.internal.FXMLProvider;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
-import org.xml.sax.SAXException;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.nio.file.FileVisitOption;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@@ -35,7 +27,6 @@ import java.util.Map;
*/
@Mojo(name = "compile", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class FXMLCompilerMojo extends AbstractMojo {
- private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
@@ -43,146 +34,61 @@ public class FXMLCompilerMojo extends AbstractMojo {
@Parameter(property = "output-directory", defaultValue = "${project.build.directory}/generated-sources/java", required = true)
private Path outputDirectory;
+ @Parameter(property = "target-version", defaultValue = "21", required = true)
+ private int targetVersion;
+
+ @Parameter(property = "use-image-inputstream-constructor", defaultValue = "true", required = true)
+ private boolean useImageInputStreamConstructor;
+
+ @Parameter(property = "controller-injection", defaultValue = "INSTANCE", required = true)
+ private ControllerInjectionTypes controllerInjectionType;
+
@Parameter(property = "field-injection", defaultValue = "REFLECTION", required = true)
- private ControllerFieldInjectionTypes fieldInjectionTypes;
+ private ControllerFieldInjectionTypes fieldInjectionType;
@Parameter(property = "method-injection", defaultValue = "REFLECTION", required = true)
- private ControllerMethodsInjectionType methodsInjectionType;
+ private ControllerMethodsInjectionType methodInjectionType;
- @Parameter(property = "bundle-injection", defaultValue = "CONSTRUCTOR", required = true)
- private ResourceBundleInjectionTypes bundleInjectionType;
+ @Parameter(property = "resource-injection", defaultValue = "CONSTRUCTOR", required = true)
+ private ResourceBundleInjectionTypes resourceInjectionType;
+
+ @Parameter(property = "resource-map")
+ private Map resourceMap;
- @Parameter(property = "bundle-map")
- private Map bundleMap;
@Override
public void execute() throws MojoExecutionException {
- final var fxmls = getAllFXMLs();
- final var controllerMapping = createControllerMapping(fxmls);
- final var mapping = createMapping(fxmls, controllerMapping);
- compile(mapping);
- }
-
- private Map getAllFXMLs() throws MojoExecutionException {
- final var map = new HashMap();
- for (final var resource : project.getResources()) {
- final var path = Paths.get(resource.getDirectory());
- if (Files.isDirectory(path)) {
- try (final var stream = Files.find(path, Integer.MAX_VALUE, (p, a) -> p.toString().endsWith(".fxml"), FileVisitOption.FOLLOW_LINKS)) {
- final var curList = stream.toList();
- getLog().info("Found " + curList);
- for (final var p : curList) {
- map.put(p, path);
- }
- } catch (final IOException e) {
- throw new MojoExecutionException("Error reading resources", e);
- }
- } else {
- getLog().info("Directory " + path + " does not exist");
- }
+ if (fieldInjectionType == ControllerFieldInjectionTypes.FACTORY && controllerInjectionType != ControllerInjectionTypes.FACTORY) {
+ getLog().warn("Field injection is set to FACTORY : Forcing controller injection to FACTORY");
+ controllerInjectionType = ControllerInjectionTypes.FACTORY;
}
- return map;
+ final var fxmls = FXMLProvider.getFXMLs(project);
+ final var controllerMapping = createControllerMapping(fxmls);
+ final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping);
+ compile(compilationInfoMapping);
}
private static Map createControllerMapping(final Map extends Path, ? extends Path> fxmls) throws MojoExecutionException {
final var mapping = new HashMap();
for (final var fxml : fxmls.keySet()) {
- mapping.put(fxml, getControllerClass(fxml));
+ mapping.put(fxml, ControllerProvider.getController(fxml));
}
return mapping;
}
- private static String getControllerClass(final Path fxml) throws MojoExecutionException {
- try {
- final var documentBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
- final var document = documentBuilder.parse(fxml.toFile());
- document.getDocumentElement().normalize();
-
- final var controller = document.getDocumentElement().getAttribute("fx:controller");
- if (controller.isBlank()) {
- throw new MojoExecutionException("Missing controller attribute for " + fxml);
- } else {
- return controller;
- }
- } catch (final SAXException | IOException | ParserConfigurationException e) {
- throw new MojoExecutionException("Error parsing fxml at " + fxml, e);
- }
- }
-
- private Map createMapping(final Map extends Path, ? extends Path> fxmls, final Map extends Path, String> controllerMapping) throws MojoExecutionException {
- final var compilationInfoProvider = new CompilationInfoProvider(project, outputDirectory, getLog());
+ private Map createCompilationInfoMapping(final Map extends Path, ? extends Path> fxmls, final Map extends Path, String> controllerMapping) throws MojoExecutionException {
final var mapping = new HashMap();
for (final var entry : fxmls.entrySet()) {
- final var info = compilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping);
+ final var info = CompilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping, outputDirectory, project);
mapping.put(entry.getKey(), info);
}
return mapping;
}
private void compile(final Map mapping) throws MojoExecutionException {
- final var generator = new GeneratorImpl();
- final var parser = new DOMFXMLParser();
- final var controllerInfoProvider = new ControllerInfoProvider(getLog());
- try {
- for (final var entry : mapping.entrySet()) {
- final var inputPath = entry.getKey();
- final var info = entry.getValue();
- getLog().info("Parsing " + inputPath + " with " + parser.getClass().getSimpleName());
- final var root = parser.parse(inputPath);
- final var controllerInjection = getControllerInjection(mapping, info);
- final var sourceToGeneratedClassName = getSourceToGeneratedClassName(mapping, info);
- final var sourceToControllerName = getSourceToControllerName(mapping, info);
- final var resourceBundleInjection = new ResourceBundleInjectionImpl(bundleInjectionType, getBundleName(info));
- final var parameters = new GenerationParametersImpl(controllerInjection, sourceToGeneratedClassName, sourceToControllerName, resourceBundleInjection);
- final var controllerInfo = controllerInfoProvider.getControllerInfo(info);
- final var output = info.outputFile();
- final var request = new GenerationRequestImpl(parameters, controllerInfo, root, info.outputClass());
- getLog().info("Compiling " + inputPath);
- final var content = generator.generate(request);
- final var outputDir = output.getParent();
- Files.createDirectories(outputDir);
- Files.writeString(output, content);
- getLog().info("Compiled " + inputPath + " to " + output);
- }
- } catch (final IOException | RuntimeException | ParseException | GenerationException e) {
- throw new MojoExecutionException("Error compiling fxml", e);
- }
+ final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion), useImageInputStreamConstructor, resourceMap,
+ controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType);
+ Compiler.compile(mapping, parameters);
project.addCompileSourceRoot(outputDirectory.toAbsolutePath().toString());
}
-
- private String getBundleName(final CompilationInfo info) {
- return bundleMap == null ? "" : bundleMap.getOrDefault(info.inputFile().toString(), "");
- }
-
- private static Map getSourceToControllerName(final Map mapping, final CompilationInfo info) {
- final var ret = new HashMap();
- for (final var entry : info.includes().entrySet()) {
- ret.put(entry.getKey(), mapping.get(entry.getValue()).controllerClass());
- }
- return ret;
- }
-
- private static Map getSourceToGeneratedClassName(final Map mapping, final CompilationInfo info) {
- final var ret = new HashMap();
- for (final var entry : info.includes().entrySet()) {
- ret.put(entry.getKey(), mapping.get(entry.getValue()).outputClass());
- }
- return ret;
- }
-
- private Map getControllerInjection(final Map compilationInfoMapping, final CompilationInfo info) {
- final var ret = new HashMap();
- ret.put(info.controllerClass(), getControllerInjection(info));
- for (final var entry : info.includes().entrySet()) {
- final var key = entry.getKey();
- final var value = entry.getValue();
- final var subInfo = compilationInfoMapping.get(value);
- ret.put(key, getControllerInjection(subInfo));
- }
- return ret;
- }
-
- private ControllerInjection getControllerInjection(final CompilationInfo info) {
- return new ControllerInjectionImpl(fieldInjectionTypes, methodsInjectionType, info.controllerClass());
- }
}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/CompilationInfo.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/CompilationInfo.java
similarity index 69%
rename from maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/CompilationInfo.java
rename to maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/CompilationInfo.java
index c30f469..df59848 100644
--- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/CompilationInfo.java
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/CompilationInfo.java
@@ -1,4 +1,4 @@
-package com.github.gtache.fxml.compiler.maven;
+package com.github.gtache.fxml.compiler.maven.internal;
import java.nio.file.Path;
import java.util.HashMap;
@@ -10,21 +10,21 @@ import java.util.Set;
/**
* Info about FXML file compilation
*
- * @param inputFile The input file
- * @param outputFile The output file
- * @param outputClass The output class name
- * @param controllerFile The controller file
- * @param controllerClass The controller class name
- * @param injectedFields The injected fields
- * @param injectedMethods The injected methods
- * @param includes The FXML inclusions
+ * @param inputFile The input file
+ * @param outputFile The output file
+ * @param outputClass The output class name
+ * @param controllerFile The controller file
+ * @param controllerClass The controller class name
+ * @param injectedFields The injected fields
+ * @param injectedMethods The injected methods
+ * @param includes The FXML inclusions
+ * @param requiresResourceBundle True if the file requires a resource bundle
*/
-record CompilationInfo(Path inputFile, Path outputFile, String outputClass, Path controllerFile,
- String controllerClass,
- Set injectedFields,
- Set injectedMethods, Map includes) {
+public record CompilationInfo(Path inputFile, Path outputFile, String outputClass, Path controllerFile,
+ String controllerClass, Set injectedFields, Set injectedMethods,
+ Map includes, boolean requiresResourceBundle) {
- CompilationInfo {
+ public CompilationInfo {
Objects.requireNonNull(inputFile);
Objects.requireNonNull(outputFile);
Objects.requireNonNull(outputClass);
@@ -44,6 +44,7 @@ record CompilationInfo(Path inputFile, Path outputFile, String outputClass, Path
private String outputClass;
private Path controllerFile;
private String controllerClass;
+ private boolean requiresResourceBundle;
private final Set injectedFields;
private final Set injectedMethods;
private final Map includes;
@@ -98,8 +99,13 @@ record CompilationInfo(Path inputFile, Path outputFile, String outputClass, Path
return this;
}
+ Builder requiresResourceBundle() {
+ this.requiresResourceBundle = true;
+ return this;
+ }
+
CompilationInfo build() {
- return new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes);
+ return new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle);
}
}
}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/CompilationInfoProvider.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/CompilationInfoProvider.java
similarity index 68%
rename from maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/CompilationInfoProvider.java
rename to maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/CompilationInfoProvider.java
index 1393208..745a7a2 100644
--- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/CompilationInfoProvider.java
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/CompilationInfoProvider.java
@@ -1,8 +1,10 @@
-package com.github.gtache.fxml.compiler.maven;
+package com.github.gtache.fxml.compiler.maven.internal;
+import com.github.gtache.fxml.compiler.maven.FXMLCompilerMojo;
import javafx.event.EventHandler;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
@@ -17,28 +19,32 @@ import java.nio.file.Paths;
import java.util.Map;
import java.util.regex.Pattern;
-import static java.util.Objects.requireNonNull;
-
/**
* Helper class for {@link FXMLCompilerMojo} to provides {@link CompilationInfo}
*/
-class CompilationInfoProvider {
+public final class CompilationInfoProvider {
+ private static final Logger logger = LogManager.getLogger(CompilationInfoProvider.class);
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
private static final Pattern START_DOT_PATTERN = Pattern.compile("^\\.");
- private final MavenProject project;
- private final Path outputDirectory;
- private final Log logger;
-
- CompilationInfoProvider(final MavenProject project, final Path outputDirectory, final Log logger) {
- this.project = requireNonNull(project);
- this.outputDirectory = requireNonNull(outputDirectory);
- this.logger = requireNonNull(logger);
+ private CompilationInfoProvider() {
}
- CompilationInfo getCompilationInfo(final Path root, final Path inputPath, final Map extends Path, String> controllerMapping) throws MojoExecutionException {
- logger.info("Parsing " + inputPath);
+ /**
+ * Gets the compilation info for the given input
+ *
+ * @param root The root path
+ * @param inputPath The input path
+ * @param controllerMapping The controller mapping
+ * @param outputDirectory The output directory
+ * @param project The Maven project
+ * @return The compilation info
+ * @throws MojoExecutionException If an error occurs
+ */
+ public static CompilationInfo getCompilationInfo(final Path root, final Path inputPath, final Map extends Path, String> controllerMapping,
+ final Path outputDirectory, final MavenProject project) throws MojoExecutionException {
+ logger.info("Parsing {}", inputPath);
try {
final var documentBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
final var document = documentBuilder.parse(inputPath.toFile());
@@ -52,8 +58,8 @@ class CompilationInfoProvider {
final var targetPath = Paths.get(replacedPrefixPath.replace(inputFilename, outputFilename));
builder.outputFile(targetPath);
builder.outputClass(outputClass);
- handleNode(document.getDocumentElement(), builder, controllerMapping);
- logger.info(inputPath + " will be compiled to " + targetPath);
+ handleNode(document.getDocumentElement(), builder, controllerMapping, project);
+ logger.info("{} will be compiled to {}", inputPath, targetPath);
return builder.build();
} catch (final SAXException | IOException | ParserConfigurationException e) {
throw new MojoExecutionException("Error parsing fxml at " + inputPath, e);
@@ -84,15 +90,15 @@ class CompilationInfoProvider {
return builder.toString().replace(".fxml", ".java");
}
- private void handleNode(final Node node, final CompilationInfo.Builder builder, final Map extends Path, String> controllerMapping) throws MojoExecutionException {
+ private static void handleNode(final Node node, final CompilationInfo.Builder builder, final Map extends Path, String> controllerMapping, final MavenProject project) throws MojoExecutionException {
if (node.getNodeName().equals("fx:include")) {
handleInclude(node, builder);
}
- handleAttributes(node, builder, controllerMapping);
- handleChildren(node, builder, controllerMapping);
+ handleAttributes(node, builder, controllerMapping, project);
+ handleChildren(node, builder, controllerMapping, project);
}
- private void handleInclude(final Node node, final CompilationInfo.Builder builder) throws MojoExecutionException {
+ private static void handleInclude(final Node node, final CompilationInfo.Builder builder) throws MojoExecutionException {
final var map = node.getAttributes();
if (map == null) {
throw new MojoExecutionException("Missing attributes for include");
@@ -103,7 +109,7 @@ class CompilationInfoProvider {
} else {
final var source = sourceAttr.getNodeValue();
final var path = getRelativePath(builder.inputFile(), source);
- logger.info("Found include " + source);
+ logger.info("Found include {}", source);
builder.addInclude(source, path);
}
}
@@ -113,14 +119,14 @@ class CompilationInfoProvider {
return base.getParent().resolve(relative).normalize();
}
- private void handleChildren(final Node node, final CompilationInfo.Builder builder, final Map extends Path, String> controllerMapping) throws MojoExecutionException {
+ private static void handleChildren(final Node node, final CompilationInfo.Builder builder, final Map extends Path, String> controllerMapping, final MavenProject project) throws MojoExecutionException {
final var nl = node.getChildNodes();
for (var i = 0; i < nl.getLength(); i++) {
- handleNode(nl.item(i), builder, controllerMapping);
+ handleNode(nl.item(i), builder, controllerMapping, project);
}
}
- private void handleAttributes(final Node node, final CompilationInfo.Builder builder, final Map extends Path, String> controllerMapping) throws MojoExecutionException {
+ private static void handleAttributes(final Node node, final CompilationInfo.Builder builder, final Map extends Path, String> controllerMapping, final MavenProject project) throws MojoExecutionException {
final var map = node.getAttributes();
if (map != null) {
for (var i = 0; i < map.getLength(); i++) {
@@ -130,20 +136,20 @@ class CompilationInfoProvider {
if (name.startsWith("on")) {
if (value.startsWith("#")) {
final var methodName = value.replace("#", "");
- logger.debug("Found injected method " + methodName);
+ logger.debug("Found injected method {}", methodName);
builder.addInjectedMethod(methodName);
} else if (value.startsWith("$controller.")) {
final var fieldName = value.replace("$controller.", "");
- logger.debug("Found injected field " + fieldName);
+ logger.debug("Found injected field {}", fieldName);
builder.addInjectedField(fieldName, EventHandler.class.getName());
} else {
throw new MojoExecutionException("Unexpected attribute " + name + " with value " + value);
}
} else if (name.equals("fx:controller")) {
- handleController(value, builder);
+ handleController(value, builder, project);
} else if (name.equals("fx:id")) {
final var type = node.getNodeName();
- logger.debug("Found injected field " + value + " of type " + type);
+ logger.debug("Found injected field {} of type {}", value, type);
if (type.equals("fx:include")) {
final var path = getRelativePath(builder.inputFile(), map.getNamedItem("source").getNodeValue()).normalize();
final var controllerClass = controllerMapping.get(path);
@@ -154,19 +160,21 @@ class CompilationInfoProvider {
} else {
builder.addInjectedField(value, type);
}
+ } else if (value.startsWith("%")) {
+ builder.requiresResourceBundle();
}
}
}
}
- private void handleController(final String controllerClass, final CompilationInfo.Builder builder) throws MojoExecutionException {
+ private static void handleController(final String controllerClass, final CompilationInfo.Builder builder, final MavenProject project) throws MojoExecutionException {
final var subPath = controllerClass.replace(".", "/") + ".java";
final var path = project.getCompileSourceRoots().stream()
.map(s -> Paths.get(s).resolve(subPath))
.filter(Files::exists)
.findFirst()
.orElseThrow(() -> new MojoExecutionException("Cannot find controller " + controllerClass));
- logger.info("Found controller " + controllerClass);
+ logger.info("Found controller {}", controllerClass);
builder.controllerFile(path);
builder.controllerClass(controllerClass);
}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/Compiler.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/Compiler.java
new file mode 100644
index 0000000..b25f8e4
--- /dev/null
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/Compiler.java
@@ -0,0 +1,66 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.GenerationParameters;
+import com.github.gtache.fxml.compiler.Generator;
+import com.github.gtache.fxml.compiler.impl.GenerationRequestImpl;
+import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
+import com.github.gtache.fxml.compiler.parsing.FXMLParser;
+import com.github.gtache.fxml.compiler.parsing.ParseException;
+import com.github.gtache.fxml.compiler.parsing.xml.DOMFXMLParser;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.maven.plugin.MojoExecutionException;
+
+import javax.inject.Named;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+
+/**
+ * Creates compiled Java code
+ */
+@Named
+public final class Compiler {
+
+ private static final Logger logger = LogManager.getLogger(Compiler.class);
+
+ private static final FXMLParser PARSER = new DOMFXMLParser();
+ private static final Generator GENERATOR = new GeneratorImpl();
+
+ private Compiler() {
+ }
+
+ /**
+ * Compiles the given files
+ *
+ * @param mapping The mapping of file to compile to compilation info
+ * @param parameters The generation parameters
+ * @throws MojoExecutionException If an error occurs
+ */
+ public static void compile(final Map mapping, final GenerationParameters parameters) throws MojoExecutionException {
+ for (final var entry : mapping.entrySet()) {
+ compile(entry.getKey(), entry.getValue(), mapping, parameters);
+ }
+ }
+
+ private static void compile(final Path inputPath, final CompilationInfo info, final Map mapping, final GenerationParameters parameters) throws MojoExecutionException {
+ try {
+ logger.info("Parsing {} with {}", inputPath, PARSER.getClass().getSimpleName());
+ final var root = PARSER.parse(inputPath);
+ final var controllerInfo = ControllerInfoProvider.getControllerInfo(info);
+ final var output = info.outputFile();
+ final var sourceInfo = SourceInfoProvider.getSourceInfo(info, mapping);
+ final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, root, info.outputClass());
+ logger.info("Compiling {}", inputPath);
+ final var content = GENERATOR.generate(request);
+ final var outputDir = output.getParent();
+ Files.createDirectories(outputDir);
+ Files.writeString(output, content);
+ logger.info("Compiled {} to {}", inputPath, output);
+ } catch (final IOException | RuntimeException | ParseException | GenerationException e) {
+ throw new MojoExecutionException("Error compiling fxml", e);
+ }
+ }
+}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/ControllerInfoProvider.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerInfoProvider.java
similarity index 62%
rename from maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/ControllerInfoProvider.java
rename to maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerInfoProvider.java
index 4b3ebd3..b89d6b0 100644
--- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/ControllerInfoProvider.java
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerInfoProvider.java
@@ -1,55 +1,45 @@
-package com.github.gtache.fxml.compiler.maven;
+package com.github.gtache.fxml.compiler.maven.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.impl.ClassesFinder;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInfoImpl;
import com.github.gtache.fxml.compiler.impl.ControllerInfoImpl;
+import com.github.gtache.fxml.compiler.maven.FXMLCompilerMojo;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.logging.Log;
import java.io.IOException;
import java.nio.file.Files;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
/**
* Helper class for {@link FXMLCompilerMojo} to provides {@link ControllerInfo}
*/
-class ControllerInfoProvider {
+final class ControllerInfoProvider {
+ private static final Logger logger = LogManager.getLogger(ControllerInfoProvider.class);
private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+(?:static\\s+)?(?[^;]+);");
+ private static final Pattern INITIALIZE_PATTERN = Pattern.compile("void\\s+initialize\\s*\\(\\s*\\)\\s*");
- private static final Set JAVA_LANG_CLASSES;
- static {
- final var set = new HashSet();
- set.add("Object");
- set.add("String");
- set.add("Boolean");
- set.add("Character");
- set.add("Byte");
- set.add("Short");
- set.add("Integer");
- set.add("Long");
- set.add("Float");
- set.add("Double");
- JAVA_LANG_CLASSES = Set.copyOf(set);
+ private ControllerInfoProvider() {
}
- private final Log logger;
-
- ControllerInfoProvider(final Log logger) {
- this.logger = Objects.requireNonNull(logger);
- }
-
- ControllerInfo getControllerInfo(final CompilationInfo info) throws MojoExecutionException {
+ /**
+ * Gets the controller info for the given compilation info
+ *
+ * @param info The compilation info
+ * @return The controller info
+ * @throws MojoExecutionException If an error occurs
+ */
+ static ControllerInfo getControllerInfo(final CompilationInfo info) throws MojoExecutionException {
try {
final var content = Files.readString(info.controllerFile());
final var imports = getImports(content);
@@ -58,16 +48,14 @@ class ControllerInfoProvider {
final var name = fieldInfo.name();
final var type = fieldInfo.type();
if (fillFieldInfo(type, name, content, imports, propertyGenericTypes)) {
- logger.debug("Found injected field " + name + " of type " + type + " with generic types "
- + propertyGenericTypes.get(name) + " in controller " + info.controllerFile());
+ logger.debug("Found injected field {} of type {} with generic types {} in controller {}", name, type, propertyGenericTypes.get(name), info.controllerFile());
} else if (type.contains(".")) {
final var simpleName = type.substring(type.lastIndexOf('.') + 1);
if (fillFieldInfo(simpleName, name, content, imports, propertyGenericTypes)) {
- logger.debug("Found injected field " + name + " of type " + simpleName + " with generic types "
- + propertyGenericTypes.get(name) + " in controller " + info.controllerFile());
+ logger.debug("Found injected field {} of type {} with generic types {} in controller {}", name, simpleName, propertyGenericTypes.get(name), info.controllerFile());
}
} else {
- logger.info("Field " + name + "(" + type + ")" + " not found in controller " + info.controllerFile());
+ logger.info("Field {}({}) not found in controller {}", name, type, info.controllerFile());
}
}
final var handlerHasArgument = new HashMap();
@@ -77,12 +65,13 @@ class ControllerInfoProvider {
if (matcher.find()) {
final var arg = matcher.group("arg");
handlerHasArgument.put(name, arg != null && !arg.isBlank());
- logger.debug("Found injected method " + name + " with argument " + arg + " in controller " + info.controllerFile());
+ logger.debug("Found injected method {} with argument {} in controller {}", name, arg, info.controllerFile());
} else {
throw new MojoExecutionException("Cannot find method " + name + " in controller " + info.controllerFile());
}
}
- return new ControllerInfoImpl(handlerHasArgument, propertyGenericTypes);
+ final var hasInitialize = INITIALIZE_PATTERN.matcher(content).find();
+ return new ControllerInfoImpl(info.controllerClass(), handlerHasArgument, propertyGenericTypes, hasInitialize);
} catch (final IOException e) {
throw new MojoExecutionException("Error reading controller " + info.controllerFile(), e);
}
@@ -115,28 +104,13 @@ class ControllerInfoProvider {
}
private static boolean fillFieldInfo(final String type, final String name, final CharSequence content, final Imports imports, final Map super String, ? super ControllerFieldInfo> fieldInfos) throws MojoExecutionException {
- final var pattern = Pattern.compile(Pattern.quote(type) + "(?<[^>]+>)?\\s+" + Pattern.quote(name) + "\\s*;");
+ final var pattern = Pattern.compile(Pattern.quote(type) + "\\s*(?<.+>)?\\s*" + Pattern.quote(name) + "\\s*;");
final var matcher = pattern.matcher(content);
if (matcher.find()) {
final var genericTypes = matcher.group("type");
if (genericTypes != null && !genericTypes.isBlank()) {
- final var split = genericTypes.replace("<", "").replace(">", "").split(",");
- final var resolved = new ArrayList();
- for (final var s : split) {
- final var trimmed = s.trim();
- if (trimmed.contains(".") || JAVA_LANG_CLASSES.contains(trimmed)) {
- resolved.add(trimmed);
- } else {
- final var imported = imports.imports().get(trimmed);
- if (imported == null) {
- throw new MojoExecutionException("Cannot find class " + trimmed + " probably in one of " + imports.packages() + " ; " +
- "Use non-wildcard imports, use fully qualified name or put the classes in a dependency.");
- } else {
- resolved.add(imported);
- }
- }
- }
- fieldInfos.put(name, new ControllerFieldInfoImpl(name, resolved));
+ final var parsed = new GenericParser(genericTypes, imports.imports()).parse();
+ fieldInfos.put(name, new ControllerFieldInfoImpl(name, parsed));
} else {
fieldInfos.put(name, new ControllerFieldInfoImpl(name, List.of()));
}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerProvider.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerProvider.java
new file mode 100644
index 0000000..eda7c28
--- /dev/null
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerProvider.java
@@ -0,0 +1,53 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.xml.sax.SAXException;
+
+import javax.inject.Named;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.nio.file.Path;
+
+/**
+ * Extracts controller class from FXMLs
+ */
+@Named
+public final class ControllerProvider {
+ private static final DocumentBuilder DOCUMENT_BUILDER;
+
+ static {
+ try {
+ DOCUMENT_BUILDER = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (final ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private ControllerProvider() {
+ }
+
+ /**
+ * Gets the controller class for the given FXML
+ *
+ * @param fxml The FXML
+ * @return The controller class
+ * @throws MojoExecutionException If an error occurs
+ */
+ public static String getController(final Path fxml) throws MojoExecutionException {
+ try {
+ final var document = DOCUMENT_BUILDER.parse(fxml.toFile());
+ document.getDocumentElement().normalize();
+
+ final var controller = document.getDocumentElement().getAttribute("fx:controller");
+ if (controller.isBlank()) {
+ throw new MojoExecutionException("Missing controller attribute for " + fxml);
+ } else {
+ return controller;
+ }
+ } catch (final SAXException | IOException e) {
+ throw new MojoExecutionException("Error parsing fxml at " + fxml, e);
+ }
+ }
+}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FXMLProvider.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FXMLProvider.java
new file mode 100644
index 0000000..b32431a
--- /dev/null
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FXMLProvider.java
@@ -0,0 +1,54 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+
+import javax.inject.Named;
+import java.io.IOException;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Extracts FXML paths from Maven project
+ */
+@Named
+public final class FXMLProvider {
+ private static final Logger logger = LogManager.getLogger(FXMLProvider.class);
+
+ private FXMLProvider() {
+ }
+
+ /**
+ * Returns all the FXML files in the project's resources
+ *
+ * @param project The Maven project
+ * @return A mapping of file to resource directory
+ * @throws MojoExecutionException If an error occurs
+ */
+ public static Map getFXMLs(final MavenProject project) throws MojoExecutionException {
+ final var map = new HashMap();
+ for (final var resource : project.getResources()) {
+ final var path = Paths.get(resource.getDirectory());
+ if (Files.isDirectory(path)) {
+ try (final var stream = Files.find(path, Integer.MAX_VALUE, (p, a) -> p.toString().endsWith(".fxml"), FileVisitOption.FOLLOW_LINKS)) {
+ final var curList = stream.toList();
+ logger.info("Found {}", curList);
+ for (final var p : curList) {
+ map.put(p, path);
+ }
+ } catch (final IOException e) {
+ throw new MojoExecutionException("Error reading resources", e);
+ }
+ } else {
+ logger.info("Directory {} does not exist", path);
+ }
+ }
+ return map;
+ }
+}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FieldInfo.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FieldInfo.java
similarity index 52%
rename from maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FieldInfo.java
rename to maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FieldInfo.java
index 9791f1c..63f9c2b 100644
--- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FieldInfo.java
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FieldInfo.java
@@ -1,4 +1,4 @@
-package com.github.gtache.fxml.compiler.maven;
+package com.github.gtache.fxml.compiler.maven.internal;
import java.util.Objects;
@@ -9,6 +9,14 @@ import java.util.Objects;
* @param name The field name
*/
record FieldInfo(String type, String name) {
+
+ /**
+ * Instantiates a new info
+ *
+ * @param type The field type
+ * @param name The field name
+ * @throws NullPointerException if any parameter is null
+ */
FieldInfo {
Objects.requireNonNull(type);
Objects.requireNonNull(name);
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/GenericParser.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/GenericParser.java
new file mode 100644
index 0000000..1ec1f1c
--- /dev/null
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/GenericParser.java
@@ -0,0 +1,102 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import com.github.gtache.fxml.compiler.GenericTypes;
+import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
+import org.apache.maven.plugin.MojoExecutionException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Parser of generic types
+ */
+final class GenericParser {
+
+ private static final Set JAVA_LANG_CLASSES = Set.of(
+ "Boolean",
+ "Byte",
+ "Character",
+ "Double",
+ "Float",
+ "Integer",
+ "Long",
+ "Object",
+ "Short",
+ "String"
+ );
+
+ private final String content;
+ private final Map imports;
+ private int index;
+
+ GenericParser(final String content, final Map imports) {
+ this.content = Objects.requireNonNull(content);
+ this.imports = Map.copyOf(imports);
+ }
+
+ List parse() throws MojoExecutionException {
+ return parseGenericTypes();
+ }
+
+ private List parseGenericTypes() throws MojoExecutionException {
+ final var ret = new ArrayList();
+ eatSpaces();
+ eat('<');
+ do {
+ eatSpaces();
+ final var type = parseType();
+ eatSpaces();
+ if (peek() == '<') {
+ final var genericTypes = parseGenericTypes();
+ ret.add(new GenericTypesImpl(type, genericTypes));
+ } else if (peek() == '>') {
+ eat('>');
+ return ret;
+ } else if (peek() == ',') {
+ eat(',');
+ ret.add(new GenericTypesImpl(type, List.of()));
+ }
+ } while (index < content.length());
+ return ret;
+ }
+
+ private void eat(final char c) {
+ if (peek() == c) {
+ read();
+ } else {
+ throw new IllegalArgumentException("Expected " + c + " at " + index + " in " + content);
+ }
+ }
+
+ private void eatSpaces() {
+ while (peek() == ' ') {
+ read();
+ }
+ }
+
+ private String parseType() throws MojoExecutionException {
+ final var sb = new StringBuilder();
+ while (peek() != '<' && index < content.length()) {
+ sb.append(read());
+ }
+ final var type = sb.toString();
+ if (type.contains(".") || JAVA_LANG_CLASSES.contains(type)) {
+ return type;
+ } else if (imports.containsKey(type)) {
+ return imports.get(type);
+ } else {
+ throw new MojoExecutionException("Cannot find class " + type + " ; Use fully qualified name or put the classes in a dependency.");
+ }
+ }
+
+ private char peek() {
+ return content.charAt(index);
+ }
+
+ private char read() {
+ return content.charAt(index++);
+ }
+}
diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/SourceInfoProvider.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/SourceInfoProvider.java
new file mode 100644
index 0000000..766a059
--- /dev/null
+++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/SourceInfoProvider.java
@@ -0,0 +1,38 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import com.github.gtache.fxml.compiler.SourceInfo;
+import com.github.gtache.fxml.compiler.impl.SourceInfoImpl;
+import com.github.gtache.fxml.compiler.maven.FXMLCompilerMojo;
+
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class for {@link FXMLCompilerMojo} to provides {@link SourceInfo}
+ */
+final class SourceInfoProvider {
+
+ private SourceInfoProvider() {
+ }
+
+ /**
+ * Provides the {@link SourceInfo} for the given compilation info
+ *
+ * @param info The compilation info
+ * @param mapping The mapping of file to compilation info
+ * @return The source info
+ */
+ static SourceInfo getSourceInfo(final CompilationInfo info, final Map mapping) {
+ final var outputClass = info.outputClass();
+ final var controllerClass = info.controllerClass();
+ final var inputFile = info.inputFile();
+ final var includes = info.includes();
+ final var requiresResourceBundle = info.requiresResourceBundle();
+ final var includesMapping = new HashMap();
+ includes.forEach((k, v) -> includesMapping.put(k, getSourceInfo(mapping.get(v), mapping)));
+ //FIXME mutliple includes
+ return new SourceInfoImpl(outputClass, controllerClass, inputFile, List.copyOf(includesMapping.values()), includesMapping, requiresResourceBundle);
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/TestFXMLCompilerMojo.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/TestFXMLCompilerMojo.java
new file mode 100644
index 0000000..aefe4df
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/TestFXMLCompilerMojo.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.maven;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestFXMLCompilerMojo {
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfo.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfo.java
new file mode 100644
index 0000000..80f52cc
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfo.java
@@ -0,0 +1,107 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(MockitoExtension.class)
+class TestCompilationInfo {
+ private final Path inputFile;
+ private final Path outputFile;
+ private final String outputClass;
+ private final Path controllerFile;
+ private final String controllerClass;
+ private final Set injectedFields;
+ private final Set injectedMethods;
+ private final Map includes;
+ private final boolean requiresResourceBundle;
+ private final CompilationInfo info;
+
+ TestCompilationInfo(@Mock final Path inputFile, @Mock final Path outputFile, @Mock final Path controllerFile, @Mock final FieldInfo fieldInfo) {
+ this.inputFile = Objects.requireNonNull(inputFile);
+ this.outputFile = Objects.requireNonNull(outputFile);
+ this.outputClass = "outputClass";
+ this.controllerFile = Objects.requireNonNull(controllerFile);
+ this.controllerClass = "controllerClass";
+ this.injectedFields = new HashSet<>(Set.of(fieldInfo));
+ this.injectedMethods = new HashSet<>(Set.of("one", "two"));
+ this.includes = new HashMap<>(Map.of("one", Objects.requireNonNull(inputFile)));
+ this.requiresResourceBundle = true;
+ this.info = new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(inputFile, info.inputFile());
+ assertEquals(outputFile, info.outputFile());
+ assertEquals(outputClass, info.outputClass());
+ assertEquals(controllerFile, info.controllerFile());
+ assertEquals(controllerClass, info.controllerClass());
+ assertEquals(injectedFields, info.injectedFields());
+ assertEquals(injectedMethods, info.injectedMethods());
+ assertEquals(includes, info.includes());
+ assertEquals(requiresResourceBundle, info.requiresResourceBundle());
+ }
+
+ @Test
+ void testCopyInjectedFields() {
+ final var originalInjectedFields = info.injectedFields();
+ injectedFields.clear();
+ assertEquals(originalInjectedFields, info.injectedFields());
+ }
+
+ @Test
+ void testCopyInjectedMethods() {
+ final var originalInjectedMethods = info.injectedMethods();
+ injectedMethods.clear();
+ assertEquals(originalInjectedMethods, info.injectedMethods());
+ }
+
+ @Test
+ void testCopyIncludes() {
+ final var originalIncludes = Map.copyOf(includes);
+ includes.clear();
+ assertEquals(originalIncludes, info.includes());
+ }
+
+ @Test
+ void testUnmodifiableInjectedFields() {
+ final var originalInjectedFields = info.injectedFields();
+ assertThrows(UnsupportedOperationException.class, originalInjectedFields::clear);
+ }
+
+ @Test
+ void testUnmodifiableInjectedMethods() {
+ final var originalInjectedMethods = info.injectedMethods();
+ assertThrows(UnsupportedOperationException.class, originalInjectedMethods::clear);
+ }
+
+ @Test
+ void testUnmodifiableIncludes() {
+ final var originalIncludes = Map.copyOf(includes);
+ assertThrows(UnsupportedOperationException.class, originalIncludes::clear);
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(null, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(inputFile, null, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(inputFile, outputFile, null, controllerFile, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(inputFile, outputFile, outputClass, null, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, null, injectedFields, injectedMethods, includes, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, null, injectedMethods, includes, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, null, includes, requiresResourceBundle));
+ assertThrows(NullPointerException.class, () -> new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, null, requiresResourceBundle));
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfoBuilder.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfoBuilder.java
new file mode 100644
index 0000000..e2436e9
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfoBuilder.java
@@ -0,0 +1,59 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@ExtendWith(MockitoExtension.class)
+class TestCompilationInfoBuilder {
+ private final Path inputFile;
+ private final Path outputFile;
+ private final String outputClass;
+ private final Path controllerFile;
+ private final String controllerClass;
+ private final Set injectedFields;
+ private final Set injectedMethods;
+ private final Map includes;
+ private final boolean requiresResourceBundle;
+ private final CompilationInfo info;
+
+ TestCompilationInfoBuilder(@Mock final Path inputFile, @Mock final Path outputFile, @Mock final Path controllerFile, @Mock final FieldInfo fieldInfo) {
+ this.inputFile = Objects.requireNonNull(inputFile);
+ this.outputFile = Objects.requireNonNull(outputFile);
+ this.outputClass = "outputClass";
+ this.controllerFile = Objects.requireNonNull(controllerFile);
+ this.controllerClass = "controllerClass";
+ this.injectedFields = new HashSet<>(Set.of(fieldInfo));
+ this.injectedMethods = new HashSet<>(Set.of("one", "two"));
+ this.includes = new HashMap<>(Map.of("one", Objects.requireNonNull(inputFile)));
+ this.requiresResourceBundle = true;
+ this.info = new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle);
+ }
+
+ @Test
+ void testBuilder() {
+ final var builder = new CompilationInfo.Builder();
+ builder.inputFile(inputFile);
+ assertEquals(inputFile, builder.inputFile());
+ builder.outputFile(outputFile);
+ builder.outputClass(outputClass);
+ builder.controllerFile(controllerFile);
+ builder.controllerClass(controllerClass);
+ injectedFields.forEach(f -> builder.addInjectedField(f.name(), f.type()));
+ injectedMethods.forEach(builder::addInjectedMethod);
+ includes.forEach(builder::addInclude);
+ builder.requiresResourceBundle();
+ final var actual = builder.build();
+ assertEquals(info, actual);
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfoProvider.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfoProvider.java
new file mode 100644
index 0000000..790e882
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompilationInfoProvider.java
@@ -0,0 +1,8 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TestCompilationInfoProvider {
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompiler.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompiler.java
new file mode 100644
index 0000000..c51d395
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompiler.java
@@ -0,0 +1,155 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import com.github.gtache.fxml.compiler.ControllerInfo;
+import com.github.gtache.fxml.compiler.GenerationException;
+import com.github.gtache.fxml.compiler.GenerationParameters;
+import com.github.gtache.fxml.compiler.Generator;
+import com.github.gtache.fxml.compiler.SourceInfo;
+import com.github.gtache.fxml.compiler.impl.GenerationRequestImpl;
+import com.github.gtache.fxml.compiler.parsing.FXMLParser;
+import com.github.gtache.fxml.compiler.parsing.ParseException;
+import com.github.gtache.fxml.compiler.parsing.ParsedObject;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class TestCompiler {
+
+ private final ControllerInfoProvider controllerInfoProvider;
+ private final SourceInfoProvider sourceInfoProvider;
+ private final FXMLParser fxmlParser;
+ private final Generator generator;
+ private final CompilationInfo compilationInfo;
+ private final ParsedObject object;
+ private final ControllerInfo controllerInfo;
+ private final SourceInfo sourceInfo;
+ private final String content;
+ private final GenerationParameters parameters;
+ private final Compiler compiler;
+
+ TestCompiler(@Mock final ControllerInfoProvider controllerInfoProvider, @Mock final SourceInfoProvider sourceInfoProvider,
+ @Mock final FXMLParser fxmlParser, @Mock final CompilationInfo compilationInfo, @Mock final ParsedObject object,
+ @Mock final ControllerInfo controllerInfo, @Mock final SourceInfo sourceInfo,
+ @Mock final GenerationParameters parameters, @Mock final Generator generator) {
+ this.controllerInfoProvider = Objects.requireNonNull(controllerInfoProvider);
+ this.sourceInfoProvider = Objects.requireNonNull(sourceInfoProvider);
+ this.fxmlParser = Objects.requireNonNull(fxmlParser);
+ this.compilationInfo = Objects.requireNonNull(compilationInfo);
+ this.object = Objects.requireNonNull(object);
+ this.controllerInfo = Objects.requireNonNull(controllerInfo);
+ this.sourceInfo = Objects.requireNonNull(sourceInfo);
+ this.content = "content";
+ this.parameters = Objects.requireNonNull(parameters);
+ this.generator = Objects.requireNonNull(generator);
+ this.compiler = new Compiler(controllerInfoProvider, sourceInfoProvider, fxmlParser, generator);
+ }
+
+ @BeforeEach
+ void beforeEach() throws MojoExecutionException, GenerationException, ParseException {
+ when(fxmlParser.parse((Path) any())).thenReturn(object);
+ when(ControllerInfoProvider.getControllerInfo(compilationInfo)).thenReturn(controllerInfo);
+ when(SourceInfoProvider.getSourceInfo(eq(compilationInfo), anyMap())).thenReturn(sourceInfo);
+ when(generator.generate(any())).thenReturn(content);
+ }
+
+ @Test
+ void testCompile(@TempDir final Path tempDir) throws Exception {
+ final var path = tempDir.resolve("fxml1.fxml");
+ final var outputPath = tempDir.resolve("subFolder").resolve("fxml1.java");
+ final var outputClass = "outputClass";
+ when(compilationInfo.outputFile()).thenReturn(outputPath);
+ when(compilationInfo.outputClass()).thenReturn(outputClass);
+ final var mapping = Map.of(path, compilationInfo);
+ final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
+ compiler.compile(mapping, parameters);
+ verify(fxmlParser).parse(path);
+ ControllerInfoProvider.getControllerInfo(compilationInfo);
+ SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
+ verify(generator).generate(request);
+ assertEquals(content, Files.readString(outputPath));
+ }
+
+ @Test
+ void testCompileIOException(@TempDir final Path tempDir) throws Exception {
+ final var path = tempDir.resolve("fxml1.fxml");
+ final var outputPath = Paths.get("/whatever");
+ final var outputClass = "outputClass";
+ when(compilationInfo.outputFile()).thenReturn(outputPath);
+ when(compilationInfo.outputClass()).thenReturn(outputClass);
+ final var mapping = Map.of(path, compilationInfo);
+ final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
+ assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
+ verify(fxmlParser).parse(path);
+ ControllerInfoProvider.getControllerInfo(compilationInfo);
+ SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
+ verify(generator).generate(request);
+ }
+
+ @Test
+ void testCompileRuntimeException(@TempDir final Path tempDir) throws Exception {
+ final var path = tempDir.resolve("fxml1.fxml");
+ final var outputPath = tempDir.resolve("subFolder").resolve("fxml1.java");
+ final var outputClass = "outputClass";
+ when(compilationInfo.outputFile()).thenReturn(outputPath);
+ when(compilationInfo.outputClass()).thenReturn(outputClass);
+ final var mapping = Map.of(path, compilationInfo);
+ final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
+ when(generator.generate(request)).thenThrow(RuntimeException.class);
+ assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
+ verify(fxmlParser).parse(path);
+ ControllerInfoProvider.getControllerInfo(compilationInfo);
+ SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
+ verify(generator).generate(request);
+ }
+
+ @Test
+ void testCompileParseException(@TempDir final Path tempDir) throws Exception {
+ when(fxmlParser.parse((Path) any())).thenThrow(ParseException.class);
+ final var path = tempDir.resolve("fxml1.fxml");
+ final var mapping = Map.of(path, compilationInfo);
+ assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
+ verify(fxmlParser).parse(path);
+ verifyNoInteractions(controllerInfoProvider, sourceInfoProvider, generator);
+ }
+
+ @Test
+ void testCompileGenerationException(@TempDir final Path tempDir) throws Exception {
+ final var path = tempDir.resolve("fxml1.fxml");
+ final var outputPath = tempDir.resolve("subFolder").resolve("fxml1.java");
+ final var outputClass = "outputClass";
+ when(compilationInfo.outputFile()).thenReturn(outputPath);
+ when(compilationInfo.outputClass()).thenReturn(outputClass);
+ final var mapping = Map.of(path, compilationInfo);
+ final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
+ when(generator.generate(request)).thenThrow(GenerationException.class);
+ assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
+ verify(fxmlParser).parse(path);
+ ControllerInfoProvider.getControllerInfo(compilationInfo);
+ SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
+ verify(generator).generate(request);
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(NullPointerException.class, () -> new Compiler(null, sourceInfoProvider, fxmlParser, generator));
+ assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, null, fxmlParser, generator));
+ assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, sourceInfoProvider, null, generator));
+ assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, sourceInfoProvider, fxmlParser, null));
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerInfoProvider.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerInfoProvider.java
new file mode 100644
index 0000000..750952d
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerInfoProvider.java
@@ -0,0 +1,81 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import com.github.gtache.fxml.compiler.impl.ControllerFieldInfoImpl;
+import com.github.gtache.fxml.compiler.impl.ControllerInfoImpl;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class TestControllerInfoProvider {
+
+ private final CompilationInfo compilationInfo;
+ private final ControllerInfoProvider controllerInfoProvider;
+
+ TestControllerInfoProvider(@Mock final CompilationInfo compilationInfo) {
+ this.compilationInfo = Objects.requireNonNull(compilationInfo);
+ this.controllerInfoProvider = new ControllerInfoProvider();
+ }
+
+
+ @Test
+ void testGetControllerInfo(@TempDir final Path tempDir) throws Exception {
+ final var fxml = tempDir.resolve("fxml.fxml");
+ Files.writeString(fxml, """
+ package com.github.gtache.fxml.compiler.maven.internal;
+
+ import javafx.event.EventHandler;
+ import javafx.event.KeyEvent;
+ import javafx.scene.control.*;
+
+ public class LoadController {
+
+ private EventHandler keyEventHandler;
+ private ComboBox comboBox;
+ private Button button;
+ private ComboBox rawBox;
+ private TableColumn> tableColumn;
+
+ @FXML
+ void initialize() {
+ }
+
+ @FXML
+ private void onClick(){
+ }
+
+ @FXML
+ private void onOtherClick(final KeyEvent event){
+ }
+ }
+ """);
+ when(compilationInfo.controllerFile()).thenReturn(fxml);
+ final var expectedInfo = new ControllerInfoImpl("com.github.gtache.fxml.compiler.maven.internal.LoadController",
+ Map.of("onClick", false, "onOtherClick", true), Map.of("keyEventHandler", new ControllerFieldInfoImpl("keyEventHandler", List.of()),
+ "comboBox", new ControllerFieldInfoImpl("comboBox", List.of("String")), "button", new ControllerFieldInfoImpl("button", List.of()),
+ "rawBox", new ControllerFieldInfoImpl("rawBox", List.of()),
+ "tableColumn", new ControllerFieldInfoImpl("tableColumn", List.of("Integer", "javafx.scene.control.ComboBox"))), true);
+ final var actual = ControllerInfoProvider.getControllerInfo(compilationInfo);
+ assertEquals(expectedInfo, actual);
+ }
+
+ @Test
+ void testGetControllerInfoException() {
+ when(compilationInfo.controllerFile()).thenReturn(Paths.get("/whatever"));
+ assertThrows(MojoExecutionException.class, () -> ControllerInfoProvider.getControllerInfo(compilationInfo));
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerProvider.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerProvider.java
new file mode 100644
index 0000000..1142a55
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerProvider.java
@@ -0,0 +1,48 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(MockitoExtension.class)
+class TestControllerProvider {
+
+ private final ControllerProvider provider;
+
+ TestControllerProvider() {
+ this.provider = new ControllerProvider();
+ }
+
+ @Test
+ void testGetController(@TempDir final Path tempDir) throws Exception {
+ final var fxml = tempDir.resolve("fxml.fxml");
+ Files.writeString(fxml, "" +
+ "" +
+ "\n");
+ assertEquals("LoadController", ControllerProvider.getController(fxml));
+ }
+
+ @Test
+ void testGetControllerBlank(@TempDir final Path tempDir) throws Exception {
+ final var fxml = tempDir.resolve("fxml.fxml");
+ Files.writeString(fxml, "" +
+ "" +
+ "\n");
+ assertThrows(MojoExecutionException.class, () -> ControllerProvider.getController(fxml));
+ }
+
+ @Test
+ void testGetControllerError() {
+ assertThrows(MojoExecutionException.class, () -> ControllerProvider.getController(Paths.get("fxml.fxml")));
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFXMLProvider.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFXMLProvider.java
new file mode 100644
index 0000000..68ae3af
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFXMLProvider.java
@@ -0,0 +1,65 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.apache.maven.model.Resource;
+import org.apache.maven.project.MavenProject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class TestFXMLProvider {
+
+ private final MavenProject project;
+ private final FXMLProvider provider;
+
+ TestFXMLProvider(@Mock final MavenProject project) {
+ this.project = Objects.requireNonNull(project);
+ this.provider = new FXMLProvider();
+ }
+
+ @Test
+ void testGetFXMLs(@TempDir final Path tempDir, @TempDir final Path otherTempDir) throws Exception {
+ final var subFolder = tempDir.resolve("subFolder");
+ Files.createFile(subFolder.resolve("subfxml1.fxml"));
+ Files.createFile(subFolder.resolve("subfxml2.fxml"));
+ Files.createFile(tempDir.resolve("fxml1.fxml"));
+ Files.createFile(tempDir.resolve("fxml2.fxml"));
+
+ final var otherSubFolder = otherTempDir.resolve("subFolder");
+ Files.createFile(otherSubFolder.resolve("subfxml1.fxml"));
+ Files.createFile(otherSubFolder.resolve("subfxml2.fxml"));
+ Files.createFile(otherTempDir.resolve("fxml1.fxml"));
+ Files.createFile(otherTempDir.resolve("fxml2.fxml"));
+
+ final var resource1 = mock(Resource.class);
+ final var resource2 = mock(Resource.class);
+ when(resource1.getDirectory()).thenReturn(tempDir.toString());
+ when(resource2.getDirectory()).thenReturn(otherTempDir.toString());
+ when(project.getResources()).thenReturn(List.of(resource1, resource2));
+
+ final var expected = Map.of(
+ subFolder.resolve("subfxml1.fxml"), tempDir,
+ subFolder.resolve("subfxml2.fxml"), tempDir,
+ tempDir.resolve("fxml1.fxml"), tempDir,
+ tempDir.resolve("fxml2.fxml"), tempDir,
+ otherSubFolder.resolve("subfxml1.fxml"), otherTempDir,
+ otherSubFolder.resolve("subfxml2.fxml"), otherTempDir,
+ otherTempDir.resolve("fxml1.fxml"), otherTempDir,
+ otherTempDir.resolve("fxml2.fxml"), otherTempDir
+ );
+ final var map = FXMLProvider.getFXMLs(project);
+ assertEquals(expected, map);
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFieldInfo.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFieldInfo.java
new file mode 100644
index 0000000..e67399d
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFieldInfo.java
@@ -0,0 +1,31 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class TestFieldInfo {
+
+ private final String type;
+ private final String name;
+ private final FieldInfo fieldInfo;
+
+ TestFieldInfo() {
+ this.type = "type";
+ this.name = "name";
+ this.fieldInfo = new FieldInfo(type, name);
+ }
+
+ @Test
+ void testGetters() {
+ assertEquals(type, fieldInfo.type());
+ assertEquals(name, fieldInfo.name());
+ }
+
+ @Test
+ void testIllegal() {
+ assertThrows(NullPointerException.class, () -> new FieldInfo(null, name));
+ assertThrows(NullPointerException.class, () -> new FieldInfo(type, null));
+ }
+}
diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestGenericParser.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestGenericParser.java
new file mode 100644
index 0000000..253ea19
--- /dev/null
+++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestGenericParser.java
@@ -0,0 +1,49 @@
+package com.github.gtache.fxml.compiler.maven.internal;
+
+import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class TestGenericParser {
+
+ @ParameterizedTest
+ @MethodSource("providesParseTests")
+ void testParse(final String genericTypes, final Map imports, final List expectedGenericTypes) throws MojoExecutionException {
+ final var parser = new GenericParser(genericTypes, imports);
+ final var parsed = parser.parse();
+ assertEquals(expectedGenericTypes, parsed);
+ }
+
+ private static Stream providesParseTests() {
+ return Stream.of(
+ Arguments.of("