package org.kie.server.api.model.dmn;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.drools.core.xml.jaxb.util.JaxbMapAdapter;
import org.drools.core.xml.jaxb.util.JaxbUnknownAdapter;
import org.kie.dmn.api.core.DMNContext;
import org.kie.dmn.api.core.DMNDecisionResult;
import org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus;
import org.kie.dmn.api.core.DMNMessage;
import org.kie.dmn.api.core.DMNMessage.Severity;
import org.kie.server.api.marshalling.json.JSONMarshaller;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.kie.dmn.api.core.DMNResult;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "dmn-evaluation-result")
@XStreamAlias("dmn-evaluation-result")
public class DMNResultKS implements DMNResult {
@XmlElement(name="model-namespace")
@XStreamAlias("model-namespace")
private String namespace;
@XmlElement(name="model-name")
@XStreamAlias("model-name")
private String modelName;
@XmlElement(name="decision-name")
@XStreamAlias("decision-name")
private String decisionName;
@XmlElement(name="dmn-context")
@XStreamAlias("dmn-context")
@XmlJavaTypeAdapter(JaxbUnknownAdapter.class)
@JsonSerialize(using = JSONMarshaller.PassThruSerializer.class)
private Map<String, Object> dmnContext = new HashMap<>();
// concrete implementation of DMNMessage and DMNDecisionResult are needed in order to have proper marshalling
@XmlElementWrapper(name="messages")
@XStreamAlias("messages")
private List<DMNMessageKS> messages = new ArrayList<>();
@XmlElement(name="decision-results")
@XStreamAlias("decision-results")
private Map<String, DMNDecisionResultKS> decisionResults = new HashMap<>();
public DMNResultKS() {
// no-arg constructor for marshalling
}
public DMNResultKS(DMNResult dmnResult) {
this.setDmnContext( dmnResult.getContext().getAll() );
this.setMessages( dmnResult.getMessages() );
this.setDecisionResults( dmnResult.getDecisionResults() );
}
public DMNResultKS(String namespace, String modelName, DMNResult dmnResult) {
this(dmnResult);
this.namespace = namespace;
this.modelName = modelName;
}
public DMNResultKS(String namespace, String modelName, String decisionName, DMNResult dmnResult) {
this(namespace, modelName, dmnResult);
this.decisionName = decisionName;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public String getDecisionName() {
return decisionName;
}
public void setDecisionName(String decisionName) {
this.decisionName = decisionName;
}
public Map<String, Object> getDmnContext() {
return dmnContext;
}
public void setDmnContext(Map<String, Object> dmnContext) {
dmnContext.replaceAll( (k, v) -> stubDMNResult(v) );
this.dmnContext = dmnContext;
}
public void setMessages(List<DMNMessage> messages) {
// wrap for serialization:
for ( DMNMessage m : messages ) {
this.messages.add(DMNMessageKS.of(m));
}
}
public void setDecisionResults(List<DMNDecisionResult> decisionResults) {
for ( DMNDecisionResult dr : decisionResults ) {
this.decisionResults.put(dr.getDecisionId(), DMNDecisionResultKS.of(dr));
}
}
@Override
public DMNContext getContext() {
// TODO rewiew, this means the DMNContext returned is detached from the internal context here.
return MapBackedDMNContext.of(dmnContext);
}
private static class MapBackedDMNContext implements DMNContext {
private Map<String, Object> ctx = new HashMap<>();
private MapBackedDMNContext() {
// intentional
}
static MapBackedDMNContext of(Map<String, Object> ctx) {
MapBackedDMNContext result = new MapBackedDMNContext();
result.ctx = ctx;
return result;
}
@Override
public Object set(String name, Object value) {
return ctx.put(name, value);
}
@Override
public Object get(String name) {
return ctx.get(name);
}
@Override
public Map<String, Object> getAll() {
return ctx;
}
@Override
public boolean isDefined(String name) {
return ctx.containsKey(name);
}
@Override
public DMNContext clone() {
return of(this.ctx);
}
}
@Override
public List<DMNMessage> getMessages() {
List<DMNMessage> res = new ArrayList<>();
messages.forEach(x -> res.add(x));
return res;
}
@Override
public List<DMNMessage> getMessages(Severity... sevs) {
return this.messages.stream()
.filter( m -> Arrays.asList(sevs).stream().anyMatch( f -> f.equals(m.getSeverity())) )
.collect(Collectors.toList());
}
@Override
public boolean hasErrors() {
return messages.stream().anyMatch( m -> DMNMessage.Severity.ERROR.equals( m.getSeverity() ) );
}
@Override
public List<DMNDecisionResult> getDecisionResults() {
return new ArrayList<>( decisionResults.values() );
}
@Override
public DMNDecisionResult getDecisionResultByName( String name ) {
return decisionResults.values().stream().filter( dr -> dr.getDecisionName().equals( name ) ).findFirst().get();
}
@Override
public DMNDecisionResult getDecisionResultById( String id ) {
return decisionResults.get( id );
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DMNResultKS [namespace=").append(namespace).append(", modelName=").append(modelName).append(", decisionName=").append(decisionName).append(", dmnContext=").append(dmnContext).append(", messages=").append(messages).append(", decisionResults=").append(decisionResults).append("]");
return builder.toString();
}
public static Object stubDMNResult(Object result) {
if ( result instanceof DMNContext ) {
((DMNContext) result).getAll().replaceAll( (k, v) -> stubDMNResult(v) );
return MapBackedDMNContext.of(((DMNContext) result).getAll());
} else if ( result instanceof Map<?, ?> ) {
((Map) result).replaceAll( (k, v) -> stubDMNResult(v) );
} else if ( result instanceof List<?> ) {
((List<Object>) result).replaceAll( DMNResultKS::stubDMNResult );
return result;
} else if ( result instanceof Set<?> ) {
Set<?> originalSet = (Set<?>) result;
Collection mappedSet = originalSet.stream().map( DMNResultKS::stubDMNResult ).collect(Collectors.toSet());
originalSet.clear();
originalSet.addAll(mappedSet);
return result;
} else if ( result != null && result.getClass().getPackage().getName().startsWith("org.kie.dmn") ) {
return DMNNodeStub.of(result);
}
return result;
}
}