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

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

View File

@@ -1,25 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
/**
* Base field {@link InjectionType}s
*/
public enum ControllerFieldInjectionTypes implements InjectionType {
/**
* Inject using variable assignment
*/
ASSIGN,
/**
* Inject using a factory
*/
FACTORY,
/**
* Inject using reflection
*/
REFLECTION,
/**
* Inject using setters
*/
SETTERS
}

View File

@@ -18,6 +18,14 @@ public record ControllerInfoImpl(String className, Map<String, Boolean> handlerH
Map<String, ControllerFieldInfo> fieldInfo,
boolean hasInitialize) implements ControllerInfo {
/**
* Instantiates a new controller info
* @param className The controller class name
* @param handlerHasArgument The mapping of method name to true if the method has an argument
* @param fieldInfo The mapping of property name to controller field info
* @param hasInitialize True if the controller has an initialize method
* @throws NullPointerException If any parameter is null
*/
public ControllerInfoImpl {
Objects.requireNonNull(className);
handlerHasArgument = Map.copyOf(handlerHasArgument);

View File

@@ -1,17 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
/**
* Base controller {@link InjectionType}s
*/
public enum ControllerInjectionTypes implements InjectionType {
/**
* Inject the controller instance
*/
INSTANCE,
/**
* Inject a controller factory
*/
FACTORY
}

View File

@@ -1,17 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
/**
* Base methods {@link InjectionType}s
*/
public enum ControllerMethodsInjectionType implements InjectionType {
/**
* Inject using visible methods
*/
REFERENCE,
/**
* Inject using reflection
*/
REFLECTION,
}

View File

@@ -1,7 +1,10 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import java.util.Map;
@@ -22,11 +25,23 @@ import static java.util.Objects.requireNonNull;
*/
public record GenerationParametersImpl(GenerationCompatibility compatibility, boolean useImageInputStreamConstructor,
Map<String, String> bundleMap,
InjectionType controllerInjectionType,
InjectionType fieldInjectionType,
InjectionType methodInjectionType,
InjectionType resourceInjectionType) implements GenerationParameters {
ControllerInjectionType controllerInjectionType,
ControllerFieldInjectionType fieldInjectionType,
ControllerMethodsInjectionType methodInjectionType,
ResourceBundleInjectionType resourceInjectionType) implements GenerationParameters {
/**
* Instantiates new parameters
*
* @param compatibility The compatibility info
* @param useImageInputStreamConstructor True if the InputStream constructor should be used
* @param bundleMap The mapping of controller class to resource bundle path
* @param controllerInjectionType The controller injection type
* @param fieldInjectionType The field injection type
* @param methodInjectionType The method injection type
* @param resourceInjectionType The resource injection type
* @throws NullPointerException if any parameter is null
*/
public GenerationParametersImpl {
requireNonNull(compatibility);
bundleMap = Map.copyOf(bundleMap);

View File

@@ -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);

View File

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

View File

@@ -1,29 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
/**
* Base {@link InjectionType}s for resource bundles
*/
public enum ResourceBundleInjectionTypes implements InjectionType {
/**
* Resource bundle is injected in the constructor
*/
CONSTRUCTOR,
/**
* Resource bundle is injected as a function in the constructor
*/
CONSTRUCTOR_FUNCTION,
/**
* Resource bundle name is injected in the constructor
*/
CONSTRUCTOR_NAME,
/**
* Resource bundle is loaded using getBundle
*/
GET_BUNDLE,
/**
* Resource bundle is retrieved from controller using getter
*/
GETTER
}

View File

@@ -22,6 +22,16 @@ public record SourceInfoImpl(String generatedClassName, String controllerClassNa
Map<String, SourceInfo> sourceToSourceInfo,
boolean requiresResourceBundle) implements SourceInfo {
/**
* Instantiates a new source info
* @param generatedClassName The generated class name
* @param controllerClassName The controller class name
* @param sourceFile The source file
* @param includedSources The included sources
* @param sourceToSourceInfo The mapping of source value to source info
* @param requiresResourceBundle True if the subtree requires a resource bundle
* @throws NullPointerException If any parameter is null
*/
public SourceInfoImpl {
Objects.requireNonNull(generatedClassName);
Objects.requireNonNull(controllerClassName);

View File

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

View File

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

View File

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

View File

@@ -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");

View File

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

View File

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

View File

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

View File

@@ -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);
});
}
}

View File

@@ -4,10 +4,9 @@ import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);

View File

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

View File

@@ -15,11 +15,11 @@ import static java.util.Objects.requireNonNull;
final class WebViewFormatter {
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");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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));
}
}

View File

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

View File

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

View File

@@ -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));
}
}

View File

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

View File

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

View File

