Fixes generics, only creates one constructor, adds some tests, adds java compatibility options, rework some classes, fixes some problems

This commit is contained in:
Guillaume Tâche
2024-12-13 21:20:49 +01:00
parent 7ec52cd175
commit d63188e8ee
172 changed files with 3955 additions and 17340 deletions

View File

@@ -0,0 +1,22 @@
package com.github.gtache.fxml.compiler.compatibility.impl;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
/**
* Implementation of {@link GenerationCompatibility}
*
* @param javaVersion The minimum supported Java version
*/
public record GenerationCompatibilityImpl(int javaVersion) implements GenerationCompatibility {
/**
* Instantiates a new compatibility
*
* @param javaVersion The minimum supported Java version
*/
public GenerationCompatibilityImpl {
if (javaVersion < 8) {
throw new IllegalArgumentException("Java version must be at least 8");
}
}
}

View File

@@ -1,6 +1,7 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.GenericTypes;
import java.util.List;
import java.util.Objects;
@@ -11,7 +12,7 @@ import java.util.Objects;
* @param name The field name
* @param genericTypes The generic types
*/
public record ControllerFieldInfoImpl(String name, List<String> genericTypes) implements ControllerFieldInfo {
public record ControllerFieldInfoImpl(String name, List<GenericTypes> genericTypes) implements ControllerFieldInfo {
/**
* Instantiates a new info

View File

@@ -1,10 +1,9 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.InjectionType;
/**
* Base field {@link InjectionType}s for {@link ControllerInjection}
* Base field {@link InjectionType}s
*/
public enum ControllerFieldInjectionTypes implements InjectionType {
/**

View File

@@ -4,17 +4,22 @@ import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import java.util.Map;
import java.util.Objects;
/**
* Implementation of {@link ControllerInfo}
*
* @param className The controller class name
* @param handlerHasArgument The mapping of method name to true if the method has an argument
* @param fieldInfo The mapping of property name to controller field info
* @param hasInitialize True if the controller has an initialize method
*/
public record ControllerInfoImpl(Map<String, Boolean> handlerHasArgument,
Map<String, ControllerFieldInfo> fieldInfo) implements ControllerInfo {
public record ControllerInfoImpl(String className, Map<String, Boolean> handlerHasArgument,
Map<String, ControllerFieldInfo> fieldInfo,
boolean hasInitialize) implements ControllerInfo {
public ControllerInfoImpl {
Objects.requireNonNull(className);
handlerHasArgument = Map.copyOf(handlerHasArgument);
fieldInfo = Map.copyOf(fieldInfo);
}

View File

@@ -1,22 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.InjectionType;
import java.util.Objects;
/**
* Implementation of {@link ControllerInjection}
*
* @param fieldInjectionType The field injection type
* @param methodInjectionType The method injection type
* @param injectionClass The injection class name
*/
public record ControllerInjectionImpl(InjectionType fieldInjectionType, InjectionType methodInjectionType,
String injectionClass) implements ControllerInjection {
public ControllerInjectionImpl {
Objects.requireNonNull(fieldInjectionType);
Objects.requireNonNull(methodInjectionType);
Objects.requireNonNull(injectionClass);
}
}

View File

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

View File

@@ -1,10 +1,9 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.InjectionType;
/**
* Base methods {@link InjectionType}s for {@link ControllerInjection}
* Base methods {@link InjectionType}s
*/
public enum ControllerMethodsInjectionType implements InjectionType {
/**

View File

@@ -1,30 +1,38 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.ResourceBundleInjection;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import java.util.Map;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link GenerationParameters}
*
* @param controllerInjections The mapping of controller class name to controller injection
* @param sourceToGeneratedClassName The mapping of fx:include source to generated class name
* @param sourceToControllerName The mapping of fx:include source to controller class name
* @param resourceBundleInjection The resource bundle injection
* @param compatibility The compatibility info
* @param useImageInputStreamConstructor True if the InputStream constructor should be used
* @param bundleMap The mapping of controller class to resource bundle path
* @param controllerInjectionType The controller injection type
* @param fieldInjectionType The field injection type
* @param methodInjectionType The method injection type
* @param resourceInjectionType The resource injection type
*/
public record GenerationParametersImpl(Map<String, ControllerInjection> controllerInjections,
Map<String, String> sourceToGeneratedClassName,
Map<String, String> sourceToControllerName,
ResourceBundleInjection resourceBundleInjection) implements GenerationParameters {
public record GenerationParametersImpl(GenerationCompatibility compatibility, boolean useImageInputStreamConstructor,
Map<String, String> bundleMap,
InjectionType controllerInjectionType,
InjectionType fieldInjectionType,
InjectionType methodInjectionType,
InjectionType resourceInjectionType) implements GenerationParameters {
public GenerationParametersImpl {
controllerInjections = Map.copyOf(controllerInjections);
sourceToGeneratedClassName = Map.copyOf(sourceToGeneratedClassName);
sourceToControllerName = Map.copyOf(sourceToControllerName);
Objects.requireNonNull(resourceBundleInjection);
requireNonNull(compatibility);
bundleMap = Map.copyOf(bundleMap);
requireNonNull(controllerInjectionType);
requireNonNull(fieldInjectionType);
requireNonNull(methodInjectionType);
requireNonNull(resourceInjectionType);
}
}

View File

@@ -3,6 +3,7 @@ package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import java.util.Objects;
@@ -12,15 +13,17 @@ import java.util.Objects;
*
* @param parameters The generation parameters
* @param controllerInfo The controller info
* @param sourceInfo The source info
* @param rootObject The root object
* @param outputClassName The output class name
*/
public record GenerationRequestImpl(GenerationParameters parameters, ControllerInfo controllerInfo,
ParsedObject rootObject,
SourceInfo sourceInfo, ParsedObject rootObject,
String outputClassName) implements GenerationRequest {
public GenerationRequestImpl {
Objects.requireNonNull(parameters);
Objects.requireNonNull(controllerInfo);
Objects.requireNonNull(sourceInfo);
Objects.requireNonNull(rootObject);
Objects.requireNonNull(outputClassName);
}

View File

@@ -3,12 +3,10 @@ package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.Generator;
import com.github.gtache.fxml.compiler.impl.internal.ConstructorFormatter;
import com.github.gtache.fxml.compiler.impl.internal.GenerationProgress;
import com.github.gtache.fxml.compiler.impl.internal.HelperMethodsProvider;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getControllerInjection;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getVariablePrefix;
import static com.github.gtache.fxml.compiler.impl.internal.ObjectFormatter.format;
import com.github.gtache.fxml.compiler.impl.internal.HelperMethodsFormatter;
import com.github.gtache.fxml.compiler.impl.internal.LoadMethodFormatter;
//TODO handle binding (${})
@@ -16,7 +14,7 @@ import static com.github.gtache.fxml.compiler.impl.internal.ObjectFormatter.form
* Implementation of {@link Generator}
*/
public class GeneratorImpl implements Generator {
@Override
public String generate(final GenerationRequest request) throws GenerationException {
@@ -24,143 +22,37 @@ public class GeneratorImpl implements Generator {
final var className = request.outputClassName();
final var pkgName = className.substring(0, className.lastIndexOf('.'));
final var simpleClassName = className.substring(className.lastIndexOf('.') + 1);
final var loadMethod = getLoadMethod(progress);
final var controllerInjection = getControllerInjection(progress);
final var controllerInjectionType = controllerInjection.fieldInjectionType();
final var controllerInjectionClass = controllerInjection.injectionClass();
final String constructorArgument;
final String constructorControllerJavadoc;
final String controllerArgumentType;
final String controllerMapType;
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
constructorArgument = "controllerFactory";
constructorControllerJavadoc = "controller factory";
controllerArgumentType = "com.github.gtache.fxml.compiler.ControllerFactory<" + controllerInjectionClass + ">";
controllerMapType = "com.github.gtache.fxml.compiler.ControllerFactory<?>";
} else {
constructorArgument = "controller";
constructorControllerJavadoc = "controller";
controllerArgumentType = controllerInjectionClass;
controllerMapType = "Object";
}
final var helperMethods = HelperMethodsProvider.getHelperMethods(progress);
return """
package %1$s;
/**
* Generated code, not thread-safe
*/
public final class %2$s {
private final java.util.Map<Class<?>, %7$s> controllersMap;
private final java.util.Map<Class<?>, java.util.ResourceBundle> resourceBundlesMap;
private boolean loaded;
private %3$s controller;
/**
* Instantiates a new %2$s with no nested controllers and no resource bundle
* @param %4$s The %5$s
*/
public %2$s(final %8$s %4$s) {
this(java.util.Map.of(%3$s.class, %4$s), java.util.Map.of());
}
/**
* Instantiates a new %2$s with no nested controllers
* @param %4$s The %5$s
* @param resourceBundle The resource bundle
*/
public %2$s(final %8$s %4$s, final java.util.ResourceBundle resourceBundle) {
this(java.util.Map.of(%3$s.class, %4$s), java.util.Map.of(%3$s.class, resourceBundle));
}
/**
* Instantiates a new %2$s with nested controllers
* @param controllersMap The map of controller class to %5$s
* @param resourceBundlesMap The map of controller class to resource bundle
*/
public %2$s(final java.util.Map<Class<?>, %7$s> controllersMap, final java.util.Map<Class<?>, java.util.ResourceBundle> resourceBundlesMap) {
this.controllersMap = java.util.Map.copyOf(controllersMap);
this.resourceBundlesMap = java.util.Map.copyOf(resourceBundlesMap);
}
/**
* Loads the view. Can only be called once.
*
* @return The view parent
*/
%6$s
%9$s
/**
* @return The controller
*/
public %3$s controller() {
if (loaded) {
return controller;
} else {
throw new IllegalStateException("Not loaded");
}
}
}
""".formatted(pkgName, simpleClassName, controllerInjectionClass, constructorArgument, constructorControllerJavadoc,
loadMethod, controllerMapType, controllerArgumentType, helperMethods);
}
/**
* Computes the load method
*
* @param progress The generation progress
* @return The load method
*/
private static String getLoadMethod(final GenerationProgress progress) throws GenerationException {
final var request = progress.request();
final var rootObject = request.rootObject();
final var controllerInjection = getControllerInjection(progress);
final var controllerInjectionType = controllerInjection.fieldInjectionType();
final var controllerClass = controllerInjection.injectionClass();
final var controllerInjectionClass = request.controllerInfo().className();
final var sb = progress.stringBuilder();
sb.append("public <T> T load() {\n");
sb.append(" if (loaded) {\n");
sb.append(" throw new IllegalStateException(\"Already loaded\");\n");
sb.append(" }\n");
final var resourceBundleInjection = request.parameters().resourceBundleInjection();
if (resourceBundleInjection.injectionType() == ResourceBundleInjectionTypes.GET_BUNDLE) {
sb.append(" final var bundle = java.util.ResourceBundle.getBundle(\"").append(resourceBundleInjection.bundleName()).append("\");\n");
} else if (resourceBundleInjection.injectionType() == ResourceBundleInjectionTypes.CONSTRUCTOR) {
sb.append(" final var bundle = resourceBundlesMap.get(").append(controllerClass).append(".class);\n");
}
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
sb.append(" final var fieldMap = new HashMap<String, Object>();\n");
} else {
sb.append(" controller = (").append(controllerClass).append(") controllersMap.get(").append(controllerClass).append(".class);\n");
}
final var variableName = progress.getNextVariableName(getVariablePrefix(rootObject));
format(progress, rootObject, variableName);
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
sb.append(" final var controllerFactory = controllersMap.get(").append(controllerClass).append(".class);\n");
sb.append(" controller = (").append(controllerClass).append(") controllerFactory.create(fieldMap);\n");
progress.controllerFactoryPostAction().forEach(sb::append);
}
if (controllerInjection.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) {
sb.append(" try {\n");
sb.append(" final var initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n");
sb.append(" initialize.setAccessible(true);\n");
sb.append(" initialize.invoke(controller);\n");
sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n");
sb.append(" } catch (final NoSuchMethodException ignored) {\n");
sb.append(" }\n");
} else {
sb.append(" controller.initialize();\n");
}
sb.append(" loaded = true;\n");
sb.append(" return (T) ").append(variableName).append(";\n");
sb.append("}");
sb.append("package ").append(pkgName).append(";\n");
sb.append("\n");
sb.append("/**\n");
sb.append(" * Generated code, not thread-safe\n");
sb.append(" */\n");
sb.append("public final class ").append(simpleClassName).append(" {\n");
sb.append("\n");
ConstructorFormatter.formatFieldsAndConstructor(progress);
sb.append("\n");
LoadMethodFormatter.formatLoadMethod(progress);
sb.append("\n");
HelperMethodsFormatter.formatHelperMethods(progress);
sb.append("\n");
formatControllerMethod(progress, controllerInjectionClass);
sb.append("}\n");
return sb.toString();
}
private static void formatControllerMethod(final GenerationProgress progress, final String controllerInjectionClass) {
final var sb = progress.stringBuilder();
sb.append(" /**\n");
sb.append(" * @return The controller\n");
sb.append(" */\n");
sb.append(" public ").append(controllerInjectionClass).append(" controller() {\n");
sb.append(" if (loaded) {\n");
sb.append(" return controller;\n");
sb.append(" } else {\n");
sb.append(" throw new IllegalStateException(\"Not loaded\");\n");
sb.append(" }\n");
sb.append(" }\n");
}
}

View File

@@ -0,0 +1,27 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.GenericTypes;
import java.util.List;
import java.util.Objects;
/**
* Implementation of {@link GenericTypes}
*
* @param name The name
* @param subTypes The subtypes
*/
public record GenericTypesImpl(String name, List<GenericTypes> subTypes) implements GenericTypes {
/**
* Instantiates a new generic types
*
* @param name The name
* @param subTypes The subtypes
* @throws NullPointerException if any parameter is null
*/
public GenericTypesImpl {
Objects.requireNonNull(name);
subTypes = List.copyOf(subTypes);
}
}

View File

@@ -1,21 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.ResourceBundleInjection;
import java.util.Objects;
/**
* Implementation of {@link ResourceBundleInjection}
*
* @param injectionType The injection type
* @param bundleName The bundle name
*/
public record ResourceBundleInjectionImpl(InjectionType injectionType,
String bundleName) implements ResourceBundleInjection {
public ResourceBundleInjectionImpl {
Objects.requireNonNull(injectionType);
Objects.requireNonNull(bundleName);
}
}

View File

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

View File

@@ -0,0 +1,32 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.SourceInfo;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Implementation of {@link SourceInfo}
*
* @param generatedClassName The generated class name
* @param controllerClassName The controller class name
* @param sourceFile The source file
* @param includedSources The included sources
* @param sourceToSourceInfo The mapping of source value to source info
* @param requiresResourceBundle True if the subtree requires a resource bundle
*/
public record SourceInfoImpl(String generatedClassName, String controllerClassName, Path sourceFile,
List<SourceInfo> includedSources,
Map<String, SourceInfo> sourceToSourceInfo,
boolean requiresResourceBundle) implements SourceInfo {
public SourceInfoImpl {
Objects.requireNonNull(generatedClassName);
Objects.requireNonNull(controllerClassName);
Objects.requireNonNull(sourceFile);
includedSources = List.copyOf(includedSources);
sourceToSourceInfo = Map.copyOf(sourceToSourceInfo);
}
}

View File

@@ -0,0 +1,193 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.impl.ControllerInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Utility class to provide the view's constructor and fields
*/
public final class ConstructorFormatter {
private ConstructorFormatter() {
}
public static void formatFieldsAndConstructor(final GenerationProgress progress) throws GenerationException {
final var className = progress.request().outputClassName();
final var simpleClassName = className.substring(className.lastIndexOf('.') + 1);
final var mainControllerClass = progress.request().controllerInfo().className();
final var isFactory = progress.request().parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY;
if (hasDuplicateControllerClass(progress) && !isFactory) {
throw new GenerationException("Some controllers in the view tree have the same class ; Factory field injection is required");
}
fillControllers(progress);
final var sb = progress.stringBuilder();
final var controllerMap = progress.controllerClassToVariable();
controllerMap.forEach((c, v) -> sb.append(" private final ").append(c).append(" ").append(v).append(isFactory ? "Factory" : "").append(";\n"));
final var controllerArg = getVariableName("controller", isFactory);
final var controllerArgClass = getType(mainControllerClass, isFactory);
final var resourceBundleInfo = getResourceBundleInfo(progress);
final var resourceBundleType = resourceBundleInfo.type();
final var resourceBundleArg = resourceBundleInfo.variableName();
if (isFactory) {
sb.append(" private final ").append(controllerArgClass).append(" ").append(controllerArg).append(";\n");
sb.append(" private ").append(mainControllerClass).append(" controller;\n");
} else {
sb.append(" private final ").append(mainControllerClass).append(" controller;\n");
}
if (resourceBundleType != null) {
sb.append(" private final ").append(resourceBundleType).append(" ").append(resourceBundleArg).append(";\n");
}
sb.append(" private boolean loaded;\n");
sb.append("\n");
sb.append(" /**\n");
sb.append(" * Instantiates a new ").append(simpleClassName).append("\n");
sb.append(" * @param ").append(controllerArg).append(" The controller ").append(isFactory ? "factory" : "instance").append("\n");
controllerMap.forEach((c, s) -> sb.append(" * @param ").append(getVariableName(s, isFactory))
.append(" The subcontroller ").append(isFactory ? "factory" : "instance").append(" for ").append(c).append("\n"));
if (resourceBundleType != null) {
sb.append(" * @param ").append(resourceBundleArg).append(" The resource bundle\n");
}
sb.append(" */\n");
final var arguments = "final " + controllerArgClass + " " + controllerArg +
((controllerMap.isEmpty()) ? "" : ", ") +
controllerMap.entrySet().stream().map(e -> "final " + getType(e.getKey(), isFactory) + " " + getVariableName(e.getValue(), isFactory))
.sorted().collect(Collectors.joining(", "))
+ (resourceBundleType == null ? "" : ", final " + resourceBundleType + " " + resourceBundleArg);
sb.append(" public ").append(simpleClassName).append("(").append(arguments).append(") {\n");
sb.append(" this.").append(controllerArg).append(" = java.util.Objects.requireNonNull(").append(controllerArg).append(");\n");
controllerMap.values().forEach(s -> sb.append(" this.").append(getVariableName(s, isFactory)).append(" = java.util.Objects.requireNonNull(").append(getVariableName(s, isFactory)).append(");\n"));
if (resourceBundleType != null) {
sb.append(" this.").append(resourceBundleArg).append(" = java.util.Objects.requireNonNull(").append(resourceBundleArg).append(");\n");
}
sb.append(" }\n");
}
private static ResourceBundleInfo getResourceBundleInfo(final GenerationProgress progress) throws GenerationException {
final var injectionType = progress.request().parameters().resourceInjectionType();
if (injectionType instanceof final ResourceBundleInjectionTypes types) {
return switch (types) {
case CONSTRUCTOR -> new ResourceBundleInfo("java.util.ResourceBundle", "resourceBundle");
case CONSTRUCTOR_FUNCTION ->
new ResourceBundleInfo("java.util.function.Function<String, String>", "resourceBundleFunction");
case CONSTRUCTOR_NAME -> new ResourceBundleInfo("String", "resourceBundleName");
case GETTER -> new ResourceBundleInfo(null, null);
case GET_BUNDLE -> new ResourceBundleInfo(null, null);
};
} else {
throw new GenerationException("Unknown resource injection type : " + injectionType);
}
}
private record ResourceBundleInfo(String type, String variableName) {
}
private static String getType(final String controllerClass, final boolean isFactory) {
if (isFactory) {
return "java.util.function.Function<java.util.Map<String, Object>, " + controllerClass + ">";
} else {
return controllerClass;
}
}
private static String getVariableName(final String variableName, final boolean isFactory) {
if (isFactory) {
return getVariableName(variableName, "Factory");
} else {
return variableName;
}
}
private static String getVariableName(final String variableName, final String suffix) {
return variableName + suffix;
}
private static boolean hasDuplicateControllerClass(final GenerationProgress progress) {
final var set = new HashSet<String>();
return hasDuplicateControllerClass(progress.request().sourceInfo(), set);
}
private static boolean hasDuplicateControllerClass(final SourceInfo info, final Set<String> controllers) {
final var controllerClass = info.controllerClassName();
if (controllers.contains(controllerClass)) {
return true;
}
return info.includedSources().stream().anyMatch(s -> hasDuplicateControllerClass(s, controllers));
}
private static void fillControllers(final GenerationProgress progress) {
progress.request().sourceInfo().includedSources().forEach(s -> fillControllers(progress, s));
}
private static void fillControllers(final GenerationProgress progress, final SourceInfo info) {
progress.controllerClassToVariable().put(info.controllerClassName(), progress.getNextVariableName(GenerationHelper.getVariablePrefix(info.controllerClassName())));
info.includedSources().forEach(s -> fillControllers(progress, s));
}
private static void fillControllers(final SourceInfo info, final Set<? super String> controllers) {
controllers.add(info.controllerClassName());
info.includedSources().forEach(s -> fillControllers(s, controllers));
}
static String formatSubViewConstructorCall(final GenerationProgress progress, final ParsedInclude include) throws GenerationException {
final var request = progress.request();
final var info = request.sourceInfo();
final var subInfo = info.sourceToSourceInfo().get(include.source());
if (subInfo == null) {
throw new GenerationException("Unknown include source : " + include.source());
} else {
final var isFactory = request.parameters().controllerInjectionType() == ControllerInjectionTypes.FACTORY;
final var subClassName = subInfo.controllerClassName();
final var subControllerVariable = getVariableName(progress.controllerClassToVariable().get(subClassName), isFactory);
final var subControllers = new HashSet<String>();
subInfo.includedSources().forEach(s -> fillControllers(s, subControllers));
final var arguments = subControllers.stream().sorted().map(c -> getVariableName(progress.controllerClassToVariable().get(c), isFactory)).collect(Collectors.joining(", "));
final var bundleVariable = subInfo.requiresResourceBundle() ? getBundleVariable(progress, include) : null;
final var argumentList = subControllerVariable + (arguments.isEmpty() ? "" : ", " + arguments) + (bundleVariable == null ? "" : ", " + bundleVariable);
final var subViewName = subInfo.generatedClassName();
final var variable = progress.getNextVariableName(GenerationHelper.getVariablePrefix(subViewName));
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, subViewName)).append(variable).append(" = new ").append(subViewName).append("(").append(argumentList).append(");\n");
return variable;
}
}
private static String getBundleVariable(final GenerationProgress progress, final ParsedInclude include) throws GenerationException {
final var info = getResourceBundleInfo(progress);
if (info.type() == null) {
return null;
} else if (include.resources() == null) {
return info.variableName();
} else {
final var sb = progress.stringBuilder();
if (progress.request().parameters().resourceInjectionType() instanceof final ResourceBundleInjectionTypes types) {
return switch (types) {
case GETTER, GET_BUNDLE -> null;
case CONSTRUCTOR_NAME -> {
final var bundleVariable = progress.getNextVariableName("resourceBundleName");
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "String")).append(bundleVariable).append(" = \"").append(include.resources()).append("\";\n");
yield bundleVariable;
}
case CONSTRUCTOR_FUNCTION -> {
final var bundleVariable = progress.getNextVariableName("resourceBundleFunction");
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.function.Function<String, String>")).append(bundleVariable).append(" = (java.util.function.Function<String, String>) s -> \"").append(include.resources()).append("\";\n");
yield bundleVariable;
}
case CONSTRUCTOR -> {
final var bundleVariable = progress.getNextVariableName("resourceBundle");
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.ResourceBundle")).append(bundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(include.resources()).append("\");\n");
yield bundleVariable;
}
};
} else {
throw new GenerationException("Unknown resource injection type : " + progress.request().parameters().resourceInjectionType());
}
}
}
}

