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

@@ -1,20 +0,0 @@
package com.github.gtache.fxml.compiler;
import java.util.Map;
/**
* Factory for creating controllers
*
* @param <T> The type of the controller
*/
@FunctionalInterface
public interface ControllerFactory<T> {
/**
* Creates a controller
*
* @param fieldMap The assignment of field name to value
* @return The created controller
*/
T create(final Map<String, Object> fieldMap);
}

View File

@@ -28,5 +28,5 @@ public interface ControllerFieldInfo {
*
* @return The generic types as a list, empty if not generic or raw
*/
List<String> genericTypes();
List<GenericTypes> genericTypes();
}

View File

@@ -7,10 +7,17 @@ import java.util.Map;
*/
public interface ControllerInfo {
/**
* Returns the controller class name
*
* @return The name
*/
String className();
/**
* Returns a mapping of event handler method name -> boolean
*
* @return A mapping of method name to true if the method has an argument
* @return A mapping of method name to true if the method has an argument (event handler may not have an argument)
*/
Map<String, Boolean> handlerHasArgument();
@@ -18,7 +25,7 @@ public interface ControllerInfo {
* Returns whether the given event handler method has an argument
*
* @param methodName The method name
* @return A mapping of method name to true if the method has an event
* @return True if the method has an argument
*/
default boolean handlerHasArgument(final String methodName) {
return handlerHasArgument().getOrDefault(methodName, true);
@@ -40,4 +47,11 @@ public interface ControllerInfo {
default ControllerFieldInfo fieldInfo(final String property) {
return fieldInfo().get(property);
}
/**
* Returns whether the controller has an initialize method
*
* @return True if the controller has an initialize method
*/
boolean hasInitialize();
}

View File

@@ -1,28 +0,0 @@
package com.github.gtache.fxml.compiler;
/**
* Represents a controller injection to use for generated code
*/
public interface ControllerInjection {
/**
* Returns the injection type of class fields
*
* @return The injection type for fields
*/
InjectionType fieldInjectionType();
/**
* Returns the injection type for event handlers methods
*
* @return The injection type for event handlers
*/
InjectionType methodInjectionType();
/**
* The name of the controller class
*
* @return The class
*/
String injectionClass();
}

View File

@@ -6,7 +6,7 @@ package com.github.gtache.fxml.compiler;
public class GenerationException extends Exception {
/**
* Instantiates a new GenerationException
* Instantiates a new exception
*
* @param message The message
*/
@@ -15,7 +15,7 @@ public class GenerationException extends Exception {
}
/**
* Instantiates a new GenerationException
* Instantiates a new exception
*
* @param message The message
* @param cause The cause
@@ -25,7 +25,7 @@ public class GenerationException extends Exception {
}
/**
* Instantiates a new GenerationException
* Instantiates a new exception
*
* @param cause The cause
*/

View File

@@ -1,5 +1,7 @@
package com.github.gtache.fxml.compiler;
import com.github.gtache.fxml.compiler.compatibility.GenerationCompatibility;
import java.util.Map;
/**
@@ -8,31 +10,52 @@ import java.util.Map;
public interface GenerationParameters {
/**
* Returns the mapping of controller class name to controller injection
* Returns the compatibility information
*
* @return The mapping
* @return The compatibility
*/
Map<String, ControllerInjection> controllerInjections();
GenerationCompatibility compatibility();
/**
* Returns the mapping of fx:include source to generated class name
* Returns whether to use Image InputStream constructor instead of the String (url) one.
* This allows avoiding opening some packages with JPMS
*
* @return The mapping
* @return True if the constructor should be used
*/
Map<String, String> sourceToGeneratedClassName();
boolean useImageInputStreamConstructor();
/**
* Returns the mapping of fx:include source to controller class name
* Returns the mapping of controller class to resource bundle path (in case of GET-BUNDLE injection)
*
* @return The mapping
* @return The map
*/
Map<String, String> sourceToControllerName();
Map<String, String> bundleMap();
/**
* Returns the resource bundle injection to use
* Returns the controller injection to use
*
* @return The injection
*/
ResourceBundleInjection resourceBundleInjection();
InjectionType controllerInjectionType();
/**
* Returns the field injection to use
*
* @return The injection
*/
InjectionType fieldInjectionType();
/**
* Returns the method injection to use
*
* @return The injection
*/
InjectionType methodInjectionType();
/**
* Returns the resource injection to use
*
* @return The injection
*/
InjectionType resourceInjectionType();
}

View File

@@ -21,6 +21,13 @@ public interface GenerationRequest {
*/
GenerationParameters parameters();
/**
* Returns the info about the main source file
*
* @return The info
*/
SourceInfo sourceInfo();
/**
* Returns the object to generate code for
*

View File

@@ -0,0 +1,23 @@
package com.github.gtache.fxml.compiler;
import java.util.List;
/**
* Represents generic types for a field
*/
public interface GenericTypes {
/**
* Returns the name of the type
*
* @return The name
*/
String name();
/**
* Returns the possible subtypes of the type
*
* @return The list of subtypes, empty if no subtypes
*/
List<GenericTypes> subTypes();
}

View File

@@ -1,21 +0,0 @@
package com.github.gtache.fxml.compiler;
/**
* Represents a controller injection to use for generated code
*/
public interface ResourceBundleInjection {
/**
* Returns the injection type for the resource bundle
*
* @return The injection type
*/
InjectionType injectionType();
/**
* Returns the resource bundle name
*
* @return The path
*/
String bundleName();
}

View File

@@ -0,0 +1,54 @@
package com.github.gtache.fxml.compiler;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
/**
* Info about a source file
*/
public interface SourceInfo {
/**
* Returns the generated view class name
*
* @return The class name
*/
String generatedClassName();
/**
* Returns the controller class name
*
* @return The class name
*/
String controllerClassName();
/**
* Returns the source file
*
* @return The file
*/
Path sourceFile();
/**
* Returns the included sources.
* Note that there can be multiple times the same source.
*
* @return The sources
*/
List<SourceInfo> includedSources();
/**
* Returns the mapping of source value to source info
*
* @return The mapping
*/
Map<String, SourceInfo> sourceToSourceInfo();
/**
* Returns whether the source or its children requires a resource bundle
*
* @return True if the subtree requires a resource bundle
*/
boolean requiresResourceBundle();
}

View File

@@ -0,0 +1,57 @@
package com.github.gtache.fxml.compiler.compatibility;
/**
* Compatibility information for generated code
*/
@FunctionalInterface
public interface GenerationCompatibility {
/**
* Returns the minimum supported Java version
*
* @return The version
*/
int javaVersion();
/**
* Returns whether to use var for object declaration
*
* @return True if var should be used
*/
default boolean useVar() {
return javaVersion() >= 10;
}
/**
* Returns the type of list collector to use
*
* @return The collector
*/
default ListCollector listCollector() {
if (javaVersion() >= 16) {
return ListCollector.TO_LIST;
} else if (javaVersion() >= 10) {
return ListCollector.COLLECT_TO_UNMODIFIABLE_LIST;
} else {
return ListCollector.COLLECT_TO_LIST;
}
}
/**
* Returns whether to use List.of() (or Set.of() etc.) instead of Arrays.asList()
*
* @return True if List.of() should be used
*/
default boolean useCollectionsOf() {
return javaVersion() >= 9;
}
/**
* Returns whether to use getFirst() or get(0)
*
* @return True if getFirst() should be used
*/
default boolean useGetFirst() {
return javaVersion() >= 21;
}
}

View File

@@ -0,0 +1,22 @@
package com.github.gtache.fxml.compiler.compatibility;
/**
* Type of list collector to use for generated code
*/
public enum ListCollector {
/**
* Use .toList()
*/
TO_LIST,
/**
* Use .collect(Collectors.toUnmodifiableList())
*/
COLLECT_TO_UNMODIFIABLE_LIST,
/**
* Use .collect(Collectors.toList())
*/
COLLECT_TO_LIST
}

View File

@@ -6,7 +6,7 @@ package com.github.gtache.fxml.compiler.parsing;
public class ParseException extends Exception {
/**
* Instantiates a new ParseException
* Instantiates a new exception
*
* @param message The message
*/
@@ -15,7 +15,7 @@ public class ParseException extends Exception {
}
/**
* Instantiates a new ParseException
* Instantiates a new exception
*
* @param message The message
* @param cause The cause
@@ -25,7 +25,7 @@ public class ParseException extends Exception {
}
/**
* Instantiates a new ParseException
* Instantiates a new exception
*
* @param cause The cause
*/

View File

@@ -1,5 +1,6 @@
package com.github.gtache.fxml.compiler.parsing;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SequencedCollection;
import java.util.SequencedMap;
@@ -10,30 +11,18 @@ import java.util.SequencedMap;
@FunctionalInterface
public interface ParsedDefine extends ParsedObject {
/**
* Returns the object defined by this fx:define
*
* @return The object
*/
ParsedObject object();
@Override
default String className() {
return object().className();
return ParsedDefine.class.getName();
}
@Override
default Map<String, ParsedProperty> attributes() {
return object().attributes();
return Map.of();
}
@Override
default SequencedMap<ParsedProperty, SequencedCollection<ParsedObject>> properties() {
return object().properties();
}
@Override
default SequencedCollection<ParsedObject> children() {
return object().children();
return new LinkedHashMap<>();
}
}

View File

@@ -29,7 +29,8 @@ public interface ParsedFactory extends ParsedObject {
}
/**
* Returns the arguments for the factory
* Returns the arguments for the factory.
* Different from {@link ParsedObject#children()} (in practice, children should only contain fx:define)
*
* @return The arguments
*/

View File

@@ -10,7 +10,7 @@ import java.util.SequencedMap;
public interface ParsedObject {
/**
* The type of the object
* Returns the type of the object
*
* @return The class name
*/

View File

@@ -21,7 +21,7 @@ public interface ParsedText extends ParsedObject {
@Override
default String className() {
return "java.lang.String";
return String.class.getName();
}
@Override

View File

@@ -3,5 +3,6 @@
*/
module com.github.gtache.fxml.compiler.api {
exports com.github.gtache.fxml.compiler;
exports com.github.gtache.fxml.compiler.compatibility;
exports com.github.gtache.fxml.compiler.parsing;
}

View File

@@ -6,8 +6,7 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;
class TestControllerFieldInfo {
@@ -25,7 +24,7 @@ class TestControllerFieldInfo {
@Test
void testIsGenericTrue() {
when(info.genericTypes()).thenReturn(List.of("A", "B", "C"));
when(info.genericTypes()).thenReturn(List.of(mock(GenericTypes.class)));
assertTrue(info.isGeneric());
}
}

View File

@@ -0,0 +1,72 @@
package com.github.gtache.fxml.compiler.compatibility;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
class TestGenerationCompatibility {
private final GenerationCompatibility compatibility;
TestGenerationCompatibility() {
this.compatibility = spy(GenerationCompatibility.class);
}
@Test
void testUseVar() {
when(compatibility.javaVersion()).thenReturn(10);
assertTrue(compatibility.useVar());
}
@Test
void testDontUseVar() {
when(compatibility.javaVersion()).thenReturn(9);
assertFalse(compatibility.useVar());
}
@Test
void testToListCollector() {
when(compatibility.javaVersion()).thenReturn(16);
assertEquals(ListCollector.TO_LIST, compatibility.listCollector());
}
@Test
void testCollectToUnmodifiableListCollector() {
when(compatibility.javaVersion()).thenReturn(15);
assertEquals(ListCollector.COLLECT_TO_UNMODIFIABLE_LIST, compatibility.listCollector());
when(compatibility.javaVersion()).thenReturn(10);
assertEquals(ListCollector.COLLECT_TO_UNMODIFIABLE_LIST, compatibility.listCollector());
}
@Test
void testCollectToListCollector() {
when(compatibility.javaVersion()).thenReturn(9);
assertEquals(ListCollector.COLLECT_TO_LIST, compatibility.listCollector());
}
@Test
void testUseCollectionsOf() {
when(compatibility.javaVersion()).thenReturn(9);
assertTrue(compatibility.useCollectionsOf());
}
@Test
void testDontUseCollectionsOf() {
when(compatibility.javaVersion()).thenReturn(8);
assertFalse(compatibility.useCollectionsOf());
}
@Test
void testUseGetFirst() {
when(compatibility.javaVersion()).thenReturn(21);
assertTrue(compatibility.useGetFirst());
}
@Test
void testDontUseGetFirst() {
when(compatibility.javaVersion()).thenReturn(20);
assertFalse(compatibility.useGetFirst());
}
}

View File

@@ -1,80 +1,34 @@
package com.github.gtache.fxml.compiler.parsing;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SequencedCollection;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.spy;
@ExtendWith(MockitoExtension.class)
class TestParsedDefine {
private final ParsedProperty property;
private final ParsedObject object;
private final String string;
private final ParsedDefine define;
TestParsedDefine(@Mock final ParsedProperty property, @Mock final ParsedObject object) {
this.property = requireNonNull(property);
this.object = requireNonNull(object);
this.string = "str/ing";
TestParsedDefine() {
this.define = spy(ParsedDefine.class);
}
@BeforeEach
void beforeEach() {
when(property.value()).thenReturn(string);
when(define.object()).thenReturn(object);
when(object.className()).thenReturn(string);
when(object.children()).thenReturn(List.of(define));
final var map = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
map.put(property, List.of(object));
when(object.properties()).thenReturn(map);
when(object.attributes()).thenReturn(Map.of(string, property));
}
@Test
void testObject() {
assertEquals(object, define.object());
}
@Test
void testClassName() {
assertEquals(string, define.className());
verify(define).object();
verify(object).className();
assertEquals(ParsedDefine.class.getName(), define.className());
}
@Test
void testAttributes() {
assertEquals(Map.of(string, property), define.attributes());
verify(define).object();
verify(object).attributes();
assertEquals(Map.of(), define.attributes());
}
@Test
void testProperties() {
final var map = new LinkedHashMap<ParsedProperty, SequencedCollection<ParsedObject>>();
map.put(property, List.of(object));
assertEquals(map, define.properties());
verify(define).object();
verify(object).properties();
}
@Test
void testChildren() {
assertEquals(List.of(define), define.children());
verify(define).object();
verify(object).children();
assertEquals(new LinkedHashMap<>(), define.properties());
}
}