use of org.kie.dmn.feel.lang.EvaluationContext in project drools by kiegroup.
the class FunctionInvocationNode method evaluate.
@Override
public Object evaluate(EvaluationContext ctx) {
FEELFunction function = null;
Object value = null;
if (name instanceof NameRefNode) {
// simple name
value = ctx.getValue(name.getText());
} else {
QualifiedNameNode qn = (QualifiedNameNode) name;
String[] qns = qn.getPartsAsStringArray();
value = ctx.getValue(qns);
}
if (value instanceof FEELFunction) {
function = (FEELFunction) value;
if (function != null) {
Object[] p = params.getElements().stream().map(e -> e.evaluate(ctx)).toArray(Object[]::new);
Object result = function.invokeReflectively(ctx, p);
return result;
} else {
ctx.notifyEvt(astEvent(Severity.ERROR, Msg.createMessage(Msg.FUNCTION_NOT_FOUND, name.getText())));
}
} else if (value instanceof UnaryTest) {
if (params.getElements().size() == 1) {
Object p = params.getElements().get(0).evaluate(ctx);
return ((UnaryTest) value).apply(ctx, p);
} else {
ctx.notifyEvt(astEvent(Severity.ERROR, Msg.createMessage(Msg.CAN_T_INVOKE_AN_UNARY_TEST_WITH_S_PARAMETERS_UNARY_TESTS_REQUIRE_1_SINGLE_PARAMETER, params.getElements().size())));
}
}
return null;
}
use of org.kie.dmn.feel.lang.EvaluationContext in project drools by kiegroup.
the class DMNCompilerTest method testItemDefAllowedValuesString.
@Test
public void testItemDefAllowedValuesString() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime("0003-input-data-string-allowed-values.dmn", this.getClass());
DMNModel dmnModel = runtime.getModel("https://github.com/kiegroup/kie-dmn", "0003-input-data-string-allowed-values");
assertThat(dmnModel, notNullValue());
ItemDefNode itemDef = dmnModel.getItemDefinitionByName("tEmploymentStatus");
assertThat(itemDef.getName(), is("tEmploymentStatus"));
assertThat(itemDef.getId(), is(nullValue()));
DMNType type = itemDef.getType();
assertThat(type, is(notNullValue()));
assertThat(type.getName(), is("tEmploymentStatus"));
assertThat(type.getId(), is(nullValue()));
assertThat(type, is(instanceOf(SimpleTypeImpl.class)));
SimpleTypeImpl feelType = (SimpleTypeImpl) type;
EvaluationContext ctx = new EvaluationContextImpl(null);
assertThat(feelType.getFeelType(), is(BuiltInType.STRING));
assertThat(feelType.getAllowedValuesFEEL().size(), is(4));
assertThat(feelType.getAllowedValuesFEEL().get(0).apply(ctx, "UNEMPLOYED"), is(true));
assertThat(feelType.getAllowedValuesFEEL().get(1).apply(ctx, "EMPLOYED"), is(true));
assertThat(feelType.getAllowedValuesFEEL().get(2).apply(ctx, "SELF-EMPLOYED"), is(true));
assertThat(feelType.getAllowedValuesFEEL().get(3).apply(ctx, "STUDENT"), is(true));
}
use of org.kie.dmn.feel.lang.EvaluationContext in project drools by kiegroup.
the class DecisionTableImpl method actualInputsMatchInputValues.
/**
* If valid input values are defined, check that all parameters match the respective valid inputs
* @param ctx
* @param params
* @return
*/
private Either<FEELEvent, Object> actualInputsMatchInputValues(EvaluationContext ctx, Object[] params) {
// check that all the parameters match the input list values if they are defined
for (int i = 0; i < params.length; i++) {
final DTInputClause input = inputs.get(i);
// if a list of values is defined, check the the parameter matches the value
if (input.getInputValues() != null && !input.getInputValues().isEmpty()) {
final Object parameter = params[i];
boolean satisfies = input.getInputValues().stream().map(ut -> ut.apply(ctx, parameter)).filter(Boolean::booleanValue).findAny().orElse(false);
if (!satisfies) {
String values = input.getInputValuesText();
return Either.ofLeft(new InvalidInputEvent(FEELEvent.Severity.ERROR, input.getInputExpression() + "='" + parameter + "' does not match any of the valid values " + values + " for decision table '" + getName() + "'.", getName(), null, values));
}
}
}
return Either.ofRight(true);
}
use of org.kie.dmn.feel.lang.EvaluationContext in project drools by kiegroup.
the class BaseFEELFunction method getCandidateMethod.
private CandidateMethod getCandidateMethod(EvaluationContext ctx, Object[] params, boolean isNamedParams, List<String> available) {
CandidateMethod candidate = null;
// first, look for exact matches
for (Method m : getClass().getDeclaredMethods()) {
if (!Modifier.isPublic(m.getModifiers()) || !m.getName().equals("invoke")) {
continue;
}
Object[] actualParams = null;
boolean injectCtx = Arrays.stream(m.getParameterTypes()).anyMatch(p -> EvaluationContext.class.isAssignableFrom(p));
if (injectCtx) {
actualParams = new Object[params.length + 1];
int j = 0;
for (int i = 0; i < m.getParameterCount(); i++) {
if (EvaluationContext.class.isAssignableFrom(m.getParameterTypes()[i])) {
if (isNamedParams) {
actualParams[i] = new NamedParameter("ctx", ctx);
} else {
actualParams[i] = ctx;
}
} else if (j < params.length) {
actualParams[i] = params[j];
j++;
}
}
} else {
actualParams = params;
}
if (isNamedParams) {
actualParams = calculateActualParams(ctx, m, actualParams, available);
if (actualParams == null) {
// incompatible method
continue;
}
}
CandidateMethod cm = new CandidateMethod(actualParams);
Class<?>[] parameterTypes = m.getParameterTypes();
if (!isNamedParams) {
// if named parameters, then it has been adjusted already in the calculateActualParams method,
// otherwise adjust here
adjustForVariableParameters(cm, parameterTypes);
}
if (parameterTypes.length != cm.getActualParams().length) {
continue;
}
boolean found = true;
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> currentIdxActualParameterType = cm.getActualClasses()[i];
if (currentIdxActualParameterType != null && !parameterTypes[i].isAssignableFrom(currentIdxActualParameterType)) {
// and vice-versa
if (Collection.class.isAssignableFrom(currentIdxActualParameterType)) {
Collection<?> valueCollection = (Collection<?>) actualParams[i];
if (valueCollection.size() == 1) {
Object singletonValue = valueCollection.iterator().next();
// re-perform the assignable-from check, this time using the element itself the singleton value from the original parameter list
if (parameterTypes[i].isAssignableFrom(singletonValue.getClass())) {
Object[] newParams = new Object[cm.getActualParams().length];
// can't rely on adjustForVariableParameters() have actually copied
System.arraycopy(cm.getActualParams(), 0, newParams, 0, cm.getActualParams().length);
newParams[i] = singletonValue;
cm.setActualParams(newParams);
continue;
}
}
}
found = false;
break;
}
}
if (found) {
cm.setApply(m);
if (candidate == null || cm.getScore() > candidate.getScore()) {
candidate = cm;
}
}
}
return candidate;
}
use of org.kie.dmn.feel.lang.EvaluationContext in project drools by kiegroup.
the class DecisionTableFunction method invoke.
/**
* @param inputExpressionList a list of the N>=0 input expressions in display order
* @param inputValuesList * a list of N input values, corresponding to the input expressions. Each
* list element is a unary tests literal (see below).
* @param outputs * a name (a string matching grammar rule 27) or a list of M>0 names
* @param outputValues * if outputs is a list, then output values is a list of lists of values, one list
* per output; else output values is a list of values for the one output.
* Each value is a string.
* @param ruleList a list of R>0 rules. A rule is a list of N input entries followed by M
* output entries. An input entry is a unary tests literal. An output entry is
* an expression represented as a string.
* @param hitPolicy * one of: "U", "A", “P”, “F”, "R", "O", "C", "C+", "C#", "C<", “C>”
* (default is "U")
* @param defaultOutputValue * if outputs is a list, then default output value is a context with entries
* composed of outputs and output values; else default output value is one
* of the output values.
*/
public Object invoke(@ParameterName("ctx") EvaluationContext ctx, @ParameterName("outputs") Object outputs, @ParameterName("input expression list") Object inputExpressionList, @ParameterName("input values list") List<?> inputValuesList, @ParameterName("output values") Object outputValues, @ParameterName("rule list") List<List> ruleList, @ParameterName("hit policy") String hitPolicy, @ParameterName("default output value") Object defaultOutputValue) {
// input expression list can have a single element or be a list
// TODO isn't ^ conflicting with the specs page 136 "input expression list: a LIST of the"
List<String> inputExpressions = inputExpressionList instanceof List ? (List) inputExpressionList : Collections.singletonList((String) inputExpressionList);
List<DTInputClause> inputs;
if (inputValuesList != null) {
List<UnaryTest> inputValues = inputValuesList.stream().map(o -> toUnaryTest(ctx, o)).collect(Collectors.toList());
if (inputValues.size() != inputExpressions.size()) {
// TODO handle compilation error
}
// zip inputExpression with its inputValue
inputs = IntStream.range(0, inputExpressions.size()).mapToObj(i -> new DTInputClause(inputExpressions.get(i), inputValuesList.toString(), Collections.singletonList(inputValues.get(i)), null)).collect(Collectors.toList());
} else {
inputs = inputExpressions.stream().map(ie -> new DTInputClause(ie, null, null, null)).collect(Collectors.toList());
}
List<String> parseOutputs = outputs instanceof List ? (List) outputs : Collections.singletonList((String) outputs);
List<DTOutputClause> outputClauses;
if (outputValues != null) {
if (parseOutputs.size() == 1) {
outputClauses = new ArrayList<>();
List<UnaryTest> outputValuesCompiled = objectToUnaryTestList(ctx, Collections.singletonList((List<Object>) outputValues)).get(0);
outputClauses.add(new DTOutputClause(parseOutputs.get(0), outputValuesCompiled));
} else {
List<List<UnaryTest>> listOfList = objectToUnaryTestList(ctx, (List<List<Object>>) outputValues);
// zip inputExpression with its inputValue
outputClauses = IntStream.range(0, parseOutputs.size()).mapToObj(i -> new DTOutputClause(parseOutputs.get(i), listOfList.get(i))).collect(Collectors.toList());
}
} else {
outputClauses = parseOutputs.stream().map(out -> new DTOutputClause(out, null)).collect(Collectors.toList());
}
// TODO parse default output value.
FEEL feel = FEEL.newInstance();
List<DTDecisionRule> decisionRules = IntStream.range(0, ruleList.size()).mapToObj(index -> toDecisionRule(ctx, feel, index, ruleList.get(index), inputExpressions.size())).collect(Collectors.toList());
// TODO is there a way to avoid UUID and get from _evaluation_ ctx the name of the wrapping context?
// TODO also in this case it is using an ad-hoc created FEEL instance instead of the "hosted" one.
DecisionTableImpl dti = new DecisionTableImpl(UUID.randomUUID().toString(), inputExpressions, inputs, outputClauses, decisionRules, HitPolicy.fromString(hitPolicy), FEEL.newInstance());
return new DTInvokerFunction(dti);
}
Aggregations