View File

@@ -11,12 +11,11 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.getConstructorArgs;
/**
* Helper methods for {@link GeneratorImpl} to handle constructors
* Helper methods for {@link GeneratorImpl} to handle objects constructors
*/
final class ConstructorHelper {
private ConstructorHelper() {
}
@@ -58,7 +57,7 @@ final class ConstructorHelper {
static ConstructorArgs getMatchingConstructorArgs(final Constructor<?>[] constructors, final Set<String> allPropertyNames) {
ConstructorArgs matchingConstructorArgs = null;
for (final var constructor : constructors) {
final var constructorArgs = getConstructorArgs(constructor);
final var constructorArgs = ReflectionHelper.getConstructorArgs(constructor);
final var matchingArgsCount = getMatchingArgsCount(constructorArgs, allPropertyNames);
if (matchingConstructorArgs == null ? matchingArgsCount > 0 : matchingArgsCount > getMatchingArgsCount(matchingConstructorArgs, allPropertyNames)) {
matchingConstructorArgs = constructorArgs;

View File

@@ -7,15 +7,12 @@ import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getControllerInjection;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSetMethod;
/**
* Various methods to help {@link GeneratorImpl} for injecting controllers
*/
final class ControllerInjector {
private ControllerInjector() {
private ControllerInjector() {
}
/**
@@ -27,23 +24,22 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
static void injectControllerField(final GenerationProgress progress, final String id, final String variable) throws GenerationException {
final var controllerInjection = getControllerInjection(progress);
final var controllerInjectionType = controllerInjection.fieldInjectionType();
if (controllerInjectionType instanceof final ControllerFieldInjectionTypes types) {
final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes types) {
final var sb = progress.stringBuilder();
switch (types) {
case FACTORY ->
sb.append(" fieldMap.put(\"").append(id).append("\", ").append(variable).append(");\n");
case ASSIGN -> sb.append(" controller.").append(id).append(" = ").append(variable).append(";\n");
sb.append(" fieldMap.put(\"").append(id).append("\", ").append(variable).append(");\n");
case ASSIGN -> sb.append(" controller.").append(id).append(" = ").append(variable).append(";\n");
case SETTERS -> {
final var setMethod = getSetMethod(id);
sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
final var setMethod = GenerationHelper.getSetMethod(id);
sb.append(" controller.").append(setMethod).append("(").append(variable).append(");\n");
}
case REFLECTION ->
sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
sb.append(" injectField(\"").append(id).append("\", ").append(variable).append(");\n");
}
} else {
throw new GenerationException("Unknown controller injection type : " + controllerInjectionType);
throw new GenerationException("Unknown controller injection type : " + fieldInjectionType);
}
}
@@ -80,14 +76,14 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
private static void injectControllerMethod(final GenerationProgress progress, final String methodInjection) throws GenerationException {
final var injection = getControllerInjection(progress);
if (injection.fieldInjectionType() instanceof final ControllerFieldInjectionTypes fieldTypes) {
final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) {
switch (fieldTypes) {
case FACTORY -> progress.controllerFactoryPostAction().add(methodInjection);
case ASSIGN, SETTERS, REFLECTION -> progress.stringBuilder().append(methodInjection);
}
} else {
throw getUnknownInjectionException(injection.fieldInjectionType());
throw getUnknownInjectionException(fieldInjectionType);
}
}
@@ -101,24 +97,24 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
private static String getEventHandlerMethodInjection(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) throws GenerationException {
final var setMethod = getSetMethod(property.name());
final var injection = getControllerInjection(progress);
final var setMethod = GenerationHelper.getSetMethod(property.name());
final var methodInjectionType = progress.request().parameters().methodInjectionType();
final var controllerMethod = property.value().replace("#", "");
if (injection.methodInjectionType() instanceof final ControllerMethodsInjectionType methodTypes) {
if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) {
return switch (methodTypes) {
case REFERENCE -> {
final var hasArgument = progress.request().controllerInfo().handlerHasArgument(controllerMethod);
if (hasArgument) {
yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
yield " " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
} else {
yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
yield " " + parentVariable + "." + setMethod + "(e -> controller." + controllerMethod + "());\n";
}
}
case REFLECTION ->
" " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n";
" " + parentVariable + "." + setMethod + "(e -> callEventHandlerMethod(\"" + controllerMethod + "\", e));\n";
};
} else {
throw getUnknownInjectionException(injection.methodInjectionType());
throw getUnknownInjectionException(methodInjectionType);
}
}
@@ -133,18 +129,18 @@ final class ControllerInjector {
* @throws GenerationException if an error occurs
*/
private static String getCallbackMethodInjection(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String argumentClazz) throws GenerationException {
final var setMethod = getSetMethod(property.name());
final var injection = getControllerInjection(progress);
final var setMethod = GenerationHelper.getSetMethod(property.name());
final var methodInjectionType = progress.request().parameters().methodInjectionType();
final var controllerMethod = property.value().replace("#", "");
if (injection.methodInjectionType() instanceof final ControllerMethodsInjectionType methodTypes) {
if (methodInjectionType instanceof final ControllerMethodsInjectionType methodTypes) {
return switch (methodTypes) {
case REFERENCE ->
" " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
" " + parentVariable + "." + setMethod + "(controller::" + controllerMethod + ");\n";
case REFLECTION ->
" " + parentVariable + "." + setMethod + "(e -> callCallbackMethod(\"" + controllerMethod + "\", e, " + argumentClazz + "));\n";
" " + parentVariable + "." + setMethod + "(e -> callCallbackMethod(\"" + controllerMethod + "\", e, " + argumentClazz + "));\n";
};
} else {
throw getUnknownInjectionException(injection.methodInjectionType());
throw getUnknownInjectionException(methodInjectionType);
}
}

View File

@@ -5,7 +5,7 @@ import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.EXPRESSION_PREFIX;
/**
* Helper methods for {@link GeneratorImpl} to set fields
@@ -13,7 +13,6 @@ import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
final class FieldSetter {
private FieldSetter() {
}
/**
@@ -39,8 +38,8 @@ final class FieldSetter {
* @throws GenerationException if an error occurs
*/
static void setField(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String fieldType) throws GenerationException {
final var injection = getControllerInjection(progress);
if (injection.fieldInjectionType() instanceof final ControllerFieldInjectionTypes fieldTypes) {
final var fieldInjectionType = progress.request().parameters().fieldInjectionType();
if (fieldInjectionType instanceof final ControllerFieldInjectionTypes fieldTypes) {
switch (fieldTypes) {
case ASSIGN -> setAssign(progress, property, parentVariable);
case FACTORY -> setFactory(progress, property, parentVariable);
@@ -48,14 +47,14 @@ final class FieldSetter {
case REFLECTION -> setReflection(progress, property, parentVariable, fieldType);
}
} else {
throw new GenerationException("Unknown injection type : " + injection.fieldInjectionType());
throw new GenerationException("Unknown injection type : " + fieldInjectionType);
}
}
private static void setAssign(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) {
final var methodName = getSetMethod(property);
final var value = property.value().replace("$", "");
progress.stringBuilder().append(" ").append(parentVariable).append(".").append(methodName).append("(").append(value).append(");\n");
final var methodName = GenerationHelper.getSetMethod(property);
final var value = property.value().replace(EXPRESSION_PREFIX, "");
progress.stringBuilder().append(" ").append(parentVariable).append(".").append(methodName).append("(").append(value).append(");\n");
}
private static void setFactory(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) {
@@ -67,28 +66,27 @@ final class FieldSetter {
}
private static String getSetString(final ParsedProperty property, final String parentVariable) {
final var methodName = getSetMethod(property);
final var value = property.value().replace("$", "");
final var methodName = GenerationHelper.getSetMethod(property);
final var value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\.");
final var getterName = getGetMethod(split[1]);
return " " + parentVariable + "." + methodName + "(" + split[0] + "." + getterName + ");\n";
final var getterName = GenerationHelper.getGetMethod(split[1]);
return " " + parentVariable + "." + methodName + "(" + split[0] + "." + getterName + ");\n";
}
private static void setReflection(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String fieldType) {
final var methodName = getSetMethod(property);
final var value = property.value().replace("$", "");
final var methodName = GenerationHelper.getSetMethod(property);
final var value = property.value().replace(EXPRESSION_PREFIX, "");
final var split = value.split("\\.");
final var fieldName = split[1];
progress.stringBuilder().append("""
try {
final var field = controller.getClass().getDeclaredField("%s");
field.setAccessible(true);
final var value = (%s) field.get(controller);
%s.%s(value);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
""".formatted(fieldName, fieldType, parentVariable, methodName));
final var sb = progress.stringBuilder();
sb.append(" try {\n");
sb.append(" ").append(GenerationCompatibilityHelper.getStartVar(progress, "java.lang.reflect.Field", 0)).append("field = controller.getClass().getDeclaredField(\"").append(fieldName).append("\");\n");
sb.append(" field.setAccessible(true);\n");
sb.append(" final var value = (").append(fieldType).append(") field.get(controller);\n");
sb.append(" ").append(parentVariable).append(".").append(methodName).append("(value);\n");
sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(e);\n");
sb.append(" }\n");
}
}

View File

@@ -12,7 +12,8 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format fonts
@@ -23,6 +24,10 @@ final class FontFormatter {
}
private static String getStartFont(final GenerationProgress progress) {
return GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.text.Font");
}
static void formatFont(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
final var value = parseFontValue(parsedObject);
@@ -38,32 +43,31 @@ final class FontFormatter {
} else {
formatStyle(progress, fw, fp, size, name, variableName);
}
handleId(progress, parsedObject, variableName);
GenerationHelper.handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("Font cannot have children or properties : " + parsedObject);
}
}
private static void formatURL(final GenerationProgress progress, final URL url, final double size, final String variableName) {
final var urlVariableName = URLBuilder.formatURL(progress, url.toString());
progress.stringBuilder().append("""
Font %1$s;
try (final var in = %2$s.openStream()) {
%1$s = Font.loadFont(in, %3$s);
} catch (final IOException e) {
throw new RuntimeException(e);
}
""".formatted(variableName, urlVariableName, size));
final var urlVariableName = URLFormatter.formatURL(progress, url.toString());
final var sb = progress.stringBuilder();
sb.append(" final javafx.scene.text.Font ").append(variableName).append(";\n");
sb.append(" try (").append(GenerationCompatibilityHelper.getStartVar(progress, "java.io.InputStream", 0)).append(" in = ").append(urlVariableName).append(".openStream()) {\n");
sb.append(" ").append(variableName).append(" = javafx.scene.text.Font.loadFont(in, ").append(size).append(");\n");
sb.append(" } catch (final java.io.IOException e) {\n");
sb.append(" throw new RuntimeException(e);\n");
sb.append(" }\n");
}
private static void formatNoStyle(final GenerationProgress progress, final String name, final double size, final String variableName) {
progress.stringBuilder().append(START_VAR).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name).append("\", ").append(size).append(");\n");
progress.stringBuilder().append(getStartFont(progress)).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name).append("\", ").append(size).append(");\n");
}
private static void formatStyle(final GenerationProgress progress, final FontWeight fw, final FontPosture fp, final double size, final String name, final String variableName) {
final var finalFW = fw == null ? FontWeight.NORMAL : fw;
final var finalFP = fp == null ? FontPosture.REGULAR : fp;
progress.stringBuilder().append(START_VAR).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name)
progress.stringBuilder().append(getStartFont(progress)).append(variableName).append(" = new javafx.scene.text.Font(\"").append(name)
.append("\", javafx.scene.text.FontWeight.").append(finalFW.name()).append(", javafx.scene.text.FontPosture.")
.append(finalFP.name()).append(", ").append(size).append(");\n");
}

View File

@@ -0,0 +1,55 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
/**
* Various helper methods for {@link GeneratorImpl} to handle compatibility with older java versions
*/
final class GenerationCompatibilityHelper {
private GenerationCompatibilityHelper() {
}
static String getStartVar(final GenerationProgress progress, final ParsedObject parsedObject) throws GenerationException {
return getStartVar(progress, parsedObject.className() + ReflectionHelper.getGenericTypes(progress, parsedObject));
}
static String getStartVar(final GenerationProgress progress, final String className) {
return getStartVar(progress, className, 8);
}
static String getStartVar(final GenerationProgress progress, final String className, final int indent) {
if (progress.request().parameters().compatibility().useVar()) {
return " ".repeat(indent) + "final var ";
} else {
return " ".repeat(indent) + "final " + className + " ";
}
}
static String getToList(final GenerationProgress progress) {
return switch (progress.request().parameters().compatibility().listCollector()) {
case TO_LIST -> ".toList()";
case COLLECT_TO_UNMODIFIABLE_LIST -> ".collect(java.util.stream.Collectors.toUnmodifiableList())";
case COLLECT_TO_LIST -> ".collect(java.util.stream.Collectors.toList())";
};
}
static String getGetFirst(final GenerationProgress progress) {
if (progress.request().parameters().compatibility().useGetFirst()) {
return ".getFirst()";
} else {
return ".get(0)";
}
}
static String getListOf(final GenerationProgress progress) {
if (progress.request().parameters().compatibility().useCollectionsOf()) {
return "java.util.List.of(";
} else {
return "java.util.Arrays.asList(";
}
}
}

View File

@@ -1,6 +1,5 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
@@ -11,21 +10,54 @@ import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Map;
import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectControllerField;
/**
* Various helper methods for {@link GeneratorImpl}
*/
public final class GenerationHelper {
final class GenerationHelper {
private static final Logger logger = LogManager.getLogger(GenerationHelper.class);
static final String FX_ID = "fx:id";
static final String FX_VALUE = "fx:value";
static final String VALUE = "value";
static final String START_VAR = " final var ";
//Taken from FXMLLoader
static final String ESCAPE_PREFIX = "\\";
static final String RELATIVE_PATH_PREFIX = "@";
static final String RESOURCE_KEY_PREFIX = "%";
static final String EXPRESSION_PREFIX = "$";
static final String BINDING_EXPRESSION_PREFIX = "${";
static final String BI_DIRECTIONAL_BINDING_PREFIX = "#{";
private GenerationHelper() {
}
/**
* Handles the fx:id attribute of an object
*
* @param progress The generation progress
* @param parsedObject The parsed object
* @param variableName The variable name
* @throws GenerationException if an error occurs
*/
static void handleId(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var id = parsedObject.attributes().get(FX_ID);
if (id != null) {
final var idValue = id.value();
final String className;
if (progress.request().controllerInfo().fieldInfo(idValue) == null) {
className = parsedObject.className();
logger.debug("Not injecting {} because it is not found in controller", idValue);
} else {
if (ReflectionHelper.isGeneric(ReflectionHelper.getClass(parsedObject.className()))) {
className = parsedObject.className() + ReflectionHelper.getGenericTypes(progress, parsedObject);
} else {
className = parsedObject.className();
}
ControllerInjector.injectControllerField(progress, idValue, variableName);
}
progress.idToVariableInfo().put(idValue, new VariableInfo(idValue, parsedObject, variableName, className));
}
}
/**
@@ -34,27 +66,18 @@ public final class GenerationHelper {
* @param object The object
* @return The variable prefix
*/
public static String getVariablePrefix(final ParsedObject object) {
final var className = object.className();
return className.substring(className.lastIndexOf('.') + 1).toLowerCase();
static String getVariablePrefix(final ParsedObject object) {
return getVariablePrefix(object.className());
}
/**
* Gets the controller injection object from the generation request
* Returns the variable prefix for the given class name
*
* @param progress The generation progress
* @return The controller injection
* @throws GenerationException If the controller is not found
* @param className The class name
* @return The variable prefix
*/
public static ControllerInjection getControllerInjection(final GenerationProgress progress) throws GenerationException {
final var request = progress.request();
final var property = request.rootObject().attributes().get("fx:controller");
if (property == null) {
throw new GenerationException("Root object must have a controller property");
} else {
final var id = property.value();
return request.parameters().controllerInjections().get(id);
}
static String getVariablePrefix(final String className) {
return className.substring(className.lastIndexOf('.') + 1).toLowerCase();
}
/**
@@ -97,27 +120,6 @@ public final class GenerationHelper {
return "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
}
/**
* Handles the fx:id attribute of an object
*
* @param progress The generation progress
* @param parsedObject The parsed object
* @param variableName The variable name
* @throws GenerationException if an error occurs
*/
static void handleId(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var id = parsedObject.attributes().get(FX_ID);
if (id != null) {
progress.idToVariableName().put(id.value(), variableName);
progress.idToObject().put(id.value(), parsedObject);
if (progress.request().controllerInfo().fieldInfo(id.value()) == null) {
logger.debug("Not injecting {} because it is not found in controller", id.value());
} else {
injectControllerField(progress, id.value(), variableName);
}
}
}
/**
* Returns the sorted attributes of the given object
*

View File

@@ -2,28 +2,29 @@ package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.SequencedCollection;
import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Used by {@link GeneratorImpl} to track the generation progress
*
* @param request The generation request
* @param idToVariableName The id to variable name mapping
* @param idToObject The id to parsed object mapping
* @param idToVariableInfo The id to variable info
* @param variableNameCounters The variable name counters for variable name generation
* @param controllerClassToVariable The controller class to variable mapping
* @param controllerFactoryPostAction The controller factory post action for factory injection
* @param stringBuilder The string builder
*/
public record GenerationProgress(GenerationRequest request, Map<String, String> idToVariableName,
Map<String, ParsedObject> idToObject,
public record GenerationProgress(GenerationRequest request, Map<String, VariableInfo> idToVariableInfo,
Map<String, AtomicInteger> variableNameCounters,
SequencedMap<String, String> controllerClassToVariable,
SequencedCollection<String> controllerFactoryPostAction,
StringBuilder stringBuilder) {
@@ -31,18 +32,18 @@ public record GenerationProgress(GenerationRequest request, Map<String, String>
* Instantiates a new GenerationProgress
*
* @param request The generation request
* @param idToVariableName The id to variable name mapping
* @param idToObject The id to parsed object mapping
* @param idToVariableInfo The id to variable info mapping
* @param variableNameCounters The variable name counters
* @param controllerClassToVariable The controller class to variable mapping
* @param controllerFactoryPostAction The controller factory post action
* @param stringBuilder The string builder
* @throws NullPointerException if any parameter is null
*/
public GenerationProgress {
Objects.requireNonNull(request);
Objects.requireNonNull(idToVariableName);
Objects.requireNonNull(idToObject);
Objects.requireNonNull(idToVariableInfo);
Objects.requireNonNull(variableNameCounters);
Objects.requireNonNull(controllerClassToVariable);
Objects.requireNonNull(controllerFactoryPostAction);
Objects.requireNonNull(stringBuilder);
}
@@ -54,7 +55,7 @@ public record GenerationProgress(GenerationRequest request, Map<String, String>
* @throws NullPointerException if request is null
*/
public GenerationProgress(final GenerationRequest request) {
this(request, new HashMap<>(), new HashMap<>(), new HashMap<>(), new ArrayList<>(), new StringBuilder());
this(request, new HashMap<>(), new HashMap<>(), new LinkedHashMap<>(), new ArrayList<>(), new StringBuilder());
}
/**

View File

@@ -0,0 +1,99 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
/**
* Formats the helper methods for the generated code
*/
public final class HelperMethodsFormatter {
private HelperMethodsFormatter() {
}
/**
* Formats the helper methods for the given generation progress
*
* @param progress The generation progress
*/
public static void formatHelperMethods(final GenerationProgress progress) {
final var parameters = progress.request().parameters();
final var methodInjectionType = parameters.methodInjectionType();
final var sb = progress.stringBuilder();
if (methodInjectionType == ControllerMethodsInjectionType.REFLECTION) {
final var toList = GenerationCompatibilityHelper.getToList(progress);
final var getFirst = GenerationCompatibilityHelper.getGetFirst(progress);
final var startVariableMethodList = GenerationCompatibilityHelper.getStartVar(progress, "java.util.List<java.lang.reflect.Method>", 0);
sb.append(" private <T extends javafx.event.Event> void callEventHandlerMethod(final String methodName, final T event) {\n");
sb.append(" try {\n");
sb.append(" final java.lang.reflect.Method method;\n");
sb.append(" ").append(startVariableMethodList).append("methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())\n");
sb.append(" .filter(m -> m.getName().equals(methodName))").append(toList).append(";\n");
sb.append(" if (methods.size() > 1) {\n");
sb.append(" ").append(startVariableMethodList).append("eventMethods = methods.stream().filter(m ->\n");
sb.append(" m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0]))").append(toList).append(";\n");
sb.append(" if (eventMethods.size() == 1) {\n");
sb.append(" method = eventMethods").append(getFirst).append(";\n");
sb.append(" } else {\n");
sb.append(" ").append(startVariableMethodList).append("emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0)").append(toList).append(";\n");
sb.append(" if (emptyMethods.size() == 1) {\n");
sb.append(" method = emptyMethods").append(getFirst).append(";\n");
sb.append(" } else {\n");
sb.append(" throw new IllegalArgumentException(\"Multiple matching methods for \" + methodName);\n");
sb.append(" }\n");
sb.append(" }\n");
sb.append(" } else if (methods.size() == 1) {\n");
sb.append(" method = methods").append(getFirst).append(";\n");
sb.append(" } else {\n");
sb.append(" throw new IllegalArgumentException(\"No matching method for \" + methodName);\n");
sb.append(" }\n");
sb.append(" method.setAccessible(true);\n");
sb.append(" if (method.getParameterCount() == 0) {\n");
sb.append(" method.invoke(controller);\n");
sb.append(" } else {\n");
sb.append(" method.invoke(controller, event);\n");
sb.append(" }\n");
sb.append(" } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {\n");
sb.append(" throw new RuntimeException(\"Error using reflection on \" + methodName, ex);\n");
sb.append(" }\n");
sb.append(" }\n");
sb.append("\n");
sb.append(" private <T, U> U callCallbackMethod(final String methodName, final T value, final Class<T> clazz) {\n");
sb.append(" try {\n");
sb.append(" final java.lang.reflect.Method method;\n");
sb.append(" ").append(startVariableMethodList).append("methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())\n");
sb.append(" .filter(m -> m.getName().equals(methodName))").append(toList).append(";\n");
sb.append(" if (methods.size() > 1) {\n");
sb.append(" ").append(startVariableMethodList).append("eventMethods = methods.stream().filter(m ->\n");
sb.append(" m.getParameterCount() == 1 && clazz.isAssignableFrom(m.getParameterTypes()[0]))").append(toList).append(";\n");
sb.append(" if (eventMethods.size() == 1) {\n");
sb.append(" method = eventMethods").append(getFirst).append(";\n");
sb.append(" } else {\n");
sb.append(" throw new IllegalArgumentException(\"Multiple matching methods for \" + methodName);\n");
sb.append(" }\n");
sb.append(" } else if (methods.size() == 1) {\n");
sb.append(" method = methods").append(getFirst).append(";\n");
sb.append(" } else {\n");
sb.append(" throw new IllegalArgumentException(\"No matching method for \" + methodName);\n");
sb.append(" }\n");
sb.append(" method.setAccessible(true);\n");
sb.append(" return (U) method.invoke(controller, value);\n");
sb.append(" } catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {\n");
sb.append(" throw new RuntimeException(\"Error using reflection on \" + methodName, ex);\n");
sb.append(" }\n");
sb.append(" }\n");
}
if (parameters.fieldInjectionType() == ControllerFieldInjectionTypes.REFLECTION) {
sb.append(" private <T> void injectField(final String fieldName, final T object) {\n");
sb.append(" try {\n");
sb.append(" ").append(GenerationCompatibilityHelper.getStartVar(progress, "java.lang.reflect.Field", 0)).append("field = controller.getClass().getDeclaredField(fieldName);\n");
sb.append(" field.setAccessible(true);\n");
sb.append(" field.set(controller, object);\n");
sb.append(" } catch (final NoSuchFieldException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(\"Error using reflection on \" + fieldName, e);\n");
sb.append(" }\n");
sb.append(" }\n");
}
}
}

View File

@@ -1,106 +0,0 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getControllerInjection;
/**
* Provides the helper methods for the generated code
*/
public final class HelperMethodsProvider {
private HelperMethodsProvider() {
}
/**
* Gets helper methods string for the given generation progress
*
* @param progress The generation progress
* @return The helper methods
* @throws GenerationException if an error occurs
*/
public static String getHelperMethods(final GenerationProgress progress) throws GenerationException {
final var injection = getControllerInjection(progress);
final var methodInjectionType = injection.methodInjectionType();
final var sb = new StringBuilder();
if (methodInjectionType == ControllerMethodsInjectionType.REFLECTION) {
sb.append("""
private <T extends javafx.event.Event> void callEventHandlerMethod(final String methodName, final T event) {
try {
final java.lang.reflect.Method method;
final var methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
final var eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 1 && javafx.event.Event.class.isAssignableFrom(m.getParameterTypes()[0])).toList();
if (eventMethods.size() == 1) {
method = eventMethods.getFirst();
} else {
final var emptyMethods = methods.stream().filter(m -> m.getParameterCount() == 0).toList();
if (emptyMethods.size() == 1) {
method = emptyMethods.getFirst();
} else {
throw new IllegalArgumentException("Multiple matching methods for " + methodName);
}
}
} else if (methods.size() == 1) {
method = methods.getFirst();
} else {
throw new IllegalArgumentException("No matching method for " + methodName);
}
method.setAccessible(true);
if (method.getParameterCount() == 0) {
method.invoke(controller);
} else {
method.invoke(controller, event);
}
} catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
throw new RuntimeException("Error using reflection on " + methodName, ex);
}
}
private <T, U> U callCallbackMethod(final String methodName, final T value, final Class<T> clazz) {
try {
final java.lang.reflect.Method method;
final var methods = java.util.Arrays.stream(controller.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName)).toList();
if (methods.size() > 1) {
final var eventMethods = methods.stream().filter(m ->
m.getParameterCount() == 1 && clazz.isAssignableFrom(m.getParameterTypes()[0])).toList();
if (eventMethods.size() == 1) {
method = eventMethods.getFirst();
} else {
throw new IllegalArgumentException("Multiple matching methods for " + methodName);
}
} else if (methods.size() == 1) {
method = methods.getFirst();
} else {
throw new IllegalArgumentException("No matching method for " + methodName);
}
method.setAccessible(true);
return (U) method.invoke(controller, value);
} catch (final IllegalAccessException | java.lang.reflect.InvocationTargetException ex) {
throw new RuntimeException("Error using reflection on " + methodName, ex);
}
}
""");
}
if (injection.fieldInjectionType() == ControllerFieldInjectionTypes.REFLECTION) {
sb.append("""
private <T> void injectField(final String fieldName, final T object) {
try {
final var field = controller.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(controller, object);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException("Error using reflection on " + fieldName, e);
}
}
""");
}
return sb.toString();
}
}

View File

@@ -1,52 +0,0 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.URLBuilder.formatURL;
/**
* Helper methods for {@link GeneratorImpl} to format Images
*/
final class ImageBuilder {
private ImageBuilder() {
}
static void formatImage(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
final var sortedAttributes = getSortedAttributes(parsedObject);
String url = null;
var requestedWidth = 0.0;
var requestedHeight = 0.0;
var preserveRatio = false;
var smooth = false;
var backgroundLoading = false;
for (final var property : sortedAttributes) {
switch (property.name()) {
case FX_ID -> {
//Do nothing
}
case "url" -> url = formatURL(progress, property.value());
case "requestedWidth" -> requestedWidth = Double.parseDouble(property.value());
case "requestedHeight" -> requestedHeight = Double.parseDouble(property.value());
case "preserveRatio" -> preserveRatio = Boolean.parseBoolean(property.value());
case "smooth" -> smooth = Boolean.parseBoolean(property.value());
case "backgroundLoading" -> backgroundLoading = Boolean.parseBoolean(property.value());
default -> throw new GenerationException("Unknown image attribute : " + property.name());
}
}
final var urlString = progress.getNextVariableName("urlStr");
progress.stringBuilder().append(START_VAR).append(urlString).append(" = ").append(url).append(".toString();\n");
progress.stringBuilder().append(START_VAR).append(variableName).append(" = new javafx.scene.image.Image(").append(urlString)
.append(", ").append(requestedWidth).append(", ").append(requestedHeight).append(", ")
.append(preserveRatio).append(", ").append(smooth).append(", ").append(backgroundLoading).append(");\n");
handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("Image cannot have children or properties : " + parsedObject);
}
}
}

View File

@@ -0,0 +1,81 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format Images
*/
final class ImageFormatter {
private ImageFormatter() {
}
static void formatImage(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
doFormatImage(progress, parsedObject, variableName);
} else {
throw new GenerationException("Image cannot have children or properties : " + parsedObject);
}
}
private static void formatInputStream(final GenerationProgress progress, final String url, final double requestedWidth,
final double requestedHeight, final boolean preserveRatio, final boolean smooth, final String variableName) {
final var inputStream = progress.getNextVariableName("inputStream");
final var sb = progress.stringBuilder();
sb.append(" final javafx.scene.image.Image ").append(variableName).append(";\n");
sb.append(" try (").append(GenerationCompatibilityHelper.getStartVar(progress, "java.io.InputStream", 0)).append(inputStream).append(" = ").append(url).append(".openStream()) {\n");
sb.append(" ").append(variableName).append(" = new javafx.scene.image.Image(").append(inputStream);
sb.append(", ").append(requestedWidth).append(", ").append(requestedHeight).append(", ").append(preserveRatio).append(", ").append(smooth).append(");\n");
sb.append(" } catch (final java.io.IOException e) {\n");
sb.append(" throw new RuntimeException(e);\n");
sb.append(" }\n");
}
private static void doFormatImage(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var sortedAttributes = getSortedAttributes(parsedObject);
String url = null;
var requestedWidth = 0.0;
var requestedHeight = 0.0;
var preserveRatio = false;
var smooth = false;
var backgroundLoading = false;
for (final var property : sortedAttributes) {
switch (property.name()) {
case FX_ID -> {
//Do nothing
}
case "url" -> url = URLFormatter.formatURL(progress, property.value());
case "requestedWidth" -> requestedWidth = Double.parseDouble(property.value());
case "requestedHeight" -> requestedHeight = Double.parseDouble(property.value());
case "preserveRatio" -> preserveRatio = Boolean.parseBoolean(property.value());
case "smooth" -> smooth = Boolean.parseBoolean(property.value());
case "backgroundLoading" -> backgroundLoading = Boolean.parseBoolean(property.value());
default -> throw new GenerationException("Unknown image attribute : " + property.name());
}
}
if (progress.request().parameters().useImageInputStreamConstructor()) {
formatInputStream(progress, url, requestedWidth, requestedHeight, preserveRatio, smooth, variableName);
} else {
formatURL(progress, url, requestedWidth, requestedHeight, preserveRatio, smooth, backgroundLoading, variableName);
}
GenerationHelper.handleId(progress, parsedObject, variableName);
}
private static void formatURL(final GenerationProgress progress, final String url, final double requestedWidth,
final double requestedHeight, final boolean preserveRatio, final boolean smooth,
final boolean backgroundLoading, final String variableName) {
final var urlString = progress.getNextVariableName("urlStr");
final var sb = progress.stringBuilder();
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "String")).append(urlString).append(" = ").append(url).append(".toString();\n");
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.image.Image")).append(variableName).append(" = new javafx.scene.image.Image(").append(urlString)
.append(", ").append(requestedWidth).append(", ").append(requestedHeight).append(", ")
.append(preserveRatio).append(", ").append(smooth).append(", ").append(backgroundLoading).append(");\n");
}
}

View File

@@ -0,0 +1,70 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
/**
* Formats the load method for the generated code
*/
public final class LoadMethodFormatter {
private LoadMethodFormatter() {
}
/**
* Formats the load method
*
* @param progress The generation progress
* @throws GenerationException if an error occurs
*/
public static void formatLoadMethod(final GenerationProgress progress) throws GenerationException {
final var request = progress.request();
final var rootObject = request.rootObject();
final var parameters = progress.request().parameters();
final var controllerInjectionType = parameters.fieldInjectionType();
final var controllerClass = progress.request().controllerInfo().className();
final var sb = progress.stringBuilder();
sb.append(" /**\n");
sb.append(" * Loads the view. Can only be called once.\n");
sb.append(" *\n");
sb.append(" * @return The view parent\n");
sb.append(" */\n");
sb.append(" public <T> T load() {\n");
sb.append(" if (loaded) {\n");
sb.append(" throw new IllegalStateException(\"Already loaded\");\n");
sb.append(" }\n");
final var resourceBundleInjection = parameters.resourceInjectionType();
if (resourceBundleInjection == ResourceBundleInjectionTypes.CONSTRUCTOR_NAME) {
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"resourceBundleName\");\n");
} else if (resourceBundleInjection == ResourceBundleInjectionTypes.GET_BUNDLE && parameters.bundleMap().containsKey(controllerClass)) {
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.ResourceBundle")).append("resourceBundle = java.util.ResourceBundle.getBundle(\"").append(parameters.bundleMap().get(controllerClass)).append("\");\n");
}
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.Map<String, Object>")).append("fieldMap = new java.util.HashMap<String, Object>();\n");
}
final var variableName = progress.getNextVariableName(GenerationHelper.getVariablePrefix(rootObject));
ObjectFormatter.format(progress, rootObject, variableName);
if (controllerInjectionType == ControllerFieldInjectionTypes.FACTORY) {
sb.append(" controller = (").append(controllerClass).append(") controllerFactory.create(fieldMap);\n");
progress.controllerFactoryPostAction().forEach(sb::append);
}
if (parameters.methodInjectionType() == ControllerMethodsInjectionType.REFLECTION) {
sb.append(" try {\n");
sb.append(" ").append(GenerationCompatibilityHelper.getStartVar(progress, "java.lang.reflect.Method", 0)).append("initialize = controller.getClass().getDeclaredMethod(\"initialize\");\n");
sb.append(" initialize.setAccessible(true);\n");
sb.append(" initialize.invoke(controller);\n");
sb.append(" } catch (final java.lang.reflect.InvocationTargetException | IllegalAccessException e) {\n");
sb.append(" throw new RuntimeException(\"Error using reflection\", e);\n");
sb.append(" } catch (final NoSuchMethodException ignored) {\n");
sb.append(" }\n");
} else {
sb.append(" controller.initialize();\n");
}
sb.append(" loaded = true;\n");
sb.append(" return (T) ").append(variableName).append(";\n");
sb.append(" }\n");
}
}

View File

@@ -15,18 +15,7 @@ import java.util.SequencedCollection;
import java.util.Set;
import java.util.stream.Collectors;
import static com.github.gtache.fxml.compiler.impl.internal.ConstructorHelper.getListConstructorArgs;
import static com.github.gtache.fxml.compiler.impl.internal.ConstructorHelper.getMatchingConstructorArgs;
import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectControllerField;
import static com.github.gtache.fxml.compiler.impl.internal.FontFormatter.formatFont;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.ImageBuilder.formatImage;
import static com.github.gtache.fxml.compiler.impl.internal.PropertyFormatter.formatProperty;
import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.SceneBuilder.formatScene;
import static com.github.gtache.fxml.compiler.impl.internal.TriangleMeshBuilder.formatTriangleMesh;
import static com.github.gtache.fxml.compiler.impl.internal.URLBuilder.formatURL;
import static com.github.gtache.fxml.compiler.impl.internal.WebViewBuilder.formatWebView;
/**
* Helper methods for {@link GeneratorImpl} to format properties
@@ -34,7 +23,7 @@ import static com.github.gtache.fxml.compiler.impl.internal.WebViewBuilder.forma
public final class ObjectFormatter {
private static final Logger logger = LogManager.getLogger(ObjectFormatter.class);
private static final String NEW_ASSIGN = " = new ";
private static final Set<String> BUILDER_CLASSES = Set.of(
@@ -57,7 +46,6 @@ public final class ObjectFormatter {
);
private ObjectFormatter() {
}
/**
@@ -72,7 +60,7 @@ public final class ObjectFormatter {
switch (parsedObject) {
case final ParsedConstant constant -> formatConstant(progress, constant, variableName);
case final ParsedCopy copy -> formatCopy(progress, copy, variableName);
case final ParsedDefine define -> formatDefine(progress, define, variableName);
case final ParsedDefine define -> formatDefine(progress, define);
case final ParsedFactory factory -> formatFactory(progress, factory, variableName);
case final ParsedInclude include -> formatInclude(progress, include, variableName);
case final ParsedReference reference -> formatReference(progress, reference, variableName);
@@ -90,7 +78,7 @@ public final class ObjectFormatter {
* @param variableName The variable name
*/
private static void formatText(final GenerationProgress progress, final ParsedText text, final String variableName) {
progress.stringBuilder().append(START_VAR).append(variableName).append(" = \"").append(text.text()).append("\";\n");
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, "String")).append(variableName).append(" = \"").append(text.text()).append("\";\n");
}
/**
@@ -118,12 +106,13 @@ public final class ObjectFormatter {
private static void formatBuilderObject(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var className = parsedObject.className();
switch (className) {
case "javafx.scene.Scene" -> formatScene(progress, parsedObject, variableName);
case "javafx.scene.text.Font" -> formatFont(progress, parsedObject, variableName);
case "javafx.scene.image.Image" -> formatImage(progress, parsedObject, variableName);
case "java.net.URL" -> formatURL(progress, parsedObject, variableName);
case "javafx.scene.shape.TriangleMesh" -> formatTriangleMesh(progress, parsedObject, variableName);
case "javafx.scene.web.WebView" -> formatWebView(progress, parsedObject, variableName);
case "javafx.scene.Scene" -> SceneFormatter.formatScene(progress, parsedObject, variableName);
case "javafx.scene.text.Font" -> FontFormatter.formatFont(progress, parsedObject, variableName);
case "javafx.scene.image.Image" -> ImageFormatter.formatImage(progress, parsedObject, variableName);
case "java.net.URL" -> URLFormatter.formatURL(progress, parsedObject, variableName);
case "javafx.scene.shape.TriangleMesh" ->
TriangleMeshFormatter.formatTriangleMesh(progress, parsedObject, variableName);
case "javafx.scene.web.WebView" -> WebViewFormatter.formatWebView(progress, parsedObject, variableName);
default -> throw new IllegalArgumentException("Unknown builder class : " + className);
}
}
@@ -145,8 +134,8 @@ public final class ObjectFormatter {
}
final var value = getSimpleValue(progress, parsedObject);
final var valueStr = ValueFormatter.toString(value, ReflectionHelper.getClass(parsedObject.className()));
progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(valueStr).append(";\n");
handleId(progress, parsedObject, variableName);
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, parsedObject)).append(variableName).append(" = ").append(valueStr).append(";\n");
GenerationHelper.handleId(progress, parsedObject, variableName);
}
private static String getSimpleValue(final GenerationProgress progress, final ParsedObject parsedObject) throws GenerationException {
@@ -217,12 +206,12 @@ public final class ObjectFormatter {
}
}
if (!notDefinedChildren.isEmpty()) {
final var defaultProperty = getDefaultProperty(parsedObject.className());
final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
if (defaultProperty != null) {
allPropertyNames.add(defaultProperty);
}
}
final var constructorArgs = getMatchingConstructorArgs(constructors, allPropertyNames);
final var constructorArgs = ConstructorHelper.getMatchingConstructorArgs(constructors, allPropertyNames);
if (constructorArgs == null) {
formatNoConstructor(progress, parsedObject, variableName, allPropertyNames);
} else {
@@ -234,21 +223,21 @@ public final class ObjectFormatter {
final var clazz = ReflectionHelper.getClass(parsedObject.className());
if (allPropertyNames.size() == 1 && allPropertyNames.iterator().next().equals("fx:constant")) {
final var property = parsedObject.attributes().get("fx:constant");
progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(clazz.getCanonicalName()).append(".").append(property.value()).append(";\n");
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, parsedObject)).append(variableName).append(" = ").append(clazz.getCanonicalName()).append(".").append(property.value()).append(";\n");
} else {
throw new GenerationException("Cannot find constructor for " + clazz.getCanonicalName());
}
}
private static void formatConstructor(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName, final ConstructorArgs constructorArgs) throws GenerationException {
final var args = getListConstructorArgs(constructorArgs, parsedObject);
final var genericTypes = getGenericTypes(progress, parsedObject);
progress.stringBuilder().append(START_VAR).append(variableName).append(NEW_ASSIGN).append(parsedObject.className())
final var args = ConstructorHelper.getListConstructorArgs(constructorArgs, parsedObject);
final var genericTypes = ReflectionHelper.getGenericTypes(progress, parsedObject);
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, parsedObject)).append(variableName).append(NEW_ASSIGN).append(parsedObject.className())
.append(genericTypes).append("(").append(String.join(", ", args)).append(");\n");
final var sortedAttributes = getSortedAttributes(parsedObject);
for (final var value : sortedAttributes) {
if (!constructorArgs.namedArgs().containsKey(value.name())) {
formatProperty(progress, value, parsedObject, variableName);
PropertyFormatter.formatProperty(progress, value, parsedObject, variableName);
}
}
final var sortedProperties = parsedObject.properties().entrySet().stream().sorted(Comparator.comparing(e -> e.getKey().name())).toList();
@@ -261,7 +250,7 @@ public final class ObjectFormatter {
}
final var notDefinedChildren = parsedObject.children().stream().filter(c -> !(c instanceof ParsedDefine)).toList();
if (!notDefinedChildren.isEmpty()) {
final var defaultProperty = getDefaultProperty(parsedObject.className());
final var defaultProperty = ReflectionHelper.getDefaultProperty(parsedObject.className());
if (!constructorArgs.namedArgs().containsKey(defaultProperty)) {
final var property = new ParsedPropertyImpl(defaultProperty, null, null);
formatChild(progress, parsedObject, property, notDefinedChildren, variableName);
@@ -278,26 +267,8 @@ public final class ObjectFormatter {
*/
private static void formatInclude(final GenerationProgress progress, final ParsedInclude include, final String subNodeName) throws GenerationException {
final var subViewVariable = progress.getNextVariableName("view");
final var source = include.source();
final var resources = include.resources();
final var request = progress.request();
final var subControllerClass = request.parameters().sourceToControllerName().get(source);
final var subClassName = request.parameters().sourceToGeneratedClassName().get(source);
if (subClassName == null) {
throw new GenerationException("Unknown include source : " + source);
}
final var sb = progress.stringBuilder();
if (resources == null) {
sb.append(START_VAR).append(subViewVariable).append(NEW_ASSIGN).append(subClassName).append("(controllersMap, resourceBundlesMap);\n");
} else {
final var subResourceBundlesMapVariable = progress.getNextVariableName("map");
final var subBundleVariable = progress.getNextVariableName("bundle");
sb.append(START_VAR).append(subResourceBundlesMapVariable).append(" = new HashMap<>(resourceBundlesMap);\n");
sb.append(START_VAR).append(subBundleVariable).append(" = java.util.ResourceBundle.getBundle(\"").append(resources).append("\");\n");
sb.append(" ").append(subResourceBundlesMapVariable).append(".put(").append(subControllerClass).append(", ").append(subBundleVariable).append(");\n");
sb.append(START_VAR).append(subViewVariable).append(NEW_ASSIGN).append(subClassName).append("(controllersMap, ").append(subResourceBundlesMapVariable).append(");\n");
}
sb.append(" final javafx.scene.Parent ").append(subNodeName).append(" = ").append(subViewVariable).append(".load();\n");
final var viewVariable = ConstructorFormatter.formatSubViewConstructorCall(progress, include);
progress.stringBuilder().append(" final javafx.scene.Parent ").append(subNodeName).append(" = ").append(viewVariable).append(".load();\n");
injectSubController(progress, include, subViewVariable);
}
@@ -305,13 +276,13 @@ public final class ObjectFormatter {
final var id = include.controllerId();
if (id != null) {
final var subControllerVariable = progress.getNextVariableName("controller");
progress.stringBuilder().append(START_VAR).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
progress.idToVariableName().put(id, subControllerVariable);
progress.idToObject().put(id, include);
final var controllerClass = progress.request().sourceInfo().sourceToSourceInfo().get(include.source()).controllerClassName();
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, controllerClass)).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
progress.idToVariableInfo().put(id, new VariableInfo(id, include, subControllerVariable, controllerClass));
if (progress.request().controllerInfo().fieldInfo(id) == null) {
logger.debug("Not injecting {} because it is not found in controller", id);
} else {
injectControllerField(progress, id, subControllerVariable);
ControllerInjector.injectControllerField(progress, id, subControllerVariable);
}
}
}
@@ -319,13 +290,14 @@ public final class ObjectFormatter {
/**
* Formats a fx:define
*
* @param progress The generation progress
* @param define The parsed define
* @param variableName The variable name
* @param progress The generation progress
* @param define The parsed define
* @throws GenerationException if an error occurs
*/
private static void formatDefine(final GenerationProgress progress, final ParsedDefine define, final String variableName) throws GenerationException {
formatObject(progress, define.object(), variableName);
private static void formatDefine(final GenerationProgress progress, final ParsedObject define) throws GenerationException {
for (final var child : define.children()) {
format(progress, child, progress.getNextVariableName("definedObject"));
}
}
/**
@@ -337,11 +309,12 @@ public final class ObjectFormatter {
*/
private static void formatReference(final GenerationProgress progress, final ParsedReference reference, final String variableName) throws GenerationException {
final var id = reference.source();
final var variable = progress.idToVariableName().get(id);
if (variable == null) {
final var variableInfo = progress.idToVariableInfo().get(id);
if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id);
}
progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(variable).append(";\n");
final var referenceName = variableInfo.variableName();
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, variableInfo.className())).append(variableName).append(" = ").append(referenceName).append(";\n");
}
/**
@@ -354,12 +327,12 @@ public final class ObjectFormatter {
*/
private static void formatCopy(final GenerationProgress progress, final ParsedCopy copy, final String variableName) throws GenerationException {
final var id = copy.source();
final var variable = progress.idToVariableName().get(id);
final var object = progress.idToObject().get(id);
if (variable == null || object == null) {
final var variableInfo = progress.idToVariableInfo().get(id);
if (variableInfo == null) {
throw new GenerationException("Unknown id : " + id);
}
progress.stringBuilder().append(START_VAR).append(variableName).append(NEW_ASSIGN).append(object.className()).append("(").append(variable).append(");\n");
final var copyVariable = variableInfo.variableName();
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, variableInfo.className())).append(variableName).append(NEW_ASSIGN).append(variableInfo.className()).append("(").append(copyVariable).append(");\n");
}
/**
@@ -370,7 +343,7 @@ public final class ObjectFormatter {
* @param variableName The variable name
*/
private static void formatConstant(final GenerationProgress progress, final ParsedConstant constant, final String variableName) {
progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(constant.className()).append(".").append(constant.constant()).append(";\n");
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, constant.className())).append(variableName).append(" = ").append(constant.className()).append(".").append(constant.constant()).append(";\n");
}
/**
@@ -381,7 +354,7 @@ public final class ObjectFormatter {
* @param variableName The variable name
*/
private static void formatValue(final GenerationProgress progress, final ParsedValue value, final String variableName) {
progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(value.className()).append(".valueOf(\"").append(value.value()).append("\");\n");
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, value.className())).append(variableName).append(" = ").append(value.className()).append(".valueOf(\"").append(value.value()).append("\");\n");
}
/**
@@ -398,8 +371,14 @@ public final class ObjectFormatter {
variables.add(argumentVariable);
format(progress, argument, argumentVariable);
}
progress.stringBuilder().append(START_VAR).append(variableName).append(" = ").append(factory.className())
.append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
if (progress.request().parameters().compatibility().useVar()) {
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, factory.className())).append(variableName).append(" = ").append(factory.className())
.append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
} else {
final var returnType = ReflectionHelper.getReturnType(factory.className(), factory.factory());
progress.stringBuilder().append(GenerationCompatibilityHelper.getStartVar(progress, returnType)).append(variableName).append(" = ").append(factory.className())
.append(".").append(factory.factory()).append("(").append(String.join(", ", variables)).append(");\n");
}
}
/**
@@ -416,7 +395,7 @@ public final class ObjectFormatter {
final var propertyName = property.name();
final var variables = new ArrayList<String>();
for (final var object : objects) {
final var vn = progress.getNextVariableName(getVariablePrefix(object));
final var vn = progress.getNextVariableName(GenerationHelper.getVariablePrefix(object));
format(progress, object, vn);
if (!(object instanceof ParsedDefine)) {
variables.add(vn);
@@ -441,9 +420,9 @@ public final class ObjectFormatter {
*/
private static void formatMultipleChildren(final GenerationProgress progress, final Iterable<String> variables, final String propertyName, final ParsedObject parent,
final String parentVariable) throws GenerationException {
final var getMethod = getGetMethod(propertyName);
if (hasMethod(ReflectionHelper.getClass(parent.className()), getMethod)) {
progress.stringBuilder().append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(java.util.List.of(").append(String.join(", ", variables)).append("));\n");
final var getMethod = GenerationHelper.getGetMethod(propertyName);
if (ReflectionHelper.hasMethod(ReflectionHelper.getClass(parent.className()), getMethod)) {
progress.stringBuilder().append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(").append(GenerationCompatibilityHelper.getListOf(progress)).append(String.join(", ", variables)).append("));\n");
} else {
throw getCannotSetException(propertyName, parent.className());
}
@@ -479,15 +458,15 @@ public final class ObjectFormatter {
private static void formatSingleChildInstance(final GenerationProgress progress, final String variableName,
final ParsedProperty property, final ParsedObject parent,
final String parentVariable) throws GenerationException {
final var setMethod = getSetMethod(property);
final var getMethod = getGetMethod(property);
final var setMethod = GenerationHelper.getSetMethod(property);
final var getMethod = GenerationHelper.getGetMethod(property);
final var parentClass = ReflectionHelper.getClass(parent.className());
final var sb = progress.stringBuilder();
if (hasMethod(parentClass, setMethod)) {
sb.append(" ").append(parentVariable).append(".").append(setMethod).append("(").append(variableName).append(");\n");
} else if (hasMethod(parentClass, getMethod)) {
if (ReflectionHelper.hasMethod(parentClass, setMethod)) {
sb.append(" ").append(parentVariable).append(".").append(setMethod).append("(").append(variableName).append(");\n");
} else if (ReflectionHelper.hasMethod(parentClass, getMethod)) {
//Probably a list method that has only one element
sb.append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(java.util.List.of(").append(variableName).append("));\n");
sb.append(" ").append(parentVariable).append(".").append(getMethod).append("().addAll(").append(GenerationCompatibilityHelper.getListOf(progress)).append(variableName).append("));\n");
} else {
throw getCannotSetException(property.name(), parent.className());
}
@@ -503,9 +482,9 @@ public final class ObjectFormatter {
*/
private static void formatSingleChildStatic(final GenerationProgress progress, final String variableName,
final ParsedProperty property, final String parentVariable) throws GenerationException {
final var setMethod = getSetMethod(property);
if (hasStaticMethod(ReflectionHelper.getClass(property.sourceType()), setMethod)) {
progress.stringBuilder().append(" ").append(property.sourceType()).append(".").append(setMethod)
final var setMethod = GenerationHelper.getSetMethod(property);
if (ReflectionHelper.hasStaticMethod(ReflectionHelper.getClass(property.sourceType()), setMethod)) {
progress.stringBuilder().append(" ").append(property.sourceType()).append(".").append(setMethod)
.append("(").append(parentVariable).append(", ").append(variableName).append(");\n");
} else {
throw getCannotSetException(property.name(), property.sourceType());

View File

@@ -19,7 +19,7 @@ record Parameter(String name, Class<?> type, String defaultValue) {
* @param defaultValue The parameter default value
* @throws NullPointerException if any parameter is null
*/
public Parameter {
Parameter {
requireNonNull(name);
requireNonNull(type);
requireNonNull(defaultValue);

View File

@@ -10,18 +10,15 @@ import javafx.event.EventHandler;
import java.util.Objects;
import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectEventHandlerControllerMethod;
import static com.github.gtache.fxml.compiler.impl.internal.FieldSetter.setEventHandler;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.ValueFormatter.getArg;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.RESOURCE_KEY_PREFIX;
/**
* Helper methods for {@link GeneratorImpl} to format properties
*/
final class PropertyFormatter {
private PropertyFormatter() {
private PropertyFormatter() {
}
/**
@@ -36,7 +33,7 @@ final class PropertyFormatter {
static void formatProperty(final GenerationProgress progress, final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException {
final var propertyName = property.name();
if (propertyName.equals(FX_ID)) {
handleId(progress, parent, parentVariable);
GenerationHelper.handleId(progress, parent, parentVariable);
} else if (propertyName.equals("fx:controller")) {
checkDuplicateController(progress, parent);
} else if (Objects.equals(property.sourceType(), EventHandler.class.getName())) {
@@ -56,20 +53,20 @@ final class PropertyFormatter {
private static void handleEventHandler(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) throws GenerationException {
if (property.value().startsWith("#")) {
injectEventHandlerControllerMethod(progress, property, parentVariable);
ControllerInjector.injectEventHandlerControllerMethod(progress, property, parentVariable);
} else {
setEventHandler(progress, property, parentVariable);
FieldSetter.setEventHandler(progress, property, parentVariable);
}
}
private static void handleStaticProperty(final GenerationProgress progress, final ParsedProperty property, final String parentVariable, final String propertyName) throws GenerationException {
final var setMethod = getSetMethod(propertyName);
final var setMethod = GenerationHelper.getSetMethod(propertyName);
final var propertySourceTypeClass = ReflectionHelper.getClass(property.sourceType());
if (hasStaticMethod(propertySourceTypeClass, setMethod)) {
final var method = getStaticMethod(propertySourceTypeClass, setMethod);
if (ReflectionHelper.hasStaticMethod(propertySourceTypeClass, setMethod)) {
final var method = ReflectionHelper.getStaticMethod(propertySourceTypeClass, setMethod);
final var parameterType = method.getParameterTypes()[1];
final var arg = getArg(progress, property.value(), parameterType);
setLaterIfNeeded(progress, property, parameterType, " " + property.sourceType() + "." + setMethod + "(" + parentVariable + ", " + arg + ");\n");
final var arg = ValueFormatter.getArg(progress, property.value(), parameterType);
setLaterIfNeeded(progress, property, parameterType, " " + property.sourceType() + "." + setMethod + "(" + parentVariable + ", " + arg + ");\n");
} else {
throw new GenerationException("Cannot set " + propertyName + " on " + property.sourceType());
}
@@ -77,12 +74,12 @@ final class PropertyFormatter {
private static void handleProperty(final GenerationProgress progress, final ParsedProperty property, final ParsedObject parent, final String parentVariable) throws GenerationException {
final var propertyName = property.name();
final var setMethod = getSetMethod(propertyName);
final var getMethod = getGetMethod(propertyName);
final var setMethod = GenerationHelper.getSetMethod(propertyName);
final var getMethod = GenerationHelper.getGetMethod(propertyName);
final var parentClass = ReflectionHelper.getClass(parent.className());
if (hasMethod(parentClass, setMethod)) {
if (ReflectionHelper.hasMethod(parentClass, setMethod)) {
handleSetProperty(progress, property, parentClass, parentVariable);
} else if (hasMethod(parentClass, getMethod)) {
} else if (ReflectionHelper.hasMethod(parentClass, getMethod)) {
handleGetProperty(progress, property, parentClass, parentVariable);
} else {
throw new GenerationException("Cannot set " + propertyName + " on " + parent.className());
@@ -90,20 +87,20 @@ final class PropertyFormatter {
}
private static void handleSetProperty(final GenerationProgress progress, final ParsedProperty property, final Class<?> parentClass, final String parentVariable) throws GenerationException {
final var setMethod = getSetMethod(property.name());
final var method = getMethod(parentClass, setMethod);
final var setMethod = GenerationHelper.getSetMethod(property.name());
final var method = ReflectionHelper.getMethod(parentClass, setMethod);
final var parameterType = method.getParameterTypes()[0];
final var arg = getArg(progress, property.value(), parameterType);
setLaterIfNeeded(progress, property, parameterType, " " + parentVariable + "." + setMethod + "(" + arg + ");\n");
final var arg = ValueFormatter.getArg(progress, property.value(), parameterType);
setLaterIfNeeded(progress, property, parameterType, " " + parentVariable + "." + setMethod + "(" + arg + ");\n");
}
private static void handleGetProperty(final GenerationProgress progress, final ParsedProperty property, final Class<?> parentClass, final String parentVariable) throws GenerationException {
final var getMethod = getGetMethod(property.name());
final var method = getMethod(parentClass, getMethod);
final var getMethod = GenerationHelper.getGetMethod(property.name());
final var method = ReflectionHelper.getMethod(parentClass, getMethod);
final var returnType = method.getReturnType();
if (hasMethod(returnType, "addAll")) {
final var arg = getArg(progress, property.value(), String.class);
setLaterIfNeeded(progress, property, String.class, " " + parentVariable + "." + getMethod + "().addAll(java.util.List.of(" + arg + "));\n");
if (ReflectionHelper.hasMethod(returnType, "addAll")) {
final var arg = ValueFormatter.getArg(progress, property.value(), String.class);
setLaterIfNeeded(progress, property, String.class, " " + parentVariable + "." + getMethod + "().addAll(" + GenerationCompatibilityHelper.getListOf(progress) + arg + "));\n");
}
}
@@ -114,11 +111,11 @@ final class PropertyFormatter {
* @param property The property
* @param type The type
* @param arg The argument
* @throws GenerationException if an error occurs
*/
private static void setLaterIfNeeded(final GenerationProgress progress, final ParsedProperty property, final Class<?> type, final String arg) throws GenerationException {
if (type == String.class && property.value().startsWith("%") && progress.request().parameters().resourceBundleInjection().injectionType() == ResourceBundleInjectionTypes.GETTER
&& getControllerInjection(progress).fieldInjectionType() == ControllerFieldInjectionTypes.FACTORY) {
private static void setLaterIfNeeded(final GenerationProgress progress, final ParsedProperty property, final Class<?> type, final String arg) {
final var parameters = progress.request().parameters();
if (type == String.class && property.value().startsWith(RESOURCE_KEY_PREFIX) && parameters.resourceInjectionType() == ResourceBundleInjectionTypes.GETTER
&& parameters.fieldInjectionType() == ControllerFieldInjectionTypes.FACTORY) {
progress.controllerFactoryPostAction().add(arg);
} else {
progress.stringBuilder().append(arg);

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.GenericTypes;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import javafx.beans.DefaultProperty;
import javafx.beans.NamedArg;
@@ -8,11 +9,14 @@ import javafx.scene.Node;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -24,11 +28,23 @@ import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_
*/
final class ReflectionHelper {
private static final Logger logger = LogManager.getLogger(ReflectionHelper.class);
private static final Map<Class<?>, Boolean> HAS_VALUE_OF = new ConcurrentHashMap<>();
private static final Map<Class<?>, Boolean> IS_GENERIC = new ConcurrentHashMap<>();
private static final Map<String, String> DEFAULT_PROPERTY = new ConcurrentHashMap<>();
private static final Map<Class<?>, Map<String, Method>> METHODS = new ConcurrentHashMap<>();
private static final Map<Class<?>, Map<String, Method>> STATIC_METHODS = new ConcurrentHashMap<>();
private static final Map<String, Class<?>> classMap = new HashMap<>();
private static final Map<Class<?>, Boolean> hasValueOf = new HashMap<>();
private static final Map<Class<?>, Boolean> isGeneric = new HashMap<>();
private static final Map<String, String> defaultProperty = new HashMap<>();
private static final Map<Class<?>, Map<String, Method>> methods = new HashMap<>();
private static final Map<Class<?>, Map<String, Method>> staticMethods = new HashMap<>();
private static final Map<String, Class<?>> PRIMITIVE_TYPES = Map.of(
"boolean", boolean.class,
"byte", byte.class,
"char", char.class,
"short", short.class,
"int", int.class,
"long", long.class,
"float", float.class,
"double", double.class
);
private ReflectionHelper() {
}
@@ -41,7 +57,7 @@ final class ReflectionHelper {
* @return True if the class is generic
*/
static boolean isGeneric(final Class<?> clazz) {
return IS_GENERIC.computeIfAbsent(clazz, c -> c.getTypeParameters().length > 0);
return isGeneric.computeIfAbsent(clazz, c -> c.getTypeParameters().length > 0);
}
/**
@@ -53,7 +69,7 @@ final class ReflectionHelper {
* @return True if the class has a method with the given name
*/
static boolean hasMethod(final Class<?> clazz, final String methodName) {
final var methodMap = METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var methodMap = methods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var method = methodMap.computeIfAbsent(methodName, m -> computeMethod(clazz, m));
return method != null;
}
@@ -67,7 +83,7 @@ final class ReflectionHelper {
* @return The method
*/
static Method getMethod(final Class<?> clazz, final String methodName) {
final var methodMap = METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var methodMap = methods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
return methodMap.computeIfAbsent(methodName, m -> computeMethod(clazz, m));
}
@@ -110,7 +126,7 @@ final class ReflectionHelper {
* @return True if the class has a static method with the given name
*/
static boolean hasStaticMethod(final Class<?> clazz, final String methodName) {
final var methodMap = STATIC_METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var methodMap = staticMethods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var method = methodMap.computeIfAbsent(methodName, m -> computeStaticMethod(clazz, m));
return method != null;
}
@@ -124,7 +140,7 @@ final class ReflectionHelper {
* @return The method
*/
static Method getStaticMethod(final Class<?> clazz, final String methodName) {
final var methodMap = STATIC_METHODS.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
final var methodMap = staticMethods.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>());
return methodMap.computeIfAbsent(methodName, m -> computeStaticMethod(clazz, m));
}
@@ -161,7 +177,7 @@ final class ReflectionHelper {
* @return True if the class has a valueOf(String)
*/
static boolean hasValueOf(final Class<?> clazz) {
return HAS_VALUE_OF.computeIfAbsent(clazz, ReflectionHelper::computeHasValueOf);
return hasValueOf.computeIfAbsent(clazz, ReflectionHelper::computeHasValueOf);
}
/**
@@ -216,14 +232,14 @@ final class ReflectionHelper {
* @throws GenerationException If the class is not found or no default property is found
*/
static String getDefaultProperty(final String className) throws GenerationException {
if (DEFAULT_PROPERTY.containsKey(className)) {
return DEFAULT_PROPERTY.get(className);
if (defaultProperty.containsKey(className)) {
return defaultProperty.get(className);
} else {
final var defaultProperty = computeDefaultProperty(className);
if (defaultProperty != null) {
DEFAULT_PROPERTY.put(className, defaultProperty);
final var property = computeDefaultProperty(className);
if (property != null) {
defaultProperty.put(className, property);
}
return defaultProperty;
return property;
}
}
@@ -238,7 +254,7 @@ final class ReflectionHelper {
if (name.contains(".") || Character.isUpperCase(name.charAt(0))) {
return name;
} else {
return name.substring(0, 1).toUpperCase() + name.substring(1);
return MethodType.methodType(clazz).wrap().returnType().getName();
}
}
@@ -249,11 +265,25 @@ final class ReflectionHelper {
* @return The class
* @throws GenerationException If the class is not found
*/
public static Class<?> getClass(final String className) throws GenerationException {
try {
return Class.forName(className, false, Thread.currentThread().getContextClassLoader());
} catch (final ClassNotFoundException e) {
throw new GenerationException("Cannot find class " + className + " ; Is a dependency missing for the plugin?", e);
static Class<?> getClass(final String className) throws GenerationException {
if (classMap.containsKey(className)) {
return classMap.get(className);
} else {
final var clazz = computeClass(className);
classMap.put(className, clazz);
return clazz;
}
}
private static Class<?> computeClass(final String className) throws GenerationException {
if (PRIMITIVE_TYPES.containsKey(className)) {
return PRIMITIVE_TYPES.get(className);
} else {
try {
return Class.forName(className, false, Thread.currentThread().getContextClassLoader());
} catch (final ClassNotFoundException e) {
throw new GenerationException("Cannot find class " + className + " ; Is a dependency missing for the plugin?", e);
}
}
}
@@ -312,10 +342,55 @@ final class ReflectionHelper {
logger.warn("No field found for generic class {} (id={}) ; Using raw", clazz.getName(), id);
return "";
} else if (fieldInfo.isGeneric()) {
return "<" + String.join(", ", fieldInfo.genericTypes()) + ">";
return formatGenerics(fieldInfo.genericTypes());
}
}
}
return "";
}
private static String formatGenerics(final List<? extends GenericTypes> genericTypes) {
final var sb = new StringBuilder();
sb.append("<");
for (var i = 0; i < genericTypes.size(); i++) {
final var genericType = genericTypes.get(i);
sb.append(genericType.name());
formatGenerics(genericType.subTypes(), sb);
if (i < genericTypes.size() - 1) {
sb.append(", ");
}
}
sb.append(">");
return sb.toString();
}
private static void formatGenerics(final List<? extends GenericTypes> genericTypes, final StringBuilder sb) {
if (!genericTypes.isEmpty()) {
sb.append("<");
for (var i = 0; i < genericTypes.size(); i++) {
final var genericType = genericTypes.get(i);
sb.append(genericType.name());
formatGenerics(genericType.subTypes(), sb);
if (i < genericTypes.size() - 1) {
sb.append(", ");
}
}
sb.append(">");
}
}
/**
* Gets the return type of the given method for the given class
*
* @param className The class
* @param methodName The method
* @return The return type
* @throws GenerationException if an error occurs
*/
static String getReturnType(final String className, final String methodName) throws GenerationException {
final var clazz = getClass(className);
final var method = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals(methodName))
.findFirst().orElseThrow(() -> new GenerationException("Method " + methodName + " not found in class " + className));
return method.getReturnType().getName();
}
}

View File

@@ -8,23 +8,21 @@ import javafx.scene.paint.Color;
import java.util.ArrayList;
import java.util.Collection;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.ObjectFormatter.format;
import static com.github.gtache.fxml.compiler.impl.internal.URLBuilder.formatURL;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format Scenes
*/
final class SceneBuilder {
private SceneBuilder() {
final class SceneFormatter {
private SceneFormatter() {
}
static void formatScene(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
final var root = findRoot(parsedObject);
final var rootVariableName = progress.getNextVariableName("root");
format(progress, root, rootVariableName);
ObjectFormatter.format(progress, root, rootVariableName);
final var sortedAttributes = getSortedAttributes(parsedObject);
double width = -1;
double height = -1;
@@ -43,10 +41,10 @@ final class SceneBuilder {
}
}
final var sb = progress.stringBuilder();
sb.append(START_VAR).append(variableName).append(" = new javafx.scene.Scene(").append(rootVariableName).append(", ")
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.Scene")).append(variableName).append(" = new javafx.scene.Scene(").append(rootVariableName).append(", ")
.append(width).append(", ").append(height).append(", javafx.scene.paint.Color.valueOf(\"").append(paint).append("\"));\n");
addStylesheets(progress, variableName, stylesheets);
handleId(progress, parsedObject, variableName);
GenerationHelper.handleId(progress, parsedObject, variableName);
}
private static ParsedObject findRoot(final ParsedObject parsedObject) throws GenerationException {
@@ -64,14 +62,11 @@ final class SceneBuilder {
private static void addStylesheets(final GenerationProgress progress, final String variableName, final Collection<String> stylesheets) {
if (!stylesheets.isEmpty()) {
final var urlVariables = formatURL(progress, stylesheets);
final var urlVariables = URLFormatter.formatURL(progress, stylesheets);
final var tmpVariable = progress.getNextVariableName("stylesheets");
final var sb = progress.stringBuilder();
sb.append("""
final var %1$s = %2$s.getStylesheets();
%1$s.addAll(java.util.List.of(%3$s));
""".formatted(tmpVariable, variableName, String.join(", ", urlVariables)));
stylesheets.forEach(s -> sb.append(" ").append(variableName).append(".getStyleSheets().add(\"").append(s).append("\");\n"));
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "java.util.List<String>")).append(tmpVariable).append(" = ").append(variableName).append(".getStyleSheets();\n");
sb.append(" ").append(tmpVariable).append(".addAll(").append(GenerationCompatibilityHelper.getListOf(progress)).append(String.join(", ", urlVariables)).append("));\n");
}
}
}

View File

@@ -14,15 +14,15 @@ import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format TriangleMeshes
*/
final class TriangleMeshBuilder {
private TriangleMeshBuilder() {
final class TriangleMeshFormatter {
private TriangleMeshFormatter() {
}
static void formatTriangleMesh(final GenerationProgress progress, final ParsedObject parsedObject, final String variableName) throws GenerationException {
@@ -64,14 +64,14 @@ final class TriangleMeshBuilder {
}
}
final var sb = progress.stringBuilder();
sb.append(START_VAR).append(variableName).append(" = new javafx.scene.shape.TriangleMesh();\n");
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.shape.TriangleMesh")).append(variableName).append(" = new javafx.scene.shape.TriangleMesh();\n");
setPoints(progress, variableName, points);
setTexCoords(progress, variableName, texCoords);
setNormals(progress, variableName, normals);
setFaces(progress, variableName, faces);
setFaceSmoothingGroups(progress, variableName, faceSmoothingGroups);
setVertexFormat(progress, variableName, vertexFormat);
handleId(progress, parsedObject, variableName);
GenerationHelper.handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("Image cannot have children or properties : " + parsedObject);
}
@@ -89,37 +89,37 @@ final class TriangleMeshBuilder {
private static void setPoints(final GenerationProgress progress, final String variableName, final Collection<Float> points) {
if (!points.isEmpty()) {
progress.stringBuilder().append(" ").append(variableName).append(".getPoints().setAll(new float[]{").append(formatList(points)).append("});\n");
progress.stringBuilder().append(" ").append(variableName).append(".getPoints().setAll(new float[]{").append(formatList(points)).append("});\n");
}
}
private static void setTexCoords(final GenerationProgress progress, final String variableName, final Collection<Float> texCoords) {
if (!texCoords.isEmpty()) {
progress.stringBuilder().append(" ").append(variableName).append(".getTexCoords().setAll(new float[]{").append(formatList(texCoords)).append("});\n");
progress.stringBuilder().append(" ").append(variableName).append(".getTexCoords().setAll(new float[]{").append(formatList(texCoords)).append("});\n");
}
}
private static void setNormals(final GenerationProgress progress, final String variableName, final Collection<Float> normals) {
if (!normals.isEmpty()) {
progress.stringBuilder().append(" ").append(variableName).append(".getNormals().setAll(new float[]{").append(formatList(normals)).append("});\n");
progress.stringBuilder().append(" ").append(variableName).append(".getNormals().setAll(new float[]{").append(formatList(normals)).append("});\n");
}
}
private static void setFaces(final GenerationProgress progress, final String variableName, final Collection<Integer> faces) {
if (!faces.isEmpty()) {
progress.stringBuilder().append(" ").append(variableName).append(".getFaces().setAll(new int[]{").append(formatList(faces)).append("});\n");
progress.stringBuilder().append(" ").append(variableName).append(".getFaces().setAll(new int[]{").append(formatList(faces)).append("});\n");
}
}
private static void setFaceSmoothingGroups(final GenerationProgress progress, final String variableName, final Collection<Integer> faceSmoothingGroups) {
if (!faceSmoothingGroups.isEmpty()) {
progress.stringBuilder().append(" ").append(variableName).append(".getFaceSmoothingGroups().setAll(new int[]{").append(formatList(faceSmoothingGroups)).append("});\n");
progress.stringBuilder().append(" ").append(variableName).append(".getFaceSmoothingGroups().setAll(new int[]{").append(formatList(faceSmoothingGroups)).append("});\n");
}
}
private static void setVertexFormat(final GenerationProgress progress, final String variableName, final VertexFormat vertexFormat) {
if (vertexFormat != null) {
progress.stringBuilder().append(" ").append(variableName).append(".setVertexFormat(javafx.scene.shape.VertexFormat.").append(vertexFormat).append(");\n");
progress.stringBuilder().append(" ").append(variableName).append(".setVertexFormat(javafx.scene.shape.VertexFormat.").append(vertexFormat).append(");\n");
}
}

View File

@@ -9,13 +9,13 @@ import java.util.List;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
/**
* Helper methods for {@link GeneratorImpl} to format URLs
*/
final class URLBuilder {
private URLBuilder() {
final class URLFormatter {
private URLFormatter() {
}
static List<String> formatURL(final GenerationProgress progress, final Collection<String> stylesheets) {
@@ -25,17 +25,15 @@ final class URLBuilder {
static String formatURL(final GenerationProgress progress, final String url) {
final var variableName = progress.getNextVariableName("url");
final var sb = progress.stringBuilder();
if (url.startsWith("@")) {
sb.append(START_VAR).append(variableName).append(" = getClass().getResource(\"").append(url.substring(1)).append("\");\n");
if (url.startsWith(RELATIVE_PATH_PREFIX)) {
sb.append(getStartURL(progress)).append(variableName).append(" = getClass().getResource(\"").append(url.substring(1)).append("\");\n");
} else {
sb.append("""
final java.net.URL %1$s;
try {
%1$s = new java.net.URI("%2$s").toURL();
} catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {
throw new RuntimeException("Couldn't parse url : %2$s", e);
}
""".formatted(variableName, url));
sb.append(" final java.net.URL ").append(variableName).append(";\n");
sb.append(" try {\n");
sb.append(" ").append(variableName).append(" = new java.net.URI(\"").append(url).append("\").toURL();\n");
sb.append(" } catch (final java.net.MalformedURLException | java.net.URISyntaxException e) {\n");
sb.append(" throw new RuntimeException(\"Couldn't parse url : ").append(url).append("\", e);\n");
sb.append(" }\n");
}
return variableName;
}
@@ -53,10 +51,14 @@ final class URLBuilder {
default -> throw new GenerationException("Unknown URL attribute : " + property.name());
}
}
progress.stringBuilder().append(START_VAR).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n");
progress.stringBuilder().append(getStartURL(progress)).append(variableName).append(" = getClass().getResource(\"").append(value).append("\");\n");
handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("URL cannot have children or properties : " + parsedObject);
}
}
private static String getStartURL(final GenerationProgress progress) {
return GenerationCompatibilityHelper.getStartVar(progress, "java.net.URL");
}
}

View File

@@ -6,8 +6,7 @@ import com.github.gtache.fxml.compiler.impl.ResourceBundleInjectionTypes;
import java.util.regex.Pattern;
import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.getWrapperClass;
import static com.github.gtache.fxml.compiler.impl.internal.ReflectionHelper.hasValueOf;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
/**
* Helper methods for {@link GeneratorImpl} to format values
@@ -31,19 +30,19 @@ final class ValueFormatter {
* @throws GenerationException if an error occurs
*/
static String getArg(final GenerationProgress progress, final String value, final Class<?> parameterType) throws GenerationException {
if (parameterType == String.class && value.startsWith("%")) {
if (parameterType == String.class && value.startsWith(RESOURCE_KEY_PREFIX)) {
return getBundleValue(progress, value.substring(1));
} else if (value.startsWith("@")) {
} else if (value.startsWith(RELATIVE_PATH_PREFIX)) {
final var subpath = value.substring(1);
return getResourceValue(subpath);
} else if (value.startsWith("${")) {
} else if (value.startsWith(BINDING_EXPRESSION_PREFIX)) {
throw new UnsupportedOperationException("Not implemented yet");
} else if (value.startsWith("$")) {
final var variable = progress.idToVariableName().get(value.substring(1));
} else if (value.startsWith(EXPRESSION_PREFIX)) {
final var variable = progress.idToVariableInfo().get(value.substring(1));
if (variable == null) {
throw new GenerationException("Unknown variable : " + value.substring(1));
}
return variable;
return variable.variableName();
} else {
return toString(value, parameterType);
}
@@ -62,11 +61,12 @@ final class ValueFormatter {
* @throws GenerationException if an error occurs
*/
private static String getBundleValue(final GenerationProgress progress, final String value) throws GenerationException {
final var resourceBundleInjectionType = progress.request().parameters().resourceBundleInjection().injectionType();
final var resourceBundleInjectionType = progress.request().parameters().resourceInjectionType();
if (resourceBundleInjectionType instanceof final ResourceBundleInjectionTypes types) {
return switch (types) {
case CONSTRUCTOR, GET_BUNDLE -> "bundle.getString(\"" + value + "\")";
case CONSTRUCTOR, GET_BUNDLE, CONSTRUCTOR_NAME -> "resourceBundle.getString(\"" + value + "\")";
case GETTER -> "controller.resources().getString(\"" + value + "\")";
case CONSTRUCTOR_FUNCTION -> "resourceBundleFunction.apply(\"" + value + "\")";
};
} else {
throw new GenerationException("Unknown resource bundle injection type : " + resourceBundleInjectionType);
@@ -90,28 +90,40 @@ final class ValueFormatter {
return value;
} else if (clazz == byte.class || clazz == Byte.class || clazz == short.class || clazz == Short.class ||
clazz == int.class || clazz == Integer.class || clazz == long.class || clazz == Long.class) {
if (INT_PATTERN.matcher(value).matches()) {
return value;
} else {
return getValueOf(getWrapperClass(clazz), value);
}
return intToString(value, clazz);
} else if (clazz == float.class || clazz == Float.class || clazz == double.class || clazz == Double.class) {
if (DECIMAL_PATTERN.matcher(value).matches()) {
return value;
} else {
return getValueOf(getWrapperClass(clazz), value);
}
} else if (hasValueOf(clazz)) {
if (clazz.isEnum()) {
return clazz.getCanonicalName() + "." + value;
} else {
return getValueOf(clazz.getCanonicalName(), value);
}
return decimalToString(value, clazz);
} else if (ReflectionHelper.hasValueOf(clazz)) {
return valueOfToString(value, clazz);
} else {
return value;
}
}
private static String intToString(final String value, final Class<?> clazz) {
if (INT_PATTERN.matcher(value).matches()) {
return value;
} else {
return getValueOf(ReflectionHelper.getWrapperClass(clazz), value);
}
}
private static String decimalToString(final String value, final Class<?> clazz) {
if (DECIMAL_PATTERN.matcher(value).matches()) {
return value;
} else {
return getValueOf(ReflectionHelper.getWrapperClass(clazz), value);
}
}
private static String valueOfToString(final String value, final Class<?> clazz) {
if (clazz.isEnum()) {
return clazz.getCanonicalName() + "." + value;
} else {
return getValueOf(clazz.getCanonicalName(), value);
}
}
private static String getValueOf(final String clazz, final String value) {
return clazz + ".valueOf(\"" + value + "\")";
}

View File

@@ -0,0 +1,23 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import java.util.Objects;
/**
* Info about a variable
*
* @param id The fx:id of the variable
* @param parsedObject The parsed object of the variable
* @param variableName The variable name
* @param className The class name of the variable
*/
record VariableInfo(String id, ParsedObject parsedObject, String variableName, String className) {
VariableInfo {
Objects.requireNonNull(id);
Objects.requireNonNull(parsedObject);
Objects.requireNonNull(variableName);
Objects.requireNonNull(className);
}
}

View File

@@ -5,20 +5,15 @@ import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectCallbackControllerMethod;
import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.injectEventHandlerControllerMethod;
import static com.github.gtache.fxml.compiler.impl.internal.FieldSetter.setEventHandler;
import static com.github.gtache.fxml.compiler.impl.internal.FieldSetter.setField;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.*;
import static com.github.gtache.fxml.compiler.impl.internal.PropertyFormatter.formatProperty;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_ID;
import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.getSortedAttributes;
/**
* Helper methods for {@link GeneratorImpl} to format WebViews
*/
final class WebViewBuilder {
private WebViewBuilder() {
final class WebViewFormatter {
private WebViewFormatter() {
}
/**
@@ -33,13 +28,13 @@ final class WebViewBuilder {
if (parsedObject.children().isEmpty() && parsedObject.properties().isEmpty()) {
final var sortedAttributes = getSortedAttributes(parsedObject);
final var sb = progress.stringBuilder();
sb.append(START_VAR).append(variableName).append(" = new javafx.scene.web.WebView();\n");
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.web.WebView")).append(variableName).append(" = new javafx.scene.web.WebView();\n");
final var engineVariable = progress.getNextVariableName("engine");
sb.append(START_VAR).append(engineVariable).append(" = ").append(variableName).append(".getEngine();\n");
sb.append(GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.web.WebEngine")).append(engineVariable).append(" = ").append(variableName).append(".getEngine();\n");
for (final var value : sortedAttributes) {
formatAttribute(progress, value, parsedObject, variableName, engineVariable);
}
handleId(progress, parsedObject, variableName);
GenerationHelper.handleId(progress, parsedObject, variableName);
} else {
throw new GenerationException("WebView cannot have children or properties : " + parsedObject);
}
@@ -57,13 +52,13 @@ final class WebViewBuilder {
injectEventHandler(progress, value, engineVariable);
case "promptHandler" -> injectPromptHandler(progress, value, engineVariable);
case "location" -> injectLocation(progress, value, engineVariable);
default -> formatProperty(progress, value, parsedObject, variableName);
default -> PropertyFormatter.formatProperty(progress, value, parsedObject, variableName);
}
}
private static void injectConfirmHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
injectCallbackControllerMethod(progress, value, engineVariable, "String.class");
ControllerInjector.injectCallbackControllerMethod(progress, value, engineVariable, "String.class");
} else {
setCallback(progress, value, engineVariable);
}
@@ -71,7 +66,7 @@ final class WebViewBuilder {
private static void injectCreatePopupHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PopupFeatures.class");
ControllerInjector.injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PopupFeatures.class");
} else {
setCallback(progress, value, engineVariable);
}
@@ -79,22 +74,22 @@ final class WebViewBuilder {
private static void injectEventHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
injectEventHandlerControllerMethod(progress, value, engineVariable);
ControllerInjector.injectEventHandlerControllerMethod(progress, value, engineVariable);
} else {
setEventHandler(progress, value, engineVariable);
FieldSetter.setEventHandler(progress, value, engineVariable);
}
}
private static void injectPromptHandler(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) throws GenerationException {
if (value.value().startsWith("#")) {
injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PromptData.class");
ControllerInjector.injectCallbackControllerMethod(progress, value, engineVariable, "javafx.scene.web.PromptData.class");
} else {
setCallback(progress, value, engineVariable);
}
}
private static void injectLocation(final GenerationProgress progress, final ParsedProperty value, final String engineVariable) {
progress.stringBuilder().append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n");
progress.stringBuilder().append(" ").append(engineVariable).append(".load(\"").append(value.value()).append("\");\n");
}
@@ -106,6 +101,6 @@ final class WebViewBuilder {
* @param parentVariable The parent variable
*/
private static void setCallback(final GenerationProgress progress, final ParsedProperty property, final String parentVariable) throws GenerationException {
setField(progress, property, parentVariable, "javafx.util.Callback");
FieldSetter.setField(progress, property, parentVariable, "javafx.util.Callback");
}
}

View File

@@ -3,7 +3,7 @@ package com.github.gtache.fxml.compiler.parsing.impl;
import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import java.util.Objects;
import java.util.List;
/**
* Implementation of {@link ParsedObject}
@@ -12,9 +12,9 @@ import java.util.Objects;
* @param attributes The object properties
* @param properties The object children (complex properties)
*/
public record ParsedDefineImpl(ParsedObject object) implements ParsedDefine {
public record ParsedDefineImpl(List<ParsedObject> children) implements ParsedDefine {
public ParsedDefineImpl {
Objects.requireNonNull(object);
children = List.copyOf(children);
}
}

View File

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

View File

@@ -0,0 +1,28 @@
package com.github.gtache.fxml.compiler.compatibility.impl;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class TestGenerationCompatibilityImpl {
private final int javaVersion;
private final GenerationCompatibility compatibility;
TestGenerationCompatibilityImpl() {
this.javaVersion = 8;
this.compatibility = new GenerationCompatibilityImpl(javaVersion);
}
@Test
void testGetters() {
assertEquals(javaVersion, compatibility.javaVersion());
}
@Test
void testIllegal() {
assertThrows(IllegalArgumentException.class, () -> new GenerationCompatibilityImpl(7));
}
}

View File

@@ -14,6 +14,7 @@ class TestClassesFinder {
final var expected = Set.of(
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedConstantImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedCopyImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedDefineImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedFactoryImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedIncludeImpl",
"com.github.gtache.fxml.compiler.parsing.impl.TestParsedObjectImpl",

View File

@@ -1,6 +1,7 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.GenericTypes;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
@@ -8,16 +9,17 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
class TestControllerFieldInfoImpl {
private final String name;
private final List<String> genericTypes;
private final List<GenericTypes> genericTypes;
private final ControllerFieldInfo info;
TestControllerFieldInfoImpl() {
this.name = "name";
this.genericTypes = new ArrayList<>(List.of("A", "B", "C"));
this.genericTypes = new ArrayList<>(List.of(mock(GenericTypes.class)));
this.info = new ControllerFieldInfoImpl(name, genericTypes);
}

View File

@@ -16,29 +16,39 @@ import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class TestControllerInfoImpl {
private final String className;
private final Map<String, Boolean> handlerHasArgument;
private final ControllerFieldInfo fieldInfo;
private final Map<String, ControllerFieldInfo> fieldInfoMap;
private final boolean hasInitialize;
private final ControllerInfo info;
TestControllerInfoImpl(@Mock final ControllerFieldInfo fieldInfo) {
this.className = "controllerName";
this.handlerHasArgument = new HashMap<>(Map.of("one", true, "two", false));
this.fieldInfo = Objects.requireNonNull(fieldInfo);
this.fieldInfoMap = new HashMap<>(Map.of("one", fieldInfo));
this.info = new ControllerInfoImpl(handlerHasArgument, fieldInfoMap);
this.hasInitialize = true;
this.info = new ControllerInfoImpl(className, handlerHasArgument, fieldInfoMap, hasInitialize);
}
@Test
void testGetters() {
assertEquals(className, info.className());
assertEquals(handlerHasArgument, info.handlerHasArgument());
assertEquals(fieldInfoMap, info.fieldInfo());
assertEquals(hasInitialize, info.hasInitialize());
}
@Test
void testHandlerHasArgument() {
assertEquals(handlerHasArgument, info.handlerHasArgument());
assertTrue(info.handlerHasArgument("one"));
assertFalse(info.handlerHasArgument("two"));
assertTrue(info.handlerHasArgument("three"));
}
@Test
void testPropertyGenericTypes() {
assertEquals(fieldInfoMap, info.fieldInfo());
void testFieldInfo() {
assertEquals(fieldInfo, info.fieldInfo("one"));
}
@@ -65,7 +75,8 @@ class TestControllerInfoImpl {
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(null, fieldInfoMap));
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(handlerHasArgument, null));
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(null, handlerHasArgument, fieldInfoMap, hasInitialize));
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(className, null, fieldInfoMap, hasInitialize));
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(className, handlerHasArgument, null, hasInitialize));
}
}

View File

@@ -1,43 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.InjectionType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestControllerInjectionImpl {
private final InjectionType fieldInjectionType;
private final InjectionType methodInjectionType;
private final String injectionClass;
private final ControllerInjection controllerInjection;
TestControllerInjectionImpl(@Mock final InjectionType fieldInjectionType, @Mock final InjectionType methodInjectionType) {
this.fieldInjectionType = Objects.requireNonNull(fieldInjectionType);
this.methodInjectionType = Objects.requireNonNull(methodInjectionType);
this.injectionClass = "class";
this.controllerInjection = new ControllerInjectionImpl(fieldInjectionType, methodInjectionType, injectionClass);
}
@Test
void testGetters() {
assertEquals(fieldInjectionType, controllerInjection.fieldInjectionType());
assertEquals(methodInjectionType, controllerInjection.methodInjectionType());
assertEquals(injectionClass, controllerInjection.injectionClass());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ControllerInjectionImpl(null, methodInjectionType, injectionClass));
assertThrows(NullPointerException.class, () -> new ControllerInjectionImpl(fieldInjectionType, null, injectionClass));
assertThrows(NullPointerException.class, () -> new ControllerInjectionImpl(fieldInjectionType, methodInjectionType, null));
}
}

View File

@@ -1,49 +1,62 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInjection;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.ResourceBundleInjection;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Map;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestGenerationParametersImpl {
private final Map<String, ControllerInjection> controllerInjections;
private final Map<String, String> sourceToGeneratedClassName;
private final Map<String, String> sourceToControllerName;
private final ResourceBundleInjection resourceBundleInjection;
private final GenerationCompatibility compatibility;
private final boolean useImageInputStreamConstructor;
private final Map<String, String> bundleMap;
private final InjectionType controllerInjectionType;
private final InjectionType fieldInjectionType;
private final InjectionType methodInjectionType;
private final InjectionType resourceInjectionType;
private final GenerationParameters parameters;
TestGenerationParametersImpl(@Mock final ControllerInjection controllerInjection, @Mock final ResourceBundleInjection resourceBundleInjection) {
this.controllerInjections = Map.of("class", controllerInjection);
this.sourceToGeneratedClassName = Map.of("source", "generated");
this.sourceToControllerName = Map.of("source", "class");
this.resourceBundleInjection = Objects.requireNonNull(resourceBundleInjection);
this.parameters = new GenerationParametersImpl(controllerInjections, sourceToGeneratedClassName, sourceToControllerName, resourceBundleInjection);
TestGenerationParametersImpl(@Mock final GenerationCompatibility compatibility, @Mock final InjectionType controllerInjectionType,
@Mock final InjectionType fieldInjectionType, @Mock final InjectionType methodInjectionType,
@Mock final InjectionType resourceInjectionType) {
this.compatibility = requireNonNull(compatibility);
this.useImageInputStreamConstructor = true;
this.controllerInjectionType = requireNonNull(controllerInjectionType);
this.fieldInjectionType = requireNonNull(fieldInjectionType);
this.methodInjectionType = requireNonNull(methodInjectionType);
this.resourceInjectionType = requireNonNull(resourceInjectionType);
this.bundleMap = Map.of("source", "generated");
this.parameters = new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType);
}
@Test
void testGetters() {
assertEquals(controllerInjections, parameters.controllerInjections());
assertEquals(sourceToGeneratedClassName, parameters.sourceToGeneratedClassName());
assertEquals(sourceToControllerName, parameters.sourceToControllerName());
assertEquals(resourceBundleInjection, parameters.resourceBundleInjection());
assertEquals(compatibility, parameters.compatibility());
assertEquals(useImageInputStreamConstructor, parameters.useImageInputStreamConstructor());
assertEquals(bundleMap, parameters.bundleMap());
assertEquals(controllerInjectionType, parameters.controllerInjectionType());
assertEquals(fieldInjectionType, parameters.fieldInjectionType());
assertEquals(methodInjectionType, parameters.methodInjectionType());
assertEquals(resourceInjectionType, parameters.resourceInjectionType());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(null, sourceToGeneratedClassName, sourceToControllerName, resourceBundleInjection));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(controllerInjections, null, sourceToControllerName, resourceBundleInjection));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(controllerInjections, sourceToGeneratedClassName, null, resourceBundleInjection));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(controllerInjections, sourceToGeneratedClassName, sourceToControllerName, null));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(null, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, null, controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, null, fieldInjectionType, methodInjectionType, resourceInjectionType));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, null, methodInjectionType, resourceInjectionType));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, null, resourceInjectionType));
assertThrows(NullPointerException.class, () -> new GenerationParametersImpl(compatibility, useImageInputStreamConstructor, bundleMap, controllerInjectionType, fieldInjectionType, methodInjectionType, null));
}
}

View File

@@ -3,6 +3,7 @@ package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -19,31 +20,36 @@ class TestGenerationRequestImpl {
private final GenerationParameters parameters;
private final ControllerInfo controllerInfo;
private final SourceInfo sourceInfo;
private final ParsedObject rootObject;
private final String outputClassName;
private final GenerationRequest request;
TestGenerationRequestImpl(@Mock final GenerationParameters parameters, @Mock final ControllerInfo controllerInfo, @Mock final ParsedObject rootObject) {
TestGenerationRequestImpl(@Mock final GenerationParameters parameters, @Mock final ControllerInfo controllerInfo,
@Mock final SourceInfo sourceInfo, @Mock final ParsedObject rootObject) {
this.parameters = Objects.requireNonNull(parameters);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.sourceInfo = Objects.requireNonNull(sourceInfo);
this.rootObject = Objects.requireNonNull(rootObject);
this.outputClassName = "class";
this.request = new GenerationRequestImpl(parameters, controllerInfo, rootObject, outputClassName);
this.request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, rootObject, outputClassName);
}
@Test
void testGetters() {
assertEquals(parameters, request.parameters());
assertEquals(controllerInfo, request.controllerInfo());
assertEquals(sourceInfo, request.sourceInfo());
assertEquals(rootObject, request.rootObject());
assertEquals(outputClassName, request.outputClassName());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(null, controllerInfo, rootObject, outputClassName));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, null, rootObject, outputClassName));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, null, outputClassName));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, rootObject, null));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(null, controllerInfo, sourceInfo, rootObject, outputClassName));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, null, sourceInfo, rootObject, outputClassName));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, null, rootObject, outputClassName));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, null, outputClassName));
assertThrows(NullPointerException.class, () -> new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, rootObject, null));
}
}

View File

@@ -0,0 +1,49 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.GenericTypes;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
class TestGenericTypesImpl {
private final String name;
private final List<GenericTypes> subTypes;
private final GenericTypes types;
TestGenericTypesImpl() {
this.name = "name";
this.subTypes = new ArrayList<>(List.of(mock(GenericTypes.class)));
this.types = new GenericTypesImpl(name, subTypes);
}
@Test
void testGetters() {
assertEquals(name, types.name());
assertEquals(subTypes, types.subTypes());
}
@Test
void testCopyList() {
final var originalGenericTypes = types.subTypes();
subTypes.clear();
assertEquals(originalGenericTypes, types.subTypes());
}
@Test
void testUnmodifiable() {
final var infoList = types.subTypes();
assertThrows(UnsupportedOperationException.class, infoList::clear);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ControllerFieldInfoImpl(null, subTypes));
assertThrows(NullPointerException.class, () -> new ControllerFieldInfoImpl(name, null));
}
}

View File

@@ -1,39 +0,0 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.InjectionType;
import com.github.gtache.fxml.compiler.ResourceBundleInjection;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestResourceBundleInjectionImpl {
private final InjectionType injectionType;
private final String bundleName;
private final ResourceBundleInjection resourceBundleInjection;
TestResourceBundleInjectionImpl(@Mock final InjectionType injectionType) {
this.injectionType = Objects.requireNonNull(injectionType);
this.bundleName = "bundle";
this.resourceBundleInjection = new ResourceBundleInjectionImpl(injectionType, bundleName);
}
@Test
void testGetters() {
assertEquals(injectionType, resourceBundleInjection.injectionType());
assertEquals(bundleName, resourceBundleInjection.bundleName());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ResourceBundleInjectionImpl(null, bundleName));
assertThrows(NullPointerException.class, () -> new ResourceBundleInjectionImpl(injectionType, null));
}
}

View File

@@ -0,0 +1,80 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.SourceInfo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestSourceInfoImpl {
private final String generatedClassName;
private final String controllerClassName;
private final Path sourceFile;
private final List<SourceInfo> includedSources;
private final Map<String, SourceInfo> sourceToSourceInfo;
private final boolean requiresResourceBundle;
private final SourceInfo info;
TestSourceInfoImpl(@Mock final SourceInfo subInfo) {
this.generatedClassName = "class";
this.controllerClassName = "controller";
this.sourceFile = Paths.get("path");
this.includedSources = new ArrayList<>(List.of(subInfo));
this.sourceToSourceInfo = new HashMap<>(Map.of("source", subInfo));
this.requiresResourceBundle = false;
this.info = new SourceInfoImpl(generatedClassName, controllerClassName, sourceFile, includedSources, sourceToSourceInfo, requiresResourceBundle);
}
@Test
void testGetters() {
assertEquals(generatedClassName, info.generatedClassName());
assertEquals(controllerClassName, info.controllerClassName());
assertEquals(sourceFile, info.sourceFile());
assertEquals(includedSources, info.includedSources());
assertEquals(sourceToSourceInfo, info.sourceToSourceInfo());
assertEquals(requiresResourceBundle, info.requiresResourceBundle());
}
@Test
void testCopyList() {
final var originalIncludedSources = info.includedSources();
includedSources.clear();
assertEquals(originalIncludedSources, info.includedSources());
}
@Test
void testCopyMap() {
final var originalSourceToSourceInfo = info.sourceToSourceInfo();
sourceToSourceInfo.clear();
assertEquals(originalSourceToSourceInfo, info.sourceToSourceInfo());
}
@Test
void testUnmodifiable() {
final var infoIncludedSources = info.includedSources();
final var infoSourceToSourceInfo = info.sourceToSourceInfo();
assertThrows(UnsupportedOperationException.class, infoIncludedSources::clear);
assertThrows(UnsupportedOperationException.class, infoSourceToSourceInfo::clear);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new SourceInfoImpl(null, controllerClassName, sourceFile, includedSources, sourceToSourceInfo, requiresResourceBundle));
assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, null, sourceFile, includedSources, sourceToSourceInfo, requiresResourceBundle));
assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, controllerClassName, null, includedSources, sourceToSourceInfo, requiresResourceBundle));
assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, controllerClassName, sourceFile, null, sourceToSourceInfo, requiresResourceBundle));
assertThrows(NullPointerException.class, () -> new SourceInfoImpl(generatedClassName, controllerClassName, sourceFile, includedSources, null, requiresResourceBundle));
}
}

View File

@@ -0,0 +1,58 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.parsing.ParsedInclude;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestConstructorFormatter {
private final GenerationProgress progress;
private final GenerationRequest request;
private final GenerationParameters parameters;
private final StringBuilder stringBuilder;
private final SourceInfo sourceInfo;
private final ParsedInclude include;
private final Map<String, SourceInfo> sourceToSourceInfo;
TestConstructorFormatter(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
@Mock final GenerationParameters parameters, @Mock final StringBuilder stringBuilder,
@Mock final SourceInfo sourceInfo, @Mock final ParsedInclude include) {
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.parameters = Objects.requireNonNull(parameters);
this.stringBuilder = Objects.requireNonNull(stringBuilder);
this.sourceInfo = Objects.requireNonNull(sourceInfo);
this.include = Objects.requireNonNull(include);
this.sourceToSourceInfo = new HashMap<>();
}
@BeforeEach
void beforeEach() {
when(request.parameters()).thenReturn(parameters);
when(progress.request()).thenReturn(request);
when(progress.stringBuilder()).thenReturn(stringBuilder);
when(request.sourceInfo()).thenReturn(sourceInfo);
when(sourceInfo.sourceToSourceInfo()).thenReturn(sourceToSourceInfo);
}
@Test
void testFormatSubViewConstructorCallNullSubInfo() {
assertThrows(GenerationException.class, () -> ConstructorFormatter.formatSubViewConstructorCall(progress, include));
}
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestConstructorHelper {
}

View File

@@ -0,0 +1,172 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInjectionTypes;
import com.github.gtache.fxml.compiler.impl.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestControllerInjector {
private final GenerationProgress progress;
private final GenerationRequest request;
private final GenerationParameters parameters;
private final ControllerInfo controllerInfo;
private final List<String> controllerFactoryPostAction;
private final String id;
private final String variable;
private final ParsedProperty property;
private final String propertyValue;
private final StringBuilder sb;
TestControllerInjector(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
@Mock final GenerationParameters parameters, @Mock final ControllerInfo controllerInfo,
@Mock final ParsedProperty property) {
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.parameters = Objects.requireNonNull(parameters);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.controllerFactoryPostAction = new ArrayList<>();
this.id = "id";
this.variable = "variable";
this.propertyValue = "#property";
this.property = Objects.requireNonNull(property);
this.sb = new StringBuilder();
}
@BeforeEach
void beforeEach() {
when(progress.request()).thenReturn(request);
when(request.parameters()).thenReturn(parameters);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(progress.controllerFactoryPostAction()).thenReturn(controllerFactoryPostAction);
when(progress.stringBuilder()).thenReturn(sb);
when(property.name()).thenReturn("name");
when(property.value()).thenReturn(propertyValue);
}
@Test
void testInjectControllerFieldFactory() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
ControllerInjector.injectControllerField(progress, id, variable);
final var expected = " fieldMap.put(\"" + id + "\", " + variable + ");\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testInjectControllerFieldAssign() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
ControllerInjector.injectControllerField(progress, id, variable);
final var expected = " controller." + id + " = " + variable + ";\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testInjectControllerFieldSetters() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.SETTERS);
ControllerInjector.injectControllerField(progress, id, variable);
final var expected = " controller." + GenerationHelper.getSetMethod(id) + "(" + variable + ");\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testInjectControllerFieldReflection() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.REFLECTION);
ControllerInjector.injectControllerField(progress, id, variable);
final var expected = " injectField(\"" + id + "\", " + variable + ");\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testInjectControllerFieldUnknown() {
when(parameters.fieldInjectionType()).thenReturn(null);
assertThrows(GenerationException.class, () -> ControllerInjector.injectControllerField(progress, id, variable));
}
@Test
void testInjectEventHandlerReferenceFactoryNoArgument() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFERENCE);
ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> controller." + property.value().replace("#", "") + "());\n";
assertEquals(1, controllerFactoryPostAction.size());
assertEquals(expected, controllerFactoryPostAction.getFirst());
assertEquals("", sb.toString());
}
@Test
void testInjectEventHandlerReferenceFactoryWithArgument() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFERENCE);
when(controllerInfo.handlerHasArgument(propertyValue.replace("#", ""))).thenReturn(true);
ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
assertEquals(1, controllerFactoryPostAction.size());
assertEquals(expected, controllerFactoryPostAction.getFirst());
assertEquals("", sb.toString());
}
@Test
void testInjectEventHandlerReflectionAssign() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFLECTION);
ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable);
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callEventHandlerMethod(\"" + propertyValue.replace("#", "") + "\", e));\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testInjectEventHandlerUnknownMethod() {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
when(parameters.methodInjectionType()).thenReturn(null);
assertThrows(GenerationException.class, () -> ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable));
}
@Test
void testInjectEventHandlerUnknownField() {
when(parameters.fieldInjectionType()).thenReturn(null);
when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFLECTION);
assertThrows(GenerationException.class, () -> ControllerInjector.injectEventHandlerControllerMethod(progress, property, variable));
}
@Test
void testInjectCallbackReflectionSetters() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.ASSIGN);
when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFLECTION);
ControllerInjector.injectCallbackControllerMethod(progress, property, variable, "clazz");
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(e -> callCallbackMethod(\"" + propertyValue.replace("#", "") + "\", e, clazz));\n";
assertEquals(expected, sb.toString());
assertTrue(controllerFactoryPostAction.isEmpty());
}
@Test
void testInjectCallbackReferenceFactory() throws GenerationException {
when(parameters.fieldInjectionType()).thenReturn(ControllerFieldInjectionTypes.FACTORY);
when(parameters.methodInjectionType()).thenReturn(ControllerMethodsInjectionType.REFERENCE);
ControllerInjector.injectCallbackControllerMethod(progress, property, variable, "clazz");
final var expected = " " + variable + "." + GenerationHelper.getSetMethod(property.name()) + "(controller::" + propertyValue.replace("#", "") + ");\n";
assertEquals(1, controllerFactoryPostAction.size());
assertEquals(expected, controllerFactoryPostAction.getFirst());
assertEquals("", sb.toString());
}
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestFieldSetter {
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestFontFormatter {
}

View File

@@ -0,0 +1,141 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.GenerationParameters;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import com.github.gtache.fxml.compiler.compatibility.ListCollector;
import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestGenerationCompatibilityHelper {
private final GenerationProgress progress;
private final GenerationRequest request;
private final GenerationParameters parameters;
private final GenerationCompatibility compatibility;
private final ParsedObject parsedObject;
TestGenerationCompatibilityHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
@Mock final GenerationParameters parameters, @Mock final GenerationCompatibility compatibility,
@Mock final ParsedObject parsedObject) {
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.parameters = Objects.requireNonNull(parameters);
this.compatibility = Objects.requireNonNull(compatibility);
this.parsedObject = Objects.requireNonNull(parsedObject);
}
@BeforeEach
void beforeEach() {
when(progress.request()).thenReturn(request);
when(request.parameters()).thenReturn(parameters);
when(parameters.compatibility()).thenReturn(compatibility);
when(parsedObject.className()).thenReturn("java.lang.String");
}
@Test
void testGetStartVarUseVar() {
when(compatibility.useVar()).thenReturn(true);
assertEquals(" final var ", GenerationCompatibilityHelper.getStartVar(progress, "class", 2));
}
@Test
void testGetStartVarUseVarDefaultIndent() {
when(compatibility.useVar()).thenReturn(true);
assertEquals(" final var ", GenerationCompatibilityHelper.getStartVar(progress, "class"));
}
@Test
void testGetStartVarUseVarObject() throws GenerationException {
when(compatibility.useVar()).thenReturn(true);
assertEquals(" final var ", GenerationCompatibilityHelper.getStartVar(progress, parsedObject));
}
@Test
void testGetStartVarDontUseVar() {
when(compatibility.useVar()).thenReturn(false);
assertEquals(" final javafx.scene.control.Label ", GenerationCompatibilityHelper.getStartVar(progress, "javafx.scene.control.Label", 2));
}
@Test
void testGetStartVarDontUseVarObject() throws GenerationException {
when(compatibility.useVar()).thenReturn(false);
when(parsedObject.className()).thenReturn("javafx.scene.control.Label");
assertEquals(" final javafx.scene.control.Label ", GenerationCompatibilityHelper.getStartVar(progress, parsedObject));
}
@Test
void testGetStartVarDontUseVarGenericObject() throws GenerationException {
when(compatibility.useVar()).thenReturn(false);
when(parsedObject.className()).thenReturn("javafx.scene.control.TableView");
final var id = "tableView";
when(parsedObject.attributes()).thenReturn(Map.of("fx:id", new ParsedPropertyImpl("fx:id", null, id)));
final var info = mock(ControllerInfo.class);
final var fieldInfo = mock(ControllerFieldInfo.class);
when(info.fieldInfo(id)).thenReturn(fieldInfo);
when(request.controllerInfo()).thenReturn(info);
when(fieldInfo.isGeneric()).thenReturn(true);
when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("java.lang.Integer", List.of())));
assertEquals(" final javafx.scene.control.TableView<java.lang.String, java.lang.Integer> ", GenerationCompatibilityHelper.getStartVar(progress, parsedObject));
}
@Test
void testGetToListToList() {
when(compatibility.listCollector()).thenReturn(ListCollector.TO_LIST);
assertEquals(".toList()", GenerationCompatibilityHelper.getToList(progress));
}
@Test
void testGetToListCollectToUnmodifiableList() {
when(compatibility.listCollector()).thenReturn(ListCollector.COLLECT_TO_UNMODIFIABLE_LIST);
assertEquals(".collect(java.util.stream.Collectors.toUnmodifiableList())", GenerationCompatibilityHelper.getToList(progress));
}
@Test
void testGetToListCollectToList() {
when(compatibility.listCollector()).thenReturn(ListCollector.COLLECT_TO_LIST);
assertEquals(".collect(java.util.stream.Collectors.toList())", GenerationCompatibilityHelper.getToList(progress));
}
@Test
void testGetFirstUse() {
when(compatibility.useGetFirst()).thenReturn(true);
assertEquals(".getFirst()", GenerationCompatibilityHelper.getGetFirst(progress));
}
@Test
void testGetFirstDontUse() {
when(compatibility.useGetFirst()).thenReturn(false);
assertEquals(".get(0)", GenerationCompatibilityHelper.getGetFirst(progress));
}
@Test
void testGetListOfUse() {
when(compatibility.useCollectionsOf()).thenReturn(true);
assertEquals("java.util.List.of(", GenerationCompatibilityHelper.getListOf(progress));
}
@Test
void testGetListOfDontUse() {
when(compatibility.useCollectionsOf()).thenReturn(false);
assertEquals("java.util.Arrays.asList(", GenerationCompatibilityHelper.getListOf(progress));
}
}

