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
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
+32 -88
View File
@@ -6,8 +6,9 @@ This projects aims at generating Java code from FXML files.
## Requirements
- Java 21 (at least for the plugin, the generated code can be compatible with older Java versions)
- Maven 3.8.0
- Java 21+ for the plugin
- The generated code can be compatible with older java versions.
- Maven 3.6.3+
## Installation
@@ -79,100 +80,55 @@ Optionally add dependencies to the plugin (e.g. when using MediaView and control
## Disadvantages
- `fx:script` is not supported
- Possible bugs (file an issue if you see one)
- Expression binding is limited
- Probably not fully compatible with all FXML features (file an issue if you need one in specific)
## Parameters
### 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
There are four ways to inject fields into a controller:
- `REFLECTION`: Inject fields using reflection (like FXMLLoader)
-
- ```java
try {
final var field = controller.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(controller, object);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException("Error using reflection on " + fieldName, e);
}
```
-
- Slowest method
-
- Fully compatible with FXMLLoader, so this allows easy switching between the two.
-
- This is the default injection method (for compatibility reasons).
- `ASSIGN`: variable assignment
-
- `controller.field = value`
-
- This means that the field must be accessible from the view (e.g. package-private).
- `SETTERS`: controller setters methods
-
- `controller.setField(value)`
- `FACTORY`: controller factory
-
- `controller = factory.create(fieldMap)`
-
- `factory` is a `ControllerFactory` instance that is created at runtime and passed to the view.
-
- `factory` is a `Function<Map<String, Object>, Controller>` instance that is created at runtime and passed to the
view.
- `fieldMap` is a map of field name (String) to value (Object) that is computed during the view `load` method.
-
- This allows the controller to have final fields.
- This also forces the `controller-injection` method to be `FACTORY`.
### Method injections
There are two ways to inject methods (meaning use them as event handlers) into a controller:
- `REFLECTION`: Inject methods using reflection (like FXMLLoader)
-
- ```java
try {
final java.lang.reflect.Method method;
final var methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
final var eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0])).toList();
if (eventMethods.size() == 1) {
method = eventMethods.getFirst();
} else {
final var emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList();
if (emptyMethods.size() == 1) {
method = emptyMethods.getFirst();
} else {
throw new IllegalArgumentException("Multiple matching methods for " + methodName);
}
}
} else if (methods.size() == 1) {
method = methods.getFirst();
} else {
throw new IllegalArgumentException("No matching method for " + methodName);
}
method.setAccessible(true);
if (method.getParameterCount() == 0) {
method.invoke(controller);
} else {
method.invoke(controller, event);
}
} catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
throw new RuntimeException("Error using reflection on " + methodName, ex);
}
```
-
- Slowest method
-
- Fully compatible with FXMLLoader, so this allows easy switching between the two.
-
- This is the default injection method (for compatibility reasons).
- `REFERENCE`: Directly reference the method
-
- `controller.method(event)`
-
- This means that the method must be accessible from the view (e.g. package-private).
### Resource bundle injection
@@ -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:
- `CONSTRUCTOR`: Inject resource bundle in the view constructor
-
- ```java
view = new View(controller, resourceBundle);
```
-
- This is the default injection method because it is the most similar to FXMLLoader (
`FXMLLoader.setResources(resourceBundle)`).
- `CONSTRUCTOR_FUNCTION`: Injects a function in the view constructor
-
- `bundleFunction.apply(key)`
-
- The function takes a string (the key) and returns a string (the value)
- This allows using another object than a resource bundle for example
- `GETTER`: Retrieves the resource bundle using a controller getter method
-
- `controller.resources()`
-
- The method name (resources) was chosen because it matches the name of the field injected by FXMLLoader.
-
- The method must be accessible from the view (e.g. package-private).
- `GET-BUNDLE`: Injects the bundle name in the view constructor and retrieves it using
`ResourceBundle.getBundle(bundleName)`
-
- `CONSTRUCTOR_NAME`: Injects the resource bundle name in the view constructor
- `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
@@ -229,47 +177,43 @@ The smallest constructor will have only one argument: The controller (or control
### Parameters
- output-directory
-
- The output directory of the generated classes
-
- default: `${project.build.directory}/generated-sources/java`)
- target-version
-
- The target Java version for the generated code
- default: `21`
- minimum: `8`
- File an issue if the generated code is not compatible with the target version
- use-image-inputstream-constructor
-
- Use the InputStream constructor for Image instead of the String (URL) one.
-
- default: `true`
- Disables background loading
- controller-injection
- The type of controller injections to use (see [Controller injection](#controller-injection))
- default: `INSTANCE`
- field-injection
-
- The type of field injections to use (see [Field injection](#field-injection))
- default: `REFLECTION`
- method-injection
-
- The type of method injections to use (see [Method injection](#method-injection))
- default: `REFLECTION`
- bundle-injection
-
- The type of resource bundle injection to use (see [Resource bundle injection](#resource-bundle-injection))
- default: `CONSTRUCTOR`
- bundle-map
-
- A map of resource bundle name to resource bundle path
- Used with `GET-BUNDLE` injection
- default: `{}`
- parallelism
- The number of threads to use for compilation
- default: `1`
- if `<1`, the number of cores will be used
### Limitations
- Given that the plugin operates during the `generate-sources` phase, it doesn't have access to the classes of the
application.
-
- The controller info (fields, methods) is obtained from the source file and may therefore be inaccurate.
-
- Custom classes instantiated in the FXML files are not available during generation and may therefore cause it to
fail.
- If the application uses e.g. WebView, the javafx-web dependency must be added to the plugin dependencies.
@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
package com.github.gtache.fxml.compiler;
/**
* Base field {@link InjectionType}s
*/
public enum ControllerFieldInjectionTypes implements InjectionType {
public enum ControllerFieldInjectionType implements InjectionType {
/**
* Inject using variable assignment
*/
@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
package com.github.gtache.fxml.compiler;
/**
* Base controller {@link InjectionType}s
*/
public enum ControllerInjectionTypes implements InjectionType {
public enum ControllerInjectionType implements InjectionType {
/**
* Inject the controller instance
*/
@@ -1,6 +1,4 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
package com.github.gtache.fxml.compiler;
/**
* Base methods {@link InjectionType}s
@@ -36,26 +36,26 @@ public interface GenerationParameters {
*
* @return The injection
*/
InjectionType controllerInjectionType();
ControllerInjectionType controllerInjectionType();
/**
* Returns the field injection to use
*
* @return The injection
*/
InjectionType fieldInjectionType();
ControllerFieldInjectionType fieldInjectionType();
/**
* Returns the method injection to use
*
* @return The injection
*/
InjectionType methodInjectionType();
ControllerMethodsInjectionType methodInjectionType();
/**
* Returns the resource injection to use
*
* @return The injection
*/
InjectionType resourceInjectionType();
ResourceBundleInjectionType resourceInjectionType();
}
@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
package com.github.gtache.fxml.compiler;
/**
* Base {@link InjectionType}s for resource bundles
*/
public enum ResourceBundleInjectionTypes implements InjectionType {
public enum ResourceBundleInjectionType implements InjectionType {
/**
* Resource bundle is injected in the constructor
*/
@@ -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());
}
}
@@ -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());
}
}
@@ -18,6 +18,14 @@ public record ControllerInfoImpl(String className, Map<String, Boolean> handlerH
Map<String, ControllerFieldInfo> fieldInfo,
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 {
Objects.requireNonNull(className);
handlerHasArgument = Map.copyOf(handlerHasArgument);
@@ -1,7 +1,10 @@
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.InjectionType;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import java.util.Map;
@@ -22,11 +25,23 @@ import static java.util.Objects.requireNonNull;
*/
public record GenerationParametersImpl(GenerationCompatibility compatibility, boolean useImageInputStreamConstructor,
Map<String, String> bundleMap,
InjectionType controllerInjectionType,
InjectionType fieldInjectionType,
InjectionType methodInjectionType,
InjectionType resourceInjectionType) implements GenerationParameters {
ControllerInjectionType controllerInjectionType,
ControllerFieldInjectionType fieldInjectionType,
ControllerMethodsInjectionType methodInjectionType,
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 {
requireNonNull(compatibility);
bundleMap = Map.copyOf(bundleMap);
@@ -20,6 +20,15 @@ import java.util.Objects;
public record GenerationRequestImpl(GenerationParameters parameters, ControllerInfo controllerInfo,
SourceInfo sourceInfo, ParsedObject rootObject,
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 {
Objects.requireNonNull(parameters);
Objects.requireNonNull(controllerInfo);
@@ -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.HelperProvider;
import java.util.Objects;
import java.util.function.Function;
//TODO handle binding (${})
/**
@@ -13,10 +16,28 @@ import com.github.gtache.fxml.compiler.impl.internal.HelperProvider;
*/
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
public String generate(final GenerationRequest request) throws GenerationException {
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 pkgName = className.substring(0, className.lastIndexOf('.'));
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) {
final var sb = progress.stringBuilder();
sb.append(" /**\n");
sb.append(" * Returns the controller if available\n");
sb.append(" * @return The controller\n");
sb.append(" * @throws IllegalStateException If the view is not loaded\n");
sb.append(" */\n");
sb.append(" public ").append(controllerInjectionClass).append(" controller() {\n");
sb.append(" if (loaded) {\n");
@@ -22,6 +22,16 @@ public record SourceInfoImpl(String generatedClassName, String controllerClassNa
Map<String, SourceInfo> sourceToSourceInfo,
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 {
Objects.requireNonNull(generatedClassName);
Objects.requireNonNull(controllerClassName);
@@ -9,7 +9,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
@@ -17,10 +16,8 @@ import java.util.Set;
*/
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
* @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 valueFormatter = helperProvider.getValueFormatter();
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());
if (p == null) {
final var c = parsedObject.properties().entrySet().stream().filter(e ->
e.getKey().name().equals(entry.getKey())).findFirst().orElse(null);
if (c == null) {
args.add(valueFormatter.toString(entry.getValue().defaultValue(), type));
args.add(ValueFormatter.toString(parameter.defaultValue(), type));
} else {
throw new GenerationException("Constructor using complex property not supported yet");
}
} else {
args.add(valueFormatter.toString(p.value(), type));
args.add(ValueFormatter.toString(p.value(), type));
}
}
return args;
@@ -69,7 +66,8 @@ final class ConstructorHelper {
}
}
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 {
return matchingConstructorArgs;
}
@@ -83,6 +81,10 @@ final class ConstructorHelper {
* @return The number of matching arguments
*/
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();
}
}
}
@@ -1,10 +1,8 @@
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.GenerationException;
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.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
@@ -18,14 +16,15 @@ import static java.util.Objects.requireNonNull;
final class ControllerInjector {
private final ControllerInfo controllerInfo;
private final InjectionType fieldInjectionType;
private final InjectionType methodInjectionType;
private final ControllerFieldInjectionType fieldInjectionType;
private final ControllerMethodsInjectionType methodInjectionType;
private final StringBuilder sb;
private final SequencedCollection<String> controllerFactoryPostAction;
ControllerInjector(final ControllerInfo controllerInfo, final InjectionType fieldInjectionType, final InjectionType methodInjectionType,
final StringBuilder sb, final SequencedCollection<String> controllerFactoryPostAction) {
this.controllerInfo = controllerInfo;
ControllerInjector(final ControllerInfo controllerInfo, final ControllerFieldInjectionType fieldInjectionType,
final ControllerMethodsInjectionType methodInjectionType, final StringBuilder sb,
final SequencedCollection<String> controllerFactoryPostAction) {
this.controllerInfo = requireNonNull(controllerInfo);
this.fieldInjectionType = requireNonNull(fieldInjectionType);
this.methodInjectionType = requireNonNull(methodInjectionType);
this.sb = requireNonNull(sb);
@@ -37,23 +36,18 @@ final class ControllerInjector {
*
* @param id The object id
* @param variable The object variable
* @throws GenerationException if an error occurs
*/
void injectControllerField(final String id, final String variable) throws GenerationException {
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes types) {
switch (types) {
case FACTORY ->
sb.append(" fieldMap.put(\"").append(id).append("\", ").append(variable).append(");\n");
case ASSIGN -> sb.append(" controller.").append(id).append(" = ").append(variable).append(";\n");
case SETTERS -> {
final var setMethod = GenerationHelper.getSetMethod(id);
sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
}
case REFLECTION ->
sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
void injectControllerField(final String id, final String variable) {
switch (fieldInjectionType) {
case FACTORY ->
sb.append(" fieldMap.put(\"").append(id).append("\", ").append(variable).append(");\n");
case ASSIGN -> sb.append(" controller.").append(id).append(" = ").append(variable).append(";\n");
case SETTERS -> {
final var setMethod = GenerationHelper.getSetMethod(id);
sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
}
} else {
throw new GenerationException("Unknown controller injection type : " + fieldInjectionType);
case REFLECTION ->
sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
}
}
@@ -62,9 +56,8 @@ final class ControllerInjector {
*
* @param property The property to inject
* @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));
}
@@ -74,9 +67,8 @@ final class ControllerInjector {
* @param property The property to inject
* @param parentVariable The parent variable
* @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));
}
@@ -84,16 +76,11 @@ final class ControllerInjector {
* Injects a controller method
*
* @param methodInjection The method injection
* @throws GenerationException if an error occurs
*/
private void injectControllerMethod(final String methodInjection) throws GenerationException {
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) {
switch (fieldTypes) {
case FACTORY -> controllerFactoryPostAction.add(methodInjection);
case ASSIGN, SETTERS, REFLECTION -> sb.append(methodInjection);
}
} else {
throw getUnknownInjectionException(fieldInjectionType);
private void injectControllerMethod(final String methodInjection) {
switch (fieldInjectionType) {
case FACTORY -> controllerFactoryPostAction.add(methodInjection);
case ASSIGN, SETTERS, REFLECTION -> sb.append(methodInjection);
}
}
@@ -103,27 +90,22 @@ final class ControllerInjector {
* @param property The property
* @param parentVariable The parent variable
* @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 controllerMethod = property.value().replace("#", "");
if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) {
return switch (methodTypes) {
case REFERENCE -> {
final var hasArgument = controllerInfo.handlerHasArgument(controllerMethod);
if (hasArgument) {
yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
} else {
yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
}
return switch (methodInjectionType) {
case REFERENCE -> {
final var hasArgument = controllerInfo.handlerHasArgument(controllerMethod);
if (hasArgument) {
yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
} else {
yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
}
case REFLECTION ->
" " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n";
};
} else {
throw getUnknownInjectionException(methodInjectionType);
}
}
case REFLECTION ->
" " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n";
};
}
/**
@@ -133,24 +115,15 @@ final class ControllerInjector {
* @param parentVariable The parent variable
* @param argumentClazz The argument class
* @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 controllerMethod = property.value().replace("#", "");
if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) {
return switch (methodTypes) {
case REFERENCE ->
" " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
case REFLECTION ->
" " + 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);
return switch (methodInjectionType) {
case REFERENCE ->
" " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
case REFLECTION ->
" " + parentVariable + "." + setMethod + "(e -> callCallbackMethod(\"" + controllerMethod + "\", e, " + argumentClazz + "));\n";
};
}
}
@@ -1,27 +1,27 @@
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.InjectionType;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import java.util.Objects;
import java.util.SequencedCollection;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.EXPRESSION_PREFIX;
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 {
private final HelperProvider helperProvider;
private final InjectionType fieldInjectionType;
private final ControllerFieldInjectionType fieldInjectionType;
private final StringBuilder sb;
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) {
this.helperProvider = requireNonNull(helperProvider);
this.fieldInjectionType = requireNonNull(fieldInjectionType);
@@ -34,7 +34,7 @@ final class FieldSetter {
*
* @param property The property to inject
* @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 {
setField(property, parentVariable, "javafx.event.EventHandler");
@@ -50,54 +50,66 @@ final class FieldSetter {
* @throws GenerationException if an error occurs
*/
void setField(final ParsedProperty property, final String parentVariable, final String fieldType) throws GenerationException {
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) {
switch (fieldTypes) {
case ASSIGN -> setAssign(property, parentVariable);
case FACTORY -> setFactory(property, parentVariable);
case SETTERS -> setSetter(property, parentVariable);
case REFLECTION -> setReflection(property, parentVariable, fieldType);
}
} else {
throw new GenerationException("Unknown injection type : " + fieldInjectionType);
switch (fieldInjectionType) {
case ASSIGN -> setAssign(property, parentVariable);
case FACTORY -> setFactory(property, parentVariable);
case REFLECTION -> setReflection(property, parentVariable, fieldType);
case SETTERS -> setSetter(property, parentVariable);
}
}
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 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));
}
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));
}
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 value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\.");
final var getterName = GenerationHelper.getGetMethod(split[1]);
return " " + parentVariable + "." + methodName + "(" + split[0] + "." + getterName + ");\n";
final var holderName = split[0];
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 value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\.");
final var fieldName = split[1];
sb.append(" try {\n");
sb.append(" ").append(helperProvider.getCompatibilityHelper().getStartVar("java.lang.reflect.Field", 0))
.append("field = controller.getClass().getDeclaredField(\"").append(fieldName).append("\");\n");
sb.append(" field.setAccessible(true);\n");
sb.append(" final var value = (").append(fieldType).append(") field.get(controller);\n");
sb.append(" ").append(parentVariable).append(".").append(methodName).append("(value);\n");
sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(e);\n");
sb.append(" }\n");
final var holderName = split[0];
if (Objects.equals(holderName, "controller")) {
final var fieldName = split[1];
sb.append(" try {\n");
sb.append(" ").append(helperProvider.getCompatibilityHelper().getStartVar("java.lang.reflect.Field", 0))
.append("field = controller.getClass().getDeclaredField(\"").append(fieldName).append("\");\n");
sb.append(" field.setAccessible(true);\n");
sb.append(" final var value = (").append(fieldType).append(") field.get(controller);\n");
sb.append(" ").append(parentVariable).append(".").append(methodName).append("(value);\n");
sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(e);\n");
sb.append(" }\n");
} else {
throw new GenerationException("Unexpected variable holder : " + holderName + " ; expected : controller");
}
}
}
@@ -41,14 +41,17 @@ final class FontFormatter {
final var fp = value.fontPosture();
final var size = value.size();
final var name = value.name();
if (url != null) {
formatURL(url, size, variableName);
} else if (fw == null && fp == null) {
formatNoStyle(name, size, variableName);
if (url == null) {
if (name == null) {
throw new GenerationException("Font must have a name or url : " + parsedObject);
} else if (fw == null && fp == null) {
formatNoStyle(name, size, variableName);
} else {
formatStyle(fw, fp, size, name, variableName);
}
} else {
formatStyle(fw, fp, size, name, variableName);
formatURL(url, size, variableName);
}
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} else {
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) {
final var urlVariableName = helperProvider.getURLFormatter().formatURL(url.toString());
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(" } catch (final java.io.IOException e) {\n");
sb.append(" throw new RuntimeException(e);\n");
@@ -1,7 +1,5 @@
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.parsing.ParsedObject;
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.Map;
import static java.util.Objects.requireNonNull;
/**
* Various helper methods for {@link GeneratorImpl}
*/
@@ -31,42 +27,8 @@ final class GenerationHelper {
static final String BINDING_EXPRESSION_PREFIX = "${";
static final String BI_DIRECTIONAL_BINDING_PREFIX = "#{";
private final HelperProvider helperProvider;
private final ControllerInfo controllerInfo;
private final Map<String, VariableInfo> idToVariableInfo;
private GenerationHelper() {
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));
}
}
/**
@@ -4,27 +4,17 @@ import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.SequencedCollection;
import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Used by {@link GeneratorImpl} to track the generation progress
*
* @param request The generation request
* @param idToVariableInfo The id to variable info
* @param variableNameCounters The variable name counters for variable name generation
* @param controllerClassToVariable The controller class to variable mapping
* @param controllerFactoryPostAction The controller factory post action for factory injection
* @param stringBuilder The string builder
*/
public record GenerationProgress(GenerationRequest request, Map<String, VariableInfo> idToVariableInfo,
Map<String, AtomicInteger> variableNameCounters,
SequencedMap<String, String> controllerClassToVariable,
public record GenerationProgress(GenerationRequest request,
SequencedCollection<String> controllerFactoryPostAction,
StringBuilder stringBuilder) {
@@ -32,18 +22,12 @@ public record GenerationProgress(GenerationRequest request, Map<String, Variable
* Instantiates a new GenerationProgress
*
* @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 stringBuilder The string builder
* @throws NullPointerException if any parameter is null
*/
public GenerationProgress {
Objects.requireNonNull(request);
Objects.requireNonNull(idToVariableInfo);
Objects.requireNonNull(variableNameCounters);
Objects.requireNonNull(controllerClassToVariable);
Objects.requireNonNull(controllerFactoryPostAction);
Objects.requireNonNull(stringBuilder);
}
@@ -55,17 +39,6 @@ public record GenerationProgress(GenerationRequest request, Map<String, Variable
* @throws NullPointerException if request is null
*/
public GenerationProgress(final GenerationRequest request) {
this(request, new HashMap<>(), new HashMap<>(), new LinkedHashMap<>(), 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();
this(request, new ArrayList<>(), new StringBuilder());
}
}
@@ -1,8 +1,7 @@
package com.github.gtache.fxml.compiler.impl.internal;
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.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import java.util.Objects;
@@ -12,11 +11,12 @@ import java.util.Objects;
public final class HelperMethodsFormatter {
private final HelperProvider helperProvider;
private final InjectionType fieldInjectionType;
private final InjectionType methodInjectionType;
private final ControllerFieldInjectionType fieldInjectionType;
private final ControllerMethodsInjectionType methodInjectionType;
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.fieldInjectionType = Objects.requireNonNull(fieldInjectionType);
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(" if (methods.size() > 1) {\n");
sb.append(" ").append(startVariableMethodList).append("eventMethods = methods.stream().filter(m ->\n");
sb.append(" m.getParameterCount() == 1 && clazz.isAssignableFrom(m.getParameterTypes()[0]))").append(toList).append(";\n");
sb.append(" m.getParameterCount() == 2 && clazz.isAssignableFrom(m.getParameterTypes()[1]))").append(toList).append(";\n");
sb.append(" if (eventMethods.size() == 1) {\n");
sb.append(" method = eventMethods").append(getFirst).append(";\n");
sb.append(" } else {\n");
@@ -91,7 +91,7 @@ public final class HelperMethodsFormatter {
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(" try {\n");
sb.append(" ").append(compatibilityHelper.getStartVar("java.lang.reflect.Field", 0)).append("field = controller.getClass().getDeclaredField(fieldName);\n");
@@ -14,6 +14,7 @@ public class HelperProvider {
/**
* Instantiates a new helper provider
*
* @param progress The generation progress
*/
public HelperProvider(final GenerationProgress progress) {
@@ -21,58 +22,66 @@ public class HelperProvider {
this.helpers = new HashMap<>();
}
ConstructorHelper getConstructorHelper() {
return (ConstructorHelper) helpers.computeIfAbsent(ConstructorHelper.class, c -> new ConstructorHelper(this));
}
ControllerInjector getControllerInjector() {
final var request = progress.request();
final var controllerInfo = request.controllerInfo();
final var parameters = request.parameters();
final var fieldInjectionType = parameters.fieldInjectionType();
final var methodInjectionType = parameters.methodInjectionType();
final var sb = progress.stringBuilder();
final var controllerFactoryPostAction = progress.controllerFactoryPostAction();
return (ControllerInjector) helpers.computeIfAbsent(ControllerInjector.class, c -> new ControllerInjector(controllerInfo, fieldInjectionType, methodInjectionType, sb, controllerFactoryPostAction));
return (ControllerInjector) helpers.computeIfAbsent(ControllerInjector.class, c -> {
final var request = progress.request();
final var controllerInfo = request.controllerInfo();
final var parameters = request.parameters();
final var fieldInjectionType = parameters.fieldInjectionType();
final var methodInjectionType = parameters.methodInjectionType();
final var sb = progress.stringBuilder();
final var controllerFactoryPostAction = progress.controllerFactoryPostAction();
return new ControllerInjector(controllerInfo, fieldInjectionType, methodInjectionType, sb, controllerFactoryPostAction);
});
}
FieldSetter getFieldSetter() {
final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
final var sb = progress.stringBuilder();
final var controllerFactoryPostAction = progress.controllerFactoryPostAction();
return (FieldSetter) helpers.computeIfAbsent(FieldSetter.class, c -> new FieldSetter(this, fieldInjectionType, sb, controllerFactoryPostAction));
return (FieldSetter) helpers.computeIfAbsent(FieldSetter.class, c -> {
final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
final var sb = progress.stringBuilder();
final var controllerFactoryPostAction = progress.controllerFactoryPostAction();
return new FieldSetter(this, fieldInjectionType, sb, controllerFactoryPostAction);
});
}
FontFormatter getFontFormatter() {
final var sb = progress.stringBuilder();
return (FontFormatter) helpers.computeIfAbsent(FontFormatter.class, c -> new FontFormatter(this, sb));
return (FontFormatter) helpers.computeIfAbsent(FontFormatter.class, c -> {
final var sb = progress.stringBuilder();
return new FontFormatter(this, sb);
});
}
GenerationCompatibilityHelper getCompatibilityHelper() {
final var compatibility = progress.request().parameters().compatibility();
return (GenerationCompatibilityHelper) helpers.computeIfAbsent(GenerationCompatibilityHelper.class, c -> 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));
return (GenerationCompatibilityHelper) helpers.computeIfAbsent(GenerationCompatibilityHelper.class, c -> {
final var compatibility = progress.request().parameters().compatibility();
return new GenerationCompatibilityHelper(this, compatibility);
});
}
public HelperMethodsFormatter getHelperMethodsFormatter() {
final var parameters = progress.request().parameters();
final var fieldInjectionType = parameters.fieldInjectionType();
final var methodInjectionType = parameters.methodInjectionType();
final var sb = progress.stringBuilder();
return (HelperMethodsFormatter) helpers.computeIfAbsent(HelperMethodsFormatter.class, c -> new HelperMethodsFormatter(this, fieldInjectionType, methodInjectionType, sb));
return (HelperMethodsFormatter) helpers.computeIfAbsent(HelperMethodsFormatter.class, c -> {
final var parameters = progress.request().parameters();
final var fieldInjectionType = parameters.fieldInjectionType();
final var methodInjectionType = parameters.methodInjectionType();
final var sb = progress.stringBuilder();
return new HelperMethodsFormatter(this, fieldInjectionType, methodInjectionType, sb);
});
}
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() {
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() {
@@ -80,7 +89,11 @@ public class HelperProvider {
}
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() {
@@ -88,30 +101,48 @@ public class HelperProvider {
}
ReflectionHelper getReflectionHelper() {
final var controllerInfo = progress.request().controllerInfo();
return (ReflectionHelper) helpers.computeIfAbsent(ReflectionHelper.class, c -> new ReflectionHelper(controllerInfo));
return (ReflectionHelper) helpers.computeIfAbsent(ReflectionHelper.class, c -> {
final var controllerInfo = progress.request().controllerInfo();
return new ReflectionHelper(controllerInfo);
});
}
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() {
final var sb = progress.stringBuilder();
return (TriangleMeshFormatter) helpers.computeIfAbsent(TriangleMeshFormatter.class, c -> new TriangleMeshFormatter(this, sb));
return (TriangleMeshFormatter) helpers.computeIfAbsent(TriangleMeshFormatter.class, c -> {
final var sb = progress.stringBuilder();
return new TriangleMeshFormatter(this, sb);
});
}
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() {
final var resourceInjectionType = progress.request().parameters().resourceInjectionType();
final var idToVariableInfo = progress.idToVariableInfo();
return (ValueFormatter) helpers.computeIfAbsent(ValueFormatter.class, c -> new ValueFormatter(resourceInjectionType, idToVariableInfo));
return (ValueFormatter) helpers.computeIfAbsent(ValueFormatter.class, c -> {
final var resourceInjectionType = progress.request().parameters().resourceInjectionType();
return new ValueFormatter(this, resourceInjectionType);
});
}
VariableProvider getVariableProvider() {
return (VariableProvider) helpers.computeIfAbsent(VariableProvider.class, c -> new VariableProvider());
}
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);
});
}
}
@@ -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.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.getSortedAttributes;
import static java.util.Objects.requireNonNull;
/**
* 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 {
private final HelperProvider helperProvider;
private final GenerationProgress progress;
private final StringBuilder sb;
private final boolean useImageInputStreamConstructor;
ImageFormatter(final HelperProvider helperProvider, final GenerationProgress progress) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.progress = Objects.requireNonNull(progress);
ImageFormatter(final HelperProvider helperProvider, final StringBuilder sb, final boolean useImageInputStreamConstructor) {
this.helperProvider = requireNonNull(helperProvider);
this.sb = requireNonNull(sb);
this.useImageInputStreamConstructor = useImageInputStreamConstructor;
}
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 {
final var sortedAttributes = getSortedAttributes(parsedObject);
String url = null;
@@ -65,20 +53,32 @@ final class ImageFormatter {
default -> throw new GenerationException("Unknown image attribute : " + property.name());
}
}
if (progress.request().parameters().useImageInputStreamConstructor()) {
if (url == null) {
throw new GenerationException("Image must have a url attribute : " + parsedObject);
}
if (useImageInputStreamConstructor) {
formatInputStream(url, requestedWidth, requestedHeight, preserveRatio, smooth, variableName);
} else {
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,
final double requestedHeight, final boolean preserveRatio, final boolean smooth,
final boolean backgroundLoading, final String variableName) {
final var urlString = progress.getNextVariableName("urlStr");
final var sb = progress.stringBuilder();
final var urlString = helperProvider.getVariableProvider().getNextVariableName("urlStr");
final var compatibilityHelper = helperProvider.getCompatibilityHelper();
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)
@@ -1,51 +1,75 @@
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.GenerationRequest;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.impl.ControllerInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/**
* Utility class to provide the view's constructor and fields
*/
public final class InitializationFormatter {
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) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.progress = Objects.requireNonNull(progress);
InitializationFormatter(final HelperProvider helperProvider, final GenerationRequest request, final StringBuilder sb) {
this(helperProvider, request, sb, new HashMap<>());
}
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 {
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 mainControllerClass = progress.request().controllerInfo().className();
final var isFactory = progress.request().parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY;
final var mainControllerClass = request.controllerInfo().className();
final var parameters = request.parameters();
final var controllerInjectionType = parameters.controllerInjectionType();
final var fieldInjectionType = parameters.fieldInjectionType();
final var isFactory = controllerInjectionType == ControllerInjectionType.FACTORY;
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();
final var sb = progress.stringBuilder();
final var controllerMap = progress.controllerClassToVariable();
controllerMap.forEach((c, v) -> sb.append(" private final ").append(c).append(" ").append(v).append(isFactory ? "Factory" : "").append(";\n"));
final var sortedControllersKeys = controllerClassToVariable.keySet().stream().sorted().toList();
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 resourceBundleType = resourceBundleInfo.type();
final var resourceBundleArg = resourceBundleInfo.variableName();
if (isFactory) {
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");
} else {
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) {
sb.append(" private final ").append(resourceBundleType).append(" ").append(resourceBundleArg).append(";\n");
@@ -55,49 +79,49 @@ public final class InitializationFormatter {
sb.append(" /**\n");
sb.append(" * Instantiates a new ").append(simpleClassName).append("\n");
sb.append(" * @param ").append(controllerArg).append(" The controller ").append(isFactory ? "factory" : "instance").append("\n");
controllerMap.forEach((c, s) -> sb.append(" * @param ").append(getVariableName(s, isFactory))
.append(" The subcontroller ").append(isFactory ? "factory" : "instance").append(" for ").append(c).append("\n"));
sortedControllersKeys.forEach(e -> sb.append(" * @param ").append(getVariableName(controllerClassToVariable.get(e), isFactory))
.append(" The subcontroller ").append(isFactory ? "factory" : "instance").append(" for ").append(e).append("\n"));
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");
final var arguments = "final " + controllerArgClass + " " + controllerArg +
((controllerMap.isEmpty()) ? "" : ", ") +
controllerMap.entrySet().stream().map(e -> "final " + getType(e.getKey(), isFactory) + " " + getVariableName(e.getValue(), isFactory))
((sortedControllersKeys.isEmpty()) ? "" : ", ") +
sortedControllersKeys.stream().map(e -> "final " + getType(e, controllerInjectionType, fieldInjectionType) + " " + getVariableName(controllerClassToVariable.get(e), isFactory))
.sorted().collect(Collectors.joining(", "))
+ (resourceBundleType == null ? "" : ", final " + resourceBundleType + " " + resourceBundleArg);
sb.append(" public ").append(simpleClassName).append("(").append(arguments).append(") {\n");
sb.append(" this.").append(controllerArg).append(" = java.util.Objects.requireNonNull(").append(controllerArg).append(");\n");
controllerMap.values().forEach(s -> sb.append(" this.").append(getVariableName(s, isFactory)).append(" = java.util.Objects.requireNonNull(").append(getVariableName(s, isFactory)).append(");\n"));
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) {
sb.append(" this.").append(resourceBundleArg).append(" = java.util.Objects.requireNonNull(").append(resourceBundleArg).append(");\n");
}
sb.append(" }\n");
}
private ResourceBundleInfo getResourceBundleInfo() throws GenerationException {
final var injectionType = progress.request().parameters().resourceInjectionType();
if (injectionType instanceof final ResourceBundleInjectionTypes types) {
return switch (types) {
case CONSTRUCTOR -> new ResourceBundleInfo("java.util.ResourceBundle", "resourceBundle");
case CONSTRUCTOR_FUNCTION ->
new ResourceBundleInfo("java.util.function.Function<String, String>", "resourceBundleFunction");
case CONSTRUCTOR_NAME -> new ResourceBundleInfo("String", "resourceBundleName");
case GETTER -> new ResourceBundleInfo(null, null);
case GET_BUNDLE -> new ResourceBundleInfo(null, null);
};
} else {
throw new GenerationException("Unknown resource injection type : " + injectionType);
}
private ResourceBundleInfo getResourceBundleInfo() {
final var injectionType = request.parameters().resourceInjectionType();
return switch (injectionType) {
case CONSTRUCTOR -> new ResourceBundleInfo("java.util.ResourceBundle", "resourceBundle");
case CONSTRUCTOR_FUNCTION ->
new ResourceBundleInfo("java.util.function.Function<String, String>", "resourceBundleFunction");
case CONSTRUCTOR_NAME -> new ResourceBundleInfo("String", "resourceBundleName");
case GETTER, GET_BUNDLE -> new ResourceBundleInfo(null, null);
};
}
private record ResourceBundleInfo(String type, String variableName) {
}
private static String getType(final String controllerClass, final boolean isFactory) {
if (isFactory) {
private static String getType(final String controllerClass, final InjectionType controllerInjectionTypes, final InjectionType fieldInjectionTypes) {
if (fieldInjectionTypes == ControllerFieldInjectionType.FACTORY) {
return "java.util.function.Function<java.util.Map<String, Object>, " + controllerClass + ">";
} else if (controllerInjectionTypes == ControllerInjectionType.FACTORY) {
return "java.util.function.Supplier<" + controllerClass + ">";
} else {
return controllerClass;
}
@@ -105,19 +129,15 @@ public final class InitializationFormatter {
private static String getVariableName(final String variableName, final boolean isFactory) {
if (isFactory) {
return getVariableName(variableName, "Factory");
return variableName + "Factory";
} else {
return variableName;
}
}
private static String getVariableName(final String variableName, final String suffix) {
return variableName + suffix;
}
private boolean hasDuplicateControllerClass() {
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) {
@@ -125,15 +145,17 @@ public final class InitializationFormatter {
if (controllers.contains(controllerClass)) {
return true;
}
controllers.add(controllerClass);
return info.includedSources().stream().anyMatch(s -> hasDuplicateControllerClass(s, controllers));
}
private void fillControllers() {
progress.request().sourceInfo().includedSources().forEach(this::fillControllers);
request.sourceInfo().includedSources().forEach(this::fillControllers);
}
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);
}
@@ -143,58 +165,55 @@ public final class InitializationFormatter {
}
String formatSubViewConstructorCall(final ParsedInclude include) throws GenerationException {
final var request = progress.request();
final var info = request.sourceInfo();
final var subInfo = info.sourceToSourceInfo().get(include.source());
if (subInfo == null) {
throw new GenerationException("Unknown include source : " + include.source());
} else {
final var isFactory = request.parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY;
final var isFactory = request.parameters().controllerInjectionType() == ControllerInjectionType.FACTORY;
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>();
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 argumentList = subControllerVariable + (arguments.isEmpty() ? "" : ", " + arguments) + (bundleVariable == null ? "" : ", " + bundleVariable);
final var subViewName = subInfo.generatedClassName();
final var variable = progress.getNextVariableName(GenerationHelper.getVariablePrefix(subViewName));
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(subViewName)).append(variable).append(" = new ").append(subViewName).append("(").append(argumentList).append(");\n");
final var variable = helperProvider.getVariableProvider().getNextVariableName(GenerationHelper.getVariablePrefix(subViewName));
sb.append(helperProvider.getCompatibilityHelper().getStartVar(subViewName)).append(variable).append(" = new ").append(subViewName).append("(").append(argumentList).append(");\n");
return variable;
}
}
private String getBundleVariable(final ParsedInclude include) throws GenerationException {
private String getBundleVariable(final ParsedInclude include) {
final var info = getResourceBundleInfo();
if (info.type() == null) {
return null;
} else if (include.resources() == null) {
return info.variableName();
} else {
final var sb = progress.stringBuilder();
if (progress.request().parameters().resourceInjectionType() instanceof final ResourceBundleInjectionTypes types) {
final var compatibilityHelper = helperProvider.getCompatibilityHelper();
return switch (types) {
case GETTER, GET_BUNDLE -> null;
case CONSTRUCTOR_NAME -> {
final var bundleVariable = progress.getNextVariableName("resourceBundleName");
sb.append(compatibilityHelper.getStartVar("String")).append(bundleVariable).append(" = \"").append(include.resources()).append("\";\n");
yield bundleVariable;
}
case CONSTRUCTOR_FUNCTION -> {
final var bundleVariable = progress.getNextVariableName("resourceBundleFunction");
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");
yield bundleVariable;
}
case CONSTRUCTOR -> {
final var bundleVariable = progress.getNextVariableName("resourceBundle");
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());
}
final var compatibilityHelper = helperProvider.getCompatibilityHelper();
final var variableProvider = helperProvider.getVariableProvider();
return switch (request.parameters().resourceInjectionType()) {
case GETTER, GET_BUNDLE -> null;
case CONSTRUCTOR_NAME -> {
final var bundleVariable = variableProvider.getNextVariableName("resourceBundleName");
sb.append(compatibilityHelper.getStartVar("String")).append(bundleVariable).append(" = \"").append(include.resources()).append("\";\n");
yield bundleVariable;
}
case CONSTRUCTOR_FUNCTION -> {
final var bundleVariable = variableProvider.getNextVariableName("resourceBundle");
sb.append(compatibilityHelper.getStartVar("java.util.ResourceBundle")).append(bundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(include.resources()).append("\");\n");
final var bundleFunctionVariable = variableProvider.getNextVariableName("resourceBundleFunction");
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 = variableProvider.getNextVariableName("resourceBundle");
sb.append(compatibilityHelper.getStartVar("java.util.ResourceBundle")).append(bundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(include.resources()).append("\");\n");
yield bundleVariable;
}
};
}
}
}
@@ -1,9 +1,10 @@
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.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import static java.util.Objects.requireNonNull;
@@ -29,7 +30,8 @@ public final class LoadMethodFormatter {
final var request = progress.request();
final var rootObject = request.rootObject();
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 sb = progress.stringBuilder();
sb.append(" /**\n");
@@ -43,35 +45,38 @@ public final class LoadMethodFormatter {
sb.append(" }\n");
final var resourceBundleInjection = parameters.resourceInjectionType();
final var generationCompatibilityHelper = helperProvider.getCompatibilityHelper();
if (resourceBundleInjection == ResourceBundleInjectionTypes.CONSTRUCTOR_NAME) {
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)) {
sb.append(generationCompatibilityHelper.getStartVar("java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"").append(parameters.bundleMap().get(controllerClass)).append("\");\n");
if (resourceBundleInjection == ResourceBundleInjectionType.CONSTRUCTOR_NAME) {
sb.append(generationCompatibilityHelper.getStartVar("java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(resourceBundleName);\n");
} 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");
}
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");
} 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);
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
sb.append(" controller = (").append(controllerClass).append(") controllerFactory.create(fieldMap);\n");
if (fieldInjectionType == ControllerFieldInjectionType.FACTORY) {
sb.append(" controller = controllerFactory.create(fieldMap);\n");
progress.controllerFactoryPostAction().forEach(sb::append);
}
if (parameters.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) {
sb.append(" try {\n");
sb.append(" ").append(generationCompatibilityHelper.getStartVar("java.lang.reflect.Method", 0)).append("initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n");
sb.append(" initialize.setAccessible(true);\n");
sb.append(" initialize.invoke(controller);\n");
sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n");
sb.append(" } catch (final NoSuchMethodException ignored) {\n");
sb.append(" }\n");
} else {
sb.append(" controller.initialize();\n");
if (request.controllerInfo().hasInitialize()) {
if (parameters.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) {
sb.append(" try {\n");
sb.append(" ").append(generationCompatibilityHelper.getStartVar("java.lang.reflect.Method", 0)).append("initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n");
sb.append(" initialize.setAccessible(true);\n");
sb.append(" initialize.invoke(controller);\n");
sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {\n");
sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n");
sb.append(" }\n");
} else {
sb.append(" controller.initialize();\n");
}
}
sb.append(" loaded = true;\n");
sb.append(" return (T) ").append(variableName).append(";\n");
sb.append(" }\n");
}
}
@@ -1,6 +1,7 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.*;
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
*/
public final class ObjectFormatter {
final class ObjectFormatter {
private static final Logger logger = LogManager.getLogger(ObjectFormatter.class);
@@ -47,11 +48,13 @@ public final class ObjectFormatter {
);
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.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);
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
*/
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.image.Image" ->
helperProvider.getImageFormatter().formatImage(parsedObject, variableName);
case "java.net.URL" -> helperProvider.getURLFormatter().formatURL(parsedObject, variableName);
case "javafx.scene.shape.TriangleMesh" ->
helperProvider.getTriangleMeshFormatter().formatTriangleMesh(parsedObject, variableName);
case "java.net.URL" -> helperProvider.getURLFormatter().formatURL(parsedObject, variableName);
case "javafx.scene.web.WebView" ->
helperProvider.getWebViewFormatter().formatWebView(parsedObject, variableName);
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);
}
final var value = getSimpleValue(parsedObject);
final var valueStr = helperProvider.getValueFormatter().toString(value, ReflectionHelper.getClass(parsedObject.className()));
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(" = ").append(valueStr).append(";\n");
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
final var valueStr = ValueFormatter.toString(value, ReflectionHelper.getClass(parsedObject.className()));
sb.append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(" = ").append(valueStr).append(";\n");
}
private String getSimpleValue(final ParsedObject parsedObject) throws GenerationException {
final var definedChildren = parsedObject.children().stream().filter(ParsedDefine.class::isInstance).toList();
for (final var definedChild : definedChildren) {
formatObject(definedChild, progress.getNextVariableName("define"));
}
final var notDefinedChildren = parsedObject.children().stream().filter(c -> !(c instanceof ParsedDefine)).toList();
formatDefines(parsedObject.children());
final var notDefinedChildren = getNotDefines(parsedObject.children());
if (parsedObject.attributes().containsKey(FX_VALUE)) {
return getSimpleFXValue(parsedObject, notDefinedChildren);
} 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 {
if (notDefinedChildren.isEmpty() && !parsedObject.attributes().containsKey(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();
if (SIMPLE_CLASSES.contains(className)) {
return true;
@@ -199,35 +270,31 @@ public final class ObjectFormatter {
private void formatComplexClass(final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var clazz = ReflectionHelper.getClass(parsedObject.className());
final var children = parsedObject.children();
final var definedChildren = children.stream().filter(ParsedDefine.class::isInstance).toList();
final var notDefinedChildren = children.stream().filter(c -> !(c instanceof ParsedDefine)).toList();
final var notDefinedChildren = getNotDefines(children);
final var constructors = clazz.getConstructors();
final var allPropertyNames = new HashSet<>(parsedObject.attributes().keySet());
allPropertyNames.addAll(parsedObject.properties().keySet().stream().map(ParsedProperty::name).collect(Collectors.toSet()));
if (!definedChildren.isEmpty()) {
for (final var definedChild : definedChildren) {
format(definedChild, progress.getNextVariableName("define"));
}
}
final var allAttributesNames = new HashSet<>(parsedObject.attributes().keySet());
allAttributesNames.addAll(parsedObject.properties().keySet().stream().map(ParsedProperty::name).collect(Collectors.toSet()));
formatDefines(children);
if (!notDefinedChildren.isEmpty()) {
final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
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) {
formatNoConstructor(parsedObject, variableName, allPropertyNames);
formatNoConstructor(parsedObject, variableName, allAttributesNames);
} else {
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());
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");
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 {
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 {
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);
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName).append(NEW_ASSIGN).append(parsedObject.className())
.append(genericTypes).append("(").append(String.join(", ", args)).append(");\n");
sb.append(helperProvider.getCompatibilityHelper().getStartVar(parsedObject)).append(variableName)
.append(NEW_ASSIGN).append(parsedObject.className()).append(genericTypes).append("(")
.append(String.join(", ", args)).append(");\n");
final var sortedAttributes = getSortedAttributes(parsedObject);
for (final var value : sortedAttributes) {
if (!constructorArgs.namedArgs().containsKey(value.name())) {
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) {
if (!constructorArgs.namedArgs().containsKey(e.getKey().name())) {
final var p = e.getKey();
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();
if (!notDefinedChildren.isEmpty()) {
final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
if (!constructorArgs.namedArgs().containsKey(defaultProperty)) {
final var property = new ParsedPropertyImpl(defaultProperty, null, null);
formatChild(parsedObject, property, notDefinedChildren, variableName);
final var property = new ParsedPropertyImpl(defaultProperty, null, "");
helperProvider.getPropertyFormatter().formatProperty(property, notDefinedChildren, parsedObject, variableName);
}
}
}
@@ -270,20 +338,21 @@ public final class ObjectFormatter {
* @param subNodeName The sub node name
*/
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);
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);
}
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();
if (id != null) {
final var subControllerVariable = progress.getNextVariableName("controller");
final var controllerClass = progress.request().sourceInfo().sourceToSourceInfo().get(include.source()).controllerClassName();
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(controllerClass)).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
progress.idToVariableInfo().put(id, new VariableInfo(id, include, subControllerVariable, controllerClass));
if (progress.request().controllerInfo().fieldInfo(id) == null) {
final var variableProvider = helperProvider.getVariableProvider();
final var subControllerVariable = variableProvider.getNextVariableName("controller");
final var controllerClass = request.sourceInfo().sourceToSourceInfo().get(include.source()).controllerClassName();
sb.append(helperProvider.getCompatibilityHelper().getStartVar(controllerClass)).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
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);
} else {
helperProvider.getControllerInjector().injectControllerField(id, subControllerVariable);
@@ -294,12 +363,12 @@ public final class ObjectFormatter {
/**
* Formats a fx:define
*
* @param define The parsed define
* @param define The parsed define
* @throws GenerationException if an error occurs
*/
private void formatDefine(final ParsedObject define) throws GenerationException {
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 {
final var id = reference.source();
final var variableInfo = progress.idToVariableInfo().get(id);
final var variableInfo = helperProvider.getVariableProvider().getVariableInfo(id);
if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id);
}
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 {
final var id = copy.source();
final var variableInfo = progress.idToVariableInfo().get(id);
final var variableInfo = helperProvider.getVariableProvider().getVariableInfo(id);
if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id);
}
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 variableName The variable name
*/
private void formatConstant(final ParsedConstant constant, final String variableName) throws GenerationException {
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(constant.className())).append(variableName).append(" = ").append(constant.className()).append(".").append(constant.constant()).append(";\n");
private void formatConstant(final ParsedConstant constant, final String variableName) {
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 variableName The variable name
*/
private void formatValue(final ParsedValue value, final String variableName) throws GenerationException {
progress.stringBuilder().append(helperProvider.getCompatibilityHelper().getStartVar(value.className())).append(variableName).append(" = ").append(value.className()).append(".valueOf(\"").append(value.value()).append("\");\n");
private void formatValue(final ParsedValue value, final String variableName) {
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 {
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()) {
final var argumentVariable = progress.getNextVariableName("arg");
final var argumentVariable = helperProvider.getVariableProvider().getNextVariableName("arg");
variables.add(argumentVariable);
format(argument, argumentVariable);
}
final var compatibilityHelper = helperProvider.getCompatibilityHelper();
if (progress.request().parameters().compatibility().useVar()) {
progress.stringBuilder().append(compatibilityHelper.getStartVar(factory.className())).append(variableName).append(" = ").append(factory.className())
if (request.parameters().compatibility().useVar()) {
sb.append(compatibilityHelper.getStartVar(factory.className())).append(variableName).append(" = ").append(factory.className())
.append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
} else {
final var returnType = ReflectionHelper.getReturnType(factory.className(), factory.factory());
progress.stringBuilder().append(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");
}
}
/**
* 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);
}
}
@@ -1,17 +1,21 @@
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.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
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.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.ParsedText;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import javafx.event.EventHandler;
import java.util.ArrayList;
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.RESOURCE_KEY_PREFIX;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
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 {
final var propertyName = property.name();
if (propertyName.equals(FX_ID)) {
helperProvider.getGenerationHelper().handleId(parent, parentVariable);
//Do nothing
} else if (propertyName.equals("fx:controller")) {
checkDuplicateController(parent);
} 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 {
if (parent != progress.request().rootObject()) {
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 {
final var setMethod = GenerationHelper.getSetMethod(propertyName);
final var setMethod = getSetMethod(propertyName);
final var propertySourceTypeClass = ReflectionHelper.getClass(property.sourceType());
if (ReflectionHelper.hasStaticMethod(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 {
final var propertyName = property.name();
final var setMethod = GenerationHelper.getSetMethod(propertyName);
final var getMethod = GenerationHelper.getGetMethod(propertyName);
final var setMethod = getSetMethod(propertyName);
final var getMethod = getGetMethod(propertyName);
final var parentClass = ReflectionHelper.getClass(parent.className());
if (ReflectionHelper.hasMethod(parentClass, setMethod)) {
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 {
final var setMethod = GenerationHelper.getSetMethod(property.name());
final var setMethod = getSetMethod(property.name());
final var method = ReflectionHelper.getMethod(parentClass, setMethod);
final var parameterType = method.getParameterTypes()[0];
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 {
final var getMethod = GenerationHelper.getGetMethod(property.name());
final var getMethod = getGetMethod(property.name());
final var method = ReflectionHelper.getMethod(parentClass, getMethod);
final var returnType = method.getReturnType();
if (ReflectionHelper.hasMethod(returnType, "addAll")) {
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) {
final var parameters = progress.request().parameters();
if (type == String.class && property.value().startsWith(RESOURCE_KEY_PREFIX) && parameters.resourceInjectionType() == ResourceBundleInjectionTypes.GETTER
&& parameters.fieldInjectionType() == ControllerFieldInjectionTypes.FACTORY) {
if (type == String.class && property.value().startsWith(RESOURCE_KEY_PREFIX) && parameters.resourceInjectionType() == ResourceBundleInjectionType.GETTER
&& parameters.fieldInjectionType() == ControllerFieldInjectionType.FACTORY) {
progress.controllerFactoryPostAction().add(arg);
} else {
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);
}
}
@@ -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.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.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import javafx.scene.paint.Color;
import java.util.ArrayList;
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.getSortedAttributes;
@@ -17,17 +22,21 @@ import static java.util.Objects.requireNonNull;
*/
final class SceneFormatter {
private final HelperProvider helperProvider;
private final GenerationProgress progress;
private static final ParsedProperty ROOT_PROPERTY = new ParsedPropertyImpl("root", null, "");
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.progress = requireNonNull(progress);
this.sb = requireNonNull(sb);
}
void formatScene(final ParsedObject parsedObject, final String variableName) throws GenerationException {
checkPropertiesAndChildren(parsedObject);
formatDefines(parsedObject);
final var root = findRoot(parsedObject);
final var rootVariableName = progress.getNextVariableName("root");
final var rootVariableName = helperProvider.getVariableProvider().getNextVariableName("root");
helperProvider.getObjectFormatter().format(root, rootVariableName);
final var sortedAttributes = getSortedAttributes(parsedObject);
double width = -1;
@@ -46,31 +55,64 @@ final class SceneFormatter {
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(", ")
.append(width).append(", ").append(height).append(", javafx.scene.paint.Color.valueOf(\"").append(paint).append("\"));\n");
addStylesheets(variableName, stylesheets);
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
}
private static ParsedObject findRoot(final ParsedObject parsedObject) throws GenerationException {
final var rootProperty = parsedObject.properties().entrySet().stream().filter(e -> e.getKey().name().equals("root"))
.filter(e -> e.getValue().size() == 1)
.map(e -> e.getValue().getFirst()).findFirst().orElse(null);
if (rootProperty != null) {
return rootProperty;
} else if (parsedObject.children().size() == 1) {
return parsedObject.children().getFirst();
} else {
throw new GenerationException("Scene must have a root");
private void formatDefines(final ParsedObject parsedObject) throws GenerationException {
final var objectFormatter = helperProvider.getObjectFormatter();
for (final var define : parsedObject.children()) {
if (define instanceof ParsedDefine) {
objectFormatter.format(define, helperProvider.getVariableProvider().getNextVariableName("define"));
}
}
for (final var define : parsedObject.properties().getOrDefault(ROOT_PROPERTY, List.of())) {
if (define instanceof ParsedDefine) {
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()) {
final var urlVariables = helperProvider.getURLFormatter().formatURL(stylesheets);
final var tmpVariable = progress.getNextVariableName("stylesheets");
final var sb = progress.stringBuilder();
final var tmpVariable = helperProvider.getVariableProvider().getNextVariableName("stylesheets");
final var compatibilityHelper = helperProvider.getCompatibilityHelper();
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");
@@ -76,7 +76,6 @@ final class TriangleMeshFormatter {
setFaces(variableName, faces);
setFaceSmoothingGroups(variableName, faceSmoothingGroups);
setVertexFormat(variableName, vertexFormat);
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} else {
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) {
final var splitPattern = Pattern.compile("[\\s+,]");
final var splitPattern = Pattern.compile("\\s*,\\s*|\\s+");
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());
}
}
@@ -17,11 +17,11 @@ import static java.util.Objects.requireNonNull;
final class URLFormatter {
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.progress = requireNonNull(progress);
this.sb = requireNonNull(sb);
}
List<String> formatURL(final Iterable<String> stylesheets) {
@@ -33,8 +33,7 @@ final class URLFormatter {
}
String formatURL(final String url) {
final var variableName = progress.getNextVariableName("url");
final var sb = progress.stringBuilder();
final var variableName = helperProvider.getVariableProvider().getNextVariableName("url");
if (url.startsWith(RELATIVE_PATH_PREFIX)) {
sb.append(getStartURL()).append(variableName).append(" = getClass().getResource(\"").append(url.substring(1)).append("\");\n");
} else {
@@ -62,8 +61,7 @@ final class URLFormatter {
}
}
//FIXME only relative path (@) ?
progress.stringBuilder().append(getStartURL()).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n");
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
sb.append(getStartURL()).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n");
} else {
throw new GenerationException("URL cannot have children or properties : " + parsedObject);
}
@@ -1,11 +1,9 @@
package com.github.gtache.fxml.compiler.impl.internal;
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.ResourceBundleInjectionTypes;
import java.util.Map;
import java.util.regex.Pattern;
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 START_BACKSLASH_PATTERN = Pattern.compile("^\\\\");
private final InjectionType resourceInjectionType;
private final Map<String, VariableInfo> idToVariableInfo;
private final HelperProvider helperProvider;
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.idToVariableInfo = requireNonNull(idToVariableInfo);
}
/**
@@ -45,7 +43,7 @@ final class ValueFormatter {
} else if (value.startsWith(BINDING_EXPRESSION_PREFIX)) {
throw new GenerationException("Not implemented yet");
} 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) {
throw new GenerationException("Unknown variable : " + value.substring(1));
}
@@ -62,20 +60,15 @@ final class ValueFormatter {
/**
* Gets the resource bundle value for the given value
*
* @param value The value
* @param value The value
* @return The resource bundle value
* @throws GenerationException if an error occurs
*/
private String getBundleValue(final String value) throws GenerationException {
if (resourceInjectionType instanceof final ResourceBundleInjectionTypes types) {
return switch (types) {
case CONSTRUCTOR, GET_BUNDLE, CONSTRUCTOR_NAME -> "resourceBundle.getString(\"" + value + "\")";
case GETTER -> "controller.resources().getString(\"" + value + "\")";
case CONSTRUCTOR_FUNCTION -> "resourceBundleFunction.apply(\"" + value + "\")";
};
} else {
throw new GenerationException("Unknown resource bundle injection type : " + resourceInjectionType);
}
private String getBundleValue(final String value) {
return switch (resourceInjectionType) {
case CONSTRUCTOR, GET_BUNDLE, CONSTRUCTOR_NAME -> "resourceBundle.getString(\"" + value + "\")";
case GETTER -> "controller.resources().getString(\"" + value + "\")";
case CONSTRUCTOR_FUNCTION -> "resourceBundleFunction.apply(\"" + value + "\")";
};
}
@@ -86,7 +79,7 @@ final class ValueFormatter {
* @param clazz The value class
* @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) {
return "\"" + START_BACKSLASH_PATTERN.matcher(value).replaceAll("").replace("\\", "\\\\")
.replace("\"", "\\\"") + "\"";
@@ -14,6 +14,14 @@ import java.util.Objects;
*/
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 {
Objects.requireNonNull(id);
Objects.requireNonNull(parsedObject);
@@ -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);
}
}
@@ -15,11 +15,11 @@ import static java.util.Objects.requireNonNull;
final class WebViewFormatter {
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.progress = requireNonNull(progress);
this.sb = requireNonNull(sb);
}
/**
@@ -32,15 +32,13 @@ final class WebViewFormatter {
void formatWebView(final ParsedObject parsedObject, final String variableName) throws GenerationException {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
final var sortedAttributes = getSortedAttributes(parsedObject);
final var sb = progress.stringBuilder();
final var compatibilityHelper = helperProvider.getCompatibilityHelper();
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");
for (final var value : sortedAttributes) {
formatAttribute(value, parsedObject, variableName, engineVariable);
}
helperProvider.getGenerationHelper().handleId(parsedObject, variableName);
} else {
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) {
progress.stringBuilder().append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n");
sb.append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n");
}
@@ -10,12 +10,36 @@ import java.util.Objects;
* Implementation of {@link ParsedConstant}
*
* @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 {
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 {
Objects.requireNonNull(className);
if (!attributes.containsKey(FX_CONSTANT)) {
throw new IllegalArgumentException("Missing " + FX_CONSTANT);
}
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)));
}
}
@@ -8,11 +8,33 @@ import java.util.Map;
/**
* Implementation of {@link ParsedCopy}
*
* @param attributes The reference properties
* @param attributes The copy attributes
*/
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 {
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)));
}
}
@@ -8,12 +8,15 @@ import java.util.List;
/**
* Implementation of {@link ParsedObject}
*
* @param className The object class
* @param attributes The object properties
* @param properties The object children (complex properties)
* @param children The objects in this define
*/
public record ParsedDefineImpl(List<ParsedObject> children) implements ParsedDefine {
/**
* Instantiates the define
* @param children The children
* @throws NullPointerException If the children are null
*/
public ParsedDefineImpl {
children = List.copyOf(children);
}
@@ -13,13 +13,23 @@ import java.util.SequencedCollection;
* Implementation of {@link ParsedFactory}
*
* @param className The factory class
* @param attributes The factory properties
* @param attributes The factory attributes
* @param arguments The factory arguments
* @param children The factory children
*/
public record ParsedFactoryImpl(String className, Map<String, ParsedProperty> attributes,
SequencedCollection<ParsedObject> arguments,
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 {
Objects.requireNonNull(className);
attributes = Map.copyOf(attributes);
@@ -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.ParsedProperty;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of {@link ParsedInclude}
*
* @param attributes The object properties
* @param attributes The include attributes
*/
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 {
if (!attributes.containsKey(SOURCE)) {
throw new IllegalArgumentException("Missing " + SOURCE);
}
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;
}
}
@@ -15,13 +15,22 @@ import java.util.SequencedMap;
* Implementation of {@link ParsedObject}
*
* @param className The object class
* @param attributes The object properties
* @param properties The object children (complex properties)
* @param attributes The object attributes
* @param properties The object properties
* @param children The object children
*/
public record ParsedObjectImpl(String className, Map<String, ParsedProperty> attributes,
SequencedMap<ParsedProperty, SequencedCollection<ParsedObject>> properties,
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 {
Objects.requireNonNull(className);
attributes = Map.copyOf(attributes);
@@ -2,7 +2,7 @@ package com.github.gtache.fxml.compiler.parsing.impl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link ParsedProperty}
@@ -10,11 +10,18 @@ import java.util.Objects;
* @param name The property name
* @param sourceType The property source type
* @param value The property value
* @param defines The property defines
*/
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 {
Objects.requireNonNull(name);
requireNonNull(name);
requireNonNull(value);
}
}
@@ -8,11 +8,33 @@ import java.util.Map;
/**
* Implementation of {@link ParsedReference}
*
* @param attributes The reference properties
* @param attributes The reference attributes
*/
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 {
if (!attributes.containsKey(SOURCE)) {
throw new IllegalArgumentException("Missing " + SOURCE);
}
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)));
}
}
@@ -14,8 +14,32 @@ import java.util.Objects;
*/
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 {
Objects.requireNonNull(className);
if (!attributes.containsKey(FX_VALUE)) {
throw new IllegalArgumentException("Missing " + FX_VALUE);
}
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)));
}
}
-1
View File
@@ -5,7 +5,6 @@ module com.github.gtache.fxml.compiler.core {
requires transitive com.github.gtache.fxml.compiler.api;
requires transitive javafx.graphics;
requires org.apache.logging.log4j;
requires java.sql;
exports com.github.gtache.fxml.compiler.impl;
exports com.github.gtache.fxml.compiler.parsing.impl;
@@ -1,7 +1,10 @@
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.InjectionType;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -20,15 +23,15 @@ class TestGenerationParametersImpl {
private final GenerationCompatibility compatibility;
private final boolean useImageInputStreamConstructor;
private final Map<String, String> bundleMap;
private final InjectionType controllerInjectionType;
private final InjectionType fieldInjectionType;
private final InjectionType methodInjectionType;
private final InjectionType resourceInjectionType;
private final ControllerInjectionType controllerInjectionType;
private final ControllerFieldInjectionType fieldInjectionType;
private final ControllerMethodsInjectionType methodInjectionType;
private final ResourceBundleInjectionType resourceInjectionType;
private final GenerationParameters parameters;
TestGenerationParametersImpl(@Mock final GenerationCompatibility compatibility, @Mock final InjectionType controllerInjectionType,
@Mock final InjectionType fieldInjectionType, @Mock final InjectionType methodInjectionType,
@Mock final InjectionType resourceInjectionType) {
TestGenerationParametersImpl(@Mock final GenerationCompatibility compatibility, @Mock final ControllerInjectionType controllerInjectionType,
@Mock final ControllerFieldInjectionType fieldInjectionType, @Mock final ControllerMethodsInjectionType methodInjectionType,
@Mock final ResourceBundleInjectionType resourceInjectionType) {
this.compatibility = requireNonNull(compatibility);
this.useImageInputStreamConstructor = true;
this.controllerInjectionType = requireNonNull(controllerInjectionType);
@@ -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();
}
}
@@ -1,8 +1,123 @@
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.mockito.Mock;
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)
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;
}
}
}
@@ -1,10 +1,8 @@
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.GenerationException;
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.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -17,7 +15,6 @@ import java.util.List;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@@ -49,8 +46,8 @@ class TestControllerInjector {
}
@Test
void testInjectControllerFieldFactory() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
void testInjectControllerFieldFactory() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable);
final var expected = " fieldMap.put(\"" + id + "\", " + variable + ");\n";
assertEquals(expected, sb.toString());
@@ -58,8 +55,8 @@ class TestControllerInjector {
}
@Test
void testInjectControllerFieldAssign() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.ASSIGN, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
void testInjectControllerFieldAssign() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable);
final var expected = " controller." + id + " = " + variable + ";\n";
assertEquals(expected, sb.toString());
@@ -67,8 +64,8 @@ class TestControllerInjector {
}
@Test
void testInjectControllerFieldSetters() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.SETTERS, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
void testInjectControllerFieldSetters() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.SETTERS, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable);
final var expected = " controller." + GenerationHelper.getSetMethod(id) + "(" + variable + ");\n";
assertEquals(expected, sb.toString());
@@ -76,8 +73,8 @@ class TestControllerInjector {
}
@Test
void testInjectControllerFieldReflection() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.REFLECTION, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
void testInjectControllerFieldReflection() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.REFLECTION, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectControllerField(id, variable);
final var expected = " injectField(\"" + id + "\", " + variable + ");\n";
assertEquals(expected, sb.toString());
@@ -85,14 +82,8 @@ class TestControllerInjector {
}
@Test
void testInjectControllerFieldUnknown() {
final var injector = new ControllerInjector(controllerInfo, mock(InjectionType.class), 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);
void testInjectEventHandlerReferenceFactoryNoArgument() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectEventHandlerControllerMethod(property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> controller." + property.value().replace("#", "") + "());\n";
assertEquals(1, controllerFactoryPostAction.size());
@@ -101,8 +92,8 @@ class TestControllerInjector {
}
@Test
void testInjectEventHandlerReferenceFactoryWithArgument() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
void testInjectEventHandlerReferenceFactoryWithArgument() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
when(controllerInfo.handlerHasArgument(propertyValue.replace("#", ""))).thenReturn(true);
injector.injectEventHandlerControllerMethod(property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
@@ -112,8 +103,8 @@ class TestControllerInjector {
}
@Test
void testInjectEventHandlerReflectionAssign() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.ASSIGN, ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction);
void testInjectEventHandlerReflectionAssign() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction);
injector.injectEventHandlerControllerMethod(property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callEventHandlerMethod(\"" + propertyValue.replace("#", "") + "\", e));\n";
assertEquals(expected, sb.toString());
@@ -121,20 +112,8 @@ class TestControllerInjector {
}
@Test
void testInjectEventHandlerUnknownMethod() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.ASSIGN, mock(InjectionType.class), 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);
void testInjectCallbackReflectionSetters() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.ASSIGN, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFLECTION, sb, controllerFactoryPostAction);
injector.injectCallbackControllerMethod(property, variable, "clazz");
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callCallbackMethod(\"" + propertyValue.replace("#", "") + "\", e, clazz));\n";
assertEquals(expected, sb.toString());
@@ -142,12 +121,21 @@ class TestControllerInjector {
}
@Test
void testInjectCallbackReferenceFactory() throws GenerationException {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionTypes.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
void testInjectCallbackReferenceFactory() {
final var injector = new ControllerInjector(controllerInfo, ControllerFieldInjectionType.FACTORY, com.github.gtache.fxml.compiler.ControllerMethodsInjectionType.REFERENCE, sb, controllerFactoryPostAction);
injector.injectCallbackControllerMethod(property, variable, "clazz");
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
assertEquals(1, controllerFactoryPostAction.size());
assertEquals(expected, controllerFactoryPostAction.getFirst());
assertEquals("", sb.toString());
}
@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));
}
}
@@ -1,8 +1,134 @@
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.mockito.Mock;
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)
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));
}
}
@@ -1,8 +1,189 @@
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.mockito.Mock;
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)
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));
}
}
@@ -13,6 +13,7 @@ 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.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -123,4 +124,10 @@ class TestGenerationCompatibilityHelper {
when(compatibility.useCollectionsOf()).thenReturn(false);
assertEquals("java.util.Arrays.asList(", compatibilityHelper.getListOf());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new GenerationCompatibilityHelper(null, compatibility));
assertThrows(NullPointerException.class, () -> new GenerationCompatibilityHelper(helperProvider, null));
}
}
@@ -1,8 +1,5 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
@@ -23,29 +20,15 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestGenerationHelper {
private final GenerationProgress progress;
private final GenerationRequest request;
private final ControllerInfo controllerInfo;
private final ControllerFieldInfo fieldInfo;
private final Map<String, VariableInfo> idToVariableInfo;
private final String variableName;
private final ParsedObject object;
private final ParsedObject parsedObject;
private final Map<String, ParsedProperty> attributes;
private final String className;
private final ParsedProperty property;
private final String propertyName;
TestGenerationHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
@Mock final ControllerInfo controllerInfo, @Mock final ControllerFieldInfo fieldInfo,
@Mock final ParsedObject object, @Mock final ParsedProperty property) {
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.fieldInfo = Objects.requireNonNull(fieldInfo);
this.object = Objects.requireNonNull(object);
TestGenerationHelper(@Mock final ParsedObject parsedObject, @Mock final ParsedProperty property) {
this.parsedObject = Objects.requireNonNull(parsedObject);
this.property = Objects.requireNonNull(property);
this.idToVariableInfo = new HashMap<>();
this.variableName = "variable";
this.attributes = new HashMap<>();
this.className = "java.lang.String";
this.propertyName = "property";
@@ -53,17 +36,14 @@ class TestGenerationHelper {
@BeforeEach
void beforeEach() {
when(progress.request()).thenReturn(request);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(object.attributes()).thenReturn(attributes);
when(object.className()).thenReturn(className);
when(parsedObject.attributes()).thenReturn(attributes);
when(parsedObject.className()).thenReturn(className);
when(property.name()).thenReturn(propertyName);
when(progress.idToVariableInfo()).thenReturn(idToVariableInfo);
}
@Test
void testGetVariablePrefixObject() {
assertEquals("string", GenerationHelper.getVariablePrefix(object));
assertEquals("string", GenerationHelper.getVariablePrefix(parsedObject));
}
@Test
@@ -97,6 +77,6 @@ class TestGenerationHelper {
attributes.put("b", new ParsedPropertyImpl("b", null, "valueB"));
attributes.put("c", new ParsedPropertyImpl("c", null, "valueC"));
final var expected = List.of(attributes.get("a"), attributes.get("b"), attributes.get("c"));
assertEquals(expected, GenerationHelper.getSortedAttributes(object));
assertEquals(expected, GenerationHelper.getSortedAttributes(parsedObject));
}
}
@@ -7,13 +7,8 @@ import org.mockito.Mock;
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.SequencedCollection;
import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -23,51 +18,27 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
class TestGenerationProgress {
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 StringBuilder sb;
private final GenerationProgress progress;
TestGenerationProgress(@Mock final GenerationRequest request, @Mock final VariableInfo variableInfo) {
TestGenerationProgress(@Mock final GenerationRequest 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<>();
controllerFactoryPostAction.add("bla");
this.sb = new StringBuilder("test");
this.progress = new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb);
this.progress = new GenerationProgress(request, controllerFactoryPostAction, sb);
}
@Test
void testGetters() {
assertEquals(request, progress.request());
assertEquals(idToVariableInfo, progress.idToVariableInfo());
assertEquals(variableNameCounters, progress.variableNameCounters());
assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
assertEquals(sb, progress.stringBuilder());
}
@Test
void testConstructorDoesntCopy() {
idToVariableInfo.clear();
assertEquals(idToVariableInfo, progress.idToVariableInfo());
variableNameCounters.clear();
assertEquals(variableNameCounters, progress.variableNameCounters());
controllerClassToVariable.clear();
assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
controllerFactoryPostAction.clear();
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -77,15 +48,6 @@ class TestGenerationProgress {
@Test
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");
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -97,29 +59,15 @@ class TestGenerationProgress {
void testOtherConstructor() {
final var progress2 = new GenerationProgress(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("", 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
void testIllegal() {
assertThrows(NullPointerException.class, () -> new GenerationProgress(null, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, null, controllerClassToVariable, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, null, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, null, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, null));
assertThrows(NullPointerException.class, () -> new GenerationProgress(null, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, controllerFactoryPostAction, null));
}
}
@@ -1,8 +1,217 @@
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.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
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));
}
}
@@ -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());
}
}
@@ -1,8 +1,173 @@
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.mockito.Mock;
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)
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));
}
}
@@ -1,8 +1,12 @@
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.GenerationParameters;
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.parsing.ParsedInclude;
import org.junit.jupiter.api.BeforeEach;
@@ -12,51 +16,577 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestInitializationFormatter {
private final GenerationProgress progress;
private final GenerationRequest request;
private final GenerationParameters parameters;
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 ParsedInclude include;
private final Map<String, SourceInfo> sourceToSourceInfo;
private final StringBuilder sb;
private final Map<String, String> controllerClassToVariable;
private final InitializationFormatter initializationFormatter;
TestInitializationFormatter(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
@Mock final GenerationParameters parameters, @Mock final HelperProvider helperProvider,
@Mock final SourceInfo sourceInfo, @Mock final ParsedInclude include) {
this.progress = Objects.requireNonNull(progress);
TestInitializationFormatter(@Mock final GenerationRequest request, @Mock final GenerationParameters parameters, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final HelperProvider helperProvider, @Mock final VariableProvider variableProvider,
@Mock final ControllerInfo controllerInfo, @Mock final SourceInfo sourceInfo,
@Mock final ParsedInclude include) {
this.request = Objects.requireNonNull(request);
this.parameters = Objects.requireNonNull(parameters);
this.stringBuilder = new StringBuilder();
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.include = Objects.requireNonNull(include);
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
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(progress.request()).thenReturn(request);
when(progress.stringBuilder()).thenReturn(stringBuilder);
when(request.outputClassName()).thenReturn("com.github.gtache.fxml.OutputClassName");
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(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
void testFormatSubViewConstructorCallNullSubInfo() {
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));
}
}
@@ -1,8 +1,203 @@
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.mockito.Mock;
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)
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));
}
}
@@ -1,8 +1,631 @@
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.mockito.Mock;
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)
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));
}
}
@@ -1,8 +1,342 @@
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.mockito.Mock;
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)
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));
}
}
@@ -114,7 +114,7 @@ class TestReflectionHelper {
parameters.put("p16", new Parameter("p16", Double.class, "0"));
parameters.put("p17", new Parameter("p17", String.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 actual = ReflectionHelper.getConstructorArgs(defaultConstructor);
assertEquals(expected, actual);
@@ -132,7 +132,7 @@ class TestReflectionHelper {
parameters.put("p13", new Parameter("p13", float.class, "5.5"));
parameters.put("p15", new Parameter("p15", double.class, "6.6"));
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 actual = ReflectionHelper.getConstructorArgs(constructor);
assertEquals(expected, actual);
@@ -140,7 +140,7 @@ class TestReflectionHelper {
@Test
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 actual = ReflectionHelper.getConstructorArgs(constructor);
assertEquals(expected, actual);
@@ -148,7 +148,7 @@ class TestReflectionHelper {
@Test
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));
}
@@ -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())));
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));
}
}
@@ -1,8 +1,172 @@
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.mockito.Mock;
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)
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));
}
}
@@ -1,8 +1,143 @@
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.mockito.Mock;
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)
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));
}
}
@@ -18,55 +18,53 @@ 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.*;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestURLFormatter {
private final HelperProvider helperProvider;
private final GenerationHelper generationHelper;
private final GenerationCompatibilityHelper compatibilityHelper;
private final VariableProvider variableProvider;
private final ParsedObject parsedObject;
private final String variableName;
private final StringBuilder sb;
private final GenerationProgress progress;
private final URLFormatter urlFormatter;
TestURLFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationHelper generationHelper,
@Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final ParsedObject parsedObject,
@Mock final GenerationProgress progress) {
TestURLFormatter(@Mock final HelperProvider helperProvider, @Mock final VariableProvider variableProvider,
@Mock final GenerationCompatibilityHelper compatibilityHelper, @Mock final ParsedObject parsedObject) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.generationHelper = Objects.requireNonNull(generationHelper);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.parsedObject = Objects.requireNonNull(parsedObject);
this.progress = Objects.requireNonNull(progress);
this.sb = new StringBuilder();
this.variableName = "variable";
this.urlFormatter = new URLFormatter(helperProvider, progress);
this.urlFormatter = new URLFormatter(helperProvider, sb);
}
@BeforeEach
void beforeEach() throws GenerationException {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getGenerationHelper()).thenReturn(generationHelper);
when(progress.stringBuilder()).thenReturn(sb);
when(progress.getNextVariableName("url")).thenReturn("url1", "url2");
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
when(variableProvider.getNextVariableName("url")).thenReturn("url1", "url2");
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(parsedObject.children()).thenReturn(List.of());
when(parsedObject.properties()).thenReturn(new LinkedHashMap<>());
doAnswer(i -> sb.append("handleId")).when(generationHelper).handleId(any(), anyString());
}
@Test
void testFormatURLSheets() {
final var styleSheets = List.of("style1.css", "@style2.css");
final var expected = " final java.net.URL url1;\n" +
" try {\n" +
" url1 = new java.net.URI(\"style1.css\").toURL();\n" +
" } catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {\n" +
" throw new RuntimeException(\"Couldn't parse url : style1.css\", e);\n" +
" }\njava.net.URLurl2 = getClass().getResource(\"style2.css\");\n";
final var expected = """
final java.net.URL url1;
try {
url1 = new java.net.URI("style1.css").toURL();
} catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {
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(expected, sb.toString());
}
@@ -80,7 +78,7 @@ class TestURLFormatter {
@Test
void testFormatURLObjectProperties() {
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);
assertThrows(GenerationException.class, () -> urlFormatter.formatURL(parsedObject, variableName));
}
@@ -101,8 +99,13 @@ class TestURLFormatter {
when(parsedObject.attributes()).thenReturn(attributes);
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());
verify(generationHelper).handleId(parsedObject, variableName);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new URLFormatter(null, sb));
assertThrows(NullPointerException.class, () -> new URLFormatter(helperProvider, null));
}
}
@@ -1,20 +1,18 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import javafx.geometry.Pos;
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.net.URL;
import java.util.HashMap;
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.assertThrows;
import static org.mockito.Mockito.mock;
@@ -23,39 +21,40 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestValueFormatter {
private final Map<String, VariableInfo> idToVariableInfo;
private final InjectionType resourceInjectionType;
private final HelperProvider helperProvider;
private final VariableProvider variableProvider;
private final ValueFormatter formatter;
TestValueFormatter(@Mock final InjectionType resourceInjectionType) {
this.resourceInjectionType = requireNonNull(resourceInjectionType);
this.idToVariableInfo = new HashMap<>();
this.formatter = new ValueFormatter(resourceInjectionType, idToVariableInfo);
TestValueFormatter(@Mock final HelperProvider helperProvider, @Mock final VariableProvider variableProvider,
@Mock final ResourceBundleInjectionType resourceInjectionType) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.formatter = new ValueFormatter(helperProvider, resourceInjectionType);
}
@Test
void testGetArgStringResourceUnknown() {
assertThrows(GenerationException.class, () -> formatter.getArg("%value", String.class));
@BeforeEach
void beforeEach() {
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
}
@Test
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) {
final var resourceFormatter = new ValueFormatter(type, idToVariableInfo);
final var resourceFormatter = new ValueFormatter(helperProvider, type);
assertEquals("resourceBundle.getString(\"value\")", resourceFormatter.getArg("%value", String.class));
}
}
@Test
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));
}
@Test
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));
}
@@ -78,8 +77,8 @@ class TestValueFormatter {
@Test
void testGetArgExpression() throws GenerationException {
final var info = mock(VariableInfo.class);
when(variableProvider.getVariableInfo("value")).thenReturn(info);
when(info.variableName()).thenReturn("variable");
idToVariableInfo.put("value", info);
assertEquals("variable", formatter.getArg("$value", String.class));
}
@@ -90,32 +89,32 @@ class TestValueFormatter {
@Test
void testToStringString() {
assertEquals("\"value\"", formatter.toString("value", String.class));
assertEquals("\"value\"", ValueFormatter.toString("value", String.class));
}
@Test
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
void testToStringChar() {
assertEquals("'v'", formatter.toString("v", char.class));
assertEquals("'v'", formatter.toString("v", Character.class));
assertEquals("'v'", ValueFormatter.toString("v", char.class));
assertEquals("'v'", ValueFormatter.toString("v", Character.class));
}
@Test
void testToStringBoolean() {
assertEquals("true", formatter.toString("true", boolean.class));
assertEquals("true", formatter.toString("true", Boolean.class));
assertEquals("true", ValueFormatter.toString("true", boolean.class));
assertEquals("true", ValueFormatter.toString("true", Boolean.class));
}
@Test
void testToStringInteger() {
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) {
assertEquals("1", formatter.toString("1", type));
assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", formatter.toString("value", type));
assertEquals("1", ValueFormatter.toString("1", type));
assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", ValueFormatter.toString("value", type));
}
}
@@ -123,23 +122,29 @@ class TestValueFormatter {
void testToStringDecimal() {
final var types = List.of(float.class, Float.class, double.class, Double.class);
for (final var type : types) {
assertEquals("1.0", formatter.toString("1.0", type));
assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", formatter.toString("value", type));
assertEquals("1.0", ValueFormatter.toString("1.0", type));
assertEquals(ReflectionHelper.getWrapperClass(type) + ".valueOf(\"value\")", ValueFormatter.toString("value", type));
}
}
@Test
void testToStringValueOfEnum() {
assertEquals("javafx.geometry.Pos.value", formatter.toString("value", Pos.class));
assertEquals("javafx.geometry.Pos.value", ValueFormatter.toString("value", Pos.class));
}
@Test
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
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));
}
}
@@ -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));
}
}
@@ -19,7 +19,6 @@ 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.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
@@ -30,35 +29,30 @@ class TestWebViewFormatter {
private final ControllerInjector controllerInjector;
private final FieldSetter fieldSetter;
private final PropertyFormatter propertyFormatter;
private final GenerationHelper generationHelper;
private final GenerationProgress progress;
private final VariableProvider variableProvider;
private final ParsedObject parsedObject;
private final String variableName;
private final String engineVariable;
private final ParsedProperty parsedProperty;
private final StringBuilder sb;
private final Map<String, ParsedProperty> attributes;
private final WebViewFormatter webViewFormatter;
TestWebViewFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper,
@Mock final ControllerInjector controllerInjector, @Mock final FieldSetter fieldSetter,
@Mock final PropertyFormatter propertyFormatter, @Mock final GenerationHelper generationHelper,
@Mock final GenerationProgress progress, @Mock final ParsedObject parsedObject,
@Mock final ParsedProperty parsedProperty) {
@Mock final PropertyFormatter propertyFormatter, @Mock final VariableProvider variableProvider,
@Mock final ParsedObject parsedObject) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.controllerInjector = Objects.requireNonNull(controllerInjector);
this.fieldSetter = Objects.requireNonNull(fieldSetter);
this.propertyFormatter = Objects.requireNonNull(propertyFormatter);
this.generationHelper = Objects.requireNonNull(generationHelper);
this.progress = Objects.requireNonNull(progress);
this.variableProvider = Objects.requireNonNull(variableProvider);
this.parsedObject = Objects.requireNonNull(parsedObject);
this.variableName = "variable";
this.engineVariable = "engine";
this.parsedProperty = Objects.requireNonNull(parsedProperty);
this.sb = new StringBuilder();
this.attributes = new HashMap<>();
this.webViewFormatter = new WebViewFormatter(helperProvider, progress);
this.webViewFormatter = new WebViewFormatter(helperProvider, sb);
}
@BeforeEach
@@ -68,19 +62,17 @@ class TestWebViewFormatter {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(helperProvider.getControllerInjector()).thenReturn(controllerInjector);
when(helperProvider.getFieldSetter()).thenReturn(fieldSetter);
when(helperProvider.getGenerationHelper()).thenReturn(generationHelper);
when(helperProvider.getPropertyFormatter()).thenReturn(propertyFormatter);
when(helperProvider.getVariableProvider()).thenReturn(variableProvider);
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())).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()).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());
when(parsedObject.attributes()).thenReturn(attributes);
when(progress.stringBuilder()).thenReturn(sb);
when(progress.getNextVariableName("engine")).thenReturn(engineVariable);
when(variableProvider.getNextVariableName("engine")).thenReturn(engineVariable);
}
@Test
@@ -92,7 +84,7 @@ class TestWebViewFormatter {
@Test
void testFormatHasProperty() {
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);
assertThrows(GenerationException.class, () -> webViewFormatter.formatWebView(parsedObject, variableName));
}
@@ -111,11 +103,13 @@ class TestWebViewFormatter {
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
webViewFormatter.formatWebView(parsedObject, variableName);
final var expected = "javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();\n" +
"javafx.scene.web.WebEngineengine = variable.getEngine();\n" +
"#confirmHandlerString.class#createPopupHandlerjavafx.scene.web.PopupFeatures.class" +
" engine.load(\"location\");\n#onAlert#onResized#onStatusChanged#onVisibilityChanged#promptHandlerjavafx.scene.web.PromptData.class" +
"propertyhandleId";
final var expected = """
javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();
javafx.scene.web.WebEngineengine = variable.getEngine();
#confirmHandlerString.class#createPopupHandlerjavafx.scene.web.PopupFeatures.class\
engine.load("location");
#onAlert#onResized#onStatusChanged#onVisibilityChanged#promptHandlerjavafx.scene.web.PromptData.class\
property""";
assertEquals(expected, sb.toString());
verify(propertyFormatter).formatProperty(attributes.get("property"), parsedObject, variableName);
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("onVisibilityChanged"), engineVariable);
verify(controllerInjector).injectCallbackControllerMethod(attributes.get("promptHandler"), engineVariable, "javafx.scene.web.PromptData.class");
verify(generationHelper).handleId(parsedObject, variableName);
}
@Test
@@ -142,12 +135,14 @@ class TestWebViewFormatter {
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
webViewFormatter.formatWebView(parsedObject, variableName);
final var expected = "javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();\n" +
"javafx.scene.web.WebEngineengine = variable.getEngine();\n" +
"$controller.confirmHandlerjavafx.util.Callback$controller.createPopupHandlerjavafx.util.Callback" +
" engine.load(\"location\");\n$controller.onAlert$controller.onResized$controller.onStatusChanged$controller.onVisibilityChanged" +
"$controller.promptHandlerjavafx.util.Callback" +
"propertyhandleId";
final var expected = """
javafx.scene.web.WebViewvariable = new javafx.scene.web.WebView();
javafx.scene.web.WebEngineengine = variable.getEngine();
$controller.confirmHandlerjavafx.util.Callback$controller.createPopupHandlerjavafx.util.Callback\
engine.load("location");
$controller.onAlert$controller.onResized$controller.onStatusChanged$controller.onVisibilityChanged\
$controller.promptHandlerjavafx.util.Callback\
property""";
assertEquals(expected, sb.toString());
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("createPopupHandler"), 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));
}
}
@@ -2,34 +2,34 @@ package com.github.gtache.fxml.compiler.impl.internal;
import javafx.beans.NamedArg;
public class WholeConstructorArgs {
public WholeConstructorArgs(@NamedArg("p1") final int p1, @NamedArg("p2") final Integer p2,
@NamedArg("p3") final char p3, @NamedArg("p4") final Character p4,
@NamedArg("p5") final boolean p5, @NamedArg("p6") final Boolean p6,
@NamedArg("p7") final byte p7, @NamedArg("p8") final Byte p8,
@NamedArg("p9") final short p9, @NamedArg("p10") final Short p10,
@NamedArg("p11") final long p11, @NamedArg("p12") final Long p12,
@NamedArg("p13") final float p13, @NamedArg("p14") final Float p14,
@NamedArg("p15") final double p15, @NamedArg("p16") final Double p16,
@NamedArg("p17") final String p17, @NamedArg("p18") final Object p18) {
class WholeConstructorArgs {
WholeConstructorArgs(@NamedArg("p1") final int p1, @NamedArg("p2") final Integer p2,
@NamedArg("p3") final char p3, @NamedArg("p4") final Character p4,
@NamedArg("p5") final boolean p5, @NamedArg("p6") final Boolean p6,
@NamedArg("p7") final byte p7, @NamedArg("p8") final Byte p8,
@NamedArg("p9") final short p9, @NamedArg("p10") final Short p10,
@NamedArg("p11") final long p11, @NamedArg("p12") final Long p12,
@NamedArg("p13") final float p13, @NamedArg("p14") final Float p14,
@NamedArg("p15") final double p15, @NamedArg("p16") final Double p16,
@NamedArg("p17") final String p17, @NamedArg("p18") final Object p18) {
}
public WholeConstructorArgs(@NamedArg(value = "p1", defaultValue = "1") final int p1,
@NamedArg(value = "p3", defaultValue = "a") final char p3,
@NamedArg(value = "p5", defaultValue = "true") final boolean p5,
@NamedArg(value = "p7", defaultValue = "2") final byte p7,
@NamedArg(value = "p9", defaultValue = "3") final short p9,
@NamedArg(value = "p11", defaultValue = "4") final long p11,
@NamedArg(value = "p13", defaultValue = "5.5") final float p13,
@NamedArg(value = "p15", defaultValue = "6.6") final double p15,
@NamedArg(value = "p17", defaultValue = "str") final String p17) {
WholeConstructorArgs(@NamedArg(value = "p1", defaultValue = "1") final int p1,
@NamedArg(value = "p3", defaultValue = "a") final char p3,
@NamedArg(value = "p5", defaultValue = "true") final boolean p5,
@NamedArg(value = "p7", defaultValue = "2") final byte p7,
@NamedArg(value = "p9", defaultValue = "3") final short p9,
@NamedArg(value = "p11", defaultValue = "4") final long p11,
@NamedArg(value = "p13", defaultValue = "5.5") final float p13,
@NamedArg(value = "p15", defaultValue = "6.6") final double p15,
@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) {
}
}
@@ -17,7 +17,7 @@ class TestParsedConstantImpl {
TestParsedConstantImpl() {
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);
}
@@ -43,9 +43,19 @@ class TestParsedConstantImpl {
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
void testIllegal() {
final var emptyMap = Map.<String, ParsedProperty>of();
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));
}
}
@@ -8,6 +8,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.*;
@@ -20,7 +21,7 @@ class TestParsedCopyImpl {
TestParsedCopyImpl(@Mock final ParsedProperty property) {
this.properties = new LinkedHashMap<>();
this.properties.put("name", property);
this.properties.put("source", property);
this.copy = new ParsedCopyImpl(properties);
}
@@ -45,8 +46,16 @@ class TestParsedCopyImpl {
assertThrows(UnsupportedOperationException.class, objectProperties::clear);
}
@Test
void testOtherConstructor() {
final var otherCopy = new ParsedCopyImpl("source");
assertEquals("source", otherCopy.source());
}
@Test
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));
}
}
@@ -19,7 +19,7 @@ class TestParsedDefineImpl {
private final List<ParsedObject> children;
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.parsedDefine = new ParsedDefineImpl(children);
}
@@ -8,6 +8,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.*;
@@ -20,7 +21,7 @@ class TestParsedIncludeImpl {
TestParsedIncludeImpl(@Mock final ParsedProperty property) {
this.properties = new LinkedHashMap<>();
this.properties.put("name", property);
this.properties.put("source", property);
this.include = new ParsedIncludeImpl(properties);
}
@@ -45,8 +46,21 @@ class TestParsedIncludeImpl {
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
void testIllegal() {
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));
}
}
@@ -31,7 +31,7 @@ class TestParsedObjectImpl {
this.attributes = new LinkedHashMap<>();
this.attributes.put("name", property);
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.parsedObject = new ParsedObjectImpl(clazz, attributes, properties, objects);
}
@@ -30,6 +30,6 @@ class TestParsedPropertyImpl {
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ParsedPropertyImpl(null, sourceType, value));
assertDoesNotThrow(() -> new ParsedPropertyImpl(name, null, value));
assertDoesNotThrow(() -> new ParsedPropertyImpl(name, sourceType, null));
assertThrows(NullPointerException.class, () -> new ParsedPropertyImpl(name, sourceType, null));
}
}
@@ -8,6 +8,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.*;
@@ -20,7 +21,7 @@ class TestParsedReferenceImpl {
TestParsedReferenceImpl(@Mock final ParsedProperty property) {
this.properties = new LinkedHashMap<>();
this.properties.put("name", property);
this.properties.put("source", property);
this.reference = new ParsedReferenceImpl(properties);
}
@@ -45,8 +46,16 @@ class TestParsedReferenceImpl {
assertThrows(UnsupportedOperationException.class, objectProperties::clear);
}
@Test
void testOtherConstructor() {
final var otherReference = new ParsedReferenceImpl("name");
assertEquals("name", otherReference.source());
}
@Test
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));
}
}
@@ -17,7 +17,7 @@ class TestParsedValueImpl {
TestParsedValueImpl() {
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);
}
@@ -43,9 +43,19 @@ class TestParsedValueImpl {
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
void testIllegal() {
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));
}
}
@@ -1,11 +1,11 @@
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.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.ResourceBundleInjectionTypes;
import com.github.gtache.fxml.compiler.maven.internal.CompilationInfo;
import com.github.gtache.fxml.compiler.maven.internal.CompilationInfoProvider;
import com.github.gtache.fxml.compiler.maven.internal.Compiler;
@@ -21,6 +21,9 @@ import org.apache.maven.project.MavenProject;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Main mojo for FXML compiler
@@ -41,31 +44,45 @@ public class FXMLCompilerMojo extends AbstractMojo {
private boolean useImageInputStreamConstructor;
@Parameter(property = "controller-injection", defaultValue = "INSTANCE", required = true)
private ControllerInjectionTypes controllerInjectionType;
private ControllerInjectionType controllerInjectionType;
@Parameter(property = "field-injection", defaultValue = "REFLECTION", required = true)
private ControllerFieldInjectionTypes fieldInjectionType;
private ControllerFieldInjectionType fieldInjectionType;
@Parameter(property = "method-injection", defaultValue = "REFLECTION", required = true)
private ControllerMethodsInjectionType methodInjectionType;
@Parameter(property = "resource-injection", defaultValue = "CONSTRUCTOR", required = true)
private ResourceBundleInjectionTypes resourceInjectionType;
private ResourceBundleInjectionType resourceInjectionType;
@Parameter(property = "resource-map")
private Map<String, String> resourceMap;
@Parameter(property = "parallelism", defaultValue = "1", required = true)
private int parallelism;
@Override
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");
controllerInjectionType = ControllerInjectionTypes.FACTORY;
controllerInjectionType = ControllerInjectionType.FACTORY;
}
final var fxmls = FXMLProvider.getFXMLs(project);
final var controllerMapping = createControllerMapping(fxmls);
final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping);
compile(compilationInfoMapping);
if (parallelism < 1) {
parallelism = Runtime.getRuntime().availableProcessors();
}
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 {
@@ -76,7 +93,8 @@ public class FXMLCompilerMojo extends AbstractMojo {
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>();
for (final var entry : fxmls.entrySet()) {
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);
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());
}
}
@@ -12,7 +12,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException;
import javax.inject.Named;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -21,7 +20,6 @@ import java.util.Map;
/**
* Creates compiled Java code
*/
@Named
public final class Compiler {
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 {
logger.info("Parsing {} with {}", inputPath, PARSER.getClass().getSimpleName());
final var root = PARSER.parse(inputPath);
@@ -3,7 +3,6 @@ package com.github.gtache.fxml.compiler.maven.internal;
import org.apache.maven.plugin.MojoExecutionException;
import org.xml.sax.SAXException;
import javax.inject.Named;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -13,7 +12,6 @@ import java.nio.file.Path;
/**
* Extracts controller class from FXMLs
*/
@Named
public final class ControllerProvider {
private static final DocumentBuilder DOCUMENT_BUILDER;
@@ -5,7 +5,6 @@ import org.apache.logging.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import javax.inject.Named;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
@@ -17,7 +16,6 @@ import java.util.Map;
/**
* Extracts FXML paths from Maven project
*/
@Named
public final class FXMLProvider {
private static final Logger logger = LogManager.getLogger(FXMLProvider.class);
@@ -25,7 +25,6 @@ import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
@@ -41,7 +40,6 @@ class TestCompiler {
private final SourceInfo sourceInfo;
private final String content;
private final GenerationParameters parameters;
private final Compiler compiler;
TestCompiler(@Mock final ControllerInfoProvider controllerInfoProvider, @Mock final SourceInfoProvider sourceInfoProvider,
@Mock final FXMLParser fxmlParser, @Mock final CompilationInfo compilationInfo, @Mock final ParsedObject object,
@@ -57,7 +55,6 @@ class TestCompiler {
this.content = "content";
this.parameters = Objects.requireNonNull(parameters);
this.generator = Objects.requireNonNull(generator);
this.compiler = new Compiler(controllerInfoProvider, sourceInfoProvider, fxmlParser, generator);
}
@BeforeEach
@@ -77,7 +74,7 @@ class TestCompiler {
when(compilationInfo.outputClass()).thenReturn(outputClass);
final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
compiler.compile(mapping, parameters);
Compiler.compile(mapping, parameters);
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
@@ -94,7 +91,7 @@ class TestCompiler {
when(compilationInfo.outputClass()).thenReturn(outputClass);
final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
@@ -111,7 +108,7 @@ class TestCompiler {
final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
when(generator.generate(request)).thenThrow(RuntimeException.class);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
@@ -123,7 +120,7 @@ class TestCompiler {
when(fxmlParser.parse((Path) any())).thenThrow(ParseException.class);
final var path = tempDir.resolve("fxml1.fxml");
final var mapping = Map.of(path, compilationInfo);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
verifyNoInteractions(controllerInfoProvider, sourceInfoProvider, generator);
}
@@ -138,18 +135,10 @@ class TestCompiler {
final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
when(generator.generate(request)).thenThrow(GenerationException.class);
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
verify(generator).generate(request);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new Compiler(null, sourceInfoProvider, fxmlParser, generator));
assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, null, fxmlParser, generator));
assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, sourceInfoProvider, null, generator));
assertThrows(NullPointerException.class, () -> new Compiler(controllerInfoProvider, sourceInfoProvider, fxmlParser, null));
}
}
@@ -16,12 +16,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestControllerProvider {
private final ControllerProvider provider;
TestControllerProvider() {
this.provider = new ControllerProvider();
}
@Test
void testGetController(@TempDir final Path tempDir) throws Exception {
final var fxml = tempDir.resolve("fxml.fxml");
@@ -22,11 +22,9 @@ import static org.mockito.Mockito.when;
class TestFXMLProvider {
private final MavenProject project;
private final FXMLProvider provider;
TestFXMLProvider(@Mock final MavenProject project) {
this.project = Objects.requireNonNull(project);
this.provider = new FXMLProvider();
}
@Test
Vendored
+259
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 "$@"
Vendored
+149
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"
+1 -32
View File
@@ -26,7 +26,7 @@
</organization>
<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.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -93,8 +93,6 @@
<version>${plugin.deploy.version}</version>
<executions>
<execution>
<id>deploy</id>
<phase>deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
@@ -107,7 +105,6 @@
<version>${plugin.enforcer.version}</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
@@ -137,16 +134,8 @@
<version>${plugin.failsafe.version}</version>
<executions>
<execution>
<id>integration-test</id>
<phase>test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>verify</goal>
</goals>
</execution>
@@ -158,26 +147,10 @@
<version>${plugin.jacoco.version}</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-prepare-agent-integration</id>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>default-report-integration</id>
<goals>
<goal>report-integration</goal>
</goals>
</execution>
@@ -189,8 +162,6 @@
<version>${plugin.javadoc.version}</version>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>install</phase>
<goals>
<goal>jar</goal>
</goals>
@@ -221,8 +192,6 @@
<version>${plugin.source.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
+1 -1
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 {
requires transitive com.github.gtache.fxml.compiler.core;
@@ -19,6 +19,7 @@ import java.util.Map;
import java.util.SequencedMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
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());
try (final var in = getClass().getResourceAsStream("loadView.fxml")) {
assertNotNull(in);
final var content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
final var actual = parser.parse(content);
assertEquals(expected, actual);
@@ -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();
}
}
@@ -1 +0,0 @@
media.volume.label=Volume
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -1,3 +0,0 @@
.clazz {
-fx-font-weight: bold;
}