use of org.kie.dmn.model.api.GwtIncompatible in project drools by kiegroup.
the class BaseFEELFunction method invokeReflectively.
@Override
@GwtIncompatible
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 {
// CandidateMethod cm could be null also if reflection failed on Platforms not supporting getClass().getDeclaredMethods()
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.getParameters().get(0).stream().map(Param::getName).collect(Collectors.toList()));
}
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.model.api.GwtIncompatible in project drools by kiegroup.
the class BaseFEELFunction method calculateActualParams.
@GwtIncompatible
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.model.api.GwtIncompatible in project drools by kiegroup.
the class BaseFEELFunction method getCandidateMethod.
@GwtIncompatible
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 && actualParams.length > 0) {
// 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 (singletonValue != null && 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) {
candidate = cm;
} else {
if (cm.getScore() > candidate.getScore()) {
candidate = cm;
} else if (cm.getScore() == candidate.getScore()) {
if (isNamedParams && nullCount(cm.actualParams) < nullCount(candidate.actualParams)) {
// `cm` narrower for named parameters without need of passing nulls.
candidate = cm;
} else if (candidate.getApply().getParameterTypes().length == 1 && cm.getApply().getParameterTypes().length == 1 && candidate.getApply().getParameterTypes()[0].equals(Object.class) && !cm.getApply().getParameterTypes()[0].equals(Object.class)) {
// `cm` is more narrowed, hence reflect `candidate` to be now `cm`.
candidate = cm;
}
} else {
// do nothing.
}
}
}
}
return candidate;
}
Aggregations