Adds maven wrapper ; rework internal a bit ; Adds all tests for core

This commit is contained in:
Guillaume Tâche
2024-12-25 22:40:01 +01:00
parent 38056c43f7
commit 58fbbff7cb
99 changed files with 5028 additions and 1649 deletions

19
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

120
README.md
View File

@@ -6,8 +6,9 @@ This projects aims at generating Java code from FXML files.
## Requirements ## Requirements
- Java 21 (at least for the plugin, the generated code can be compatible with older Java versions) - Java 21+ for the plugin
- Maven 3.8.0 - The generated code can be compatible with older java versions.
- Maven 3.6.3+
## Installation ## Installation
@@ -79,100 +80,55 @@ Optionally add dependencies to the plugin (e.g. when using MediaView and control
## Disadvantages ## Disadvantages
- `fx:script` is not supported
- Possible bugs (file an issue if you see one) - Possible bugs (file an issue if you see one)
- Expression binding is limited - Expression binding is limited
- Probably not fully compatible with all FXML features (file an issue if you need one in specific) - Probably not fully compatible with all FXML features (file an issue if you need one in specific)
## Parameters ## Parameters
### Controller injection
There are two ways to inject controllers into a view:
- `INSTANCE`: Inject the controller instance
- This is the default injection method
- `FACTORY`: Inject the controller factory
- This injection method is required if the FXML tree contains multiple times the same controller class.
- By default, the factory is a `Supplier<Controller>`, but if used in conjunction with `field-injection`set to
`FACTORY`, the factory is a `Function<Map<String, Object>, Controller>`.
### Field injection ### Field injection
There are four ways to inject fields into a controller: There are four ways to inject fields into a controller:
- `REFLECTION`: Inject fields using reflection (like FXMLLoader) - `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 - Slowest method
-
- Fully compatible with FXMLLoader, so this allows easy switching between the two. - Fully compatible with FXMLLoader, so this allows easy switching between the two.
-
- This is the default injection method (for compatibility reasons). - This is the default injection method (for compatibility reasons).
- `ASSIGN`: variable assignment - `ASSIGN`: variable assignment
-
- `controller.field = value` - `controller.field = value`
-
- This means that the field must be accessible from the view (e.g. package-private). - This means that the field must be accessible from the view (e.g. package-private).
- `SETTERS`: controller setters methods - `SETTERS`: controller setters methods
-
- `controller.setField(value)` - `controller.setField(value)`
- `FACTORY`: controller factory - `FACTORY`: controller factory
-
- `controller = factory.create(fieldMap)` - `controller = factory.create(fieldMap)`
- - `factory` is a `Function<Map<String, Object>, Controller>` instance that is created at runtime and passed to the
- `factory` is a `ControllerFactory` instance that is created at runtime and passed to the view. view.
-
- `fieldMap` is a map of field name (String) to value (Object) that is computed during the view `load` method. - `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. - This allows the controller to have final fields.
- This also forces the `controller-injection` method to be `FACTORY`.
### Method injections ### Method injections
There are two ways to inject methods (meaning use them as event handlers) into a controller: There are two ways to inject methods (meaning use them as event handlers) into a controller:
- `REFLECTION`: Inject methods using reflection (like FXMLLoader) - `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 - Slowest method
-
- Fully compatible with FXMLLoader, so this allows easy switching between the two. - Fully compatible with FXMLLoader, so this allows easy switching between the two.
-
- This is the default injection method (for compatibility reasons). - This is the default injection method (for compatibility reasons).
- `REFERENCE`: Directly reference the method - `REFERENCE`: Directly reference the method
-
- `controller.method(event)` - `controller.method(event)`
-
- This means that the method must be accessible from the view (e.g. package-private). - This means that the method must be accessible from the view (e.g. package-private).
### Resource bundle injection ### Resource bundle injection
@@ -180,31 +136,23 @@ There are two ways to inject methods (meaning use them as event handlers) into a
There are three ways to inject resource bundles into a controller: There are three ways to inject resource bundles into a controller:
- `CONSTRUCTOR`: Inject resource bundle in the view constructor - `CONSTRUCTOR`: Inject resource bundle in the view constructor
-
- ```java - ```java
view = new View(controller, resourceBundle); view = new View(controller, resourceBundle);
``` ```
-
- This is the default injection method because it is the most similar to FXMLLoader ( - This is the default injection method because it is the most similar to FXMLLoader (
`FXMLLoader.setResources(resourceBundle)`). `FXMLLoader.setResources(resourceBundle)`).
- `CONSTRUCTOR_FUNCTION`: Injects a function in the view constructor - `CONSTRUCTOR_FUNCTION`: Injects a function in the view constructor
-
- `bundleFunction.apply(key)` - `bundleFunction.apply(key)`
-
- The function takes a string (the key) and returns a string (the value) - The function takes a string (the key) and returns a string (the value)
- This allows using another object than a resource bundle for example - This allows using another object than a resource bundle for example
- `GETTER`: Retrieves the resource bundle using a controller getter method - `CONSTRUCTOR_NAME`: Injects the resource bundle name in the view constructor
-
- `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)` - `ResourceBundle.getBundle(bundleName)`
- Also used when fx:include specifies a resource attribute to pass it to the included view. - `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`: Retrieves the resource bundle using a resource path
- The resource path is passed to the generator (see [Maven Plugin](#maven-plugin)).
## View creation ## View creation
@@ -229,47 +177,43 @@ The smallest constructor will have only one argument: The controller (or control
### Parameters ### Parameters
- output-directory - output-directory
-
- The output directory of the generated classes - The output directory of the generated classes
-
- default: `${project.build.directory}/generated-sources/java`) - default: `${project.build.directory}/generated-sources/java`)
- target-version - target-version
-
- The target Java version for the generated code - The target Java version for the generated code
- default: `21` - default: `21`
- minimum: `8` - minimum: `8`
- File an issue if the generated code is not compatible with the target version - File an issue if the generated code is not compatible with the target version
- use-image-inputstream-constructor - use-image-inputstream-constructor
-
- Use the InputStream constructor for Image instead of the String (URL) one. - Use the InputStream constructor for Image instead of the String (URL) one.
-
- default: `true` - default: `true`
- Disables background loading - Disables background loading
- controller-injection
- The type of controller injections to use (see [Controller injection](#controller-injection))
- default: `INSTANCE`
- field-injection - field-injection
-
- The type of field injections to use (see [Field injection](#field-injection)) - The type of field injections to use (see [Field injection](#field-injection))
- default: `REFLECTION` - default: `REFLECTION`
- method-injection - method-injection
-
- The type of method injections to use (see [Method injection](#method-injection)) - The type of method injections to use (see [Method injection](#method-injection))
- default: `REFLECTION` - default: `REFLECTION`
- bundle-injection - bundle-injection
-
- The type of resource bundle injection to use (see [Resource bundle injection](#resource-bundle-injection)) - The type of resource bundle injection to use (see [Resource bundle injection](#resource-bundle-injection))
- default: `CONSTRUCTOR` - default: `CONSTRUCTOR`
- bundle-map - bundle-map
-
- A map of resource bundle name to resource bundle path - A map of resource bundle name to resource bundle path
- Used with `GET-BUNDLE` injection - Used with `GET-BUNDLE` injection
- default: `{}` - default: `{}`
- parallelism
- The number of threads to use for compilation
- default: `1`
- if `<1`, the number of cores will be used
### Limitations ### Limitations
- Given that the plugin operates during the `generate-sources` phase, it doesn't have access to the classes of the - Given that the plugin operates during the `generate-sources` phase, it doesn't have access to the classes of the
application. application.
-
- The controller info (fields, methods) is obtained from the source file and may therefore be inaccurate. - 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 - Custom classes instantiated in the FXML files are not available during generation and may therefore cause it to
fail. fail.
- If the application uses e.g. WebView, the javafx-web dependency must be added to the plugin dependencies. - If the application uses e.g. WebView, the javafx-web dependency must be added to the plugin dependencies.

View File

@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl; package com.github.gtache.fxml.compiler;
import com.github.gtache.fxml.compiler.InjectionType;
/** /**
* Base field {@link InjectionType}s * Base field {@link InjectionType}s
*/ */
public enum ControllerFieldInjectionTypes implements InjectionType { public enum ControllerFieldInjectionType implements InjectionType {
/** /**
* Inject using variable assignment * Inject using variable assignment
*/ */

View File

@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl; package com.github.gtache.fxml.compiler;
import com.github.gtache.fxml.compiler.InjectionType;
/** /**
* Base controller {@link InjectionType}s * Base controller {@link InjectionType}s
*/ */
public enum ControllerInjectionTypes implements InjectionType { public enum ControllerInjectionType implements InjectionType {
/** /**
* Inject the controller instance * Inject the controller instance
*/ */

View File

@@ -1,6 +1,4 @@
package com.github.gtache.fxml.compiler.impl; package com.github.gtache.fxml.compiler;
import com.github.gtache.fxml.compiler.InjectionType;
/** /**
* Base methods {@link InjectionType}s * Base methods {@link InjectionType}s

View File

@@ -36,26 +36,26 @@ public interface GenerationParameters {
* *
* @return The injection * @return The injection
*/ */
InjectionType controllerInjectionType(); ControllerInjectionType controllerInjectionType();
/** /**
* Returns the field injection to use * Returns the field injection to use
* *
* @return The injection * @return The injection
*/ */
InjectionType fieldInjectionType(); ControllerFieldInjectionType fieldInjectionType();
/** /**
* Returns the method injection to use * Returns the method injection to use
* *
* @return The injection * @return The injection
*/ */
InjectionType methodInjectionType(); ControllerMethodsInjectionType methodInjectionType();
/** /**
* Returns the resource injection to use * Returns the resource injection to use
* *
* @return The injection * @return The injection
*/ */
InjectionType resourceInjectionType(); ResourceBundleInjectionType resourceInjectionType();
} }

View File

@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl; package com.github.gtache.fxml.compiler;
import com.github.gtache.fxml.compiler.InjectionType;
/** /**
* Base {@link InjectionType}s for resource bundles * Base {@link InjectionType}s for resource bundles
*/ */
public enum ResourceBundleInjectionTypes implements InjectionType { public enum ResourceBundleInjectionType implements InjectionType {
/** /**
* Resource bundle is injected in the constructor * Resource bundle is injected in the constructor
*/ */

View File

@@ -0,0 +1,53 @@
package com.github.gtache.fxml.compiler;
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.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestGenerationException {
private final String message;
private final Throwable throwable;
private final String throwableString;
TestGenerationException(@Mock final Throwable throwable) {
this.message = "message";
this.throwable = Objects.requireNonNull(throwable);
this.throwableString = "throwable";
}
@BeforeEach
void beforeEach() {
when(throwable.toString()).thenReturn(throwableString);
}
@Test
void testOnlyMessage() {
final var exception = new GenerationException(message);
assertEquals(message, exception.getMessage());
assertNull(exception.getCause());
}
@Test
void testOnlyCause() {
final var exception = new GenerationException(throwable);
assertEquals(throwableString, exception.getMessage());
assertEquals(throwable, exception.getCause());
}
@Test
void testMessageAndCause() {
final var exception = new GenerationException(message, throwable);
assertEquals(message, exception.getMessage());
assertEquals(throwable, exception.getCause());
}
}

View File

@@ -0,0 +1,53 @@
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.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestParseException {
private final String message;
private final Throwable throwable;
private final String throwableString;
TestParseException(@Mock final Throwable throwable) {
this.message = "message";
this.throwable = Objects.requireNonNull(throwable);
this.throwableString = "throwable";
}
@BeforeEach
void beforeEach() {
when(throwable.toString()).thenReturn(throwableString);
}
@Test
void testOnlyMessage() {
final var exception = new ParseException(message);
assertEquals(message, exception.getMessage());
assertNull(exception.getCause());
}
@Test
void testOnlyCause() {
final var exception = new ParseException(throwable);
assertEquals(throwableString, exception.getMessage());
assertEquals(throwable, exception.getCause());
}
@Test
void testMessageAndCause() {
final var exception = new ParseException(message, throwable);
assertEquals(message, exception.getMessage());
assertEquals(throwable, exception.getCause());
}
}

View File

@@ -18,6 +18,14 @@ public record ControllerInfoImpl(String className, Map<String, Boolean> handlerH
Map<String, ControllerFieldInfo> fieldInfo, Map<String, ControllerFieldInfo> fieldInfo,
boolean hasInitialize) implements ControllerInfo { boolean hasInitialize) implements ControllerInfo {
/**
* Instantiates a new controller info
* @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
* @throws NullPointerException If any parameter is null
*/
public ControllerInfoImpl { public ControllerInfoImpl {
Objects.requireNonNull(className); Objects.requireNonNull(className);
handlerHasArgument = Map.copyOf(handlerHasArgument); handlerHasArgument = Map.copyOf(handlerHasArgument);

View File

@@ -1,7 +1,10 @@
package com.github.gtache.fxml.compiler.impl; package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.GenerationParameters; import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.InjectionType; import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility; import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import java.util.Map; import java.util.Map;
@@ -22,11 +25,23 @@ import static java.util.Objects.requireNonNull;
*/ */
public record GenerationParametersImpl(GenerationCompatibility compatibility, boolean useImageInputStreamConstructor, public record GenerationParametersImpl(GenerationCompatibility compatibility, boolean useImageInputStreamConstructor,
Map<String, String> bundleMap, Map<String, String> bundleMap,
InjectionType controllerInjectionType, ControllerInjectionType controllerInjectionType,
InjectionType fieldInjectionType, ControllerFieldInjectionType fieldInjectionType,
InjectionType methodInjectionType, ControllerMethodsInjectionType methodInjectionType,
InjectionType resourceInjectionType) implements GenerationParameters { ResourceBundleInjectionType resourceInjectionType) implements GenerationParameters {
/**
* Instantiates new parameters
*
* @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
* @throws NullPointerException if any parameter is null
*/
public GenerationParametersImpl { public GenerationParametersImpl {
requireNonNull(compatibility); requireNonNull(compatibility);
bundleMap = Map.copyOf(bundleMap); bundleMap = Map.copyOf(bundleMap);

View File

@@ -20,6 +20,15 @@ import java.util.Objects;
public record GenerationRequestImpl(GenerationParameters parameters, ControllerInfo controllerInfo, public record GenerationRequestImpl(GenerationParameters parameters, ControllerInfo controllerInfo,
SourceInfo sourceInfo, ParsedObject rootObject, SourceInfo sourceInfo, ParsedObject rootObject,
String outputClassName) implements GenerationRequest { String outputClassName) implements GenerationRequest {
/**
* Instantiates a new request
* @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
* @throws NullPointerException If any parameter is null
*/
public GenerationRequestImpl { public GenerationRequestImpl {
Objects.requireNonNull(parameters); Objects.requireNonNull(parameters);
Objects.requireNonNull(controllerInfo); Objects.requireNonNull(controllerInfo);

View File

@@ -6,6 +6,9 @@ import com.github.gtache.fxml.compiler.Generator;
import com.github.gtache.fxml.compiler.impl.internal.GenerationProgress; import com.github.gtache.fxml.compiler.impl.internal.GenerationProgress;
import com.github.gtache.fxml.compiler.impl.internal.HelperProvider; import com.github.gtache.fxml.compiler.impl.internal.HelperProvider;
import java.util.Objects;
import java.util.function.Function;
//TODO handle binding (${}) //TODO handle binding (${})
/** /**
@@ -13,10 +16,28 @@ import com.github.gtache.fxml.compiler.impl.internal.HelperProvider;
*/ */
public class GeneratorImpl implements Generator { public class GeneratorImpl implements Generator {
private final Function<GenerationProgress, HelperProvider> helperProviderFactory;
/**
* Instantiates a new generator
*/
public GeneratorImpl() {
this(HelperProvider::new);
}
/**
* Used for testing
* @param helperProviderFactory The helper provider factory
*/
GeneratorImpl(final Function<GenerationProgress, HelperProvider> helperProviderFactory) {
this.helperProviderFactory = Objects.requireNonNull(helperProviderFactory);
}
@Override @Override
public String generate(final GenerationRequest request) throws GenerationException { public String generate(final GenerationRequest request) throws GenerationException {
final var progress = new GenerationProgress(request); final var progress = new GenerationProgress(request);
final var helperProvider = new HelperProvider(progress); final var helperProvider = helperProviderFactory.apply(progress);
final var className = request.outputClassName(); final var className = request.outputClassName();
final var pkgName = className.substring(0, className.lastIndexOf('.')); final var pkgName = className.substring(0, className.lastIndexOf('.'));
final var simpleClassName = className.substring(className.lastIndexOf('.') + 1); final var simpleClassName = className.substring(className.lastIndexOf('.') + 1);
@@ -43,7 +64,9 @@ public class GeneratorImpl implements Generator {
private static void formatControllerMethod(final GenerationProgress progress, final String controllerInjectionClass) { private static void formatControllerMethod(final GenerationProgress progress, final String controllerInjectionClass) {
final var sb = progress.stringBuilder(); final var sb = progress.stringBuilder();
sb.append(" /**\n"); sb.append(" /**\n");
sb.append(" * Returns the controller if available\n");
sb.append(" * @return The controller\n"); sb.append(" * @return The controller\n");
sb.append(" * @throws IllegalStateException If the view is not loaded\n");
sb.append(" */\n"); sb.append(" */\n");
sb.append(" public ").append(controllerInjectionClass).append(" controller() {\n"); sb.append(" public ").append(controllerInjectionClass).append(" controller() {\n");
sb.append(" if (loaded) {\n"); sb.append(" if (loaded) {\n");

View File

@@ -22,6 +22,16 @@ public record SourceInfoImpl(String generatedClassName, String controllerClassNa
Map<String, SourceInfo> sourceToSourceInfo, Map<String, SourceInfo> sourceToSourceInfo,
boolean requiresResourceBundle) implements SourceInfo { boolean requiresResourceBundle) implements SourceInfo {
/**
* Instantiates a new source info
* @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
* @throws NullPointerException If any parameter is null
*/
public SourceInfoImpl { public SourceInfoImpl {
Objects.requireNonNull(generatedClassName); Objects.requireNonNull(generatedClassName);
Objects.requireNonNull(controllerClassName); Objects.requireNonNull(controllerClassName);

View File

@@ -9,7 +9,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@@ -17,10 +16,8 @@ import java.util.Set;
*/ */
final class ConstructorHelper { final class ConstructorHelper {
private final HelperProvider helperProvider; private ConstructorHelper() {
ConstructorHelper(final HelperProvider helperProvider) {
this.helperProvider = Objects.requireNonNull(helperProvider);
} }
/** /**
@@ -31,22 +28,22 @@ final class ConstructorHelper {
* @return The list of constructor arguments * @return The list of constructor arguments
* @throws GenerationException if an error occurs * @throws GenerationException if an error occurs
*/ */
List<String> getListConstructorArgs(final ConstructorArgs constructorArgs, final ParsedObject parsedObject) throws GenerationException { static List<String> getListConstructorArgs(final ConstructorArgs constructorArgs, final ParsedObject parsedObject) throws GenerationException {
final var args = new ArrayList<String>(constructorArgs.namedArgs().size()); final var args = new ArrayList<String>(constructorArgs.namedArgs().size());
final var valueFormatter = helperProvider.getValueFormatter();
for (final var entry : constructorArgs.namedArgs().entrySet()) { for (final var entry : constructorArgs.namedArgs().entrySet()) {
final var type = entry.getValue().type(); final var parameter = entry.getValue();
final var type = parameter.type();
final var p = parsedObject.attributes().get(entry.getKey()); final var p = parsedObject.attributes().get(entry.getKey());
if (p == null) { if (p == null) {
final var c = parsedObject.properties().entrySet().stream().filter(e -> final var c = parsedObject.properties().entrySet().stream().filter(e ->
e.getKey().name().equals(entry.getKey())).findFirst().orElse(null); e.getKey().name().equals(entry.getKey())).findFirst().orElse(null);
if (c == null) { if (c == null) {
args.add(valueFormatter.toString(entry.getValue().defaultValue(), type)); args.add(ValueFormatter.toString(parameter.defaultValue(), type));
} else { } else {
throw new GenerationException("Constructor using complex property not supported yet"); throw new GenerationException("Constructor using complex property not supported yet");
} }
} else { } else {
args.add(valueFormatter.toString(p.value(), type)); args.add(ValueFormatter.toString(p.value(), type));
} }
} }
return args; return args;
@@ -69,7 +66,8 @@ final class ConstructorHelper {
} }
} }
if (matchingConstructorArgs == null) { if (matchingConstructorArgs == null) {
return Arrays.stream(constructors).filter(c -> c.getParameterCount() == 0).findFirst().map(c -> new ConstructorArgs(c, new LinkedHashMap<>())).orElse(null); return Arrays.stream(constructors).filter(c -> c.getParameterCount() == 0).findFirst()
.map(c -> new ConstructorArgs(c, new LinkedHashMap<>())).orElse(null);
} else { } else {
return matchingConstructorArgs; return matchingConstructorArgs;
} }
@@ -83,6 +81,10 @@ final class ConstructorHelper {
* @return The number of matching arguments * @return The number of matching arguments
*/ */
private static long getMatchingArgsCount(final ConstructorArgs constructorArgs, final Set<String> allPropertyNames) { private static long getMatchingArgsCount(final ConstructorArgs constructorArgs, final Set<String> allPropertyNames) {
return constructorArgs.namedArgs().keySet().stream().filter(allPropertyNames::contains).count(); if (constructorArgs.namedArgs().keySet().stream().anyMatch(s -> !allPropertyNames.contains(s))) {
return 0;
} else {
return constructorArgs.namedArgs().keySet().stream().filter(allPropertyNames::contains).count();
}
} }
} }

View File

@@ -1,10 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInfo; import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty; import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
@@ -18,14 +16,15 @@ import static java.util.Objects.requireNonNull;
final class ControllerInjector { final class ControllerInjector {
private final ControllerInfo controllerInfo; private final ControllerInfo controllerInfo;
private final InjectionType fieldInjectionType; private final ControllerFieldInjectionType fieldInjectionType;
private final InjectionType methodInjectionType; private final ControllerMethodsInjectionType methodInjectionType;
private final StringBuilder sb; private final StringBuilder sb;
private final SequencedCollection<String> controllerFactoryPostAction; private final SequencedCollection<String> controllerFactoryPostAction;
ControllerInjector(final ControllerInfo controllerInfo, final InjectionType fieldInjectionType, final InjectionType methodInjectionType, ControllerInjector(final ControllerInfo controllerInfo, final ControllerFieldInjectionType fieldInjectionType,
final StringBuilder sb, final SequencedCollection<String> controllerFactoryPostAction) { final ControllerMethodsInjectionType methodInjectionType, final StringBuilder sb,
this.controllerInfo = controllerInfo; final SequencedCollection<String> controllerFactoryPostAction) {
this.controllerInfo = requireNonNull(controllerInfo);
this.fieldInjectionType = requireNonNull(fieldInjectionType); this.fieldInjectionType = requireNonNull(fieldInjectionType);
this.methodInjectionType = requireNonNull(methodInjectionType); this.methodInjectionType = requireNonNull(methodInjectionType);
this.sb = requireNonNull(sb); this.sb = requireNonNull(sb);
@@ -37,23 +36,18 @@ final class ControllerInjector {
* *
* @param id The object id * @param id The object id
* @param variable The object variable * @param variable The object variable
* @throws GenerationException if an error occurs
*/ */
void injectControllerField(final String id, final String variable) throws GenerationException { void injectControllerField(final String id, final String variable) {
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes types) { switch (fieldInjectionType) {
switch (types) { case FACTORY ->
case FACTORY -> sb.append(" fieldMap.put(\"").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 ASSIGN -> sb.append(" controller.").append(id).append(" = ").append(variable).append(";\n"); case SETTERS -> {
case SETTERS -> { final var setMethod = GenerationHelper.getSetMethod(id);
final var setMethod = GenerationHelper.getSetMethod(id); sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
}
case REFLECTION ->
sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
} }
} else { case REFLECTION ->
throw new GenerationException("Unknown controller injection type : " + fieldInjectionType); sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
} }
} }
@@ -62,9 +56,8 @@ final class ControllerInjector {
* *
* @param property The property to inject * @param property The property to inject
* @param parentVariable The parent variable * @param parentVariable The parent variable
* @throws GenerationException if an error occurs
*/ */
void injectEventHandlerControllerMethod(final ParsedProperty property, final String parentVariable) throws GenerationException { void injectEventHandlerControllerMethod(final ParsedProperty property, final String parentVariable) {
injectControllerMethod(getEventHandlerMethodInjection(property, parentVariable)); injectControllerMethod(getEventHandlerMethodInjection(property, parentVariable));
} }
@@ -74,9 +67,8 @@ final class ControllerInjector {
* @param property The property to inject * @param property The property to inject
* @param parentVariable The parent variable * @param parentVariable The parent variable
* @param argumentClazz The argument class * @param argumentClazz The argument class
* @throws GenerationException if an error occurs
*/ */
void injectCallbackControllerMethod(final ParsedProperty property, final String parentVariable, final String argumentClazz) throws GenerationException { void injectCallbackControllerMethod(final ParsedProperty property, final String parentVariable, final String argumentClazz) {
injectControllerMethod(getCallbackMethodInjection(property, parentVariable, argumentClazz)); injectControllerMethod(getCallbackMethodInjection(property, parentVariable, argumentClazz));
} }
@@ -84,16 +76,11 @@ final class ControllerInjector {
* Injects a controller method * Injects a controller method
* *
* @param methodInjection The method injection * @param methodInjection The method injection
* @throws GenerationException if an error occurs
*/ */
private void injectControllerMethod(final String methodInjection) throws GenerationException { private void injectControllerMethod(final String methodInjection) {
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) { switch (fieldInjectionType) {
switch (fieldTypes) { case FACTORY -> controllerFactoryPostAction.add(methodInjection);
case FACTORY -> controllerFactoryPostAction.add(methodInjection); case ASSIGN, SETTERS, REFLECTION -> sb.append(methodInjection);
case ASSIGN, SETTERS, REFLECTION -> sb.append(methodInjection);
}
} else {
throw getUnknownInjectionException(fieldInjectionType);
} }
} }
@@ -103,27 +90,22 @@ final class ControllerInjector {
* @param property The property * @param property The property
* @param parentVariable The parent variable * @param parentVariable The parent variable
* @return The method injection * @return The method injection
* @throws GenerationException if an error occurs
*/ */
private String getEventHandlerMethodInjection(final ParsedProperty property, final String parentVariable) throws GenerationException { private String getEventHandlerMethodInjection(final ParsedProperty property, final String parentVariable) {
final var setMethod = GenerationHelper.getSetMethod(property.name()); final var setMethod = GenerationHelper.getSetMethod(property.name());
final var controllerMethod = property.value().replace("#", ""); final var controllerMethod = property.value().replace("#", "");
if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) { return switch (methodInjectionType) {
return switch (methodTypes) { case REFERENCE -> {
case REFERENCE -> { final var hasArgument = controllerInfo.handlerHasArgument(controllerMethod);
final var hasArgument = controllerInfo.handlerHasArgument(controllerMethod); if (hasArgument) {
if (hasArgument) { yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n"; } else {
} else { yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
}
} }
case REFLECTION -> }
" " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n"; case REFLECTION ->
}; " " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n";
} else { };
throw getUnknownInjectionException(methodInjectionType);
}
} }
/** /**
@@ -133,24 +115,15 @@ final class ControllerInjector {
* @param parentVariable The parent variable * @param parentVariable The parent variable
* @param argumentClazz The argument class * @param argumentClazz The argument class
* @return The method injection * @return The method injection
* @throws GenerationException if an error occurs
*/ */
private String getCallbackMethodInjection(final ParsedProperty property, final String parentVariable, final String argumentClazz) throws GenerationException { private String getCallbackMethodInjection(final ParsedProperty property, final String parentVariable, final String argumentClazz) {
final var setMethod = GenerationHelper.getSetMethod(property.name()); final var setMethod = GenerationHelper.getSetMethod(property.name());
final var controllerMethod = property.value().replace("#", ""); final var controllerMethod = property.value().replace("#", "");
if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) { return switch (methodInjectionType) {
return switch (methodTypes) { case REFERENCE ->
case REFERENCE -> " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
" " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n"; case REFLECTION ->
case REFLECTION -> " " + parentVariable + "." + setMethod + "(e -> callCallbackMethod(\"" + controllerMethod + "\", e, " + argumentClazz + "));\n";
" " + parentVariable + "." + setMethod + "(e -> callCallbackMethod(\"" + controllerMethod + "\", e, " + argumentClazz + "));\n"; };
};
} else {
throw getUnknownInjectionException(methodInjectionType);
}
}
private static GenerationException getUnknownInjectionException(final InjectionType type) {
return new GenerationException("Unknown injection type : " + type);
} }
} }

View File

@@ -1,27 +1,27 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty; import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import java.util.Objects;
import java.util.SequencedCollection; import java.util.SequencedCollection;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.EXPRESSION_PREFIX; import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.EXPRESSION_PREFIX;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
/** /**
* Helper methods for {@link GeneratorImpl} to set fields * Helper methods for {@link GeneratorImpl} to set nodes properties using controller's fields
*/ */
final class FieldSetter { final class FieldSetter {
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final InjectionType fieldInjectionType; private final ControllerFieldInjectionType fieldInjectionType;
private final StringBuilder sb; private final StringBuilder sb;
private final SequencedCollection<String> controllerFactoryPostAction; private final SequencedCollection<String> controllerFactoryPostAction;
FieldSetter(final HelperProvider helperProvider, final InjectionType fieldInjectionType, FieldSetter(final HelperProvider helperProvider, final ControllerFieldInjectionType fieldInjectionType,
final StringBuilder sb, final SequencedCollection<String> controllerFactoryPostAction) { final StringBuilder sb, final SequencedCollection<String> controllerFactoryPostAction) {
this.helperProvider = requireNonNull(helperProvider); this.helperProvider = requireNonNull(helperProvider);
this.fieldInjectionType = requireNonNull(fieldInjectionType); this.fieldInjectionType = requireNonNull(fieldInjectionType);
@@ -34,7 +34,7 @@ final class FieldSetter {
* *
* @param property The property to inject * @param property The property to inject
* @param parentVariable The parent variable * @param parentVariable The parent variable
* @throws GenerationException if an error occurs@ * @throws GenerationException if an error occurs
*/ */
void setEventHandler(final ParsedProperty property, final String parentVariable) throws GenerationException { void setEventHandler(final ParsedProperty property, final String parentVariable) throws GenerationException {
setField(property, parentVariable, "javafx.event.EventHandler"); setField(property, parentVariable, "javafx.event.EventHandler");
@@ -50,54 +50,66 @@ final class FieldSetter {
* @throws GenerationException if an error occurs * @throws GenerationException if an error occurs
*/ */
void setField(final ParsedProperty property, final String parentVariable, final String fieldType) throws GenerationException { void setField(final ParsedProperty property, final String parentVariable, final String fieldType) throws GenerationException {
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) { switch (fieldInjectionType) {
switch (fieldTypes) { case ASSIGN -> setAssign(property, parentVariable);
case ASSIGN -> setAssign(property, parentVariable); case FACTORY -> setFactory(property, parentVariable);
case FACTORY -> setFactory(property, parentVariable); case REFLECTION -> setReflection(property, parentVariable, fieldType);
case SETTERS -> setSetter(property, parentVariable); case SETTERS -> setSetter(property, parentVariable);
case REFLECTION -> setReflection(property, parentVariable, fieldType);
}
} else {
throw new GenerationException("Unknown injection type : " + fieldInjectionType);
} }
} }
private void setAssign(final ParsedProperty property, final String parentVariable) { private void setAssign(final ParsedProperty property, final String parentVariable) throws GenerationException {
final var methodName = GenerationHelper.getSetMethod(property); final var methodName = GenerationHelper.getSetMethod(property);
final var value = property.value().replace(EXPRESSION_PREFIX, ""); final var value = property.value().replace(EXPRESSION_PREFIX, "");
sb.append(" ").append(parentVariable).append(".").append(methodName).append("(").append(value).append(");\n"); final var split = value.split("\\.");
final var holderName = split[0];
if (Objects.equals(holderName, "controller")) {
sb.append(" ").append(parentVariable).append(".").append(methodName).append("(").append(value).append(");\n");
} else {
throw new GenerationException("Unexpected variable holder : " + holderName + " ; expected : controller");
}
} }
private void setFactory(final ParsedProperty property, final String parentVariable) { private void setFactory(final ParsedProperty property, final String parentVariable) throws GenerationException {
controllerFactoryPostAction.add(getSetString(property, parentVariable)); controllerFactoryPostAction.add(getSetString(property, parentVariable));
} }
private void setSetter(final ParsedProperty property, final String parentVariable) { private void setSetter(final ParsedProperty property, final String parentVariable) throws GenerationException {
sb.append(getSetString(property, parentVariable)); sb.append(getSetString(property, parentVariable));
} }
private static String getSetString(final ParsedProperty property, final String parentVariable) { private static String getSetString(final ParsedProperty property, final String parentVariable) throws GenerationException {
final var methodName = GenerationHelper.getSetMethod(property); final var methodName = GenerationHelper.getSetMethod(property);
final var value = property.value().replace(EXPRESSION_PREFIX, ""); final var value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\."); final var split = value.split("\\.");
final var getterName = GenerationHelper.getGetMethod(split[1]); final var holderName = split[0];
return " " + parentVariable + "." + methodName + "(" + split[0] + "." + getterName + ");\n"; if (Objects.equals(holderName, "controller")) {
final var getterName = GenerationHelper.getGetMethod(split[1]);
return " " + parentVariable + "." + methodName + "(controller." + getterName + "());\n";
} else {
throw new GenerationException("Unexpected variable holder : " + holderName + " ; expected : controller");
}
} }
private void setReflection(final ParsedProperty property, final String parentVariable, final String fieldType) { private void setReflection(final ParsedProperty property, final String parentVariable, final String fieldType) throws GenerationException {
final var methodName = GenerationHelper.getSetMethod(property); final var methodName = GenerationHelper.getSetMethod(property);
final var value = property.value().replace(EXPRESSION_PREFIX, ""); final var value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\."); final var split = value.split("\\.");
final var fieldName = split[1]; final var holderName = split[0];
sb.append(" try {\n"); if (Objects.equals(holderName, "controller")) {
sb.append(" ").append(helperProvider.getCompatibilityHelper().getStartVar("java.lang.reflect.Field", 0)) final var fieldName = split[1];
.append("field = controller.getClass().getDeclaredField(\"").append(fieldName).append("\");\n"); sb.append(" try {\n");
sb.append(" field.setAccessible(true);\n"); sb.append(" ").append(helperProvider.getCompatibilityHelper().getStartVar("java.lang.reflect.Field", 0))
sb.append(" final var value = (").append(fieldType).append(") field.get(controller);\n"); .append("field = controller.getClass().getDeclaredField(\"").append(fieldName).append("\");\n");
sb.append(" ").append(parentVariable).append(".").append(methodName).append("(value);\n"); sb.append(" field.setAccessible(true);\n");
sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n"); sb.append(" final var value = (").append(fieldType).append(") field.get(controller);\n");
sb.append(" throw new RuntimeException(e);\n"); sb.append(" ").append(parentVariable).append(".").append(methodName).append("(value);\n");
sb.append(" }\n"); sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(e);\n");
sb.append(" }\n");
} else {
throw new GenerationException("Unexpected variable holder : " + holderName + " ; expected : controller");
}
} }
} }

View File

@@ -41,14 +41,17 @@ final class FontFormatter {
final var fp = value.fontPosture(); final var fp = value.fontPosture();
final var size = value.size(); final var size = value.size();
final var name = value.name(); final var name = value.name();
if (url != null) { if (url == null) {
formatURL(url, size, variableName); if (name == null) {
} else if (fw == null && fp == null) { throw new GenerationException("Font must have a name or url : " + parsedObject);
formatNoStyle(name, size, variableName); } else if (fw == null && fp == null) {
formatNoStyle(name, size, variableName);
} else {
formatStyle(fw, fp, size, name, variableName);
}
} else { } else {
formatStyle(fw, fp, size, name, variableName); formatURL(url, size, variableName);
} }
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} else { } else {
throw new GenerationException("Font cannot have children or properties : " + parsedObject); throw new GenerationException("Font cannot have children or properties : " + parsedObject);
} }
@@ -57,7 +60,7 @@ final class FontFormatter {
private void formatURL(final URL url, final double size, final String variableName) { private void formatURL(final URL url, final double size, final String variableName) {
final var urlVariableName = helperProvider.getURLFormatter().formatURL(url.toString()); final var urlVariableName = helperProvider.getURLFormatter().formatURL(url.toString());
sb.append(" final javafx.scene.text.Font ").append(variableName).append(";\n"); sb.append(" final javafx.scene.text.Font ").append(variableName).append(";\n");
sb.append(" try (").append(helperProvider.getCompatibilityHelper().getStartVar("java.io.InputStream", 0)).append(" in = ").append(urlVariableName).append(".openStream()) {\n"); sb.append(" try (").append(helperProvider.getCompatibilityHelper().getStartVar("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(" ").append(variableName).append(" = javafx.scene.text.Font.loadFont(in, ").append(size).append(");\n");
sb.append(" } catch (final java.io.IOException e) {\n"); sb.append(" } catch (final java.io.IOException e) {\n");
sb.append(" throw new RuntimeException(e);\n"); sb.append(" throw new RuntimeException(e);\n");

View File

@@ -1,7 +1,5 @@
package com.github.gtache.fxml.compiler.impl.internal; 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.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject; import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty; import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
@@ -11,8 +9,6 @@ import org.apache.logging.log4j.Logger;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static java.util.Objects.requireNonNull;
/** /**
* Various helper methods for {@link GeneratorImpl} * Various helper methods for {@link GeneratorImpl}
*/ */
@@ -31,42 +27,8 @@ final class GenerationHelper {
static final String BINDING_EXPRESSION_PREFIX = "${"; static final String BINDING_EXPRESSION_PREFIX = "${";
static final String BI_DIRECTIONAL_BINDING_PREFIX = "#{"; static final String BI_DIRECTIONAL_BINDING_PREFIX = "#{";
private final HelperProvider helperProvider; private GenerationHelper() {
private final ControllerInfo controllerInfo;
private final Map<String, VariableInfo> idToVariableInfo;
GenerationHelper(final HelperProvider helperProvider, final ControllerInfo controllerInfo, final Map<String, VariableInfo> idToVariableInfo) {
this.helperProvider = requireNonNull(helperProvider);
this.controllerInfo = requireNonNull(controllerInfo);
this.idToVariableInfo = requireNonNull(idToVariableInfo);
}
/**
* Handles the fx:id attribute of an object
*
* @param parsedObject The parsed object
* @param variableName The variable name
* @throws GenerationException if an error occurs
*/
void handleId(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 (controllerInfo.fieldInfo(idValue) == null) {
className = parsedObject.className();
logger.debug("Not injecting {} because it is not found in controller", idValue);
} else {
final var reflectionHelper = helperProvider.getReflectionHelper();
if (ReflectionHelper.isGeneric(ReflectionHelper.getClass(parsedObject.className()))) {
className = parsedObject.className() + reflectionHelper.getGenericTypes(parsedObject);
} else {
className = parsedObject.className();
}
helperProvider.getControllerInjector().injectControllerField(idValue, variableName);
}
idToVariableInfo.put(idValue, new VariableInfo(idValue, parsedObject, variableName, className));
}
} }
/** /**

View File

@@ -4,27 +4,17 @@ import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.SequencedCollection; import java.util.SequencedCollection;
import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* Used by {@link GeneratorImpl} to track the generation progress * Used by {@link GeneratorImpl} to track the generation progress
* *
* @param request The generation request * @param request The generation request
* @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 controllerFactoryPostAction The controller factory post action for factory injection
* @param stringBuilder The string builder * @param stringBuilder The string builder
*/ */
public record GenerationProgress(GenerationRequest request, Map<String, VariableInfo> idToVariableInfo, public record GenerationProgress(GenerationRequest request,
Map<String, AtomicInteger> variableNameCounters,
SequencedMap<String, String> controllerClassToVariable,
SequencedCollection<String> controllerFactoryPostAction, SequencedCollection<String> controllerFactoryPostAction,
StringBuilder stringBuilder) { StringBuilder stringBuilder) {
@@ -32,18 +22,12 @@ public record GenerationProgress(GenerationRequest request, Map<String, Variable
* Instantiates a new GenerationProgress * Instantiates a new GenerationProgress
* *
* @param request The generation request * @param request The generation request
* @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 controllerFactoryPostAction The controller factory post action
* @param stringBuilder The string builder * @param stringBuilder The string builder
* @throws NullPointerException if any parameter is null * @throws NullPointerException if any parameter is null
*/ */
public GenerationProgress { public GenerationProgress {
Objects.requireNonNull(request); Objects.requireNonNull(request);
Objects.requireNonNull(idToVariableInfo);
Objects.requireNonNull(variableNameCounters);
Objects.requireNonNull(controllerClassToVariable);
Objects.requireNonNull(controllerFactoryPostAction); Objects.requireNonNull(controllerFactoryPostAction);
Objects.requireNonNull(stringBuilder); Objects.requireNonNull(stringBuilder);
} }
@@ -55,17 +39,6 @@ public record GenerationProgress(GenerationRequest request, Map<String, Variable
* @throws NullPointerException if request is null * @throws NullPointerException if request is null
*/ */
public GenerationProgress(final GenerationRequest request) { public GenerationProgress(final GenerationRequest request) {
this(request, new HashMap<>(), new HashMap<>(), new LinkedHashMap<>(), new ArrayList<>(), new StringBuilder()); this(request, new ArrayList<>(), new StringBuilder());
}
/**
* Gets the next available variable name for the given prefix
*
* @param prefix The variable name prefix
* @return The next available variable name
*/
String getNextVariableName(final String prefix) {
final var counter = variableNameCounters.computeIfAbsent(prefix, k -> new AtomicInteger(0));
return prefix + counter.getAndIncrement();
} }
} }

View File

@@ -1,8 +1,7 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.InjectionType; import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes; import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import java.util.Objects; import java.util.Objects;
@@ -12,11 +11,12 @@ import java.util.Objects;
public final class HelperMethodsFormatter { public final class HelperMethodsFormatter {
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final InjectionType fieldInjectionType; private final ControllerFieldInjectionType fieldInjectionType;
private final InjectionType methodInjectionType; private final ControllerMethodsInjectionType methodInjectionType;
private final StringBuilder sb; private final StringBuilder sb;
HelperMethodsFormatter(final HelperProvider helperProvider, final InjectionType fieldInjectionType, final InjectionType methodInjectionType, final StringBuilder sb) { HelperMethodsFormatter(final HelperProvider helperProvider, final ControllerFieldInjectionType fieldInjectionType,
final ControllerMethodsInjectionType methodInjectionType, final StringBuilder sb) {
this.helperProvider = Objects.requireNonNull(helperProvider); this.helperProvider = Objects.requireNonNull(helperProvider);
this.fieldInjectionType = Objects.requireNonNull(fieldInjectionType); this.fieldInjectionType = Objects.requireNonNull(fieldInjectionType);
this.methodInjectionType = Objects.requireNonNull(methodInjectionType); this.methodInjectionType = Objects.requireNonNull(methodInjectionType);
@@ -73,7 +73,7 @@ public final class HelperMethodsFormatter {
sb.append(" .filter(m -> m.getName().equals(methodName))").append(toList).append(";\n"); sb.append(" .filter(m -> m.getName().equals(methodName))").append(toList).append(";\n");
sb.append(" if (methods.size() > 1) {\n"); sb.append(" if (methods.size() > 1) {\n");
sb.append(" ").append(startVariableMethodList).append("eventMethods = methods.stream().filter(m ->\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(" m.getParameterCount() == 2 && clazz.isAssignableFrom(m.getParameterTypes()[1]))").append(toList).append(";\n");
sb.append(" if (eventMethods.size() == 1) {\n"); sb.append(" if (eventMethods.size() == 1) {\n");
sb.append(" method = eventMethods").append(getFirst).append(";\n"); sb.append(" method = eventMethods").append(getFirst).append(";\n");
sb.append(" } else {\n"); sb.append(" } else {\n");
@@ -91,7 +91,7 @@ public final class HelperMethodsFormatter {
sb.append(" }\n"); sb.append(" }\n");
sb.append(" }\n"); sb.append(" }\n");
} }
if (fieldInjectionType == ControllerFieldInjectionTypes.REFLECTION) { if (fieldInjectionType == ControllerFieldInjectionType.REFLECTION) {
sb.append(" private <T> void injectField(final String fieldName, final T object) {\n"); sb.append(" private <T> void injectField(final String fieldName, final T object) {\n");
sb.append(" try {\n"); sb.append(" try {\n");
sb.append(" ").append(compatibilityHelper.getStartVar("java.lang.reflect.Field", 0)).append("field = controller.getClass().getDeclaredField(fieldName);\n"); sb.append(" ").append(compatibilityHelper.getStartVar("java.lang.reflect.Field", 0)).append("field = controller.getClass().getDeclaredField(fieldName);\n");

View File

@@ -14,6 +14,7 @@ public class HelperProvider {
/** /**
* Instantiates a new helper provider * Instantiates a new helper provider
*
* @param progress The generation progress * @param progress The generation progress
*/ */
public HelperProvider(final GenerationProgress progress) { public HelperProvider(final GenerationProgress progress) {
@@ -21,58 +22,66 @@ public class HelperProvider {
this.helpers = new HashMap<>(); this.helpers = new HashMap<>();
} }
ConstructorHelper getConstructorHelper() {
return (ConstructorHelper) helpers.computeIfAbsent(ConstructorHelper.class, c -> new ConstructorHelper(this));
}
ControllerInjector getControllerInjector() { ControllerInjector getControllerInjector() {
final var request = progress.request(); return (ControllerInjector) helpers.computeIfAbsent(ControllerInjector.class, c -> {
final var controllerInfo = request.controllerInfo(); final var request = progress.request();
final var parameters = request.parameters(); final var controllerInfo = request.controllerInfo();
final var fieldInjectionType = parameters.fieldInjectionType(); final var parameters = request.parameters();
final var methodInjectionType = parameters.methodInjectionType(); final var fieldInjectionType = parameters.fieldInjectionType();
final var sb = progress.stringBuilder(); final var methodInjectionType = parameters.methodInjectionType();
final var controllerFactoryPostAction = progress.controllerFactoryPostAction(); final var sb = progress.stringBuilder();
return (ControllerInjector) helpers.computeIfAbsent(ControllerInjector.class, c -> new ControllerInjector(controllerInfo, fieldInjectionType, methodInjectionType, sb, controllerFactoryPostAction)); final var controllerFactoryPostAction = progress.controllerFactoryPostAction();
return new ControllerInjector(controllerInfo, fieldInjectionType, methodInjectionType, sb, controllerFactoryPostAction);
});
} }
FieldSetter getFieldSetter() { FieldSetter getFieldSetter() {
final var fieldInjectionType = progress.request().parameters().fieldInjectionType(); return (FieldSetter) helpers.computeIfAbsent(FieldSetter.class, c -> {
final var sb = progress.stringBuilder(); final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
final var controllerFactoryPostAction = progress.controllerFactoryPostAction(); final var sb = progress.stringBuilder();
return (FieldSetter) helpers.computeIfAbsent(FieldSetter.class, c -> new FieldSetter(this, fieldInjectionType, sb, controllerFactoryPostAction)); final var controllerFactoryPostAction = progress.controllerFactoryPostAction();
return new FieldSetter(this, fieldInjectionType, sb, controllerFactoryPostAction);
});
} }
FontFormatter getFontFormatter() { FontFormatter getFontFormatter() {
final var sb = progress.stringBuilder(); return (FontFormatter) helpers.computeIfAbsent(FontFormatter.class, c -> {
return (FontFormatter) helpers.computeIfAbsent(FontFormatter.class, c -> new FontFormatter(this, sb)); final var sb = progress.stringBuilder();
return new FontFormatter(this, sb);
});
} }
GenerationCompatibilityHelper getCompatibilityHelper() { GenerationCompatibilityHelper getCompatibilityHelper() {
final var compatibility = progress.request().parameters().compatibility(); return (GenerationCompatibilityHelper) helpers.computeIfAbsent(GenerationCompatibilityHelper.class, c -> {
return (GenerationCompatibilityHelper) helpers.computeIfAbsent(GenerationCompatibilityHelper.class, c -> new GenerationCompatibilityHelper(this, compatibility)); final var compatibility = progress.request().parameters().compatibility();
} return new GenerationCompatibilityHelper(this, compatibility);
});
GenerationHelper getGenerationHelper() {
final var controllerInfo = progress.request().controllerInfo();
final var idToVariableInfo = progress.idToVariableInfo();
return (GenerationHelper) helpers.computeIfAbsent(GenerationHelper.class, c -> new GenerationHelper(this, controllerInfo, idToVariableInfo));
} }
public HelperMethodsFormatter getHelperMethodsFormatter() { public HelperMethodsFormatter getHelperMethodsFormatter() {
final var parameters = progress.request().parameters(); return (HelperMethodsFormatter) helpers.computeIfAbsent(HelperMethodsFormatter.class, c -> {
final var fieldInjectionType = parameters.fieldInjectionType(); final var parameters = progress.request().parameters();
final var methodInjectionType = parameters.methodInjectionType(); final var fieldInjectionType = parameters.fieldInjectionType();
final var sb = progress.stringBuilder(); final var methodInjectionType = parameters.methodInjectionType();
return (HelperMethodsFormatter) helpers.computeIfAbsent(HelperMethodsFormatter.class, c -> new HelperMethodsFormatter(this, fieldInjectionType, methodInjectionType, sb)); final var sb = progress.stringBuilder();
return new HelperMethodsFormatter(this, fieldInjectionType, methodInjectionType, sb);
});
} }
ImageFormatter getImageFormatter() { ImageFormatter getImageFormatter() {
return (ImageFormatter) helpers.computeIfAbsent(ImageFormatter.class, c -> new ImageFormatter(this, progress)); return (ImageFormatter) helpers.computeIfAbsent(ImageFormatter.class, c -> {
final var sb = progress.stringBuilder();
final var useImageInputStreamConstructor = progress.request().parameters().useImageInputStreamConstructor();
return new ImageFormatter(this, sb, useImageInputStreamConstructor);
});
} }
public InitializationFormatter getInitializationFormatter() { public InitializationFormatter getInitializationFormatter() {
return (InitializationFormatter) helpers.computeIfAbsent(InitializationFormatter.class, c -> new InitializationFormatter(this, progress)); return (InitializationFormatter) helpers.computeIfAbsent(InitializationFormatter.class, c -> {
final var request = progress.request();
final var sb = progress.stringBuilder();
return new InitializationFormatter(this, request, sb);
});
} }
public LoadMethodFormatter getLoadMethodFormatter() { public LoadMethodFormatter getLoadMethodFormatter() {
@@ -80,7 +89,11 @@ public class HelperProvider {
} }
ObjectFormatter getObjectFormatter() { ObjectFormatter getObjectFormatter() {
return (ObjectFormatter) helpers.computeIfAbsent(ObjectFormatter.class, c -> new ObjectFormatter(this, progress)); return (ObjectFormatter) helpers.computeIfAbsent(ObjectFormatter.class, c -> {
final var request = progress.request();
final var sb = progress.stringBuilder();
return new ObjectFormatter(this, request, sb);
});
} }
PropertyFormatter getPropertyFormatter() { PropertyFormatter getPropertyFormatter() {
@@ -88,30 +101,48 @@ public class HelperProvider {
} }
ReflectionHelper getReflectionHelper() { ReflectionHelper getReflectionHelper() {
final var controllerInfo = progress.request().controllerInfo(); return (ReflectionHelper) helpers.computeIfAbsent(ReflectionHelper.class, c -> {
return (ReflectionHelper) helpers.computeIfAbsent(ReflectionHelper.class, c -> new ReflectionHelper(controllerInfo)); final var controllerInfo = progress.request().controllerInfo();
return new ReflectionHelper(controllerInfo);
});
} }
SceneFormatter getSceneFormatter() { SceneFormatter getSceneFormatter() {
return (SceneFormatter) helpers.computeIfAbsent(SceneFormatter.class, c -> new SceneFormatter(this, progress)); return (SceneFormatter) helpers.computeIfAbsent(SceneFormatter.class, c -> {
final var sb = progress.stringBuilder();
return new SceneFormatter(this, sb);
});
} }
TriangleMeshFormatter getTriangleMeshFormatter() { TriangleMeshFormatter getTriangleMeshFormatter() {
final var sb = progress.stringBuilder(); return (TriangleMeshFormatter) helpers.computeIfAbsent(TriangleMeshFormatter.class, c -> {
return (TriangleMeshFormatter) helpers.computeIfAbsent(TriangleMeshFormatter.class, c -> new TriangleMeshFormatter(this, sb)); final var sb = progress.stringBuilder();
return new TriangleMeshFormatter(this, sb);
});
} }
URLFormatter getURLFormatter() { URLFormatter getURLFormatter() {
return (URLFormatter) helpers.computeIfAbsent(URLFormatter.class, c -> new URLFormatter(this, progress)); return (URLFormatter) helpers.computeIfAbsent(URLFormatter.class, c -> {
final var sb = progress.stringBuilder();
return new URLFormatter(this, sb);
});
} }
ValueFormatter getValueFormatter() { ValueFormatter getValueFormatter() {
final var resourceInjectionType = progress.request().parameters().resourceInjectionType(); return (ValueFormatter) helpers.computeIfAbsent(ValueFormatter.class, c -> {
final var idToVariableInfo = progress.idToVariableInfo(); final var resourceInjectionType = progress.request().parameters().resourceInjectionType();
return (ValueFormatter) helpers.computeIfAbsent(ValueFormatter.class, c -> new ValueFormatter(resourceInjectionType, idToVariableInfo)); return new ValueFormatter(this, resourceInjectionType);
});
}
VariableProvider getVariableProvider() {
return (VariableProvider) helpers.computeIfAbsent(VariableProvider.class, c -> new VariableProvider());
} }
WebViewFormatter getWebViewFormatter() { WebViewFormatter getWebViewFormatter() {
return (WebViewFormatter) helpers.computeIfAbsent(WebViewFormatter.class, c -> new WebViewFormatter(this, progress)); return (WebViewFormatter) helpers.computeIfAbsent(WebViewFormatter.class, c -> {
final var sb = progress.stringBuilder();
return new WebViewFormatter(this, sb);
});
} }
} }

View File

@@ -4,10 +4,9 @@ import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject; import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import java.util.Objects;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID; import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes; import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
import static java.util.Objects.requireNonNull;
/** /**
* Helper methods for {@link GeneratorImpl} to format Images * Helper methods for {@link GeneratorImpl} to format Images
@@ -15,11 +14,13 @@ import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.get
final class ImageFormatter { final class ImageFormatter {
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final GenerationProgress progress; private final StringBuilder sb;
private final boolean useImageInputStreamConstructor;
ImageFormatter(final HelperProvider helperProvider, final GenerationProgress progress) { ImageFormatter(final HelperProvider helperProvider, final StringBuilder sb, final boolean useImageInputStreamConstructor) {
this.helperProvider = Objects.requireNonNull(helperProvider); this.helperProvider = requireNonNull(helperProvider);
this.progress = Objects.requireNonNull(progress); this.sb = requireNonNull(sb);
this.useImageInputStreamConstructor = useImageInputStreamConstructor;
} }
void formatImage(final ParsedObject parsedObject, final String variableName) throws GenerationException { void formatImage(final ParsedObject parsedObject, final String variableName) throws GenerationException {
@@ -30,19 +31,6 @@ final class ImageFormatter {
} }
} }
private void formatInputStream(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(helperProvider.getCompatibilityHelper().getStartVar("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 void doFormatImage(final ParsedObject parsedObject, final String variableName) throws GenerationException { private void doFormatImage(final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var sortedAttributes = getSortedAttributes(parsedObject); final var sortedAttributes = getSortedAttributes(parsedObject);
String url = null; String url = null;
@@ -65,20 +53,32 @@ final class ImageFormatter {
default -> throw new GenerationException("Unknown image attribute : " + property.name()); default -> throw new GenerationException("Unknown image attribute : " + property.name());
} }
} }
if (url == null) {
if (progress.request().parameters().useImageInputStreamConstructor()) { throw new GenerationException("Image must have a url attribute : " + parsedObject);
}
if (useImageInputStreamConstructor) {
formatInputStream(url, requestedWidth, requestedHeight, preserveRatio, smooth, variableName); formatInputStream(url, requestedWidth, requestedHeight, preserveRatio, smooth, variableName);
} else { } else {
formatURL(url, requestedWidth, requestedHeight, preserveRatio, smooth, backgroundLoading, variableName); formatURL(url, requestedWidth, requestedHeight, preserveRatio, smooth, backgroundLoading, variableName);
} }
helperProvider.getGenerationHelper().handleId(parsedObject, variableName); }
private void formatInputStream(final String url, final double requestedWidth,
final double requestedHeight, final boolean preserveRatio, final boolean smooth, final String variableName) {
final var inputStream = helperProvider.getVariableProvider().getNextVariableName("inputStream");
sb.append(" final javafx.scene.image.Image ").append(variableName).append(";\n");
sb.append(" try (").append(helperProvider.getCompatibilityHelper().getStartVar("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 void formatURL(final String url, final double requestedWidth, private void formatURL(final String url, final double requestedWidth,
final double requestedHeight, final boolean preserveRatio, final boolean smooth, final double requestedHeight, final boolean preserveRatio, final boolean smooth,
final boolean backgroundLoading, final String variableName) { final boolean backgroundLoading, final String variableName) {
final var urlString = progress.getNextVariableName("urlStr"); final var urlString = helperProvider.getVariableProvider().getNextVariableName("urlStr");
final var sb = progress.stringBuilder();
final var compatibilityHelper = helperProvider.getCompatibilityHelper(); final var compatibilityHelper = helperProvider.getCompatibilityHelper();
sb.append(compatibilityHelper.getStartVar("String")).append(urlString).append(" = ").append(url).append(".toString();\n"); sb.append(compatibilityHelper.getStartVar("String")).append(urlString).append(" = ").append(url).append(".toString();\n");
sb.append(compatibilityHelper.getStartVar("javafx.scene.image.Image")).append(variableName).append(" = new javafx.scene.image.Image(").append(urlString) sb.append(compatibilityHelper.getStartVar("javafx.scene.image.Image")).append(variableName).append(" = new javafx.scene.image.Image(").append(urlString)

View File

@@ -1,51 +1,75 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.SourceInfo; 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 com.github.gtache.fxml.compiler.parsing.ParsedInclude;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/** /**
* Utility class to provide the view's constructor and fields * Utility class to provide the view's constructor and fields
*/ */
public final class InitializationFormatter { public final class InitializationFormatter {
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final GenerationProgress progress; private final GenerationRequest request;
private final StringBuilder sb;
private final Map<String, String> controllerClassToVariable;
InitializationFormatter(final HelperProvider helperProvider, final GenerationProgress progress) { InitializationFormatter(final HelperProvider helperProvider, final GenerationRequest request, final StringBuilder sb) {
this.helperProvider = Objects.requireNonNull(helperProvider); this(helperProvider, request, sb, new HashMap<>());
this.progress = Objects.requireNonNull(progress);
} }
InitializationFormatter(final HelperProvider helperProvider, final GenerationRequest request, final StringBuilder sb, final Map<String, String> controllerClassToVariable) {
this.helperProvider = requireNonNull(helperProvider);
this.request = requireNonNull(request);
this.sb = requireNonNull(sb);
this.controllerClassToVariable = requireNonNull(controllerClassToVariable);
}
/**
* Formats the class initialization (fields and constructor)
*
* @throws GenerationException if an error occurs
*/
public void formatFieldsAndConstructor() throws GenerationException { public void formatFieldsAndConstructor() throws GenerationException {
final var className = progress.request().outputClassName(); if (!controllerClassToVariable.isEmpty()) {
throw new GenerationException("Method has already been called");
}
final var className = request.outputClassName();
final var simpleClassName = className.substring(className.lastIndexOf('.') + 1); final var simpleClassName = className.substring(className.lastIndexOf('.') + 1);
final var mainControllerClass = progress.request().controllerInfo().className(); final var mainControllerClass = request.controllerInfo().className();
final var isFactory = progress.request().parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY; final var parameters = request.parameters();
final var controllerInjectionType = parameters.controllerInjectionType();
final var fieldInjectionType = parameters.fieldInjectionType();
final var isFactory = controllerInjectionType == ControllerInjectionType.FACTORY;
if (hasDuplicateControllerClass() && !isFactory) { if (hasDuplicateControllerClass() && !isFactory) {
throw new GenerationException("Some controllers in the view tree have the same class ; Factory field injection is required"); throw new GenerationException("Some controllers in the view tree have the same class ; Factory controller injection is required");
} }
fillControllers(); fillControllers();
final var sb = progress.stringBuilder(); final var sortedControllersKeys = controllerClassToVariable.keySet().stream().sorted().toList();
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 controllerArg = getVariableName("controller", isFactory);
final var controllerArgClass = getType(mainControllerClass, isFactory); final var controllerArgClass = getType(mainControllerClass, controllerInjectionType, fieldInjectionType);
final var resourceBundleInfo = getResourceBundleInfo(); final var resourceBundleInfo = getResourceBundleInfo();
final var resourceBundleType = resourceBundleInfo.type(); final var resourceBundleType = resourceBundleInfo.type();
final var resourceBundleArg = resourceBundleInfo.variableName(); final var resourceBundleArg = resourceBundleInfo.variableName();
if (isFactory) { if (isFactory) {
sb.append(" private final ").append(controllerArgClass).append(" ").append(controllerArg).append(";\n"); sb.append(" private final ").append(controllerArgClass).append(" ").append(controllerArg).append(";\n");
sortedControllersKeys.forEach(e -> sb.append(" private final ").append(getType(e, controllerInjectionType, fieldInjectionType)).append(" ").append(controllerClassToVariable.get(e)).append("Factory").append(";\n"));
sb.append(" private ").append(mainControllerClass).append(" controller;\n"); sb.append(" private ").append(mainControllerClass).append(" controller;\n");
} else { } else {
sb.append(" private final ").append(mainControllerClass).append(" controller;\n"); sb.append(" private final ").append(mainControllerClass).append(" controller;\n");
sortedControllersKeys.forEach(e -> sb.append(" private final ").append(getType(e, controllerInjectionType, fieldInjectionType)).append(" ").append(controllerClassToVariable.get(e)).append(";\n"));
} }
if (resourceBundleType != null) { if (resourceBundleType != null) {
sb.append(" private final ").append(resourceBundleType).append(" ").append(resourceBundleArg).append(";\n"); sb.append(" private final ").append(resourceBundleType).append(" ").append(resourceBundleArg).append(";\n");
@@ -55,49 +79,49 @@ public final class InitializationFormatter {
sb.append(" /**\n"); sb.append(" /**\n");
sb.append(" * Instantiates a new ").append(simpleClassName).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"); 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)) sortedControllersKeys.forEach(e -> sb.append(" * @param ").append(getVariableName(controllerClassToVariable.get(e), isFactory))
.append(" The subcontroller ").append(isFactory ? "factory" : "instance").append(" for ").append(c).append("\n")); .append(" The subcontroller ").append(isFactory ? "factory" : "instance").append(" for ").append(e).append("\n"));
if (resourceBundleType != null) { if (resourceBundleType != null) {
sb.append(" * @param ").append(resourceBundleArg).append(" The resource bundle\n"); sb.append(" * @param ").append(resourceBundleArg).append(" The resource bundle\n");
} }
sb.append(" */\n"); sb.append(" */\n");
final var arguments = "final " + controllerArgClass + " " + controllerArg + final var arguments = "final " + controllerArgClass + " " + controllerArg +
((controllerMap.isEmpty()) ? "" : ", ") + ((sortedControllersKeys.isEmpty()) ? "" : ", ") +
controllerMap.entrySet().stream().map(e -> "final " + getType(e.getKey(), isFactory) + " " + getVariableName(e.getValue(), isFactory)) sortedControllersKeys.stream().map(e -> "final " + getType(e, controllerInjectionType, fieldInjectionType) + " " + getVariableName(controllerClassToVariable.get(e), isFactory))
.sorted().collect(Collectors.joining(", ")) .sorted().collect(Collectors.joining(", "))
+ (resourceBundleType == null ? "" : ", final " + resourceBundleType + " " + resourceBundleArg); + (resourceBundleType == null ? "" : ", final " + resourceBundleType + " " + resourceBundleArg);
sb.append(" public ").append(simpleClassName).append("(").append(arguments).append(") {\n"); sb.append(" public ").append(simpleClassName).append("(").append(arguments).append(") {\n");
sb.append(" this.").append(controllerArg).append(" = java.util.Objects.requireNonNull(").append(controllerArg).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")); sortedControllersKeys.forEach(s -> {
final var variableName = getVariableName(controllerClassToVariable.get(s), isFactory);
sb.append(" this.").append(variableName).append(" = java.util.Objects.requireNonNull(").append(variableName).append(");\n");
});
if (resourceBundleType != null) { if (resourceBundleType != null) {
sb.append(" this.").append(resourceBundleArg).append(" = java.util.Objects.requireNonNull(").append(resourceBundleArg).append(");\n"); sb.append(" this.").append(resourceBundleArg).append(" = java.util.Objects.requireNonNull(").append(resourceBundleArg).append(");\n");
} }
sb.append(" }\n"); sb.append(" }\n");
} }
private ResourceBundleInfo getResourceBundleInfo() throws GenerationException { private ResourceBundleInfo getResourceBundleInfo() {
final var injectionType = progress.request().parameters().resourceInjectionType(); final var injectionType = request.parameters().resourceInjectionType();
if (injectionType instanceof final ResourceBundleInjectionTypes types) { return switch (injectionType) {
return switch (types) { case CONSTRUCTOR -> new ResourceBundleInfo("java.util.ResourceBundle", "resourceBundle");
case CONSTRUCTOR -> new ResourceBundleInfo("java.util.ResourceBundle", "resourceBundle"); case CONSTRUCTOR_FUNCTION ->
case CONSTRUCTOR_FUNCTION -> new ResourceBundleInfo("java.util.function.Function<String, String>", "resourceBundleFunction");
new ResourceBundleInfo("java.util.function.Function<String, String>", "resourceBundleFunction"); case CONSTRUCTOR_NAME -> new ResourceBundleInfo("String", "resourceBundleName");
case CONSTRUCTOR_NAME -> new ResourceBundleInfo("String", "resourceBundleName"); case GETTER, GET_BUNDLE -> new ResourceBundleInfo(null, null);
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 record ResourceBundleInfo(String type, String variableName) {
} }
private static String getType(final String controllerClass, final boolean isFactory) { private static String getType(final String controllerClass, final InjectionType controllerInjectionTypes, final InjectionType fieldInjectionTypes) {
if (isFactory) { if (fieldInjectionTypes == ControllerFieldInjectionType.FACTORY) {
return "java.util.function.Function<java.util.Map<String, Object>, " + controllerClass + ">"; return "java.util.function.Function<java.util.Map<String, Object>, " + controllerClass + ">";
} else if (controllerInjectionTypes == ControllerInjectionType.FACTORY) {
return "java.util.function.Supplier<" + controllerClass + ">";
} else { } else {
return controllerClass; return controllerClass;
} }
@@ -105,19 +129,15 @@ public final class InitializationFormatter {
private static String getVariableName(final String variableName, final boolean isFactory) { private static String getVariableName(final String variableName, final boolean isFactory) {
if (isFactory) { if (isFactory) {
return getVariableName(variableName, "Factory"); return variableName + "Factory";
} else { } else {
return variableName; return variableName;
} }
} }
private static String getVariableName(final String variableName, final String suffix) {
return variableName + suffix;
}
private boolean hasDuplicateControllerClass() { private boolean hasDuplicateControllerClass() {
final var set = new HashSet<String>(); final var set = new HashSet<String>();
return hasDuplicateControllerClass(progress.request().sourceInfo(), set); return hasDuplicateControllerClass(request.sourceInfo(), set);
} }
private static boolean hasDuplicateControllerClass(final SourceInfo info, final Set<String> controllers) { private static boolean hasDuplicateControllerClass(final SourceInfo info, final Set<String> controllers) {
@@ -125,15 +145,17 @@ public final class InitializationFormatter {
if (controllers.contains(controllerClass)) { if (controllers.contains(controllerClass)) {
return true; return true;
} }
controllers.add(controllerClass);
return info.includedSources().stream().anyMatch(s -> hasDuplicateControllerClass(s, controllers)); return info.includedSources().stream().anyMatch(s -> hasDuplicateControllerClass(s, controllers));
} }
private void fillControllers() { private void fillControllers() {
progress.request().sourceInfo().includedSources().forEach(this::fillControllers); request.sourceInfo().includedSources().forEach(this::fillControllers);
} }
private void fillControllers(final SourceInfo info) { private void fillControllers(final SourceInfo info) {
progress.controllerClassToVariable().put(info.controllerClassName(), progress.getNextVariableName(GenerationHelper.getVariablePrefix(info.controllerClassName()))); controllerClassToVariable.put(info.controllerClassName(), helperProvider.getVariableProvider()
.getNextVariableName(GenerationHelper.getVariablePrefix(info.controllerClassName())));
info.includedSources().forEach(this::fillControllers); info.includedSources().forEach(this::fillControllers);
} }
@@ -143,58 +165,55 @@ public final class InitializationFormatter {
} }
String formatSubViewConstructorCall(final ParsedInclude include) throws GenerationException { String formatSubViewConstructorCall(final ParsedInclude include) throws GenerationException {
final var request = progress.request();
final var info = request.sourceInfo(); final var info = request.sourceInfo();
final var subInfo = info.sourceToSourceInfo().get(include.source()); final var subInfo = info.sourceToSourceInfo().get(include.source());
if (subInfo == null) { if (subInfo == null) {
throw new GenerationException("Unknown include source : " + include.source()); throw new GenerationException("Unknown include source : " + include.source());
} else { } else {
final var isFactory = request.parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY; final var isFactory = request.parameters().controllerInjectionType() == ControllerInjectionType.FACTORY;
final var subClassName = subInfo.controllerClassName(); final var subClassName = subInfo.controllerClassName();
final var subControllerVariable = getVariableName(progress.controllerClassToVariable().get(subClassName), isFactory); final var subControllerVariable = getVariableName(controllerClassToVariable.get(subClassName), isFactory);
final var subControllers = new HashSet<String>(); final var subControllers = new HashSet<String>();
subInfo.includedSources().forEach(s -> fillControllers(s, subControllers)); 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 arguments = subControllers.stream().sorted().map(c -> getVariableName(controllerClassToVariable.get(c), isFactory)).collect(Collectors.joining(", "));
final var bundleVariable = subInfo.requiresResourceBundle() ? getBundleVariable(include) : null; final var bundleVariable = subInfo.requiresResourceBundle() ? getBundleVariable(include) : null;
final var argumentList = subControllerVariable + (arguments.isEmpty() ? "" : ", " + arguments) + (bundleVariable == null ? "" : ", " + bundleVariable); final var argumentList = subControllerVariable + (arguments.isEmpty() ? "" : ", " + arguments) + (bundleVariable == null ? "" : ", " + bundleVariable);
final var subViewName = subInfo.generatedClassName(); final var subViewName = subInfo.generatedClassName();
final var variable = progress.getNextVariableName(GenerationHelper.getVariablePrefix(subViewName)); final var variable = helperProvider.getVariableProvider().getNextVariableName(GenerationHelper.getVariablePrefix(subViewName));
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(subViewName)).append(variable).append(" = new ").append(subViewName).append("(").append(argumentList).append(");\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar(subViewName)).append(variable).append(" = new ").append(subViewName).append("(").append(argumentList).append(");\n");
return variable; return variable;
} }
} }
private String getBundleVariable(final ParsedInclude include) throws GenerationException { private String getBundleVariable(final ParsedInclude include) {
final var info = getResourceBundleInfo(); final var info = getResourceBundleInfo();
if (info.type() == null) { if (info.type() == null) {
return null; return null;
} else if (include.resources() == null) { } else if (include.resources() == null) {
return info.variableName(); return info.variableName();
} else { } else {
final var sb = progress.stringBuilder(); final var compatibilityHelper = helperProvider.getCompatibilityHelper();
if (progress.request().parameters().resourceInjectionType() instanceof final ResourceBundleInjectionTypes types) { final var variableProvider = helperProvider.getVariableProvider();
final var compatibilityHelper = helperProvider.getCompatibilityHelper(); return switch (request.parameters().resourceInjectionType()) {
return switch (types) { case GETTER, GET_BUNDLE -> null;
case GETTER, GET_BUNDLE -> null; case CONSTRUCTOR_NAME -> {
case CONSTRUCTOR_NAME -> { final var bundleVariable = variableProvider.getNextVariableName("resourceBundleName");
final var bundleVariable = progress.getNextVariableName("resourceBundleName"); sb.append(compatibilityHelper.getStartVar("String")).append(bundleVariable).append(" = \"").append(include.resources()).append("\";\n");
sb.append(compatibilityHelper.getStartVar("String")).append(bundleVariable).append(" = \"").append(include.resources()).append("\";\n"); yield bundleVariable;
yield bundleVariable; }
} case CONSTRUCTOR_FUNCTION -> {
case CONSTRUCTOR_FUNCTION -> { final var bundleVariable = variableProvider.getNextVariableName("resourceBundle");
final var bundleVariable = progress.getNextVariableName("resourceBundleFunction"); sb.append(compatibilityHelper.getStartVar("java.util.ResourceBundle")).append(bundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(include.resources()).append("\");\n");
sb.append(compatibilityHelper.getStartVar("java.util.function.Function<String, String>")).append(bundleVariable).append(" = (java.util.function.Function<String, String>) s -> \"").append(include.resources()).append("\";\n"); final var bundleFunctionVariable = variableProvider.getNextVariableName("resourceBundleFunction");
yield bundleVariable; sb.append(compatibilityHelper.getStartVar("java.util.function.Function<String, String>")).append(bundleFunctionVariable).append(" = (java.util.function.Function<String, String>) s -> ").append(bundleVariable).append(".getString(s);\n");
} yield bundleFunctionVariable;
case CONSTRUCTOR -> { }
final var bundleVariable = progress.getNextVariableName("resourceBundle"); case CONSTRUCTOR -> {
sb.append(compatibilityHelper.getStartVar("java.util.ResourceBundle")).append(bundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(include.resources()).append("\");\n"); final var bundleVariable = variableProvider.getNextVariableName("resourceBundle");
yield bundleVariable; sb.append(compatibilityHelper.getStartVar("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());
}
} }
} }
} }

View File

@@ -1,9 +1,10 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes; import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
@@ -29,7 +30,8 @@ public final class LoadMethodFormatter {
final var request = progress.request(); final var request = progress.request();
final var rootObject = request.rootObject(); final var rootObject = request.rootObject();
final var parameters = progress.request().parameters(); final var parameters = progress.request().parameters();
final var controllerInjectionType = parameters.fieldInjectionType(); final var controllerInjectionType = parameters.controllerInjectionType();
final var fieldInjectionType = parameters.fieldInjectionType();
final var controllerClass = progress.request().controllerInfo().className(); final var controllerClass = progress.request().controllerInfo().className();
final var sb = progress.stringBuilder(); final var sb = progress.stringBuilder();
sb.append(" /**\n"); sb.append(" /**\n");
@@ -43,35 +45,38 @@ public final class LoadMethodFormatter {
sb.append(" }\n"); sb.append(" }\n");
final var resourceBundleInjection = parameters.resourceInjectionType(); final var resourceBundleInjection = parameters.resourceInjectionType();
final var generationCompatibilityHelper = helperProvider.getCompatibilityHelper(); final var generationCompatibilityHelper = helperProvider.getCompatibilityHelper();
if (resourceBundleInjection == ResourceBundleInjectionTypes.CONSTRUCTOR_NAME) { if (resourceBundleInjection == ResourceBundleInjectionType.CONSTRUCTOR_NAME) {
sb.append(generationCompatibilityHelper.getStartVar("java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"resourceBundleName\");\n"); sb.append(generationCompatibilityHelper.getStartVar("java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(resourceBundleName);\n");
} else if (resourceBundleInjection == ResourceBundleInjectionTypes.GET_BUNDLE && parameters.bundleMap().containsKey(controllerClass)) { } else if (resourceBundleInjection == ResourceBundleInjectionType.GET_BUNDLE && parameters.bundleMap().containsKey(controllerClass)) {
sb.append(generationCompatibilityHelper.getStartVar("java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"").append(parameters.bundleMap().get(controllerClass)).append("\");\n"); sb.append(generationCompatibilityHelper.getStartVar("java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"")
.append(parameters.bundleMap().get(controllerClass)).append("\");\n");
} }
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) { if (fieldInjectionType == ControllerFieldInjectionType.FACTORY) {
sb.append(generationCompatibilityHelper.getStartVar("java.util.Map<String, Object>")).append("fieldMap = new java.util.HashMap<String, Object>();\n"); sb.append(generationCompatibilityHelper.getStartVar("java.util.Map<String, Object>")).append("fieldMap = new java.util.HashMap<String, Object>();\n");
} else if (controllerInjectionType == ControllerInjectionType.FACTORY) {
sb.append(" controller = controllerFactory.create();\n");
} }
final var variableName = progress.getNextVariableName(GenerationHelper.getVariablePrefix(rootObject)); final var variableName = helperProvider.getVariableProvider().getNextVariableName(GenerationHelper.getVariablePrefix(rootObject));
helperProvider.getObjectFormatter().format(rootObject, variableName); helperProvider.getObjectFormatter().format(rootObject, variableName);
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) { if (fieldInjectionType == ControllerFieldInjectionType.FACTORY) {
sb.append(" controller = (").append(controllerClass).append(") controllerFactory.create(fieldMap);\n"); sb.append(" controller = controllerFactory.create(fieldMap);\n");
progress.controllerFactoryPostAction().forEach(sb::append); progress.controllerFactoryPostAction().forEach(sb::append);
} }
if (parameters.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) { if (request.controllerInfo().hasInitialize()) {
sb.append(" try {\n"); if (parameters.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) {
sb.append(" ").append(generationCompatibilityHelper.getStartVar("java.lang.reflect.Method", 0)).append("initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n"); sb.append(" try {\n");
sb.append(" initialize.setAccessible(true);\n"); sb.append(" ").append(generationCompatibilityHelper.getStartVar("java.lang.reflect.Method", 0)).append("initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n");
sb.append(" initialize.invoke(controller);\n"); sb.append(" initialize.setAccessible(true);\n");
sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException e) {\n"); sb.append(" initialize.invoke(controller);\n");
sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n"); sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {\n");
sb.append(" } catch (final NoSuchMethodException ignored) {\n"); sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n");
sb.append(" }\n"); sb.append(" }\n");
} else { } else {
sb.append(" controller.initialize();\n"); sb.append(" controller.initialize();\n");
}
} }
sb.append(" loaded = true;\n"); sb.append(" loaded = true;\n");
sb.append(" return (T) ").append(variableName).append(";\n"); sb.append(" return (T) ").append(variableName).append(";\n");
sb.append(" }\n"); sb.append(" }\n");
} }
} }

View File

@@ -1,6 +1,7 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.*; import com.github.gtache.fxml.compiler.parsing.*;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl; import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
@@ -21,7 +22,7 @@ import static java.util.Objects.requireNonNull;
/** /**
* Helper methods for {@link GeneratorImpl} to format properties * Helper methods for {@link GeneratorImpl} to format properties
*/ */
public final class ObjectFormatter { final class ObjectFormatter {
private static final Logger logger = LogManager.getLogger(ObjectFormatter.class); private static final Logger logger = LogManager.getLogger(ObjectFormatter.class);
@@ -47,11 +48,13 @@ public final class ObjectFormatter {
); );
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final GenerationProgress progress; private final GenerationRequest request;
private final StringBuilder sb;
ObjectFormatter(final HelperProvider helperProvider, final GenerationProgress progress) { ObjectFormatter(final HelperProvider helperProvider, final GenerationRequest request, final StringBuilder sb) {
this.helperProvider = requireNonNull(helperProvider); this.helperProvider = requireNonNull(helperProvider);
this.progress = requireNonNull(progress); this.request = requireNonNull(request);
this.sb = requireNonNull(sb);
} }
/** /**
@@ -73,6 +76,70 @@ public final class ObjectFormatter {
case final ParsedText text -> formatText(text, variableName); case final ParsedText text -> formatText(text, variableName);
default -> formatObject(parsedObject, variableName); default -> formatObject(parsedObject, variableName);
} }
handleId(parsedObject, variableName);
}
/**
* Handles the fx:id attribute of an object
*
* @param parsedObject The parsed object
* @param variableName The variable name
* @throws GenerationException if an error occurs
*/
private void handleId(final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var id = parsedObject.attributes().get(FX_ID);
if (id == null) {
handleIdProperty(parsedObject, variableName);
} else {
final var idValue = id.value();
handleId(parsedObject, variableName, idValue);
}
}
private void handleIdProperty(final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var property = parsedObject.properties().entrySet().stream().filter(
e -> e.getKey().name().equals(FX_ID) && e.getKey().sourceType() == null).findFirst().orElse(null);
if (property != null) {
final var values = property.getValue();
formatDefines(values);
final var notDefinedChildren = getNotDefines(values);
if (notDefinedChildren.size() == 1) {
final var object = notDefinedChildren.getFirst();
if (object instanceof final ParsedText text) {
final var idValue = text.text();
handleId(parsedObject, variableName, idValue);
} else {
throw new GenerationException("Malformed fx:id property : " + parsedObject);
}
} else {
throw new GenerationException("Malformed fx:id property : " + parsedObject);
}
}
}
private static SequencedCollection<ParsedDefine> getDefines(final SequencedCollection<ParsedObject> objects) {
return objects.stream().filter(ParsedDefine.class::isInstance).map(ParsedDefine.class::cast).toList();
}
private static SequencedCollection<ParsedObject> getNotDefines(final SequencedCollection<ParsedObject> objects) {
return objects.stream().filter(c -> !(c instanceof ParsedDefine)).toList();
}
private void handleId(final ParsedObject parsedObject, final String variableName, final String value) throws GenerationException {
final String className;
if (request.controllerInfo().fieldInfo(value) == null) {
className = parsedObject.className();
logger.debug("Not injecting {} because it is not found in controller", value);
} else {
final var reflectionHelper = helperProvider.getReflectionHelper();
if (ReflectionHelper.isGeneric(ReflectionHelper.getClass(parsedObject.className()))) {
className = parsedObject.className() + reflectionHelper.getGenericTypes(parsedObject);
} else {
className = parsedObject.className();
}
helperProvider.getControllerInjector().injectControllerField(value, variableName);
}
helperProvider.getVariableProvider().addVariableInfo(value, new VariableInfo(value, parsedObject, variableName, className));
} }
/** /**
@@ -82,7 +149,8 @@ public final class ObjectFormatter {
* @param variableName The variable name * @param variableName The variable name
*/ */
private void formatText(final ParsedText text, final String variableName) { private void formatText(final ParsedText text, final String variableName) {
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar("String")).append(variableName).append(" = \"").append(text.text()).append("\";\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar("String"))
.append(variableName).append(" = \"").append(text.text()).append("\";\n");
} }
/** /**
@@ -112,9 +180,9 @@ public final class ObjectFormatter {
case "javafx.scene.text.Font" -> helperProvider.getFontFormatter().formatFont(parsedObject, variableName); case "javafx.scene.text.Font" -> helperProvider.getFontFormatter().formatFont(parsedObject, variableName);
case "javafx.scene.image.Image" -> case "javafx.scene.image.Image" ->
helperProvider.getImageFormatter().formatImage(parsedObject, variableName); helperProvider.getImageFormatter().formatImage(parsedObject, variableName);
case "java.net.URL" -> helperProvider.getURLFormatter().formatURL(parsedObject, variableName);
case "javafx.scene.shape.TriangleMesh" -> case "javafx.scene.shape.TriangleMesh" ->
helperProvider.getTriangleMeshFormatter().formatTriangleMesh(parsedObject, variableName); helperProvider.getTriangleMeshFormatter().formatTriangleMesh(parsedObject, variableName);
case "java.net.URL" -> helperProvider.getURLFormatter().formatURL(parsedObject, variableName);
case "javafx.scene.web.WebView" -> case "javafx.scene.web.WebView" ->
helperProvider.getWebViewFormatter().formatWebView(parsedObject, variableName); helperProvider.getWebViewFormatter().formatWebView(parsedObject, variableName);
default -> throw new IllegalArgumentException("Unknown builder class : " + className); default -> throw new IllegalArgumentException("Unknown builder class : " + className);
@@ -137,17 +205,13 @@ public final class ObjectFormatter {
throw new GenerationException("Invalid attributes for simple class : " + parsedObject); throw new GenerationException("Invalid attributes for simple class : " + parsedObject);
} }
final var value = getSimpleValue(parsedObject); final var value = getSimpleValue(parsedObject);
final var valueStr = helperProvider.getValueFormatter().toString(value, ReflectionHelper.getClass(parsedObject.className())); final var valueStr = ValueFormatter.toString(value, ReflectionHelper.getClass(parsedObject.className()));
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(" = ").append(valueStr).append(";\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(" = ").append(valueStr).append(";\n");
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} }
private String getSimpleValue(final ParsedObject parsedObject) throws GenerationException { private String getSimpleValue(final ParsedObject parsedObject) throws GenerationException {
final var definedChildren = parsedObject.children().stream().filter(ParsedDefine.class::isInstance).toList(); formatDefines(parsedObject.children());
for (final var definedChild : definedChildren) { final var notDefinedChildren = getNotDefines(parsedObject.children());
formatObject(definedChild, progress.getNextVariableName("define"));
}
final var notDefinedChildren = parsedObject.children().stream().filter(c -> !(c instanceof ParsedDefine)).toList();
if (parsedObject.attributes().containsKey(FX_VALUE)) { if (parsedObject.attributes().containsKey(FX_VALUE)) {
return getSimpleFXValue(parsedObject, notDefinedChildren); return getSimpleFXValue(parsedObject, notDefinedChildren);
} else if (parsedObject.attributes().containsKey(VALUE)) { } else if (parsedObject.attributes().containsKey(VALUE)) {
@@ -157,6 +221,13 @@ public final class ObjectFormatter {
} }
} }
private void formatDefines(final SequencedCollection<ParsedObject> values) throws GenerationException {
final var defines = getDefines(values);
for (final var definedChild : defines) {
format(definedChild, helperProvider.getVariableProvider().getNextVariableName("define"));
}
}
private static String getSimpleFXValue(final ParsedObject parsedObject, final Collection<ParsedObject> notDefinedChildren) throws GenerationException { private static String getSimpleFXValue(final ParsedObject parsedObject, final Collection<ParsedObject> notDefinedChildren) throws GenerationException {
if (notDefinedChildren.isEmpty() && !parsedObject.attributes().containsKey(VALUE)) { if (notDefinedChildren.isEmpty() && !parsedObject.attributes().containsKey(VALUE)) {
return parsedObject.attributes().get(FX_VALUE).value(); return parsedObject.attributes().get(FX_VALUE).value();
@@ -186,7 +257,7 @@ public final class ObjectFormatter {
} }
} }
private boolean isSimpleClass(final ParsedObject object) throws GenerationException { private static boolean isSimpleClass(final ParsedObject object) throws GenerationException {
final var className = object.className(); final var className = object.className();
if (SIMPLE_CLASSES.contains(className)) { if (SIMPLE_CLASSES.contains(className)) {
return true; return true;
@@ -199,35 +270,31 @@ public final class ObjectFormatter {
private void formatComplexClass(final ParsedObject parsedObject, final String variableName) throws GenerationException { private void formatComplexClass(final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var clazz = ReflectionHelper.getClass(parsedObject.className()); final var clazz = ReflectionHelper.getClass(parsedObject.className());
final var children = parsedObject.children(); final var children = parsedObject.children();
final var definedChildren = children.stream().filter(ParsedDefine.class::isInstance).toList(); final var notDefinedChildren = getNotDefines(children);
final var notDefinedChildren = children.stream().filter(c -> !(c instanceof ParsedDefine)).toList();
final var constructors = clazz.getConstructors(); final var constructors = clazz.getConstructors();
final var allPropertyNames = new HashSet<>(parsedObject.attributes().keySet()); final var allAttributesNames = new HashSet<>(parsedObject.attributes().keySet());
allPropertyNames.addAll(parsedObject.properties().keySet().stream().map(ParsedProperty::name).collect(Collectors.toSet())); allAttributesNames.addAll(parsedObject.properties().keySet().stream().map(ParsedProperty::name).collect(Collectors.toSet()));
if (!definedChildren.isEmpty()) { formatDefines(children);
for (final var definedChild : definedChildren) {
format(definedChild, progress.getNextVariableName("define"));
}
}
if (!notDefinedChildren.isEmpty()) { if (!notDefinedChildren.isEmpty()) {
final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className()); final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
if (defaultProperty != null) { if (defaultProperty != null) {
allPropertyNames.add(defaultProperty); allAttributesNames.add(defaultProperty);
} }
} }
final var constructorArgs = ConstructorHelper.getMatchingConstructorArgs(constructors, allPropertyNames); final var constructorArgs = ConstructorHelper.getMatchingConstructorArgs(constructors, allAttributesNames);
if (constructorArgs == null) { if (constructorArgs == null) {
formatNoConstructor(parsedObject, variableName, allPropertyNames); formatNoConstructor(parsedObject, variableName, allAttributesNames);
} else { } else {
formatConstructor(parsedObject, variableName, constructorArgs); formatConstructor(parsedObject, variableName, constructorArgs);
} }
} }
private void formatNoConstructor(final ParsedObject parsedObject, final String variableName, final Collection<String> allPropertyNames) throws GenerationException { private void formatNoConstructor(final ParsedObject parsedObject, final String variableName, final Collection<String> allAttributesNames) throws GenerationException {
final var clazz = ReflectionHelper.getClass(parsedObject.className()); final var clazz = ReflectionHelper.getClass(parsedObject.className());
if (allPropertyNames.size() == 1 && allPropertyNames.iterator().next().equals("fx:constant")) { if (allAttributesNames.contains("fx:constant") && (allAttributesNames.size() == 1 ||
(allAttributesNames.size() == 2 && allAttributesNames.contains("fx:id")))) {
final var property = parsedObject.attributes().get("fx:constant"); final var property = parsedObject.attributes().get("fx:constant");
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(" = ").append(clazz.getCanonicalName()).append(".").append(property.value()).append(";\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(" = ").append(clazz.getCanonicalName()).append(".").append(property.value()).append(";\n");
} else { } else {
throw new GenerationException("Cannot find constructor for " + clazz.getCanonicalName()); throw new GenerationException("Cannot find constructor for " + clazz.getCanonicalName());
} }
@@ -235,30 +302,31 @@ public final class ObjectFormatter {
private void formatConstructor(final ParsedObject parsedObject, final String variableName, final ConstructorArgs constructorArgs) throws GenerationException { private void formatConstructor(final ParsedObject parsedObject, final String variableName, final ConstructorArgs constructorArgs) throws GenerationException {
final var reflectionHelper = helperProvider.getReflectionHelper(); final var reflectionHelper = helperProvider.getReflectionHelper();
final var args = helperProvider.getConstructorHelper().getListConstructorArgs(constructorArgs, parsedObject); final var args = ConstructorHelper.getListConstructorArgs(constructorArgs, parsedObject);
final var genericTypes = reflectionHelper.getGenericTypes(parsedObject); final var genericTypes = reflectionHelper.getGenericTypes(parsedObject);
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(NEW_ASSIGN).append(parsedObject.className()) sb.append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName)
.append(genericTypes).append("(").append(String.join(", ", args)).append(");\n"); .append(NEW_ASSIGN).append(parsedObject.className()).append(genericTypes).append("(")
.append(String.join(", ", args)).append(");\n");
final var sortedAttributes = getSortedAttributes(parsedObject); final var sortedAttributes = getSortedAttributes(parsedObject);
for (final var value : sortedAttributes) { for (final var value : sortedAttributes) {
if (!constructorArgs.namedArgs().containsKey(value.name())) { if (!constructorArgs.namedArgs().containsKey(value.name())) {
helperProvider.getPropertyFormatter().formatProperty(value, parsedObject, variableName); helperProvider.getPropertyFormatter().formatProperty(value, parsedObject, variableName);
} }
} }
final var sortedProperties = parsedObject.properties().entrySet().stream().sorted(Comparator.comparing(e -> e.getKey().name())).toList(); final var sortedProperties = parsedObject.properties().entrySet().stream().sorted(Comparator.comparing(p -> p.getKey().name())).toList();
for (final var e : sortedProperties) { for (final var e : sortedProperties) {
if (!constructorArgs.namedArgs().containsKey(e.getKey().name())) { if (!constructorArgs.namedArgs().containsKey(e.getKey().name())) {
final var p = e.getKey(); final var p = e.getKey();
final var o = e.getValue(); final var o = e.getValue();
formatChild(parsedObject, p, o, variableName); helperProvider.getPropertyFormatter().formatProperty(p, o, parsedObject, variableName);
} }
} }
final var notDefinedChildren = parsedObject.children().stream().filter(c -> !(c instanceof ParsedDefine)).toList(); final var notDefinedChildren = parsedObject.children().stream().filter(c -> !(c instanceof ParsedDefine)).toList();
if (!notDefinedChildren.isEmpty()) { if (!notDefinedChildren.isEmpty()) {
final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className()); final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
if (!constructorArgs.namedArgs().containsKey(defaultProperty)) { if (!constructorArgs.namedArgs().containsKey(defaultProperty)) {
final var property = new ParsedPropertyImpl(defaultProperty, null, null); final var property = new ParsedPropertyImpl(defaultProperty, null, "");
formatChild(parsedObject, property, notDefinedChildren, variableName); helperProvider.getPropertyFormatter().formatProperty(property, notDefinedChildren, parsedObject, variableName);
} }
} }
} }
@@ -270,20 +338,21 @@ public final class ObjectFormatter {
* @param subNodeName The sub node name * @param subNodeName The sub node name
*/ */
private void formatInclude(final ParsedInclude include, final String subNodeName) throws GenerationException { private void formatInclude(final ParsedInclude include, final String subNodeName) throws GenerationException {
final var subViewVariable = progress.getNextVariableName("view"); final var subViewVariable = helperProvider.getVariableProvider().getNextVariableName("view");
final var viewVariable = helperProvider.getInitializationFormatter().formatSubViewConstructorCall(include); final var viewVariable = helperProvider.getInitializationFormatter().formatSubViewConstructorCall(include);
progress.stringBuilder().append(" final javafx.scene.Parent ").append(subNodeName).append(" = ").append(viewVariable).append(".load();\n"); sb.append(" final javafx.scene.Parent ").append(subNodeName).append(" = ").append(viewVariable).append(".load();\n");
injectSubController(include, subViewVariable); injectSubController(include, subViewVariable);
} }
private void injectSubController(final ParsedInclude include, final String subViewVariable) throws GenerationException { private void injectSubController(final ParsedInclude include, final String subViewVariable) {
final var id = include.controllerId(); final var id = include.controllerId();
if (id != null) { if (id != null) {
final var subControllerVariable = progress.getNextVariableName("controller"); final var variableProvider = helperProvider.getVariableProvider();
final var controllerClass = progress.request().sourceInfo().sourceToSourceInfo().get(include.source()).controllerClassName(); final var subControllerVariable = variableProvider.getNextVariableName("controller");
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(controllerClass)).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n"); final var controllerClass = request.sourceInfo().sourceToSourceInfo().get(include.source()).controllerClassName();
progress.idToVariableInfo().put(id, new VariableInfo(id, include, subControllerVariable, controllerClass)); sb.append(helperProvider.getCompatibilityHelper().getStartVar(controllerClass)).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
if (progress.request().controllerInfo().fieldInfo(id) == null) { variableProvider.addVariableInfo(id, new VariableInfo(id, include, subControllerVariable, controllerClass));
if (request.controllerInfo().fieldInfo(id) == null) {
logger.debug("Not injecting {} because it is not found in controller", id); logger.debug("Not injecting {} because it is not found in controller", id);
} else { } else {
helperProvider.getControllerInjector().injectControllerField(id, subControllerVariable); helperProvider.getControllerInjector().injectControllerField(id, subControllerVariable);
@@ -294,12 +363,12 @@ public final class ObjectFormatter {
/** /**
* Formats a fx:define * Formats a fx:define
* *
* @param define The parsed define * @param define The parsed define
* @throws GenerationException if an error occurs * @throws GenerationException if an error occurs
*/ */
private void formatDefine(final ParsedObject define) throws GenerationException { private void formatDefine(final ParsedObject define) throws GenerationException {
for (final var child : define.children()) { for (final var child : define.children()) {
format(child, progress.getNextVariableName("definedObject")); format(child, helperProvider.getVariableProvider().getNextVariableName("definedObject"));
} }
} }
@@ -311,12 +380,12 @@ public final class ObjectFormatter {
*/ */
private void formatReference(final ParsedReference reference, final String variableName) throws GenerationException { private void formatReference(final ParsedReference reference, final String variableName) throws GenerationException {
final var id = reference.source(); final var id = reference.source();
final var variableInfo = progress.idToVariableInfo().get(id); final var variableInfo = helperProvider.getVariableProvider().getVariableInfo(id);
if (variableInfo == null) { if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id); throw new GenerationException("Unknown id : " + id);
} }
final var referenceName = variableInfo.variableName(); final var referenceName = variableInfo.variableName();
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(variableInfo.className())).append(variableName).append(" = ").append(referenceName).append(";\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar(variableInfo.className())).append(variableName).append(" = ").append(referenceName).append(";\n");
} }
/** /**
@@ -328,12 +397,13 @@ public final class ObjectFormatter {
*/ */
private void formatCopy(final ParsedCopy copy, final String variableName) throws GenerationException { private void formatCopy(final ParsedCopy copy, final String variableName) throws GenerationException {
final var id = copy.source(); final var id = copy.source();
final var variableInfo = progress.idToVariableInfo().get(id); final var variableInfo = helperProvider.getVariableProvider().getVariableInfo(id);
if (variableInfo == null) { if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id); throw new GenerationException("Unknown id : " + id);
} }
final var copyVariable = variableInfo.variableName(); final var copyVariable = variableInfo.variableName();
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(variableInfo.className())).append(variableName).append(NEW_ASSIGN).append(variableInfo.className()).append("(").append(copyVariable).append(");\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar(variableInfo.className())).append(variableName)
.append(NEW_ASSIGN).append(variableInfo.className()).append("(").append(copyVariable).append(");\n");
} }
/** /**
@@ -342,8 +412,9 @@ public final class ObjectFormatter {
* @param constant The constant * @param constant The constant
* @param variableName The variable name * @param variableName The variable name
*/ */
private void formatConstant(final ParsedConstant constant, final String variableName) throws GenerationException { private void formatConstant(final ParsedConstant constant, final String variableName) {
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(constant.className())).append(variableName).append(" = ").append(constant.className()).append(".").append(constant.constant()).append(";\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar(constant.className()))
.append(variableName).append(" = ").append(constant.className()).append(".").append(constant.constant()).append(";\n");
} }
/** /**
@@ -352,8 +423,8 @@ public final class ObjectFormatter {
* @param value The value * @param value The value
* @param variableName The variable name * @param variableName The variable name
*/ */
private void formatValue(final ParsedValue value, final String variableName) throws GenerationException { private void formatValue(final ParsedValue value, final String variableName) {
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(value.className())).append(variableName).append(" = ").append(value.className()).append(".valueOf(\"").append(value.value()).append("\");\n"); sb.append(helperProvider.getCompatibilityHelper().getStartVar(value.className())).append(variableName).append(" = ").append(value.className()).append(".valueOf(\"").append(value.value()).append("\");\n");
} }
/** /**
@@ -364,128 +435,23 @@ public final class ObjectFormatter {
*/ */
private void formatFactory(final ParsedFactory factory, final String variableName) throws GenerationException { private void formatFactory(final ParsedFactory factory, final String variableName) throws GenerationException {
final var variables = new ArrayList<String>(); final var variables = new ArrayList<String>();
for (final var child : factory.children()) {
final var vn = helperProvider.getVariableProvider().getNextVariableName(getVariablePrefix(child));
format(child, vn);
}
for (final var argument : factory.arguments()) { for (final var argument : factory.arguments()) {
final var argumentVariable = progress.getNextVariableName("arg"); final var argumentVariable = helperProvider.getVariableProvider().getNextVariableName("arg");
variables.add(argumentVariable); variables.add(argumentVariable);
format(argument, argumentVariable); format(argument, argumentVariable);
} }
final var compatibilityHelper = helperProvider.getCompatibilityHelper(); final var compatibilityHelper = helperProvider.getCompatibilityHelper();
if (progress.request().parameters().compatibility().useVar()) { if (request.parameters().compatibility().useVar()) {
progress.stringBuilder().append(compatibilityHelper.getStartVar(factory.className())).append(variableName).append(" = ").append(factory.className()) sb.append(compatibilityHelper.getStartVar(factory.className())).append(variableName).append(" = ").append(factory.className())
.append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n"); .append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
} else { } else {
final var returnType = ReflectionHelper.getReturnType(factory.className(), factory.factory()); final var returnType = ReflectionHelper.getReturnType(factory.className(), factory.factory());
progress.stringBuilder().append(compatibilityHelper.getStartVar(returnType)).append(variableName).append(" = ").append(factory.className()) sb.append(compatibilityHelper.getStartVar(returnType)).append(variableName).append(" = ").append(factory.className())
.append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n"); .append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
} }
} }
/**
* Formats the children objects of a property
*
* @param parent The parent object
* @param property The parent property
* @param objects The child objects
* @param parentVariable The parent object variable
*/
private void formatChild(final ParsedObject parent, final ParsedProperty property,
final Iterable<? extends ParsedObject> objects, final String parentVariable) throws GenerationException {
final var propertyName = property.name();
final var variables = new ArrayList<String>();
for (final var object : objects) {
final var vn = progress.getNextVariableName(getVariablePrefix(object));
format(object, vn);
if (!(object instanceof ParsedDefine)) {
variables.add(vn);
}
}
if (variables.size() > 1) {
formatMultipleChildren(variables, propertyName, parent, parentVariable);
} else if (variables.size() == 1) {
final var vn = variables.getFirst();
formatSingleChild(vn, property, parent, parentVariable);
}
}
/**
* Formats children objects given that they are more than one
*
* @param variables The children variables
* @param propertyName The property name
* @param parent The parent object
* @param parentVariable The parent object variable
*/
private void formatMultipleChildren(final Iterable<String> variables, final String propertyName, final ParsedObject parent,
final String parentVariable) throws GenerationException {
final var getMethod = getGetMethod(propertyName);
if (ReflectionHelper.hasMethod(ReflectionHelper.getClass(parent.className()), getMethod)) {
progress.stringBuilder().append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(").append(helperProvider.getCompatibilityHelper().getListOf()).append(String.join(", ", variables)).append("));\n");
} else {
throw getCannotSetException(propertyName, parent.className());
}
}
/**
* Formats a single child object
*
* @param variableName The child's variable name
* @param property The parent property
* @param parent The parent object
* @param parentVariable The parent object variable
*/
private void formatSingleChild(final String variableName, final ParsedProperty property, final ParsedObject parent,
final String parentVariable) throws GenerationException {
if (property.sourceType() == null) {
formatSingleChildInstance(variableName, property, parent, parentVariable);
} else {
formatSingleChildStatic(variableName, property, parentVariable);
}
}
/**
* Formats a single child object using an instance method on the parent object
*
* @param variableName The child's variable name
* @param property The parent property
* @param parent The parent object
* @param parentVariable The parent object variable
*/
private void formatSingleChildInstance(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 parentClass = ReflectionHelper.getClass(parent.className());
final var sb = progress.stringBuilder();
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(").append(helperProvider.getCompatibilityHelper().getListOf()).append(variableName).append("));\n");
} else {
throw getCannotSetException(property.name(), parent.className());
}
}
/**
* Formats a child object using a static method
*
* @param variableName The child's variable name
* @param property The parent property
* @param parentVariable The parent variable
*/
private void formatSingleChildStatic(final String variableName,
final ParsedProperty property, final String parentVariable) throws GenerationException {
final var setMethod = 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());
}
}
private static GenerationException getCannotSetException(final String propertyName, final String className) {
return new GenerationException("Cannot set " + propertyName + " on " + className);
}
} }

View File

@@ -1,17 +1,21 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes; import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes; import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
import com.github.gtache.fxml.compiler.parsing.ParsedObject; import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty; import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.ParsedText;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
import java.util.SequencedCollection;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID; import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.RESOURCE_KEY_PREFIX;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
/** /**
@@ -39,7 +43,7 @@ final class PropertyFormatter {
void formatProperty(final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException { void formatProperty(final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException {
final var propertyName = property.name(); final var propertyName = property.name();
if (propertyName.equals(FX_ID)) { if (propertyName.equals(FX_ID)) {
helperProvider.getGenerationHelper().handleId(parent, parentVariable); //Do nothing
} else if (propertyName.equals("fx:controller")) { } else if (propertyName.equals("fx:controller")) {
checkDuplicateController(parent); checkDuplicateController(parent);
} else if (Objects.equals(property.sourceType(), EventHandler.class.getName())) { } else if (Objects.equals(property.sourceType(), EventHandler.class.getName())) {
@@ -51,6 +55,27 @@ final class PropertyFormatter {
} }
} }
/**
* Formats a complex property (containing a list of objects).
* The values should not contain any ParsedDefine
*
* @param property The property to format
* @param values The property's values
* @param parent The property's parent object
* @param parentVariable The parent variable
* @throws GenerationException if an error occurs or if the values contain a ParsedDefine
*/
void formatProperty(final ParsedProperty property, final SequencedCollection<? extends ParsedObject> values, final ParsedObject parent, final String parentVariable) throws GenerationException {
if (values.stream().anyMatch(ParsedDefine.class::isInstance)) {
throw new GenerationException("Values should not contain any ParsedDefine");
} else if (values.size() == 1 && values.getFirst() instanceof final ParsedText text) {
final var newProperty = new ParsedPropertyImpl(property.name(), property.sourceType(), text.text());
formatProperty(newProperty, parent, parentVariable);
} else {
formatChild(parent, property, values, parentVariable);
}
}
private void checkDuplicateController(final ParsedObject parent) throws GenerationException { private void checkDuplicateController(final ParsedObject parent) throws GenerationException {
if (parent != progress.request().rootObject()) { if (parent != progress.request().rootObject()) {
throw new GenerationException("Invalid nested controller"); throw new GenerationException("Invalid nested controller");
@@ -66,7 +91,7 @@ final class PropertyFormatter {
} }
private void handleStaticProperty(final ParsedProperty property, final String parentVariable, final String propertyName) throws GenerationException { private void handleStaticProperty(final ParsedProperty property, final String parentVariable, final String propertyName) throws GenerationException {
final var setMethod = GenerationHelper.getSetMethod(propertyName); final var setMethod = getSetMethod(propertyName);
final var propertySourceTypeClass = ReflectionHelper.getClass(property.sourceType()); final var propertySourceTypeClass = ReflectionHelper.getClass(property.sourceType());
if (ReflectionHelper.hasStaticMethod(propertySourceTypeClass, setMethod)) { if (ReflectionHelper.hasStaticMethod(propertySourceTypeClass, setMethod)) {
final var method = ReflectionHelper.getStaticMethod(propertySourceTypeClass, setMethod); final var method = ReflectionHelper.getStaticMethod(propertySourceTypeClass, setMethod);
@@ -80,8 +105,8 @@ final class PropertyFormatter {
private void handleProperty(final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException { private void handleProperty(final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException {
final var propertyName = property.name(); final var propertyName = property.name();
final var setMethod = GenerationHelper.getSetMethod(propertyName); final var setMethod = getSetMethod(propertyName);
final var getMethod = GenerationHelper.getGetMethod(propertyName); final var getMethod = getGetMethod(propertyName);
final var parentClass = ReflectionHelper.getClass(parent.className()); final var parentClass = ReflectionHelper.getClass(parent.className());
if (ReflectionHelper.hasMethod(parentClass, setMethod)) { if (ReflectionHelper.hasMethod(parentClass, setMethod)) {
handleSetProperty(property, parentClass, parentVariable); handleSetProperty(property, parentClass, parentVariable);
@@ -93,7 +118,7 @@ final class PropertyFormatter {
} }
private void handleSetProperty(final ParsedProperty property, final Class<?> parentClass, final String parentVariable) throws GenerationException { private void handleSetProperty(final ParsedProperty property, final Class<?> parentClass, final String parentVariable) throws GenerationException {
final var setMethod = GenerationHelper.getSetMethod(property.name()); final var setMethod = getSetMethod(property.name());
final var method = ReflectionHelper.getMethod(parentClass, setMethod); final var method = ReflectionHelper.getMethod(parentClass, setMethod);
final var parameterType = method.getParameterTypes()[0]; final var parameterType = method.getParameterTypes()[0];
final var arg = helperProvider.getValueFormatter().getArg(property.value(), parameterType); final var arg = helperProvider.getValueFormatter().getArg(property.value(), parameterType);
@@ -101,12 +126,15 @@ final class PropertyFormatter {
} }
private void handleGetProperty(final ParsedProperty property, final Class<?> parentClass, final String parentVariable) throws GenerationException { private void handleGetProperty(final ParsedProperty property, final Class<?> parentClass, final String parentVariable) throws GenerationException {
final var getMethod = GenerationHelper.getGetMethod(property.name()); final var getMethod = getGetMethod(property.name());
final var method = ReflectionHelper.getMethod(parentClass, getMethod); final var method = ReflectionHelper.getMethod(parentClass, getMethod);
final var returnType = method.getReturnType(); final var returnType = method.getReturnType();
if (ReflectionHelper.hasMethod(returnType, "addAll")) { if (ReflectionHelper.hasMethod(returnType, "addAll")) {
final var arg = helperProvider.getValueFormatter().getArg(property.value(), String.class); final var arg = helperProvider.getValueFormatter().getArg(property.value(), String.class);
setLaterIfNeeded(property, String.class, " " + parentVariable + "." + getMethod + "().addAll(" + helperProvider.getCompatibilityHelper().getListOf() + arg + "));\n"); setLaterIfNeeded(property, String.class, " " + parentVariable + "." + getMethod + "().addAll(" +
helperProvider.getCompatibilityHelper().getListOf() + arg + "));\n");
} else {
throw new GenerationException("Cannot set " + property.name() + " on " + parentClass);
} }
} }
@@ -119,11 +147,119 @@ final class PropertyFormatter {
*/ */
private void setLaterIfNeeded(final ParsedProperty property, final Class<?> type, final String arg) { private void setLaterIfNeeded(final ParsedProperty property, final Class<?> type, final String arg) {
final var parameters = progress.request().parameters(); final var parameters = progress.request().parameters();
if (type == String.class && property.value().startsWith(RESOURCE_KEY_PREFIX) && parameters.resourceInjectionType() == ResourceBundleInjectionTypes.GETTER if (type == String.class && property.value().startsWith(RESOURCE_KEY_PREFIX) && parameters.resourceInjectionType() == ResourceBundleInjectionType.GETTER
&& parameters.fieldInjectionType() == ControllerFieldInjectionTypes.FACTORY) { && parameters.fieldInjectionType() == ControllerFieldInjectionType.FACTORY) {
progress.controllerFactoryPostAction().add(arg); progress.controllerFactoryPostAction().add(arg);
} else { } else {
progress.stringBuilder().append(arg); progress.stringBuilder().append(arg);
} }
} }
/**
* Formats the children objects of a property
*
* @param parent The parent object
* @param property The parent property
* @param objects The child objects
* @param parentVariable The parent object variable
*/
private void formatChild(final ParsedObject parent, final ParsedProperty property,
final Iterable<? extends ParsedObject> objects, final String parentVariable) throws GenerationException {
final var propertyName = property.name();
final var variables = new ArrayList<String>();
for (final var object : objects) {
final var vn = helperProvider.getVariableProvider().getNextVariableName(getVariablePrefix(object));
helperProvider.getObjectFormatter().format(object, vn);
variables.add(vn);
}
if (variables.size() > 1) {
formatMultipleChildren(variables, propertyName, parent, parentVariable);
} else if (variables.size() == 1) {
final var vn = variables.getFirst();
formatSingleChild(vn, property, parent, parentVariable);
}
}
/**
* Formats children objects given that they are more than one
*
* @param variables The children variables
* @param propertyName The property name
* @param parent The parent object
* @param parentVariable The parent object variable
*/
private void formatMultipleChildren(final Iterable<String> variables, final String propertyName, final ParsedObject parent,
final String parentVariable) throws GenerationException {
final var getMethod = getGetMethod(propertyName);
if (ReflectionHelper.hasMethod(ReflectionHelper.getClass(parent.className()), getMethod)) {
progress.stringBuilder().append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(").append(helperProvider.getCompatibilityHelper().getListOf()).append(String.join(", ", variables)).append("));\n");
} else {
throw getCannotSetException(propertyName, parent.className());
}
}
/**
* Formats a single child object
*
* @param variableName The child's variable name
* @param property The parent property
* @param parent The parent object
* @param parentVariable The parent object variable
*/
private void formatSingleChild(final String variableName, final ParsedProperty property, final ParsedObject parent,
final String parentVariable) throws GenerationException {
if (property.sourceType() == null) {
formatSingleChildInstance(variableName, property, parent, parentVariable);
} else {
formatSingleChildStatic(variableName, property, parentVariable);
}
}
/**
* Formats a single child object using an instance method on the parent object
*
* @param variableName The child's variable name
* @param property The parent property
* @param parent The parent object
* @param parentVariable The parent object variable
*/
private void formatSingleChildInstance(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 parentClass = ReflectionHelper.getClass(parent.className());
final var sb = progress.stringBuilder();
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(").append(helperProvider.getCompatibilityHelper().getListOf()).append(variableName).append("));\n");
} else {
throw getCannotSetException(property.name(), parent.className());
}
}
/**
* Formats a child object using a static method
*
* @param variableName The child's variable name
* @param property The parent property
* @param parentVariable The parent variable
*/
private void formatSingleChildStatic(final String variableName,
final ParsedProperty property, final String parentVariable) throws GenerationException {
final var setMethod = 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());
}
}
private static GenerationException getCannotSetException(final String propertyName, final String className) {
return new GenerationException("Cannot set " + propertyName + " on " + className);
}
} }

View File

@@ -2,11 +2,16 @@ package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
import com.github.gtache.fxml.compiler.parsing.ParsedObject; 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.scene.paint.Color; import javafx.scene.paint.Color;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID; import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes; import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
@@ -17,17 +22,21 @@ import static java.util.Objects.requireNonNull;
*/ */
final class SceneFormatter { final class SceneFormatter {
private final HelperProvider helperProvider; private static final ParsedProperty ROOT_PROPERTY = new ParsedPropertyImpl("root", null, "");
private final GenerationProgress progress;
SceneFormatter(final HelperProvider helperProvider, final GenerationProgress progress) { private final HelperProvider helperProvider;
private final StringBuilder sb;
SceneFormatter(final HelperProvider helperProvider, final StringBuilder sb) {
this.helperProvider = requireNonNull(helperProvider); this.helperProvider = requireNonNull(helperProvider);
this.progress = requireNonNull(progress); this.sb = requireNonNull(sb);
} }
void formatScene(final ParsedObject parsedObject, final String variableName) throws GenerationException { void formatScene(final ParsedObject parsedObject, final String variableName) throws GenerationException {
checkPropertiesAndChildren(parsedObject);
formatDefines(parsedObject);
final var root = findRoot(parsedObject); final var root = findRoot(parsedObject);
final var rootVariableName = progress.getNextVariableName("root"); final var rootVariableName = helperProvider.getVariableProvider().getNextVariableName("root");
helperProvider.getObjectFormatter().format(root, rootVariableName); helperProvider.getObjectFormatter().format(root, rootVariableName);
final var sortedAttributes = getSortedAttributes(parsedObject); final var sortedAttributes = getSortedAttributes(parsedObject);
double width = -1; double width = -1;
@@ -46,31 +55,64 @@ final class SceneFormatter {
default -> throw new GenerationException("Unknown font attribute : " + property.name()); default -> throw new GenerationException("Unknown font attribute : " + property.name());
} }
} }
final var sb = progress.stringBuilder();
sb.append(helperProvider.getCompatibilityHelper().getStartVar("javafx.scene.Scene")).append(variableName).append(" = new javafx.scene.Scene(").append(rootVariableName).append(", ") sb.append(helperProvider.getCompatibilityHelper().getStartVar("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"); .append(width).append(", ").append(height).append(", javafx.scene.paint.Color.valueOf(\"").append(paint).append("\"));\n");
addStylesheets(variableName, stylesheets); addStylesheets(variableName, stylesheets);
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} }
private static ParsedObject findRoot(final ParsedObject parsedObject) throws GenerationException { private void formatDefines(final ParsedObject parsedObject) throws GenerationException {
final var rootProperty = parsedObject.properties().entrySet().stream().filter(e -> e.getKey().name().equals("root")) final var objectFormatter = helperProvider.getObjectFormatter();
.filter(e -> e.getValue().size() == 1) for (final var define : parsedObject.children()) {
.map(e -> e.getValue().getFirst()).findFirst().orElse(null); if (define instanceof ParsedDefine) {
if (rootProperty != null) { objectFormatter.format(define, helperProvider.getVariableProvider().getNextVariableName("define"));
return rootProperty; }
} else if (parsedObject.children().size() == 1) { }
return parsedObject.children().getFirst(); for (final var define : parsedObject.properties().getOrDefault(ROOT_PROPERTY, List.of())) {
} else { if (define instanceof ParsedDefine) {
throw new GenerationException("Scene must have a root"); objectFormatter.format(define, helperProvider.getVariableProvider().getNextVariableName("define"));
}
} }
} }
private void addStylesheets(final String variableName, final Collection<String> stylesheets) throws GenerationException { private static ParsedObject findRoot(final ParsedObject parsedObject) throws GenerationException {
final var rootPropertyChildren = parsedObject.properties().get(ROOT_PROPERTY);
if (rootPropertyChildren == null) {
return getNonDefineObjects(parsedObject.children()).findFirst()
.orElseThrow(() -> new GenerationException("Expected only one child for scene : " + parsedObject));
} else {
return getNonDefineObjects(rootPropertyChildren).findFirst()
.orElseThrow(() -> new GenerationException("Expected only one root property child for scene : " + parsedObject));
}
}
private static Stream<ParsedObject> getNonDefineObjects(final Collection<ParsedObject> objects) {
return objects.stream().filter(c -> !(c instanceof ParsedDefine));
}
private static void checkPropertiesAndChildren(final ParsedObject parsedObject) throws GenerationException {
if (parsedObject.properties().keySet().stream().anyMatch(k -> !k.equals(ROOT_PROPERTY))) {
throw new GenerationException("Unsupported scene properties : " + parsedObject);
}
final var nonDefineCount = getNonDefineObjects(parsedObject.children()).count();
final var rootPropertyChildren = parsedObject.properties().get(ROOT_PROPERTY);
if (rootPropertyChildren == null) {
if (nonDefineCount != 1) {
throw new GenerationException("Expected only one child for scene : " + parsedObject);
}
} else {
final var nonDefinePropertyChildren = getNonDefineObjects(rootPropertyChildren).count();
if (nonDefinePropertyChildren != 1) {
throw new GenerationException("Expected only one root property child for scene : " + parsedObject);
} else if (nonDefineCount != 0) {
throw new GenerationException("Expected no children for scene : " + parsedObject);
}
}
}
private void addStylesheets(final String variableName, final Collection<String> stylesheets) {
if (!stylesheets.isEmpty()) { if (!stylesheets.isEmpty()) {
final var urlVariables = helperProvider.getURLFormatter().formatURL(stylesheets); final var urlVariables = helperProvider.getURLFormatter().formatURL(stylesheets);
final var tmpVariable = progress.getNextVariableName("stylesheets"); final var tmpVariable = helperProvider.getVariableProvider().getNextVariableName("stylesheets");
final var sb = progress.stringBuilder();
final var compatibilityHelper = helperProvider.getCompatibilityHelper(); final var compatibilityHelper = helperProvider.getCompatibilityHelper();
sb.append(compatibilityHelper.getStartVar("java.util.List<String>")).append(tmpVariable).append(" = ").append(variableName).append(".getStyleSheets();\n"); sb.append(compatibilityHelper.getStartVar("java.util.List<String>")).append(tmpVariable).append(" = ").append(variableName).append(".getStyleSheets();\n");
sb.append(" ").append(tmpVariable).append(".addAll(").append(compatibilityHelper.getListOf()).append(String.join(", ", urlVariables)).append("));\n"); sb.append(" ").append(tmpVariable).append(".addAll(").append(compatibilityHelper.getListOf()).append(String.join(", ", urlVariables)).append("));\n");

View File

@@ -76,7 +76,6 @@ final class TriangleMeshFormatter {
setFaces(variableName, faces); setFaces(variableName, faces);
setFaceSmoothingGroups(variableName, faceSmoothingGroups); setFaceSmoothingGroups(variableName, faceSmoothingGroups);
setVertexFormat(variableName, vertexFormat); setVertexFormat(variableName, vertexFormat);
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} else { } else {
throw new GenerationException("Image cannot have children or properties : " + parsedObject); throw new GenerationException("Image cannot have children or properties : " + parsedObject);
} }
@@ -133,8 +132,8 @@ final class TriangleMeshFormatter {
} }
private static <T> List<T> parseList(final CharSequence value, final Function<? super String, ? extends T> parser) { private static <T> List<T> parseList(final CharSequence value, final Function<? super String, ? extends T> parser) {
final var splitPattern = Pattern.compile("[\\s+,]"); final var splitPattern = Pattern.compile("\\s*,\\s*|\\s+");
final var split = splitPattern.split(value); final var split = splitPattern.split(value);
return Arrays.stream(split).map(parser).collect(Collectors.toList()); return Arrays.stream(split).map(String::trim).filter(s -> !s.isEmpty()).map(parser).collect(Collectors.toList());
} }
} }

View File

@@ -17,11 +17,11 @@ import static java.util.Objects.requireNonNull;
final class URLFormatter { final class URLFormatter {
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final GenerationProgress progress; private final StringBuilder sb;
URLFormatter(final HelperProvider helperProvider, final GenerationProgress progress) { URLFormatter(final HelperProvider helperProvider, final StringBuilder sb) {
this.helperProvider = requireNonNull(helperProvider); this.helperProvider = requireNonNull(helperProvider);
this.progress = requireNonNull(progress); this.sb = requireNonNull(sb);
} }
List<String> formatURL(final Iterable<String> stylesheets) { List<String> formatURL(final Iterable<String> stylesheets) {
@@ -33,8 +33,7 @@ final class URLFormatter {
} }
String formatURL(final String url) { String formatURL(final String url) {
final var variableName = progress.getNextVariableName("url"); final var variableName = helperProvider.getVariableProvider().getNextVariableName("url");
final var sb = progress.stringBuilder();
if (url.startsWith(RELATIVE_PATH_PREFIX)) { if (url.startsWith(RELATIVE_PATH_PREFIX)) {
sb.append(getStartURL()).append(variableName).append(" = getClass().getResource(\"").append(url.substring(1)).append("\");\n"); sb.append(getStartURL()).append(variableName).append(" = getClass().getResource(\"").append(url.substring(1)).append("\");\n");
} else { } else {
@@ -62,8 +61,7 @@ final class URLFormatter {
} }
} }
//FIXME only relative path (@) ? //FIXME only relative path (@) ?
progress.stringBuilder().append(getStartURL()).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n"); sb.append(getStartURL()).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n");
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} else { } else {
throw new GenerationException("URL cannot have children or properties : " + parsedObject); throw new GenerationException("URL cannot have children or properties : " + parsedObject);
} }

View File

@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.InjectionType; import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl; import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*; import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
@@ -20,12 +18,12 @@ final class ValueFormatter {
private static final Pattern DECIMAL_PATTERN = Pattern.compile("\\d+(?:\\.\\d+)?"); private static final Pattern DECIMAL_PATTERN = Pattern.compile("\\d+(?:\\.\\d+)?");
private static final Pattern START_BACKSLASH_PATTERN = Pattern.compile("^\\\\"); private static final Pattern START_BACKSLASH_PATTERN = Pattern.compile("^\\\\");
private final InjectionType resourceInjectionType; private final HelperProvider helperProvider;
private final Map<String, VariableInfo> idToVariableInfo; private final ResourceBundleInjectionType resourceInjectionType;
ValueFormatter(final InjectionType resourceInjectionType, final Map<String, VariableInfo> idToVariableInfo) { ValueFormatter(final HelperProvider helperProvider, final ResourceBundleInjectionType resourceInjectionType) {
this.helperProvider = requireNonNull(helperProvider);
this.resourceInjectionType = requireNonNull(resourceInjectionType); this.resourceInjectionType = requireNonNull(resourceInjectionType);
this.idToVariableInfo = requireNonNull(idToVariableInfo);
} }
/** /**
@@ -45,7 +43,7 @@ final class ValueFormatter {
} else if (value.startsWith(BINDING_EXPRESSION_PREFIX)) { } else if (value.startsWith(BINDING_EXPRESSION_PREFIX)) {
throw new GenerationException("Not implemented yet"); throw new GenerationException("Not implemented yet");
} else if (value.startsWith(EXPRESSION_PREFIX)) { } else if (value.startsWith(EXPRESSION_PREFIX)) {
final var variable = idToVariableInfo.get(value.substring(1)); final var variable = helperProvider.getVariableProvider().getVariableInfo(value.substring(1));
if (variable == null) { if (variable == null) {
throw new GenerationException("Unknown variable : " + value.substring(1)); throw new GenerationException("Unknown variable : " + value.substring(1));
} }
@@ -62,20 +60,15 @@ final class ValueFormatter {
/** /**
* Gets the resource bundle value for the given value * Gets the resource bundle value for the given value
* *
* @param value The value * @param value The value
* @return The resource bundle value * @return The resource bundle value
* @throws GenerationException if an error occurs
*/ */
private String getBundleValue(final String value) throws GenerationException { private String getBundleValue(final String value) {
if (resourceInjectionType instanceof final ResourceBundleInjectionTypes types) { return switch (resourceInjectionType) {
return switch (types) { case CONSTRUCTOR, GET_BUNDLE, CONSTRUCTOR_NAME -> "resourceBundle.getString(\"" + value + "\")";
case CONSTRUCTOR, GET_BUNDLE, CONSTRUCTOR_NAME -> "resourceBundle.getString(\"" + value + "\")"; case GETTER -> "controller.resources().getString(\"" + value + "\")";
case GETTER -> "controller.resources().getString(\"" + value + "\")"; case CONSTRUCTOR_FUNCTION -> "resourceBundleFunction.apply(\"" + value + "\")";
case CONSTRUCTOR_FUNCTION -> "resourceBundleFunction.apply(\"" + value + "\")"; };
};
} else {
throw new GenerationException("Unknown resource bundle injection type : " + resourceInjectionType);
}
} }
@@ -86,7 +79,7 @@ final class ValueFormatter {
* @param clazz The value class * @param clazz The value class
* @return The computed string value * @return The computed string value
*/ */
String toString(final String value, final Class<?> clazz) { static String toString(final String value, final Class<?> clazz) {
if (clazz == String.class) { if (clazz == String.class) {
return "\"" + START_BACKSLASH_PATTERN.matcher(value).replaceAll("").replace("\\", "\\\\") return "\"" + START_BACKSLASH_PATTERN.matcher(value).replaceAll("").replace("\\", "\\\\")
.replace("\"", "\\\"") + "\""; .replace("\"", "\\\"") + "\"";

View File

@@ -13,7 +13,15 @@ import java.util.Objects;
* @param className The class name of the variable * @param className The class name of the variable
*/ */
record VariableInfo(String id, ParsedObject parsedObject, String variableName, String className) { record VariableInfo(String id, ParsedObject parsedObject, String variableName, String className) {
/**
* Instantiates a new variable info
* @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
* @throws NullPointerException if any parameter is null
*/
VariableInfo { VariableInfo {
Objects.requireNonNull(id); Objects.requireNonNull(id);
Objects.requireNonNull(parsedObject); Objects.requireNonNull(parsedObject);

View File

@@ -0,0 +1,51 @@
package com.github.gtache.fxml.compiler.impl.internal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Provider of variable names and info
*/
class VariableProvider {
private final Map<String, AtomicInteger> variableNameCounters;
private final Map<String, VariableInfo> idToVariableInfo;
/**
* Instantiates a new provider
*/
VariableProvider() {
this.variableNameCounters = new HashMap<>();
this.idToVariableInfo = new HashMap<>();
}
/**
* Gets the next available variable name for the given prefix
*
* @param prefix The variable name prefix
* @return The next available variable name
*/
String getNextVariableName(final String prefix) {
final var counter = variableNameCounters.computeIfAbsent(prefix, k -> new AtomicInteger(0));
return prefix + counter.getAndIncrement();
}
/**
* Adds a variable info
* @param id The variable id
* @param variableInfo The variable info
*/
void addVariableInfo(final String id, final VariableInfo variableInfo) {
idToVariableInfo.put(id, variableInfo);
}
/**
* Gets the variable info
* @param id The variable id
* @return The variable info
*/
VariableInfo getVariableInfo(final String id) {
return idToVariableInfo.get(id);
}
}

View File

@@ -15,11 +15,11 @@ import static java.util.Objects.requireNonNull;
final class WebViewFormatter { final class WebViewFormatter {
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final GenerationProgress progress; private final StringBuilder sb;
WebViewFormatter(final HelperProvider helperProvider, final GenerationProgress progress) { WebViewFormatter(final HelperProvider helperProvider, final StringBuilder sb) {
this.helperProvider = requireNonNull(helperProvider); this.helperProvider = requireNonNull(helperProvider);
this.progress = requireNonNull(progress); this.sb = requireNonNull(sb);
} }
/** /**
@@ -32,15 +32,13 @@ final class WebViewFormatter {
void formatWebView(final ParsedObject parsedObject, final String variableName) throws GenerationException { void formatWebView(final ParsedObject parsedObject, final String variableName) throws GenerationException {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) { if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
final var sortedAttributes = getSortedAttributes(parsedObject); final var sortedAttributes = getSortedAttributes(parsedObject);
final var sb = progress.stringBuilder();
final var compatibilityHelper = helperProvider.getCompatibilityHelper(); final var compatibilityHelper = helperProvider.getCompatibilityHelper();
sb.append(compatibilityHelper.getStartVar("javafx.scene.web.WebView")).append(variableName).append(" = new javafx.scene.web.WebView();\n"); sb.append(compatibilityHelper.getStartVar("javafx.scene.web.WebView")).append(variableName).append(" = new javafx.scene.web.WebView();\n");
final var engineVariable = progress.getNextVariableName("engine"); final var engineVariable = helperProvider.getVariableProvider().getNextVariableName("engine");
sb.append(compatibilityHelper.getStartVar("javafx.scene.web.WebEngine")).append(engineVariable).append(" = ").append(variableName).append(".getEngine();\n"); sb.append(compatibilityHelper.getStartVar("javafx.scene.web.WebEngine")).append(engineVariable).append(" = ").append(variableName).append(".getEngine();\n");
for (final var value : sortedAttributes) { for (final var value : sortedAttributes) {
formatAttribute(value, parsedObject, variableName, engineVariable); formatAttribute(value, parsedObject, variableName, engineVariable);
} }
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} else { } else {
throw new GenerationException("WebView cannot have children or properties : " + parsedObject); throw new GenerationException("WebView cannot have children or properties : " + parsedObject);
} }
@@ -95,7 +93,7 @@ final class WebViewFormatter {
} }
private void injectLocation(final ParsedProperty value, final String engineVariable) { private void injectLocation(final ParsedProperty value, final String engineVariable) {
progress.stringBuilder().append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n"); sb.append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n");
} }

View File

@@ -10,12 +10,36 @@ import java.util.Objects;
* Implementation of {@link ParsedConstant} * Implementation of {@link ParsedConstant}
* *
* @param className The constant class * @param className The constant class
* @param attributes The constant properties * @param attributes The constant attributes
*/ */
public record ParsedConstantImpl(String className, Map<String, ParsedProperty> attributes) implements ParsedConstant { public record ParsedConstantImpl(String className, Map<String, ParsedProperty> attributes) implements ParsedConstant {
private static final String FX_CONSTANT = "fx:constant";
/**
* Instantiates the constant
*
* @param className The constant class
* @param attributes The constant attributes
* @throws NullPointerException if any argument is null
* @throws IllegalArgumentException If the attributes do not contain fx:constant
*/
public ParsedConstantImpl { public ParsedConstantImpl {
Objects.requireNonNull(className); Objects.requireNonNull(className);
if (!attributes.containsKey(FX_CONSTANT)) {
throw new IllegalArgumentException("Missing " + FX_CONSTANT);
}
attributes = Map.copyOf(attributes); attributes = Map.copyOf(attributes);
} }
/**
* Instantiates the constant
*
* @param className The constant class
* @param value The constant value
* @throws NullPointerException if any argument is null
*/
public ParsedConstantImpl(final String className, final String value) {
this(className, Map.of(FX_CONSTANT, new ParsedPropertyImpl(FX_CONSTANT, null, value)));
}
} }

View File

@@ -8,11 +8,33 @@ import java.util.Map;
/** /**
* Implementation of {@link ParsedCopy} * Implementation of {@link ParsedCopy}
* *
* @param attributes The reference properties * @param attributes The copy attributes
*/ */
public record ParsedCopyImpl(Map<String, ParsedProperty> attributes) implements ParsedCopy { public record ParsedCopyImpl(Map<String, ParsedProperty> attributes) implements ParsedCopy {
private static final String SOURCE = "source";
/**
* Instantiates the copy
*
* @param attributes The copy attributes
* @throws NullPointerException If the attributes are null
* @throws IllegalArgumentException If the attributes don't contain source
*/
public ParsedCopyImpl { public ParsedCopyImpl {
attributes = Map.copyOf(attributes); attributes = Map.copyOf(attributes);
if (!attributes.containsKey(SOURCE)) {
throw new IllegalArgumentException("Missing " + SOURCE);
}
}
/**
* Instantiates the copy
*
* @param source The source
* @throws NullPointerException If the source is null
*/
public ParsedCopyImpl(final String source) {
this(Map.of(SOURCE, new ParsedPropertyImpl(SOURCE, null, source)));
} }
} }

View File

@@ -8,12 +8,15 @@ import java.util.List;
/** /**
* Implementation of {@link ParsedObject} * Implementation of {@link ParsedObject}
* *
* @param className The object class * @param children The objects in this define
* @param attributes The object properties
* @param properties The object children (complex properties)
*/ */
public record ParsedDefineImpl(List<ParsedObject> children) implements ParsedDefine { public record ParsedDefineImpl(List<ParsedObject> children) implements ParsedDefine {
/**
* Instantiates the define
* @param children The children
* @throws NullPointerException If the children are null
*/
public ParsedDefineImpl { public ParsedDefineImpl {
children = List.copyOf(children); children = List.copyOf(children);
} }

View File

@@ -13,13 +13,23 @@ import java.util.SequencedCollection;
* Implementation of {@link ParsedFactory} * Implementation of {@link ParsedFactory}
* *
* @param className The factory class * @param className The factory class
* @param attributes The factory properties * @param attributes The factory attributes
* @param arguments The factory arguments * @param arguments The factory arguments
* @param children The factory children
*/ */
public record ParsedFactoryImpl(String className, Map<String, ParsedProperty> attributes, public record ParsedFactoryImpl(String className, Map<String, ParsedProperty> attributes,
SequencedCollection<ParsedObject> arguments, SequencedCollection<ParsedObject> arguments,
SequencedCollection<ParsedObject> children) implements ParsedFactory { SequencedCollection<ParsedObject> children) implements ParsedFactory {
/**
* Instantiates the factory
*
* @param className The factory class
* @param attributes The factory attributes
* @param arguments The factory arguments
* @param children The factory children
* @throws NullPointerException if any argument is null
*/
public ParsedFactoryImpl { public ParsedFactoryImpl {
Objects.requireNonNull(className); Objects.requireNonNull(className);
attributes = Map.copyOf(attributes); attributes = Map.copyOf(attributes);

View File

@@ -3,16 +3,53 @@ package com.github.gtache.fxml.compiler.parsing.impl;
import com.github.gtache.fxml.compiler.parsing.ParsedInclude; import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty; import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Implementation of {@link ParsedInclude} * Implementation of {@link ParsedInclude}
* *
* @param attributes The object properties * @param attributes The include attributes
*/ */
public record ParsedIncludeImpl(Map<String, ParsedProperty> attributes) implements ParsedInclude { public record ParsedIncludeImpl(Map<String, ParsedProperty> attributes) implements ParsedInclude {
private static final String SOURCE = "source";
/**
* Instantiates an include
*
* @param attributes The include attributes
* @throws NullPointerException If attributes is null
* @throws IllegalArgumentException If attributes does not contain source
*/
public ParsedIncludeImpl { public ParsedIncludeImpl {
if (!attributes.containsKey(SOURCE)) {
throw new IllegalArgumentException("Missing " + SOURCE);
}
attributes = Map.copyOf(attributes); attributes = Map.copyOf(attributes);
} }
/**
* Instantiates an include
*
* @param source The source
* @param resources The resources
* @param fxId The fx:id
* @throws NullPointerException If source is null
*/
public ParsedIncludeImpl(final String source, final String resources, final String fxId) {
this(createAttributes(source, resources, fxId));
}
private static Map<String, ParsedProperty> createAttributes(final String source, final String resources, final String fxId) {
final var map = HashMap.<String, ParsedProperty>newHashMap(3);
map.put(SOURCE, new ParsedPropertyImpl(SOURCE, null, source));
if (resources != null) {
map.put("resources", new ParsedPropertyImpl("resources", null, resources));
}
if (fxId != null) {
map.put("fx:id", new ParsedPropertyImpl("fx:id", null, fxId));
}
return map;
}
} }

View File

@@ -15,13 +15,22 @@ import java.util.SequencedMap;
* Implementation of {@link ParsedObject} * Implementation of {@link ParsedObject}
* *
* @param className The object class * @param className The object class
* @param attributes The object properties * @param attributes The object attributes
* @param properties The object children (complex properties) * @param properties The object properties
* @param children The object children
*/ */
public record ParsedObjectImpl(String className, Map<String, ParsedProperty> attributes, public record ParsedObjectImpl(String className, Map<String, ParsedProperty> attributes,
SequencedMap<ParsedProperty, SequencedCollection<ParsedObject>> properties, SequencedMap<ParsedProperty, SequencedCollection<ParsedObject>> properties,
SequencedCollection<ParsedObject> children) implements ParsedObject { SequencedCollection<ParsedObject> children) implements ParsedObject {
/**
* Instantiates a new object
* @param className The object class
* @param attributes The object attributes
* @param properties The object properties
* @param children The object children
* @throws NullPointerException if any parameter is null
*/
public ParsedObjectImpl { public ParsedObjectImpl {
Objects.requireNonNull(className); Objects.requireNonNull(className);
attributes = Map.copyOf(attributes); attributes = Map.copyOf(attributes);

View File

@@ -2,7 +2,7 @@ package com.github.gtache.fxml.compiler.parsing.impl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty; import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import java.util.Objects; import static java.util.Objects.requireNonNull;
/** /**
* Implementation of {@link ParsedProperty} * Implementation of {@link ParsedProperty}
@@ -10,11 +10,18 @@ import java.util.Objects;
* @param name The property name * @param name The property name
* @param sourceType The property source type * @param sourceType The property source type
* @param value The property value * @param value The property value
* @param defines The property defines
*/ */
public record ParsedPropertyImpl(String name, String sourceType, String value) implements ParsedProperty { public record ParsedPropertyImpl(String name, String sourceType, String value) implements ParsedProperty {
/**
* Instantiates a property
* @param name The property name
* @param sourceType The property source type
* @param value The property value
* @throws NullPointerException If the name or value is null
*/
public ParsedPropertyImpl { public ParsedPropertyImpl {
Objects.requireNonNull(name); requireNonNull(name);
requireNonNull(value);
} }
} }

View File

@@ -8,11 +8,33 @@ import java.util.Map;
/** /**
* Implementation of {@link ParsedReference} * Implementation of {@link ParsedReference}
* *
* @param attributes The reference properties * @param attributes The reference attributes
*/ */
public record ParsedReferenceImpl(Map<String, ParsedProperty> attributes) implements ParsedReference { public record ParsedReferenceImpl(Map<String, ParsedProperty> attributes) implements ParsedReference {
private static final String SOURCE = "source";
/**
* Instantiates a new reference
*
* @param attributes The reference attributes
* @throws NullPointerException If the attributes are null
* @throws IllegalArgumentException If the attributes do not contain source
*/
public ParsedReferenceImpl { public ParsedReferenceImpl {
if (!attributes.containsKey(SOURCE)) {
throw new IllegalArgumentException("Missing " + SOURCE);
}
attributes = Map.copyOf(attributes); attributes = Map.copyOf(attributes);
} }
/**
* Instantiates a new reference
*
* @param source The reference source
* @throws NullPointerException If the source is null
*/
public ParsedReferenceImpl(final String source) {
this(Map.of(SOURCE, new ParsedPropertyImpl(SOURCE, null, source)));
}
} }

View File

@@ -14,8 +14,32 @@ import java.util.Objects;
*/ */
public record ParsedValueImpl(String className, Map<String, ParsedProperty> attributes) implements ParsedValue { public record ParsedValueImpl(String className, Map<String, ParsedProperty> attributes) implements ParsedValue {
private static final String FX_VALUE = "fx:value";
/**
* Instantiates a new value
*
* @param className The value class
* @param attributes The value properties
* @throws NullPointerException If any parameter is null
* @throws IllegalArgumentException If the attributes don't contain fx:value
*/
public ParsedValueImpl { public ParsedValueImpl {
Objects.requireNonNull(className); Objects.requireNonNull(className);
if (!attributes.containsKey(FX_VALUE)) {
throw new IllegalArgumentException("Missing " + FX_VALUE);
}
attributes = Map.copyOf(attributes); attributes = Map.copyOf(attributes);
} }
/**
* Instantiates a new value
*
* @param className The value class
* @param value The value
* @throws NullPointerException If any parameter is null
*/
public ParsedValueImpl(final String className, final String value) {
this(className, Map.of(FX_VALUE, new ParsedPropertyImpl(FX_VALUE, null, value)));
}
} }

View File

@@ -5,7 +5,6 @@ module com.github.gtache.fxml.compiler.core {
requires transitive com.github.gtache.fxml.compiler.api; requires transitive com.github.gtache.fxml.compiler.api;
requires transitive javafx.graphics; requires transitive javafx.graphics;
requires org.apache.logging.log4j; requires org.apache.logging.log4j;
requires java.sql;
exports com.github.gtache.fxml.compiler.impl; exports com.github.gtache.fxml.compiler.impl;
exports com.github.gtache.fxml.compiler.parsing.impl; exports com.github.gtache.fxml.compiler.parsing.impl;

View File

@@ -1,7 +1,10 @@
package com.github.gtache.fxml.compiler.impl; package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.GenerationParameters; import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.InjectionType; import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility; import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@@ -20,15 +23,15 @@ class TestGenerationParametersImpl {
private final GenerationCompatibility compatibility; private final GenerationCompatibility compatibility;
private final boolean useImageInputStreamConstructor; private final boolean useImageInputStreamConstructor;
private final Map<String, String> bundleMap; private final Map<String, String> bundleMap;
private final InjectionType controllerInjectionType; private final ControllerInjectionType controllerInjectionType;
private final InjectionType fieldInjectionType; private final ControllerFieldInjectionType fieldInjectionType;
private final InjectionType methodInjectionType; private final ControllerMethodsInjectionType methodInjectionType;
private final InjectionType resourceInjectionType; private final ResourceBundleInjectionType resourceInjectionType;
private final GenerationParameters parameters; private final GenerationParameters parameters;
TestGenerationParametersImpl(@Mock final GenerationCompatibility compatibility, @Mock final InjectionType controllerInjectionType, TestGenerationParametersImpl(@Mock final GenerationCompatibility compatibility, @Mock final ControllerInjectionType controllerInjectionType,
@Mock final InjectionType fieldInjectionType, @Mock final InjectionType methodInjectionType, @Mock final ControllerFieldInjectionType fieldInjectionType, @Mock final ControllerMethodsInjectionType methodInjectionType,
@Mock final InjectionType resourceInjectionType) { @Mock final ResourceBundleInjectionType resourceInjectionType) {
this.compatibility = requireNonNull(compatibility); this.compatibility = requireNonNull(compatibility);
this.useImageInputStreamConstructor = true; this.useImageInputStreamConstructor = true;
this.controllerInjectionType = requireNonNull(controllerInjectionType); this.controllerInjectionType = requireNonNull(controllerInjectionType);

View File

@@ -0,0 +1,91 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInfo;
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.HelperMethodsFormatter;
import com.github.gtache.fxml.compiler.impl.internal.HelperProvider;
import com.github.gtache.fxml.compiler.impl.internal.InitializationFormatter;
import com.github.gtache.fxml.compiler.impl.internal.LoadMethodFormatter;
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 static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestGeneratorImpl {
private final HelperProvider helperProvider;
private final InitializationFormatter initializationFormatter;
private final LoadMethodFormatter loadMethodFormatter;
private final HelperMethodsFormatter helperMethodsFormatter;
private final GenerationRequest request;
private final String outputClassName;
private final ControllerInfo controllerInfo;
private final String className;
private final Generator generator;
TestGeneratorImpl(@Mock final HelperProvider helperProvider, @Mock final InitializationFormatter initializationFormatter,
@Mock final LoadMethodFormatter loadMethodFormatter, @Mock final HelperMethodsFormatter helperMethodsFormatter,
@Mock final GenerationRequest request, @Mock final ControllerInfo controllerInfo) {
this.helperProvider = requireNonNull(helperProvider);
this.initializationFormatter = requireNonNull(initializationFormatter);
this.loadMethodFormatter = requireNonNull(loadMethodFormatter);
this.helperMethodsFormatter = requireNonNull(helperMethodsFormatter);
this.request = requireNonNull(request);
this.controllerInfo = requireNonNull(controllerInfo);
this.outputClassName = "com.github.gtache.fxml.compiler.OutputClass";
this.className = "com.github.gtache.fxml.compiler.ControllerClass";
this.generator = new GeneratorImpl(p -> helperProvider);
}
@BeforeEach
void beforeEach() {
when(helperProvider.getInitializationFormatter()).thenReturn(initializationFormatter);
when(helperProvider.getLoadMethodFormatter()).thenReturn(loadMethodFormatter);
when(helperProvider.getHelperMethodsFormatter()).thenReturn(helperMethodsFormatter);
when(request.outputClassName()).thenReturn(outputClassName);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(controllerInfo.className()).thenReturn(className);
}
@Test
void testGenerate() throws GenerationException {
final var expected = """
package com.github.gtache.fxml.compiler;
/**
* Generated code
*/
public final class OutputClass {
/**
* Returns the controller if available
* @return The controller
* @throws IllegalStateException If the view is not loaded
*/
public com.github.gtache.fxml.compiler.ControllerClass controller() {
if (loaded) {
return controller;
} else {
throw new IllegalStateException("Not loaded");
}
}
}
""";
assertEquals(expected, generator.generate(request));
verify(initializationFormatter).formatFieldsAndConstructor();
verify(loadMethodFormatter).formatLoadMethod();
verify(helperMethodsFormatter).formatHelperMethods();
}
}

View File

@@ -1,8 +1,123 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
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.beans.NamedArg;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
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.Set;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestConstructorHelper { class TestConstructorHelper {
private final ConstructorArgs args;
private final Constructor<Object>[] constructors;
private final ParsedObject parsedObject;
private final Set<String> propertyNames;
TestConstructorHelper(@Mock final ConstructorArgs args, @Mock final Constructor<Object> constructor1,
@Mock final Constructor<Object> constructor2, @Mock final ParsedObject parsedObject) {
this.args = Objects.requireNonNull(args);
this.constructors = new Constructor[]{constructor1, constructor2};
this.parsedObject = Objects.requireNonNull(parsedObject);
this.propertyNames = Set.of("p1", "p2");
}
@Test
void testGetListConstructorArgsExists() throws GenerationException {
final var namedArgs = new LinkedHashMap<String, Parameter>();
namedArgs.put("p1", new Parameter("p1", int.class, "1"));
namedArgs.put("p2", new Parameter("p2", String.class, "value2"));
when(args.namedArgs()).thenReturn(namedArgs);
final var attributes = new HashMap<String, ParsedProperty>();
when(parsedObject.attributes()).thenReturn(attributes);
attributes.put("p1", new ParsedPropertyImpl("p1", null, "10"));
attributes.put("p2", new ParsedPropertyImpl("p2", null, "value"));
final var expected = List.of("10", "\"value\"");
assertEquals(expected, ConstructorHelper.getListConstructorArgs(args, parsedObject));
}
@Test
void tesGetListConstructorArgsDefault() throws GenerationException {
final var namedArgs = new LinkedHashMap<String, Parameter>();
namedArgs.put("p1", new Parameter("p1", int.class, "1"));
namedArgs.put("p2", new Parameter("p2", String.class, "value2"));
when(args.namedArgs()).thenReturn(namedArgs);
when(parsedObject.attributes()).thenReturn(Map.of());
when(parsedObject.properties()).thenReturn(new LinkedHashMap<>());
final var expected = List.of("1", "\"value2\"");
assertEquals(expected, ConstructorHelper.getListConstructorArgs(args, parsedObject));
}
@Test
void tetsGetListConstructorArgsComplex() {
final var namedArgs = new LinkedHashMap<String, Parameter>();
namedArgs.put("p1", new Parameter("p1", int.class, "1"));
when(args.namedArgs()).thenReturn(namedArgs);
when(parsedObject.attributes()).thenReturn(Map.of());
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("p1", null, "1"), List.of());
when(parsedObject.properties()).thenReturn(properties);
assertThrows(GenerationException.class, () -> ConstructorHelper.getListConstructorArgs(args, parsedObject));
}
@Test
void testGetMatchingConstructorArgs() {
final var namedArgs = new LinkedHashMap<String, Parameter>();
namedArgs.put("p1", new Parameter("p1", int.class, "0"));
namedArgs.put("p2", new Parameter("p2", String.class, "value2"));
when(constructors[0].getParameterAnnotations()).thenReturn(new Annotation[0][]);
when(constructors[1].getParameterAnnotations()).thenReturn(new Annotation[][]{{new NamedArgImpl("p1", "")}, {
new NamedArgImpl("p2", "value2")}});
when(constructors[1].getParameterTypes()).thenReturn(new Class[]{int.class, String.class});
final var expectedArgs = new ConstructorArgs(constructors[1], namedArgs);
assertEquals(expectedArgs, ConstructorHelper.getMatchingConstructorArgs(constructors, propertyNames));
}
@Test
void testGetMatchingConstructorArgsEmpty() {
final var namedArgs = new LinkedHashMap<String, Parameter>();
when(constructors[0].getParameterAnnotations()).thenReturn(new Annotation[0][]);
when(constructors[1].getParameterAnnotations()).thenReturn(new Annotation[0][]);
when(constructors[0].getParameterCount()).thenReturn(0);
when(constructors[1].getParameterCount()).thenReturn(1);
final var expectedArgs = new ConstructorArgs(constructors[0], namedArgs);
assertEquals(expectedArgs, ConstructorHelper.getMatchingConstructorArgs(constructors, propertyNames));
}
@Test
void testGetMatchingConstructorArgsNull() {
when(constructors[0].getParameterAnnotations()).thenReturn(new Annotation[0][]);
when(constructors[1].getParameterAnnotations()).thenReturn(new Annotation[0][]);
when(constructors[0].getParameterCount()).thenReturn(1);
when(constructors[1].getParameterCount()).thenReturn(1);
assertNull(ConstructorHelper.getMatchingConstructorArgs(constructors, propertyNames));
}
private record NamedArgImpl(String value, String defaultValue) implements NamedArg {
@Override
public Class<? extends Annotation> annotationType() {
return NamedArg.class;
}
}
} }

View File

@@ -1,10 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInfo; import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.InjectionType;
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 com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -17,7 +15,6 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@@ -49,8 +46,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectControllerFieldFactory() throws GenerationException { void testInjectControllerFieldFactory() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable); injector.injectControllerField(id, variable);
final var expected = " fieldMap.put(\"" + id + "\", " + variable + ");\n"; final var expected = " fieldMap.put(\"" + id + "\", " + variable + ");\n";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
@@ -58,8 +55,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectControllerFieldAssign() throws GenerationException { void testInjectControllerFieldAssign() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.ASSIGN, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable); injector.injectControllerField(id, variable);
final var expected = " controller." + id + " = " + variable + ";\n"; final var expected = " controller." + id + " = " + variable + ";\n";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
@@ -67,8 +64,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectControllerFieldSetters() throws GenerationException { void testInjectControllerFieldSetters() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.SETTERS, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.SETTERS, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable); injector.injectControllerField(id, variable);
final var expected = " controller." + GenerationHelper.getSetMethod(id) + "(" + variable + ");\n"; final var expected = " controller." + GenerationHelper.getSetMethod(id) + "(" + variable + ");\n";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
@@ -76,8 +73,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectControllerFieldReflection() throws GenerationException { void testInjectControllerFieldReflection() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.REFLECTION, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.REFLECTION, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable); injector.injectControllerField(id, variable);
final var expected = " injectField(\"" + id + "\", " + variable + ");\n"; final var expected = " injectField(\"" + id + "\", " + variable + ");\n";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
@@ -85,14 +82,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectControllerFieldUnknown() { void testInjectEventHandlerReferenceFactoryNoArgument() {
final var injector = new ControllerInjector(controllerInfo, mock(InjectionType.class), ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
assertThrows(GenerationException.class, () -> injector.injectControllerField(id, variable));
}
@Test
void testInjectEventHandlerReferenceFactoryNoArgument() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectEventHandlerControllerMethod(property, variable); injector.injectEventHandlerControllerMethod(property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> controller." + property.value().replace("#", "") + "());\n"; final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> controller." + property.value().replace("#", "") + "());\n";
assertEquals(1, controllerFactoryPostAction.size()); assertEquals(1, controllerFactoryPostAction.size());
@@ -101,8 +92,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectEventHandlerReferenceFactoryWithArgument() throws GenerationException { void testInjectEventHandlerReferenceFactoryWithArgument() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
when(controllerInfo.handlerHasArgument(propertyValue.replace("#", ""))).thenReturn(true); when(controllerInfo.handlerHasArgument(propertyValue.replace("#", ""))).thenReturn(true);
injector.injectEventHandlerControllerMethod(property, variable); injector.injectEventHandlerControllerMethod(property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n"; final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
@@ -112,8 +103,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectEventHandlerReflectionAssign() throws GenerationException { void testInjectEventHandlerReflectionAssign() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.ASSIGN, ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction);
injector.injectEventHandlerControllerMethod(property, variable); injector.injectEventHandlerControllerMethod(property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callEventHandlerMethod(\"" + propertyValue.replace("#", "") + "\", e));\n"; final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callEventHandlerMethod(\"" + propertyValue.replace("#", "") + "\", e));\n";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
@@ -121,20 +112,8 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectEventHandlerUnknownMethod() { void testInjectCallbackReflectionSetters() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.ASSIGN, mock(InjectionType.class), sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction);
assertThrows(GenerationException.class, () -> injector.injectEventHandlerControllerMethod(property, variable));
}
@Test
void testInjectEventHandlerUnknownField() {
final var injector = new ControllerInjector(controllerInfo, mock(InjectionType.class), ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction);
assertThrows(GenerationException.class, () -> injector.injectEventHandlerControllerMethod(property, variable));
}
@Test
void testInjectCallbackReflectionSetters() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.ASSIGN, ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction);
injector.injectCallbackControllerMethod(property, variable, "clazz"); injector.injectCallbackControllerMethod(property, variable, "clazz");
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callCallbackMethod(\"" + propertyValue.replace("#", "") + "\", e, clazz));\n"; final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callCallbackMethod(\"" + propertyValue.replace("#", "") + "\", e, clazz));\n";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
@@ -142,12 +121,21 @@ class TestControllerInjector {
} }
@Test @Test
void testInjectCallbackReferenceFactory() throws GenerationException { void testInjectCallbackReferenceFactory() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction); final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectCallbackControllerMethod(property, variable, "clazz"); injector.injectCallbackControllerMethod(property, variable, "clazz");
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n"; final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
assertEquals(1, controllerFactoryPostAction.size()); assertEquals(1, controllerFactoryPostAction.size());
assertEquals(expected, controllerFactoryPostAction.getFirst()); assertEquals(expected, controllerFactoryPostAction.getFirst());
assertEquals("", sb.toString()); assertEquals("", sb.toString());
} }
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ControllerInjector(null, ControllerFieldInjectionType.ASSIGN, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction));
assertThrows(NullPointerException.class, () -> new ControllerInjector(controllerInfo, null, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction));
assertThrows(NullPointerException.class, () -> new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, null, sb, controllerFactoryPostAction));
assertThrows(NullPointerException.class, () -> new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, ControllerMethodsInjectionType.REFERENCE, null, controllerFactoryPostAction));
assertThrows(NullPointerException.class, () -> new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, ControllerMethodsInjectionType.REFERENCE, sb, null));
}
} }

View File

@@ -1,8 +1,134 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.GenerationException;
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.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.SequencedCollection;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestFieldSetter { class TestFieldSetter {
private final HelperProvider helperProvider;
private final GenerationCompatibilityHelper compatibilityHelper;
private final StringBuilder sb;
private final SequencedCollection<String> controllerFactoryPostAction;
private final ParsedProperty property;
private final String propertyName;
private final String propertyValue;
private final String parentVariable;
TestFieldSetter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final ParsedProperty property) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.property = Objects.requireNonNull(property);
this.propertyName = "propertyName";
this.propertyValue = "$controller.value";
this.parentVariable = "variable";
this.sb = new StringBuilder();
this.controllerFactoryPostAction = new ArrayList<>();
}
@BeforeEach
void beforeEach() {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(compatibilityHelper.getStartVar(anyString(), anyInt())).then(i -> i.getArgument(0));
when(property.name()).thenReturn(propertyName);
when(property.value()).thenReturn(propertyValue);
}
@Test
void testSetEventHandlerAssign() throws GenerationException {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.ASSIGN, sb, controllerFactoryPostAction);
setter.setEventHandler(property, parentVariable);
final var expected = " " + parentVariable + ".setPropertyName(controller.value);\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testSetFieldAssignException() {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.ASSIGN, sb, controllerFactoryPostAction);
when(property.value()).thenReturn("x.value");
assertThrows(GenerationException.class, () -> setter.setField(property, parentVariable, ""));
}
@Test
void testSetFieldFactory() throws GenerationException {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.FACTORY, sb, controllerFactoryPostAction);
setter.setField(property, parentVariable, "");
final var expected = " " + parentVariable + ".setPropertyName(controller.getValue());\n";
assertEquals("", sb.toString());
assertEquals(List.of(expected), controllerFactoryPostAction);
}
@Test
void testSetFieldFactoryException() {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.FACTORY, sb, controllerFactoryPostAction);
when(property.value()).thenReturn("x.value");
assertThrows(GenerationException.class, () -> setter.setField(property, parentVariable, ""));
}
@Test
void testSetReflection() throws GenerationException {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.REFLECTION, sb, controllerFactoryPostAction);
setter.setField(property, parentVariable, "javafx.scene.control.Button");
final var expected = """
try {
java.lang.reflect.Fieldfield = controller.getClass().getDeclaredField("value");
field.setAccessible(true);
final var value = (javafx.scene.control.Button) field.get(controller);
variable.setPropertyName(value);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
""";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testSetReflectionException() {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.REFLECTION, sb, controllerFactoryPostAction);
when(property.value()).thenReturn("x.value");
assertThrows(GenerationException.class, () -> setter.setField(property, parentVariable, ""));
}
@Test
void testSetFieldSetters() throws GenerationException {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.SETTERS, sb, controllerFactoryPostAction);
setter.setField(property, parentVariable, "");
final var expected = " " + parentVariable + ".setPropertyName(controller.getValue());\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testSetFieldSettersException() {
final var setter = new FieldSetter(helperProvider, ControllerFieldInjectionType.SETTERS, sb, controllerFactoryPostAction);
when(property.value()).thenReturn("x.value");
assertThrows(GenerationException.class, () -> setter.setField(property, parentVariable, ""));
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new FieldSetter(null, ControllerFieldInjectionType.ASSIGN, sb, controllerFactoryPostAction));
assertThrows(NullPointerException.class, () -> new FieldSetter(helperProvider, null, sb, controllerFactoryPostAction));
assertThrows(NullPointerException.class, () -> new FieldSetter(helperProvider, ControllerFieldInjectionType.ASSIGN, null, controllerFactoryPostAction));
assertThrows(NullPointerException.class, () -> new FieldSetter(helperProvider, ControllerFieldInjectionType.ASSIGN, sb, null));
}
} }

View File

@@ -1,8 +1,189 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
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.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestFontFormatter { class TestFontFormatter {
private final HelperProvider helperProvider;
private final GenerationCompatibilityHelper compatibilityHelper;
private final URLFormatter urlFormatter;
private final StringBuilder sb;
private final ParsedObject parsedObject;
private final Map<String, ParsedProperty> attributes;
private final String variableName;
private final FontFormatter fontFormatter;
TestFontFormatter(@Mock final HelperProvider helperProvider,
@Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final URLFormatter urlFormatter,
@Mock final ParsedObject parsedObject) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.urlFormatter = Objects.requireNonNull(urlFormatter);
this.parsedObject = Objects.requireNonNull(parsedObject);
this.attributes = new HashMap<>();
this.variableName = "variable";
this.sb = new StringBuilder();
this.fontFormatter = new FontFormatter(helperProvider, sb);
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getURLFormatter()).thenReturn(urlFormatter);
when(parsedObject.attributes()).thenReturn(attributes);
when(parsedObject.children()).thenReturn(List.of());
when(parsedObject.properties()).thenReturn(new LinkedHashMap<>());
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(compatibilityHelper.getStartVar(anyString(), anyInt())).then(i -> i.getArgument(0));
when(urlFormatter.formatURL(anyString())).then(i -> i.getArgument(0) + "url");
}
@Test
void testHasChildren() {
when(parsedObject.children()).thenReturn(List.of(parsedObject));
assertThrows(GenerationException.class, () -> fontFormatter.formatFont(parsedObject, variableName));
}
@Test
void testHasProperties() {
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("str", null, ""), List.of(parsedObject));
when(parsedObject.properties()).thenReturn(properties);
assertThrows(GenerationException.class, () -> fontFormatter.formatFont(parsedObject, variableName));
}
@Test
void testUnknownAttribute() {
attributes.put("unknown", new ParsedPropertyImpl("unknown", null, "value"));
assertThrows(GenerationException.class, () -> fontFormatter.formatFont(parsedObject, variableName));
}
@Test
void testNoNameNorURL() {
attributes.put("size", new ParsedPropertyImpl("size", null, "14.0"));
attributes.put("style", new ParsedPropertyImpl("style", null, "Bold italic"));
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
assertThrows(GenerationException.class, () -> fontFormatter.formatFont(parsedObject, variableName));
}
@Test
void testNameDefault() throws GenerationException {
attributes.put("name", new ParsedPropertyImpl("name", null, "Arial"));
final var expected = """
javafx.scene.text.Fontvariable = new javafx.scene.text.Font("Arial", 12.0);
""";
fontFormatter.formatFont(parsedObject, variableName);
assertEquals(expected, sb.toString());
}
@Test
void testName() throws GenerationException {
attributes.put("name", new ParsedPropertyImpl("name", null, "Arial"));
attributes.put("size", new ParsedPropertyImpl("size", null, "14.0"));
final var expected = """
javafx.scene.text.Fontvariable = new javafx.scene.text.Font("Arial", 14.0);
""";
fontFormatter.formatFont(parsedObject, variableName);
assertEquals(expected, sb.toString());
}
@Test
void testNameWeight() throws GenerationException {
attributes.put("name", new ParsedPropertyImpl("name", null, "Arial"));
attributes.put("size", new ParsedPropertyImpl("size", null, "14.0"));
attributes.put("style", new ParsedPropertyImpl("style", null, "bold"));
final var expected = """
javafx.scene.text.Fontvariable = new javafx.scene.text.Font("Arial", javafx.scene.text.FontWeight.BOLD, javafx.scene.text.FontPosture.REGULAR, 14.0);
""";
fontFormatter.formatFont(parsedObject, variableName);
assertEquals(expected, sb.toString());
}
@Test
void testNamePosture() throws GenerationException {
attributes.put("name", new ParsedPropertyImpl("name", null, "Arial"));
attributes.put("size", new ParsedPropertyImpl("size", null, "14.0"));
attributes.put("style", new ParsedPropertyImpl("style", null, "italic"));
final var expected = """
javafx.scene.text.Fontvariable = new javafx.scene.text.Font("Arial", javafx.scene.text.FontWeight.NORMAL, javafx.scene.text.FontPosture.ITALIC, 14.0);
""";
fontFormatter.formatFont(parsedObject, variableName);
assertEquals(expected, sb.toString());
}
@Test
void testNameStyle() throws GenerationException {
attributes.put("name", new ParsedPropertyImpl("name", null, "Arial"));
attributes.put("size", new ParsedPropertyImpl("size", null, "14.0"));
attributes.put("style", new ParsedPropertyImpl("style", null, "bold italic"));
final var expected = """
javafx.scene.text.Fontvariable = new javafx.scene.text.Font("Arial", javafx.scene.text.FontWeight.BOLD, javafx.scene.text.FontPosture.ITALIC, 14.0);
""";
fontFormatter.formatFont(parsedObject, variableName);
assertEquals(expected, sb.toString());
}
@Test
void testURL() throws GenerationException {
attributes.put("url", new ParsedPropertyImpl("url", null, "file:/urlValue"));
final var expected = """
final javafx.scene.text.Font variable;
try (java.io.InputStreamin = file:/urlValueurl.openStream()) {
variable = javafx.scene.text.Font.loadFont(in, 12.0);
} catch (final java.io.IOException e) {
throw new RuntimeException(e);
}
""";
fontFormatter.formatFont(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(urlFormatter).formatURL("file:/urlValue");
}
@Test
void testURLAllAttributes() throws GenerationException {
attributes.put("url", new ParsedPropertyImpl("url", null, "file:/urlValue"));
attributes.put("name", new ParsedPropertyImpl("name", null, "Arial"));
attributes.put("size", new ParsedPropertyImpl("size", null, "14.0"));
attributes.put("style", new ParsedPropertyImpl("style", null, "bold italic"));
final var expected = """
final javafx.scene.text.Font variable;
try (java.io.InputStreamin = file:/urlValueurl.openStream()) {
variable = javafx.scene.text.Font.loadFont(in, 14.0);
} catch (final java.io.IOException e) {
throw new RuntimeException(e);
}
""";
fontFormatter.formatFont(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(urlFormatter).formatURL("file:/urlValue");
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new FontFormatter(null, sb));
assertThrows(NullPointerException.class, () -> new FontFormatter(helperProvider, null));
}
} }

View File

@@ -13,6 +13,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects; import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -123,4 +124,10 @@ class TestGenerationCompatibilityHelper {
when(compatibility.useCollectionsOf()).thenReturn(false); when(compatibility.useCollectionsOf()).thenReturn(false);
assertEquals("java.util.Arrays.asList(", compatibilityHelper.getListOf()); assertEquals("java.util.Arrays.asList(", compatibilityHelper.getListOf());
} }
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new GenerationCompatibilityHelper(null, compatibility));
assertThrows(NullPointerException.class, () -> new GenerationCompatibilityHelper(helperProvider, null));
}
} }

View File

@@ -1,8 +1,5 @@
package com.github.gtache.fxml.compiler.impl.internal; 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.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty; import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl; import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
@@ -23,29 +20,15 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestGenerationHelper { class TestGenerationHelper {
private final GenerationProgress progress; private final ParsedObject parsedObject;
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 Map<String, ParsedProperty> attributes;
private final String className; private final String className;
private final ParsedProperty property; private final ParsedProperty property;
private final String propertyName; private final String propertyName;
TestGenerationHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request, TestGenerationHelper(@Mock final ParsedObject parsedObject, @Mock final ParsedProperty property) {
@Mock final ControllerInfo controllerInfo, @Mock final ControllerFieldInfo fieldInfo, this.parsedObject = Objects.requireNonNull(parsedObject);
@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.property = Objects.requireNonNull(property);
this.idToVariableInfo = new HashMap<>();
this.variableName = "variable";
this.attributes = new HashMap<>(); this.attributes = new HashMap<>();
this.className = "java.lang.String"; this.className = "java.lang.String";
this.propertyName = "property"; this.propertyName = "property";
@@ -53,17 +36,14 @@ class TestGenerationHelper {
@BeforeEach @BeforeEach
void beforeEach() { void beforeEach() {
when(progress.request()).thenReturn(request); when(parsedObject.attributes()).thenReturn(attributes);
when(request.controllerInfo()).thenReturn(controllerInfo); when(parsedObject.className()).thenReturn(className);
when(object.attributes()).thenReturn(attributes);
when(object.className()).thenReturn(className);
when(property.name()).thenReturn(propertyName); when(property.name()).thenReturn(propertyName);
when(progress.idToVariableInfo()).thenReturn(idToVariableInfo);
} }
@Test @Test
void testGetVariablePrefixObject() { void testGetVariablePrefixObject() {
assertEquals("string", GenerationHelper.getVariablePrefix(object)); assertEquals("string", GenerationHelper.getVariablePrefix(parsedObject));
} }
@Test @Test
@@ -97,6 +77,6 @@ class TestGenerationHelper {
attributes.put("b", new ParsedPropertyImpl("b", null, "valueB")); attributes.put("b", new ParsedPropertyImpl("b", null, "valueB"));
attributes.put("c", new ParsedPropertyImpl("c", null, "valueC")); attributes.put("c", new ParsedPropertyImpl("c", null, "valueC"));
final var expected = List.of(attributes.get("a"), attributes.get("b"), attributes.get("c")); final var expected = List.of(attributes.get("a"), attributes.get("b"), attributes.get("c"));
assertEquals(expected, GenerationHelper.getSortedAttributes(object)); assertEquals(expected, GenerationHelper.getSortedAttributes(parsedObject));
} }
} }

View File

@@ -7,13 +7,8 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.SequencedCollection; import java.util.SequencedCollection;
import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -23,51 +18,27 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
class TestGenerationProgress { class TestGenerationProgress {
private final GenerationRequest request; private final GenerationRequest request;
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 SequencedCollection<String> controllerFactoryPostAction;
private final StringBuilder sb; private final StringBuilder sb;
private final GenerationProgress progress; private final GenerationProgress progress;
TestGenerationProgress(@Mock final GenerationRequest request, @Mock final VariableInfo variableInfo) { TestGenerationProgress(@Mock final GenerationRequest request) {
this.request = requireNonNull(request); 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<>(); this.controllerFactoryPostAction = new ArrayList<>();
controllerFactoryPostAction.add("bla"); controllerFactoryPostAction.add("bla");
this.sb = new StringBuilder("test"); this.sb = new StringBuilder("test");
this.progress = new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb); this.progress = new GenerationProgress(request, controllerFactoryPostAction, sb);
} }
@Test @Test
void testGetters() { void testGetters() {
assertEquals(request, progress.request()); assertEquals(request, progress.request());
assertEquals(idToVariableInfo, progress.idToVariableInfo());
assertEquals(variableNameCounters, progress.variableNameCounters());
assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction()); assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
assertEquals(sb, progress.stringBuilder()); assertEquals(sb, progress.stringBuilder());
} }
@Test @Test
void testConstructorDoesntCopy() { void testConstructorDoesntCopy() {
idToVariableInfo.clear();
assertEquals(idToVariableInfo, progress.idToVariableInfo());
variableNameCounters.clear();
assertEquals(variableNameCounters, progress.variableNameCounters());
controllerClassToVariable.clear();
assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
controllerFactoryPostAction.clear(); controllerFactoryPostAction.clear();
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction()); assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -77,15 +48,6 @@ class TestGenerationProgress {
@Test @Test
void testCanModify() { void testCanModify() {
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"); progress.controllerFactoryPostAction().add("bla2");
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction()); assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -97,29 +59,15 @@ class TestGenerationProgress {
void testOtherConstructor() { void testOtherConstructor() {
final var progress2 = new GenerationProgress(request); final var progress2 = new GenerationProgress(request);
assertEquals(request, progress2.request()); assertEquals(request, progress2.request());
assertEquals(Map.of(), progress2.idToVariableInfo());
assertEquals(Map.of(), progress2.variableNameCounters());
assertEquals(Map.of(), progress2.controllerClassToVariable());
assertEquals(List.of(), progress2.controllerFactoryPostAction()); assertEquals(List.of(), progress2.controllerFactoryPostAction());
assertEquals("", progress2.stringBuilder().toString()); assertEquals("", progress2.stringBuilder().toString());
} }
@Test
void testGetNextVariableName() {
assertEquals("var0", progress.getNextVariableName("var"));
assertEquals("var1", progress.getNextVariableName("var"));
assertEquals("var2", progress.getNextVariableName("var"));
assertEquals("bla0", progress.getNextVariableName("bla"));
}
@Test @Test
void testIllegal() { void testIllegal() {
assertThrows(NullPointerException.class, () -> new GenerationProgress(null, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb)); assertThrows(NullPointerException.class, () -> new GenerationProgress(null, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb)); assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, null, controllerClassToVariable, controllerFactoryPostAction, sb)); assertThrows(NullPointerException.class, () -> new GenerationProgress(request, controllerFactoryPostAction, null));
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));
} }
} }

View File

@@ -1,8 +1,217 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; 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;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestHelperMethodsFormatter { class TestHelperMethodsFormatter {
private final HelperProvider helperProvider;
private final GenerationCompatibilityHelper compatibilityHelper;
private final StringBuilder sb;
TestHelperMethodsFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.sb = new StringBuilder();
}
@BeforeEach
void beforeEach() {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(compatibilityHelper.getStartVar(anyString(), anyInt())).then(i -> i.getArgument(0));
when(compatibilityHelper.getListOf()).thenReturn("listof(");
when(compatibilityHelper.getGetFirst()).thenReturn(".getFirst()");
when(compatibilityHelper.getToList()).thenReturn(".toList()");
}
@Test
void testMethodReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.ASSIGN, ControllerMethodsInjectionType.REFLECTION, sb);
final var expected = """
private <T extends javafx.event.Event> void callEventHandlerMethod(final String methodName, final T event) {
try {
final java.lang.reflect.Method method;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>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 {
java.util.List<java.lang.reflect.Method>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;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 2 && clazz.isAssignableFrom(m.getParameterTypes()[1])).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);
}
}
""";
helperMethodsFormatter.formatHelperMethods();
assertEquals(expected, sb.toString());
}
@Test
void testFieldReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.REFLECTION, ControllerMethodsInjectionType.REFERENCE, sb);
final var expected = """
private <T> void injectField(final String fieldName, final T object) {
try {
java.lang.reflect.Fieldfield = 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);
}
}
""";
helperMethodsFormatter.formatHelperMethods();
assertEquals(expected, sb.toString());
}
@Test
void testNoReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb);
helperMethodsFormatter.formatHelperMethods();
assertEquals("", sb.toString());
}
@Test
void testBothReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.REFLECTION, ControllerMethodsInjectionType.REFLECTION, sb);
final var expected = """
private <T extends javafx.event.Event> void callEventHandlerMethod(final String methodName, final T event) {
try {
final java.lang.reflect.Method method;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>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 {
java.util.List<java.lang.reflect.Method>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;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 2 && clazz.isAssignableFrom(m.getParameterTypes()[1])).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);
}
}
private <T> void injectField(final String fieldName, final T object) {
try {
java.lang.reflect.Fieldfield = 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);
}
}
""";
helperMethodsFormatter.formatHelperMethods();
assertEquals(expected, sb.toString());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(null, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb));
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(helperProvider, null, ControllerMethodsInjectionType.REFERENCE, sb));
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.FACTORY, null, sb));
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, null));
}
} }

View File

@@ -0,0 +1,169 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
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.Objects;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestHelperProvider {
private final GenerationProgress progress;
private final GenerationRequest request;
private final GenerationParameters parameters;
private final GenerationCompatibility compatibility;
private final ControllerInjectionType controllerInjectionType;
private final ControllerFieldInjectionType fieldInjectionType;
private final ControllerMethodsInjectionType methodInjectionType;
private final ResourceBundleInjectionType resourceInjectionType;
private final ControllerInfo controllerInfo;
private final StringBuilder sb;
private final List<String> controllerFactoryPostAction;
private final HelperProvider helperProvider;
@BeforeEach
void beforeEach() {
when(progress.request()).thenReturn(request);
when(request.parameters()).thenReturn(parameters);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(parameters.controllerInjectionType()).thenReturn(controllerInjectionType);
when(parameters.fieldInjectionType()).thenReturn(fieldInjectionType);
when(parameters.methodInjectionType()).thenReturn(methodInjectionType);
when(parameters.resourceInjectionType()).thenReturn(resourceInjectionType);
when(parameters.compatibility()).thenReturn(compatibility);
when(progress.stringBuilder()).thenReturn(sb);
when(progress.controllerFactoryPostAction()).thenReturn(controllerFactoryPostAction);
}
TestHelperProvider(@Mock final GenerationProgress progress, @Mock final GenerationRequest request, @Mock final GenerationCompatibility compatibility,
@Mock final GenerationParameters parameters, @Mock final ControllerInfo controllerInfo) {
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.compatibility = Objects.requireNonNull(compatibility);
this.parameters = Objects.requireNonNull(parameters);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.controllerInjectionType = ControllerInjectionType.INSTANCE;
this.fieldInjectionType = ControllerFieldInjectionType.FACTORY;
this.methodInjectionType = ControllerMethodsInjectionType.REFLECTION;
this.resourceInjectionType = ResourceBundleInjectionType.CONSTRUCTOR;
this.sb = new StringBuilder();
this.controllerFactoryPostAction = List.of();
this.helperProvider = new HelperProvider(progress);
}
@Test
void testControllerInjector() {
final var injector = helperProvider.getControllerInjector();
assertSame(injector, helperProvider.getControllerInjector());
}
@Test
void testGetFieldSetter() {
final var fieldSetter = helperProvider.getFieldSetter();
assertSame(fieldSetter, helperProvider.getFieldSetter());
}
@Test
void testGetFontFormatter() {
final var fontFormatter = helperProvider.getFontFormatter();
assertSame(fontFormatter, helperProvider.getFontFormatter());
}
@Test
void testGetCompatibilityHelper() {
final var compatibilityHelper = helperProvider.getCompatibilityHelper();
assertSame(compatibilityHelper, helperProvider.getCompatibilityHelper());
}
@Test
void testGetHelperMethodsFormatter() {
final var helperMethodsFormatter = helperProvider.getHelperMethodsFormatter();
assertSame(helperMethodsFormatter, helperProvider.getHelperMethodsFormatter());
}
@Test
void testGetImageFormatter() {
final var imageFormatter = helperProvider.getImageFormatter();
assertSame(imageFormatter, helperProvider.getImageFormatter());
}
@Test
void testGetInitializationFormatter() {
final var initializationFormatter = helperProvider.getInitializationFormatter();
assertSame(initializationFormatter, helperProvider.getInitializationFormatter());
}
@Test
void testGetLoadMethodFormatter() {
final var loadMethodFormatter = helperProvider.getLoadMethodFormatter();
assertSame(loadMethodFormatter, helperProvider.getLoadMethodFormatter());
}
@Test
void testGetObjectFormatter() {
final var objectFormatter = helperProvider.getObjectFormatter();
assertSame(objectFormatter, helperProvider.getObjectFormatter());
}
@Test
void testGetPropertyFormatter() {
final var propertyFormatter = helperProvider.getPropertyFormatter();
assertSame(propertyFormatter, helperProvider.getPropertyFormatter());
}
@Test
void testGetReflectionHelper() {
final var reflectionHelper = helperProvider.getReflectionHelper();
assertSame(reflectionHelper, helperProvider.getReflectionHelper());
}
@Test
void testGetSceneFormatter() {
final var sceneFormatter = helperProvider.getSceneFormatter();
assertSame(sceneFormatter, helperProvider.getSceneFormatter());
}
@Test
void testGetTriangleMeshFormatter() {
final var triangleMeshFormatter = helperProvider.getTriangleMeshFormatter();
assertSame(triangleMeshFormatter, helperProvider.getTriangleMeshFormatter());
}
@Test
void testGetURLFormatter() {
final var urlFormatter = helperProvider.getURLFormatter();
assertSame(urlFormatter, helperProvider.getURLFormatter());
}
@Test
void testGetValueFormatter() {
final var valueFormatter = helperProvider.getValueFormatter();
assertSame(valueFormatter, helperProvider.getValueFormatter());
}
@Test
void testGetVariableProvider() {
final var variableProvider = helperProvider.getVariableProvider();
assertSame(variableProvider, helperProvider.getVariableProvider());
}
@Test
void getWebViewFormatter() {
final var webViewFormatter = helperProvider.getWebViewFormatter();
assertSame(webViewFormatter, helperProvider.getWebViewFormatter());
}
}

View File

@@ -1,8 +1,173 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
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.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestImageFormatter { class TestImageFormatter {
private final HelperProvider helperProvider;
private final GenerationCompatibilityHelper compatibilityHelper;
private final URLFormatter urlFormatter;
private final VariableProvider variableProvider;
private final ParsedObject parsedObject;
private final Map<String, ParsedProperty> attributes;
private final String variableName;
private final StringBuilder sb;
private final ImageFormatter imageFormatter;
TestImageFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final URLFormatter urlFormatter, @Mock final VariableProvider variableProvider,
@Mock final ParsedObject parsedObject) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.urlFormatter = Objects.requireNonNull(urlFormatter);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.parsedObject = Objects.requireNonNull(parsedObject);
this.attributes = new HashMap<>();
this.variableName = "variable";
this.sb = new StringBuilder();
this.imageFormatter = new ImageFormatter(helperProvider, sb, true);
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getURLFormatter()).thenReturn(urlFormatter);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(variableProvider.getNextVariableName(anyString())).then(i -> i.getArgument(0));
when(parsedObject.children()).thenReturn(List.of());
when(parsedObject.properties()).thenReturn(new LinkedHashMap<>());
when(parsedObject.attributes()).thenReturn(attributes);
when(urlFormatter.formatURL(anyString())).then(i -> i.getArgument(0) + "url");
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(compatibilityHelper.getStartVar(anyString(), anyInt())).then(i -> i.getArgument(0));
}
@Test
void testHasChildren() {
when(parsedObject.children()).thenReturn(List.of(parsedObject));
assertThrows(GenerationException.class, () -> imageFormatter.formatImage(parsedObject, variableName));
}
@Test
void testHasProperties() {
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("str", null, ""), List.of(parsedObject));
when(parsedObject.properties()).thenReturn(properties);
assertThrows(GenerationException.class, () -> imageFormatter.formatImage(parsedObject, variableName));
}
@Test
void testUnknownAttribute() {
attributes.put("unknown", new ParsedPropertyImpl("unknown", null, "value"));
assertThrows(GenerationException.class, () -> imageFormatter.formatImage(parsedObject, variableName));
}
@Test
void testMissingUrl() {
attributes.put("requestedWidth", new ParsedPropertyImpl("requestedWidth", null, "50"));
attributes.put("requestedHeight", new ParsedPropertyImpl("requestedHeight", null, "12.0"));
attributes.put("preserveRatio", new ParsedPropertyImpl("preserveRatio", null, "true"));
attributes.put("smooth", new ParsedPropertyImpl("smooth", null, "true"));
attributes.put("backgroundLoading", new ParsedPropertyImpl("backgroundLoading", null, "true"));
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
assertThrows(GenerationException.class, () -> imageFormatter.formatImage(parsedObject, variableName));
}
@Test
void testMinimumAttributesURL() throws GenerationException {
final var urlImageFormatter = new ImageFormatter(helperProvider, sb, false);
attributes.put("url", new ParsedPropertyImpl("url", null, "urlValue"));
final var expected = """
StringurlStr = urlValueurl.toString();
javafx.scene.image.Imagevariable = new javafx.scene.image.Image(urlStr, 0.0, 0.0, false, false, false);
""";
urlImageFormatter.formatImage(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(urlFormatter).formatURL("urlValue");
}
@Test
void testAllAttributesURL() throws GenerationException {
final var urlImageFormatter = new ImageFormatter(helperProvider, sb, false);
attributes.put("url", new ParsedPropertyImpl("url", null, "urlValue"));
attributes.put("requestedWidth", new ParsedPropertyImpl("requestedWidth", null, "50"));
attributes.put("requestedHeight", new ParsedPropertyImpl("requestedHeight", null, "12.0"));
attributes.put("preserveRatio", new ParsedPropertyImpl("preserveRatio", null, "true"));
attributes.put("smooth", new ParsedPropertyImpl("smooth", null, "true"));
attributes.put("backgroundLoading", new ParsedPropertyImpl("backgroundLoading", null, "true"));
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
final var expected = """
StringurlStr = urlValueurl.toString();
javafx.scene.image.Imagevariable = new javafx.scene.image.Image(urlStr, 50.0, 12.0, true, true, true);
""";
urlImageFormatter.formatImage(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(urlFormatter).formatURL("urlValue");
}
@Test
void testMinimumAttributesInputStream() throws GenerationException {
attributes.put("url", new ParsedPropertyImpl("url", null, "urlValue"));
final var expected = """
final javafx.scene.image.Image variable;
try (java.io.InputStreaminputStream = urlValueurl.openStream()) {
variable = new javafx.scene.image.Image(inputStream, 0.0, 0.0, false, false);
} catch (final java.io.IOException e) {
throw new RuntimeException(e);
}
""";
imageFormatter.formatImage(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(urlFormatter).formatURL("urlValue");
}
@Test
void testAllAttributesInputStream() throws GenerationException {
attributes.put("url", new ParsedPropertyImpl("url", null, "urlValue"));
attributes.put("requestedWidth", new ParsedPropertyImpl("requestedWidth", null, "50"));
attributes.put("requestedHeight", new ParsedPropertyImpl("requestedHeight", null, "12.0"));
attributes.put("preserveRatio", new ParsedPropertyImpl("preserveRatio", null, "true"));
attributes.put("smooth", new ParsedPropertyImpl("smooth", null, "true"));
attributes.put("backgroundLoading", new ParsedPropertyImpl("backgroundLoading", null, "true"));
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
final var expected = """
final javafx.scene.image.Image variable;
try (java.io.InputStreaminputStream = urlValueurl.openStream()) {
variable = new javafx.scene.image.Image(inputStream, 50.0, 12.0, true, true);
} catch (final java.io.IOException e) {
throw new RuntimeException(e);
}
""";
imageFormatter.formatImage(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(urlFormatter).formatURL("urlValue");
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ImageFormatter(null, sb, true));
assertThrows(NullPointerException.class, () -> new ImageFormatter(helperProvider, null, true));
}
} }

View File

@@ -1,8 +1,12 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationParameters; import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest; import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.SourceInfo; import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.parsing.ParsedInclude; import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@@ -12,51 +16,577 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestInitializationFormatter { class TestInitializationFormatter {
private final GenerationProgress progress;
private final GenerationRequest request; private final GenerationRequest request;
private final GenerationParameters parameters; private final GenerationParameters parameters;
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final StringBuilder stringBuilder; private final GenerationCompatibilityHelper compatibilityHelper;
private final VariableProvider variableProvider;
private final ControllerInfo controllerInfo;
private final SourceInfo sourceInfo; private final SourceInfo sourceInfo;
private final ParsedInclude include; private final ParsedInclude include;
private final Map<String, SourceInfo> sourceToSourceInfo; private final Map<String, SourceInfo> sourceToSourceInfo;
private final StringBuilder sb;
private final Map<String, String> controllerClassToVariable;
private final InitializationFormatter initializationFormatter; private final InitializationFormatter initializationFormatter;
TestInitializationFormatter(@Mock final GenerationProgress progress, @Mock final GenerationRequest request, TestInitializationFormatter(@Mock final GenerationRequest request, @Mock final GenerationParameters parameters, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final GenerationParameters parameters, @Mock final HelperProvider helperProvider, @Mock final HelperProvider helperProvider, @Mock final VariableProvider variableProvider,
@Mock final SourceInfo sourceInfo, @Mock final ParsedInclude include) { @Mock final ControllerInfo controllerInfo, @Mock final SourceInfo sourceInfo,
this.progress = Objects.requireNonNull(progress); @Mock final ParsedInclude include) {
this.request = Objects.requireNonNull(request); this.request = Objects.requireNonNull(request);
this.parameters = Objects.requireNonNull(parameters); this.parameters = Objects.requireNonNull(parameters);
this.stringBuilder = new StringBuilder();
this.helperProvider = Objects.requireNonNull(helperProvider); this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.sourceInfo = Objects.requireNonNull(sourceInfo); this.sourceInfo = Objects.requireNonNull(sourceInfo);
this.include = Objects.requireNonNull(include); this.include = Objects.requireNonNull(include);
this.sourceToSourceInfo = new HashMap<>(); this.sourceToSourceInfo = new HashMap<>();
this.initializationFormatter = new InitializationFormatter(helperProvider, progress); this.sb = new StringBuilder();
this.controllerClassToVariable = new HashMap<>();
this.initializationFormatter = new InitializationFormatter(helperProvider, request, sb, controllerClassToVariable);
} }
@BeforeEach @BeforeEach
void beforeEach() { void beforeEach() {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(variableProvider.getNextVariableName(anyString())).then(i -> i.getArgument(0));
when(request.parameters()).thenReturn(parameters); when(request.parameters()).thenReturn(parameters);
when(progress.request()).thenReturn(request); when(request.outputClassName()).thenReturn("com.github.gtache.fxml.OutputClassName");
when(progress.stringBuilder()).thenReturn(stringBuilder);
when(request.sourceInfo()).thenReturn(sourceInfo); when(request.sourceInfo()).thenReturn(sourceInfo);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(controllerInfo.className()).thenReturn("com.github.gtache.fxml.ControllerClassName");
when(sourceInfo.sourceToSourceInfo()).thenReturn(sourceToSourceInfo); when(sourceInfo.sourceToSourceInfo()).thenReturn(sourceToSourceInfo);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.GET_BUNDLE);
}
@Test
void testDuplicateControllersNoFactory(@Mock final SourceInfo sourceInfo2, @Mock final SourceInfo sourceInfo3,
@Mock final SourceInfo sourceInfo4) {
when(sourceInfo.controllerClassName()).thenReturn("controller");
when(sourceInfo.includedSources()).thenReturn(List.of(sourceInfo2));
when(sourceInfo2.controllerClassName()).thenReturn("controller2");
when(sourceInfo2.includedSources()).thenReturn(List.of(sourceInfo3, sourceInfo4));
when(sourceInfo3.controllerClassName()).thenReturn("controller3");
when(sourceInfo3.includedSources()).thenReturn(List.of(sourceInfo4));
when(sourceInfo4.controllerClassName()).thenReturn("controller4");
assertThrows(GenerationException.class, initializationFormatter::formatFieldsAndConstructor);
}
@Test
void testFormatFieldFactory() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionType.FACTORY);
final var expected = """
private final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.ControllerClassName> controllerFactory;
private com.github.gtache.fxml.ControllerClassName controller;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controllerFactory The controller factory
*/
public OutputClassName(final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.ControllerClassName> controllerFactory) {
this.controllerFactory = java.util.Objects.requireNonNull(controllerFactory);
}
""";
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testFormatBaseFactory() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
final var expected = """
private final java.util.function.Supplier<com.github.gtache.fxml.ControllerClassName> controllerFactory;
private com.github.gtache.fxml.ControllerClassName controller;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controllerFactory The controller factory
*/
public OutputClassName(final java.util.function.Supplier<com.github.gtache.fxml.ControllerClassName> controllerFactory) {
this.controllerFactory = java.util.Objects.requireNonNull(controllerFactory);
}
""";
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testFormatInstance() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
final var expected = """
private final com.github.gtache.fxml.ControllerClassName controller;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controller The controller instance
*/
public OutputClassName(final com.github.gtache.fxml.ControllerClassName controller) {
this.controller = java.util.Objects.requireNonNull(controller);
}
""";
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testConstructorResourceBundle() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR);
final var expected = """
private final com.github.gtache.fxml.ControllerClassName controller;
private final java.util.ResourceBundle resourceBundle;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controller The controller instance
* @param resourceBundle The resource bundle
*/
public OutputClassName(final com.github.gtache.fxml.ControllerClassName controller, final java.util.ResourceBundle resourceBundle) {
this.controller = java.util.Objects.requireNonNull(controller);
this.resourceBundle = java.util.Objects.requireNonNull(resourceBundle);
}
""";
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testConstructorFunctionResourceBundle() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_FUNCTION);
final var expected = """
private final com.github.gtache.fxml.ControllerClassName controller;
private final java.util.function.Function<String, String> resourceBundleFunction;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controller The controller instance
* @param resourceBundleFunction The resource bundle
*/
public OutputClassName(final com.github.gtache.fxml.ControllerClassName controller, final java.util.function.Function<String, String> resourceBundleFunction) {
this.controller = java.util.Objects.requireNonNull(controller);
this.resourceBundleFunction = java.util.Objects.requireNonNull(resourceBundleFunction);
}
""";
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testConstructorNamedResourceBundle() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_NAME);
final var expected = """
private final com.github.gtache.fxml.ControllerClassName controller;
private final String resourceBundleName;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controller The controller instance
* @param resourceBundleName The resource bundle
*/
public OutputClassName(final com.github.gtache.fxml.ControllerClassName controller, final String resourceBundleName) {
this.controller = java.util.Objects.requireNonNull(controller);
this.resourceBundleName = java.util.Objects.requireNonNull(resourceBundleName);
}
""";
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testHasControllersFieldFactory() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionType.FACTORY);
final var expected = """
private final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.ControllerClassName> controllerFactory;
private final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.Controller2> controller2Factory;
private final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.Controller3> controller3Factory;
private com.github.gtache.fxml.ControllerClassName controller;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controllerFactory The controller factory
* @param controller2Factory The subcontroller factory for com.github.gtache.fxml.Controller2
* @param controller3Factory The subcontroller factory for com.github.gtache.fxml.Controller3
*/
public OutputClassName(final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.ControllerClassName> controllerFactory, final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.Controller2> controller2Factory, final java.util.function.Function<java.util.Map<String, Object>, com.github.gtache.fxml.Controller3> controller3Factory) {
this.controllerFactory = java.util.Objects.requireNonNull(controllerFactory);
this.controller2Factory = java.util.Objects.requireNonNull(controller2Factory);
this.controller3Factory = java.util.Objects.requireNonNull(controller3Factory);
}
""";
final var source2 = mock(SourceInfo.class);
when(source2.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
final var source3 = mock(SourceInfo.class);
when(source3.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller3");
when(sourceInfo.includedSources()).thenReturn(List.of(source2));
when(source2.includedSources()).thenReturn(List.of(source3));
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testHasControllersBaseFactory() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
final var expected = """
private final java.util.function.Supplier<com.github.gtache.fxml.ControllerClassName> controllerFactory;
private final java.util.function.Supplier<com.github.gtache.fxml.Controller2> controller2Factory;
private final java.util.function.Supplier<com.github.gtache.fxml.Controller3> controller3Factory;
private com.github.gtache.fxml.ControllerClassName controller;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controllerFactory The controller factory
* @param controller2Factory The subcontroller factory for com.github.gtache.fxml.Controller2
* @param controller3Factory The subcontroller factory for com.github.gtache.fxml.Controller3
*/
public OutputClassName(final java.util.function.Supplier<com.github.gtache.fxml.ControllerClassName> controllerFactory, final java.util.function.Supplier<com.github.gtache.fxml.Controller2> controller2Factory, final java.util.function.Supplier<com.github.gtache.fxml.Controller3> controller3Factory) {
this.controllerFactory = java.util.Objects.requireNonNull(controllerFactory);
this.controller2Factory = java.util.Objects.requireNonNull(controller2Factory);
this.controller3Factory = java.util.Objects.requireNonNull(controller3Factory);
}
""";
final var source2 = mock(SourceInfo.class);
when(source2.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
final var source3 = mock(SourceInfo.class);
when(source3.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller3");
when(sourceInfo.includedSources()).thenReturn(List.of(source2));
when(source2.includedSources()).thenReturn(List.of(source3));
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testHasControllersInstance() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
final var expected = """
private final com.github.gtache.fxml.ControllerClassName controller;
private final com.github.gtache.fxml.Controller2 controller2;
private final com.github.gtache.fxml.Controller3 controller3;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controller The controller instance
* @param controller2 The subcontroller instance for com.github.gtache.fxml.Controller2
* @param controller3 The subcontroller instance for com.github.gtache.fxml.Controller3
*/
public OutputClassName(final com.github.gtache.fxml.ControllerClassName controller, final com.github.gtache.fxml.Controller2 controller2, final com.github.gtache.fxml.Controller3 controller3) {
this.controller = java.util.Objects.requireNonNull(controller);
this.controller2 = java.util.Objects.requireNonNull(controller2);
this.controller3 = java.util.Objects.requireNonNull(controller3);
}
""";
final var source2 = mock(SourceInfo.class);
when(source2.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
final var source3 = mock(SourceInfo.class);
when(source3.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller3");
when(sourceInfo.includedSources()).thenReturn(List.of(source2));
when(source2.includedSources()).thenReturn(List.of(source3));
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
}
@Test
void testComplete() throws GenerationException {
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR);
final var expected = """
private final java.util.function.Supplier<com.github.gtache.fxml.ControllerClassName> controllerFactory;
private final java.util.function.Supplier<com.github.gtache.fxml.Controller2> controller2Factory;
private final java.util.function.Supplier<com.github.gtache.fxml.Controller3> controller3Factory;
private com.github.gtache.fxml.ControllerClassName controller;
private final java.util.ResourceBundle resourceBundle;
private boolean loaded;
/**
* Instantiates a new OutputClassName
* @param controllerFactory The controller factory
* @param controller2Factory The subcontroller factory for com.github.gtache.fxml.Controller2
* @param controller3Factory The subcontroller factory for com.github.gtache.fxml.Controller3
* @param resourceBundle The resource bundle
*/
public OutputClassName(final java.util.function.Supplier<com.github.gtache.fxml.ControllerClassName> controllerFactory, final java.util.function.Supplier<com.github.gtache.fxml.Controller2> controller2Factory, final java.util.function.Supplier<com.github.gtache.fxml.Controller3> controller3Factory, final java.util.ResourceBundle resourceBundle) {
this.controllerFactory = java.util.Objects.requireNonNull(controllerFactory);
this.controller2Factory = java.util.Objects.requireNonNull(controller2Factory);
this.controller3Factory = java.util.Objects.requireNonNull(controller3Factory);
this.resourceBundle = java.util.Objects.requireNonNull(resourceBundle);
}
""";
final var source2 = mock(SourceInfo.class);
when(source2.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
final var source3 = mock(SourceInfo.class);
when(source3.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller3");
when(sourceInfo.includedSources()).thenReturn(List.of(source2, source3));
when(source2.includedSources()).thenReturn(List.of(source3));
initializationFormatter.formatFieldsAndConstructor();
assertEquals(expected, sb.toString());
} }
@Test @Test
void testFormatSubViewConstructorCallNullSubInfo() { void testFormatSubViewConstructorCallNullSubInfo() {
assertThrows(GenerationException.class, () -> initializationFormatter.formatSubViewConstructorCall(include)); assertThrows(GenerationException.class, () -> initializationFormatter.formatSubViewConstructorCall(include));
} }
@Test
void testFormatSubViewInstance(@Mock final SourceInfo subInfo) throws GenerationException {
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
when(subInfo.includedSources()).thenReturn(List.of());
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewFactory(@Mock final SourceInfo subInfo) throws GenerationException {
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
when(subInfo.includedSources()).thenReturn(List.of());
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2Factory);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewMultipleInstanceFactory(@Mock final SourceInfo subInfo) throws GenerationException {
final var source3 = mock(SourceInfo.class);
when(source3.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller3");
when(source3.includedSources()).thenReturn(List.of());
final var source4 = mock(SourceInfo.class);
when(source4.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller4");
when(source4.includedSources()).thenReturn(List.of());
when(subInfo.includedSources()).thenReturn(List.of(source3, source4));
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller3", "controller3");
controllerClassToVariable.put("com.github.gtache.fxml.Controller4", "controller4");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2Factory, controller3Factory, controller4Factory);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewMultipleInstance(@Mock final SourceInfo subInfo) throws GenerationException {
final var source3 = mock(SourceInfo.class);
when(source3.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller3");
when(source3.includedSources()).thenReturn(List.of());
final var source4 = mock(SourceInfo.class);
when(source4.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller4");
when(source4.includedSources()).thenReturn(List.of());
when(subInfo.includedSources()).thenReturn(List.of(source3, source4));
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller3", "controller3");
controllerClassToVariable.put("com.github.gtache.fxml.Controller4", "controller4");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, controller3, controller4);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleGetter(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.GETTER);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleGetBundle(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.GET_BUNDLE);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleConstructorNoResources(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, resourceBundle);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleConstructorFunctionNoResources(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_FUNCTION);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, resourceBundleFunction);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleConstructorNameNoResources(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_NAME);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
final var expected = "com.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, resourceBundleName);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleConstructorResources(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
when(include.resources()).thenReturn("resources");
final var expected = "java.util.ResourceBundleresourceBundle = java.util.ResourceBundle.getBundle(\"resources\");\ncom.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, resourceBundle);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleConstructorFunctionResources(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_FUNCTION);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
when(include.resources()).thenReturn("resources");
final var expected = "java.util.ResourceBundleresourceBundle = java.util.ResourceBundle.getBundle(\"resources\");\njava.util.function.Function<String, String>resourceBundleFunction = (java.util.function.Function<String, String>) s -> resourceBundle.getString(s);\ncom.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, resourceBundleFunction);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewRequiresBundleConstructorName(@Mock final SourceInfo subInfo) throws GenerationException {
when(subInfo.includedSources()).thenReturn(List.of());
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_NAME);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
when(include.resources()).thenReturn("resources");
final var expected = "StringresourceBundleName = \"resources\";\ncom.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, resourceBundleName);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testFormatSubViewComplete(@Mock final SourceInfo subInfo) throws GenerationException {
final var source3 = mock(SourceInfo.class);
when(source3.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller3");
when(source3.includedSources()).thenReturn(List.of());
final var source4 = mock(SourceInfo.class);
when(source4.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller4");
when(source4.includedSources()).thenReturn(List.of(source3));
when(subInfo.includedSources()).thenReturn(List.of(source4));
when(subInfo.requiresResourceBundle()).thenReturn(true);
when(include.source()).thenReturn("source");
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.INSTANCE);
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_NAME);
sourceToSourceInfo.put("source", subInfo);
when(subInfo.controllerClassName()).thenReturn("com.github.gtache.fxml.Controller2");
when(subInfo.generatedClassName()).thenReturn("com.github.gtache.fxml.View2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller2", "controller2");
controllerClassToVariable.put("com.github.gtache.fxml.Controller3", "controller3");
controllerClassToVariable.put("com.github.gtache.fxml.Controller4", "controller4");
when(include.resources()).thenReturn("resources");
final var expected = "StringresourceBundleName = \"resources\";\ncom.github.gtache.fxml.View2view2 = new com.github.gtache.fxml.View2(controller2, controller3, controller4, resourceBundleName);\n";
assertEquals("view2", initializationFormatter.formatSubViewConstructorCall(include));
assertEquals(expected, sb.toString());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new InitializationFormatter(null, request, sb));
assertThrows(NullPointerException.class, () -> new InitializationFormatter(helperProvider, null, sb));
assertThrows(NullPointerException.class, () -> new InitializationFormatter(helperProvider, request, null));
}
} }

View File

@@ -1,8 +1,203 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
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.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestLoadMethodFormatter { class TestLoadMethodFormatter {
}
private final HelperProvider helperProvider;
private final ObjectFormatter objectFormatter;
private final GenerationCompatibilityHelper compatibilityHelper;
private final VariableProvider variableProvider;
private final GenerationProgress progress;
private final GenerationRequest request;
private final GenerationParameters parameters;
private final ParsedObject object;
private final ControllerInfo controllerInfo;
private final String className;
private final StringBuilder sb;
private final List<String> controllerFactoryPostAction;
private final LoadMethodFormatter loadMethodFormatter;
TestLoadMethodFormatter(@Mock final HelperProvider helperProvider, @Mock final ObjectFormatter objectFormatter,
@Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final VariableProvider variableProvider,
@Mock final GenerationProgress progress, @Mock final GenerationRequest request, @Mock final GenerationParameters parameters,
@Mock final ParsedObject object, @Mock final ControllerInfo controllerInfo) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.objectFormatter = Objects.requireNonNull(objectFormatter);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.request = Objects.requireNonNull(request);
this.parameters = Objects.requireNonNull(parameters);
this.object = Objects.requireNonNull(object);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.className = "class";
this.sb = new StringBuilder();
this.controllerFactoryPostAction = new ArrayList<>();
this.progress = Objects.requireNonNull(progress);
this.loadMethodFormatter = new LoadMethodFormatter(helperProvider, progress);
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(object.className()).thenReturn(className);
when(progress.request()).thenReturn(request);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(request.parameters()).thenReturn(parameters);
when(request.rootObject()).thenReturn(object);
when(progress.stringBuilder()).thenReturn(sb);
when(progress.controllerFactoryPostAction()).thenReturn(controllerFactoryPostAction);
when(variableProvider.getNextVariableName(any(String.class))).then(i -> i.getArgument(0));
doAnswer(i -> sb.append(i.getArgument(0) + "-" + i.getArgument(1))).when(objectFormatter).format(any(), any());
when(controllerInfo.className()).thenReturn(className);
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(compatibilityHelper.getStartVar(anyString(), anyInt())).then(i -> i.getArgument(0));
}
@Test
void testEasiestCase() throws GenerationException {
when(object.toString()).thenReturn("object");
loadMethodFormatter.formatLoadMethod();
final var expected = """
/**
* Loads the view. Can only be called once.
*
* @return The view parent
*/
public <T> T load() {
if (loaded) {
throw new IllegalStateException("Already loaded");
}
object-class loaded = true;
return (T) class;
}
""";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(object, "class");
}
@Test
void testConstructorNameFactoryInitialize() throws GenerationException {
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.CONSTRUCTOR_NAME);
when(parameters.controllerInjectionType()).thenReturn(ControllerInjectionType.FACTORY);
when(controllerInfo.hasInitialize()).thenReturn(true);
when(object.toString()).thenReturn("object");
loadMethodFormatter.formatLoadMethod();
final var expected = """
/**
* Loads the view. Can only be called once.
*
* @return The view parent
*/
public <T> T load() {
if (loaded) {
throw new IllegalStateException("Already loaded");
}
java.util.ResourceBundleresourceBundle = java.util.ResourceBundle.getBundle(resourceBundleName);
controller = controllerFactory.create();
object-class controller.initialize();
loaded = true;
return (T) class;
}
""";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(object, "class");
}
@Test
void testGetBundleFieldFactoryReflectionInitialize() throws GenerationException {
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.GET_BUNDLE);
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionType.FACTORY);
when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFLECTION);
when(parameters.bundleMap()).thenReturn(Map.of(className, "bundle"));
when(controllerInfo.hasInitialize()).thenReturn(true);
when(object.toString()).thenReturn("object");
loadMethodFormatter.formatLoadMethod();
final var expected = """
/**
* Loads the view. Can only be called once.
*
* @return The view parent
*/
public <T> T load() {
if (loaded) {
throw new IllegalStateException("Already loaded");
}
java.util.ResourceBundleresourceBundle = java.util.ResourceBundle.getBundle("bundle");
java.util.Map<String, Object>fieldMap = new java.util.HashMap<String, Object>();
object-class controller = controllerFactory.create(fieldMap);
try {
java.lang.reflect.Methodinitialize = controller.getClass().getDeclaredMethod("initialize");
initialize.setAccessible(true);
initialize.invoke(controller);
} catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException("Error using reflection", e);
}
loaded = true;
return (T) class;
}
""";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(object, "class");
}
@Test
void testGetBundleFieldFactoryReflectionNoBundle() throws GenerationException {
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.GET_BUNDLE);
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionType.FACTORY);
when(parameters.methodInjectionType()).thenReturn(com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFLECTION);
when(parameters.bundleMap()).thenReturn(Map.of());
when(object.toString()).thenReturn("object");
loadMethodFormatter.formatLoadMethod();
final var expected = """
/**
* Loads the view. Can only be called once.
*
* @return The view parent
*/
public <T> T load() {
if (loaded) {
throw new IllegalStateException("Already loaded");
}
java.util.Map<String, Object>fieldMap = new java.util.HashMap<String, Object>();
object-class controller = controllerFactory.create(fieldMap);
loaded = true;
return (T) class;
}
""";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(object, "class");
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new LoadMethodFormatter(null, progress));
assertThrows(NullPointerException.class, () -> new LoadMethodFormatter(helperProvider, null));
}
}

View File

@@ -1,8 +1,631 @@
package com.github.gtache.fxml.compiler.impl.internal; 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.SourceInfo;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.ParsedReference;
import com.github.gtache.fxml.compiler.parsing.impl.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestObjectFormatter { class TestObjectFormatter {
}
private final HelperProvider helperProvider;
private final ControllerInjector controllerInjector;
private final GenerationCompatibilityHelper compatibilityHelper;
private final InitializationFormatter initializationFormatter;
private final ReflectionHelper reflectionHelper;
private final VariableProvider variableProvider;
private final GenerationRequest request;
private final ControllerInfo controllerInfo;
private final SourceInfo sourceInfo;
private final StringBuilder sb;
private final String variableName;
private final ObjectFormatter objectFormatter;
TestObjectFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final InitializationFormatter initializationFormatter, @Mock final ReflectionHelper reflectionHelper,
@Mock final VariableProvider variableProvider, @Mock final GenerationRequest request,
@Mock final ControllerInfo controllerInfo, @Mock final ControllerInjector controllerInjector,
@Mock final SourceInfo sourceInfo) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.controllerInjector = Objects.requireNonNull(controllerInjector);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.initializationFormatter = Objects.requireNonNull(initializationFormatter);
this.reflectionHelper = Objects.requireNonNull(reflectionHelper);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.request = Objects.requireNonNull(request);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.sourceInfo = Objects.requireNonNull(sourceInfo);
this.sb = new StringBuilder();
this.variableName = "variable";
this.objectFormatter = spy(new ObjectFormatter(helperProvider, request, sb));
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getControllerInjector()).thenReturn(controllerInjector);
when(helperProvider.getInitializationFormatter()).thenReturn(initializationFormatter);
when(helperProvider.getReflectionHelper()).thenReturn(reflectionHelper);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(compatibilityHelper.getStartVar(anyString(), anyInt())).then(i -> i.getArgument(0));
when(variableProvider.getNextVariableName(anyString())).then(i -> i.getArgument(0));
when(request.controllerInfo()).thenReturn(controllerInfo);
when(request.sourceInfo()).thenReturn(sourceInfo);
doAnswer(i -> {
final var value = (ParsedInclude) i.getArgument(0);
sb.append("include(").append(value.source()).append(", ").append(value.resources()).append(")");
return "view";
}).when(initializationFormatter).formatSubViewConstructorCall(any());
doAnswer(i -> {
final var id = (String) i.getArgument(0);
final var variable = (String) i.getArgument(1);
sb.append("inject(").append(id).append(", ").append(variable).append(")");
return null;
}).when(controllerInjector).injectControllerField(anyString(), anyString());
when(compatibilityHelper.getStartVar(any(ParsedObject.class))).thenReturn("startVar");
}
@Test
void testHandleIdNull() throws GenerationException {
final var parsedObject = new ParsedValueImpl("className", Map.of("fx:value", new ParsedPropertyImpl("fx:value", null, "value")));
objectFormatter.format(parsedObject, variableName);
verifyNoInteractions(controllerInjector, reflectionHelper, variableProvider);
}
@Test
void testHandleIdNotFound() throws GenerationException {
final var className = "className";
final var value = "id";
final var parsedObject = new ParsedValueImpl(className, Map.of("fx:id", new ParsedPropertyImpl("fx:id", null, value),
"fx:value", new ParsedPropertyImpl("fx:value", null, "value")));
objectFormatter.format(parsedObject, variableName);
final var variableInfo = new VariableInfo(value, parsedObject, variableName, className);
verify(variableProvider).addVariableInfo(value, variableInfo);
verifyNoInteractions(controllerInjector, reflectionHelper);
}
@Test
void testHandleIdGeneric(@Mock final ControllerFieldInfo fieldInfo) throws GenerationException {
final var value = "id";
final var genericClassName = "javafx.scene.control.ComboBox";
final var parsedObject = new ParsedValueImpl(genericClassName, Map.of("fx:id", new ParsedPropertyImpl("fx:id", null, value),
"fx:value", new ParsedPropertyImpl("fx:value", null, "value")));
final var genericTypes = "<java.lang.String, java.lang.Integer>";
when(reflectionHelper.getGenericTypes(parsedObject)).thenReturn("<java.lang.String, java.lang.Integer>");
when(controllerInfo.fieldInfo(value)).thenReturn(fieldInfo);
objectFormatter.format(parsedObject, variableName);
final var variableInfo = new VariableInfo(value, parsedObject, variableName, genericClassName + genericTypes);
verify(variableProvider).addVariableInfo(value, variableInfo);
verify(reflectionHelper).getGenericTypes(parsedObject);
verify(controllerInfo).fieldInfo(value);
verify(controllerInjector).injectControllerField(value, variableName);
}
@Test
void testHandleId(@Mock final ControllerFieldInfo fieldInfo) throws GenerationException {
final var className = "java.lang.String";
final var value = "id";
final var parsedObject = new ParsedValueImpl(className, Map.of("fx:id", new ParsedPropertyImpl("fx:id", null, value),
"fx:value", new ParsedPropertyImpl("fx:value", null, "value")));
when(controllerInfo.fieldInfo(value)).thenReturn(fieldInfo);
objectFormatter.format(parsedObject, variableName);
final var variableInfo = new VariableInfo(value, parsedObject, variableName, className);
verify(variableProvider).addVariableInfo(value, variableInfo);
verify(controllerInfo).fieldInfo(value);
verify(controllerInjector).injectControllerField(value, variableName);
verifyNoInteractions(reflectionHelper);
}
@Test
void testHandleIdPropertyTooManyChildren(@Mock final ControllerFieldInfo fieldInfo, @Mock PropertyFormatter propertyFormatter) {
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
final var className = "javafx.scene.control.Label";
final var value = "id";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("fx:id", null, ""), List.of(new ParsedTextImpl("value"), new ParsedTextImpl("value2")));
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(controllerInfo.fieldInfo(value)).thenReturn(fieldInfo);
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testHandleIdPropertyNoChildren(@Mock final ControllerFieldInfo fieldInfo, @Mock PropertyFormatter propertyFormatter) {
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
final var className = "javafx.scene.control.Label";
final var value = "id";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("fx:id", null, ""), List.of());
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(controllerInfo.fieldInfo(value)).thenReturn(fieldInfo);
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testHandleIdPropertyNotText(@Mock final ControllerFieldInfo fieldInfo, @Mock PropertyFormatter propertyFormatter) {
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
final var className = "javafx.scene.control.Label";
final var value = "id";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("fx:id", null, ""), List.of(mock(ParsedObject.class)));
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(controllerInfo.fieldInfo(value)).thenReturn(fieldInfo);
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testHandleIdProperty(@Mock final ControllerFieldInfo fieldInfo, @Mock PropertyFormatter propertyFormatter) throws GenerationException {
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
final var className = "javafx.scene.control.Label";
final var value = "id";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var define = mock(ParsedDefine.class);
properties.put(new ParsedPropertyImpl("fx:id", null, ""), List.of(define, new ParsedTextImpl(value)));
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(controllerInfo.fieldInfo(value)).thenReturn(fieldInfo);
doNothing().when(objectFormatter).format(define, "define");
objectFormatter.format(parsedObject, variableName);
final var variableInfo = new VariableInfo(value, parsedObject, variableName, className);
verify(variableProvider).addVariableInfo(value, variableInfo);
verify(controllerInfo).fieldInfo(value);
verify(controllerInjector).injectControllerField(value, variableName);
verify(objectFormatter).format(define, "define");
}
@Test
void testFormatConstant() throws GenerationException {
final var className = "java.lang.String";
final var attributes = new HashMap<String, ParsedProperty>();
attributes.put("fx:constant", new ParsedPropertyImpl("fx:constant", null, "value"));
final var constant = new ParsedConstantImpl(className, attributes);
objectFormatter.format(constant, variableName);
final var expected = "java.lang.String" + variableName + " = " + className + "." + constant.constant() + ";\n";
assertEquals(expected, sb.toString());
}
@Test
void testFormatCopyNoInfo() {
final var copy = new ParsedCopyImpl("source");
assertThrows(GenerationException.class, () -> objectFormatter.format(copy, variableName));
}
@Test
void testFormatCopy(@Mock final VariableInfo variableInfo) throws GenerationException {
when(variableProvider.getVariableInfo("source")).thenReturn(variableInfo);
final var infoVariableName = "vn";
when(variableInfo.variableName()).thenReturn(infoVariableName);
final var className = "className";
when(variableInfo.className()).thenReturn(className);
final var copy = new ParsedCopyImpl("source");
objectFormatter.format(copy, variableName);
final var expected = "classNamevariable = new className(vn);\n";
assertEquals(expected, sb.toString());
}
@Test
void testFormatDefine(@Mock final ParsedObject inner, @Mock final ParsedReference inner2) throws GenerationException {
final var define = new ParsedDefineImpl(List.of(inner, inner2));
doAnswer(i -> sb.append("object")).when(objectFormatter).format(inner, "definedObject");
doAnswer(i -> sb.append("reference")).when(objectFormatter).format(inner2, "definedObject");
objectFormatter.format(define, variableName);
assertEquals("objectreference", sb.toString());
verify(objectFormatter).format(inner, "definedObject");
verify(objectFormatter).format(inner2, "definedObject");
}
@Test
void testFormatFactoryVar(@Mock final GenerationParameters parameters, @Mock final GenerationCompatibility compatibility) throws GenerationException {
when(parameters.compatibility()).thenReturn(compatibility);
when(request.parameters()).thenReturn(parameters);
when(compatibility.useVar()).thenReturn(true);
final var children = List.<ParsedObject>of(new ParsedDefineImpl(List.of()));
final var arguments = List.of(mock(ParsedObject.class), mock(ParsedObject.class));
for (final var c : children) {
doAnswer(i -> {
final var object = (ParsedObject) i.getArgument(0);
sb.append("define");
return object;
}).when(objectFormatter).format(c, "parseddefine");
}
for (final var a : arguments) {
doAnswer(i -> {
final var object = (ParsedObject) i.getArgument(0);
sb.append("argument");
return object;
}).when(objectFormatter).format(a, "arg");
}
final var factory = new ParsedFactoryImpl("javafx.collections.FXCollections",
Map.of("fx:factory", new ParsedPropertyImpl("fx:factory", null, "observableArrayList")),
arguments, children);
objectFormatter.format(factory, variableName);
assertEquals("defineargumentargumentjavafx.collections.FXCollectionsvariable = javafx.collections.FXCollections.observableArrayList(arg, arg);\n", sb.toString());
for (final var child : children) {
verify(objectFormatter).format(child, "parseddefine");
}
for (final var argument : arguments) {
verify(objectFormatter).format(argument, "arg");
}
}
@Test
void testFormatFactory(@Mock final GenerationParameters parameters, @Mock final GenerationCompatibility compatibility) throws GenerationException {
when(parameters.compatibility()).thenReturn(compatibility);
when(request.parameters()).thenReturn(parameters);
final var children = List.<ParsedObject>of(new ParsedDefineImpl(List.of()));
for (final var c : children) {
doAnswer(i -> {
final var object = (ParsedObject) i.getArgument(0);
sb.append("define");
return object;
}).when(objectFormatter).format(c, "parseddefine");
}
final var factory = new ParsedFactoryImpl("javafx.collections.FXCollections",
Map.of("fx:factory", new ParsedPropertyImpl("fx:factory", null, "emptyObservableList")),
List.of(), children);
objectFormatter.format(factory, variableName);
assertEquals("definejavafx.collections.ObservableListvariable = javafx.collections.FXCollections.emptyObservableList();\n", sb.toString());
for (final var child : children) {
verify(objectFormatter).format(child, "parseddefine");
}
}
@Test
void testFormatIncludeOnlySource() throws GenerationException {
final var include = new ParsedIncludeImpl("source", null, null);
objectFormatter.format(include, variableName);
final var expected = "include(source, null) final javafx.scene.Parent variable = view.load();\n";
assertEquals(expected, sb.toString());
verify(initializationFormatter).formatSubViewConstructorCall(include);
}
@Test
void testFormatIncludeIDNotInController(@Mock final SourceInfo innerSourceInfo) throws GenerationException {
final var controllerClassName = "controllerClassName";
final var source = "source";
when(innerSourceInfo.controllerClassName()).thenReturn(controllerClassName);
final var sourceToSourceInfo = Map.of(source, innerSourceInfo);
when(sourceInfo.sourceToSourceInfo()).thenReturn(sourceToSourceInfo);
final var include = new ParsedIncludeImpl("source", "resources", "id");
objectFormatter.format(include, variableName);
final var expected = "include(source, resources) final javafx.scene.Parent variable = view.load();\n" +
"controllerClassNamecontroller = view.controller();\n";
assertEquals(expected, sb.toString());
verify(initializationFormatter).formatSubViewConstructorCall(include);
}
@Test
void testFormatIncludeID(@Mock final ControllerFieldInfo fieldInfo, @Mock final SourceInfo innerSourceInfo) throws GenerationException {
when(controllerInfo.fieldInfo("id")).thenReturn(fieldInfo);
when(controllerInfo.fieldInfo("idController")).thenReturn(fieldInfo);
final var controllerClassName = "controllerClassName";
final var source = "source";
when(innerSourceInfo.controllerClassName()).thenReturn(controllerClassName);
final var sourceToSourceInfo = Map.of(source, innerSourceInfo);
when(sourceInfo.sourceToSourceInfo()).thenReturn(sourceToSourceInfo);
final var include = new ParsedIncludeImpl(source, "resources", "id");
objectFormatter.format(include, variableName);
final var expected = "include(source, resources) final javafx.scene.Parent variable = view.load();\n" +
"controllerClassNamecontroller = view.controller();\ninject(idController, controller)inject(id, variable)";
assertEquals(expected, sb.toString());
verify(initializationFormatter).formatSubViewConstructorCall(include);
verify(controllerInjector).injectControllerField("id", "variable");
verify(controllerInjector).injectControllerField("idController", "controller");
}
@Test
void testFormatReferenceNullVariable() {
final var reference = new ParsedReferenceImpl("source");
assertThrows(GenerationException.class, () -> objectFormatter.format(reference, null));
}
@Test
void testFormatReference(@Mock final VariableInfo variableInfo) throws GenerationException {
when(variableProvider.getVariableInfo("source")).thenReturn(variableInfo);
final var infoVariableName = "vn";
when(variableInfo.variableName()).thenReturn(infoVariableName);
final var className = "className";
when(variableInfo.className()).thenReturn(className);
final var reference = new ParsedReferenceImpl("source");
objectFormatter.format(reference, variableName);
final var expected = "classNamevariable = vn;\n";
assertEquals(expected, sb.toString());
}
@Test
void testFormatValue() throws GenerationException {
final var value = new ParsedValueImpl("className", "value");
objectFormatter.format(value, variableName);
final var expected = "classNamevariable = className.valueOf(\"value\");\n";
assertEquals(expected, sb.toString());
}
@Test
void testFormatText() throws GenerationException {
final var text = new ParsedTextImpl("text");
objectFormatter.format(text, variableName);
final var expected = "Stringvariable = \"text\";\n";
assertEquals(expected, sb.toString());
verifyNoInteractions(variableProvider);
}
@Test
void testFormatScene(@Mock final SceneFormatter sceneFormatter) throws GenerationException {
when(helperProvider.getSceneFormatter()).thenReturn(sceneFormatter);
final var object = new ParsedObjectImpl("javafx.scene.Scene", Map.of(), new LinkedHashMap<>(), List.of());
doAnswer(i -> sb.append("scene")).when(sceneFormatter).formatScene(object, variableName);
objectFormatter.format(object, variableName);
assertEquals("scene", sb.toString());
verify(sceneFormatter).formatScene(object, variableName);
}
@Test
void testFormatFont(@Mock final FontFormatter fontFormatter) throws GenerationException {
when(helperProvider.getFontFormatter()).thenReturn(fontFormatter);
final var object = new ParsedObjectImpl("javafx.scene.text.Font", Map.of(), new LinkedHashMap<>(), List.of());
doAnswer(i -> sb.append("font")).when(fontFormatter).formatFont(object, variableName);
objectFormatter.format(object, variableName);
assertEquals("font", sb.toString());
verify(fontFormatter).formatFont(object, variableName);
}
@Test
void testFormatImage(@Mock final ImageFormatter imageFormatter) throws GenerationException {
when(helperProvider.getImageFormatter()).thenReturn(imageFormatter);
final var object = new ParsedObjectImpl("javafx.scene.image.Image", Map.of(), new LinkedHashMap<>(), List.of());
doAnswer(i -> sb.append("image")).when(imageFormatter).formatImage(object, variableName);
objectFormatter.format(object, variableName);
assertEquals("image", sb.toString());
verify(imageFormatter).formatImage(object, variableName);
}
@Test
void testFormatTriangleMesh(@Mock final TriangleMeshFormatter triangleMeshFormatter) throws GenerationException {
when(helperProvider.getTriangleMeshFormatter()).thenReturn(triangleMeshFormatter);
final var object = new ParsedObjectImpl("javafx.scene.shape.TriangleMesh", Map.of(), new LinkedHashMap<>(), List.of());
doAnswer(i -> sb.append("triangleMesh")).when(triangleMeshFormatter).formatTriangleMesh(object, variableName);
objectFormatter.format(object, variableName);
assertEquals("triangleMesh", sb.toString());
verify(triangleMeshFormatter).formatTriangleMesh(object, variableName);
}
@Test
void testFormatURL(@Mock final URLFormatter urlFormatter) throws GenerationException {
when(helperProvider.getURLFormatter()).thenReturn(urlFormatter);
final var object = new ParsedObjectImpl("java.net.URL", Map.of(), new LinkedHashMap<>(), List.of());
doAnswer(i -> sb.append("url")).when(urlFormatter).formatURL(object, variableName);
objectFormatter.format(object, variableName);
assertEquals("url", sb.toString());
verify(urlFormatter).formatURL(object, variableName);
}
@Test
void testFormatWebView(@Mock final WebViewFormatter webViewFormatter) throws GenerationException {
when(helperProvider.getWebViewFormatter()).thenReturn(webViewFormatter);
final var object = new ParsedObjectImpl("javafx.scene.web.WebView", Map.of(), new LinkedHashMap<>(), List.of());
doAnswer(i -> sb.append("webView")).when(webViewFormatter).formatWebView(object, variableName);
objectFormatter.format(object, variableName);
assertEquals("webView", sb.toString());
verify(webViewFormatter).formatWebView(object, variableName);
}
@Test
void testFormatSimpleClassProperties() {
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("str", null, ""), List.of(mock(ParsedObject.class)));
final var parsedObject = new ParsedObjectImpl("java.lang.String", Map.of(), properties, List.of());
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassInvalidAttribute() {
final var parsedObject = new ParsedObjectImpl("java.lang.Integer", Map.of("invalid", new ParsedPropertyImpl("invalid", null, "4")), new LinkedHashMap<>(), List.of());
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassFXValueHasChildren() {
final var parsedObject = new ParsedObjectImpl("java.lang.Byte", Map.of("fx:value", new ParsedPropertyImpl("fx:value", null, "1")), new LinkedHashMap<>(), List.of(mock(ParsedObject.class)));
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassFXValueHasValue() {
final var parsedObject = new ParsedObjectImpl("java.lang.Short", Map.of("fx:value", new ParsedPropertyImpl("fx:value", null, "2"), "value", new ParsedPropertyImpl("value", null, "value")), new LinkedHashMap<>(), List.of());
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassFXValue() throws GenerationException {
final var parsedObject = new ParsedObjectImpl("java.lang.Long", Map.of("fx:value", new ParsedPropertyImpl("fx:value", null, "3")), new LinkedHashMap<>(), List.of());
final var expected = "startVarvariable = 3;\n";
objectFormatter.format(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(compatibilityHelper).getStartVar(parsedObject);
}
@Test
void testFormatSimpleClassValueHasChildren() {
final var parsedObject = new ParsedObjectImpl("java.lang.Float", Map.of("value", new ParsedPropertyImpl("value", null, "4")), new LinkedHashMap<>(), List.of(mock(ParsedObject.class)));
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassValue() throws GenerationException {
final var parsedObject = new ParsedObjectImpl("java.lang.Double", Map.of("value", new ParsedPropertyImpl("value", null, "5")), new LinkedHashMap<>(), List.of());
final var expected = "startVarvariable = 5;\n";
objectFormatter.format(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(compatibilityHelper).getStartVar(parsedObject);
}
@Test
void testFormatSimpleClassNoChildren() {
final var parsedObject = new ParsedObjectImpl("javafx.geometry.Pos", Map.of(), new LinkedHashMap<>(), List.of());
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassChildren() {
final var parsedObject = new ParsedObjectImpl("javafx.geometry.Pos", Map.of(), new LinkedHashMap<>(), List.of(mock(ParsedObject.class), mock(ParsedObject.class)));
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassChildNotText() {
final var parsedObject = new ParsedObjectImpl("javafx.geometry.Pos", Map.of(), new LinkedHashMap<>(), List.of(mock(ParsedObject.class)));
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatSimpleClassChild() throws GenerationException {
final var define = mock(ParsedDefine.class);
doAnswer(i -> sb.append("define")).when(objectFormatter).format(define, "define");
final var text = new ParsedTextImpl("TOP_LEFT");
final var parsedObject = new ParsedObjectImpl("javafx.geometry.Pos", Map.of(), new LinkedHashMap<>(), List.of(define, text));
final var expected = "definestartVarvariable = javafx.geometry.Pos.TOP_LEFT;\n";
objectFormatter.format(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(objectFormatter).format(define, "define");
verify(compatibilityHelper).getStartVar(parsedObject);
}
@Test
void testFormatNoConstructorNoProperties() {
final var className = "javafx.scene.Cursor";
final var parsedObject = new ParsedObjectImpl(className, Map.of(), new LinkedHashMap<>(), List.of());
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatNoConstructorTooManyProperties() {
final var className = "javafx.scene.Cursor";
final var attributes = new HashMap<String, ParsedProperty>();
attributes.put("fx:constant", new ParsedPropertyImpl("fx:constant", null, "value"));
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
attributes.put("value", new ParsedPropertyImpl("value", null, "value"));
final var parsedObject = new ParsedObjectImpl(className, attributes, new LinkedHashMap<>(), List.of());
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatNoConstructorNoFXConstant() {
final var className = "javafx.scene.Cursor";
final var attributes = new HashMap<String, ParsedProperty>();
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
final var parsedObject = new ParsedObjectImpl(className, attributes, new LinkedHashMap<>(), List.of());
assertThrows(GenerationException.class, () -> objectFormatter.format(parsedObject, variableName));
}
@Test
void testFormatNoConstructor() throws GenerationException {
final var className = "javafx.scene.Cursor";
final var attributes = new HashMap<String, ParsedProperty>();
attributes.put("fx:constant", new ParsedPropertyImpl("fx:constant", null, "TEXT"));
final var parsedObject = new ParsedObjectImpl(className, attributes, new LinkedHashMap<>(), List.of());
final var expected = "startVarvariable = javafx.scene.Cursor.TEXT;\n";
objectFormatter.format(parsedObject, variableName);
assertEquals(expected, sb.toString());
verify(compatibilityHelper).getStartVar(parsedObject);
}
@Test
void testFormatConstructorNamedArgs(@Mock final PropertyFormatter propertyFormatter) throws GenerationException {
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
doAnswer(i -> sb.append("property")).when(propertyFormatter).formatProperty(any(ParsedProperty.class), any(), any());
final var className = "javafx.scene.control.Spinner";
final var attributes = Map.<String, ParsedProperty>of("min", new ParsedPropertyImpl("min", null, "1"),
"max", new ParsedPropertyImpl("max", null, "2"), "editable", new ParsedPropertyImpl("editable", null, "false"),
"initialValue", new ParsedPropertyImpl("initialValue", null, "3"));
final var label = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(new ParsedPropertyImpl("childrenUnmodifiable", null, ""), List.of(label));
final var define = mock(ParsedDefine.class);
doAnswer(i -> sb.append("define")).when(objectFormatter).format(define, "define");
final var parsedObject = new ParsedObjectImpl(className, attributes, properties, List.of(define));
when(reflectionHelper.getGenericTypes(parsedObject)).thenReturn("<bla>");
objectFormatter.format(parsedObject, variableName);
final var expected = "definestartVarvariable = new javafx.scene.control.Spinner<bla>(1, 2, 3);\nproperty";
assertEquals(expected, sb.toString());
verify(propertyFormatter).formatProperty(new ParsedPropertyImpl("editable", null, "false"), parsedObject, variableName);
verify(propertyFormatter).formatProperty(new ParsedPropertyImpl("childrenUnmodifiable", null, ""), List.of(label), parsedObject, variableName);
verify(objectFormatter).format(define, "define");
verify(compatibilityHelper).getStartVar(parsedObject);
verify(reflectionHelper).getGenericTypes(parsedObject);
}
@Test
void testFormatConstructorDefault(@Mock final PropertyFormatter propertyFormatter) throws GenerationException {
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
doAnswer(i -> sb.append("property")).when(propertyFormatter).formatProperty(any(ParsedProperty.class), anyList(), any(), any());
final var className = "javafx.scene.control.Spinner";
final var attributes = Map.<String, ParsedProperty>of();
final var label = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var property = new ParsedPropertyImpl("childrenUnmodifiable", null, "");
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(property, List.of(label, label));
final var parsedObject = new ParsedObjectImpl(className, attributes, properties, List.of());
when(reflectionHelper.getGenericTypes(parsedObject)).thenReturn("");
objectFormatter.format(parsedObject, variableName);
final var expected = "startVarvariable = new javafx.scene.control.Spinner();\nproperty";
assertEquals(expected, sb.toString());
verify(propertyFormatter).formatProperty(property, List.of(label, label), parsedObject, variableName);
verify(compatibilityHelper).getStartVar(parsedObject);
verify(reflectionHelper).getGenericTypes(parsedObject);
}
@Test
void testFormatConstructorDefaultProperty(@Mock final PropertyFormatter propertyFormatter) throws GenerationException {
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
doAnswer(i -> sb.append("property")).when(propertyFormatter).formatProperty(any(ParsedProperty.class), anyList(), any(), any());
final var className = "javafx.scene.layout.StackPane";
final var attributes = Map.<String, ParsedProperty>of();
final var label = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var children = List.<ParsedObject>of(label, label);
final var parsedObject = new ParsedObjectImpl(className, attributes, new LinkedHashMap<>(), children);
when(reflectionHelper.getGenericTypes(parsedObject)).thenReturn("");
objectFormatter.format(parsedObject, variableName);
final var expected = "startVarvariable = new javafx.scene.layout.StackPane();\nproperty";
assertEquals(expected, sb.toString());
verify(propertyFormatter).formatProperty(new ParsedPropertyImpl("children", null, ""), children, parsedObject, variableName);
verify(compatibilityHelper).getStartVar(parsedObject);
verify(reflectionHelper).getGenericTypes(parsedObject);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ObjectFormatter(null, request, sb));
assertThrows(NullPointerException.class, () -> new ObjectFormatter(helperProvider, null, sb));
assertThrows(NullPointerException.class, () -> new ObjectFormatter(helperProvider, request, null));
}
}

View File

@@ -1,8 +1,342 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
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.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedObjectImpl;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedTextImpl;
import javafx.event.EventHandler;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SequencedCollection;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestPropertyFormatter { class TestPropertyFormatter {
private final HelperProvider helperProvider;
private final VariableProvider variableProvider;
private final GenerationCompatibilityHelper compatibilityHelper;
private final ControllerInjector controllerInjector;
private final FieldSetter fieldSetter;
private final ValueFormatter valueFormatter;
private final GenerationProgress progress;
private final GenerationRequest request;
private final GenerationParameters parameters;
private final ParsedObject rootObject;
private final ParsedProperty property;
private final String variableName;
private final StringBuilder sb;
private final List<String> controllerFactoryPostAction;
private final PropertyFormatter propertyFormatter;
TestPropertyFormatter(@Mock final HelperProvider helperProvider, @Mock final VariableProvider variableProvider,
@Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final ControllerInjector controllerInjector,
@Mock final FieldSetter fieldSetter, @Mock final ValueFormatter valueFormatter, @Mock final GenerationProgress progress,
@Mock final GenerationRequest request, @Mock final GenerationParameters parameters,
@Mock final ParsedObject rootObject, @Mock final ParsedProperty property) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.controllerInjector = Objects.requireNonNull(controllerInjector);
this.fieldSetter = Objects.requireNonNull(fieldSetter);
this.valueFormatter = Objects.requireNonNull(valueFormatter);
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.parameters = Objects.requireNonNull(parameters);
this.rootObject = Objects.requireNonNull(rootObject);
this.property = Objects.requireNonNull(property);
this.variableName = "variable";
this.sb = new StringBuilder();
this.controllerFactoryPostAction = new ArrayList<>();
this.propertyFormatter = spy(new PropertyFormatter(helperProvider, progress));
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getControllerInjector()).thenReturn(controllerInjector);
when(helperProvider.getFieldSetter()).thenReturn(fieldSetter);
when(helperProvider.getValueFormatter()).thenReturn(valueFormatter);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(variableProvider.getNextVariableName(anyString())).then(i -> i.getArgument(0));
when(progress.request()).thenReturn(request);
when(request.parameters()).thenReturn(parameters);
when(request.rootObject()).thenReturn(rootObject);
when(progress.stringBuilder()).thenReturn(sb);
when(progress.controllerFactoryPostAction()).thenReturn(controllerFactoryPostAction);
when(compatibilityHelper.getListOf()).thenReturn("listof(");
when(valueFormatter.getArg(anyString(), any())).then(i -> i.getArgument(0) + "-" + i.getArgument(1));
doAnswer(i -> sb.append(i.getArgument(0) + "-" + i.getArgument(1))).when(controllerInjector).injectEventHandlerControllerMethod(any(), anyString());
doAnswer(i -> sb.append(i.getArgument(0) + "-" + i.getArgument(1))).when(fieldSetter).setEventHandler(any(), anyString());
}
@Test
void testFormatPropertyId() throws GenerationException {
when(property.name()).thenReturn("fx:id");
propertyFormatter.formatProperty(property, rootObject, variableName);
assertEquals("", sb.toString());
}
@Test
void testFormatControllerSame() {
when(property.name()).thenReturn("fx:controller");
assertDoesNotThrow(() -> propertyFormatter.formatProperty(property, rootObject, variableName));
}
@Test
void testFormatControllerDifferent() {
when(property.name()).thenReturn("fx:controller");
assertThrows(GenerationException.class, () -> propertyFormatter.formatProperty(property, mock(ParsedObject.class), variableName));
}
@Test
void testFormatEventHandlerMethod() throws GenerationException {
when(property.name()).thenReturn("onAction");
when(property.sourceType()).thenReturn(EventHandler.class.getName());
when(property.value()).thenReturn("#method");
propertyFormatter.formatProperty(property, rootObject, variableName);
final var expected = property + "-" + variableName;
assertEquals(expected, sb.toString());
verify(controllerInjector).injectEventHandlerControllerMethod(property, variableName);
}
@Test
void testFormatEventHandlerField() throws GenerationException {
when(property.name()).thenReturn("onAction");
when(property.sourceType()).thenReturn(EventHandler.class.getName());
when(property.value()).thenReturn("field");
propertyFormatter.formatProperty(property, rootObject, variableName);
final var expected = property + "-" + variableName;
assertEquals(expected, sb.toString());
verify(fieldSetter).setEventHandler(property, variableName);
}
@Test
void testFormatStaticProperty() throws GenerationException {
when(property.sourceType()).thenReturn(HBox.class.getName());
when(property.name()).thenReturn("hgrow");
when(property.value()).thenReturn("value");
propertyFormatter.formatProperty(property, rootObject, variableName);
final var arg = "value-" + Priority.class;
final var expected = " javafx.scene.layout.HBox.setHgrow(" + variableName + ", " + arg + ");\n";
assertEquals(expected, sb.toString());
verify(valueFormatter).getArg("value", Priority.class);
}
@Test
void testFormatStaticNotFound() {
when(property.sourceType()).thenReturn(HBox.class.getName());
when(property.name()).thenReturn("vvvvvvv");
when(property.value()).thenReturn("value");
assertThrows(GenerationException.class, () -> propertyFormatter.formatProperty(property, rootObject, variableName));
}
@Test
void testFormatSetProperty() throws GenerationException {
when(rootObject.className()).thenReturn("javafx.scene.control.Label");
when(property.name()).thenReturn("text");
when(property.value()).thenReturn("value");
propertyFormatter.formatProperty(property, rootObject, variableName);
final var arg = "value-" + String.class;
final var expected = " " + variableName + ".setText(" + arg + ");\n";
assertEquals(expected, sb.toString());
verify(valueFormatter).getArg("value", String.class);
}
@Test
void testFormatSetPropertyLater() throws GenerationException {
when(parameters.resourceInjectionType()).thenReturn(ResourceBundleInjectionType.GETTER);
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionType.FACTORY);
when(rootObject.className()).thenReturn("javafx.scene.control.Label");
when(property.name()).thenReturn("text");
when(property.value()).thenReturn("%value");
propertyFormatter.formatProperty(property, rootObject, variableName);
final var arg = "%value-" + String.class;
final var expected = " " + variableName + ".setText(" + arg + ");\n";
assertEquals("", sb.toString());
assertEquals(1, controllerFactoryPostAction.size());
assertEquals(expected, controllerFactoryPostAction.getFirst());
verify(valueFormatter).getArg("%value", String.class);
}
@Test
void testFormatGetProperty() throws GenerationException {
when(rootObject.className()).thenReturn("javafx.scene.layout.HBox");
when(property.name()).thenReturn("children");
when(property.value()).thenReturn("value");
propertyFormatter.formatProperty(property, rootObject, variableName);
final var arg = "value-" + String.class;
final var expected = " " + variableName + ".getChildren().addAll(listof(" + arg + "));\n";
assertEquals(expected, sb.toString());
verify(valueFormatter).getArg("value", String.class);
}
@Test
void testFormatNoProperty() {
when(rootObject.className()).thenReturn("javafx.scene.layout.HBox");
when(property.name()).thenReturn("whatever");
when(property.value()).thenReturn("value");
assertThrows(GenerationException.class, () -> propertyFormatter.formatProperty(property, rootObject, variableName));
}
@Test
void testFormatListParsedDefine() {
final var values = List.of(mock(ParsedDefine.class));
assertThrows(GenerationException.class, () -> propertyFormatter.formatProperty(property, values, rootObject, variableName));
}
@Test
void testFormatListSingleValue() throws GenerationException {
final var text = new ParsedTextImpl("text");
final var values = List.of(text);
final var emptyProperty = new ParsedPropertyImpl("name", null, "");
final var newProperty = new ParsedPropertyImpl("name", null, text.text());
doNothing().when(propertyFormatter).formatProperty(newProperty, rootObject, variableName);
propertyFormatter.formatProperty(emptyProperty, values, rootObject, variableName);
verify(propertyFormatter).formatProperty(newProperty, rootObject, variableName);
}
@Test
void testFormatListSingleValueStatic() throws GenerationException {
final var text = new ParsedTextImpl("text");
final var values = List.of(text);
final var emptyProperty = new ParsedPropertyImpl("name", String.class.getName(), "");
final var newProperty = new ParsedPropertyImpl("name", String.class.getName(), text.text());
doNothing().when(propertyFormatter).formatProperty(newProperty, rootObject, variableName);
propertyFormatter.formatProperty(emptyProperty, values, rootObject, variableName);
verify(propertyFormatter).formatProperty(newProperty, rootObject, variableName);
}
@Test
void testFormatSingleChildSet(@Mock final ObjectFormatter objectFormatter) throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
final var className = "javafx.scene.layout.BorderPane";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(property.name()).thenReturn("center");
final var child = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var objects = List.of(child);
doAnswer(i -> sb.append("object")).when(objectFormatter).format(child, "label");
propertyFormatter.formatProperty(property, objects, parsedObject, variableName);
final var expected = "object variable.setCenter(label);\n";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(child, "label");
}
@Test
void testFormatSingleChildGet(@Mock final ObjectFormatter objectFormatter) throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
final var className = "javafx.scene.layout.HBox";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(property.name()).thenReturn("children");
final var child = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var objects = List.of(child);
doAnswer(i -> sb.append("object")).when(objectFormatter).format(child, "label");
propertyFormatter.formatProperty(property, objects, parsedObject, variableName);
final var expected = "object variable.getChildren().addAll(listof(label));\n";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(child, "label");
}
@Test
void testFormatSingleChildNoSetter(@Mock final ObjectFormatter objectFormatter) throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
final var className = "javafx.scene.layout.BorderPane";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(property.name()).thenReturn("blabla");
final var child = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var objects = List.of(child);
assertThrows(GenerationException.class, () -> propertyFormatter.formatProperty(property, objects, parsedObject, variableName));
verify(objectFormatter).format(child, "label");
}
@Test
void testFormatChildrenGet(@Mock final ObjectFormatter objectFormatter) throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
final var className = "javafx.scene.layout.HBox";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(property.name()).thenReturn("children");
final var child = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var objects = List.of(child, child);
doAnswer(i -> sb.append("object")).when(objectFormatter).format(child, "label");
propertyFormatter.formatProperty(property, objects, parsedObject, variableName);
final var expected = "objectobject variable.getChildren().addAll(listof(label, label));\n";
assertEquals(expected, sb.toString());
verify(objectFormatter, times(2)).format(child, "label");
}
@Test
void testFormatChildrenNoGetter(@Mock final ObjectFormatter objectFormatter) throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
final var className = "javafx.scene.layout.HBox";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(property.name()).thenReturn("blabla");
final var child = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var objects = List.of(child, child);
assertThrows(GenerationException.class, () -> propertyFormatter.formatProperty(property, objects, parsedObject, variableName));
verify(objectFormatter, times(2)).format(child, "label");
}
@Test
void testFormatSingleStaticChild(@Mock final ObjectFormatter objectFormatter) throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
final var className = "javafx.scene.layout.BorderPane";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(property.name()).thenReturn("fillHeight");
when(property.sourceType()).thenReturn("javafx.scene.layout.GridPane");
final var child = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var objects = List.of(child);
doAnswer(i -> sb.append("object")).when(objectFormatter).format(child, "label");
propertyFormatter.formatProperty(property, objects, parsedObject, variableName);
final var expected = "object javafx.scene.layout.GridPane.setFillHeight(variable, label);\n";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(child, "label");
}
@Test
void testFormatSingleStaticChildNoSetter(@Mock final ObjectFormatter objectFormatter) throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
final var className = "javafx.scene.layout.BorderPane";
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
final var parsedObject = new ParsedObjectImpl(className, Map.of(), properties, List.of());
when(property.name()).thenReturn("blabla");
when(property.sourceType()).thenReturn("javafx.scene.layout.GridPane");
final var child = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of());
final var objects = List.of(child);
assertThrows(GenerationException.class, () -> propertyFormatter.formatProperty(property, objects, parsedObject, variableName));
verify(objectFormatter).format(child, "label");
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new PropertyFormatter(null, progress));
assertThrows(NullPointerException.class, () -> new PropertyFormatter(helperProvider, null));
}
} }

View File

@@ -114,7 +114,7 @@ class TestReflectionHelper {
parameters.put("p16", new Parameter("p16", Double.class, "0")); parameters.put("p16", new Parameter("p16", Double.class, "0"));
parameters.put("p17", new Parameter("p17", String.class, "null")); parameters.put("p17", new Parameter("p17", String.class, "null"));
parameters.put("p18", new Parameter("p18", Object.class, "null")); parameters.put("p18", new Parameter("p18", Object.class, "null"));
final var defaultConstructor = Arrays.stream(WholeConstructorArgs.class.getConstructors()).filter(c -> c.getParameterCount() == 18).findFirst().orElseThrow(); final var defaultConstructor = Arrays.stream(WholeConstructorArgs.class.getDeclaredConstructors()).filter(c -> c.getParameterCount() == 18).findFirst().orElseThrow();
final var expected = new ConstructorArgs(defaultConstructor, parameters); final var expected = new ConstructorArgs(defaultConstructor, parameters);
final var actual = ReflectionHelper.getConstructorArgs(defaultConstructor); final var actual = ReflectionHelper.getConstructorArgs(defaultConstructor);
assertEquals(expected, actual); assertEquals(expected, actual);
@@ -132,7 +132,7 @@ class TestReflectionHelper {
parameters.put("p13", new Parameter("p13", float.class, "5.5")); parameters.put("p13", new Parameter("p13", float.class, "5.5"));
parameters.put("p15", new Parameter("p15", double.class, "6.6")); parameters.put("p15", new Parameter("p15", double.class, "6.6"));
parameters.put("p17", new Parameter("p17", String.class, "str")); parameters.put("p17", new Parameter("p17", String.class, "str"));
final var constructor = Arrays.stream(WholeConstructorArgs.class.getConstructors()).filter(c -> c.getParameterCount() == 9).findFirst().orElseThrow(); final var constructor = Arrays.stream(WholeConstructorArgs.class.getDeclaredConstructors()).filter(c -> c.getParameterCount() == 9).findFirst().orElseThrow();
final var expected = new ConstructorArgs(constructor, parameters); final var expected = new ConstructorArgs(constructor, parameters);
final var actual = ReflectionHelper.getConstructorArgs(constructor); final var actual = ReflectionHelper.getConstructorArgs(constructor);
assertEquals(expected, actual); assertEquals(expected, actual);
@@ -140,7 +140,7 @@ class TestReflectionHelper {
@Test @Test
void testGetConstructorArgsNoNamedArgs() { void testGetConstructorArgsNoNamedArgs() {
final var constructor = Arrays.stream(WholeConstructorArgs.class.getConstructors()).filter(c -> c.getParameterCount() == 2).findFirst().orElseThrow(); final var constructor = Arrays.stream(WholeConstructorArgs.class.getDeclaredConstructors()).filter(c -> c.getParameterCount() == 2).findFirst().orElseThrow();
final var expected = new ConstructorArgs(constructor, new LinkedHashMap<>()); final var expected = new ConstructorArgs(constructor, new LinkedHashMap<>());
final var actual = ReflectionHelper.getConstructorArgs(constructor); final var actual = ReflectionHelper.getConstructorArgs(constructor);
assertEquals(expected, actual); assertEquals(expected, actual);
@@ -148,7 +148,7 @@ class TestReflectionHelper {
@Test @Test
void testGetConstructorArgsMixed() { void testGetConstructorArgsMixed() {
final var constructor = Arrays.stream(WholeConstructorArgs.class.getConstructors()).filter(c -> c.getParameterCount() == 3).findFirst().orElseThrow(); final var constructor = Arrays.stream(WholeConstructorArgs.class.getDeclaredConstructors()).filter(c -> c.getParameterCount() == 3).findFirst().orElseThrow();
assertThrows(IllegalStateException.class, () -> ReflectionHelper.getConstructorArgs(constructor)); assertThrows(IllegalStateException.class, () -> ReflectionHelper.getConstructorArgs(constructor));
} }
@@ -212,4 +212,25 @@ class TestReflectionHelper {
when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("java.lang.Integer", List.of()))); 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(parsedObject)); assertEquals("<java.lang.String, java.lang.Integer>", reflectionHelper.getGenericTypes(parsedObject));
} }
@Test
void testGetGenericTypesRecursive() throws GenerationException {
when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of(new GenericTypesImpl("java.lang.Integer", List.of()), new GenericTypesImpl("java.lang.Short", List.of()))), new GenericTypesImpl("java.lang.Byte", List.of())));
assertEquals("<java.lang.String<java.lang.Integer, java.lang.Short>, java.lang.Byte>", reflectionHelper.getGenericTypes(parsedObject));
}
@Test
void testGetReturnTypeNotFound() {
assertThrows(GenerationException.class, () -> ReflectionHelper.getReturnType("java.lang.String", "whatever"));
}
@Test
void testGetReturnType() throws GenerationException {
assertEquals(String.class.getName(), ReflectionHelper.getReturnType("java.lang.String", "valueOf"));
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ReflectionHelper(null));
}
} }

View File

@@ -1,8 +1,172 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
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.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; 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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestSceneFormatter { class TestSceneFormatter {
private final HelperProvider helperProvider;
private final ObjectFormatter objectFormatter;
private final GenerationCompatibilityHelper compatibilityHelper;
private final URLFormatter urlFormatter;
private final VariableProvider variableProvider;
private final StringBuilder sb;
private final ParsedObject parsedObject;
private final SequencedCollection<ParsedObject> children;
private final SequencedMap<ParsedProperty, SequencedCollection<ParsedObject>> properties;
private final Map<String, ParsedProperty> attributes;
private final String variableName;
private final SceneFormatter sceneFormatter;
TestSceneFormatter(@Mock final HelperProvider helperProvider, @Mock final ObjectFormatter objectFormatter,
@Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final VariableProvider variableProvider,
@Mock final URLFormatter urlFormatter, @Mock final ParsedObject parsedObject) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.objectFormatter = Objects.requireNonNull(objectFormatter);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.urlFormatter = Objects.requireNonNull(urlFormatter);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.parsedObject = Objects.requireNonNull(parsedObject);
this.children = new ArrayList<>();
this.properties = new LinkedHashMap<>();
this.attributes = new HashMap<>();
this.variableName = "variable";
this.sb = new StringBuilder();
this.sceneFormatter = new SceneFormatter(helperProvider, sb);
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getObjectFormatter()).thenReturn(objectFormatter);
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getURLFormatter()).thenReturn(urlFormatter);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(parsedObject.children()).thenReturn(children);
when(parsedObject.properties()).thenReturn(properties);
when(parsedObject.attributes()).thenReturn(attributes);
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(compatibilityHelper.getListOf()).thenReturn("listof(");
when(variableProvider.getNextVariableName(anyString())).then(i -> i.getArgument(0));
doAnswer(i -> sb.append((String) i.getArgument(1))).when(objectFormatter).format(any(), any());
doAnswer(i -> {
sb.append(((List<String>) i.getArgument(0)).getFirst());
return List.of("1", "2");
}).when(urlFormatter).formatURL(anyIterable());
}
@Test
void testUnknownAttribute() {
properties.put(new ParsedPropertyImpl("root", null, ""), List.of(parsedObject));
attributes.put("unknown", new ParsedPropertyImpl("unknown", null, "value"));
assertThrows(GenerationException.class, () -> sceneFormatter.formatScene(parsedObject, variableName));
}
@Test
void testNonRootProperty() {
properties.put(new ParsedPropertyImpl("property", null, ""), List.of(parsedObject));
assertThrows(GenerationException.class, () -> sceneFormatter.formatScene(parsedObject, variableName));
}
@Test
void testRootPropertyNonEmptyChildren() {
properties.put(new ParsedPropertyImpl("root", null, ""), List.of(parsedObject));
children.add(parsedObject);
assertThrows(GenerationException.class, () -> sceneFormatter.formatScene(parsedObject, variableName));
}
@Test
void testRootPropertyEmptyChild() {
properties.put(new ParsedPropertyImpl("root", null, ""), List.of());
assertThrows(GenerationException.class, () -> sceneFormatter.formatScene(parsedObject, variableName));
}
@Test
void testRootPropertyNonOneChild() {
properties.put(new ParsedPropertyImpl("root", null, ""), List.of(parsedObject, parsedObject));
assertThrows(GenerationException.class, () -> sceneFormatter.formatScene(parsedObject, variableName));
}
@Test
void testNonRootPropertyEmptyChild() {
assertThrows(GenerationException.class, () -> sceneFormatter.formatScene(parsedObject, variableName));
}
@Test
void testNonRootPropertyMultipleChildren() {
children.add(parsedObject);
children.add(parsedObject);
assertThrows(GenerationException.class, () -> sceneFormatter.formatScene(parsedObject, variableName));
}
@Test
void testDefaultAttributesProperty() throws GenerationException {
final var rootObject = mock(ParsedObject.class);
final var define = mock(ParsedDefine.class);
properties.put(new ParsedPropertyImpl("root", null, ""), List.of(define, rootObject));
sceneFormatter.formatScene(parsedObject, variableName);
final var expected = "definerootjavafx.scene.Scenevariable = new javafx.scene.Scene(root, -1.0, -1.0, javafx.scene.paint.Color.valueOf(\"0xffffffff\"));\n";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(define, "define");
verify(objectFormatter).format(rootObject, "root");
}
@Test
void testDefaultAttributesChild() throws GenerationException {
final var rootObject = mock(ParsedObject.class);
final var define = mock(ParsedDefine.class);
children.add(define);
children.add(rootObject);
sceneFormatter.formatScene(parsedObject, variableName);
final var expected = "definerootjavafx.scene.Scenevariable = new javafx.scene.Scene(root, -1.0, -1.0, javafx.scene.paint.Color.valueOf(\"0xffffffff\"));\n";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(define, "define");
verify(objectFormatter).format(rootObject, "root");
}
@Test
void testAllAttributesProperty() throws GenerationException {
final var rootObject = mock(ParsedObject.class);
properties.put(new ParsedPropertyImpl("root", null, ""), List.of(rootObject));
attributes.put("width", new ParsedPropertyImpl("width", null, "100"));
attributes.put("height", new ParsedPropertyImpl("height", null, "200"));
attributes.put("fill", new ParsedPropertyImpl("fill", null, "#FF0000"));
attributes.put("stylesheets", new ParsedPropertyImpl("stylesheets", null, "style.css"));
sceneFormatter.formatScene(parsedObject, variableName);
final var expected = "rootjavafx.scene.Scenevariable = new javafx.scene.Scene(root, 100.0, 200.0, javafx.scene.paint.Color.valueOf(\"#FF0000\"));\n" +
"style.cssjava.util.List<String>stylesheets = variable.getStyleSheets();\n" +
" stylesheets.addAll(listof(1, 2));\n";
assertEquals(expected, sb.toString());
verify(objectFormatter).format(rootObject, "root");
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new SceneFormatter(null, sb));
assertThrows(NullPointerException.class, () -> new SceneFormatter(helperProvider, null));
}
} }

View File

@@ -1,8 +1,143 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
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.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestTriangleMeshFormatter { class TestTriangleMeshFormatter {
private final HelperProvider helperProvider;
private final GenerationCompatibilityHelper compatibilityHelper;
private final StringBuilder sb;
private final ParsedObject parsedObject;
private final String variableName;
private final Map<String, ParsedProperty> attributes;
private final TriangleMeshFormatter triangleMeshFormatter;
TestTriangleMeshFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final ParsedObject parsedObject) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.sb = new StringBuilder();
this.parsedObject = Objects.requireNonNull(parsedObject);
this.attributes = new HashMap<>();
this.variableName = "variable";
this.triangleMeshFormatter = new TriangleMeshFormatter(helperProvider, sb);
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(parsedObject.attributes()).thenReturn(attributes);
when(parsedObject.children()).thenReturn(List.of());
when(parsedObject.properties()).thenReturn(new LinkedHashMap<>());
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
}
@Test
void testFormatChildren() {
when(parsedObject.children()).thenReturn(List.of(parsedObject));
assertThrows(GenerationException.class, () -> triangleMeshFormatter.formatTriangleMesh(parsedObject, variableName));
}
@Test
void testFormatProperties() {
final var map = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
map.put(new ParsedPropertyImpl("str", null, ""), List.of());
when(parsedObject.properties()).thenReturn(map);
assertThrows(GenerationException.class, () -> triangleMeshFormatter.formatTriangleMesh(parsedObject, variableName));
}
@Test
void testFormatUnknownAttribute() {
attributes.put("unknown", new ParsedPropertyImpl("unknown", null, "value"));
assertThrows(GenerationException.class, () -> triangleMeshFormatter.formatTriangleMesh(parsedObject, variableName));
}
@Test
void testNoAttributes() throws GenerationException {
triangleMeshFormatter.formatTriangleMesh(parsedObject, variableName);
final var expected = "javafx.scene.shape.TriangleMeshvariable = new javafx.scene.shape.TriangleMesh();\n";
assertEquals(expected, sb.toString());
}
@Test
void testUnknownVertexFormat() {
final var vertexFormat = "unknown";
attributes.put("vertexFormat", new ParsedPropertyImpl("vertexFormat", null, vertexFormat));
assertThrows(GenerationException.class, () -> triangleMeshFormatter.formatTriangleMesh(parsedObject, variableName));
}
@Test
void testVertexFormatPointNormalTexCoord() throws GenerationException {
final var vertexFormat = "point_normal_texcoord";
attributes.put("vertexFormat", new ParsedPropertyImpl("vertexFormat", null, vertexFormat));
triangleMeshFormatter.formatTriangleMesh(parsedObject, variableName);
final var expected = """
javafx.scene.shape.TriangleMeshvariable = new javafx.scene.shape.TriangleMesh();
variable.setVertexFormat(javafx.scene.shape.VertexFormat.POINT_NORMAL_TEXCOORD);
""";
assertEquals(expected, sb.toString());
}
@Test
void testAllAttributes() throws GenerationException {
final var points = "3.0, 4.1, 5, 6f";
final var texCoords = " 7 8 9.3 10.0f ";
final var normals = " 7 , 8f 9.3f";
final var faces = "1 2, 3 ,4";
final var faceSmoothingGroups = " 1 22 3 ";
final var vertexFormat = "point_texcoord";
final var pointsProperty = new ParsedPropertyImpl("points", null, points);
final var texCoordsProperty = new ParsedPropertyImpl("texCoords", null, texCoords);
final var normalsProperty = new ParsedPropertyImpl("normals", null, normals);
final var facesProperty = new ParsedPropertyImpl("faces", null, faces);
final var faceSmoothingGroupsProperty = new ParsedPropertyImpl("faceSmoothingGroups", null, faceSmoothingGroups);
final var vertexFormatProperty = new ParsedPropertyImpl("vertexFormat", null, vertexFormat);
attributes.put("points", pointsProperty);
attributes.put("texCoords", texCoordsProperty);
attributes.put("normals", normalsProperty);
attributes.put("faces", facesProperty);
attributes.put("faceSmoothingGroups", faceSmoothingGroupsProperty);
attributes.put("vertexFormat", vertexFormatProperty);
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
triangleMeshFormatter.formatTriangleMesh(parsedObject, variableName);
final var expected = """
javafx.scene.shape.TriangleMeshvariable = new javafx.scene.shape.TriangleMesh();
variable.getPoints().setAll(new float[]{3.0, 4.1, 5.0, 6.0});
variable.getTexCoords().setAll(new float[]{7.0, 8.0, 9.3, 10.0});
variable.getNormals().setAll(new float[]{7.0, 8.0, 9.3});
variable.getFaces().setAll(new int[]{1, 2, 3, 4});
variable.getFaceSmoothingGroups().setAll(new int[]{1, 22, 3});
variable.setVertexFormat(javafx.scene.shape.VertexFormat.POINT_TEXCOORD);
""";
assertEquals(expected, sb.toString());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new TriangleMeshFormatter(null, sb));
assertThrows(NullPointerException.class, () -> new TriangleMeshFormatter(helperProvider, null));
}
} }

View File

@@ -18,55 +18,53 @@ import java.util.SequencedCollection;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestURLFormatter { class TestURLFormatter {
private final HelperProvider helperProvider; private final HelperProvider helperProvider;
private final GenerationHelper generationHelper;
private final GenerationCompatibilityHelper compatibilityHelper; private final GenerationCompatibilityHelper compatibilityHelper;
private final VariableProvider variableProvider;
private final ParsedObject parsedObject; private final ParsedObject parsedObject;
private final String variableName; private final String variableName;
private final StringBuilder sb; private final StringBuilder sb;
private final GenerationProgress progress;
private final URLFormatter urlFormatter; private final URLFormatter urlFormatter;
TestURLFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationHelper generationHelper, TestURLFormatter(@Mock final HelperProvider helperProvider, @Mock final VariableProvider variableProvider,
@Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final ParsedObject parsedObject, @Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final ParsedObject parsedObject) {
@Mock final GenerationProgress progress) {
this.helperProvider = Objects.requireNonNull(helperProvider); this.helperProvider = Objects.requireNonNull(helperProvider);
this.generationHelper = Objects.requireNonNull(generationHelper); this.variableProvider = Objects.requireNonNull(variableProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper); this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.parsedObject = Objects.requireNonNull(parsedObject); this.parsedObject = Objects.requireNonNull(parsedObject);
this.progress = Objects.requireNonNull(progress);
this.sb = new StringBuilder(); this.sb = new StringBuilder();
this.variableName = "variable"; this.variableName = "variable";
this.urlFormatter = new URLFormatter(helperProvider, progress); this.urlFormatter = new URLFormatter(helperProvider, sb);
} }
@BeforeEach @BeforeEach
void beforeEach() throws GenerationException { void beforeEach() throws GenerationException {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper); when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getGenerationHelper()).thenReturn(generationHelper); when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(progress.stringBuilder()).thenReturn(sb); when(variableProvider.getNextVariableName("url")).thenReturn("url1", "url2");
when(progress.getNextVariableName("url")).thenReturn("url1", "url2");
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0)); when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(parsedObject.children()).thenReturn(List.of()); when(parsedObject.children()).thenReturn(List.of());
when(parsedObject.properties()).thenReturn(new LinkedHashMap<>()); when(parsedObject.properties()).thenReturn(new LinkedHashMap<>());
doAnswer(i -> sb.append("handleId")).when(generationHelper).handleId(any(), anyString());
} }
@Test @Test
void testFormatURLSheets() { void testFormatURLSheets() {
final var styleSheets = List.of("style1.css", "@style2.css"); final var styleSheets = List.of("style1.css", "@style2.css");
final var expected = " final java.net.URL url1;\n" + final var expected = """
" try {\n" + final java.net.URL url1;
" url1 = new java.net.URI(\"style1.css\").toURL();\n" + try {
" } catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {\n" + url1 = new java.net.URI("style1.css").toURL();
" throw new RuntimeException(\"Couldn't parse url : style1.css\", e);\n" + } catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {
" }\njava.net.URLurl2 = getClass().getResource(\"style2.css\");\n"; throw new RuntimeException("Couldn't parse url : style1.css", e);
}
java.net.URLurl2 = getClass().getResource("style2.css");
""";
assertEquals(List.of("url1", "url2"), urlFormatter.formatURL(styleSheets)); assertEquals(List.of("url1", "url2"), urlFormatter.formatURL(styleSheets));
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
} }
@@ -80,7 +78,7 @@ class TestURLFormatter {
@Test @Test
void testFormatURLObjectProperties() { void testFormatURLObjectProperties() {
final var map = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>(); final var map = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
map.put(mock(ParsedProperty.class), List.of()); map.put(new ParsedPropertyImpl("str", null, ""), List.of());
when(parsedObject.properties()).thenReturn(map); when(parsedObject.properties()).thenReturn(map);
assertThrows(GenerationException.class, () -> urlFormatter.formatURL(parsedObject, variableName)); assertThrows(GenerationException.class, () -> urlFormatter.formatURL(parsedObject, variableName));
} }
@@ -101,8 +99,13 @@ class TestURLFormatter {
when(parsedObject.attributes()).thenReturn(attributes); when(parsedObject.attributes()).thenReturn(attributes);
urlFormatter.formatURL(parsedObject, variableName); urlFormatter.formatURL(parsedObject, variableName);
final var expected = "java.net.URL" + variableName + " = getClass().getResource(\"key\");\nhandleId"; final var expected = "java.net.URL" + variableName + " = getClass().getResource(\"key\");\n";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
verify(generationHelper).handleId(parsedObject, variableName); }
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new URLFormatter(null, sb));
assertThrows(NullPointerException.class, () -> new URLFormatter(helperProvider, null));
} }
} }

View File

@@ -1,20 +1,18 @@
package com.github.gtache.fxml.compiler.impl.internal; package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException; import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.InjectionType; import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.net.URL; import java.net.URL;
import java.util.HashMap;
import java.util.List; import java.util.List;
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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -23,39 +21,40 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestValueFormatter { class TestValueFormatter {
private final Map<String, VariableInfo> idToVariableInfo; private final HelperProvider helperProvider;
private final InjectionType resourceInjectionType; private final VariableProvider variableProvider;
private final ValueFormatter formatter; private final ValueFormatter formatter;
TestValueFormatter(@Mock final InjectionType resourceInjectionType) { TestValueFormatter(@Mock final HelperProvider helperProvider, @Mock final VariableProvider variableProvider,
this.resourceInjectionType = requireNonNull(resourceInjectionType); @Mock final ResourceBundleInjectionType resourceInjectionType) {
this.idToVariableInfo = new HashMap<>(); this.helperProvider = Objects.requireNonNull(helperProvider);
this.formatter = new ValueFormatter(resourceInjectionType, idToVariableInfo); this.variableProvider = Objects.requireNonNull(variableProvider);
this.formatter = new ValueFormatter(helperProvider, resourceInjectionType);
} }
@Test @BeforeEach
void testGetArgStringResourceUnknown() { void beforeEach() {
assertThrows(GenerationException.class, () -> formatter.getArg("%value", String.class)); when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
} }
@Test @Test
void testGetArgStringResourceSimpleGet() throws GenerationException { void testGetArgStringResourceSimpleGet() throws GenerationException {
final var types = List.of(ResourceBundleInjectionTypes.CONSTRUCTOR, ResourceBundleInjectionTypes.CONSTRUCTOR_NAME, ResourceBundleInjectionTypes.GET_BUNDLE); final var types = List.of(ResourceBundleInjectionType.CONSTRUCTOR, ResourceBundleInjectionType.CONSTRUCTOR_NAME, ResourceBundleInjectionType.GET_BUNDLE);
for (final var type : types) { for (final var type : types) {
final var resourceFormatter = new ValueFormatter(type, idToVariableInfo); final var resourceFormatter = new ValueFormatter(helperProvider, type);
assertEquals("resourceBundle.getString(\"value\")", resourceFormatter.getArg("%value", String.class)); assertEquals("resourceBundle.getString(\"value\")", resourceFormatter.getArg("%value", String.class));
} }
} }
@Test @Test
void testGetArgStringResourceController() throws GenerationException { void testGetArgStringResourceController() throws GenerationException {
final var resourceFormatter = new ValueFormatter(ResourceBundleInjectionTypes.GETTER, idToVariableInfo); final var resourceFormatter = new ValueFormatter(helperProvider, ResourceBundleInjectionType.GETTER);
assertEquals("controller.resources().getString(\"value\")", resourceFormatter.getArg("%value", String.class)); assertEquals("controller.resources().getString(\"value\")", resourceFormatter.getArg("%value", String.class));
} }
@Test @Test
void testGetArgStringResourceFunction() throws GenerationException { void testGetArgStringResourceFunction() throws GenerationException {
final var resourceFormatter = new ValueFormatter(ResourceBundleInjectionTypes.CONSTRUCTOR_FUNCTION, idToVariableInfo); final var resourceFormatter = new ValueFormatter(helperProvider, ResourceBundleInjectionType.CONSTRUCTOR_FUNCTION);
assertEquals("resourceBundleFunction.apply(\"value\")", resourceFormatter.getArg("%value", String.class)); assertEquals("resourceBundleFunction.apply(\"value\")", resourceFormatter.getArg("%value", String.class));
} }
@@ -78,8 +77,8 @@ class TestValueFormatter {
@Test @Test
void testGetArgExpression() throws GenerationException { void testGetArgExpression() throws GenerationException {
final var info = mock(VariableInfo.class); final var info = mock(VariableInfo.class);
when(variableProvider.getVariableInfo("value")).thenReturn(info);
when(info.variableName()).thenReturn("variable"); when(info.variableName()).thenReturn("variable");
idToVariableInfo.put("value", info);
assertEquals("variable", formatter.getArg("$value", String.class)); assertEquals("variable", formatter.getArg("$value", String.class));
} }
@@ -90,32 +89,32 @@ class TestValueFormatter {
@Test @Test
void testToStringString() { void testToStringString() {
assertEquals("\"value\"", formatter.toString("value", String.class)); assertEquals("\"value\"", ValueFormatter.toString("value", String.class));
} }
@Test @Test
void testToStringEscape() { void testToStringEscape() {
assertEquals("\"val\\\\u\\\"e\"", formatter.toString("\\val\\u\"e", String.class)); assertEquals("\"val\\\\u\\\"e\"", ValueFormatter.toString("\\val\\u\"e", String.class));
} }
@Test @Test
void testToStringChar() { void testToStringChar() {
assertEquals("'v'", formatter.toString("v", char.class)); assertEquals("'v'", ValueFormatter.toString("v", char.class));
assertEquals("'v'", formatter.toString("v", Character.class)); assertEquals("'v'", ValueFormatter.toString("v", Character.class));
} }
@Test @Test
void testToStringBoolean() { void testToStringBoolean() {
assertEquals("true", formatter.toString("true", boolean.class)); assertEquals("true", ValueFormatter.toString("true", boolean.class));
assertEquals("true", formatter.toString("true", Boolean.class)); assertEquals("true", ValueFormatter.toString("true", Boolean.class));
} }
@Test @Test
void testToStringInteger() { void testToStringInteger() {
final var types = List.of(byte.class, Byte.class, short.class, Short.class, long.class, Long.class, int.class, Integer.class); final var types = List.of(byte.class, Byte.class, short.class, Short.class, long.class, Long.class, int.class, Integer.class);
for (final var type : types) { for (final var type : types) {
assertEquals("1", formatter.toString("1", type)); assertEquals("1", ValueFormatter.toString("1", type));
assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", formatter.toString("value", type)); assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", ValueFormatter.toString("value", type));
} }
} }
@@ -123,23 +122,29 @@ class TestValueFormatter {
void testToStringDecimal() { void testToStringDecimal() {
final var types = List.of(float.class, Float.class, double.class, Double.class); final var types = List.of(float.class, Float.class, double.class, Double.class);
for (final var type : types) { for (final var type : types) {
assertEquals("1.0", formatter.toString("1.0", type)); assertEquals("1.0", ValueFormatter.toString("1.0", type));
assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", formatter.toString("value", type)); assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", ValueFormatter.toString("value", type));
} }
} }
@Test @Test
void testToStringValueOfEnum() { void testToStringValueOfEnum() {
assertEquals("javafx.geometry.Pos.value", formatter.toString("value", Pos.class)); assertEquals("javafx.geometry.Pos.value", ValueFormatter.toString("value", Pos.class));
} }
@Test @Test
void testToStringValueOfColor() { void testToStringValueOfColor() {
assertEquals("javafx.scene.paint.Color.valueOf(\"value\")", formatter.toString("value", javafx.scene.paint.Color.class)); assertEquals("javafx.scene.paint.Color.valueOf(\"value\")", ValueFormatter.toString("value", javafx.scene.paint.Color.class));
} }
@Test @Test
void testOther() { void testOther() {
assertEquals("value", formatter.toString("value", Object.class)); assertEquals("value", ValueFormatter.toString("value", Object.class));
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ValueFormatter(null, ResourceBundleInjectionType.CONSTRUCTOR));
assertThrows(NullPointerException.class, () -> new ValueFormatter(helperProvider, null));
} }
} }

View File

@@ -0,0 +1,39 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
@ExtendWith(MockitoExtension.class)
class TestVariableProvider {
private final VariableInfo variableInfo;
private final String id;
private final VariableProvider provider;
TestVariableProvider(@Mock final VariableInfo variableInfo) {
this.variableInfo = Objects.requireNonNull(variableInfo);
this.id = "id";
this.provider = new VariableProvider();
}
@Test
void testGetVariableName() {
assertEquals("var0", provider.getNextVariableName("var"));
assertEquals("var1", provider.getNextVariableName("var"));
assertEquals("other0", provider.getNextVariableName("other"));
}
@Test
void testAddVariableInfo() {
assertNull(provider.getVariableInfo(id));
provider.addVariableInfo(id, variableInfo);
assertEquals(variableInfo, provider.getVariableInfo(id));
}
}

View File

@@ -19,7 +19,6 @@ import java.util.SequencedCollection;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@@ -30,35 +29,30 @@ class TestWebViewFormatter {
private final ControllerInjector controllerInjector; private final ControllerInjector controllerInjector;
private final FieldSetter fieldSetter; private final FieldSetter fieldSetter;
private final PropertyFormatter propertyFormatter; private final PropertyFormatter propertyFormatter;
private final GenerationHelper generationHelper; private final VariableProvider variableProvider;
private final GenerationProgress progress;
private final ParsedObject parsedObject; private final ParsedObject parsedObject;
private final String variableName; private final String variableName;
private final String engineVariable; private final String engineVariable;
private final ParsedProperty parsedProperty;
private final StringBuilder sb; private final StringBuilder sb;
private final Map<String, ParsedProperty> attributes; private final Map<String, ParsedProperty> attributes;
private final WebViewFormatter webViewFormatter; private final WebViewFormatter webViewFormatter;
TestWebViewFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper, TestWebViewFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final ControllerInjector controllerInjector, @Mock final FieldSetter fieldSetter, @Mock final ControllerInjector controllerInjector, @Mock final FieldSetter fieldSetter,
@Mock final PropertyFormatter propertyFormatter, @Mock final GenerationHelper generationHelper, @Mock final PropertyFormatter propertyFormatter, @Mock final VariableProvider variableProvider,
@Mock final GenerationProgress progress, @Mock final ParsedObject parsedObject, @Mock final ParsedObject parsedObject) {
@Mock final ParsedProperty parsedProperty) {
this.helperProvider = Objects.requireNonNull(helperProvider); this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper); this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.controllerInjector = Objects.requireNonNull(controllerInjector); this.controllerInjector = Objects.requireNonNull(controllerInjector);
this.fieldSetter = Objects.requireNonNull(fieldSetter); this.fieldSetter = Objects.requireNonNull(fieldSetter);
this.propertyFormatter = Objects.requireNonNull(propertyFormatter); this.propertyFormatter = Objects.requireNonNull(propertyFormatter);
this.generationHelper = Objects.requireNonNull(generationHelper); this.variableProvider = Objects.requireNonNull(variableProvider);
this.progress = Objects.requireNonNull(progress);
this.parsedObject = Objects.requireNonNull(parsedObject); this.parsedObject = Objects.requireNonNull(parsedObject);
this.variableName = "variable"; this.variableName = "variable";
this.engineVariable = "engine"; this.engineVariable = "engine";
this.parsedProperty = Objects.requireNonNull(parsedProperty);
this.sb = new StringBuilder(); this.sb = new StringBuilder();
this.attributes = new HashMap<>(); this.attributes = new HashMap<>();
this.webViewFormatter = new WebViewFormatter(helperProvider, progress); this.webViewFormatter = new WebViewFormatter(helperProvider, sb);
} }
@BeforeEach @BeforeEach
@@ -68,19 +62,17 @@ class TestWebViewFormatter {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper); when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getControllerInjector()).thenReturn(controllerInjector); when(helperProvider.getControllerInjector()).thenReturn(controllerInjector);
when(helperProvider.getFieldSetter()).thenReturn(fieldSetter); when(helperProvider.getFieldSetter()).thenReturn(fieldSetter);
when(helperProvider.getGenerationHelper()).thenReturn(generationHelper);
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter); when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0)); when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value()).append((String) i.getArgument(2))).when(controllerInjector).injectCallbackControllerMethod(any(), eq(engineVariable), any()); doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value()).append((String) i.getArgument(2))).when(controllerInjector).injectCallbackControllerMethod(any(), eq(engineVariable), any());
doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value())).when(controllerInjector).injectEventHandlerControllerMethod(any(), eq(engineVariable)); doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value())).when(controllerInjector).injectEventHandlerControllerMethod(any(), eq(engineVariable));
doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value())).when(fieldSetter).setEventHandler(any(), eq(engineVariable)); doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value())).when(fieldSetter).setEventHandler(any(), eq(engineVariable));
doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value()).append((String) i.getArgument(2))).when(fieldSetter).setField(any(), eq(engineVariable), any()); doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value()).append((String) i.getArgument(2))).when(fieldSetter).setField(any(), eq(engineVariable), any());
doAnswer(i -> sb.append("handleId")).when(generationHelper).handleId(parsedObject, variableName);
doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value())).when(propertyFormatter).formatProperty(any(), any(), any()); doAnswer(i -> sb.append(((ParsedProperty) i.getArgument(0)).value())).when(propertyFormatter).formatProperty(any(), any(), any());
when(parsedObject.attributes()).thenReturn(attributes); when(parsedObject.attributes()).thenReturn(attributes);
when(progress.stringBuilder()).thenReturn(sb); when(variableProvider.getNextVariableName("engine")).thenReturn(engineVariable);
when(progress.getNextVariableName("engine")).thenReturn(engineVariable);
} }
@Test @Test
@@ -92,7 +84,7 @@ class TestWebViewFormatter {
@Test @Test
void testFormatHasProperty() { void testFormatHasProperty() {
final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>(); final var properties = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
properties.put(parsedProperty, List.of(parsedObject)); properties.put(new ParsedPropertyImpl("str", null, ""), List.of(parsedObject));
when(parsedObject.properties()).thenReturn(properties); when(parsedObject.properties()).thenReturn(properties);
assertThrows(GenerationException.class, () -> webViewFormatter.formatWebView(parsedObject, variableName)); assertThrows(GenerationException.class, () -> webViewFormatter.formatWebView(parsedObject, variableName));
} }
@@ -111,11 +103,13 @@ class TestWebViewFormatter {
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id")); attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
webViewFormatter.formatWebView(parsedObject, variableName); webViewFormatter.formatWebView(parsedObject, variableName);
final var expected = "javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();\n" + final var expected = """
"javafx.scene.web.WebEngineengine = variable.getEngine();\n" + javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();
"#confirmHandlerString.class#createPopupHandlerjavafx.scene.web.PopupFeatures.class" + javafx.scene.web.WebEngineengine = variable.getEngine();
" engine.load(\"location\");\n#onAlert#onResized#onStatusChanged#onVisibilityChanged#promptHandlerjavafx.scene.web.PromptData.class" + #confirmHandlerString.class#createPopupHandlerjavafx.scene.web.PopupFeatures.class\
"propertyhandleId"; engine.load("location");
#onAlert#onResized#onStatusChanged#onVisibilityChanged#promptHandlerjavafx.scene.web.PromptData.class\
property""";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
verify(propertyFormatter).formatProperty(attributes.get("property"), parsedObject, variableName); verify(propertyFormatter).formatProperty(attributes.get("property"), parsedObject, variableName);
verify(controllerInjector).injectCallbackControllerMethod(attributes.get("confirmHandler"), engineVariable, "String.class"); verify(controllerInjector).injectCallbackControllerMethod(attributes.get("confirmHandler"), engineVariable, "String.class");
@@ -125,7 +119,6 @@ class TestWebViewFormatter {
verify(controllerInjector).injectEventHandlerControllerMethod(attributes.get("onStatusChanged"), engineVariable); verify(controllerInjector).injectEventHandlerControllerMethod(attributes.get("onStatusChanged"), engineVariable);
verify(controllerInjector).injectEventHandlerControllerMethod(attributes.get("onVisibilityChanged"), engineVariable); verify(controllerInjector).injectEventHandlerControllerMethod(attributes.get("onVisibilityChanged"), engineVariable);
verify(controllerInjector).injectCallbackControllerMethod(attributes.get("promptHandler"), engineVariable, "javafx.scene.web.PromptData.class"); verify(controllerInjector).injectCallbackControllerMethod(attributes.get("promptHandler"), engineVariable, "javafx.scene.web.PromptData.class");
verify(generationHelper).handleId(parsedObject, variableName);
} }
@Test @Test
@@ -142,12 +135,14 @@ class TestWebViewFormatter {
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id")); attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
webViewFormatter.formatWebView(parsedObject, variableName); webViewFormatter.formatWebView(parsedObject, variableName);
final var expected = "javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();\n" + final var expected = """
"javafx.scene.web.WebEngineengine = variable.getEngine();\n" + javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();
"$controller.confirmHandlerjavafx.util.Callback$controller.createPopupHandlerjavafx.util.Callback" + javafx.scene.web.WebEngineengine = variable.getEngine();
" engine.load(\"location\");\n$controller.onAlert$controller.onResized$controller.onStatusChanged$controller.onVisibilityChanged" + $controller.confirmHandlerjavafx.util.Callback$controller.createPopupHandlerjavafx.util.Callback\
"$controller.promptHandlerjavafx.util.Callback" + engine.load("location");
"propertyhandleId"; $controller.onAlert$controller.onResized$controller.onStatusChanged$controller.onVisibilityChanged\
$controller.promptHandlerjavafx.util.Callback\
property""";
assertEquals(expected, sb.toString()); assertEquals(expected, sb.toString());
verify(fieldSetter).setEventHandler(attributes.get("onAlert"), engineVariable); verify(fieldSetter).setEventHandler(attributes.get("onAlert"), engineVariable);
@@ -158,6 +153,11 @@ class TestWebViewFormatter {
verify(fieldSetter).setField(attributes.get("confirmHandler"), engineVariable, "javafx.util.Callback"); verify(fieldSetter).setField(attributes.get("confirmHandler"), engineVariable, "javafx.util.Callback");
verify(fieldSetter).setField(attributes.get("createPopupHandler"), engineVariable, "javafx.util.Callback"); verify(fieldSetter).setField(attributes.get("createPopupHandler"), engineVariable, "javafx.util.Callback");
verify(fieldSetter).setField(attributes.get("promptHandler"), engineVariable, "javafx.util.Callback"); verify(fieldSetter).setField(attributes.get("promptHandler"), engineVariable, "javafx.util.Callback");
verify(generationHelper).handleId(parsedObject, variableName); }
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new WebViewFormatter(null, sb));
assertThrows(NullPointerException.class, () -> new WebViewFormatter(helperProvider, null));
} }
} }

View File

@@ -2,34 +2,34 @@ package com.github.gtache.fxml.compiler.impl.internal;
import javafx.beans.NamedArg; import javafx.beans.NamedArg;
public class WholeConstructorArgs { class WholeConstructorArgs {
public WholeConstructorArgs(@NamedArg("p1") final int p1, @NamedArg("p2") final Integer p2, WholeConstructorArgs(@NamedArg("p1") final int p1, @NamedArg("p2") final Integer p2,
@NamedArg("p3") final char p3, @NamedArg("p4") final Character p4, @NamedArg("p3") final char p3, @NamedArg("p4") final Character p4,
@NamedArg("p5") final boolean p5, @NamedArg("p6") final Boolean p6, @NamedArg("p5") final boolean p5, @NamedArg("p6") final Boolean p6,
@NamedArg("p7") final byte p7, @NamedArg("p8") final Byte p8, @NamedArg("p7") final byte p7, @NamedArg("p8") final Byte p8,
@NamedArg("p9") final short p9, @NamedArg("p10") final Short p10, @NamedArg("p9") final short p9, @NamedArg("p10") final Short p10,
@NamedArg("p11") final long p11, @NamedArg("p12") final Long p12, @NamedArg("p11") final long p11, @NamedArg("p12") final Long p12,
@NamedArg("p13") final float p13, @NamedArg("p14") final Float p14, @NamedArg("p13") final float p13, @NamedArg("p14") final Float p14,
@NamedArg("p15") final double p15, @NamedArg("p16") final Double p16, @NamedArg("p15") final double p15, @NamedArg("p16") final Double p16,
@NamedArg("p17") final String p17, @NamedArg("p18") final Object p18) { @NamedArg("p17") final String p17, @NamedArg("p18") final Object p18) {
} }
public WholeConstructorArgs(@NamedArg(value = "p1", defaultValue = "1") final int p1, WholeConstructorArgs(@NamedArg(value = "p1", defaultValue = "1") final int p1,
@NamedArg(value = "p3", defaultValue = "a") final char p3, @NamedArg(value = "p3", defaultValue = "a") final char p3,
@NamedArg(value = "p5", defaultValue = "true") final boolean p5, @NamedArg(value = "p5", defaultValue = "true") final boolean p5,
@NamedArg(value = "p7", defaultValue = "2") final byte p7, @NamedArg(value = "p7", defaultValue = "2") final byte p7,
@NamedArg(value = "p9", defaultValue = "3") final short p9, @NamedArg(value = "p9", defaultValue = "3") final short p9,
@NamedArg(value = "p11", defaultValue = "4") final long p11, @NamedArg(value = "p11", defaultValue = "4") final long p11,
@NamedArg(value = "p13", defaultValue = "5.5") final float p13, @NamedArg(value = "p13", defaultValue = "5.5") final float p13,
@NamedArg(value = "p15", defaultValue = "6.6") final double p15, @NamedArg(value = "p15", defaultValue = "6.6") final double p15,
@NamedArg(value = "p17", defaultValue = "str") final String p17) { @NamedArg(value = "p17", defaultValue = "str") final String p17) {
} }
public WholeConstructorArgs(final int p1, final char p3) { WholeConstructorArgs(final int p1, final char p3) {
} }
public WholeConstructorArgs(final int p1, @NamedArg("p3") final char p3, @NamedArg("p5") final boolean p5) { WholeConstructorArgs(final int p1, @NamedArg("p3") final char p3, @NamedArg("p5") final boolean p5) {
} }
} }

View File

@@ -17,7 +17,7 @@ class TestParsedConstantImpl {
TestParsedConstantImpl() { TestParsedConstantImpl() {
this.className = "test"; this.className = "test";
this.attributes = new HashMap<>(Map.of("fx:constant", new ParsedPropertyImpl("fx:constant", String.class.getName(), "value"))); this.attributes = new HashMap<>(Map.of("fx:constant", new ParsedPropertyImpl("fx:constant", null, "value")));
this.constant = new ParsedConstantImpl(className, attributes); this.constant = new ParsedConstantImpl(className, attributes);
} }
@@ -43,9 +43,19 @@ class TestParsedConstantImpl {
assertThrows(UnsupportedOperationException.class, objectProperties::clear); assertThrows(UnsupportedOperationException.class, objectProperties::clear);
} }
@Test
void testOtherConstructor() {
final var otherConstant = new ParsedConstantImpl(className, "value");
assertEquals(className, otherConstant.className());
assertEquals(attributes, otherConstant.attributes());
assertEquals("value", otherConstant.constant());
}
@Test @Test
void testIllegal() { void testIllegal() {
final var emptyMap = Map.<String, ParsedProperty>of();
assertThrows(NullPointerException.class, () -> new ParsedConstantImpl(null, attributes)); assertThrows(NullPointerException.class, () -> new ParsedConstantImpl(null, attributes));
assertThrows(NullPointerException.class, () -> new ParsedConstantImpl(className, null)); assertThrows(NullPointerException.class, () -> new ParsedConstantImpl(className, (Map<String, ParsedProperty>) null));
assertThrows(IllegalArgumentException.class, () -> new ParsedConstantImpl(className, emptyMap));
} }
} }

View File

@@ -8,6 +8,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedMap; import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@@ -20,7 +21,7 @@ class TestParsedCopyImpl {
TestParsedCopyImpl(@Mock final ParsedProperty property) { TestParsedCopyImpl(@Mock final ParsedProperty property) {
this.properties = new LinkedHashMap<>(); this.properties = new LinkedHashMap<>();
this.properties.put("name", property); this.properties.put("source", property);
this.copy = new ParsedCopyImpl(properties); this.copy = new ParsedCopyImpl(properties);
} }
@@ -45,8 +46,16 @@ class TestParsedCopyImpl {
assertThrows(UnsupportedOperationException.class, objectProperties::clear); assertThrows(UnsupportedOperationException.class, objectProperties::clear);
} }
@Test
void testOtherConstructor() {
final var otherCopy = new ParsedCopyImpl("source");
assertEquals("source", otherCopy.source());
}
@Test @Test
void testIllegal() { void testIllegal() {
assertThrows(NullPointerException.class, () -> new TestParsedCopyImpl(null)); assertThrows(NullPointerException.class, () -> new ParsedCopyImpl((Map<String, ParsedProperty>) null));
final var emptyMap = Map.<String, ParsedProperty>of();
assertThrows(IllegalArgumentException.class, () -> new ParsedCopyImpl(emptyMap));
} }
} }

View File

@@ -19,7 +19,7 @@ class TestParsedDefineImpl {
private final List<ParsedObject> children; private final List<ParsedObject> children;
private final ParsedDefine parsedDefine; private final ParsedDefine parsedDefine;
TestParsedDefineImpl(@Mock ParsedObject parsedObject1, @Mock ParsedObject parsedObject2) { TestParsedDefineImpl(@Mock final ParsedObject parsedObject1, @Mock final ParsedObject parsedObject2) {
this.children = new ArrayList<>(List.of(parsedObject1, parsedObject2)); this.children = new ArrayList<>(List.of(parsedObject1, parsedObject2));
this.parsedDefine = new ParsedDefineImpl(children); this.parsedDefine = new ParsedDefineImpl(children);
} }

View File

@@ -8,6 +8,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedMap; import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@@ -20,7 +21,7 @@ class TestParsedIncludeImpl {
TestParsedIncludeImpl(@Mock final ParsedProperty property) { TestParsedIncludeImpl(@Mock final ParsedProperty property) {
this.properties = new LinkedHashMap<>(); this.properties = new LinkedHashMap<>();
this.properties.put("name", property); this.properties.put("source", property);
this.include = new ParsedIncludeImpl(properties); this.include = new ParsedIncludeImpl(properties);
} }
@@ -45,8 +46,21 @@ class TestParsedIncludeImpl {
assertThrows(UnsupportedOperationException.class, objectProperties::clear); assertThrows(UnsupportedOperationException.class, objectProperties::clear);
} }
@Test
void testOtherConstructor() {
final var otherInclude = new ParsedIncludeImpl("s", "r", "f");
final var attributes = Map.of("source", new ParsedPropertyImpl("source", null, "s"),
"resources", new ParsedPropertyImpl("resources", null, "r"),
"fx:id", new ParsedPropertyImpl("fx:id", null, "f"));
assertEquals(attributes, otherInclude.attributes());
}
@Test @Test
void testIllegal() { void testIllegal() {
assertThrows(NullPointerException.class, () -> new ParsedIncludeImpl(null)); assertThrows(NullPointerException.class, () -> new ParsedIncludeImpl(null));
assertThrows(NullPointerException.class, () -> new ParsedIncludeImpl(null, "", ""));
assertDoesNotThrow(() -> new ParsedIncludeImpl("", null, null));
final var emptyMap = Map.<String, ParsedProperty>of();
assertThrows(IllegalArgumentException.class, () -> new ParsedIncludeImpl(emptyMap));
} }
} }

View File

@@ -31,7 +31,7 @@ class TestParsedObjectImpl {
this.attributes = new LinkedHashMap<>(); this.attributes = new LinkedHashMap<>();
this.attributes.put("name", property); this.attributes.put("name", property);
this.properties = new LinkedHashMap<>(); this.properties = new LinkedHashMap<>();
this.properties.put(property, List.of(object)); this.properties.put(new ParsedPropertyImpl("property", null, ""), List.of(object));
this.objects = new ArrayList<>(List.of(define)); this.objects = new ArrayList<>(List.of(define));
this.parsedObject = new ParsedObjectImpl(clazz, attributes, properties, objects); this.parsedObject = new ParsedObjectImpl(clazz, attributes, properties, objects);
} }

View File

@@ -30,6 +30,6 @@ class TestParsedPropertyImpl {
void testIllegal() { void testIllegal() {
assertThrows(NullPointerException.class, () -> new ParsedPropertyImpl(null, sourceType, value)); assertThrows(NullPointerException.class, () -> new ParsedPropertyImpl(null, sourceType, value));
assertDoesNotThrow(() -> new ParsedPropertyImpl(name, null, value)); assertDoesNotThrow(() -> new ParsedPropertyImpl(name, null, value));
assertDoesNotThrow(() -> new ParsedPropertyImpl(name, sourceType, null)); assertThrows(NullPointerException.class, () -> new ParsedPropertyImpl(name, sourceType, null));
} }
} }

View File

@@ -8,6 +8,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedMap; import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@@ -20,7 +21,7 @@ class TestParsedReferenceImpl {
TestParsedReferenceImpl(@Mock final ParsedProperty property) { TestParsedReferenceImpl(@Mock final ParsedProperty property) {
this.properties = new LinkedHashMap<>(); this.properties = new LinkedHashMap<>();
this.properties.put("name", property); this.properties.put("source", property);
this.reference = new ParsedReferenceImpl(properties); this.reference = new ParsedReferenceImpl(properties);
} }
@@ -45,8 +46,16 @@ class TestParsedReferenceImpl {
assertThrows(UnsupportedOperationException.class, objectProperties::clear); assertThrows(UnsupportedOperationException.class, objectProperties::clear);
} }
@Test
void testOtherConstructor() {
final var otherReference = new ParsedReferenceImpl("name");
assertEquals("name", otherReference.source());
}
@Test @Test
void testIllegal() { void testIllegal() {
assertThrows(NullPointerException.class, () -> new ParsedReferenceImpl(null)); assertThrows(NullPointerException.class, () -> new ParsedReferenceImpl((Map<String, ParsedProperty>) null));
final var emptyMap = Map.<String, ParsedProperty>of();
assertThrows(IllegalArgumentException.class, () -> new ParsedReferenceImpl(emptyMap));
} }
} }

View File

@@ -17,7 +17,7 @@ class TestParsedValueImpl {
TestParsedValueImpl() { TestParsedValueImpl() {
this.className = "test"; this.className = "test";
this.attributes = new HashMap<>(Map.of("fx:value", new ParsedPropertyImpl("fx:value", String.class.getName(), "value"))); this.attributes = new HashMap<>(Map.of("fx:value", new ParsedPropertyImpl("fx:value", null, "value")));
this.value = new ParsedValueImpl(className, attributes); this.value = new ParsedValueImpl(className, attributes);
} }
@@ -43,9 +43,19 @@ class TestParsedValueImpl {
assertThrows(UnsupportedOperationException.class, objectProperties::clear); assertThrows(UnsupportedOperationException.class, objectProperties::clear);
} }
@Test
void testOtherConstructor() {
final var otherValue = new ParsedValueImpl(className, "value");
assertEquals(className, otherValue.className());
assertEquals(attributes, otherValue.attributes());
assertEquals("value", otherValue.value());
}
@Test @Test
void testIllegal() { void testIllegal() {
assertThrows(NullPointerException.class, () -> new ParsedValueImpl(null, attributes)); assertThrows(NullPointerException.class, () -> new ParsedValueImpl(null, attributes));
assertThrows(NullPointerException.class, () -> new ParsedValueImpl(className, null)); assertThrows(NullPointerException.class, () -> new ParsedValueImpl(className, (Map<String, ParsedProperty>) null));
final var emptyMap = Map.<String, ParsedProperty>of();
assertThrows(IllegalArgumentException.class, () -> new ParsedValueImpl(className, emptyMap));
} }
} }

View File

@@ -1,11 +1,11 @@
package com.github.gtache.fxml.compiler.maven; package com.github.gtache.fxml.compiler.maven;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.impl.GenerationCompatibilityImpl; import com.github.gtache.fxml.compiler.compatibility.impl.GenerationCompatibilityImpl;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
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.GenerationParametersImpl;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import com.github.gtache.fxml.compiler.maven.internal.CompilationInfo; 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.CompilationInfoProvider;
import com.github.gtache.fxml.compiler.maven.internal.Compiler; import com.github.gtache.fxml.compiler.maven.internal.Compiler;
@@ -21,6 +21,9 @@ import org.apache.maven.project.MavenProject;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/** /**
* Main mojo for FXML compiler * Main mojo for FXML compiler
@@ -41,31 +44,45 @@ public class FXMLCompilerMojo extends AbstractMojo {
private boolean useImageInputStreamConstructor; private boolean useImageInputStreamConstructor;
@Parameter(property = "controller-injection", defaultValue = "INSTANCE", required = true) @Parameter(property = "controller-injection", defaultValue = "INSTANCE", required = true)
private ControllerInjectionTypes controllerInjectionType; private ControllerInjectionType controllerInjectionType;
@Parameter(property = "field-injection", defaultValue = "REFLECTION", required = true) @Parameter(property = "field-injection", defaultValue = "REFLECTION", required = true)
private ControllerFieldInjectionTypes fieldInjectionType; private ControllerFieldInjectionType fieldInjectionType;
@Parameter(property = "method-injection", defaultValue = "REFLECTION", required = true) @Parameter(property = "method-injection", defaultValue = "REFLECTION", required = true)
private ControllerMethodsInjectionType methodInjectionType; private ControllerMethodsInjectionType methodInjectionType;
@Parameter(property = "resource-injection", defaultValue = "CONSTRUCTOR", required = true) @Parameter(property = "resource-injection", defaultValue = "CONSTRUCTOR", required = true)
private ResourceBundleInjectionTypes resourceInjectionType; private ResourceBundleInjectionType resourceInjectionType;
@Parameter(property = "resource-map") @Parameter(property = "resource-map")
private Map<String, String> resourceMap; private Map<String, String> resourceMap;
@Parameter(property = "parallelism", defaultValue = "1", required = true)
private int parallelism;
@Override @Override
public void execute() throws MojoExecutionException { public void execute() throws MojoExecutionException {
if (fieldInjectionType == ControllerFieldInjectionTypes.FACTORY && controllerInjectionType != ControllerInjectionTypes.FACTORY) { if (fieldInjectionType == ControllerFieldInjectionType.FACTORY && controllerInjectionType != ControllerInjectionType.FACTORY) {
getLog().warn("Field injection is set to FACTORY : Forcing controller injection to FACTORY"); getLog().warn("Field injection is set to FACTORY : Forcing controller injection to FACTORY");
controllerInjectionType = ControllerInjectionTypes.FACTORY; controllerInjectionType = ControllerInjectionType.FACTORY;
} }
final var fxmls = FXMLProvider.getFXMLs(project); final var fxmls = FXMLProvider.getFXMLs(project);
final var controllerMapping = createControllerMapping(fxmls); if (parallelism < 1) {
final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping); parallelism = Runtime.getRuntime().availableProcessors();
compile(compilationInfoMapping); }
if (parallelism > 1) {
try (final var executor = Executors.newFixedThreadPool(parallelism)) {
final var controllerMapping = createControllerMapping(fxmls, executor);
final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping, executor);
compile(compilationInfoMapping, executor);
}
} else {
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 { private static Map<Path, String> createControllerMapping(final Map<? extends Path, ? extends Path> fxmls) throws MojoExecutionException {
@@ -76,7 +93,8 @@ public class FXMLCompilerMojo extends AbstractMojo {
return mapping; return mapping;
} }
private Map<Path, CompilationInfo> createCompilationInfoMapping(final Map<? extends Path, ? extends Path> fxmls, final Map<? extends Path, String> controllerMapping) throws MojoExecutionException { 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>(); final var mapping = new HashMap<Path, CompilationInfo>();
for (final var entry : fxmls.entrySet()) { for (final var entry : fxmls.entrySet()) {
final var info = CompilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping, outputDirectory, project); final var info = CompilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping, outputDirectory, project);
@@ -91,4 +109,48 @@ public class FXMLCompilerMojo extends AbstractMojo {
Compiler.compile(mapping, parameters); Compiler.compile(mapping, parameters);
project.addCompileSourceRoot(outputDirectory.toAbsolutePath().toString()); project.addCompileSourceRoot(outputDirectory.toAbsolutePath().toString());
} }
private static Map<Path, String> createControllerMapping(final Map<? extends Path, ? extends Path> fxmls,
final ExecutorService executor) {
final var mapping = new ConcurrentHashMap<Path, String>();
for (final var fxml : fxmls.keySet()) {
executor.submit(() -> {
try {
mapping.put(fxml, ControllerProvider.getController(fxml));
} catch (final MojoExecutionException e) {
throw new RuntimeException(e);
}
});
}
return mapping;
}
private Map<Path, CompilationInfo> createCompilationInfoMapping(final Map<? extends Path, ? extends Path> fxmls,
final Map<? extends Path, String> controllerMapping, final ExecutorService executor) {
final var mapping = new ConcurrentHashMap<Path, CompilationInfo>();
for (final var entry : fxmls.entrySet()) {
executor.submit(() -> {
try {
final var info = CompilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping, outputDirectory, project);
mapping.put(entry.getKey(), info);
} catch (final MojoExecutionException e) {
throw new RuntimeException(e);
}
});
}
return mapping;
}
private void compile(final Map<Path, CompilationInfo> mapping, final ExecutorService executor) throws MojoExecutionException {
final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion), useImageInputStreamConstructor, resourceMap,
controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType);
mapping.forEach((p, i) -> executor.submit(() -> {
try {
Compiler.compile(p, i, mapping, parameters);
} catch (final MojoExecutionException e) {
throw new RuntimeException(e);
}
}));
project.addCompileSourceRoot(outputDirectory.toAbsolutePath().toString());
}
} }

View File

@@ -12,7 +12,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import javax.inject.Named;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@@ -21,7 +20,6 @@ import java.util.Map;
/** /**
* Creates compiled Java code * Creates compiled Java code
*/ */
@Named
public final class Compiler { public final class Compiler {
private static final Logger logger = LogManager.getLogger(Compiler.class); private static final Logger logger = LogManager.getLogger(Compiler.class);
@@ -45,7 +43,7 @@ public final class Compiler {
} }
} }
private static void compile(final Path inputPath, final CompilationInfo info, final Map<Path, CompilationInfo> mapping, final GenerationParameters parameters) throws MojoExecutionException { public static void compile(final Path inputPath, final CompilationInfo info, final Map<Path, CompilationInfo> mapping, final GenerationParameters parameters) throws MojoExecutionException {
try { try {
logger.info("Parsing {} with {}", inputPath, PARSER.getClass().getSimpleName()); logger.info("Parsing {} with {}", inputPath, PARSER.getClass().getSimpleName());
final var root = PARSER.parse(inputPath); final var root = PARSER.parse(inputPath);

View File

@@ -3,7 +3,6 @@ package com.github.gtache.fxml.compiler.maven.internal;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import javax.inject.Named;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@@ -13,7 +12,6 @@ import java.nio.file.Path;
/** /**
* Extracts controller class from FXMLs * Extracts controller class from FXMLs
*/ */
@Named
public final class ControllerProvider { public final class ControllerProvider {
private static final DocumentBuilder DOCUMENT_BUILDER; private static final DocumentBuilder DOCUMENT_BUILDER;

View File

@@ -5,7 +5,6 @@ import org.apache.logging.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import javax.inject.Named;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileVisitOption; import java.nio.file.FileVisitOption;
import java.nio.file.Files; import java.nio.file.Files;
@@ -17,7 +16,6 @@ import java.util.Map;
/** /**
* Extracts FXML paths from Maven project * Extracts FXML paths from Maven project
*/ */
@Named
public final class FXMLProvider { public final class FXMLProvider {
private static final Logger logger = LogManager.getLogger(FXMLProvider.class); private static final Logger logger = LogManager.getLogger(FXMLProvider.class);

View File

@@ -25,7 +25,6 @@ import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@@ -41,7 +40,6 @@ class TestCompiler {
private final SourceInfo sourceInfo; private final SourceInfo sourceInfo;
private final String content; private final String content;
private final GenerationParameters parameters; private final GenerationParameters parameters;
private final Compiler compiler;
TestCompiler(@Mock final ControllerInfoProvider controllerInfoProvider, @Mock final SourceInfoProvider sourceInfoProvider, TestCompiler(@Mock final ControllerInfoProvider controllerInfoProvider, @Mock final SourceInfoProvider sourceInfoProvider,
@Mock final FXMLParser fxmlParser, @Mock final CompilationInfo compilationInfo, @Mock final ParsedObject object, @Mock final FXMLParser fxmlParser, @Mock final CompilationInfo compilationInfo, @Mock final ParsedObject object,
@@ -57,7 +55,6 @@ class TestCompiler {
this.content = "content"; this.content = "content";
this.parameters = Objects.requireNonNull(parameters); this.parameters = Objects.requireNonNull(parameters);
this.generator = Objects.requireNonNull(generator); this.generator = Objects.requireNonNull(generator);
this.compiler = new Compiler(controllerInfoProvider, sourceInfoProvider, fxmlParser, generator);
} }
@BeforeEach @BeforeEach
@@ -77,7 +74,7 @@ class TestCompiler {
when(compilationInfo.outputClass()).thenReturn(outputClass); when(compilationInfo.outputClass()).thenReturn(outputClass);
final var mapping = Map.of(path, compilationInfo); final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass); final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
compiler.compile(mapping, parameters); Compiler.compile(mapping, parameters);
verify(fxmlParser).parse(path); verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo); ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping); SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
@@ -94,7 +91,7 @@ class TestCompiler {
when(compilationInfo.outputClass()).thenReturn(outputClass); when(compilationInfo.outputClass()).thenReturn(outputClass);
final var mapping = Map.of(path, compilationInfo); final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass); final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters)); assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path); verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo); ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping); SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
@@ -111,7 +108,7 @@ class TestCompiler {
final var mapping = Map.of(path, compilationInfo); final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass); final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
when(generator.generate(request)).thenThrow(RuntimeException.class); when(generator.generate(request)).thenThrow(RuntimeException.class);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters)); assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path); verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo); ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping); SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
@@ -123,7 +120,7 @@ class TestCompiler {
when(fxmlParser.parse((Path) any())).thenThrow(ParseException.class); when(fxmlParser.parse((Path) any())).thenThrow(ParseException.class);
final var path = tempDir.resolve("fxml1.fxml"); final var path = tempDir.resolve("fxml1.fxml");
final var mapping = Map.of(path, compilationInfo); final var mapping = Map.of(path, compilationInfo);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters)); assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path); verify(fxmlParser).parse(path);
verifyNoInteractions(controllerInfoProvider, sourceInfoProvider, generator); verifyNoInteractions(controllerInfoProvider, sourceInfoProvider, generator);
} }
@@ -138,18 +135,10 @@ class TestCompiler {
final var mapping = Map.of(path, compilationInfo); final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass); final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
when(generator.generate(request)).thenThrow(GenerationException.class); when(generator.generate(request)).thenThrow(GenerationException.class);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters)); assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path); verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo); ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping); SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
verify(generator).generate(request); verify(generator).generate(request);
} }
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new Compiler(null, sourceInfoProvider, fxmlParser, generator));
assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, null, fxmlParser, generator));
assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, sourceInfoProvider, null, generator));
assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, sourceInfoProvider, fxmlParser, null));
}
} }

View File

@@ -16,12 +16,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class TestControllerProvider { class TestControllerProvider {
private final ControllerProvider provider;
TestControllerProvider() {
this.provider = new ControllerProvider();
}
@Test @Test
void testGetController(@TempDir final Path tempDir) throws Exception { void testGetController(@TempDir final Path tempDir) throws Exception {
final var fxml = tempDir.resolve("fxml.fxml"); final var fxml = tempDir.resolve("fxml.fxml");

View File

@@ -22,11 +22,9 @@ import static org.mockito.Mockito.when;
class TestFXMLProvider { class TestFXMLProvider {
private final MavenProject project; private final MavenProject project;
private final FXMLProvider provider;
TestFXMLProvider(@Mock final MavenProject project) { TestFXMLProvider(@Mock final MavenProject project) {
this.project = Objects.requireNonNull(project); this.project = Objects.requireNonNull(project);
this.provider = new FXMLProvider();
} }
@Test @Test

259
mvnw vendored Normal file
View File

@@ -0,0 +1,259 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.2
#
# Optional ENV vars
# -----------------
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
# MVNW_REPOURL - repo url base for downloading maven distribution
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x
# OS specific support.
native_path() { printf %s\\n "$1"; }
case "$(uname)" in
CYGWIN* | MINGW*)
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
native_path() { cygpath --path --windows "$1"; }
;;
esac
# set JAVACMD and JAVACCMD
set_java_home() {
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
if [ -n "${JAVA_HOME-}" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACCMD="$JAVA_HOME/jre/sh/javac"
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACCMD="$JAVA_HOME/bin/javac"
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
return 1
fi
fi
else
JAVACMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v java
)" || :
JAVACCMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v javac
)" || :
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
return 1
fi
fi
}
# hash string like Java String::hashCode
hash_string() {
str="${1:-}" h=0
while [ -n "$str" ]; do
char="${str%"${str#?}"}"
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
str="${str#?}"
done
printf %x\\n $h
}
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
*)
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
distributionPlatform=linux-amd64
;;
esac
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
fi
case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac
# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
trap clean HUP INT TERM EXIT
else
die "cannot create temp dir"
fi
mkdir -p -- "${MAVEN_HOME%/*}"
# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
verbose "Found wget ... using wget"
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
verbose "Found curl ... using curl"
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
verbose "Falling back to use Java to download"
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
cat >"$javaSource" <<-END
public class Downloader extends java.net.Authenticator
{
protected java.net.PasswordAuthentication getPasswordAuthentication()
{
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
}
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
verbose " - Compiling Downloader.java ..."
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
verbose " - Running Downloader.java ..."
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi
# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
distributionSha256Result=false
if [ "$MVN_CMD" = mvnd.sh ]; then
echo "Checksum validation is not supported for maven-mvnd." >&2
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $distributionSha256Result = false ]; then
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
# unzip and move
if command -v unzip >/dev/null; then
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"

149
mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,149 @@
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.2
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
"maven-mvnd-*" {
$USE_MVND = $true
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
$MVN_CMD = "mvnd.cmd"
break
}
default {
$USE_MVND = $false
$MVN_CMD = $script -replace '^mvnw','mvn'
break
}
}
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
if ($env:MAVEN_USER_HOME) {
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
}
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"

33
pom.xml
View File

@@ -26,7 +26,7 @@
</organization> </organization>
<properties> <properties>
<maven.min-version>3.8.0</maven.min-version> <maven.min-version>3.6.3</maven.min-version>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -93,8 +93,6 @@
<version>${plugin.deploy.version}</version> <version>${plugin.deploy.version}</version>
<executions> <executions>
<execution> <execution>
<id>deploy</id>
<phase>deploy</phase>
<goals> <goals>
<goal>deploy</goal> <goal>deploy</goal>
</goals> </goals>
@@ -107,7 +105,6 @@
<version>${plugin.enforcer.version}</version> <version>${plugin.enforcer.version}</version>
<executions> <executions>
<execution> <execution>
<id>enforce-versions</id>
<goals> <goals>
<goal>enforce</goal> <goal>enforce</goal>
</goals> </goals>
@@ -137,16 +134,8 @@
<version>${plugin.failsafe.version}</version> <version>${plugin.failsafe.version}</version>
<executions> <executions>
<execution> <execution>
<id>integration-test</id>
<phase>test</phase>
<goals> <goals>
<goal>integration-test</goal> <goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>verify</goal> <goal>verify</goal>
</goals> </goals>
</execution> </execution>
@@ -158,26 +147,10 @@
<version>${plugin.jacoco.version}</version> <version>${plugin.jacoco.version}</version>
<executions> <executions>
<execution> <execution>
<id>default-prepare-agent</id>
<goals> <goals>
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-prepare-agent-integration</id>
<goals>
<goal>prepare-agent-integration</goal> <goal>prepare-agent-integration</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<goals>
<goal>report</goal> <goal>report</goal>
</goals>
</execution>
<execution>
<id>default-report-integration</id>
<goals>
<goal>report-integration</goal> <goal>report-integration</goal>
</goals> </goals>
</execution> </execution>
@@ -189,8 +162,6 @@
<version>${plugin.javadoc.version}</version> <version>${plugin.javadoc.version}</version>
<executions> <executions>
<execution> <execution>
<id>attach-javadocs</id>
<phase>install</phase>
<goals> <goals>
<goal>jar</goal> <goal>jar</goal>
</goals> </goals>
@@ -221,8 +192,6 @@
<version>${plugin.source.version}</version> <version>${plugin.source.version}</version>
<executions> <executions>
<execution> <execution>
<id>attach-sources</id>
<phase>package</phase>
<goals> <goals>
<goal>jar-no-fork</goal> <goal>jar-no-fork</goal>
</goals> </goals>

View File

@@ -1,5 +1,5 @@
/** /**
* XML parsing module for FXML compiler (not implemented yet) * XML parsing module for FXML compiler
*/ */
module com.github.gtache.fxml.compiler.xml { module com.github.gtache.fxml.compiler.xml {
requires transitive com.github.gtache.fxml.compiler.core; requires transitive com.github.gtache.fxml.compiler.core;

View File

@@ -19,6 +19,7 @@ import java.util.Map;
import java.util.SequencedMap; import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class TestDOMFXMLParser { class TestDOMFXMLParser {
@@ -76,6 +77,7 @@ class TestDOMFXMLParser {
List.of(new ParsedObjectImpl(VBox.class.getName(), newLinkedHashMap("fx:id", new ParsedPropertyImpl("fx:id", null, "vbox")), newLinkedHashMap(), List.of())) List.of(new ParsedObjectImpl(VBox.class.getName(), newLinkedHashMap("fx:id", new ParsedPropertyImpl("fx:id", null, "vbox")), newLinkedHashMap(), List.of()))
), List.of()); ), List.of());
try (final var in = getClass().getResourceAsStream("loadView.fxml")) { try (final var in = getClass().getResourceAsStream("loadView.fxml")) {
assertNotNull(in);
final var content = new String(in.readAllBytes(), StandardCharsets.UTF_8); final var content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
final var actual = parser.parse(content); final var actual = parser.parse(content);
assertEquals(expected, actual); assertEquals(expected, actual);

View File

@@ -1,144 +0,0 @@
package com.github.gtache.fxml.compiler.parsing.xml;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
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.compatibility.impl.GenerationCompatibilityImpl;
import com.github.gtache.fxml.compiler.impl.*;
import com.github.gtache.fxml.compiler.parsing.ParseException;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class TestGeneratorImpl {
private static final Map<String, ControllerFieldInfo> FIELD_INFO_MAP;
static {
FIELD_INFO_MAP = new HashMap<>();
FIELD_INFO_MAP.put("button", new ControllerFieldInfoImpl("button", List.of()));
FIELD_INFO_MAP.put("checkBox", new ControllerFieldInfoImpl("checkBox", List.of()));
FIELD_INFO_MAP.put("colorPicker", new ControllerFieldInfoImpl("colorPicker", List.of()));
FIELD_INFO_MAP.put("color", new ControllerFieldInfoImpl("color", List.of()));
FIELD_INFO_MAP.put("comboBox", new ControllerFieldInfoImpl("comboBox", List.of()));
FIELD_INFO_MAP.put("listView", new ControllerFieldInfoImpl("listView", List.of("javafx.scene.control.Label")));
FIELD_INFO_MAP.put("spinner", new ControllerFieldInfoImpl("spinner", List.of("Double")));
FIELD_INFO_MAP.put("tableView", new ControllerFieldInfoImpl("tableView", List.of("javafx.scene.control.TextArea")));
FIELD_INFO_MAP.put("treeView", new ControllerFieldInfoImpl("treeView", List.of("String")));
FIELD_INFO_MAP.put("treeTableView", new ControllerFieldInfoImpl("treeTableView", List.of("javafx.scene.control.TreeItem<String>")));
FIELD_INFO_MAP.put("treeTableColumn1", new ControllerFieldInfoImpl("treeTableColumn1", List.of("javafx.scene.control.TreeItem<String>", "String")));
FIELD_INFO_MAP.put("treeTableColumn2", new ControllerFieldInfoImpl("treeTableColumn2", List.of("javafx.scene.control.TreeItem<String>", "Integer")));
FIELD_INFO_MAP.put("tableColumn1", new ControllerFieldInfoImpl("tableColumn1", List.of("javafx.scene.control.TextArea", "Float")));
FIELD_INFO_MAP.put("tableColumn2", new ControllerFieldInfoImpl("tableColumn2", List.of("javafx.scene.control.TextArea", "String")));
}
private final Generator generator;
TestGeneratorImpl() {
this.generator = new GeneratorImpl();
}
@ParameterizedTest
@MethodSource("providesGenerationTestCases")
void testGenerate(final String file, final ControllerInjectionTypes controller, final ControllerFieldInjectionTypes field, final ControllerMethodsInjectionType method, final ResourceBundleInjectionTypes bundle) throws Exception {
final var request = getRequest(file, controller, field, method, bundle);
final var path = Paths.get(getPath(file, controller, field, method, bundle));
try (final var in = getClass().getResourceAsStream("/com/github/gtache/fxml/compiler/parsing/xml/" + path)) {
assertNotNull(in);
final var expected = new String(in.readAllBytes(), StandardCharsets.UTF_8);
final var actual = generator.generate(request);
assertEquals(expected, actual);
} catch (final IOException | GenerationException e) {
throw new RuntimeException(e);
}
}
public static void main(final String[] args) throws GenerationException, IOException, ParseException {
final var generator = new GeneratorImpl();
final var files = List.of("Controls", "Includes");
for (final var file : files) {
for (final var controller : ControllerInjectionTypes.values()) {
for (final var field : ControllerFieldInjectionTypes.values()) {
for (final var method : ControllerMethodsInjectionType.values()) {
for (final var bundle : ResourceBundleInjectionTypes.values()) {
final var request = getRequest(file, controller, field, method, bundle);
final var content = generator.generate(request);
final var path = Paths.get(getPath(file, controller, field, method, bundle));
Files.writeString(path, content);
}
}
}
}
}
}
private static String getPath(final String file, final ControllerInjectionTypes controller, final ControllerFieldInjectionTypes field, final ControllerMethodsInjectionType method, final ResourceBundleInjectionTypes bundle) {
return "expected-" + file.toLowerCase() + "-" + controller.name().toLowerCase() + "-" + field.name().toLowerCase() + "-" + method.name().toLowerCase() + "-" + bundle.name().replace("_", "").toLowerCase() + ".txt";
}
private static GenerationRequest getRequest(final String file, final ControllerInjectionTypes controller, final ControllerFieldInjectionTypes field,
final ControllerMethodsInjectionType method, final ResourceBundleInjectionTypes resource) throws IOException, ParseException {
final var controllerClass = "com.github.gtache.fxml.compiler.parsing.xml." + file + "Controller";
final var controlsControllerInfo = new ControllerInfoImpl(controllerClass, Map.of("keyPressed", false, "mouseClicked", false),
FIELD_INFO_MAP, true);
final var includesControllerInfo = new ControllerInfoImpl(controllerClass, Map.of(), Map.of(), true);
final var controllerInfo = file.equals("Controls") ? controlsControllerInfo : includesControllerInfo;
final var resourceBundlePath = "com.github.gtache.fxml.compiler.parsing.xml." + file + "Bundle";
final var viewPath = "/com/github/gtache/fxml/compiler/parsing/xml/" + file.toLowerCase() + "View.fxml";
final var controlsSourceInfo = new SourceInfoImpl("com.github.gtache.fxml.compiler.parsing.xml.ControlsController",
controllerClass, Paths.get(viewPath), List.of(), Map.of(), true);
final var includesSourceInfo = new SourceInfoImpl("com.github.gtache.fxml.compiler.parsing.xml.IncludesController",
controllerClass, Paths.get(viewPath), List.of(controlsSourceInfo), Map.of("controlsView.fxml", controlsSourceInfo), true);
final var sourceInfo = file.equals("Controls") ? controlsSourceInfo : includesSourceInfo;
final var parser = new DOMFXMLParser();
try (final var in = TestGeneratorImpl.class.getResourceAsStream(viewPath)) {
assertNotNull(in);
final var content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
final var root = parser.parse(content);
return new GenerationRequestImpl(
new GenerationParametersImpl(new GenerationCompatibilityImpl(21), false,
Map.of(controllerInfo.className(), resourceBundlePath),
controller,
field,
method,
resource
),
controllerInfo,
sourceInfo,
root,
"com.github.gtache.fxml.compiler.parsing.xml." + file + "View"
);
}
}
private static Stream<Arguments> providesGenerationTestCases() {
final var files = List.of("Controls", "Includes");
final var list = new ArrayList<Arguments>();
for (final var file : files) {
for (final var controller : ControllerInjectionTypes.values()) {
for (final var field : ControllerFieldInjectionTypes.values()) {
for (final var method : ControllerMethodsInjectionType.values()) {
for (final var bundle : ResourceBundleInjectionTypes.values()) {
list.add(Arguments.of(file, controller, field, method, bundle));
}
}
}
}
}
return list.stream();
}
}

View File

@@ -1,239 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.collections.FXCollections?>
<?import javafx.geometry.Insets?>
<?import javafx.geometry.Point3D?>
<?import javafx.scene.control.*?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.effect.Bloom?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.media.MediaView?>
<?import javafx.scene.paint.Color?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.web.HTMLEditor?>
<?import javafx.scene.web.WebView?>
<?import java.lang.String?>
<GridPane fx:id="gridPane" onInputMethodTextChanged="#inputMethodTextChanged" onKeyPressed="#keyPressed"
onKeyReleased="#keyReleased"
onKeyTyped="#keyTyped" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.parsing.xml.ControlsController">
<fx:define>
<FXCollections fx:id="list" fx:factory="observableArrayList">
<String>text1</String>
<String fx:value="text2"/>
<String value="text3"/>
</FXCollections>
<FXCollections fx:id="emptyMap" fx:factory="emptyObservableMap"/>
</fx:define>
<rowConstraints>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
<RowConstraints/>
</rowConstraints>
<columnConstraints>
<ColumnConstraints/>
<ColumnConstraints/>
<ColumnConstraints minWidth="10.0" prefWidth="100.0"/>
</columnConstraints>
<children>
<fx:define>
<String fx:id="str">text</String>
</fx:define>
<Button fx:id="button">
<mnemonicParsing>false</mnemonicParsing>
Button
</Button>
<CheckBox fx:id="checkBox">
<indeterminate>true</indeterminate>
<mnemonicParsing>false</mnemonicParsing>
CheckBox
<GridPane.columnIndex>1</GridPane.columnIndex>
</CheckBox>
<ChoiceBox fx:id="choiceBox" cacheShape="false" centerShape="false" disable="true" focusTraversable="false"
prefWidth="150.0"
scaleShape="false" visible="false" GridPane.rowIndex="1" accessibleText="$str"/>
<ColorPicker fx:id="colorPicker" nodeOrientation="LEFT_TO_RIGHT" opacity="0.5" GridPane.columnIndex="1"
GridPane.rowIndex="1">
<value>
<Color fx:id="color" red="0.7894737124443054" green="0.08771929889917374" blue="0.08771929889917374"/>
</value>
<opaqueInsets>
<Insets bottom="3.0" left="2.0" right="4.0" top="5.0"/>
</opaqueInsets>
</ColorPicker>
<ComboBox fx:id="comboBox" editable="true" prefWidth="150.0" promptText="Text" visibleRowCount="5"
GridPane.rowIndex="2">
<cursor>
<Cursor fx:constant="CLOSED_HAND"/>
</cursor>
<effect>
<Bloom/>
</effect>
</ComboBox>
<DatePicker fx:id="datePicker" showWeekNumbers="true" style="-fx-background-color: #ffffff;"
GridPane.columnIndex="1"
GridPane.rowIndex="2"/>
<HTMLEditor fx:id="htmlEditor"
htmlText="&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body contenteditable=&quot;true&quot;&gt;&lt;/body&gt;&lt;/html&gt;"
prefHeight="200.0" prefWidth="506.0" styleClass="clazz" stylesheets="@style.css"
GridPane.rowIndex="3"/>
<Hyperlink fx:id="hyperlink" text="Hyperlink" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
<ImageView fx:id="imageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true"
GridPane.rowIndex="4">
<Image url="https://github.com/gtache" backgroundLoading="true" fx:id="image" preserveRatio="true"
requestedWidth="200">
<smooth>true</smooth>
<requestedHeight>100</requestedHeight>
</Image>
</ImageView>
<ImageView>
<image>
<Image url="@image.png"/>
</image>
</ImageView>
<fx:define>
<Image fx:id="definedImage" url="/url"/>
</fx:define>
<ImageView>
<fx:reference source="definedImage"/>
</ImageView>
<Label fx:id="label" accessibleHelp="TTTTT" blendMode="ADD" cache="true"
cacheHint="QUALITY"
depthTest="ENABLE" mnemonicParsing="true" mouseTransparent="true" text="%include.label"
GridPane.columnIndex="1"
GridPane.rowIndex="4">
<accessibleText>
<fx:copy source="str"/>
</accessibleText>
</Label>
<ListView fx:id="listView" fixedCellSize="20.0" nodeOrientation="RIGHT_TO_LEFT" orientation="HORIZONTAL"
prefHeight="200.0"
prefWidth="200.0" GridPane.rowIndex="5"/>
<MediaView fx:id="mediaView" fitHeight="200.0" fitWidth="200.0" GridPane.columnIndex="1" GridPane.columnSpan="2"
GridPane.rowIndex="5" GridPane.rowSpan="2"/>
<MenuBar fx:id="menuBar" GridPane.halignment="RIGHT" GridPane.hgrow="ALWAYS" GridPane.rowIndex="7"
GridPane.valignment="BASELINE" GridPane.vgrow="SOMETIMES">
<Menu fx:id="menu1" mnemonicParsing="false">
<text>File</text>
<MenuItem fx:id="menuItem1" mnemonicParsing="false" text="Close"/>
<MenuItem fx:id="menuItem2" mnemonicParsing="false" text="Open"/>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete"/>
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About"/>
</items>
</Menu>
</MenuBar>
<MenuButton fx:id="menuButton" mnemonicParsing="false" text="MenuButton" GridPane.columnIndex="1"
GridPane.rowIndex="7">
<items>
<MenuItem mnemonicParsing="false" text="Action 1"/>
<MenuItem mnemonicParsing="false" text="Action 2"/>
</items>
<GridPane.margin>
<Insets bottom="3.0" left="2.0" right="4.0">
<top>5.0</top>
</Insets>
</GridPane.margin>
</MenuButton>
<Pagination fx:id="pagination" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="8">
<padding>
<Insets bottom="3.0" left="2.0" right="4.0" top="5.0"/>
</padding>
</Pagination>
<PasswordField fx:id="passwordField" maxHeight="Infinity" maxWidth="5.0" minHeight="-Infinity" minWidth="1.0"
prefColumnCount="7"
prefHeight="4.0" prefWidth="3.0" GridPane.columnIndex="1" GridPane.rowIndex="8"/>
<ProgressBar fx:id="progressBar" layoutX="10.0" layoutY="20.0" prefWidth="200.0" progress="0.0"
GridPane.rowIndex="9"/>
<ProgressIndicator fx:id="progressIndicator" progress="0.0" rotate="2.0" GridPane.columnIndex="1"
GridPane.rowIndex="9">
<rotationAxis>
<Point3D x="4.0" y="5.0" z="6.0"/>
</rotationAxis>
</ProgressIndicator>
<RadioButton fx:id="radioButton" mnemonicParsing="false" scaleX="7.0" scaleY="2.0" scaleZ="3.0"
text="RadioButton" translateX="4.0"
translateY="5.0" translateZ="6.0" GridPane.rowIndex="10"/>
<ScrollBar fx:id="scrollBarH" GridPane.columnIndex="1" GridPane.rowIndex="10"/>
<ScrollBar fx:id="scrollBarV" orientation="VERTICAL" GridPane.rowIndex="11"/>
<Separator fx:id="separatorH" onDragDetected="#dragDetected" onDragDone="#dragDone" onDragDropped="#dragDropped"
onDragEntered="#dragEntered" onDragExited="#dragExited" onDragOver="#dragOver"
onMouseDragEntered="#mouseDragEntered" onMouseDragExited="#mouseDragExited"
onMouseDragOver="#mouseDragOver" onMouseDragReleased="#mouseDragReleased" prefWidth="200.0"
GridPane.columnIndex="1" GridPane.rowIndex="11"/>
<Separator fx:id="separatorV" orientation="VERTICAL" prefHeight="200.0" GridPane.rowIndex="12"/>
<Slider fx:id="sliderH" onContextMenuRequested="#contextMenuRequested" onMouseClicked="#mouseClicked"
onMouseDragged="#mouseDragged" onMouseEntered="#mouseEntered" onMouseExited="#mouseExited"
onMouseMoved="#mouseMoved" onMousePressed="#mousePressed" onMouseReleased="#mouseReleased"
onScroll="#onScroll" onScrollFinished="#onScrollFinished" onScrollStarted="#onScrollStarted"
GridPane.columnIndex="1" GridPane.rowIndex="12"/>
<Slider fx:id="sliderV" onZoom="#onZoom" onZoomFinished="#onZoomFinished" onZoomStarted="#onZoomStarted"
orientation="VERTICAL"
GridPane.rowIndex="13"/>
<Spinner fx:id="spinner" GridPane.columnIndex="1" GridPane.rowIndex="13"/>
<SplitMenuButton fx:id="splitMenuButton" mnemonicParsing="false" text="SplitMenuButton" GridPane.rowIndex="14">
<items>
<MenuItem fx:id="item1" mnemonicParsing="false" text="Action 1"/>
<MenuItem fx:id="item2" mnemonicParsing="false" text="Action 2"/>
</items>
</SplitMenuButton>
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"
GridPane.rowIndex="14">
<columns>
<TableColumn fx:id="tableColumn1" prefWidth="75.0" text="C1"/>
<TableColumn fx:id="tableColumn2" prefWidth="75.0" text="C2"/>
</columns>
</TableView>
<TextArea fx:id="textArea" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="15">
<font>
<Font fx:id="font" name="Arial" size="12.0"/>
</font>
</TextArea>
<TextField fx:id="textField" GridPane.columnIndex="1" GridPane.rowIndex="15"/>
<ToggleButton mnemonicParsing="false" onAction="#onAction" onRotate="#onRotate"
onRotationFinished="#onRotationFinished" onRotationStarted="#onRotationStarted"
text="ToggleButton" GridPane.rowIndex="16"/>
<TreeTableView fx:id="treeTableView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"
GridPane.rowIndex="16">
<columns>
<TreeTableColumn fx:id="treeTableColumn1" onEditCancel="#onEditCancel" onEditCommit="#onEditCommit"
onEditStart="#onEditStart"
prefWidth="75.0" text="C1"/>
<TreeTableColumn fx:id="treeTableColumn2" prefWidth="75.0" sortType="DESCENDING" text="C2"/>
</columns>
</TreeTableView>
<TreeView fx:id="treeView" onSwipeDown="#onSwipeDown" onSwipeLeft="#onSwipeLeft" onSwipeRight="#onSwipeRight"
onSwipeUp="#onSwipeUp" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="17"/>
<WebView fx:id="webView" onTouchMoved="#onTouchMoved" onTouchPressed="#onTouchPressed"
onTouchReleased="#onTouchReleased"
onTouchStationary="#onTouchStationary" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"
location="https://github.com/gtache" confirmHandler="$controller.confirmHandler"
createPopupHandler="#createPopupHandler"
onAlert="$controller.onAlert" onResized="#onResized"
GridPane.rowIndex="17"/>
</children>
</GridPane>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.parsing.xml.IncludesController">
<fx:include source="include1.fxml"/>
<fx:include source="include2.fxml"/>
<fx:include source="include3.fxml"/>
<fx:include resources="com/github/gtache/fxml/compiler/parsing/xml/includes/IncludedBundle" source="include1.fxml"/>
</HBox>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.parsing.xml.IncludesController">
<fx:include source="include21.fxml"/>
</HBox>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.parsing.xml.IncludesController">
<Label text="%text"/>
</HBox>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.parsing.xml.IncludesController">
<fx:include source="include1.fxml"/>
<fx:include source="include2.fxml"/>
<fx:include source="include3.fxml"/>
<fx:include resources="com/github/gtache/fxml/compiler/parsing/xml/includes/IncludedBundle" source="include1.fxml"/>
</HBox>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.parsing.xml.IncludesController">
<fx:include source="include1.fxml"/>
<fx:include source="include2.fxml"/>
<fx:include source="include3.fxml"/>
<fx:include resources="com/github/gtache/fxml/compiler/parsing/xml/includes/IncludedBundle" source="include1.fxml"/>
</HBox>

View File

@@ -1,141 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.TextFlow?>
<BorderPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.parsing.xml.IncludesController">
<bottom>
<VBox BorderPane.alignment="CENTER">
<children>
<HBox alignment="CENTER" spacing="10.0">
<children>
<Slider fx:id="playSlider" HBox.hgrow="ALWAYS">
<padding>
<Insets left="10.0"/>
</padding>
</Slider>
<Label fx:id="playLabel" text="Label">
<padding>
<Insets right="10.0"/>
</padding>
</Label>
</children>
<padding>
<Insets top="10.0"/>
</padding>
</HBox>
<HBox alignment="CENTER" spacing="10.0">
<children>
<Button fx:id="playButton" mnemonicParsing="false" onAction="#playPressed">
<HBox.margin>
<Insets right="20.0"/>
</HBox.margin>
</Button>
<Label text="%media.volume.label"/>
<Slider fx:id="volumeSlider" value="100"/>
<Label fx:id="volumeValueLabel" text="Label"/>
<fx:include fx:id="controls" source="controlsView.fxml"
resources="com/github/gtache/fxml/compiler/parsing/xml/ControlsBundle"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</HBox>
</children>
</VBox>
</bottom>
<center>
<VBox fx:id="vbox">
<children>
<ToolBar fx:id="toolBar">
<items>
<TitledPane fx:id="titledPane">
<content>
<TilePane fx:id="tilePane">
<children>
<TextFlow fx:id="textFlow">
<children>
<TabPane fx:id="tabPane">
<tabs>
<Tab fx:id="tab">
<content>
<StackPane fx:id="stackPane">
<children>
<SplitPane fx:id="splitPane">
<items>
<ScrollPane fx:id="scrollPane">
<content>
<Pane fx:id="pane">
<children>
<HBox fx:id="hbox">
<children>
<Group fx:id="group">
<children>
<GridPane
fx:id="gridPane">
<columnConstraints>
<ColumnConstraints
fx:id="columnConstraints"
hgrow="SOMETIMES"
minWidth="10.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints
minHeight="10.0"
vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<FlowPane
fx:id="flowPane">
<children>
<DialogPane
fx:id="dialogPane">
<content>
<ButtonBar
fx:id="buttonBar">
<buttons>
<AnchorPane
fx:id="anchorPane">
<children>
<Label managed="false"/>
</children>
</AnchorPane>
</buttons>
</ButtonBar>
</content>
</DialogPane>
</children>
</FlowPane>
</children>
</GridPane>
</children>
</Group>
</children>
</HBox>
</children>
</Pane>
</content>
</ScrollPane>
</items>
</SplitPane>
</children>
</StackPane>
</content>
</Tab>
</tabs>
</TabPane>
</children>
</TextFlow>
</children>
</TilePane>
</content>
</TitledPane>
</items>
</ToolBar>
</children>
</VBox>
</center>
</BorderPane>

View File

@@ -1,3 +0,0 @@
.clazz {
-fx-font-weight: bold;
}