Finishes testing, cleanup, adds license

This commit is contained in:
Guillaume Tâche
2024-12-28 17:25:33 +01:00
parent 58fbbff7cb
commit b5c38bea54
73 changed files with 1259 additions and 455 deletions

View File

@@ -19,12 +19,16 @@ import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import static java.util.Objects.requireNonNull;
/**
* Main mojo for FXML compiler
*/
@@ -61,34 +65,66 @@ public class FXMLCompilerMojo extends AbstractMojo {
@Parameter(property = "parallelism", defaultValue = "1", required = true)
private int parallelism;
private final Compiler compiler;
private final CompilationInfoProvider.Factory compilationInfoProviderFactory;
private final ControllerProvider controllerProvider;
private final FXMLProvider.Factory fxmlProviderFactory;
/**
* Instantiates a new MOJO with the given helpers (used for testing)
*
* @param compiler The compiler
* @param compilationInfoProviderFactory The compilation info provider
* @param controllerProvider The controller provider
* @param fxmlProviderFactory The FXML provider factory
* @throws NullPointerException If any parameter is null
*/
FXMLCompilerMojo(final Compiler compiler, final CompilationInfoProvider.Factory compilationInfoProviderFactory,
final ControllerProvider controllerProvider, final FXMLProvider.Factory fxmlProviderFactory) {
this.compiler = requireNonNull(compiler);
this.compilationInfoProviderFactory = requireNonNull(compilationInfoProviderFactory);
this.controllerProvider = requireNonNull(controllerProvider);
this.fxmlProviderFactory = requireNonNull(fxmlProviderFactory);
}
/**
* Instantiates a new MOJO
*/
FXMLCompilerMojo() {
this(new Compiler(), CompilationInfoProvider::new, new ControllerProvider(), FXMLProvider::new);
}
@Override
public void execute() throws MojoExecutionException {
if (fieldInjectionType == ControllerFieldInjectionType.FACTORY && controllerInjectionType != ControllerInjectionType.FACTORY) {
getLog().warn("Field injection is set to FACTORY : Forcing controller injection to FACTORY");
controllerInjectionType = ControllerInjectionType.FACTORY;
}
final var fxmls = FXMLProvider.getFXMLs(project);
if (parallelism < 1) {
parallelism = Runtime.getRuntime().availableProcessors();
}
if (parallelism > 1) {
try (final var executor = Executors.newFixedThreadPool(parallelism)) {
final var controllerMapping = createControllerMapping(fxmls, executor);
final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping, executor);
compile(compilationInfoMapping, executor);
try {
if (fieldInjectionType == ControllerFieldInjectionType.FACTORY && controllerInjectionType != ControllerInjectionType.FACTORY) {
getLog().warn("Field injection is set to FACTORY : Forcing controller injection to FACTORY");
controllerInjectionType = ControllerInjectionType.FACTORY;
}
} else {
final var controllerMapping = createControllerMapping(fxmls);
final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping);
compile(compilationInfoMapping);
final var fxmls = fxmlProviderFactory.create(project).getFXMLs();
if (parallelism < 1) {
parallelism = Runtime.getRuntime().availableProcessors();
}
if (parallelism > 1) {
try (final var executor = Executors.newFixedThreadPool(parallelism)) {
final var controllerMapping = createControllerMapping(fxmls, executor);
final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping, executor);
compile(compilationInfoMapping, executor);
}
} else {
final var controllerMapping = createControllerMapping(fxmls);
final var compilationInfoMapping = createCompilationInfoMapping(fxmls, controllerMapping);
compile(compilationInfoMapping);
}
} catch (final RuntimeException e) {
throw new MojoExecutionException(e);
}
}
private static Map<Path, String> createControllerMapping(final Map<? extends Path, ? extends Path> fxmls) throws MojoExecutionException {
private Map<Path, String> createControllerMapping(final Map<? extends Path, ? extends Path> fxmls) throws MojoExecutionException {
final var mapping = new HashMap<Path, String>();
for (final var fxml : fxmls.keySet()) {
mapping.put(fxml, ControllerProvider.getController(fxml));
mapping.put(fxml, controllerProvider.getController(fxml));
}
return mapping;
}
@@ -96,8 +132,9 @@ public class FXMLCompilerMojo extends AbstractMojo {
private Map<Path, CompilationInfo> createCompilationInfoMapping(final Map<? extends Path, ? extends Path> fxmls,
final Map<? extends Path, String> controllerMapping) throws MojoExecutionException {
final var mapping = new HashMap<Path, CompilationInfo>();
final var compilationInfoProvider = compilationInfoProviderFactory.create(project, outputDirectory);
for (final var entry : fxmls.entrySet()) {
final var info = CompilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping, outputDirectory, project);
final var info = compilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping);
mapping.put(entry.getKey(), info);
}
return mapping;
@@ -106,51 +143,75 @@ public class FXMLCompilerMojo extends AbstractMojo {
private void compile(final Map<Path, CompilationInfo> mapping) throws MojoExecutionException {
final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion), useImageInputStreamConstructor, resourceMap,
controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType);
Compiler.compile(mapping, parameters);
compiler.compile(mapping, parameters);
project.addCompileSourceRoot(outputDirectory.toAbsolutePath().toString());
}
private static Map<Path, String> createControllerMapping(final Map<? extends Path, ? extends Path> fxmls,
final ExecutorService executor) {
final var mapping = new ConcurrentHashMap<Path, String>();
private Map<Path, String> createControllerMapping(final Map<? extends Path, ? extends Path> fxmls,
final Executor executor) {
final var futures = new ArrayList<CompletableFuture<ControllerMapping>>(fxmls.size());
for (final var fxml : fxmls.keySet()) {
executor.submit(() -> {
futures.add(CompletableFuture.supplyAsync(() -> {
try {
mapping.put(fxml, ControllerProvider.getController(fxml));
final var controller = controllerProvider.getController(fxml);
return new ControllerMapping(fxml, controller);
} catch (final MojoExecutionException e) {
throw new RuntimeException(e);
throw new CompletionException(e);
}
});
}, executor));
}
final var mapping = new HashMap<Path, String>();
futures.forEach(c -> {
final var joined = c.join();
mapping.put(joined.fxml(), joined.controller());
});
return mapping;
}
private record ControllerMapping(Path fxml, String controller) {
}
private Map<Path, CompilationInfo> createCompilationInfoMapping(final Map<? extends Path, ? extends Path> fxmls,
final Map<? extends Path, String> controllerMapping, final ExecutorService executor) {
final var mapping = new ConcurrentHashMap<Path, CompilationInfo>();
final Map<? extends Path, String> controllerMapping,
final Executor executor) {
final var compilationInfoProvider = compilationInfoProviderFactory.create(project, outputDirectory);
final var futures = new ArrayList<CompletableFuture<CompilationInfoMapping>>(fxmls.size());
for (final var entry : fxmls.entrySet()) {
executor.submit(() -> {
futures.add(CompletableFuture.supplyAsync(() -> {
try {
final var info = CompilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(), controllerMapping, outputDirectory, project);
mapping.put(entry.getKey(), info);
final var info = compilationInfoProvider.getCompilationInfo(entry.getValue(), entry.getKey(),
controllerMapping);
return new CompilationInfoMapping(entry.getKey(), info);
} catch (final MojoExecutionException e) {
throw new RuntimeException(e);
throw new CompletionException(e);
}
});
}, executor));
}
final var mapping = new HashMap<Path, CompilationInfo>();
futures.forEach(c -> {
final var joined = c.join();
mapping.put(joined.fxml(), joined.info());
});
return mapping;
}
private void compile(final Map<Path, CompilationInfo> mapping, final ExecutorService executor) throws MojoExecutionException {
final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion), useImageInputStreamConstructor, resourceMap,
controllerInjectionType, fieldInjectionType, methodInjectionType, resourceInjectionType);
mapping.forEach((p, i) -> executor.submit(() -> {
private record CompilationInfoMapping(Path fxml, CompilationInfo info) {
}
private void compile(final Map<Path, CompilationInfo> mapping, final Executor executor) {
final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion),
useImageInputStreamConstructor, resourceMap, controllerInjectionType, fieldInjectionType,
methodInjectionType, resourceInjectionType);
final var futures = new ArrayList<CompletableFuture<Void>>(mapping.size());
mapping.forEach((p, i) -> futures.add(CompletableFuture.runAsync(() -> {
try {
Compiler.compile(p, i, mapping, parameters);
compiler.compile(p, i, mapping, parameters);
} catch (final MojoExecutionException e) {
throw new RuntimeException(e);
throw new CompletionException(e);
}
}));
}, executor)));
futures.forEach(CompletableFuture::join);
project.addCompileSourceRoot(outputDirectory.toAbsolutePath().toString());
}
}

