Search in sources :

Example 1 with EvaluationContext

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;
}
Also used : UnaryTest(org.kie.dmn.feel.runtime.UnaryTest) Severity(org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity) FEELFunction(org.kie.dmn.feel.runtime.FEELFunction) EvaluationContext(org.kie.dmn.feel.lang.EvaluationContext) Msg(org.kie.dmn.feel.util.Msg) ParserRuleContext(org.antlr.v4.runtime.ParserRuleContext) Type(org.kie.dmn.feel.lang.Type) FEELFunction(org.kie.dmn.feel.runtime.FEELFunction) UnaryTest(org.kie.dmn.feel.runtime.UnaryTest)

Example 2 with EvaluationContext

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));
}
Also used : EvaluationContextImpl(org.kie.dmn.feel.lang.impl.EvaluationContextImpl) SimpleTypeImpl(org.kie.dmn.core.impl.SimpleTypeImpl) ItemDefNode(org.kie.dmn.api.core.ast.ItemDefNode) EvaluationContext(org.kie.dmn.feel.lang.EvaluationContext) DMNRuntime(org.kie.dmn.api.core.DMNRuntime) DMNModel(org.kie.dmn.api.core.DMNModel) DMNType(org.kie.dmn.api.core.DMNType) Test(org.junit.Test)

Example 3 with EvaluationContext

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);
}
Also used : IntStream(java.util.stream.IntStream) FEEL(org.kie.dmn.feel.FEEL) LoggerFactory(org.slf4j.LoggerFactory) Either(org.kie.dmn.feel.util.Either) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) FEELFnResult(org.kie.dmn.feel.runtime.functions.FEELFnResult) Collectors.toMap(java.util.stream.Collectors.toMap) FEELEvent(org.kie.dmn.api.feel.runtime.events.FEELEvent) EvaluationContext(org.kie.dmn.feel.lang.EvaluationContext) Map(java.util.Map) EvaluationContextImpl(org.kie.dmn.feel.lang.impl.EvaluationContextImpl) InvalidInputEvent(org.kie.dmn.feel.runtime.events.InvalidInputEvent) FEELEventBase(org.kie.dmn.feel.runtime.events.FEELEventBase) HitPolicyViolationEvent(org.kie.dmn.feel.runtime.events.HitPolicyViolationEvent) UnaryTest(org.kie.dmn.feel.runtime.UnaryTest) Logger(org.slf4j.Logger) Severity(org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity) DecisionTableRulesMatchedEvent(org.kie.dmn.feel.runtime.events.DecisionTableRulesMatchedEvent) Collection(java.util.Collection) Collectors(java.util.stream.Collectors) List(java.util.List) TreeMap(java.util.TreeMap) CompiledExpression(org.kie.dmn.feel.lang.CompiledExpression) Collections(java.util.Collections) InvalidInputEvent(org.kie.dmn.feel.runtime.events.InvalidInputEvent)

Example 4 with EvaluationContext

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;
}
Also used : NamedParameter(org.kie.dmn.feel.lang.impl.NamedParameter) Method(java.lang.reflect.Method) Collection(java.util.Collection) EvaluationContext(org.kie.dmn.feel.lang.EvaluationContext)

Example 5 with EvaluationContext

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);
}
Also used : IntStream(java.util.stream.IntStream) FEEL(org.kie.dmn.feel.FEEL) FEELEventBase(org.kie.dmn.feel.runtime.events.FEELEventBase) FEELEventListener(org.kie.dmn.api.feel.runtime.events.FEELEventListener) UnaryTest(org.kie.dmn.feel.runtime.UnaryTest) Logger(org.slf4j.Logger) DTOutputClause(org.kie.dmn.feel.runtime.decisiontables.DTOutputClause) HitPolicy(org.kie.dmn.feel.runtime.decisiontables.HitPolicy) LoggerFactory(org.slf4j.LoggerFactory) DTInputClause(org.kie.dmn.feel.runtime.decisiontables.DTInputClause) UUID(java.util.UUID) Collectors(java.util.stream.Collectors) Range(org.kie.dmn.feel.runtime.Range) ArrayList(java.util.ArrayList) List(java.util.List) CompiledExpression(org.kie.dmn.feel.lang.CompiledExpression) FEELEvent(org.kie.dmn.api.feel.runtime.events.FEELEvent) EvaluationContext(org.kie.dmn.feel.lang.EvaluationContext) DTDecisionRule(org.kie.dmn.feel.runtime.decisiontables.DTDecisionRule) Msg(org.kie.dmn.feel.util.Msg) DecisionTableImpl(org.kie.dmn.feel.runtime.decisiontables.DecisionTableImpl) Collections(java.util.Collections) FEEL(org.kie.dmn.feel.FEEL) DecisionTableImpl(org.kie.dmn.feel.runtime.decisiontables.DecisionTableImpl) DTOutputClause(org.kie.dmn.feel.runtime.decisiontables.DTOutputClause) UnaryTest(org.kie.dmn.feel.runtime.UnaryTest) DTDecisionRule(org.kie.dmn.feel.runtime.decisiontables.DTDecisionRule) DTInputClause(org.kie.dmn.feel.runtime.decisiontables.DTInputClause) ArrayList(java.util.ArrayList) List(java.util.List)

Aggregations

EvaluationContext (org.kie.dmn.feel.lang.EvaluationContext)7 ArrayList (java.util.ArrayList)4 List (java.util.List)4 Collectors (java.util.stream.Collectors)4 UnaryTest (org.kie.dmn.feel.runtime.UnaryTest)4 Msg (org.kie.dmn.feel.util.Msg)4 Collections (java.util.Collections)3 IntStream (java.util.stream.IntStream)3 FEELEvent (org.kie.dmn.api.feel.runtime.events.FEELEvent)3 Severity (org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity)3 FEEL (org.kie.dmn.feel.FEEL)3 CompiledExpression (org.kie.dmn.feel.lang.CompiledExpression)3 FEELEventBase (org.kie.dmn.feel.runtime.events.FEELEventBase)3 Logger (org.slf4j.Logger)3 LoggerFactory (org.slf4j.LoggerFactory)3 Method (java.lang.reflect.Method)2 Collection (java.util.Collection)2 Map (java.util.Map)2 UUID (java.util.UUID)2 ParserRuleContext (org.antlr.v4.runtime.ParserRuleContext)2