add flow: actions + screens
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
package ru.penkrat.stbf.templates;
|
||||||
|
|
||||||
|
import ru.penkrat.stbf.api.Action;
|
||||||
|
|
||||||
|
public interface ActionResolver {
|
||||||
|
|
||||||
|
Action getAction(String name);
|
||||||
|
}
|
||||||
@@ -4,36 +4,48 @@ import lombok.experimental.UtilityClass;
|
|||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class ReflectionUtils {
|
public class ReflectionUtils {
|
||||||
|
|
||||||
public <T> T getMethodResult(Object context, String methodName, Class<T> clazz) {
|
public <T> T getMethodResult(Object context, String methodName, Class<T> clazz) {
|
||||||
|
return getMethod(context.getClass(), methodName)
|
||||||
|
.map(invoker(context, clazz))
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Method> getMethod(Class<?> clazz, String methodName) {
|
||||||
try {
|
try {
|
||||||
Method method = context.getClass().getMethod(methodName);
|
Method method = clazz.getMethod(methodName);
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
Object result = method.invoke(context);
|
return Optional.of(method);
|
||||||
if (result != null && clazz.isAssignableFrom(result.getClass())) {
|
} catch (NoSuchMethodException e) {
|
||||||
|
// try find as private
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Method method = clazz.getDeclaredMethod(methodName);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return Optional.of(method);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Function<Method, T> invoker(Object target, Class<T> resultClass) {
|
||||||
|
return (method) -> {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
result = method.invoke(target);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (result != null && resultClass.isAssignableFrom(result.getClass())) {
|
||||||
return (T) result;
|
return (T) result;
|
||||||
}
|
}
|
||||||
} catch (NoSuchMethodException e) {
|
return null;
|
||||||
// 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,15 @@
|
|||||||
|
package ru.penkrat.stbf.templates.utils;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class StringUtils {
|
||||||
|
|
||||||
|
public boolean isEmpty(String string) {
|
||||||
|
return string == null || string.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNotEmpty(String string) {
|
||||||
|
return !isEmpty(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
class ActionItem extends NamedItem {
|
||||||
|
|
||||||
|
@JacksonXmlText
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String command;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private boolean requestContact;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private boolean requestLocation;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String callbackData;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package ru.penkrat.stbf.templates.xml;
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -13,28 +11,31 @@ import lombok.Setter;
|
|||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@JacksonXmlRootElement(localName = "button")
|
@JacksonXmlRootElement(localName = "button")
|
||||||
class Button {
|
class Button extends NamedItem {
|
||||||
|
|
||||||
@JacksonXmlText
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
private String text;
|
private String actionRef;
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "if")
|
@JacksonXmlText
|
||||||
private String ifCondition;
|
private String text;
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
@JacksonXmlProperty(isAttribute = true, localName = "if")
|
||||||
private boolean requestContact;
|
private String ifCondition;
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
private boolean requestLocation;
|
private boolean requestContact;
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
private String callbackData;
|
private boolean requestLocation;
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
private String url;
|
private String callbackData;
|
||||||
|
|
||||||
public Button(String text) {
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
this.text = text;
|
private String url;
|
||||||
}
|
|
||||||
|
public Button(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import ru.penkrat.stbf.api.Action;
|
||||||
|
import ru.penkrat.stbf.templates.ActionResolver;
|
||||||
|
import ru.penkrat.stbf.templates.utils.StringUtils;
|
||||||
|
|
||||||
|
class FlowActionResolverDelegate implements ActionResolver {
|
||||||
|
|
||||||
|
private final NamedItemResolver<ActionItem> resolver;
|
||||||
|
|
||||||
|
FlowActionResolverDelegate(FlowRoot src) {
|
||||||
|
resolver = new NamedItemResolver<>(src.getActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(String key) {
|
||||||
|
final ActionItem actionItem = resolver.get(key);
|
||||||
|
|
||||||
|
boolean isInline = StringUtils.isNotEmpty(actionItem.getCallbackData())
|
||||||
|
&& StringUtils.isNotEmpty(actionItem.getUrl());
|
||||||
|
|
||||||
|
if (isInline) {
|
||||||
|
if (StringUtils.isNotEmpty(actionItem.getCommand())) {
|
||||||
|
throw new IllegalArgumentException("'command' is not allowed for inline button");
|
||||||
|
}
|
||||||
|
if (actionItem.isRequestContact()) {
|
||||||
|
throw new IllegalArgumentException("'requestContact' is not allowed for inline button");
|
||||||
|
}
|
||||||
|
if (actionItem.isRequestLocation()) {
|
||||||
|
throw new IllegalArgumentException("'requestLocation' is not allowed for inline button");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Action.builder()
|
||||||
|
.text(actionItem.getText())
|
||||||
|
// btn only
|
||||||
|
.cmd(actionItem.getCommand())
|
||||||
|
.requestContact(actionItem.isRequestContact())
|
||||||
|
.requestLocation(actionItem.isRequestLocation())
|
||||||
|
// inline btn only
|
||||||
|
.inline(isInline)
|
||||||
|
.callbackData(actionItem.getCallbackData())
|
||||||
|
.url(actionItem.getUrl())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import ru.penkrat.stbf.templates.ActionResolver;
|
||||||
|
import ru.penkrat.stbf.templates.ScreenResolver;
|
||||||
|
|
||||||
|
public interface FlowResolver extends ScreenResolver, ActionResolver {
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@JacksonXmlRootElement(localName = "flow")
|
||||||
|
class FlowRoot {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@JacksonXmlElementWrapper(localName = "actions")
|
||||||
|
@JsonProperty("action")
|
||||||
|
private List<ActionItem> actions = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@JacksonXmlElementWrapper(localName = "screens")
|
||||||
|
@JsonProperty("screen")
|
||||||
|
private List<ScreenItem> screens = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@JacksonXmlElementWrapper(useWrapping = false)
|
||||||
|
@JsonProperty("include")
|
||||||
|
private List<IncludeItem> includes = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private transient List<FlowRoot> included = new ArrayList<>();
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package ru.penkrat.stbf.templates.xml;
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
@@ -9,46 +8,41 @@ import ru.penkrat.stbf.api.Keyboard;
|
|||||||
import ru.penkrat.stbf.api.KeyboardBuilder;
|
import ru.penkrat.stbf.api.KeyboardBuilder;
|
||||||
import ru.penkrat.stbf.api.Screen;
|
import ru.penkrat.stbf.api.Screen;
|
||||||
import ru.penkrat.stbf.common.screen.TextScreen;
|
import ru.penkrat.stbf.common.screen.TextScreen;
|
||||||
|
import ru.penkrat.stbf.templates.ActionResolver;
|
||||||
|
import ru.penkrat.stbf.templates.KeyboardProvider;
|
||||||
import ru.penkrat.stbf.templates.ScreenResolver;
|
import ru.penkrat.stbf.templates.ScreenResolver;
|
||||||
import ru.penkrat.stbf.templates.TemplateRenderer;
|
import ru.penkrat.stbf.templates.TemplateRenderer;
|
||||||
import ru.penkrat.stbf.templates.utils.ReflectionUtils;
|
import ru.penkrat.stbf.templates.utils.ReflectionUtils;
|
||||||
|
import ru.penkrat.stbf.templates.utils.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class XmlScreenResolver implements ScreenResolver {
|
class FlowScreenResolverDelegate implements ScreenResolver {
|
||||||
|
|
||||||
private final XmlMapper mapper = new XmlMapper();
|
private final NamedItemResolver<ScreenItem> resolver;
|
||||||
|
|
||||||
private final Map<String, List<ScreenItem>> byId;
|
private final ActionResolver actionResolver;
|
||||||
private final Map<String, List<ScreenItem>> byName;
|
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private TemplateRenderer templateRenderer = new NoopTemplateRenderer();
|
private TemplateRenderer templateRenderer = new NoopTemplateRenderer();
|
||||||
|
|
||||||
public XmlScreenResolver(InputStream is) throws IOException {
|
FlowScreenResolverDelegate(FlowRoot src, ActionResolver actionResolver) {
|
||||||
Screens screens = mapper.readValue(is, Screens.class);
|
this.actionResolver = actionResolver;
|
||||||
|
resolver = new NamedItemResolver<>(src.getScreens());
|
||||||
byId = screens.getScreens().stream()
|
|
||||||
.collect(Collectors.groupingBy(ScreenItem::getId));
|
|
||||||
byName = screens.getScreens().stream()
|
|
||||||
.collect(Collectors.groupingBy(ScreenItem::getName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Screen getScreen(String name) {
|
public Screen getScreen(String name) {
|
||||||
ScreenItem item = get(name);
|
ScreenItem item = resolver.get(name);
|
||||||
|
|
||||||
return new TextScreen(item.getText(), buildKeyboard(item.getKeyboard(), null));
|
return new TextScreen(item.getText(), buildKeyboard(item.getKeyboard(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Screen getScreen(String name, Object context) {
|
public Screen getScreen(String name, Object context) {
|
||||||
ScreenItem item = get(name);
|
ScreenItem item = resolver.get(name);
|
||||||
|
|
||||||
return new TextScreen(templateRenderer.render(item.getText(), context),
|
return new TextScreen(templateRenderer.render(item.getText(), context),
|
||||||
resolveKeyboard(item.getKeyboard(), context));
|
resolveKeyboard(item.getKeyboard(), context));
|
||||||
@@ -58,6 +52,13 @@ public class XmlScreenResolver implements ScreenResolver {
|
|||||||
if (wrapper == null)
|
if (wrapper == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
if (context instanceof KeyboardProvider) {
|
||||||
|
final Keyboard keyboard = ((KeyboardProvider) context).getKeyboard();
|
||||||
|
if (keyboard == null) {
|
||||||
|
log.warn("Method 'getKeyboard' returns NULL value!", wrapper.getFactoryMethod());
|
||||||
|
}
|
||||||
|
return keyboard;
|
||||||
|
}
|
||||||
if (wrapper.getFactoryMethod() != null && !wrapper.getFactoryMethod().isEmpty() && context != null) {
|
if (wrapper.getFactoryMethod() != null && !wrapper.getFactoryMethod().isEmpty() && context != null) {
|
||||||
val keyboard = ReflectionUtils.getMethodResult(context, wrapper.getFactoryMethod(), Keyboard.class);
|
val keyboard = ReflectionUtils.getMethodResult(context, wrapper.getFactoryMethod(), Keyboard.class);
|
||||||
if (keyboard == null) {
|
if (keyboard == null) {
|
||||||
@@ -77,11 +78,7 @@ public class XmlScreenResolver implements ScreenResolver {
|
|||||||
for (ButtonsRow row : wrapper.getRows()) {
|
for (ButtonsRow row : wrapper.getRows()) {
|
||||||
List<Action> buttons = row.getButtons().stream()
|
List<Action> buttons = row.getButtons().stream()
|
||||||
.filter(btn -> checkIfCondition(context, btn.getIfCondition()))
|
.filter(btn -> checkIfCondition(context, btn.getIfCondition()))
|
||||||
.map(btn -> Action.builder()
|
.map(btn -> getAction(btn))
|
||||||
.text(btn.getText())
|
|
||||||
.requestContact(btn.isRequestContact())
|
|
||||||
.requestLocation(btn.isRequestLocation())
|
|
||||||
.build())
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
builder.row(buttons.toArray(new Action[buttons.size()]));
|
builder.row(buttons.toArray(new Action[buttons.size()]));
|
||||||
@@ -92,6 +89,17 @@ public class XmlScreenResolver implements ScreenResolver {
|
|||||||
return keyboard;
|
return keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Action getAction(Button btn) {
|
||||||
|
if (StringUtils.isNotEmpty(btn.getActionRef())) {
|
||||||
|
return actionResolver.getAction(btn.getActionRef());
|
||||||
|
}
|
||||||
|
return Action.builder()
|
||||||
|
.text(btn.getText())
|
||||||
|
.requestContact(btn.isRequestContact())
|
||||||
|
.requestLocation(btn.isRequestLocation())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkIfCondition(Object context, String ifCondition) {
|
private boolean checkIfCondition(Object context, String ifCondition) {
|
||||||
if (ifCondition == null || ifCondition.isEmpty()) {
|
if (ifCondition == null || ifCondition.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -109,22 +117,4 @@ public class XmlScreenResolver implements ScreenResolver {
|
|||||||
|
|
||||||
return ReflectionUtils.getMethodResult(context, ifCondition, Boolean.class);
|
return ReflectionUtils.getMethodResult(context, ifCondition, Boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,8 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
class IncludeItem {
|
||||||
|
private String file;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
abstract class NamedItem {
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String ref;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import ru.penkrat.stbf.templates.utils.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
class NamedItemResolver<T extends NamedItem> {
|
||||||
|
|
||||||
|
private final Map<String, List<T>> byId;
|
||||||
|
private final Map<String, List<T>> byName;
|
||||||
|
|
||||||
|
NamedItemResolver(Collection<T> src) {
|
||||||
|
byId = src.stream()
|
||||||
|
.filter(item -> StringUtils.isNotEmpty(item.getId()))
|
||||||
|
.collect(Collectors.groupingBy(NamedItem::getId));
|
||||||
|
byName = src.stream()
|
||||||
|
.filter(item -> StringUtils.isNotEmpty(item.getName()))
|
||||||
|
.collect(Collectors.groupingBy(NamedItem::getName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(String key) {
|
||||||
|
List<T> 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("Element not found by 'id' or 'name' " + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,22 +1,14 @@
|
|||||||
package ru.penkrat.stbf.templates.xml;
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
class ScreenItem {
|
class ScreenItem extends NamedItem {
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
private String text;
|
||||||
private String id;
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true)
|
private KeyboardWrapper keyboard = new KeyboardWrapper();
|
||||||
private String name;
|
|
||||||
|
|
||||||
private String text;
|
|
||||||
|
|
||||||
private KeyboardWrapper keyboard = new KeyboardWrapper();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
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,45 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import ru.penkrat.stbf.api.Action;
|
||||||
|
import ru.penkrat.stbf.api.Screen;
|
||||||
|
import ru.penkrat.stbf.templates.TemplateRenderer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class XmlFlowResolver implements FlowResolver {
|
||||||
|
|
||||||
|
private final XmlMapper mapper = new XmlMapper();
|
||||||
|
|
||||||
|
private final FlowActionResolverDelegate actionDelegate;
|
||||||
|
private final FlowScreenResolverDelegate screenDelegate;
|
||||||
|
|
||||||
|
public XmlFlowResolver(InputStream is) throws IOException {
|
||||||
|
FlowRoot flow = mapper.readValue(is, FlowRoot.class);
|
||||||
|
|
||||||
|
actionDelegate = new FlowActionResolverDelegate(flow);
|
||||||
|
screenDelegate = new FlowScreenResolverDelegate(flow, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Screen getScreen(String name) {
|
||||||
|
return screenDelegate.getScreen(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Screen getScreen(String name, Object context) {
|
||||||
|
return screenDelegate.getScreen(name, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(String name) {
|
||||||
|
return actionDelegate.getAction(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemplateRenderer(TemplateRenderer templateRenderer) {
|
||||||
|
screenDelegate.setTemplateRenderer(templateRenderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import ru.penkrat.stbf.templates.utils.ReflectionUtils;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class XmlFlowResolverTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead() throws Exception {
|
||||||
|
InputStream xmlUnderTest = XmlFlowResolverTest.class.getResourceAsStream("screens.xml");
|
||||||
|
|
||||||
|
XmlFlowResolver resolver = new XmlFlowResolver(xmlUnderTest);
|
||||||
|
|
||||||
|
Screen screen1 = resolver.getScreen("screen-1");
|
||||||
|
|
||||||
|
assertThat(screen1).isNotNull();
|
||||||
|
assertThat(screen1.getText()).isEqualTo("Test text");
|
||||||
|
|
||||||
|
assertThat(screen1.getKeyboard()).isNotNull();
|
||||||
|
|
||||||
|
String keyboard = ReflectionUtils.getMethodResult(screen1.getKeyboard(), "toFriendlyString", String.class);
|
||||||
|
assertThat(keyboard).contains("Send phone");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadWithTemplates() throws Exception {
|
||||||
|
InputStream xmlUnderTest = XmlFlowResolverTest.class.getResourceAsStream("screens.xml");
|
||||||
|
|
||||||
|
XmlFlowResolver resolver = new XmlFlowResolver(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,6 @@ import org.junit.Test;
|
|||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
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 {
|
public class XmlWriterTest {
|
||||||
|
|
||||||
private final XmlMapper mapper = new XmlMapper();
|
private final XmlMapper mapper = new XmlMapper();
|
||||||
@@ -18,11 +13,12 @@ public class XmlWriterTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWrite() throws Exception {
|
public void testWrite() throws Exception {
|
||||||
Screens root = new Screens();
|
FlowRoot root = new FlowRoot();
|
||||||
|
|
||||||
ScreenItem item1 = new ScreenItem();
|
ScreenItem item1 = new ScreenItem();
|
||||||
item1.setId("1");
|
item1.setId("1");
|
||||||
@@ -52,6 +48,10 @@ public class XmlWriterTest {
|
|||||||
root.getScreens().add(item1);
|
root.getScreens().add(item1);
|
||||||
root.getScreens().add(item2);
|
root.getScreens().add(item2);
|
||||||
|
|
||||||
|
root.getActions().add(new ActionItem());
|
||||||
|
|
||||||
|
root.getIncludes().add(new IncludeItem());
|
||||||
|
|
||||||
String xml = mapper.writeValueAsString(root);
|
String xml = mapper.writeValueAsString(root);
|
||||||
|
|
||||||
System.out.println(xml);
|
System.out.println(xml);
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
<screens>
|
<flow>
|
||||||
<screen id="1" name="screen-1">
|
<actions>
|
||||||
<text>Test text</text>
|
<action id="1001" requestContact="true">Send phone</action>
|
||||||
<keyboard>
|
</actions>
|
||||||
<row>
|
<screens>
|
||||||
<button if="false">Btn1</button>
|
<screen id="1" name="screen-1">
|
||||||
<button>Btn2</button>
|
<text>Test text</text>
|
||||||
</row>
|
<keyboard>
|
||||||
<row>
|
<row>
|
||||||
<button>Btn1</button>
|
<button if="false">Btn1</button>
|
||||||
<button>Btn2</button>
|
<button>Btn2</button>
|
||||||
</row>
|
<button actionRef="1001">Action.name</button>
|
||||||
</keyboard>
|
</row>
|
||||||
</screen>
|
<row>
|
||||||
<screen id="2" name="screen-2">
|
<button>Btn1</button>
|
||||||
<text>Hello, {{ name }}</text>
|
<button>Btn2</button>
|
||||||
<keyboard factoryMethod="getKeyboard"/>
|
</row>
|
||||||
</screen>
|
</keyboard>
|
||||||
</screens>
|
</screen>
|
||||||
|
<screen id="2" name="screen-2">
|
||||||
|
<text>Hello, {{ name }}</text>
|
||||||
|
<keyboard factoryMethod="getKeyboard"/>
|
||||||
|
</screen>
|
||||||
|
</screens>
|
||||||
|
</flow>
|
||||||
Reference in New Issue
Block a user