Improve widgets' attributes parsing engine
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
package com.bartlomiejpluta.base.api.gui;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Inherited
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Attribute {
|
||||||
|
String value();
|
||||||
|
}
|
||||||
@@ -2,10 +2,12 @@ package com.bartlomiejpluta.base.lib.gui;
|
|||||||
|
|
||||||
import com.bartlomiejpluta.base.api.event.Event;
|
import com.bartlomiejpluta.base.api.event.Event;
|
||||||
import com.bartlomiejpluta.base.api.event.EventType;
|
import com.bartlomiejpluta.base.api.event.EventType;
|
||||||
|
import com.bartlomiejpluta.base.api.gui.Attribute;
|
||||||
import com.bartlomiejpluta.base.api.gui.SizeMode;
|
import com.bartlomiejpluta.base.api.gui.SizeMode;
|
||||||
import com.bartlomiejpluta.base.api.gui.Widget;
|
import com.bartlomiejpluta.base.api.gui.Widget;
|
||||||
import com.bartlomiejpluta.base.lib.event.EventHandler;
|
import com.bartlomiejpluta.base.lib.event.EventHandler;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public abstract class BaseWidget implements Widget {
|
public abstract class BaseWidget implements Widget {
|
||||||
@@ -70,6 +72,36 @@ public abstract class BaseWidget implements Widget {
|
|||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Attribute("height")
|
||||||
|
public void setHeight(String height) {
|
||||||
|
var heightLowerCased = height.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
if (heightLowerCased.equals("auto")) {
|
||||||
|
this.heightMode = SizeMode.AUTO;
|
||||||
|
} else if (heightLowerCased.equals("relative")) {
|
||||||
|
this.heightMode = SizeMode.RELATIVE;
|
||||||
|
this.height = 1f;
|
||||||
|
} else {
|
||||||
|
this.heightMode = SizeMode.ABSOLUTE;
|
||||||
|
this.height = Float.parseFloat(height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Attribute("width")
|
||||||
|
public void setWidth(String width) {
|
||||||
|
var widthLowerCased = width.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
if (widthLowerCased.equals("auto")) {
|
||||||
|
this.widthMode = SizeMode.AUTO;
|
||||||
|
} else if (widthLowerCased.equals("relative")) {
|
||||||
|
this.widthMode = SizeMode.RELATIVE;
|
||||||
|
this.width = 1f;
|
||||||
|
} else {
|
||||||
|
this.widthMode = SizeMode.ABSOLUTE;
|
||||||
|
this.width = Float.parseFloat(width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSize(Float width, Float height) {
|
public void setSize(Float width, Float height) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
@@ -88,6 +120,7 @@ public abstract class BaseWidget implements Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Attribute("widthMode")
|
||||||
public void setWidthMode(SizeMode mode) {
|
public void setWidthMode(SizeMode mode) {
|
||||||
this.widthMode = mode;
|
this.widthMode = mode;
|
||||||
}
|
}
|
||||||
@@ -98,6 +131,7 @@ public abstract class BaseWidget implements Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Attribute("heightMode")
|
||||||
public void setHeightMode(SizeMode mode) {
|
public void setHeightMode(SizeMode mode) {
|
||||||
this.heightMode = mode;
|
this.heightMode = mode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public abstract class BaseWindow extends BaseWidget implements Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Attribute("windowPosition")
|
||||||
public void setWindowPosition(WindowPosition windowPosition) {
|
public void setWindowPosition(WindowPosition windowPosition) {
|
||||||
this.windowPosition = requireNonNull(windowPosition);
|
this.windowPosition = requireNonNull(windowPosition);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.bartlomiejpluta.base.lib.gui;
|
package com.bartlomiejpluta.base.lib.gui;
|
||||||
|
|
||||||
import com.bartlomiejpluta.base.api.context.Context;
|
import com.bartlomiejpluta.base.api.context.Context;
|
||||||
|
import com.bartlomiejpluta.base.api.gui.Attribute;
|
||||||
import com.bartlomiejpluta.base.api.gui.Color;
|
import com.bartlomiejpluta.base.api.gui.Color;
|
||||||
import com.bartlomiejpluta.base.api.gui.GUI;
|
import com.bartlomiejpluta.base.api.gui.GUI;
|
||||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||||
@@ -50,8 +51,15 @@ public class Label extends BaseComponent {
|
|||||||
return alignment;
|
return alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAlignment(Integer alignment) {
|
@Attribute("alignment")
|
||||||
this.alignment = alignment;
|
public void setAlignment(TextAlignment... alignment) {
|
||||||
|
byte b = 0;
|
||||||
|
|
||||||
|
for (var elem : alignment) {
|
||||||
|
b |= elem.getAlign();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.alignment = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getRed() {
|
public float getRed() {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.bartlomiejpluta.base.lib.gui;
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.api.gui.GUI;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public enum TextAlignment {
|
||||||
|
LEFT(GUI.ALIGN_LEFT),
|
||||||
|
CENTER(GUI.ALIGN_CENTER),
|
||||||
|
RIGHT(GUI.ALIGN_RIGHT),
|
||||||
|
TOP(GUI.ALIGN_TOP),
|
||||||
|
MIDDLE(GUI.ALIGN_MIDDLE),
|
||||||
|
BOTTOM(GUI.ALIGN_BOTTOM),
|
||||||
|
BASELINE(GUI.ALIGN_BASELINE);
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final int align;
|
||||||
|
}
|
||||||
@@ -14,16 +14,14 @@ import javax.xml.parsers.DocumentBuilder;
|
|||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@org.springframework.stereotype.Component
|
@org.springframework.stereotype.Component
|
||||||
public class DefaultInflater implements Inflater {
|
public class DefaultInflater implements Inflater {
|
||||||
private static final String[] IMPORTS = Stream.of(GUI.class, SizeMode.class, WindowPosition.class)
|
|
||||||
.map(Class::getCanonicalName)
|
|
||||||
.toArray(String[]::new);
|
|
||||||
|
|
||||||
private final DocumentBuilder builder;
|
private final DocumentBuilder builder;
|
||||||
private final ClassLoader loader;
|
private final ClassLoader loader;
|
||||||
|
|
||||||
@@ -95,7 +93,7 @@ public class DefaultInflater implements Inflater {
|
|||||||
.replaceAll("\\.+", ".")
|
.replaceAll("\\.+", ".")
|
||||||
.replaceAll("-+", "\\$");
|
.replaceAll("-+", "\\$");
|
||||||
|
|
||||||
var windowClass = loader.loadClass(canonicalName);
|
var windowClass = loader.<Window>loadClass(canonicalName);
|
||||||
|
|
||||||
var window = (Window) windowClass.getConstructor(Context.class, GUI.class, Map.class).newInstance(context, gui, refs);
|
var window = (Window) windowClass.getConstructor(Context.class, GUI.class, Map.class).newInstance(context, gui, refs);
|
||||||
var attributes = root.getAttributes();
|
var attributes = root.getAttributes();
|
||||||
@@ -109,10 +107,7 @@ public class DefaultInflater implements Inflater {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var setterName = createSetterMethodName(attribute.getLocalName());
|
setAttribute(windowClass, window, attribute);
|
||||||
var value = parseValue(attribute.getNodeValue());
|
|
||||||
var setter = windowClass.getMethod(setterName, value.getClass());
|
|
||||||
setter.invoke(window, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse window content
|
// Parse window content
|
||||||
@@ -143,6 +138,22 @@ public class DefaultInflater implements Inflater {
|
|||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setAttribute(Class<? extends Widget> clazz, Widget widget, Node attribute) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
for (var setter : clazz.getMethods()) {
|
||||||
|
if (setter.isAnnotationPresent(Attribute.class) && setter.getAnnotation(Attribute.class).value().equals(attribute.getLocalName()) && setter.getParameterCount() == 1) {
|
||||||
|
var value = parseSimpleValue(setter, attribute.getNodeValue());
|
||||||
|
setter.invoke(widget, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var setterName = createSetterMethodName(attribute.getLocalName());
|
||||||
|
var value = parseValue(attribute.getNodeValue());
|
||||||
|
var setter = clazz.getMethod(setterName, value.getClass());
|
||||||
|
setter.invoke(widget, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void invokeInflatableHook(Widget widget) {
|
private void invokeInflatableHook(Widget widget) {
|
||||||
if (widget instanceof Inflatable) {
|
if (widget instanceof Inflatable) {
|
||||||
((Inflatable) widget).onInflate();
|
((Inflatable) widget).onInflate();
|
||||||
@@ -191,7 +202,7 @@ public class DefaultInflater implements Inflater {
|
|||||||
.replaceAll("\\.+", ".")
|
.replaceAll("\\.+", ".")
|
||||||
.replaceAll("-+", "\\$");
|
.replaceAll("-+", "\\$");
|
||||||
|
|
||||||
var componentClass = loader.loadClass(canonicalName);
|
var componentClass = loader.<Component>loadClass(canonicalName);
|
||||||
var component = createComponent(componentClass, node.getAttributes(), refs, context, gui);
|
var component = createComponent(componentClass, node.getAttributes(), refs, context, gui);
|
||||||
|
|
||||||
var children = node.getChildNodes();
|
var children = node.getChildNodes();
|
||||||
@@ -221,7 +232,7 @@ public class DefaultInflater implements Inflater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private Component createComponent(Class<?> componentClass, NamedNodeMap attributes, Map<String, Component> refs, Context context, GUI gui) {
|
private Component createComponent(Class<? extends Widget> componentClass, NamedNodeMap attributes, Map<String, Component> refs, Context context, GUI gui) {
|
||||||
var component = (Component) componentClass.getConstructor(Context.class, GUI.class).newInstance(context, gui);
|
var component = (Component) componentClass.getConstructor(Context.class, GUI.class).newInstance(context, gui);
|
||||||
|
|
||||||
// Set attributes via setter methods
|
// Set attributes via setter methods
|
||||||
@@ -239,10 +250,7 @@ public class DefaultInflater implements Inflater {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var setterName = createSetterMethodName(attribute.getLocalName());
|
setAttribute(componentClass, component, attribute);
|
||||||
var value = parseValue(attribute.getNodeValue());
|
|
||||||
var setter = componentClass.getMethod(setterName, value.getClass());
|
|
||||||
setter.invoke(component, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return component;
|
return component;
|
||||||
@@ -252,10 +260,63 @@ 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) {
|
||||||
|
var type = method.getParameterTypes()[0];
|
||||||
|
|
||||||
|
if (type.isArray()) {
|
||||||
|
var arrayType = type.getComponentType();
|
||||||
|
var items = value.split("\\|");
|
||||||
|
var values = Array.newInstance(arrayType, items.length);
|
||||||
|
for (int i = 0; i < items.length; ++i) {
|
||||||
|
Array.set(values, i, parseSimpleValue(arrayType, items[i].trim()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseSimpleValue(type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Object parseSimpleValue(Class<?> type, String value) {
|
||||||
|
if (type == String.class) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Short.TYPE || type == Short.class) {
|
||||||
|
return Short.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Integer.TYPE || type == Integer.class) {
|
||||||
|
return Integer.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Long.TYPE || type == Long.class) {
|
||||||
|
return Long.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Boolean.TYPE || type == Boolean.class) {
|
||||||
|
return Boolean.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Float.TYPE || type == Float.class) {
|
||||||
|
return Float.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Double.TYPE || type == Double.class) {
|
||||||
|
return Double.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.isEnum()) {
|
||||||
|
return Enum.valueOf((Class<? extends Enum>) type, value.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AppException("Attribute of type [" + type.getCanonicalName() + "] is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private Object parseValue(String value) {
|
private static Object parseValue(String value) {
|
||||||
var evaluator = new ExpressionEvaluator();
|
var evaluator = new ExpressionEvaluator();
|
||||||
evaluator.setDefaultImports(IMPORTS);
|
|
||||||
evaluator.cook(value);
|
evaluator.cook(value);
|
||||||
return evaluator.evaluate();
|
return evaluator.evaluate();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user