Add support for multi-argument widgets' attribute methods

This commit is contained in:
2022-11-23 16:28:29 +01:00
parent 95c11e5375
commit 3b62d7c06b
5 changed files with 105 additions and 55 deletions

View File

@@ -7,4 +7,5 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Attribute { public @interface Attribute {
String value(); String value();
String separator() default "|";
} }

View File

@@ -24,13 +24,9 @@ public class FPSMonitor extends BaseComponent {
background.setRGBA(0x444444AA); background.setRGBA(0x444444AA);
} }
@Attribute("monitor") @Attribute(value = "monitor", separator = ",")
public void setMonitor(Integer[] options) { public void setMonitor(Integer batchSize, Integer samples) {
if (options.length != 2) { this.fpsProfiler = FPSProfiler.create(batchSize, samples);
throw new IllegalArgumentException("Expected 2 parameters: batch size and number of samples");
}
this.fpsProfiler = FPSProfiler.create(options[0], options[1]);
} }
public void setColor(Integer hex) { public void setColor(Integer hex) {

View File

@@ -28,6 +28,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) @RequiredArgsConstructor(onConstructor = @__(@Autowired))

View File

@@ -0,0 +1,11 @@
package com.bartlomiejpluta.base.engine.error;
public class ParseException extends Exception {
public ParseException(String message) {
super(message);
}
public ParseException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -3,6 +3,7 @@ package com.bartlomiejpluta.base.engine.gui.xml.inflater;
import com.bartlomiejpluta.base.api.context.Context; import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.gui.*; import com.bartlomiejpluta.base.api.gui.*;
import com.bartlomiejpluta.base.engine.error.AppException; import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.error.ParseException;
import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader; import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.codehaus.janino.ExpressionEvaluator; import org.codehaus.janino.ExpressionEvaluator;
@@ -19,6 +20,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern;
@org.springframework.stereotype.Component @org.springframework.stereotype.Component
public class DefaultInflater implements Inflater { public class DefaultInflater implements Inflater {
@@ -140,9 +142,16 @@ public class DefaultInflater implements Inflater {
private void setAttribute(Class<? extends Widget> clazz, Widget widget, Node attribute) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { private void setAttribute(Class<? extends Widget> clazz, Widget widget, Node attribute) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
for (var setter : clazz.getMethods()) { for (var setter : clazz.getMethods()) {
if (setter.isAnnotationPresent(Attribute.class) && setter.getAnnotation(Attribute.class).value().equals(attribute.getLocalName()) && setter.getParameterCount() == 1) { var annotation = setter.getAnnotation(Attribute.class);
var value = parseSimpleValue(setter, attribute.getNodeValue()); if (setter.isAnnotationPresent(Attribute.class) && annotation.value().equals(attribute.getLocalName())) {
if (setter.getParameterCount() == 1) {
var value = parseSimpleValue(setter, annotation.separator(), attribute.getNodeValue());
setter.invoke(widget, value); setter.invoke(widget, value);
} else {
var values = parseExpandedValues(setter, annotation.separator(), attribute.getNodeValue());
setter.invoke(widget, values);
}
return; return;
} }
} }
@@ -260,12 +269,35 @@ public class DefaultInflater implements Inflater {
return "set" + name.substring(0, 1).toUpperCase() + name.substring(1); return "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
} }
private static Object parseSimpleValue(Method method, String value) { private static Object[] parseExpandedValues(Method method, String separator, String value) {
var parameters = method.getParameters();
var types = method.getParameterTypes();
var items = value.split(separator);
if (types.length != items.length) {
throw new AppException("Unable to parse [" + value + "] for [" + method.getName() + "] method: expected [" + types.length + "] arguments, get [" + items.length + "]");
}
var values = (Object[]) Array.newInstance(Object.class, items.length);
for (int i = 0; i < items.length; ++i) {
try {
var parsedValue = parseSimpleValue(types[i], items[i].trim());
Array.set(values, i, parsedValue);
} catch (ParseException ex) {
throw new AppException("Parsing [" + parameters[i].getName() + "] error: " + ex.getMessage(), ex);
}
}
return values;
}
private static Object parseSimpleValue(Method method, String separator, String value) {
try {
var type = method.getParameterTypes()[0]; var type = method.getParameterTypes()[0];
if (type.isArray()) { if (type.isArray()) {
var arrayType = type.getComponentType(); var arrayType = type.getComponentType();
var items = value.split("\\|"); var items = value.split(Pattern.quote(separator));
var values = Array.newInstance(arrayType, items.length); var values = Array.newInstance(arrayType, items.length);
for (int i = 0; i < items.length; ++i) { for (int i = 0; i < items.length; ++i) {
Array.set(values, i, parseSimpleValue(arrayType, items[i].trim())); Array.set(values, i, parseSimpleValue(arrayType, items[i].trim()));
@@ -275,10 +307,14 @@ public class DefaultInflater implements Inflater {
} }
return parseSimpleValue(type, value); return parseSimpleValue(type, value);
} catch (ParseException ex) {
throw new AppException(ex.getMessage(), ex);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings({"unchecked", "rawtypes"})
private static Object parseSimpleValue(Class<?> type, String value) { private static Object parseSimpleValue(Class<?> type, String value) throws ParseException {
try {
if (type == String.class) { if (type == String.class) {
return value; return value;
} }
@@ -310,8 +346,11 @@ public class DefaultInflater implements Inflater {
if (type.isEnum()) { if (type.isEnum()) {
return Enum.valueOf((Class<? extends Enum>) type, value.toUpperCase()); return Enum.valueOf((Class<? extends Enum>) type, value.toUpperCase());
} }
} catch (IllegalArgumentException | ClassCastException ex) {
throw new ParseException("Unable to parse [" + value + "] as [" + type.getSimpleName() + "]", ex);
}
throw new AppException("Attribute of type [" + type.getCanonicalName() + "] is not supported"); throw new ParseException("Attribute of type [" + type.getCanonicalName() + "] is not supported");
} }
@SneakyThrows @SneakyThrows