View File

@@ -24,10 +24,25 @@ public record CompilationInfo(Path inputFile, Path outputFile, String outputClas
String controllerClass, Set<FieldInfo> injectedFields, Set<String> injectedMethods,
Map<String, Path> includes, boolean requiresResourceBundle) {
/**
* Instantiates a new info
*
* @param inputFile The input file
* @param outputFile The output file
* @param outputClass The output class name
* @param controllerFile The controller file
* @param controllerClass The controller class name
* @param injectedFields The injected fields
* @param injectedMethods The injected methods
* @param includes The FXML inclusions
* @param requiresResourceBundle True if the file requires a resource bundle
* @throws NullPointerException if any parameter is null
*/
public CompilationInfo {
Objects.requireNonNull(inputFile);
Objects.requireNonNull(outputFile);
Objects.requireNonNull(outputClass);
Objects.requireNonNull(controllerClass);
Objects.requireNonNull(controllerFile);
injectedFields = Set.copyOf(injectedFields);
injectedMethods = Set.copyOf(injectedMethods);

View File

@@ -15,10 +15,11 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.regex.Pattern;
import static java.util.Objects.requireNonNull;
/**
* Helper class for {@link FXMLCompilerMojo} to provides {@link CompilationInfo}
*/
@@ -28,22 +29,31 @@ public final class CompilationInfoProvider {
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
private static final Pattern START_DOT_PATTERN = Pattern.compile("^\\.");
private CompilationInfoProvider() {
private final MavenProject project;
private final Path outputDirectory;
/**
* Instantiates the provider
*
* @param project The Maven project
* @param outputDirectory The output directory
* @throws NullPointerException If any parameter is null
*/
public CompilationInfoProvider(final MavenProject project, final Path outputDirectory) {
this.project = requireNonNull(project);
this.outputDirectory = requireNonNull(outputDirectory);
}
/**
* Gets the compilation info for the given input
*
* @param root The root path
* @param root The root path for the input
* @param inputPath The input path
* @param controllerMapping The controller mapping
* @param outputDirectory The output directory
* @param project The Maven project
* @return The compilation info
* @throws MojoExecutionException If an error occurs
*/
public static CompilationInfo getCompilationInfo(final Path root, final Path inputPath, final Map<? extends Path, String> controllerMapping,
final Path outputDirectory, final MavenProject project) throws MojoExecutionException {
public CompilationInfo getCompilationInfo(final Path root, final Path inputPath, final Map<? extends Path, String> controllerMapping) throws MojoExecutionException {
logger.info("Parsing {}", inputPath);
try {
final var documentBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
@@ -55,7 +65,7 @@ public final class CompilationInfoProvider {
final var outputFilename = getOutputFilename(inputFilename);
final var outputClass = getOutputClass(root, inputPath, outputFilename);
final var replacedPrefixPath = inputPath.toString().replace(root.toString(), outputDirectory.toString());
final var targetPath = Paths.get(replacedPrefixPath.replace(inputFilename, outputFilename));
final var targetPath = Path.of(replacedPrefixPath.replace(inputFilename, outputFilename));
builder.outputFile(targetPath);
builder.outputClass(outputClass);
handleNode(document.getDocumentElement(), builder, controllerMapping, project);
@@ -170,7 +180,7 @@ public final class CompilationInfoProvider {
private static void handleController(final String controllerClass, final CompilationInfo.Builder builder, final MavenProject project) throws MojoExecutionException {
final var subPath = controllerClass.replace(".", "/") + ".java";
final var path = project.getCompileSourceRoots().stream()
.map(s -> Paths.get(s).resolve(subPath))
.map(s -> Path.of(s).resolve(subPath))
.filter(Files::exists)
.findFirst()
.orElseThrow(() -> new MojoExecutionException("Cannot find controller " + controllerClass));
@@ -178,4 +188,20 @@ public final class CompilationInfoProvider {
builder.controllerFile(path);
builder.controllerClass(controllerClass);
}
/**
* Factory for {@link CompilationInfoProvider}
*/
@FunctionalInterface
public interface Factory {
/**
* Creates a new compilation info provider
*
* @param project The Maven project
* @param outputDirectory The output directory
* @return The compilation info provider
*/
CompilationInfoProvider create(final MavenProject project, final Path outputDirectory);
}
}

View File

@@ -16,6 +16,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;
/**
* Creates compiled Java code
@@ -24,10 +25,26 @@ public final class Compiler {
private static final Logger logger = LogManager.getLogger(Compiler.class);
private static final FXMLParser PARSER = new DOMFXMLParser();
private static final Generator GENERATOR = new GeneratorImpl();
private final FXMLParser parser;
private final Generator generator;
private Compiler() {
/**
* Instantiates a new compiler
*
* @param parser The parser to use
* @param generator The generator to use
* @throws NullPointerException If any parameter is null
*/
Compiler(final FXMLParser parser, final Generator generator) {
this.parser = Objects.requireNonNull(parser);
this.generator = Objects.requireNonNull(generator);
}
/**
* Instantiates a new compiler
*/
public Compiler() {
this(new DOMFXMLParser(), new GeneratorImpl());
}
/**
@@ -37,22 +54,31 @@ public final class Compiler {
* @param parameters The generation parameters
* @throws MojoExecutionException If an error occurs
*/
public static void compile(final Map<Path, CompilationInfo> mapping, final GenerationParameters parameters) throws MojoExecutionException {
public void compile(final Map<Path, CompilationInfo> mapping, final GenerationParameters parameters) throws MojoExecutionException {
for (final var entry : mapping.entrySet()) {
compile(entry.getKey(), entry.getValue(), mapping, parameters);
}
}
public static void compile(final Path inputPath, final CompilationInfo info, final Map<Path, CompilationInfo> mapping, final GenerationParameters parameters) throws MojoExecutionException {
/**
* Compiles the given file
*
* @param inputPath The input path
* @param info The compilation info
* @param mapping The mapping of file to compile to compilation info
* @param parameters The generation parameters
* @throws MojoExecutionException If an error occurs
*/
public void compile(final Path inputPath, final CompilationInfo info, final Map<Path, CompilationInfo> mapping, final GenerationParameters parameters) throws MojoExecutionException {
try {
logger.info("Parsing {} with {}", inputPath, PARSER.getClass().getSimpleName());
final var root = PARSER.parse(inputPath);
logger.info("Parsing {} with {}", inputPath, parser.getClass().getSimpleName());
final var root = parser.parse(inputPath);
final var controllerInfo = ControllerInfoProvider.getControllerInfo(info);
final var output = info.outputFile();
final var sourceInfo = SourceInfoProvider.getSourceInfo(info, mapping);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, root, info.outputClass());
logger.info("Compiling {}", inputPath);
final var content = GENERATOR.generate(request);
final var content = generator.generate(request);
final var outputDir = output.getParent();
Files.createDirectories(outputDir);
Files.writeString(output, content);

View File

@@ -13,19 +13,21 @@ import java.nio.file.Path;
* Extracts controller class from FXMLs
*/
public final class ControllerProvider {
private static final DocumentBuilder DOCUMENT_BUILDER;
static {
private final DocumentBuilder documentBuilder;
/**
* Instantiates a new provider
*/
public ControllerProvider() {
final var factory = DocumentBuilderFactory.newInstance();
try {
DOCUMENT_BUILDER = DocumentBuilderFactory.newInstance().newDocumentBuilder();
this.documentBuilder = factory.newDocumentBuilder();
} catch (final ParserConfigurationException e) {
throw new RuntimeException(e);
}
}
private ControllerProvider() {
}
/**
* Gets the controller class for the given FXML
*
@@ -33,9 +35,9 @@ public final class ControllerProvider {
* @return The controller class
* @throws MojoExecutionException If an error occurs
*/
public static String getController(final Path fxml) throws MojoExecutionException {
public String getController(final Path fxml) throws MojoExecutionException {
try {
final var document = DOCUMENT_BUILDER.parse(fxml.toFile());
final var document = documentBuilder.parse(fxml.toFile());
document.getDocumentElement().normalize();
final var controller = document.getDocumentElement().getAttribute("fx:controller");

View File

@@ -9,9 +9,9 @@ import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Extracts FXML paths from Maven project
@@ -19,20 +19,28 @@ import java.util.Map;
public final class FXMLProvider {
private static final Logger logger = LogManager.getLogger(FXMLProvider.class);
private FXMLProvider() {
private final MavenProject project;
/**
* Instantiates a new provider
*
* @param project The Maven project
* @throws NullPointerException If the project is null
*/
public FXMLProvider(final MavenProject project) {
this.project = Objects.requireNonNull(project);
}
/**
* Returns all the FXML files in the project's resources
*
* @param project The Maven project
* @return A mapping of file to resource directory
* @throws MojoExecutionException If an error occurs
*/
public static Map<Path, Path> getFXMLs(final MavenProject project) throws MojoExecutionException {
public Map<Path, Path> getFXMLs() throws MojoExecutionException {
final var map = new HashMap<Path, Path>();
for (final var resource : project.getResources()) {
final var path = Paths.get(resource.getDirectory());
final var path = Path.of(resource.getDirectory());
if (Files.isDirectory(path)) {
try (final var stream = Files.find(path, Integer.MAX_VALUE, (p, a) -> p.toString().endsWith(".fxml"), FileVisitOption.FOLLOW_LINKS)) {
final var curList = stream.toList();
@@ -49,4 +57,18 @@ public final class FXMLProvider {
}
return map;
}
/**
* Factory for {@link FXMLProvider}
*/
@FunctionalInterface
public interface Factory {
/**
* Creates a new provider
*
* @param project The Maven project
* @return The provider
*/
FXMLProvider create(final MavenProject project);
}
}

View File

@@ -38,10 +38,17 @@ final class GenericParser {
}
List<GenericTypes> parse() throws MojoExecutionException {
return parseGenericTypes();
final var parsed = parseGenericTypes();
if (index < content.length()) {
throw new MojoExecutionException("Expected EOF at " + index + " in " + content);
}
return parsed;
}
private List<GenericTypes> parseGenericTypes() throws MojoExecutionException {
if (content.isEmpty()) {
throw new MojoExecutionException("Empty generic types");
}
final var ret = new ArrayList<GenericTypes>();
eatSpaces();
eat('<');
@@ -52,12 +59,16 @@ final class GenericParser {
if (peek() == '<') {
final var genericTypes = parseGenericTypes();
ret.add(new GenericTypesImpl(type, genericTypes));
} else {
ret.add(new GenericTypesImpl(type, List.of()));
}
eatSpaces();
if (peek() == ',') {
eat(',');
} else if (peek() == '>') {
eat('>');
eatSpaces();
return ret;
} else if (peek() == ',') {
eat(',');
ret.add(new GenericTypesImpl(type, List.of()));
}
} while (index < content.length());
return ret;
@@ -72,17 +83,17 @@ final class GenericParser {
}
private void eatSpaces() {
while (peek() == ' ') {
while (index < content.length() && peek() == ' ') {
read();
}
}
private String parseType() throws MojoExecutionException {
final var sb = new StringBuilder();
while (peek() != '<' && index < content.length()) {
while (peek() != '<' && peek() != '>' && peek() != ',' && index < content.length()) {
sb.append(read());
}
final var type = sb.toString();
final var type = sb.toString().trim();
if (type.contains(".") || JAVA_LANG_CLASSES.contains(type)) {
return type;
} else if (imports.containsKey(type)) {

View File

@@ -32,7 +32,7 @@ final class SourceInfoProvider {
final var requiresResourceBundle = info.requiresResourceBundle();
final var includesMapping = new HashMap<String, SourceInfo>();
includes.forEach((k, v) -> includesMapping.put(k, getSourceInfo(mapping.get(v), mapping)));
//FIXME mutliple includes
//FIXME mutliple same includes
return new SourceInfoImpl(outputClass, controllerClass, inputFile, List.copyOf(includesMapping.values()), includesMapping, requiresResourceBundle);
}
}

View File

@@ -1,8 +1,209 @@
package com.github.gtache.fxml.compiler.maven;
import com.github.gtache.fxml.compiler.ControllerFieldInjectionType;
import com.github.gtache.fxml.compiler.ControllerInjectionType;
import com.github.gtache.fxml.compiler.ControllerMethodsInjectionType;
import com.github.gtache.fxml.compiler.ResourceBundleInjectionType;
import com.github.gtache.fxml.compiler.compatibility.impl.GenerationCompatibilityImpl;
import com.github.gtache.fxml.compiler.impl.GenerationParametersImpl;
import com.github.gtache.fxml.compiler.maven.internal.CompilationInfo;
import com.github.gtache.fxml.compiler.maven.internal.CompilationInfoProvider;
import com.github.gtache.fxml.compiler.maven.internal.Compiler;
import com.github.gtache.fxml.compiler.maven.internal.ControllerProvider;
import com.github.gtache.fxml.compiler.maven.internal.FXMLProvider;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
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.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class TestFXMLCompilerMojo {
private final Compiler compiler;
private final CompilationInfoProvider compilationInfoProvider;
private final ControllerProvider controllerProvider;
private final FXMLProvider fxmlProvider;
private final MavenProject mavenProject;
private final Path outputDirectory;
private final int targetVersion;
private final boolean useImageInputStreamConstructor;
private final ControllerInjectionType controllerInjectionType;
private final ControllerFieldInjectionType controllerFieldInjectionType;
private final ControllerMethodsInjectionType controllerMethodsInjectionType;
private final ResourceBundleInjectionType resourceBundleInjectionType;
private final Map<String, String> resourceMap;
private final Map<Path, Path> fxmls;
private final CompilationInfo compilationInfo;
private final FXMLCompilerMojo mojo;
TestFXMLCompilerMojo(@Mock final Compiler compiler, @Mock final CompilationInfoProvider compilationInfoProvider,
@Mock final ControllerProvider controllerProvider, @Mock final FXMLProvider fxmlProvider,
@Mock final MavenProject mavenProject,
@Mock final ControllerInjectionType controllerInjectionType,
@Mock final ControllerFieldInjectionType controllerFieldInjectionType,
@Mock final ControllerMethodsInjectionType controllerMethodsInjectionType,
@Mock final ResourceBundleInjectionType resourceBundleInjectionType,
@Mock final CompilationInfo compilationInfo) {
this.compiler = Objects.requireNonNull(compiler);
this.compilationInfoProvider = Objects.requireNonNull(compilationInfoProvider);
this.controllerProvider = Objects.requireNonNull(controllerProvider);
this.fxmlProvider = Objects.requireNonNull(fxmlProvider);
this.mavenProject = Objects.requireNonNull(mavenProject);
this.outputDirectory = Path.of("output");
this.targetVersion = 11;
this.useImageInputStreamConstructor = true;
this.controllerInjectionType = Objects.requireNonNull(controllerInjectionType);
this.controllerFieldInjectionType = Objects.requireNonNull(controllerFieldInjectionType);
this.controllerMethodsInjectionType = Objects.requireNonNull(controllerMethodsInjectionType);
this.resourceBundleInjectionType = Objects.requireNonNull(resourceBundleInjectionType);
this.resourceMap = Map.of("a", "b", "c", "d");
this.compilationInfo = Objects.requireNonNull(compilationInfo);
this.fxmls = new HashMap<>();
this.mojo = new FXMLCompilerMojo(compiler, (p, o) -> compilationInfoProvider, controllerProvider,
p -> fxmlProvider);
}
@BeforeEach
void beforeEach() throws Exception {
setValue("project", mavenProject);
setValue("outputDirectory", outputDirectory);
setIntValue("targetVersion", targetVersion);
setBooleanValue("useImageInputStreamConstructor", useImageInputStreamConstructor);
setValue("controllerInjectionType", controllerInjectionType);
setValue("fieldInjectionType", controllerFieldInjectionType);
setValue("methodInjectionType", controllerMethodsInjectionType);
setValue("resourceInjectionType", resourceBundleInjectionType);
setValue("resourceMap", resourceMap);
when(fxmlProvider.getFXMLs()).thenReturn(fxmls);
when(controllerProvider.getController(any())).then(i -> ((Path) i.getArgument(0)).toString());
when(compilationInfoProvider.getCompilationInfo(any(), any(), anyMap())).thenReturn(compilationInfo);
fxmls.put(Path.of("a"), Path.of("b"));
fxmls.put(Path.of("c"), Path.of("d"));
}
@Test
void testExecuteParallel() throws Exception {
final var pathA = Path.of("a");
final var pathC = Path.of("c");
setIntValue("parallelism", 4);
mojo.execute();
for (final var p : fxmls.keySet()) {
verify(controllerProvider).getController(p);
}
final var controllerMapping = Map.of(pathA, "a", pathC, "c");
for (final var e : fxmls.entrySet()) {
verify(compilationInfoProvider).getCompilationInfo(e.getValue(), e.getKey(), controllerMapping);
}
final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion), useImageInputStreamConstructor, resourceMap,
controllerInjectionType, controllerFieldInjectionType, controllerMethodsInjectionType, resourceBundleInjectionType);
final var compilationInfoMapping = Map.of(pathA, compilationInfo, pathC, compilationInfo);
for (final var entry : compilationInfoMapping.entrySet()) {
verify(compiler).compile(entry.getKey(), entry.getValue(), compilationInfoMapping, parameters);
}
}
@Test
void testExecuteDefaultCores() throws Exception {
final var pathA = Path.of("a");
final var pathC = Path.of("c");
setIntValue("parallelism", 0);
mojo.execute();
assertEquals(Runtime.getRuntime().availableProcessors(), getValue("parallelism"));
for (final var p : fxmls.keySet()) {
verify(controllerProvider).getController(p);
}
final var controllerMapping = Map.of(pathA, "a", pathC, "c");
for (final var e : fxmls.entrySet()) {
verify(compilationInfoProvider).getCompilationInfo(e.getValue(), e.getKey(), controllerMapping);
}
final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion), useImageInputStreamConstructor, resourceMap,
controllerInjectionType, controllerFieldInjectionType, controllerMethodsInjectionType, resourceBundleInjectionType);
final var compilationInfoMapping = Map.of(pathA, compilationInfo, pathC, compilationInfo);
for (final var entry : compilationInfoMapping.entrySet()) {
verify(compiler).compile(entry.getKey(), entry.getValue(), compilationInfoMapping, parameters);
}
}
@Test
void testControllerProviderException() throws Exception {
setIntValue("parallelism", 4);
doThrow(MojoExecutionException.class).when(controllerProvider).getController(any());
assertThrows(MojoExecutionException.class, mojo::execute);
}
@Test
void testCompilationInfoProviderException() throws Exception {
setIntValue("parallelism", 4);
doThrow(MojoExecutionException.class).when(compilationInfoProvider).getCompilationInfo(any(), any(), anyMap());
assertThrows(MojoExecutionException.class, mojo::execute);
}
@Test
void testCompilerException() throws Exception {
setIntValue("parallelism", 4);
doThrow(MojoExecutionException.class).when(compiler).compile(any(), any(), anyMap(), any());
assertThrows(MojoExecutionException.class, mojo::execute);
}
@Test
void testExecuteSingleCore() throws Exception {
final var pathA = Path.of("a");
final var pathC = Path.of("c");
setIntValue("parallelism", 1);
mojo.execute();
for (final var p : fxmls.keySet()) {
verify(controllerProvider).getController(p);
}
final var controllerMapping = Map.of(pathA, "a", pathC, "c");
for (final var e : fxmls.entrySet()) {
verify(compilationInfoProvider).getCompilationInfo(e.getValue(), e.getKey(), controllerMapping);
}
final var parameters = new GenerationParametersImpl(new GenerationCompatibilityImpl(targetVersion), useImageInputStreamConstructor, resourceMap,
controllerInjectionType, controllerFieldInjectionType, controllerMethodsInjectionType, resourceBundleInjectionType);
final var compilationInfoMapping = Map.of(pathA, compilationInfo, pathC, compilationInfo);
verify(compiler).compile(compilationInfoMapping, parameters);
}
@Test
void testOverrideControllerInjectionType() throws Exception {
setValue("fieldInjectionType", ControllerFieldInjectionType.FACTORY);
mojo.execute();
assertEquals(ControllerInjectionType.FACTORY, getValue("controllerInjectionType"));
}
private Object getValue(final String name) throws Exception {
final var field = FXMLCompilerMojo.class.getDeclaredField(name);
field.setAccessible(true);
return field.get(mojo);
}
private void setBooleanValue(final String name, final boolean value) throws Exception {
final var field = FXMLCompilerMojo.class.getDeclaredField(name);
field.setAccessible(true);
field.setBoolean(mojo, value);
}
private void setIntValue(final String name, final int value) throws Exception {
final var field = FXMLCompilerMojo.class.getDeclaredField(name);
field.setAccessible(true);
field.setInt(mojo, value);
}
private void setValue(final String name, final Object value) throws Exception {
final var field = FXMLCompilerMojo.class.getDeclaredField(name);
field.setAccessible(true);
field.set(mojo, value);
}
}

View File

@@ -6,8 +6,6 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -24,7 +22,6 @@ class TestCompilationInfoBuilder {
private final Set<FieldInfo> injectedFields;
private final Set<String> injectedMethods;
private final Map<String, Path> includes;
private final boolean requiresResourceBundle;
private final CompilationInfo info;
TestCompilationInfoBuilder(@Mock final Path inputFile, @Mock final Path outputFile, @Mock final Path controllerFile, @Mock final FieldInfo fieldInfo) {
@@ -33,11 +30,10 @@ class TestCompilationInfoBuilder {
this.outputClass = "outputClass";
this.controllerFile = Objects.requireNonNull(controllerFile);
this.controllerClass = "controllerClass";
this.injectedFields = new HashSet<>(Set.of(fieldInfo));
this.injectedMethods = new HashSet<>(Set.of("one", "two"));
this.includes = new HashMap<>(Map.of("one", Objects.requireNonNull(inputFile)));
this.requiresResourceBundle = true;
this.info = new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes, requiresResourceBundle);
this.injectedFields = Set.of(new FieldInfo("type", "name"));
this.injectedMethods = Set.of("one", "two");
this.includes = Map.of("one", Objects.requireNonNull(inputFile));
this.info = new CompilationInfo(inputFile, outputFile, outputClass, controllerFile, controllerClass, injectedFields, injectedMethods, includes, true);
}
@Test

View File

@@ -1,8 +1,143 @@
package com.github.gtache.fxml.compiler.maven.internal;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestCompilationInfoProvider {
private final MavenProject project;
TestCompilationInfoProvider(@Mock final MavenProject project) {
this.project = Objects.requireNonNull(project);
}
@Test
void testCompleteExample(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("infoView.fxml", tempDir);
final var includedPath = path.getParent().resolve("includeView.fxml");
final var controllerPath = path.getParent().resolve("InfoController.java");
final var controllerClass = "com.github.gtache.fxml.compiler.maven.internal.InfoController";
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of(tempDir.toString()));
final var expected = new CompilationInfo(path, path.getParent().resolve("InfoView.java"),
"com.github.gtache.fxml.compiler.maven.internal.InfoView", controllerPath, controllerClass,
Set.of(new FieldInfo("javafx.event.EventHandler", "onContextMenuRequested"), new FieldInfo("Button", "button"),
new FieldInfo("com.github.gtache.fxml.compiler.maven.internal.IncludeController", "includeViewController")),
Set.of("onAction"), Map.of("includeView.fxml", path.getParent().resolve("includeView.fxml")), true);
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
final var actual = compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of(includedPath, "com.github.gtache.fxml.compiler.maven.internal.IncludeController"));
assertEquals(expected, actual);
}
@Test
void testComplexFilename(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("com_plex-view.fxml", tempDir);
final var controllerPath = path.getParent().resolve("InfoController.java");
final var controllerClass = "com.github.gtache.fxml.compiler.maven.internal.InfoController";
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of(tempDir.toString()));
final var expected = new CompilationInfo(path, path.getParent().resolve("ComPlexView.java"),
"com.github.gtache.fxml.compiler.maven.internal.ComPlexView", controllerPath, controllerClass,
Set.of(), Set.of(), Map.of(), false);
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
final var actual = compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of());
assertEquals(expected, actual);
}
@Test
void testNoController(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("noController.fxml", tempDir);
final var controllerPath = path.getParent().resolve("InfoController.java");
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of(tempDir.toString()));
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
assertThrows(MojoExecutionException.class, () -> compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of()));
}
@Test
void testIncludeNoSource(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("missingSource.fxml", tempDir);
final var includedPath = path.getParent().resolve("includeView.fxml");
final var controllerPath = path.getParent().resolve("InfoController.java");
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of(tempDir.toString()));
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
assertThrows(MojoExecutionException.class, () -> compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of(includedPath, "com.github.gtache.fxml.compiler.maven.internal.IncludeController")));
}
@Test
void testCantFindControllerFile(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("infoView.fxml", tempDir);
final var includedPath = path.getParent().resolve("includeView.fxml");
final var controllerPath = path.getParent().resolve("InfoController.java");
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of());
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
assertThrows(MojoExecutionException.class, () -> compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of(includedPath, "com.github.gtache.fxml.compiler.maven.internal.IncludeController")));
}
@Test
void testCantFindIncludedControllerClass(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("infoView.fxml", tempDir);
final var controllerPath = path.getParent().resolve("InfoController.java");
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of(tempDir.toString()));
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
assertThrows(MojoExecutionException.class, () -> compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of()));
}
@Test
void testNoResourceBundle(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("noResourceBundle.fxml", tempDir);
final var includedPath = path.getParent().resolve("includeView.fxml");
final var controllerPath = path.getParent().resolve("InfoController.java");
final var controllerClass = "com.github.gtache.fxml.compiler.maven.internal.InfoController";
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of(tempDir.toString()));
final var expected = new CompilationInfo(path, path.getParent().resolve("NoResourceBundle.java"),
"com.github.gtache.fxml.compiler.maven.internal.NoResourceBundle", controllerPath, controllerClass,
Set.of(new FieldInfo("javafx.event.EventHandler", "onContextMenuRequested"), new FieldInfo("Button", "button"),
new FieldInfo("com.github.gtache.fxml.compiler.maven.internal.IncludeController", "includeViewController")),
Set.of("onAction"), Map.of("includeView.fxml", path.getParent().resolve("includeView.fxml")), false);
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
final var actual = compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of(includedPath, "com.github.gtache.fxml.compiler.maven.internal.IncludeController"));
assertEquals(expected, actual);
}
@Test
void testUnknownOn(@TempDir final Path tempDir) throws Exception {
final var path = copyFile("unknownOn.fxml", tempDir);
final var controllerPath = path.getParent().resolve("InfoController.java");
Files.createFile(controllerPath);
when(project.getCompileSourceRoots()).thenReturn(List.of(tempDir.toString()));
final var compilationInfoProvider = new CompilationInfoProvider(project, tempDir);
assertThrows(MojoExecutionException.class, () -> compilationInfoProvider.getCompilationInfo(tempDir, path, Map.of()));
}
private Path copyFile(final String source, final Path tempDir) throws IOException {
final var out = tempDir.resolve("com").resolve("github").resolve("gtache").resolve("fxml").resolve("compiler").resolve("maven").resolve("internal").resolve(source);
Files.createDirectories(out.getParent());
try (final var in = getClass().getResourceAsStream(source)) {
assertNotNull(in);
Files.copy(in, out);
}
return out;
}
}

View File

@@ -1,16 +1,15 @@
package com.github.gtache.fxml.compiler.maven.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.Generator;
import com.github.gtache.fxml.compiler.SourceInfo;
import com.github.gtache.fxml.compiler.impl.GenerationRequestImpl;
import com.github.gtache.fxml.compiler.parsing.FXMLParser;
import com.github.gtache.fxml.compiler.parsing.ParseException;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import org.apache.maven.plugin.MojoExecutionException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
@@ -19,7 +18,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Objects;
@@ -30,88 +28,88 @@ import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class TestCompiler {
private final ControllerInfoProvider controllerInfoProvider;
private final SourceInfoProvider sourceInfoProvider;
private final FXMLParser fxmlParser;
private final Generator generator;
private final CompilationInfo compilationInfo;
private final ParsedObject object;
private final ControllerInfo controllerInfo;
private final SourceInfo sourceInfo;
private final String content;
private final GenerationParameters parameters;
private final Compiler compiler;
TestCompiler(@Mock final ControllerInfoProvider controllerInfoProvider, @Mock final SourceInfoProvider sourceInfoProvider,
@Mock final FXMLParser fxmlParser, @Mock final CompilationInfo compilationInfo, @Mock final ParsedObject object,
@Mock final ControllerInfo controllerInfo, @Mock final SourceInfo sourceInfo,
TestCompiler(@Mock final FXMLParser fxmlParser, @Mock final CompilationInfo compilationInfo, @Mock final ParsedObject object,
@Mock final GenerationParameters parameters, @Mock final Generator generator) {
this.controllerInfoProvider = Objects.requireNonNull(controllerInfoProvider);
this.sourceInfoProvider = Objects.requireNonNull(sourceInfoProvider);
this.fxmlParser = Objects.requireNonNull(fxmlParser);
this.compilationInfo = Objects.requireNonNull(compilationInfo);
this.object = Objects.requireNonNull(object);
this.controllerInfo = Objects.requireNonNull(controllerInfo);
this.sourceInfo = Objects.requireNonNull(sourceInfo);
this.content = "content";
this.parameters = Objects.requireNonNull(parameters);
this.generator = Objects.requireNonNull(generator);
this.compiler = new Compiler(fxmlParser, generator);
}
@BeforeEach
void beforeEach() throws MojoExecutionException, GenerationException, ParseException {
void beforeEach() throws GenerationException, ParseException {
when(fxmlParser.parse((Path) any())).thenReturn(object);
when(ControllerInfoProvider.getControllerInfo(compilationInfo)).thenReturn(controllerInfo);
when(SourceInfoProvider.getSourceInfo(eq(compilationInfo), anyMap())).thenReturn(sourceInfo);
when(generator.generate(any())).thenReturn(content);
when(compilationInfo.outputClass()).thenReturn("outputClass");
when(compilationInfo.controllerClass()).thenReturn("controllerClass");
when(compilationInfo.includes()).thenReturn(Map.of());
}
@Test
void testCompile(@TempDir final Path tempDir) throws Exception {
final var path = tempDir.resolve("fxml1.fxml");
Files.createFile(path);
final var outputPath = tempDir.resolve("subFolder").resolve("fxml1.java");
final var outputClass = "outputClass";
when(compilationInfo.outputFile()).thenReturn(outputPath);
when(compilationInfo.outputClass()).thenReturn(outputClass);
when(compilationInfo.inputFile()).thenReturn(path);
when(compilationInfo.controllerFile()).thenReturn(path);
final var controllerInfo = ControllerInfoProvider.getControllerInfo(compilationInfo);
final var sourceInfo = SourceInfoProvider.getSourceInfo(compilationInfo, Map.of());
final var mapping = Map.of(path, compilationInfo);
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
Compiler.compile(mapping, parameters);
compiler.compile(mapping, parameters);
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
verify(generator).generate(request);
assertEquals(content, Files.readString(outputPath));
}
@Test
@Disabled("Need cross-platform unwritable path")
void testCompileIOException(@TempDir final Path tempDir) throws Exception {
final var path = tempDir.resolve("fxml1.fxml");
final var outputPath = Paths.get("/whatever");
Files.createFile(path);
final var outputPath = Path.of("/in/a/b/c/whatever");
final var outputClass = "outputClass";
when(compilationInfo.outputFile()).thenReturn(outputPath);
when(compilationInfo.outputClass()).thenReturn(outputClass);
when(compilationInfo.inputFile()).thenReturn(path);
when(compilationInfo.controllerFile()).thenReturn(path);
final var mapping = Map.of(path, compilationInfo);
final var controllerInfo = ControllerInfoProvider.getControllerInfo(compilationInfo);
final var sourceInfo = SourceInfoProvider.getSourceInfo(compilationInfo, Map.of());
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
verify(generator).generate(request);
}
@Test
void testCompileRuntimeException(@TempDir final Path tempDir) throws Exception {
final var path = tempDir.resolve("fxml1.fxml");
Files.createFile(path);
final var outputPath = tempDir.resolve("subFolder").resolve("fxml1.java");
final var outputClass = "outputClass";
when(compilationInfo.outputFile()).thenReturn(outputPath);
when(compilationInfo.outputClass()).thenReturn(outputClass);
when(compilationInfo.inputFile()).thenReturn(path);
when(compilationInfo.controllerFile()).thenReturn(path);
final var mapping = Map.of(path, compilationInfo);
final var controllerInfo = ControllerInfoProvider.getControllerInfo(compilationInfo);
final var sourceInfo = SourceInfoProvider.getSourceInfo(compilationInfo, Map.of());
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
when(generator.generate(request)).thenThrow(RuntimeException.class);
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
verify(generator).generate(request);
}
@@ -120,25 +118,27 @@ class TestCompiler {
when(fxmlParser.parse((Path) any())).thenThrow(ParseException.class);
final var path = tempDir.resolve("fxml1.fxml");
final var mapping = Map.of(path, compilationInfo);
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
verifyNoInteractions(controllerInfoProvider, sourceInfoProvider, generator);
verifyNoInteractions(generator);
}
@Test
void testCompileGenerationException(@TempDir final Path tempDir) throws Exception {
final var path = tempDir.resolve("fxml1.fxml");
Files.createFile(path);
final var outputPath = tempDir.resolve("subFolder").resolve("fxml1.java");
final var outputClass = "outputClass";
when(compilationInfo.outputFile()).thenReturn(outputPath);
when(compilationInfo.outputClass()).thenReturn(outputClass);
when(compilationInfo.inputFile()).thenReturn(path);
when(compilationInfo.controllerFile()).thenReturn(path);
final var mapping = Map.of(path, compilationInfo);
final var controllerInfo = ControllerInfoProvider.getControllerInfo(compilationInfo);
final var sourceInfo = SourceInfoProvider.getSourceInfo(compilationInfo, Map.of());
final var request = new GenerationRequestImpl(parameters, controllerInfo, sourceInfo, object, outputClass);
when(generator.generate(request)).thenThrow(GenerationException.class);
assertThrows(MojoExecutionException.class, () -> Compiler.compile(mapping, parameters));
assertThrows(MojoExecutionException.class, () -> compiler.compile(mapping, parameters));
verify(fxmlParser).parse(path);
ControllerInfoProvider.getControllerInfo(compilationInfo);
SourceInfoProvider.getSourceInfo(compilationInfo, mapping);
verify(generator).generate(request);
}
}

View File

@@ -2,6 +2,7 @@ package com.github.gtache.fxml.compiler.maven.internal;
import com.github.gtache.fxml.compiler.impl.ControllerFieldInfoImpl;
import com.github.gtache.fxml.compiler.impl.ControllerInfoImpl;
import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
import org.apache.maven.plugin.MojoExecutionException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -11,10 +12,10 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -24,11 +25,9 @@ import static org.mockito.Mockito.when;
class TestControllerInfoProvider {
private final CompilationInfo compilationInfo;
private final ControllerInfoProvider controllerInfoProvider;
TestControllerInfoProvider(@Mock final CompilationInfo compilationInfo) {
this.compilationInfo = Objects.requireNonNull(compilationInfo);
this.controllerInfoProvider = new ControllerInfoProvider();
}
@@ -49,6 +48,7 @@ class TestControllerInfoProvider {
private Button button;
private ComboBox rawBox;
private TableColumn<Integer, ComboBox<String>> tableColumn;
private ComboBox<Integer> fullBox;
@FXML
void initialize() {
@@ -64,18 +64,47 @@ class TestControllerInfoProvider {
}
""");
when(compilationInfo.controllerFile()).thenReturn(fxml);
final var expectedInfo = new ControllerInfoImpl("com.github.gtache.fxml.compiler.maven.internal.LoadController",
Map.of("onClick", false, "onOtherClick", true), Map.of("keyEventHandler", new ControllerFieldInfoImpl("keyEventHandler", List.of()),
"comboBox", new ControllerFieldInfoImpl("comboBox", List.of("String")), "button", new ControllerFieldInfoImpl("button", List.of()),
"rawBox", new ControllerFieldInfoImpl("rawBox", List.of()),
"tableColumn", new ControllerFieldInfoImpl("tableColumn", List.of("Integer", "javafx.scene.control.ComboBox<String>"))), true);
final var controllerClass = "com.github.gtache.fxml.compiler.maven.internal.LoadController";
when(compilationInfo.controllerClass()).thenReturn(controllerClass);
when(compilationInfo.injectedFields()).thenReturn(Set.of(new FieldInfo("EventHandler", "keyEventHandler"),
new FieldInfo("ComboBox", "comboBox"), new FieldInfo("Button", "button"),
new FieldInfo("ComboBox", "rawBox"), new FieldInfo("TableColumn", "tableColumn"),
new FieldInfo("ComboBox", "abc"), new FieldInfo("javafx.scene.control.ComboBox", "fullBox")));
when(compilationInfo.injectedMethods()).thenReturn(Set.of("onClick", "onOtherClick"));
final var expectedInfo = new ControllerInfoImpl(controllerClass,
Map.of("onClick", false, "onOtherClick", true), Map.of("keyEventHandler", new ControllerFieldInfoImpl("keyEventHandler", List.of(new GenericTypesImpl("javafx.event.KeyEvent", List.of()))),
"comboBox", new ControllerFieldInfoImpl("comboBox", List.of(new GenericTypesImpl("String", List.of()))), "button", new ControllerFieldInfoImpl("button", List.of()),
"rawBox", new ControllerFieldInfoImpl("rawBox", List.of()), "fullBox", new ControllerFieldInfoImpl("fullBox", List.of(new GenericTypesImpl("Integer", List.of()))),
"tableColumn", new ControllerFieldInfoImpl("tableColumn", List.of(new GenericTypesImpl("Integer", List.of()),
new GenericTypesImpl("javafx.scene.control.ComboBox", List.of(new GenericTypesImpl("String", List.of())))))), true);
final var actual = ControllerInfoProvider.getControllerInfo(compilationInfo);
assertEquals(expectedInfo, actual);
}
@Test
void testGetControllerInfoMethodNotFound(@TempDir final Path tempDir) throws Exception {
final var fxml = tempDir.resolve("fxml.fxml");
Files.writeString(fxml, """
package com.github.gtache.fxml.compiler.maven.internal;
import javafx.event.EventHandler;
import javafx.event.KeyEvent;
import javafx.scene.control.*;
public class LoadController {
}
""");
when(compilationInfo.controllerFile()).thenReturn(fxml);
when(compilationInfo.controllerClass()).thenReturn("com.github.gtache.fxml.compiler.maven.internal.LoadController");
when(compilationInfo.injectedFields()).thenReturn(Set.of());
when(compilationInfo.injectedMethods()).thenReturn(Set.of("onClick"));
assertThrows(MojoExecutionException.class, () -> ControllerInfoProvider.getControllerInfo(compilationInfo));
}
@Test
void testGetControllerInfoException() {
when(compilationInfo.controllerFile()).thenReturn(Paths.get("/whatever"));
when(compilationInfo.controllerFile()).thenReturn(Path.of("/in/a/b/c/whatever"));
assertThrows(MojoExecutionException.class, () -> ControllerInfoProvider.getControllerInfo(compilationInfo));
}
}

View File

@@ -8,7 +8,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -16,6 +15,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class TestControllerProvider {
private final ControllerProvider controllerProvider;
TestControllerProvider() {
this.controllerProvider = new ControllerProvider();
}
@Test
void testGetController(@TempDir final Path tempDir) throws Exception {
final var fxml = tempDir.resolve("fxml.fxml");
@@ -23,7 +28,7 @@ class TestControllerProvider {
"<BorderPane xmlns=\"http://javafx.com/javafx/22\" xmlns:fx=\"http://javafx.com/fxml/1\"" +
" fx:controller=\"LoadController\">" +
"</BorderPane>\n");
assertEquals("LoadController", ControllerProvider.getController(fxml));
assertEquals("LoadController", controllerProvider.getController(fxml));
}
@Test
@@ -32,11 +37,11 @@ class TestControllerProvider {
Files.writeString(fxml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<BorderPane xmlns=\"http://javafx.com/javafx/22\" xmlns:fx=\"http://javafx.com/fxml/1\">" +
"</BorderPane>\n");
assertThrows(MojoExecutionException.class, () -> ControllerProvider.getController(fxml));
assertThrows(MojoExecutionException.class, () -> controllerProvider.getController(fxml));
}
@Test
void testGetControllerError() {
assertThrows(MojoExecutionException.class, () -> ControllerProvider.getController(Paths.get("fxml.fxml")));
assertThrows(MojoExecutionException.class, () -> controllerProvider.getController(Path.of("fxml.fxml")));
}
}

View File

@@ -22,20 +22,24 @@ import static org.mockito.Mockito.when;
class TestFXMLProvider {
private final MavenProject project;
private final FXMLProvider provider;
TestFXMLProvider(@Mock final MavenProject project) {
this.project = Objects.requireNonNull(project);
this.provider = new FXMLProvider(project);
}
@Test
void testGetFXMLs(@TempDir final Path tempDir, @TempDir final Path otherTempDir) throws Exception {
final var subFolder = tempDir.resolve("subFolder");
Files.createDirectories(subFolder);
Files.createFile(subFolder.resolve("subfxml1.fxml"));
Files.createFile(subFolder.resolve("subfxml2.fxml"));
Files.createFile(tempDir.resolve("fxml1.fxml"));
Files.createFile(tempDir.resolve("fxml2.fxml"));
final var otherSubFolder = otherTempDir.resolve("subFolder");
Files.createDirectories(otherSubFolder);
Files.createFile(otherSubFolder.resolve("subfxml1.fxml"));
Files.createFile(otherSubFolder.resolve("subfxml2.fxml"));
Files.createFile(otherTempDir.resolve("fxml1.fxml"));
@@ -57,7 +61,7 @@ class TestFXMLProvider {
otherTempDir.resolve("fxml1.fxml"), otherTempDir,
otherTempDir.resolve("fxml2.fxml"), otherTempDir
);
final var map = FXMLProvider.getFXMLs(project);
final var map = provider.getFXMLs();
assertEquals(expected, map);
}
}

View File

@@ -2,15 +2,18 @@ package com.github.gtache.fxml.compiler.maven.internal;
import com.github.gtache.fxml.compiler.impl.GenericTypesImpl;
import org.apache.maven.plugin.MojoExecutionException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class TestGenericParser {
@@ -22,12 +25,34 @@ class TestGenericParser {
assertEquals(expectedGenericTypes, parsed);
}
@Test
void testNotFound() {
final var parser = new GenericParser("<Collection>", Map.of());
assertThrows(MojoExecutionException.class, parser::parse);
}
@ParameterizedTest
@ValueSource(strings = {"<String, String<String>>", " < String , String < String > > ",
"<String,String<String>>", " <String , String< String> > "})
void testWithSpaces(final String genericTypes) throws MojoExecutionException {
final var parser = new GenericParser(genericTypes, Map.of());
final var expected = List.of(new GenericTypesImpl("String", List.of()), new GenericTypesImpl("String", List.of(new GenericTypesImpl("String", List.of()))));
assertEquals(expected, parser.parse());
}
@ParameterizedTest
@ValueSource(strings = {"", "<String>>", "<<String>", "<String,,String>"})
void testInvalid(final String genericTypes) {
final var parser = new GenericParser(genericTypes, Map.of());
assertThrows(MojoExecutionException.class, parser::parse);
}
private static Stream<Arguments> providesParseTests() {
return Stream.of(
Arguments.of("<Map<String, TableView<Node, Node>>>",
Map.of("Map", "java.util.Map", "String", "java.lang.String", "TableView", "javafx.scene.control.TableView", "Node", "javafx.scene.Node"),
List.of(new GenericTypesImpl("java.util.Map",
List.of(new GenericTypesImpl("java.lang.String", List.of()), new GenericTypesImpl("javafx.scene.control.TableView",
List.of(new GenericTypesImpl("String", List.of()), new GenericTypesImpl("javafx.scene.control.TableView",
List.of(new GenericTypesImpl("javafx.scene.Node", List.of()), new GenericTypesImpl("javafx.scene.Node", List.of()))))))),
Arguments.of("<Map<String, String>>",
Map.of("Map", "java.util.Map"),
@@ -39,10 +64,10 @@ class TestGenericParser {
Arguments.of("<Collection<String>>",
Map.of("Collection", "java.util.Collection", "String", "java.lang.String"),
List.of(new GenericTypesImpl("java.util.Collection",
List.of(new GenericTypesImpl("java.lang.String", List.of()))))),
List.of(new GenericTypesImpl("String", List.of()))))),
Arguments.of("<Collection<String>>",
Map.of(),
List.of(new GenericTypesImpl("Collection",
Map.of("Collection", "java.util.Collection"),
List.of(new GenericTypesImpl("java.util.Collection",
List.of(new GenericTypesImpl("String", List.of())))))
);
}

View File

@@ -1,8 +1,49 @@
package com.github.gtache.fxml.compiler.maven.internal;
import com.github.gtache.fxml.compiler.impl.SourceInfoImpl;
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.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestSourceInfoProvider {
@Test
void testGetSourceInfo(@Mock final CompilationInfo compilationInfo) {
final var outputClass = "outputClass";
final var controllerClass = "controllerClass";
final var inputFile = Path.of("inputFile");
final var includeFile = Path.of("includeFile");
final var includes = Map.of("one", includeFile);
when(compilationInfo.outputClass()).thenReturn(outputClass);
when(compilationInfo.controllerClass()).thenReturn(controllerClass);
when(compilationInfo.inputFile()).thenReturn(inputFile);
when(compilationInfo.includes()).thenReturn(includes);
when(compilationInfo.requiresResourceBundle()).thenReturn(true);
final var includeCompilationInfo = mock(CompilationInfo.class);
final var includeOutputClass = "includeOutputClass";
final var includeControllerClass = "includeControllerClass";
final var includeInputFile = Path.of("includeInputFile");
when(includeCompilationInfo.outputClass()).thenReturn(includeOutputClass);
when(includeCompilationInfo.controllerClass()).thenReturn(includeControllerClass);
when(includeCompilationInfo.inputFile()).thenReturn(includeInputFile);
when(includeCompilationInfo.includes()).thenReturn(Map.of());
final var mapping = Map.of(includeFile, includeCompilationInfo);
final var expectedIncludeSourceInfo = new SourceInfoImpl(includeOutputClass, includeControllerClass, includeInputFile, List.of(), Map.of(), false);
assertEquals(expectedIncludeSourceInfo, SourceInfoProvider.getSourceInfo(includeCompilationInfo, mapping));
final var expected = new SourceInfoImpl(outputClass, controllerClass, inputFile, List.of(expectedIncludeSourceInfo),
Map.of("one", expectedIncludeSourceInfo), true);
assertEquals(expected, SourceInfoProvider.getSourceInfo(compilationInfo, mapping));
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.maven.internal.InfoController">
<bottom>
</bottom>
</BorderPane>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<BorderPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.maven.internal.InfoController">
<bottom>
<HBox>
<children>
<Button fx:id="button" onAction="#onAction" onContextMenuRequested="$controller.onContextMenuRequested"
text="%text"/>
<fx:include fx:id="includeView" source="includeView.fxml"/>
</children>
</HBox>
</bottom>
</BorderPane>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<BorderPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.maven.internal.InfoController">
<bottom>
<HBox>
<children>
<fx:include fx:id="includeView"/>
</children>
</HBox>
</bottom>
</BorderPane>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1">
<bottom>
<fx:include fx:id="includeView" source="includeView.fxml"/>
</bottom>
</BorderPane>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<BorderPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.maven.internal.InfoController">
<bottom>
<HBox>
<children>
<Button fx:id="button" onAction="#onAction" onContextMenuRequested="$controller.onContextMenuRequested"
text="text"/>
<fx:include fx:id="includeView" source="includeView.fxml"/>
</children>
</HBox>
</bottom>
</BorderPane>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<BorderPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.fxml.compiler.maven.internal.InfoController">
<bottom>
<HBox>
<children>
<Button fx:id="button" onAction="onAction"/>
</children>
</HBox>
</bottom>
</BorderPane>