This commit is contained in:
7
pom.xml
7
pom.xml
@@ -1,7 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
@@ -24,7 +22,8 @@
|
||||
<module>stbf-test</module>
|
||||
<module>stbf-common</module>
|
||||
<module>stbf-rubenlagus</module>
|
||||
</modules>
|
||||
<module>stbf-templates</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
80
stbf-templates/pom.xml
Normal file
80
stbf-templates/pom.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>stbf-parent</artifactId>
|
||||
<groupId>ru.penkrat.stbf</groupId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>stbf-templates</artifactId>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.jar.plugin.version>3.0.2</maven.jar.plugin.version>
|
||||
<jacoco.maven.plugin.version>0.8.0</jacoco.maven.plugin.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<assertj.version>3.9.0</assertj.version>
|
||||
<mockito.version>2.13.0</mockito.version>
|
||||
<jmustache.version>1.14</jmustache.version>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven.jar.plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>stbf-api</artifactId>
|
||||
<groupId>ru.penkrat.stbf</groupId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>stbf-common</artifactId>
|
||||
<groupId>ru.penkrat.stbf</groupId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>2.11.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.samskivert</groupId>
|
||||
<artifactId>jmustache</artifactId>
|
||||
<version>${jmustache.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>stbf-pengrad</artifactId>
|
||||
<groupId>ru.penkrat.stbf</groupId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.penkrat.stbf.templates;
|
||||
|
||||
import ru.penkrat.stbf.api.Keyboard;
|
||||
|
||||
public interface KeyboardProvider {
|
||||
|
||||
Keyboard getKeyboard();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.penkrat.stbf.templates;
|
||||
|
||||
import ru.penkrat.stbf.api.Screen;
|
||||
|
||||
public interface ScreenResolver {
|
||||
|
||||
Screen getScreen(String name);
|
||||
|
||||
Screen getScreen(String name, Object context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.penkrat.stbf.templates;
|
||||
|
||||
public interface TemplateRenderer {
|
||||
|
||||
String render(String template, Object context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.penkrat.stbf.templates.impl;
|
||||
|
||||
import com.samskivert.mustache.Mustache;
|
||||
import ru.penkrat.stbf.templates.TemplateRenderer;
|
||||
|
||||
public class MustacheRenderer implements TemplateRenderer {
|
||||
|
||||
@Override
|
||||
public String render(String template, Object context) {
|
||||
return Mustache.compiler().compile(template).execute(context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package ru.penkrat.stbf.templates.utils;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@UtilityClass
|
||||
public class ReflectionUtils {
|
||||
|
||||
public <T> T getMethodResult(Object context, String methodName, Class<T> clazz) {
|
||||
try {
|
||||
Method method = context.getClass().getMethod(methodName);
|
||||
Object result = method.invoke(context);
|
||||
if (clazz.isAssignableFrom(result.getClass())) {
|
||||
return (T) result;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@JacksonXmlRootElement(localName = "button")
|
||||
class Button {
|
||||
|
||||
@JacksonXmlText
|
||||
private String text;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "if")
|
||||
private String ifCondition;
|
||||
|
||||
public Button(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
class ButtonsRow {
|
||||
|
||||
@JsonProperty("button")
|
||||
@JacksonXmlElementWrapper(useWrapping = false)
|
||||
private List<Button> buttons = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
class KeyboardWrapper {
|
||||
|
||||
@JsonProperty("row")
|
||||
@JacksonXmlElementWrapper(useWrapping = false)
|
||||
private List<ButtonsRow> rows = new ArrayList<>();
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String factoryMethod;
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isNotEmpty() {
|
||||
return !rows.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import ru.penkrat.stbf.templates.TemplateRenderer;
|
||||
|
||||
class NoopTemplateRenderer implements TemplateRenderer {
|
||||
|
||||
@Override
|
||||
public String render(String template, Object context) {
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
class ScreenItem {
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String id;
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true)
|
||||
private String name;
|
||||
|
||||
private String text;
|
||||
|
||||
private KeyboardWrapper keyboard = new KeyboardWrapper();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@JacksonXmlRootElement(localName = "screens")
|
||||
class Screens {
|
||||
|
||||
@Getter
|
||||
@JacksonXmlElementWrapper(useWrapping = false, localName = "screen")
|
||||
@JsonProperty("screen")
|
||||
private List<ScreenItem> screens = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
import ru.penkrat.stbf.api.Action;
|
||||
import ru.penkrat.stbf.api.Keyboard;
|
||||
import ru.penkrat.stbf.api.KeyboardBuilder;
|
||||
import ru.penkrat.stbf.api.Screen;
|
||||
import ru.penkrat.stbf.common.screen.TextScreen;
|
||||
import ru.penkrat.stbf.templates.ScreenResolver;
|
||||
import ru.penkrat.stbf.templates.TemplateRenderer;
|
||||
import ru.penkrat.stbf.templates.utils.ReflectionUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class XmlScreenResolver implements ScreenResolver {
|
||||
|
||||
private final XmlMapper mapper = new XmlMapper();
|
||||
|
||||
private final Map<String, List<ScreenItem>> byId;
|
||||
private final Map<String, List<ScreenItem>> byName;
|
||||
|
||||
@Setter
|
||||
private TemplateRenderer templateRenderer = new NoopTemplateRenderer();
|
||||
|
||||
public XmlScreenResolver(InputStream is) throws IOException {
|
||||
Screens screens = mapper.readValue(is, Screens.class);
|
||||
|
||||
byId = screens.getScreens().stream()
|
||||
.collect(Collectors.groupingBy(ScreenItem::getId));
|
||||
byName = screens.getScreens().stream()
|
||||
.collect(Collectors.groupingBy(ScreenItem::getName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Screen getScreen(String name) {
|
||||
ScreenItem item = get(name);
|
||||
|
||||
return new TextScreen(item.getText(), buildKeyboard(item.getKeyboard()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Screen getScreen(String name, Object context) {
|
||||
ScreenItem item = get(name);
|
||||
|
||||
return new TextScreen(templateRenderer.render(item.getText(), context),
|
||||
resolveKeyboard(item.getKeyboard(), context));
|
||||
}
|
||||
|
||||
private Keyboard resolveKeyboard(KeyboardWrapper wrapper, Object context) {
|
||||
if (wrapper == null)
|
||||
return null;
|
||||
|
||||
if (wrapper.getFactoryMethod() != null && !wrapper.getFactoryMethod().isEmpty() && context != null) {
|
||||
val keyboard = ReflectionUtils.getMethodResult(context, wrapper.getFactoryMethod(), Keyboard.class);
|
||||
if (keyboard == null) {
|
||||
log.warn("Method '{}' returns NULL value!", wrapper.getFactoryMethod());
|
||||
}
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
return buildKeyboard(wrapper);
|
||||
}
|
||||
|
||||
private Keyboard buildKeyboard(KeyboardWrapper wrapper) {
|
||||
Keyboard keyboard = null;
|
||||
if (wrapper.isNotEmpty()) {
|
||||
KeyboardBuilder builder = KeyboardBuilder.newKeyboard();
|
||||
|
||||
for (ButtonsRow row : wrapper.getRows()) {
|
||||
List<Action> buttons = row.getButtons().stream()
|
||||
.map(btn -> Action.builder()
|
||||
.text(btn.getText())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
builder.row(buttons.toArray(new Action[buttons.size()]));
|
||||
}
|
||||
|
||||
keyboard = builder.build();
|
||||
}
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
private ScreenItem get(String key) {
|
||||
List<ScreenItem> list = byId.get(key);
|
||||
if (list != null) {
|
||||
if (list.size() > 1) {
|
||||
throw new IllegalStateException("Non unique 'id' " + key);
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
list = byName.get(key);
|
||||
if (list != null) {
|
||||
if (list.size() > 1) {
|
||||
throw new IllegalStateException("Non unique 'name' " + key);
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
throw new IllegalStateException("Screen not found by 'id' or 'name' " + key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ru.penkrat.stbf.api.Keyboard;
|
||||
import ru.penkrat.stbf.api.KeyboardBuilder;
|
||||
import ru.penkrat.stbf.api.Screen;
|
||||
import ru.penkrat.stbf.templates.impl.MustacheRenderer;
|
||||
|
||||
public class XmlScreenResolverTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws Exception {
|
||||
InputStream xmlUnderTest = XmlScreenResolverTest.class.getResourceAsStream("screens.xml");
|
||||
|
||||
XmlScreenResolver resolver = new XmlScreenResolver(xmlUnderTest);
|
||||
|
||||
Screen screen1 = resolver.getScreen("screen-1");
|
||||
|
||||
assertThat(screen1).isNotNull();
|
||||
assertThat(screen1.getText()).isEqualTo("Test text");
|
||||
|
||||
assertThat(screen1.getKeyboard()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithTemplates() throws Exception {
|
||||
InputStream xmlUnderTest = XmlScreenResolverTest.class.getResourceAsStream("screens.xml");
|
||||
|
||||
XmlScreenResolver resolver = new XmlScreenResolver(xmlUnderTest);
|
||||
resolver.setTemplateRenderer(new MustacheRenderer());
|
||||
|
||||
Keyboard keyboard = KeyboardBuilder.newKeyboard().build();
|
||||
Screen screen2 = resolver.getScreen("screen-2", new Object() {
|
||||
@SuppressWarnings("unused")
|
||||
public String name = "Tester";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Keyboard getKeyboard() {
|
||||
return keyboard;
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(screen2).isNotNull();
|
||||
assertThat(screen2.getText()).isEqualTo("Hello, Tester");
|
||||
|
||||
assertThat(screen2.getKeyboard()).isEqualTo(keyboard);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package ru.penkrat.stbf.templates.xml;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
import ru.penkrat.stbf.templates.xml.Button;
|
||||
import ru.penkrat.stbf.templates.xml.ButtonsRow;
|
||||
import ru.penkrat.stbf.templates.xml.ScreenItem;
|
||||
import ru.penkrat.stbf.templates.xml.Screens;
|
||||
|
||||
public class XmlWriterTest {
|
||||
|
||||
private final XmlMapper mapper = new XmlMapper();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() throws Exception {
|
||||
Screens root = new Screens();
|
||||
|
||||
ScreenItem item1 = new ScreenItem();
|
||||
item1.setId("1");
|
||||
item1.setName("screen-1");
|
||||
item1.setText("Test text");
|
||||
|
||||
Button btn1 = new Button("Btn1");
|
||||
btn1.setIfCondition("false");
|
||||
Button btn2 = new Button("Btn2");
|
||||
ButtonsRow row1 = new ButtonsRow();
|
||||
row1.getButtons().add(btn1);
|
||||
row1.getButtons().add(btn2);
|
||||
ButtonsRow row2 = new ButtonsRow();
|
||||
row2.getButtons().add(btn1);
|
||||
row2.getButtons().add(btn2);
|
||||
item1.getKeyboard().getRows().add(row1);
|
||||
item1.getKeyboard().getRows().add(row2);
|
||||
|
||||
ScreenItem item2 = new ScreenItem();
|
||||
item2.setId("2");
|
||||
item2.setName("screen-2");
|
||||
item2.setText("Hello, {{ name }}");
|
||||
|
||||
item2.getKeyboard().setFactoryMethod("getKeyboard");
|
||||
|
||||
root.getScreens().add(item1);
|
||||
root.getScreens().add(item2);
|
||||
|
||||
String xml = mapper.writeValueAsString(root);
|
||||
|
||||
System.out.println(xml);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<screens>
|
||||
<screen id="1" name="screen-1">
|
||||
<text>Test text</text>
|
||||
<keyboard>
|
||||
<row>
|
||||
<button if="false">Btn1</button>
|
||||
<button>Btn2</button>
|
||||
</row>
|
||||
<row>
|
||||
<button>Btn1</button>
|
||||
<button>Btn2</button>
|
||||
</row>
|
||||
</keyboard>
|
||||
</screen>
|
||||
<screen id="2" name="screen-2">
|
||||
<text>Hello, {{ name }}</text>
|
||||
<keyboard factoryMethod="getKeyboard"/>
|
||||
</screen>
|
||||
</screens>
|
||||
Reference in New Issue
Block a user