Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.takari.orchestra.plugins.yaml.YamlConverter Maven / Gradle / Ivy
package io.takari.orchestra.plugins.yaml;
import io.takari.bpm.model.*;
import io.takari.orchestra.common.format.ParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public final class YamlConverter {
private static final Logger log = LoggerFactory.getLogger(YamlParser.class);
public static final String TYPE = "orchestra/yaml";
private static final String SUBPROCESS_KEY = "subprocess";
private static final String SWITCH_KEY = "switch";
private static final String STEPS_KEY = "steps";
private static final String EXPR_KEY = "expr";
private static final String END_KEY = "end";
private static final String CALL_KEY = "call";
private static final String REF_KEY = "ref";
private static final String IN_VARS_KEY = "in";
private static final String ERRORS_KEY = "errors";
public static Collection convert(Map data) throws ParserException {
Collection result = new ArrayList<>();
for (Map.Entry entry : data.entrySet()) {
String id = entry.getKey();
Object v = entry.getValue();
if (!(v instanceof List)) {
throw new ParserException("Unsupported element type in the process '" + id + "': " + v);
}
List l = (List) v;
IdGenerator idGenerator = new IdGenerator();
ProcessDefinition pd = new ProcessDefinition(id, addEntryPoint(idGenerator, toElements(idGenerator, l)),
Collections.singletonMap(ProcessDefinition.SOURCE_TYPE_ATTRIBUTE, TYPE));
if (log.isDebugEnabled()) {
print(pd, 0);
}
result.add(pd);
}
return result;
}
private static void print(AbstractElement e, int level) {
if (e instanceof SequenceFlow) {
SequenceFlow f = (SequenceFlow) e;
System.out.println(ident(level) + e.getClass() + ": id=" + e.getId() + " // " + f.getFrom() + " -> " + f.getTo() + " // " + f.getExpression());
} else if (e instanceof ServiceTask) {
ServiceTask t = (ServiceTask) e;
System.out.println(ident(level) + e.getClass() + ": id=" + e.getId() + " // " + t.getType() + " // " + t.getExpression());
} else {
System.out.println(ident(level) + e.getClass() + ": id=" + e.getId());
}
if (e instanceof ProcessDefinition) {
ProcessDefinition pd = (ProcessDefinition) e;
for (AbstractElement c : pd.getChildren()) {
print(c, level + 1);
}
}
}
private static String ident(int level) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < level; i++) {
b.append("\t");
}
return b.toString();
}
private static List toElements(IdGenerator idGenerator, Collection elements) throws ParserException {
List result = new ArrayList<>();
for (Object e : elements) {
if (!(e instanceof Map)) {
throw new ParserException("Unsupported element type: " + e);
}
Map m = (Map) e;
result.add(toElements(idGenerator, m));
}
return link(idGenerator, result);
}
private static Container toElements(IdGenerator idGenerator, Map m) throws ParserException {
if (m.containsKey(SUBPROCESS_KEY)) {
return toSubProcess(idGenerator, m);
}
if (m.containsKey(EXPR_KEY)) {
ServiceTask t = new ServiceTask(idGenerator.nextId(), ExpressionType.SIMPLE, m.get("expr").toString());
return new Container(t, t);
}
if (m.containsKey(END_KEY)) {
String errorRef = (String) m.get(END_KEY);
EndEvent e = new EndEvent(idGenerator.nextId(), errorRef);
return new Container(e, e);
}
if (m.containsKey(CALL_KEY)) {
return toCallActivity(idGenerator, m);
}
if (m.containsKey(SWITCH_KEY)) {
return toSwitch(idGenerator, m);
}
throw new ParserException("Unsupported element type: " + m);
}
private static Container toSwitch(IdGenerator idGenerator, Map m) throws ParserException {
Object o = m.get(SWITCH_KEY);
if (o == null) {
throw new ParserException("Empty switch branches, should be at least one");
}
if (!(o instanceof Collection)) {
throw new ParserException("Invalid switch branches type. Should be an array of objects");
}
ExclusiveGateway gw = new ExclusiveGateway(idGenerator.nextId());
List elements = new ArrayList<>();
List exits = new ArrayList<>();
exits.add(gw); // a default branch
Collection> branches = (Collection>) o;
for (Map b : branches) {
Object steps = b.get(STEPS_KEY);
if (steps == null || ((Collection) steps).isEmpty()) {
throw new ParserException("A branch must contain at least one step");
}
List es = toElements(idGenerator, (Collection) steps);
Object expr = b.get(EXPR_KEY);
if (expr != null && !(expr instanceof String)) {
throw new ParserException("Branch expression must be a string");
}
String toId = es.get(0).getId();
// if the branch doesn't finish with an end event, then add its last element as an 'exit' element
if (es.size() >= 2) {
AbstractElement exit = es.get(es.size() - 1);
if (!(exit instanceof EndEvent)) {
exits.add(exit);
}
}
SequenceFlow f = new SequenceFlow(idGenerator.nextId(), gw.getId(), toId, (String) expr);
elements.add(f);
elements.addAll(es);
}
return new Container(gw, exits, elements);
}
private static Container toCallActivity(IdGenerator idGenerator, Map m) throws ParserException {
String call = (String) m.get(CALL_KEY);
CallActivity a = new CallActivity(idGenerator.nextId(), call, toVariableMapping(m), null);
return new Container(a, a);
}
private static Set toVariableMapping(Map m) throws ParserException {
Object o = m.get(IN_VARS_KEY);
if (o == null) {
return null;
}
if (!(o instanceof Map)) {
throw new ParserException("Invalid element type of input variables mapping: " + o);
}
Set result = new HashSet<>();
Map vars = (Map) o;
for (Map.Entry e : vars.entrySet()) {
String sourceExpression = null;
Object sourceValue = null;
String target = e.getKey();
Object v = e.getValue();
if (v instanceof Number || v instanceof Boolean || v instanceof Collection) {
sourceValue = v;
} else if (v instanceof String) {
String s = v.toString();
if (isAnExpression(s)) {
sourceExpression = s;
} else {
sourceValue = s;
}
} else {
throw new ParserException("Source variable values must be a string, an expression or a supported literal value. Key: " + target + ", value: " + v);
}
result.add(new VariableMapping(null, sourceExpression, sourceValue, target));
}
return result;
}
private static Container toSubProcess(IdGenerator idGenerator, Map m) throws ParserException {
Object steps = m.get(STEPS_KEY);
if (steps == null) {
throw new ParserException("Mandatory 'steps' section missing in the subprocess: " + m);
}
if (!(steps instanceof List)) {
throw new ParserException("Unsupported element in the steps section of the subprocess: " + m);
}
List l = (List) steps;
if (l.isEmpty()) {
throw new ParserException("Empty 'steps' section in the subprocess: " + m);
}
List children = addEntryPoint(idGenerator, toElements(idGenerator, l));
String subId = idGenerator.nextId();
SubProcess sub = new SubProcess(subId, children);
return new Container(sub, sub, toBoundaryEvents(idGenerator, subId, m));
}
private static List toBoundaryEvents(IdGenerator idGenerator, String attachedTo, Map m) throws ParserException {
Object errors = m.get(ERRORS_KEY);
if (errors == null) {
return Collections.emptyList();
}
if (!(errors instanceof List)) {
throw new ParserException("Invalid errors description: expected a list of errors: " + m);
}
List el = (List) errors;
if (el.isEmpty()) {
throw new ParserException("Emptry 'errors' section in the subprocess: " + m);
}
List result = new ArrayList<>();
for (Object e : el) {
if (!(e instanceof Map)) {
throw new ParserException("Invalid format of error description. Expected a map, got: " + e);
}
Map em = (Map) e;
String ref = (String) em.get(REF_KEY);
String call = (String) em.get(CALL_KEY);
if (call == null) {
throw new ParserException("Invalid format of error description. Expected a 'call' element, got: " + e);
}
String evId = idGenerator.nextId();
result.add(new BoundaryEvent(evId, attachedTo, ref));
Container a = toCallActivity(idGenerator, em);
String callId = a.first.getId();
result.add(new SequenceFlow(idGenerator.nextId(), evId, callId));
result.add(a.first);
if (a.rest != null) {
result.addAll(a.rest);
}
String endId = idGenerator.nextId();
if (a.last != null) {
for (AbstractElement l : a.last) {
result.add(new SequenceFlow(idGenerator.nextId(), l.getId(), endId));
}
result.addAll(a.last);
}
result.add(new EndEvent(endId, ref != null ? ref : "generalError"));
}
return result;
}
private static List addEntryPoint(IdGenerator idGenerator, List elements) throws ParserException {
if (elements.size() < 1) {
throw new ParserException("Can't add an entry point to a empty block");
}
List l = new ArrayList<>(elements);
AbstractElement first = l.get(0);
AbstractElement last = l.get(l.size() - 1);
String startId = idGenerator.nextId();
String endId = idGenerator.nextId();
l.add(0, new StartEvent(startId));
l.add(1, new SequenceFlow(idGenerator.nextId(), startId, first.getId()));
l.add(new SequenceFlow(idGenerator.nextId(), last.getId(), endId));
l.add(new EndEvent(endId));
return l;
}
private static List link(IdGenerator idGenerator, List containers) {
if (containers.isEmpty()) {
return Collections.emptyList();
}
List l = new ArrayList<>();
Collection last = null;
for (Container c : containers) {
if (last != null) {
for (AbstractElement e : last) {
l.add(new SequenceFlow(idGenerator.nextId(), e.getId(), c.first.getId()));
}
}
l.add(c.first);
if (c.rest != null) {
l.addAll(c.rest);
}
l.addAll(c.last);
last = c.last;
}
return l;
}
private static boolean isAnExpression(String s) {
if (s == null) {
return false;
}
return s.startsWith("${") && s.endsWith("}");
}
private static class IdGenerator {
private int counter = 0;
public String nextId() {
return "e" + counter++;
}
}
private static class Container {
private final AbstractElement first;
private final List last;
private final List rest;
private Container(AbstractElement first, List last, List rest) {
this.first = first;
this.last = last;
this.rest = rest;
}
private Container(AbstractElement first, AbstractElement last, List rest) {
this(first, Collections.singletonList(last), rest);
}
private Container(AbstractElement first, AbstractElement last) {
this(first, last, null);
}
}
private YamlConverter() {
}
}