Fixes generics, only creates one constructor, adds some tests, adds java compatibility options, rework some classes, fixes some problems
This commit is contained in:
276
README.md
276
README.md
@@ -1 +1,275 @@
|
||||
# FXML Compiler
|
||||
# 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
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.github.gtache</groupId>
|
||||
<artifactId>fxml-compiler-maven-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
```
|
||||
|
||||
Optionally add dependencies to the plugin (e.g. when using MediaView and controlsfx):
|
||||
|
||||
```xml
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.github.gtache</groupId>
|
||||
<artifactId>fxml-compiler-maven-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-media</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.controlsfx</groupId>
|
||||
<artifactId>controlsfx</artifactId>
|
||||
<version>${controlsfx.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
```
|
||||
|
||||
## 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.
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.github.gtache.fxml.compiler;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Factory for creating controllers
|
||||
*
|
||||
* @param <T> The type of the controller
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ControllerFactory<T> {
|
||||
|
||||
/**
|
||||
* Creates a controller
|
||||
*
|
||||
* @param fieldMap The assignment of field name to value
|
||||
* @return The created controller
|
||||
*/
|
||||
T create(final Map<String, Object> fieldMap);
|
||||
}
|
||||
@@ -28,5 +28,5 @@ public interface ControllerFieldInfo {
|
||||
*
|
||||
* @return The generic types as a list, empty if not generic or raw
|
||||
*/
|
||||
List<String> genericTypes();
|
||||
List<GenericTypes> genericTypes();
|
||||
}
|
||||
|
||||
@@ -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<String, Boolean> 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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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<String, ControllerInjection> 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<String, String> 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<String, String> sourceToControllerName();
|
||||
Map<String, String> 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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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<GenericTypes> subTypes();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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<SourceInfo> includedSources();
|
||||
|
||||
/**
|
||||
* Returns the mapping of source value to source info
|
||||
*
|
||||
* @return The mapping
|
||||
*/
|
||||
Map<String, SourceInfo> sourceToSourceInfo();
|
||||
|
||||
/**
|
||||
* Returns whether the source or its children requires a resource bundle
|
||||
*
|
||||
* @return True if the subtree requires a resource bundle
|
||||
*/
|
||||
boolean requiresResourceBundle();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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<String, ParsedProperty> attributes() {
|
||||
return object().attributes();
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
default SequencedMap<ParsedProperty, SequencedCollection<ParsedObject>> properties() {
|
||||
return object().properties();
|
||||
}
|
||||
|
||||
@Override
|
||||
default SequencedCollection<ParsedObject> children() {
|
||||
return object().children();
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ public interface ParsedText extends ParsedObject {
|
||||
|
||||
@Override
|
||||
default String className() {
|
||||
return "java.lang.String";
|
||||
return String.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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<ParsedProperty, SequencedCollection<ParsedObject>>();
|
||||
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<ParsedProperty, SequencedCollection<ParsedObject>>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> genericTypes) implements ControllerFieldInfo {
|
||||
public record ControllerFieldInfoImpl(String name, List<GenericTypes> genericTypes) implements ControllerFieldInfo {
|
||||
|
||||
/**
|
||||
* Instantiates a new info
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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<String, Boolean> handlerHasArgument,
|
||||
Map<String, ControllerFieldInfo> fieldInfo) implements ControllerInfo {
|
||||
public record ControllerInfoImpl(String className, Map<String, Boolean> handlerHasArgument,
|
||||
Map<String, ControllerFieldInfo> fieldInfo,
|
||||
boolean hasInitialize) implements ControllerInfo {
|
||||
|
||||
public ControllerInfoImpl {
|
||||
Objects.requireNonNull(className);
|
||||
handlerHasArgument = Map.copyOf(handlerHasArgument);
|
||||
fieldInfo = Map.copyOf(fieldInfo);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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<String, ControllerInjection> controllerInjections,
|
||||
Map<String, String> sourceToGeneratedClassName,
|
||||
Map<String, String> sourceToControllerName,
|
||||
ResourceBundleInjection resourceBundleInjection) implements GenerationParameters {
|
||||
public record GenerationParametersImpl(GenerationCompatibility compatibility, boolean useImageInputStreamConstructor,
|
||||
Map<String, String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<Class<?>, %7$s> controllersMap;
|
||||
private final java.util.Map<Class<?>, 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<Class<?>, %7$s> controllersMap, final java.util.Map<Class<?>, 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> 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<String, Object>();\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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GenericTypes> 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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<SourceInfo> includedSources,
|
||||
Map<String, SourceInfo> sourceToSourceInfo,
|
||||
boolean requiresResourceBundle) implements SourceInfo {
|
||||
|
||||
public SourceInfoImpl {
|
||||
Objects.requireNonNull(generatedClassName);
|
||||
Objects.requireNonNull(controllerClassName);
|
||||
Objects.requireNonNull(sourceFile);
|
||||
includedSources = List.copyOf(includedSources);
|
||||
sourceToSourceInfo = Map.copyOf(sourceToSourceInfo);
|
||||
}
|
||||
}
|
||||
@@ -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<String, String>", "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<java.util.Map<String, Object>, " + 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<String>();
|
||||
return hasDuplicateControllerClass(progress.request().sourceInfo(), set);
|
||||
}
|
||||
|
||||
private static boolean hasDuplicateControllerClass(final SourceInfo info, final Set<String> 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<String>();
|
||||
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<String, String>")).append(bundleVariable).append(" = (java.util.function.Function<String, String>) 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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(";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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<String, String> idToVariableName,
|
||||
Map<String, ParsedObject> idToObject,
|
||||
public record GenerationProgress(GenerationRequest request, Map<String, VariableInfo> idToVariableInfo,
|
||||
Map<String, AtomicInteger> variableNameCounters,
|
||||
SequencedMap<String, String> controllerClassToVariable,
|
||||
SequencedCollection<String> controllerFactoryPostAction,
|
||||
StringBuilder stringBuilder) {
|
||||
|
||||
@@ -31,18 +32,18 @@ public record GenerationProgress(GenerationRequest request, Map<String, String>
|
||||
* 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<String, String>
|
||||
* @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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<java.lang.reflect.Method>", 0);
|
||||
sb.append(" private <T extends javafx.event.Event> 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 <T, U> U callCallbackMethod(final String methodName, final T value, final Class<T> 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 <T> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <T extends javafx.event.Event> 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 <T, U> U callCallbackMethod(final String methodName, final T value, final Class<T> 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 <T> 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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> 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<String, Object>")).append("fieldMap = new java.util.HashMap<String, Object>();\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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> 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<String>();
|
||||
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<String> 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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Class<?>, Boolean> HAS_VALUE_OF = new ConcurrentHashMap<>();
|
||||
private static final Map<Class<?>, Boolean> IS_GENERIC = new ConcurrentHashMap<>();
|
||||
private static final Map<String, String> DEFAULT_PROPERTY = new ConcurrentHashMap<>();
|
||||
private static final Map<Class<?>, Map<String, Method>> METHODS = new ConcurrentHashMap<>();
|
||||
private static final Map<Class<?>, Map<String, Method>> STATIC_METHODS = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Class<?>> classMap = new HashMap<>();
|
||||
private static final Map<Class<?>, Boolean> hasValueOf = new HashMap<>();
|
||||
private static final Map<Class<?>, Boolean> isGeneric = new HashMap<>();
|
||||
private static final Map<String, String> defaultProperty = new HashMap<>();
|
||||
private static final Map<Class<?>, Map<String, Method>> methods = new HashMap<>();
|
||||
private static final Map<Class<?>, Map<String, Method>> staticMethods = new HashMap<>();
|
||||
|
||||
private static final Map<String, Class<?>> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> 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<String>")).append(tmpVariable).append(" = ").append(variableName).append(".getStyleSheets();\n");
|
||||
sb.append(" ").append(tmpVariable).append(".addAll(").append(GenerationCompatibilityHelper.getListOf(progress)).append(String.join(", ", urlVariables)).append("));\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Float> 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<Float> 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<Float> 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<Integer> 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<Integer> 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> formatURL(final GenerationProgress progress, final Collection<String> 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");
|
||||
}
|
||||
}
|
||||
@@ -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 + "\")";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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<ParsedObject> children) implements ParsedDefine {
|
||||
|
||||
public ParsedDefineImpl {
|
||||
Objects.requireNonNull(object);
|
||||
children = List.copyOf(children);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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<String> genericTypes;
|
||||
private final List<GenericTypes> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,29 +16,39 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestControllerInfoImpl {
|
||||
|
||||
private final String className;
|
||||
private final Map<String, Boolean> handlerHasArgument;
|
||||
private final ControllerFieldInfo fieldInfo;
|
||||
private final Map<String, ControllerFieldInfo> 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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<String, ControllerInjection> controllerInjections;
|
||||
private final Map<String, String> sourceToGeneratedClassName;
|
||||
private final Map<String, String> sourceToControllerName;
|
||||
private final ResourceBundleInjection resourceBundleInjection;
|
||||
private final GenerationCompatibility compatibility;
|
||||
private final boolean useImageInputStreamConstructor;
|
||||
private final Map<String, String> 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GenericTypes> 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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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<SourceInfo> includedSources;
|
||||
private final Map<String, SourceInfo> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<String, SourceInfo> 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));
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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<String> 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());
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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<java.lang.String, java.lang.Integer> ", 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));
|
||||
}
|
||||
}
|
||||
@@ -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<String, VariableInfo> idToVariableInfo;
|
||||
private final String variableName;
|
||||
private final ParsedObject object;
|
||||
private final Map<String, ParsedProperty> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<String, String> idToVariableName;
|
||||
private final Map<String, ParsedObject> idToObject;
|
||||
private final VariableInfo variableInfo;
|
||||
private final Map<String, VariableInfo> idToVariableInfo;
|
||||
private final Map<String, AtomicInteger> variableNameCounters;
|
||||
private final SequencedMap<String, String> controllerClassToVariable;
|
||||
private final SequencedCollection<String> 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<String, String>();
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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<String, ParsedProperty> 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("<java.lang.String, java.lang.Integer>", ReflectionHelper.getGenericTypes(progress, parsedObject));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.fxml.compiler.impl;
|
||||
package com.github.gtache.fxml.compiler.impl.internal;
|
||||
|
||||
import javafx.beans.NamedArg;
|
||||
|
||||
@@ -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<ParsedObject> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<String, String> resourceMap;
|
||||
|
||||
@Parameter(property = "bundle-map")
|
||||
private Map<String, String> 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<Path, Path> getAllFXMLs() throws MojoExecutionException {
|
||||
final var map = new HashMap<Path, Path>();
|
||||
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<Path, String> createControllerMapping(final Map<? extends Path, ? extends Path> fxmls) throws MojoExecutionException {
|
||||
final var mapping = new HashMap<Path, String>();
|
||||
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<Path, CompilationInfo> 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<Path, CompilationInfo> createCompilationInfoMapping(final Map<? extends Path, ? extends Path> fxmls, final Map<? extends Path, String> controllerMapping) throws MojoExecutionException {
|
||||
final var mapping = new HashMap<Path, CompilationInfo>();
|
||||
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<Path, CompilationInfo> 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<String, String> getSourceToControllerName(final Map<Path, CompilationInfo> mapping, final CompilationInfo info) {
|
||||
final var ret = new HashMap<String, String>();
|
||||
for (final var entry : info.includes().entrySet()) {
|
||||
ret.put(entry.getKey(), mapping.get(entry.getValue()).controllerClass());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static Map<String, String> getSourceToGeneratedClassName(final Map<Path, CompilationInfo> mapping, final CompilationInfo info) {
|
||||
final var ret = new HashMap<String, String>();
|
||||
for (final var entry : info.includes().entrySet()) {
|
||||
ret.put(entry.getKey(), mapping.get(entry.getValue()).outputClass());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Map<String, ControllerInjection> getControllerInjection(final Map<Path, CompilationInfo> compilationInfoMapping, final CompilationInfo info) {
|
||||
final var ret = new HashMap<String, ControllerInjection>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FieldInfo> injectedFields,
|
||||
Set<String> injectedMethods, Map<String, Path> includes) {
|
||||
public record CompilationInfo(Path inputFile, Path outputFile, String outputClass, Path controllerFile,
|
||||
String controllerClass, Set<FieldInfo> injectedFields, Set<String> injectedMethods,
|
||||
Map<String, Path> 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<FieldInfo> injectedFields;
|
||||
private final Set<String> injectedMethods;
|
||||
private final Map<String, Path> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<Path, CompilationInfo> 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<Path, CompilationInfo> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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+)?(?<import>[^;]+);");
|
||||
private static final Pattern INITIALIZE_PATTERN = Pattern.compile("void\\s+initialize\\s*\\(\\s*\\)\\s*");
|
||||
|
||||
private static final Set<String> JAVA_LANG_CLASSES;
|
||||
|
||||
static {
|
||||
final var set = new HashSet<String>();
|
||||
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<String, Boolean>();
|
||||
@@ -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) + "(?<type><[^>]+>)?\\s+" + Pattern.quote(name) + "\\s*;");
|
||||
final var pattern = Pattern.compile(Pattern.quote(type) + "\\s*(?<type><.+>)?\\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<String>();
|
||||
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()));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Path, Path> getFXMLs(final MavenProject project) throws MojoExecutionException {
|
||||
final var map = new HashMap<Path, Path>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user