Tries to avoid injecting fields that are not defined in controller

This commit is contained in:
Guillaume Tâche
2024-11-30 19:04:01 +01:00
parent 17d112fb41
commit 7ec52cd175
62 changed files with 280 additions and 1537 deletions

View File

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

View File

@@ -1,21 +1,21 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
import java.util.List;
import java.util.Map;
/**
* Implementation of {@link ControllerInfo}
*
* @param handlerHasArgument The mapping of method name to true if the method has an argument
* @param propertyGenericTypes The mapping of property name to generic types
* @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
*/
public record ControllerInfoImpl(Map<String, Boolean> handlerHasArgument,
Map<String, List<String>> propertyGenericTypes) implements ControllerInfo {
Map<String, ControllerFieldInfo> fieldInfo) implements ControllerInfo {
public ControllerInfoImpl {
handlerHasArgument = Map.copyOf(handlerHasArgument);
propertyGenericTypes = Map.copyOf(propertyGenericTypes);
fieldInfo = Map.copyOf(fieldInfo);
}
}

View File

@@ -5,6 +5,8 @@ import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import com.github.gtache.fxml.compiler.parsing.ParsedProperty;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Map;
@@ -16,6 +18,7 @@ import static com.github.gtache.fxml.compiler.impl.internal.ControllerInjector.i
*/
public 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";
@@ -107,8 +110,11 @@ public final class GenerationHelper {
if (id != null) {
progress.idToVariableName().put(id.value(), variableName);
progress.idToObject().put(id.value(), parsedObject);
//TODO Don't inject if variable doesn't exist
injectControllerField(progress, id.value(), variableName);
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);
}
}
}

View File

@@ -4,6 +4,8 @@ import com.github.gtache.fxml.compiler.GenerationException;
import com.github.gtache.fxml.compiler.impl.GeneratorImpl;
import com.github.gtache.fxml.compiler.parsing.*;
import com.github.gtache.fxml.compiler.parsing.impl.ParsedPropertyImpl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
@@ -31,6 +33,8 @@ 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(
@@ -304,8 +308,11 @@ public final class ObjectFormatter {
progress.stringBuilder().append(START_VAR).append(subControllerVariable).append(" = ").append(subViewVariable).append(".controller();\n");
progress.idToVariableName().put(id, subControllerVariable);
progress.idToObject().put(id, include);
//TODO Don't inject if variable doesn't exist
injectControllerField(progress, id, subControllerVariable);
if (progress.request().controllerInfo().fieldInfo(id) == null) {
logger.debug("Not injecting {} because it is not found in controller", id);
} else {
injectControllerField(progress, id, subControllerVariable);
}
}
}

View File

