use of org.kie.dmn.feel.lang.impl.NamedParameter in project drools by kiegroup.
the class BaseFEELFunction method invokeReflectively.
@Override
public Object invokeReflectively(EvaluationContext ctx, Object[] params) {
// use reflection to call the appropriate invoke method
try {
boolean isNamedParams = params.length > 0 && params[0] instanceof NamedParameter;
if (!isCustomFunction()) {
List<String> available = null;
if (isNamedParams) {
available = Stream.of(params).map(p -> ((NamedParameter) p).getName()).collect(Collectors.toList());
}
CandidateMethod cm = getCandidateMethod(ctx, params, isNamedParams, available);
if (cm != null) {
Object result = cm.apply.invoke(this, cm.actualParams);
if (result instanceof Either) {
@SuppressWarnings("unchecked") Either<FEELEvent, Object> either = (Either<FEELEvent, Object>) result;
Object eitherResult = either.cata((left) -> {
ctx.notifyEvt(() -> {
if (left instanceof InvalidParametersEvent) {
InvalidParametersEvent invalidParametersEvent = (InvalidParametersEvent) left;
invalidParametersEvent.setNodeName(getName());
invalidParametersEvent.setActualParameters(Stream.of(cm.apply.getParameters()).map(p -> p.getAnnotation(ParameterName.class).value()).collect(Collectors.toList()), Arrays.asList(cm.actualParams));
}
return left;
});
return null;
}, Function.identity());
return eitherResult;
}
return result;
} else {
String ps = getClass().toString();
logger.error("Unable to find function '" + getName() + "( " + ps.substring(1, ps.length() - 1) + " )'");
ctx.notifyEvt(() -> {
return new FEELEventBase(Severity.ERROR, "Unable to find function '" + getName() + "( " + ps.substring(1, ps.length() - 1) + " )'", null);
});
}
} else {
if (isNamedParams) {
params = rearrangeParameters(params, this.getParameterNames().get(0));
}
Object result = invoke(ctx, params);
if (result instanceof Either) {
@SuppressWarnings("unchecked") Either<FEELEvent, Object> either = (Either<FEELEvent, Object>) result;
final Object[] usedParams = params;
Object eitherResult = either.cata((left) -> {
ctx.notifyEvt(() -> {
if (left instanceof InvalidParametersEvent) {
InvalidParametersEvent invalidParametersEvent = (InvalidParametersEvent) left;
invalidParametersEvent.setNodeName(getName());
invalidParametersEvent.setActualParameters(IntStream.of(0, usedParams.length).mapToObj(i -> "arg" + i).collect(Collectors.toList()), Arrays.asList(usedParams));
}
return left;
});
return null;
}, Function.identity());
return normalizeResult(eitherResult);
}
return normalizeResult(result);
}
} catch (Exception e) {
logger.error("Error trying to call function " + getName() + ".", e);
ctx.notifyEvt(() -> {
return new FEELEventBase(Severity.ERROR, "Error trying to call function " + getName() + ".", e);
});
}
return null;
}
use of org.kie.dmn.feel.lang.impl.NamedParameter in project drools by kiegroup.
the class BaseFEELFunction method calculateActualParams.
private Object[] calculateActualParams(EvaluationContext ctx, Method m, Object[] params, List<String> available) {
Annotation[][] pas = m.getParameterAnnotations();
List<String> names = new ArrayList<>(m.getParameterCount());
for (int i = 0; i < m.getParameterCount(); i++) {
for (int p = 0; p < pas[i].length; i++) {
if (pas[i][p] instanceof ParameterName) {
names.add(((ParameterName) pas[i][p]).value());
break;
}
}
if (names.get(i) == null) {
// no name found
return null;
}
}
Object[] actualParams = new Object[names.size()];
boolean isVariableParameters = m.getParameterCount() > 0 && m.getParameterTypes()[m.getParameterCount() - 1].isArray();
String variableParamPrefix = isVariableParameters ? names.get(names.size() - 1) : null;
List<Object> variableParams = isVariableParameters ? new ArrayList<>() : null;
for (Object o : params) {
NamedParameter np = (NamedParameter) o;
if (names.contains(np.getName())) {
actualParams[names.indexOf(np.getName())] = np.getValue();
} else if (isVariableParameters) {
// check if it is a variable parameters method
if (np.getName().matches(variableParamPrefix + "\\d+")) {
int index = Integer.parseInt(np.getName().substring(variableParamPrefix.length())) - 1;
if (variableParams.size() <= index) {
for (int i = variableParams.size(); i < index; i++) {
// need to add nulls in case the user skipped indexes
variableParams.add(null);
}
variableParams.add(np.getValue());
} else {
variableParams.set(index, np.getValue());
}
} else {
// invalid parameter, method is incompatible
return null;
}
} else {
// invalid parameter, method is incompatible
return null;
}
}
if (isVariableParameters) {
actualParams[actualParams.length - 1] = variableParams.toArray();
}
return actualParams;
}
use of org.kie.dmn.feel.lang.impl.NamedParameter in project drools by kiegroup.
the class DMNInvocationEvaluator method evaluate.
@Override
public EvaluatorResult evaluate(DMNRuntimeEventManager eventManager, DMNResult dmnr) {
final List<FEELEvent> events = new ArrayList<>();
DMNResultImpl result = (DMNResultImpl) dmnr;
DMNContext previousContext = result.getContext();
DMNContextImpl dmnContext = (DMNContextImpl) previousContext.clone();
result.setContext(dmnContext);
Object invocationResult = null;
try {
FEELFunction function = this.functionLocator.apply(previousContext, functionName);
if (function == null) {
// check if it is a configured/built-in function
Object r = null;
if (feel != null) {
r = ((FEELImpl) feel).newEvaluationContext(Collections.emptyList(), Collections.emptyMap()).getValue(functionName);
} else {
r = RootExecutionFrame.INSTANCE.getValue(functionName);
}
if (r != null && r instanceof FEELFunction) {
function = (FEELFunction) r;
}
}
if (function == null) {
MsgUtil.reportMessage(logger, DMNMessage.Severity.ERROR, node, result, null, null, Msg.FUNCTION_NOT_FOUND, functionName, nodeName);
return new EvaluatorResultImpl(null, ResultType.FAILURE);
}
Object[] namedParams = new Object[parameters.size()];
int index = 0;
for (ActualParameter param : parameters) {
try {
EvaluatorResult value = param.expression.evaluate(eventManager, result);
if (value.getResultType() == ResultType.SUCCESS) {
namedParams[index++] = new NamedParameter(param.name, value.getResult());
} else {
MsgUtil.reportMessage(logger, DMNMessage.Severity.ERROR, node, result, null, null, Msg.ERR_EVAL_PARAM_FOR_INVOCATION_ON_NODE, param.name, functionName, nodeName);
return new EvaluatorResultImpl(null, ResultType.FAILURE);
}
} catch (Exception e) {
MsgUtil.reportMessage(logger, DMNMessage.Severity.ERROR, node, result, e, null, Msg.ERR_INVOKING_PARAM_EXPR_FOR_PARAM_ON_NODE, param.name, nodeName);
return new EvaluatorResultImpl(null, ResultType.FAILURE);
}
}
FEELEventListenersManager listenerMgr = new FEELEventListenersManager();
listenerMgr.addListener(events::add);
EvaluationContextImpl ctx = new EvaluationContextImpl(listenerMgr, eventManager.getRuntime());
invocationResult = function.invokeReflectively(ctx, namedParams);
boolean hasErrors = hasErrors(events, eventManager, result);
return new EvaluatorResultImpl(invocationResult, hasErrors ? ResultType.FAILURE : ResultType.SUCCESS);
} catch (Throwable t) {
MsgUtil.reportMessage(logger, DMNMessage.Severity.ERROR, node, result, t, null, Msg.ERR_INVOKING_FUNCTION_ON_NODE, functionName, nodeName);
return new EvaluatorResultImpl(null, ResultType.FAILURE);
} finally {
result.setContext(previousContext);
}
}
use of org.kie.dmn.feel.lang.impl.NamedParameter 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;
}
Aggregations