@@ -1,8 +1,217 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestHelperMethodsFormatter {
private final HelperProvider helperProvider;
private final GenerationCompatibilityHelper compatibilityHelper;
private final StringBuilder sb;
TestHelperMethodsFormatter(@Mock final HelperProvider helperProvider, @Mock final GenerationCompatibilityHelper compatibilityHelper) {
this.helperProvider = Objects.requireNonNull(helperProvider);
this.compatibilityHelper = Objects.requireNonNull(compatibilityHelper);
this.sb = new StringBuilder();
}
@BeforeEach
void beforeEach() {
when(helperProvider.getCompatibilityHelper()).thenReturn(compatibilityHelper);
when(compatibilityHelper.getStartVar(anyString())).then(i -> i.getArgument(0));
when(compatibilityHelper.getStartVar(anyString(), anyInt())).then(i -> i.getArgument(0));
when(compatibilityHelper.getListOf()).thenReturn("listof(");
when(compatibilityHelper.getGetFirst()).thenReturn(".getFirst()");
when(compatibilityHelper.getToList()).thenReturn(".toList()");
}
@Test
void testMethodReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.ASSIGN, ControllerMethodsInjectionType.REFLECTION, sb);
final var expected = """
private <T extends javafx.event.Event> void callEventHandlerMethod(final String methodName, final T event) {
try {
final java.lang.reflect.Method method;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0])).toList();
if (eventMethods.size() == 1) {
method = eventMethods.getFirst();
} else {
java.util.List<java.lang.reflect.Method>emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList();
if (emptyMethods.size() == 1) {
method = emptyMethods.getFirst();
} else {
throw new IllegalArgumentException("Multiple matching methods for " + methodName);
}
}
} else if (methods.size() == 1) {
method = methods.getFirst();
} else {
throw new IllegalArgumentException("No matching method for " + methodName);
}
method.setAccessible(true);
if (method.getParameterCount() == 0) {
method.invoke(controller);
} else {
method.invoke(controller, event);
}
} catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
throw new RuntimeException("Error using reflection on " + methodName, ex);
}
}
private <T, U> U callCallbackMethod(final String methodName, final T value, final Class<T> clazz) {
try {
final java.lang.reflect.Method method;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 2 && clazz.isAssignableFrom(m.getParameterTypes()[1])).toList();
if (eventMethods.size() == 1) {
method = eventMethods.getFirst();
} else {
throw new IllegalArgumentException("Multiple matching methods for " + methodName);
}
} else if (methods.size() == 1) {
method = methods.getFirst();
} else {
throw new IllegalArgumentException("No matching method for " + methodName);
}
method.setAccessible(true);
return (U) method.invoke(controller, value);
} catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
throw new RuntimeException("Error using reflection on " + methodName, ex);
}
}
""";
helperMethodsFormatter.formatHelperMethods();
assertEquals(expected, sb.toString());
}
@Test
void testFieldReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.REFLECTION, ControllerMethodsInjectionType.REFERENCE, sb);
final var expected = """
private <T> void injectField(final String fieldName, final T object) {
try {
java.lang.reflect.Fieldfield = controller.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(controller, object);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException("Error using reflection on " + fieldName, e);
}
}
""";
helperMethodsFormatter.formatHelperMethods();
assertEquals(expected, sb.toString());
}
@Test
void testNoReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb);
helperMethodsFormatter.formatHelperMethods();
assertEquals("", sb.toString());
}
@Test
void testBothReflection() {
final var helperMethodsFormatter = new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.REFLECTION, ControllerMethodsInjectionType.REFLECTION, sb);
final var expected = """
private <T extends javafx.event.Event> void callEventHandlerMethod(final String methodName, final T event) {
try {
final java.lang.reflect.Method method;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0])).toList();
if (eventMethods.size() == 1) {
method = eventMethods.getFirst();
} else {
java.util.List<java.lang.reflect.Method>emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList();
if (emptyMethods.size() == 1) {
method = emptyMethods.getFirst();
} else {
throw new IllegalArgumentException("Multiple matching methods for " + methodName);
}
}
} else if (methods.size() == 1) {
method = methods.getFirst();
} else {
throw new IllegalArgumentException("No matching method for " + methodName);
}
method.setAccessible(true);
if (method.getParameterCount() == 0) {
method.invoke(controller);
} else {
method.invoke(controller, event);
}
} catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
throw new RuntimeException("Error using reflection on " + methodName, ex);
}
}
private <T, U> U callCallbackMethod(final String methodName, final T value, final Class<T> clazz) {
try {
final java.lang.reflect.Method method;
java.util.List<java.lang.reflect.Method>methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
java.util.List<java.lang.reflect.Method>eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 2 && clazz.isAssignableFrom(m.getParameterTypes()[1])).toList();
if (eventMethods.size() == 1) {
method = eventMethods.getFirst();
} else {
throw new IllegalArgumentException("Multiple matching methods for " + methodName);
}
} else if (methods.size() == 1) {
method = methods.getFirst();
} else {
throw new IllegalArgumentException("No matching method for " + methodName);
}
method.setAccessible(true);
return (U) method.invoke(controller, value);
} catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
throw new RuntimeException("Error using reflection on " + methodName, ex);
}
}
private <T> void injectField(final String fieldName, final T object) {
try {
java.lang.reflect.Fieldfield = controller.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(controller, object);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException("Error using reflection on " + fieldName, e);
}
}
""";
helperMethodsFormatter.formatHelperMethods();
assertEquals(expected, sb.toString());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(null, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, sb));
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(helperProvider, null, ControllerMethodsInjectionType.REFERENCE, sb));
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.FACTORY, null, sb));
assertThrows(NullPointerException.class, () -> new HelperMethodsFormatter(helperProvider, ControllerFieldInjectionType.FACTORY, ControllerMethodsInjectionType.REFERENCE, null));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) {
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

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

View File

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