View File

@@ -0,0 +1,102 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestGenerationHelper {
private final GenerationProgress progress;
private final GenerationRequest request;
private final ControllerInfo controllerInfo;
private final ControllerFieldInfo fieldInfo;
private final Map<String, VariableInfo> idToVariableInfo;
private final String variableName;
private final ParsedObject object;
private final Map<String, ParsedProperty> attributes;
private final String className;
private final ParsedProperty property;
private final String propertyName;
TestGenerationHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
@Mock final ControllerInfo controllerInfo, @Mock final ControllerFieldInfo fieldInfo,
@Mock final ParsedObject object, @Mock final ParsedProperty property) {
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.fieldInfo = Objects.requireNonNull(fieldInfo);
this.object = Objects.requireNonNull(object);
this.property = Objects.requireNonNull(property);
this.idToVariableInfo = new HashMap<>();
this.variableName = "variable";
this.attributes = new HashMap<>();
this.className = "java.lang.String";
this.propertyName = "property";
}
@BeforeEach
void beforeEach() {
when(progress.request()).thenReturn(request);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(object.attributes()).thenReturn(attributes);
when(object.className()).thenReturn(className);
when(property.name()).thenReturn(propertyName);
when(progress.idToVariableInfo()).thenReturn(idToVariableInfo);
}
@Test
void testGetVariablePrefixObject() {
assertEquals("string", GenerationHelper.getVariablePrefix(object));
}
@Test
void testGetVariablePrefix() {
assertEquals("int", GenerationHelper.getVariablePrefix("int"));
}
@Test
void testGetGetMethodProperty() {
assertEquals("getProperty", GenerationHelper.getGetMethod(property));
}
@Test
void testGetGetMethod() {
assertEquals("getSomething", GenerationHelper.getGetMethod("Something"));
}
@Test
void testGetSetMethodProperty() {
assertEquals("setProperty", GenerationHelper.getSetMethod(property));
}
@Test
void testGetSetMethod() {
assertEquals("setSomething", GenerationHelper.getSetMethod("Something"));
}
@Test
void testGetSortedAttributes() {
attributes.put("a", new ParsedPropertyImpl("a", null, "valueA"));
attributes.put("b", new ParsedPropertyImpl("b", null, "valueB"));
attributes.put("c", new ParsedPropertyImpl("c", null, "valueC"));
final var expected = List.of(attributes.get("a"), attributes.get("b"), attributes.get("c"));
assertEquals(expected, GenerationHelper.getSortedAttributes(object));
}
}

