Create working scaffolding of Component Inflater

This commit is contained in:
2021-03-15 14:37:30 +01:00
parent 449278b3cf
commit c92c4f31a7
10 changed files with 249 additions and 83 deletions

View File

@@ -86,6 +86,9 @@ dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation "org.codehaus.janino:janino:${janinoVersion}"
implementation "org.codehaus.janino:commons-compiler:${janinoVersion}"
// This dependency is used by the application.
implementation "org.joml:joml:${jomlVersion}"
}

View File

@@ -58,32 +58,32 @@ public class ScreenWidget implements Widget {
}
@Override
public void setX(float x) {
public void setX(Float x) {
throw new UnsupportedOperationException();
}
@Override
public void setY(float y) {
public void setY(Float y) {
throw new UnsupportedOperationException();
}
@Override
public void setPosition(float x, float y) {
public void setPosition(Float x, Float y) {
throw new UnsupportedOperationException();
}
@Override
public void setWidth(float width) {
public void setWidth(Float width) {
throw new UnsupportedOperationException();
}
@Override
public void setHeight(float height) {
public void setHeight(Float height) {
throw new UnsupportedOperationException();
}
@Override
public void setSize(float width, float height) {
public void setSize(Float width, Float height) {
throw new UnsupportedOperationException();
}
@@ -113,42 +113,42 @@ public class ScreenWidget implements Widget {
}
@Override
public void setMargin(float top, float right, float bottom, float left) {
public void setMargin(Float top, Float right, Float bottom, Float left) {
throw new UnsupportedOperationException();
}
@Override
public void setMargin(float top, float rightLeft, float bottom) {
public void setMargin(Float top, Float rightLeft, Float bottom) {
throw new UnsupportedOperationException();
}
@Override
public void setMargin(float topBottom, float rightLeft) {
public void setMargin(Float topBottom, Float rightLeft) {
throw new UnsupportedOperationException();
}
@Override
public void setMargin(float all) {
public void setMargin(Float all) {
throw new UnsupportedOperationException();
}
@Override
public void setMarginTop(float margin) {
public void setMarginTop(Float margin) {
throw new UnsupportedOperationException();
}
@Override
public void setMarginRight(float margin) {
public void setMarginRight(Float margin) {
throw new UnsupportedOperationException();
}
@Override
public void setMarginBottom(float margin) {
public void setMarginBottom(Float margin) {
throw new UnsupportedOperationException();
}
@Override
public void setMarginLeft(float margin) {
public void setMarginLeft(Float margin) {
throw new UnsupportedOperationException();
}
@@ -173,42 +173,42 @@ public class ScreenWidget implements Widget {
}
@Override
public void setPadding(float top, float right, float bottom, float left) {
public void setPadding(Float top, Float right, Float bottom, Float left) {
throw new UnsupportedOperationException();
}
@Override
public void setPadding(float top, float rightLeft, float bottom) {
public void setPadding(Float top, Float rightLeft, Float bottom) {
throw new UnsupportedOperationException();
}
@Override
public void setPadding(float topBottom, float rightLeft) {
public void setPadding(Float topBottom, Float rightLeft) {
throw new UnsupportedOperationException();
}
@Override
public void setPadding(float all) {
public void setPadding(Float all) {
throw new UnsupportedOperationException();
}
@Override
public void setPaddingTop(float padding) {
public void setPaddingTop(Float padding) {
throw new UnsupportedOperationException();
}
@Override
public void setPaddingRight(float padding) {
public void setPaddingRight(Float padding) {
throw new UnsupportedOperationException();
}
@Override
public void setPaddingBottom(float padding) {
public void setPaddingBottom(Float padding) {
throw new UnsupportedOperationException();
}
@Override
public void setPaddingLeft(float padding) {
public void setPaddingLeft(Float padding) {
throw new UnsupportedOperationException();
}

View File

@@ -0,0 +1,126 @@
package com.bartlomiejpluta.base.engine.gui.xml.inflater;
import com.bartlomiejpluta.base.api.game.gui.base.GUI;
import com.bartlomiejpluta.base.api.game.gui.base.SizeMode;
import com.bartlomiejpluta.base.api.game.gui.component.Component;
import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader;
import lombok.SneakyThrows;
import org.codehaus.janino.ExpressionEvaluator;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.InputStream;
import java.util.stream.Stream;
@org.springframework.stereotype.Component
public class ComponentInflater {
private static final String[] IMPORTS = Stream.of(GUI.class, SizeMode.class).map(Class::getCanonicalName).toArray(String[]::new);
private final DocumentBuilder builder;
private final ClassLoader loader;
@Autowired
@SneakyThrows
public ComponentInflater(ClassLoader loader) {
this.loader = loader;
var factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
this.builder = factory.newDocumentBuilder();
}
@SneakyThrows
public Component inflate(String xml) {
var document = builder.parse(xml);
return parseNode(document.getDocumentElement());
}
@SneakyThrows
public Component inflate(File file) {
var document = builder.parse(file);
return parseNode(document.getDocumentElement());
}
@SneakyThrows
public Component inflate(InputStream is) {
var document = builder.parse(is);
return parseNode(document.getDocumentElement());
}
@SneakyThrows
private Component parseNode(Node node) {
var uri = node.getNamespaceURI();
var name = node.getLocalName();
if (uri != null) {
name = uri + "." + name;
}
var canonicalName = name.replaceAll("\\*", "").replaceAll("\\.+", ".");
var componentClass = loader.loadClass(canonicalName);
var component = createComponent(componentClass, node.getAttributes());
var children = node.getChildNodes();
// The component is text-like node (e.g. <Label>Some text</Label>)
if (children.getLength() == 1) {
var child = children.item(0);
if (child.getNodeType() == Node.TEXT_NODE) {
var textSetter = componentClass.getMethod("setText", String.class);
textSetter.invoke(component, child.getNodeValue());
return component;
}
}
// The component has non-text children (e.g. <HLayout><Label>Some text</Label></HLayout>)
for (int i = 0; i < children.getLength(); ++i) {
var childNode = children.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
component.add(parseNode(childNode));
}
return component;
}
@SneakyThrows
private Component createComponent(Class<?> componentClass, NamedNodeMap attributes) {
var component = (Component) componentClass.getConstructor().newInstance();
// Set attributes via setter methods
for (int i = 0; i < attributes.getLength(); ++i) {
var attribute = attributes.item(i);
// Ignore namespaces definitions
if ("xmlns".equals(attribute.getPrefix())) {
continue;
}
var setterName = createSetterMethodName(attribute.getLocalName());
var value = parseValue(attribute.getNodeValue());
var setter = componentClass.getMethod(setterName, value.getClass());
setter.invoke(component, value);
}
return component;
}
private static String createSetterMethodName(String name) {
return "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
}
@SneakyThrows
private Object parseValue(String value) {
var evaluator = new ExpressionEvaluator();
evaluator.setDefaultImports(IMPORTS);
evaluator.cook(value);
return evaluator.evaluate();
}
}