@@ -5,6 +5,8 @@ import com.github.gtache.fxml.compiler.parsing.ParsedObject;
import javafx.beans.DefaultProperty;
import javafx.beans.NamedArg;
import javafx.scene.Node;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -21,6 +23,7 @@ import static com.github.gtache.fxml.compiler.impl.internal.GenerationHelper.FX_
* Helper methods for reflection
*/
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<>();
@@ -300,14 +303,16 @@ final class ReflectionHelper {
if (isGeneric(clazz)) {
final var idProperty = parsedObject.attributes().get(FX_ID);
if (idProperty == null) {
return "<>";
logger.warn("No id found for generic class {} ; Using raw", clazz.getName());
return "";
} else {
final var id = idProperty.value();
final var genericTypes = progress.request().controllerInfo().propertyGenericTypes(id);
if (genericTypes == null) { //Raw
final var fieldInfo = progress.request().controllerInfo().fieldInfo(id);
if (fieldInfo == null) { //Not found
logger.warn("No field found for generic class {} (id={}) ; Using raw", clazz.getName(), id);
return "";
} else {
return "<" + String.join(", ", genericTypes) + ">";
} else if (fieldInfo.isGeneric()) {
return "<" + String.join(", ", fieldInfo.genericTypes()) + ">";
}
}
}

View File

@@ -0,0 +1,48 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
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;
class TestControllerFieldInfoImpl {
private final String name;
private final List<String> genericTypes;
private final ControllerFieldInfo info;
TestControllerFieldInfoImpl() {
this.name = "name";
this.genericTypes = new ArrayList<>(List.of("A", "B", "C"));
this.info = new ControllerFieldInfoImpl(name, genericTypes);
}
@Test
void testGetters() {
assertEquals(name, info.name());
assertEquals(genericTypes, info.genericTypes());
}
@Test
void testCopyList() {
final var originalGenericTypes = info.genericTypes();
genericTypes.clear();
assertEquals(originalGenericTypes, info.genericTypes());
}
@Test
void testUnmodifiable() {
final var infoList = info.genericTypes();
assertThrows(UnsupportedOperationException.class, infoList::clear);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ControllerFieldInfoImpl(null, genericTypes));
assertThrows(NullPointerException.class, () -> new ControllerFieldInfoImpl(name, null));
}
}

View File

@@ -1,24 +1,31 @@
package com.github.gtache.fxml.compiler.impl;
import com.github.gtache.fxml.compiler.ControllerFieldInfo;
import com.github.gtache.fxml.compiler.ControllerInfo;
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.*;
@ExtendWith(MockitoExtension.class)
class TestControllerInfoImpl {
private final Map<String, Boolean> handlerHasArgument;
private final Map<String, List<String>> propertyGenericTypes;
private final ControllerFieldInfo fieldInfo;
private final Map<String, ControllerFieldInfo> fieldInfoMap;
private final ControllerInfo info;
TestControllerInfoImpl() {
TestControllerInfoImpl(@Mock final ControllerFieldInfo fieldInfo) {
this.handlerHasArgument = new HashMap<>(Map.of("one", true, "two", false));
this.propertyGenericTypes = new HashMap<>(Map.of("one", List.of("a", "b"), "two", List.of()));
this.info = new ControllerInfoImpl(handlerHasArgument, propertyGenericTypes);
this.fieldInfo = Objects.requireNonNull(fieldInfo);
this.fieldInfoMap = new HashMap<>(Map.of("one", fieldInfo));
this.info = new ControllerInfoImpl(handlerHasArgument, fieldInfoMap);
}
@Test
@@ -31,35 +38,34 @@ class TestControllerInfoImpl {
@Test
void testPropertyGenericTypes() {
assertEquals(propertyGenericTypes, info.propertyGenericTypes());
assertEquals(List.of("a", "b"), info.propertyGenericTypes("one"));
assertEquals(List.of(), info.propertyGenericTypes("two"));
assertEquals(fieldInfoMap, info.fieldInfo());
assertEquals(fieldInfo, info.fieldInfo("one"));
}
@Test
void testMapsCopied() {
final var originalHandler = Map.copyOf(handlerHasArgument);
final var originalPropertyTypes = Map.copyOf(propertyGenericTypes);
final var originalFieldInfo = Map.copyOf(fieldInfoMap);
assertEquals(originalHandler, info.handlerHasArgument());
assertEquals(originalPropertyTypes, info.propertyGenericTypes());
assertEquals(originalFieldInfo, info.fieldInfo());
handlerHasArgument.clear();
propertyGenericTypes.clear();
fieldInfoMap.clear();
assertEquals(originalHandler, info.handlerHasArgument());
assertEquals(originalPropertyTypes, info.propertyGenericTypes());
assertEquals(originalFieldInfo, info.fieldInfo());
}
@Test
void testUnmodifiable() {
final var infoHandler = info.handlerHasArgument();
final var infoProperty = info.propertyGenericTypes();
final var infoProperty = info.fieldInfo();
assertThrows(UnsupportedOperationException.class, infoHandler::clear);
assertThrows(UnsupportedOperationException.class, infoProperty::clear);
}
@Test
void testIllegal() {
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(null, propertyGenericTypes));
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(null, fieldInfoMap));
assertThrows(NullPointerException.class, () -> new ControllerInfoImpl(handlerHasArgument, null));
}
}