From 58fbbff7cb7fe92761af9d916a20e9c3bd623839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20T=C3=A2che?= Date: Wed, 25 Dec 2024 22:40:01 +0100 Subject: [PATCH] Adds maven wrapper ; rework internal a bit ; Adds all tests for core --- .mvn/wrapper/maven-wrapper.properties | 19 + README.md | 120 +--- .../ControllerFieldInjectionType.java | 6 +- .../compiler/ControllerInjectionType.java | 6 +- .../ControllerMethodsInjectionType.java | 4 +- .../fxml/compiler/GenerationParameters.java | 8 +- .../compiler/ResourceBundleInjectionType.java | 6 +- .../compiler/TestGenerationException.java | 53 ++ .../compiler/parsing/TestParseException.java | 53 ++ .../compiler/impl/ControllerInfoImpl.java | 8 + .../impl/GenerationParametersImpl.java | 25 +- .../compiler/impl/GenerationRequestImpl.java | 9 + .../fxml/compiler/impl/GeneratorImpl.java | 25 +- .../fxml/compiler/impl/SourceInfoImpl.java | 10 + .../impl/internal/ConstructorHelper.java | 24 +- .../impl/internal/ControllerInjector.java | 113 ++-- .../compiler/impl/internal/FieldSetter.java | 78 ++- .../compiler/impl/internal/FontFormatter.java | 17 +- .../impl/internal/GenerationHelper.java | 40 +- .../impl/internal/GenerationProgress.java | 31 +- .../impl/internal/HelperMethodsFormatter.java | 16 +- .../impl/internal/HelperProvider.java | 119 ++-- .../impl/internal/ImageFormatter.java | 48 +- .../internal/InitializationFormatter.java | 171 ++--- .../impl/internal/LoadMethodFormatter.java | 53 +- .../impl/internal/ObjectFormatter.java | 302 ++++----- .../impl/internal/PropertyFormatter.java | 162 ++++- .../impl/internal/SceneFormatter.java | 82 ++- .../impl/internal/TriangleMeshFormatter.java | 5 +- .../compiler/impl/internal/URLFormatter.java | 12 +- .../impl/internal/ValueFormatter.java | 35 +- .../compiler/impl/internal/VariableInfo.java | 10 +- .../impl/internal/VariableProvider.java | 51 ++ .../impl/internal/WebViewFormatter.java | 12 +- .../parsing/impl/ParsedConstantImpl.java | 26 +- .../compiler/parsing/impl/ParsedCopyImpl.java | 24 +- .../parsing/impl/ParsedDefineImpl.java | 9 +- .../parsing/impl/ParsedFactoryImpl.java | 12 +- .../parsing/impl/ParsedIncludeImpl.java | 39 +- .../parsing/impl/ParsedObjectImpl.java | 13 +- .../parsing/impl/ParsedPropertyImpl.java | 13 +- .../parsing/impl/ParsedReferenceImpl.java | 24 +- .../parsing/impl/ParsedValueImpl.java | 24 + core/src/main/java/module-info.java | 1 - .../impl/TestGenerationParametersImpl.java | 19 +- .../fxml/compiler/impl/TestGeneratorImpl.java | 91 +++ .../impl/internal/TestConstructorHelper.java | 115 ++++ .../impl/internal/TestControllerInjector.java | 70 +- .../impl/internal/TestFieldSetter.java | 126 ++++ .../impl/internal/TestFontFormatter.java | 181 +++++ .../TestGenerationCompatibilityHelper.java | 7 + .../impl/internal/TestGenerationHelper.java | 34 +- .../impl/internal/TestGenerationProgress.java | 62 +- .../internal/TestHelperMethodsFormatter.java | 209 ++++++ .../impl/internal/TestHelperProvider.java | 169 +++++ .../impl/internal/TestImageFormatter.java | 165 +++++ .../internal/TestInitializationFormatter.java | 550 ++++++++++++++- .../internal/TestLoadMethodFormatter.java | 197 +++++- .../impl/internal/TestObjectFormatter.java | 625 +++++++++++++++++- .../impl/internal/TestPropertyFormatter.java | 334 ++++++++++ .../impl/internal/TestReflectionHelper.java | 29 +- .../impl/internal/TestSceneFormatter.java | 164 +++++ .../internal/TestTriangleMeshFormatter.java | 135 ++++ .../impl/internal/TestURLFormatter.java | 49 +- .../impl/internal/TestValueFormatter.java | 69 +- .../impl/internal/TestVariableProvider.java | 39 ++ .../impl/internal/TestWebViewFormatter.java | 58 +- .../impl/internal/WholeConstructorArgs.java | 42 +- .../parsing/impl/TestParsedConstantImpl.java | 14 +- .../parsing/impl/TestParsedCopyImpl.java | 13 +- .../parsing/impl/TestParsedDefineImpl.java | 2 +- .../parsing/impl/TestParsedIncludeImpl.java | 16 +- .../parsing/impl/TestParsedObjectImpl.java | 2 +- .../parsing/impl/TestParsedPropertyImpl.java | 2 +- .../parsing/impl/TestParsedReferenceImpl.java | 13 +- .../parsing/impl/TestParsedValueImpl.java | 14 +- .../fxml/compiler/maven/FXMLCompilerMojo.java | 88 ++- .../compiler/maven/internal/Compiler.java | 4 +- .../maven/internal/ControllerProvider.java | 2 - .../compiler/maven/internal/FXMLProvider.java | 2 - .../compiler/maven/internal/TestCompiler.java | 21 +- .../internal/TestControllerProvider.java | 6 - .../maven/internal/TestFXMLProvider.java | 2 - mvnw | 259 ++++++++ mvnw.cmd | 149 +++++ pom.xml | 33 +- xml/src/main/java/module-info.java | 2 +- .../parsing/xml/TestDOMFXMLParser.java | 2 + .../parsing/xml/TestGeneratorImpl.java | 144 ---- .../parsing/xml/ControlsBundle.properties | 1 - .../parsing/xml/IncludesBundle.properties | 1 - .../compiler/parsing/xml/controlsView.fxml | 239 ------- .../parsing/xml/includes/include1.fxml | 10 - .../parsing/xml/includes/include2.fxml | 7 - .../parsing/xml/includes/include21.fxml | 8 - .../parsing/xml/includes/include3.fxml | 10 - .../parsing/xml/includes/includesView.fxml | 10 - .../compiler/parsing/xml/includesView.fxml | 141 ---- .../fxml/compiler/parsing/xml/style.css | 3 - 99 files changed, 5028 insertions(+), 1649 deletions(-) create mode 100644 .mvn/wrapper/maven-wrapper.properties rename core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java => api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInjectionType.java (63%) rename core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionTypes.java => api/src/main/java/com/github/gtache/fxml/compiler/ControllerInjectionType.java (52%) rename {core/src/main/java/com/github/gtache/fxml/compiler/impl => api/src/main/java/com/github/gtache/fxml/compiler}/ControllerMethodsInjectionType.java (71%) rename core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java => api/src/main/java/com/github/gtache/fxml/compiler/ResourceBundleInjectionType.java (75%) create mode 100644 api/src/test/java/com/github/gtache/fxml/compiler/TestGenerationException.java create mode 100644 api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParseException.java create mode 100644 core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableProvider.java create mode 100644 core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGeneratorImpl.java create mode 100644 core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperProvider.java create mode 100644 core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestVariableProvider.java create mode 100644 mvnw create mode 100644 mvnw.cmd delete mode 100644 xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestGeneratorImpl.java delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/ControlsBundle.properties delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/IncludesBundle.properties delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/controlsView.fxml delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include1.fxml delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include2.fxml delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include21.fxml delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include3.fxml delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/includesView.fxml delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includesView.fxml delete mode 100644 xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/style.css diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b2f4fc0 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -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 diff --git a/README.md b/README.md index a8e9b12..b0da7f6 100644 --- a/README.md +++ b/README.md @@ -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`, but if used in conjunction with `field-injection`set to + `FACTORY`, the factory is a `Function, 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, 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. \ No newline at end of file diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInjectionType.java similarity index 63% rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java rename to api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInjectionType.java index 1b21a7f..42a5989 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerFieldInjectionTypes.java +++ b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerFieldInjectionType.java @@ -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 */ diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionTypes.java b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInjectionType.java similarity index 52% rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionTypes.java rename to api/src/main/java/com/github/gtache/fxml/compiler/ControllerInjectionType.java index 4c28607..cebe7bb 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInjectionTypes.java +++ b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerInjectionType.java @@ -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 */ diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerMethodsInjectionType.java b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerMethodsInjectionType.java similarity index 71% rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerMethodsInjectionType.java rename to api/src/main/java/com/github/gtache/fxml/compiler/ControllerMethodsInjectionType.java index 7d2e50f..de48366 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerMethodsInjectionType.java +++ b/api/src/main/java/com/github/gtache/fxml/compiler/ControllerMethodsInjectionType.java @@ -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 diff --git a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java index b874302..ac6232a 100644 --- a/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java +++ b/api/src/main/java/com/github/gtache/fxml/compiler/GenerationParameters.java @@ -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(); } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java b/api/src/main/java/com/github/gtache/fxml/compiler/ResourceBundleInjectionType.java similarity index 75% rename from core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java rename to api/src/main/java/com/github/gtache/fxml/compiler/ResourceBundleInjectionType.java index 95b634f..34cedab 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ResourceBundleInjectionTypes.java +++ b/api/src/main/java/com/github/gtache/fxml/compiler/ResourceBundleInjectionType.java @@ -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 */ diff --git a/api/src/test/java/com/github/gtache/fxml/compiler/TestGenerationException.java b/api/src/test/java/com/github/gtache/fxml/compiler/TestGenerationException.java new file mode 100644 index 0000000..d4e233b --- /dev/null +++ b/api/src/test/java/com/github/gtache/fxml/compiler/TestGenerationException.java @@ -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()); + } +} diff --git a/api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParseException.java b/api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParseException.java new file mode 100644 index 0000000..a44a6aa --- /dev/null +++ b/api/src/test/java/com/github/gtache/fxml/compiler/parsing/TestParseException.java @@ -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()); + } +} diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java index 486e47e..7bbd39b 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/ControllerInfoImpl.java @@ -18,6 +18,14 @@ public record ControllerInfoImpl(String className, Map handlerH Map 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); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java index 53da964..e6bd8eb 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationParametersImpl.java @@ -1,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 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); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java index 7db390a..2682959 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GenerationRequestImpl.java @@ -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); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java index 18275cc..3a8c068 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/GeneratorImpl.java @@ -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 helperProviderFactory; + + /** + * Instantiates a new generator + */ + public GeneratorImpl() { + this(HelperProvider::new); + } + + /** + * Used for testing + * @param helperProviderFactory The helper provider factory + */ + GeneratorImpl(final Function 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"); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/SourceInfoImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/SourceInfoImpl.java index ab20e88..950ff02 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/SourceInfoImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/SourceInfoImpl.java @@ -22,6 +22,16 @@ public record SourceInfoImpl(String generatedClassName, String controllerClassNa Map 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); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java index 54db734..af846aa 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ConstructorHelper.java @@ -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 getListConstructorArgs(final ConstructorArgs constructorArgs, final ParsedObject parsedObject) throws GenerationException { + static List getListConstructorArgs(final ConstructorArgs constructorArgs, final ParsedObject parsedObject) throws GenerationException { final var args = new ArrayList(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 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(); + } } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java index f0589dc..dc257b9 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ControllerInjector.java @@ -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 controllerFactoryPostAction; - ControllerInjector(final ControllerInfo controllerInfo, final InjectionType fieldInjectionType, final InjectionType methodInjectionType, - final StringBuilder sb, final SequencedCollection controllerFactoryPostAction) { - this.controllerInfo = controllerInfo; + ControllerInjector(final ControllerInfo controllerInfo, final ControllerFieldInjectionType fieldInjectionType, + final ControllerMethodsInjectionType methodInjectionType, final StringBuilder sb, + final SequencedCollection 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"; + }; } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java index f91d8a1..798601f 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FieldSetter.java @@ -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 controllerFactoryPostAction; - FieldSetter(final HelperProvider helperProvider, final InjectionType fieldInjectionType, + FieldSetter(final HelperProvider helperProvider, final ControllerFieldInjectionType fieldInjectionType, final StringBuilder sb, final SequencedCollection 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"); + } } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java index b7da57e..67fedba 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/FontFormatter.java @@ -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"); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java index a198550..66cfaf8 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationHelper.java @@ -1,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 idToVariableInfo; + private GenerationHelper() { - GenerationHelper(final HelperProvider helperProvider, final ControllerInfo controllerInfo, final Map 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)); - } } /** diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java index b88794d..8bed268 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/GenerationProgress.java @@ -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 idToVariableInfo, - Map variableNameCounters, - SequencedMap controllerClassToVariable, +public record GenerationProgress(GenerationRequest request, SequencedCollection controllerFactoryPostAction, StringBuilder stringBuilder) { @@ -32,18 +22,12 @@ public record GenerationProgress(GenerationRequest request, Map(), 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()); } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsFormatter.java index 151d039..7793911 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperMethodsFormatter.java @@ -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 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"); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperProvider.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperProvider.java index ed1a4eb..bf91540 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperProvider.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/HelperProvider.java @@ -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); + }); } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageFormatter.java index 9224cb6..7a95cd2 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ImageFormatter.java @@ -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) diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/InitializationFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/InitializationFormatter.java index 33ec821..acf5ed6 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/InitializationFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/InitializationFormatter.java @@ -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 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 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", "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", "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, " + 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(); - return hasDuplicateControllerClass(progress.request().sourceInfo(), set); + return hasDuplicateControllerClass(request.sourceInfo(), set); } private static boolean hasDuplicateControllerClass(final SourceInfo info, final Set 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(); 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")).append(bundleVariable).append(" = (java.util.function.Function) s -> \"").append(include.resources()).append("\";\n"); - yield bundleVariable; - } - case CONSTRUCTOR -> { - final var bundleVariable = progress.getNextVariableName("resourceBundle"); - sb.append(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")).append(bundleFunctionVariable).append(" = (java.util.function.Function) 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; + } + }; } } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/LoadMethodFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/LoadMethodFormatter.java index aca810f..8ce2685 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/LoadMethodFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/LoadMethodFormatter.java @@ -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")).append("fieldMap = new java.util.HashMap();\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"); } - } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java index 8d3189b..7daa067 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ObjectFormatter.java @@ -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 getDefines(final SequencedCollection objects) { + return objects.stream().filter(ParsedDefine.class::isInstance).map(ParsedDefine.class::cast).toList(); + } + + private static SequencedCollection getNotDefines(final SequencedCollection 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 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 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 allPropertyNames) throws GenerationException { + private void formatNoConstructor(final ParsedObject parsedObject, final String variableName, final Collection 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(); + 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 objects, final String parentVariable) throws GenerationException { - final var propertyName = property.name(); - final var variables = new ArrayList(); - 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 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); - } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java index c7b2db9..9124256 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/PropertyFormatter.java @@ -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 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 objects, final String parentVariable) throws GenerationException { + final var propertyName = property.name(); + final var variables = new ArrayList(); + 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 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); + } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneFormatter.java index 0410856..8d7546b 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/SceneFormatter.java @@ -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 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 getNonDefineObjects(final Collection 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 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")).append(tmpVariable).append(" = ").append(variableName).append(".getStyleSheets();\n"); sb.append(" ").append(tmpVariable).append(".addAll(").append(compatibilityHelper.getListOf()).append(String.join(", ", urlVariables)).append("));\n"); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshFormatter.java index c4875a0..084ea99 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/TriangleMeshFormatter.java @@ -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 List parseList(final CharSequence value, final Function 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()); } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLFormatter.java index 317b88a..e136106 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/URLFormatter.java @@ -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 formatURL(final Iterable 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); } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java index 56f4c11..5da8bf8 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/ValueFormatter.java @@ -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 idToVariableInfo; + private final HelperProvider helperProvider; + private final ResourceBundleInjectionType resourceInjectionType; - ValueFormatter(final InjectionType resourceInjectionType, final Map 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("\"", "\\\"") + "\""; diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableInfo.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableInfo.java index eace47e..10a6da7 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableInfo.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableInfo.java @@ -13,7 +13,15 @@ import java.util.Objects; * @param className The class name of the variable */ 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); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableProvider.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableProvider.java new file mode 100644 index 0000000..089911e --- /dev/null +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/VariableProvider.java @@ -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 variableNameCounters; + private final Map 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); + } +} diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewFormatter.java b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewFormatter.java index 88956a6..786d461 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewFormatter.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/impl/internal/WebViewFormatter.java @@ -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"); } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedConstantImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedConstantImpl.java index b3f0eab..39cde18 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedConstantImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedConstantImpl.java @@ -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 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))); + } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedCopyImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedCopyImpl.java index 4507ce5..5077607 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedCopyImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedCopyImpl.java @@ -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 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))); } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java index b841971..c6b6887 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedDefineImpl.java @@ -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 children) implements ParsedDefine { + /** + * Instantiates the define + * @param children The children + * @throws NullPointerException If the children are null + */ public ParsedDefineImpl { children = List.copyOf(children); } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedFactoryImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedFactoryImpl.java index 5a026c7..38c83c9 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedFactoryImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedFactoryImpl.java @@ -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 attributes, SequencedCollection arguments, SequencedCollection 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); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedIncludeImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedIncludeImpl.java index c6b94db..a4b2803 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedIncludeImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedIncludeImpl.java @@ -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 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 createAttributes(final String source, final String resources, final String fxId) { + final var map = HashMap.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; + } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedObjectImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedObjectImpl.java index 42e1039..16c3c2f 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedObjectImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedObjectImpl.java @@ -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 attributes, SequencedMap> properties, SequencedCollection 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); diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedPropertyImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedPropertyImpl.java index a7d0887..6907691 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedPropertyImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedPropertyImpl.java @@ -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); } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedReferenceImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedReferenceImpl.java index a3a55d5..042baa4 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedReferenceImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedReferenceImpl.java @@ -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 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))); + } } diff --git a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedValueImpl.java b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedValueImpl.java index bc01b04..33eab8d 100644 --- a/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedValueImpl.java +++ b/core/src/main/java/com/github/gtache/fxml/compiler/parsing/impl/ParsedValueImpl.java @@ -14,8 +14,32 @@ import java.util.Objects; */ public record ParsedValueImpl(String className, Map 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))); + } } diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index b55eddf..579e79a 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -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; diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java index b8efa0e..1a4f87f 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGenerationParametersImpl.java @@ -1,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 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); diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGeneratorImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGeneratorImpl.java new file mode 100644 index 0000000..c1cdaec --- /dev/null +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/TestGeneratorImpl.java @@ -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(); + } +} diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorHelper.java index 336a5ce..f4e12d3 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorHelper.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestConstructorHelper.java @@ -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[] constructors; + private final ParsedObject parsedObject; + private final Set propertyNames; + + TestConstructorHelper(@Mock final ConstructorArgs args, @Mock final Constructor constructor1, + @Mock final Constructor 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(); + 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(); + 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(); + 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(); + 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>(); + 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(); + 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(); + + 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 annotationType() { + return NamedArg.class; + } + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestControllerInjector.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestControllerInjector.java index 401103c..2e623da 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestControllerInjector.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestControllerInjector.java @@ -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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFieldSetter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFieldSetter.java index 9c55b49..9c8e756 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFieldSetter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFieldSetter.java @@ -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 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFontFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFontFormatter.java index e011032..1592421 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFontFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestFontFormatter.java @@ -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 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>(); + 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationCompatibilityHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationCompatibilityHelper.java index 911217c..6a342f8 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationCompatibilityHelper.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationCompatibilityHelper.java @@ -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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationHelper.java index 549ce32..99ec1cb 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationHelper.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationHelper.java @@ -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 idToVariableInfo; - private final String variableName; - private final ParsedObject object; + private final ParsedObject parsedObject; private final Map attributes; private final String className; private final ParsedProperty property; private final String propertyName; - TestGenerationHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request, - @Mock final ControllerInfo controllerInfo, @Mock final ControllerFieldInfo fieldInfo, - @Mock final ParsedObject object, @Mock final ParsedProperty property) { - this.progress = Objects.requireNonNull(progress); - this.request = Objects.requireNonNull(request); - this.controllerInfo = Objects.requireNonNull(controllerInfo); - this.fieldInfo = Objects.requireNonNull(fieldInfo); - this.object = Objects.requireNonNull(object); + 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)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationProgress.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationProgress.java index c8f8137..238cf27 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationProgress.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestGenerationProgress.java @@ -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 idToVariableInfo; - private final Map variableNameCounters; - private final SequencedMap controllerClassToVariable; private final SequencedCollection controllerFactoryPostAction; private final StringBuilder sb; private final GenerationProgress progress; - TestGenerationProgress(@Mock final GenerationRequest request, @Mock final 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(); - 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)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperMethodsFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperMethodsFormatter.java index 90826d8..7263e5f 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperMethodsFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperMethodsFormatter.java @@ -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 void callEventHandlerMethod(final String methodName, final T event) { + try { + final java.lang.reflect.Method method; + java.util.Listmethods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods()) + .filter(m -> m.getName().equals(methodName)).toList(); + if (methods.size() > 1) { + java.util.ListeventMethods = 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.ListemptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList(); + if (emptyMethods.size() == 1) { + method = emptyMethods.getFirst(); + } else { + throw new IllegalArgumentException("Multiple matching methods for " + methodName); + } + } + } else if (methods.size() == 1) { + method = methods.getFirst(); + } else { + throw new IllegalArgumentException("No matching method for " + methodName); + } + method.setAccessible(true); + if (method.getParameterCount() == 0) { + method.invoke(controller); + } else { + method.invoke(controller, event); + } + } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) { + throw new RuntimeException("Error using reflection on " + methodName, ex); + } + } + + private U callCallbackMethod(final String methodName, final T value, final Class clazz) { + try { + final java.lang.reflect.Method method; + java.util.Listmethods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods()) + .filter(m -> m.getName().equals(methodName)).toList(); + if (methods.size() > 1) { + java.util.ListeventMethods = 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 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 void callEventHandlerMethod(final String methodName, final T event) { + try { + final java.lang.reflect.Method method; + java.util.Listmethods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods()) + .filter(m -> m.getName().equals(methodName)).toList(); + if (methods.size() > 1) { + java.util.ListeventMethods = 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.ListemptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList(); + if (emptyMethods.size() == 1) { + method = emptyMethods.getFirst(); + } else { + throw new IllegalArgumentException("Multiple matching methods for " + methodName); + } + } + } else if (methods.size() == 1) { + method = methods.getFirst(); + } else { + throw new IllegalArgumentException("No matching method for " + methodName); + } + method.setAccessible(true); + if (method.getParameterCount() == 0) { + method.invoke(controller); + } else { + method.invoke(controller, event); + } + } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) { + throw new RuntimeException("Error using reflection on " + methodName, ex); + } + } + + private U callCallbackMethod(final String methodName, final T value, final Class clazz) { + try { + final java.lang.reflect.Method method; + java.util.Listmethods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods()) + .filter(m -> m.getName().equals(methodName)).toList(); + if (methods.size() > 1) { + java.util.ListeventMethods = 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 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperProvider.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperProvider.java new file mode 100644 index 0000000..9c98c04 --- /dev/null +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestHelperProvider.java @@ -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 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()); + } +} diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestImageFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestImageFormatter.java index e906702..441ed69 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestImageFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestImageFormatter.java @@ -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 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>(); + 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestInitializationFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestInitializationFormatter.java index ad46f4d..051e74b 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestInitializationFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestInitializationFormatter.java @@ -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 sourceToSourceInfo; + private final StringBuilder sb; + private final Map 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, 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, 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 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 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 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 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, com.github.gtache.fxml.ControllerClassName> controllerFactory; + private final java.util.function.Function, com.github.gtache.fxml.Controller2> controller2Factory; + private final java.util.function.Function, 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, com.github.gtache.fxml.ControllerClassName> controllerFactory, final java.util.function.Function, com.github.gtache.fxml.Controller2> controller2Factory, final java.util.function.Function, 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 controllerFactory; + private final java.util.function.Supplier controller2Factory; + private final java.util.function.Supplier 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 controllerFactory, final java.util.function.Supplier controller2Factory, final java.util.function.Supplier 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 controllerFactory; + private final java.util.function.Supplier controller2Factory; + private final java.util.function.Supplier 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 controllerFactory, final java.util.function.Supplier controller2Factory, final java.util.function.Supplier 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.FunctionresourceBundleFunction = (java.util.function.Function) 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestLoadMethodFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestLoadMethodFormatter.java index 32a1e9b..45c970d 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestLoadMethodFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestLoadMethodFormatter.java @@ -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 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 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 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 load() { + if (loaded) { + throw new IllegalStateException("Already loaded"); + } + java.util.ResourceBundleresourceBundle = java.util.ResourceBundle.getBundle("bundle"); + java.util.MapfieldMap = new java.util.HashMap(); + 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 load() { + if (loaded) { + throw new IllegalStateException("Already loaded"); + } + java.util.MapfieldMap = new java.util.HashMap(); + 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)); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestObjectFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestObjectFormatter.java index e7d8454..6aeae94 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestObjectFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestObjectFormatter.java @@ -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 = ""; + when(reflectionHelper.getGenericTypes(parsedObject)).thenReturn(""); + 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>(); + 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>(); + 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>(); + 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>(); + 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(); + 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.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.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>(); + 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(); + 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(); + 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(); + 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.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>(); + 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(""); + objectFormatter.format(parsedObject, variableName); + final var expected = "definestartVarvariable = new javafx.scene.control.Spinner(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.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>(); + 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.of(); + final var label = new ParsedObjectImpl("javafx.scene.control.Label", Map.of(), new LinkedHashMap<>(), List.of()); + final var children = List.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)); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestPropertyFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestPropertyFormatter.java index f32fe85..06376d3 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestPropertyFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestPropertyFormatter.java @@ -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 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>(); + 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>(); + 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>(); + 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>(); + 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>(); + 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>(); + 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>(); + 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java index 5a701ef..2728420 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestReflectionHelper.java @@ -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("", 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.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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestSceneFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestSceneFormatter.java index 61f5021..790ed51 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestSceneFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestSceneFormatter.java @@ -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 children; + private final SequencedMap> properties; + private final Map 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) 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.Liststylesheets = 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestTriangleMeshFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestTriangleMeshFormatter.java index aac331f..df22a2c 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestTriangleMeshFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestTriangleMeshFormatter.java @@ -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 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>(); + 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)); + } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestURLFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestURLFormatter.java index 8b778e9..a6af9cb 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestURLFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestURLFormatter.java @@ -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>(); - 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)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestValueFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestValueFormatter.java index ea6c016..2f0b9f3 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestValueFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestValueFormatter.java @@ -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 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)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestVariableProvider.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestVariableProvider.java new file mode 100644 index 0000000..6ddc796 --- /dev/null +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestVariableProvider.java @@ -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)); + } +} diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestWebViewFormatter.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestWebViewFormatter.java index 4be2cbc..4bca54f 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestWebViewFormatter.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/TestWebViewFormatter.java @@ -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 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>(); - 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)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/WholeConstructorArgs.java b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/WholeConstructorArgs.java index cf0f75c..bc2fabb 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/WholeConstructorArgs.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/impl/internal/WholeConstructorArgs.java @@ -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) { } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedConstantImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedConstantImpl.java index 1748e4c..5a1a9c6 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedConstantImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedConstantImpl.java @@ -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.of(); assertThrows(NullPointerException.class, () -> new ParsedConstantImpl(null, attributes)); - assertThrows(NullPointerException.class, () -> new ParsedConstantImpl(className, null)); + assertThrows(NullPointerException.class, () -> new ParsedConstantImpl(className, (Map) null)); + assertThrows(IllegalArgumentException.class, () -> new ParsedConstantImpl(className, emptyMap)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedCopyImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedCopyImpl.java index 12f5881..b975df5 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedCopyImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedCopyImpl.java @@ -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) null)); + final var emptyMap = Map.of(); + assertThrows(IllegalArgumentException.class, () -> new ParsedCopyImpl(emptyMap)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedDefineImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedDefineImpl.java index c40e831..bcb382a 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedDefineImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedDefineImpl.java @@ -19,7 +19,7 @@ class TestParsedDefineImpl { private final List 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); } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedIncludeImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedIncludeImpl.java index 25f4702..25670d1 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedIncludeImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedIncludeImpl.java @@ -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.of(); + assertThrows(IllegalArgumentException.class, () -> new ParsedIncludeImpl(emptyMap)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedObjectImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedObjectImpl.java index c92a503..0afd9b3 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedObjectImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedObjectImpl.java @@ -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); } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedPropertyImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedPropertyImpl.java index 09884df..0bd441e 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedPropertyImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedPropertyImpl.java @@ -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)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedReferenceImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedReferenceImpl.java index 412aed5..679e28e 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedReferenceImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedReferenceImpl.java @@ -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) null)); + final var emptyMap = Map.of(); + assertThrows(IllegalArgumentException.class, () -> new ParsedReferenceImpl(emptyMap)); } } diff --git a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedValueImpl.java b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedValueImpl.java index 68657b6..88d274a 100644 --- a/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedValueImpl.java +++ b/core/src/test/java/com/github/gtache/fxml/compiler/parsing/impl/TestParsedValueImpl.java @@ -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) null)); + final var emptyMap = Map.of(); + assertThrows(IllegalArgumentException.class, () -> new ParsedValueImpl(className, emptyMap)); } } diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java index 10a28e3..58af699 100644 --- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java +++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/FXMLCompilerMojo.java @@ -1,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 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 createControllerMapping(final Map fxmls) throws MojoExecutionException { @@ -76,7 +93,8 @@ public class FXMLCompilerMojo extends AbstractMojo { return mapping; } - private Map createCompilationInfoMapping(final Map fxmls, final Map controllerMapping) throws MojoExecutionException { + private Map createCompilationInfoMapping(final Map fxmls, + final Map controllerMapping) throws MojoExecutionException { final var mapping = new HashMap(); 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 createControllerMapping(final Map fxmls, + final ExecutorService executor) { + final var mapping = new ConcurrentHashMap(); + 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 createCompilationInfoMapping(final Map fxmls, + final Map controllerMapping, final ExecutorService executor) { + final var mapping = new ConcurrentHashMap(); + 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 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()); + } } diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/Compiler.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/Compiler.java index b25f8e4..e30c9ce 100644 --- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/Compiler.java +++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/Compiler.java @@ -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 mapping, final GenerationParameters parameters) throws MojoExecutionException { + public static void compile(final Path inputPath, final CompilationInfo info, final Map mapping, final GenerationParameters parameters) throws MojoExecutionException { try { logger.info("Parsing {} with {}", inputPath, PARSER.getClass().getSimpleName()); final var root = PARSER.parse(inputPath); diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerProvider.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerProvider.java index eda7c28..d0358c0 100644 --- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerProvider.java +++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/ControllerProvider.java @@ -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; diff --git a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FXMLProvider.java b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FXMLProvider.java index b32431a..8509150 100644 --- a/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FXMLProvider.java +++ b/maven-plugin/src/main/java/com/github/gtache/fxml/compiler/maven/internal/FXMLProvider.java @@ -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); diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompiler.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompiler.java index c51d395..e8ef95f 100644 --- a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompiler.java +++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestCompiler.java @@ -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)); - } } diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerProvider.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerProvider.java index 1142a55..642a3c9 100644 --- a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerProvider.java +++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestControllerProvider.java @@ -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"); diff --git a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFXMLProvider.java b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFXMLProvider.java index 68ae3af..54ca98a 100644 --- a/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFXMLProvider.java +++ b/maven-plugin/src/test/java/com/github/gtache/fxml/compiler/maven/internal/TestFXMLProvider.java @@ -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 diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..19529dd --- /dev/null +++ b/mvnw @@ -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-,maven-mvnd--}/ +[ -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 "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..b150b91 --- /dev/null +++ b/mvnw.cmd @@ -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-,maven-mvnd--}/ +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" diff --git a/pom.xml b/pom.xml index 12d31a3..62bfddc 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ - 3.8.0 + 3.6.3 21 21 UTF-8 @@ -93,8 +93,6 @@ ${plugin.deploy.version} - deploy - deploy deploy @@ -107,7 +105,6 @@ ${plugin.enforcer.version} - enforce-versions enforce @@ -137,16 +134,8 @@ ${plugin.failsafe.version} - integration-test - test integration-test - - - - verify - verify - verify @@ -158,26 +147,10 @@ ${plugin.jacoco.version} - default-prepare-agent prepare-agent - - - - default-prepare-agent-integration - prepare-agent-integration - - - - default-report - report - - - - default-report-integration - report-integration @@ -189,8 +162,6 @@ ${plugin.javadoc.version} - attach-javadocs - install jar @@ -221,8 +192,6 @@ ${plugin.source.version} - attach-sources - package jar-no-fork diff --git a/xml/src/main/java/module-info.java b/xml/src/main/java/module-info.java index 582863b..90050d3 100644 --- a/xml/src/main/java/module-info.java +++ b/xml/src/main/java/module-info.java @@ -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; diff --git a/xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestDOMFXMLParser.java b/xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestDOMFXMLParser.java index 3f66394..fae9493 100644 --- a/xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestDOMFXMLParser.java +++ b/xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestDOMFXMLParser.java @@ -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); diff --git a/xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestGeneratorImpl.java b/xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestGeneratorImpl.java deleted file mode 100644 index d381c95..0000000 --- a/xml/src/test/java/com/github/gtache/fxml/compiler/parsing/xml/TestGeneratorImpl.java +++ /dev/null @@ -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 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"))); - FIELD_INFO_MAP.put("treeTableColumn1", new ControllerFieldInfoImpl("treeTableColumn1", List.of("javafx.scene.control.TreeItem", "String"))); - FIELD_INFO_MAP.put("treeTableColumn2", new ControllerFieldInfoImpl("treeTableColumn2", List.of("javafx.scene.control.TreeItem", "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 providesGenerationTestCases() { - final var files = List.of("Controls", "Includes"); - final var list = new ArrayList(); - 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(); - } -} diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/ControlsBundle.properties b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/ControlsBundle.properties deleted file mode 100644 index 3fdc95e..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/ControlsBundle.properties +++ /dev/null @@ -1 +0,0 @@ -include.label=Label \ No newline at end of file diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/IncludesBundle.properties b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/IncludesBundle.properties deleted file mode 100644 index ae4369c..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/IncludesBundle.properties +++ /dev/null @@ -1 +0,0 @@ -media.volume.label=Volume \ No newline at end of file diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/controlsView.fxml b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/controlsView.fxml deleted file mode 100644 index 798f2a2..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/controlsView.fxml +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - text1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text - - - - true - false - CheckBox - 1 - - - - - - - - - - - - - - - - - - - - - - - - true - 100 - - - - - - - - - - - - - - - - - - - File - - - - - - - - - - - - - - - - - - - - - - 5.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include1.fxml b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include1.fxml deleted file mode 100644 index a03a5ef..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include1.fxml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include2.fxml b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include2.fxml deleted file mode 100644 index 863be39..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include2.fxml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include21.fxml b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include21.fxml deleted file mode 100644 index e110758..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include21.fxml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include3.fxml b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include3.fxml deleted file mode 100644 index a03a5ef..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/include3.fxml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/includesView.fxml b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/includesView.fxml deleted file mode 100644 index a03a5ef..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includes/includesView.fxml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includesView.fxml b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includesView.fxml deleted file mode 100644 index fe1ebc8..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/includesView.fxml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
diff --git a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/style.css b/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/style.css deleted file mode 100644 index 378b0d7..0000000 --- a/xml/src/test/resources/com/github/gtache/fxml/compiler/parsing/xml/style.css +++ /dev/null @@ -1,3 +0,0 @@ -.clazz { - -fx-font-weight: bold; -} \ No newline at end of file