View File

@@ -1,8 +1,6 @@
package com.github.gtache.fxml.compiler.impl;
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.internal.GenerationProgress;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@@ -10,61 +8,66 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SequencedCollection;
import java.util.SequencedMap;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
@ExtendWith(MockitoExtension.class)
class TestGenerationProgress {
private final GenerationRequest request;
private final Map<String, String> idToVariableName;
private final Map<String, ParsedObject> idToObject;
private final VariableInfo variableInfo;
private final Map<String, VariableInfo> idToVariableInfo;
private final Map<String, AtomicInteger> variableNameCounters;
private final SequencedMap<String, String> controllerClassToVariable;
private final SequencedCollection<String> controllerFactoryPostAction;
private final StringBuilder sb;
private final GenerationProgress progress;
TestGenerationProgress(@Mock final GenerationRequest request, @Mock final ParsedObject object) {
this.request = Objects.requireNonNull(request);
this.idToVariableName = new HashMap<>();
idToVariableName.put("var1", "var2");
this.idToObject = new HashMap<>();
idToObject.put("var1", object);
TestGenerationProgress(@Mock final GenerationRequest request, @Mock final VariableInfo variableInfo) {
this.request = requireNonNull(request);
this.variableInfo = requireNonNull(variableInfo);
this.idToVariableInfo = new HashMap<>();
idToVariableInfo.put("var1", variableInfo);
this.controllerClassToVariable = new LinkedHashMap<String, String>();
controllerClassToVariable.put("bla", "var1");
controllerClassToVariable.put("bla2", "var2");
this.variableNameCounters = new HashMap<>();
variableNameCounters.put("var", new AtomicInteger(0));
this.controllerFactoryPostAction = new ArrayList<>();
controllerFactoryPostAction.add("bla");
this.sb = new StringBuilder("test");
this.progress = new GenerationProgress(request, idToVariableName, idToObject, variableNameCounters, controllerFactoryPostAction, sb);
this.progress = new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb);
}
@Test
void testGetters() {
assertEquals(request, progress.request());
assertEquals(idToVariableName, progress.idToVariableName());
assertEquals(idToVariableInfo, progress.idToVariableInfo());
assertEquals(variableNameCounters, progress.variableNameCounters());
assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
assertEquals(sb, progress.stringBuilder());
}
@Test
void testConstructorDoesntCopy() {
idToVariableName.clear();
assertEquals(idToVariableName, progress.idToVariableName());
idToObject.clear();
assertEquals(idToObject, progress.idToObject());
idToVariableInfo.clear();
assertEquals(idToVariableInfo, progress.idToVariableInfo());
variableNameCounters.clear();
assertEquals(variableNameCounters, progress.variableNameCounters());
controllerClassToVariable.clear();
assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
controllerFactoryPostAction.clear();
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -74,15 +77,15 @@ class TestGenerationProgress {
@Test
void testCanModify() {
progress.idToVariableName().put("var3", "var4");
assertEquals(idToVariableName, progress.idToVariableName());
progress.idToObject().put("var3", mock(ParsedObject.class));
assertEquals(idToObject, progress.idToObject());
progress.idToVariableInfo().put("var3", variableInfo);
assertEquals(idToVariableInfo, progress.idToVariableInfo());
progress.variableNameCounters().put("var5", new AtomicInteger(0));
assertEquals(variableNameCounters, progress.variableNameCounters());
progress.controllerClassToVariable().put("bla3", "var3");
assertEquals(controllerClassToVariable, progress.controllerClassToVariable());
progress.controllerFactoryPostAction().add("bla2");
assertEquals(controllerFactoryPostAction, progress.controllerFactoryPostAction());
@@ -94,8 +97,9 @@ class TestGenerationProgress {
void testOtherConstructor() {
final var progress2 = new GenerationProgress(request);
assertEquals(request, progress2.request());
assertEquals(Map.of(), progress2.idToVariableName());
assertEquals(Map.of(), progress2.idToVariableInfo());
assertEquals(Map.of(), progress2.variableNameCounters());
assertEquals(Map.of(), progress2.controllerClassToVariable());
assertEquals(List.of(), progress2.controllerFactoryPostAction());
assertEquals("", progress2.stringBuilder().toString());
}
@@ -110,12 +114,12 @@ class TestGenerationProgress {
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new GenerationProgress(null, idToVariableName, idToObject, variableNameCounters, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, idToObject, variableNameCounters, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, null, variableNameCounters, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, idToObject, null, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, idToObject, variableNameCounters, null, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableName, idToObject, variableNameCounters, controllerFactoryPostAction, null));
assertThrows(NullPointerException.class, () -> new GenerationProgress(null, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, null, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, null, controllerClassToVariable, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, null, controllerFactoryPostAction, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, null, sb));
assertThrows(NullPointerException.class, () -> new GenerationProgress(request, idToVariableInfo, variableNameCounters, controllerClassToVariable, controllerFactoryPostAction, null));
}
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestHelperMethodsFormatter {
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestImageFormatter {
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestLoadMethodFormatter {
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestObjectFormatter {
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestPropertyFormatter {
}

View File

@@ -1,7 +1,13 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.WholeConstructorArgs;
import com.github.gtache.fxml.compiler.GenerationRequest;
import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
@@ -9,15 +15,54 @@ import javafx.scene.control.TableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestReflectionHelper {
private final GenerationProgress progress;
private final GenerationRequest request;
private final Map<String, ParsedProperty> attributes;
private final ControllerInfo controllerInfo;
private final ControllerFieldInfo fieldInfo;
private final ParsedObject parsedObject;
TestReflectionHelper(@Mock final GenerationProgress progress, @Mock final GenerationRequest request,
@Mock final ControllerInfo controllerInfo, @Mock final ControllerFieldInfo fieldInfo,
@Mock final ParsedObject parsedObject) {
this.progress = Objects.requireNonNull(progress);
this.request = Objects.requireNonNull(request);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.fieldInfo = Objects.requireNonNull(fieldInfo);
this.parsedObject = Objects.requireNonNull(parsedObject);
this.attributes = new HashMap<>();
}
@BeforeEach
void beforeEach() {
when(progress.request()).thenReturn(request);
when(request.controllerInfo()).thenReturn(controllerInfo);
when(controllerInfo.fieldInfo("id")).thenReturn(fieldInfo);
when(parsedObject.attributes()).thenReturn(attributes);
when(parsedObject.className()).thenReturn("javafx.scene.control.ComboBox");
attributes.put("fx:id", new ParsedPropertyImpl("fx:id", null, "id"));
when(fieldInfo.isGeneric()).thenReturn(true);
}
@Test
void testIsGeneric() {
assertFalse(ReflectionHelper.isGeneric(String.class));
@@ -127,4 +172,50 @@ class TestReflectionHelper {
void testGetDefaultProperty() throws GenerationException {
assertEquals("items", ReflectionHelper.getDefaultProperty("javafx.scene.control.ListView"));
}
@Test
void testGetWrapperClass() {
assertEquals(Integer.class.getName(), ReflectionHelper.getWrapperClass(int.class));
assertEquals(String.class.getName(), ReflectionHelper.getWrapperClass(String.class));
assertEquals(Object.class.getName(), ReflectionHelper.getWrapperClass(Object.class));
assertEquals(Double.class.getName(), ReflectionHelper.getWrapperClass(double.class));
}
@Test
void testGetClass() throws GenerationException {
assertEquals(String.class, ReflectionHelper.getClass("java.lang.String"));
assertEquals(Object.class, ReflectionHelper.getClass("java.lang.Object"));
assertEquals(int.class, ReflectionHelper.getClass("int"));
}
@Test
void testGetGenericTypesNotGeneric() throws GenerationException {
when(parsedObject.className()).thenReturn("java.lang.String");
assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
}
@Test
void testGetGenericTypesNullProperty() throws GenerationException {
attributes.clear();
assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
}
@Test
void testGetGenericTypesFieldNotFound() throws GenerationException {
when(controllerInfo.fieldInfo("id")).thenReturn(null);
assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
}
@Test
void testGetGenericTypesFieldNotGeneric() throws GenerationException {
when(fieldInfo.isGeneric()).thenReturn(false);
when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("java.lang.Integer", List.of())));
assertEquals("", ReflectionHelper.getGenericTypes(progress, parsedObject));
}
@Test
void testGetGenericTypes() throws GenerationException {
when(fieldInfo.genericTypes()).thenReturn(List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("java.lang.Integer", List.of())));
assertEquals("<java.lang.String, java.lang.Integer>", ReflectionHelper.getGenericTypes(progress, parsedObject));
}
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestSceneFormatter {
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestTriangleMeshFormatter {
}

View File

@@ -0,0 +1,9 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestURLFormatter {
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestValueFormatter {
}

View File

@@ -0,0 +1,46 @@
package com.github.gtache.fxml.compiler.impl.internal;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestVariableInfo {
private final String id;
private final ParsedObject parsedObject;
private final String variableName;
private final String className;
private final VariableInfo info;
TestVariableInfo(@Mock final ParsedObject parsedObject) {
this.id = "id";
this.parsedObject = Objects.requireNonNull(parsedObject);
this.variableName = "variableName";
this.className = "className";
this.info = new VariableInfo(id, parsedObject, variableName, className);
}
@Test
void testGetters() {
assertEquals(id, info.id());
assertEquals(parsedObject, info.parsedObject());
assertEquals(variableName, info.variableName());
assertEquals(className, info.className());
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new VariableInfo(null, parsedObject, variableName, className));
assertThrows(NullPointerException.class, () -> new VariableInfo(id, null, variableName, className));
assertThrows(NullPointerException.class, () -> new VariableInfo(id, parsedObject, null, className));
assertThrows(NullPointerException.class, () -> new VariableInfo(id, parsedObject, variableName, null));
}
}

View File

@@ -0,0 +1,8 @@
package com.github.gtache.fxml.compiler.impl.internal;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class TestWebViewFormatter {
}

View File

@@ -1,4 +1,4 @@
package com.github.gtache.fxml.compiler.impl;
package com.github.gtache.fxml.compiler.impl.internal;
import javafx.beans.NamedArg;

View File

@@ -0,0 +1,49 @@
package com.github.gtache.fxml.compiler.parsing.impl;
import com.github.gtache.fxml.compiler.parsing.ParsedDefine;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestParsedDefineImpl {
private final List<ParsedObject> children;
private final ParsedDefine parsedDefine;
TestParsedDefineImpl(@Mock ParsedObject parsedObject1, @Mock ParsedObject parsedObject2) {
this.children = new ArrayList<>(List.of(parsedObject1, parsedObject2));
this.parsedDefine = new ParsedDefineImpl(children);
}
@Test
void testGetters() {
assertEquals(children, parsedDefine.children());
}
@Test
void testCopy() {
final var originalChildren = parsedDefine.children();
children.clear();
assertEquals(originalChildren, parsedDefine.children());
}
@Test
void testUnmodifiable() {
final var objectChildren = parsedDefine.children();
assertThrows(UnsupportedOperationException.class, objectChildren::clear);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ParsedDefineImpl(null));
}
}