Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KIE-781] Improve performance of InfixOpNode #5634

Merged
merged 1 commit into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.kie.dmn.api.feel.runtime.events.FEELEvent;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.Type;
import org.kie.dmn.feel.util.EvalHelper;
import org.kie.dmn.feel.util.Msg;

public class NameRefNode
Expand All @@ -43,12 +42,11 @@ public NameRefNode(ParserRuleContext ctx, String text, Type type) {

@Override
public Object evaluate(EvaluationContext ctx) {
String varName = EvalHelper.normalizeVariableName( getText() );
if( ! ctx.isDefined( varName ) ) {
Object result = ctx.getValue( getText() );
if ( result == null && !ctx.isDefined( getText() ) ) {
ctx.notifyEvt( astEvent( FEELEvent.Severity.ERROR, Msg.createMessage( Msg.UNKNOWN_VARIABLE_REFERENCE, getText()), null) );
return null;
}
return ctx.getValue( varName );
return result;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,110 +18,85 @@
*/
package org.kie.dmn.feel.lang.ast.infixexecutors;

import java.math.BigDecimal;
import java.math.MathContext;
import java.time.Duration;
import java.time.LocalDate;
import java.time.chrono.ChronoPeriod;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;

import org.kie.dmn.api.feel.runtime.events.FEELEvent;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.ast.InfixOpNode;
import org.kie.dmn.feel.lang.types.impl.ComparablePeriod;
import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
import org.kie.dmn.feel.util.Msg;

import java.math.MathContext;
import java.time.*;
import java.time.chrono.ChronoPeriod;
import java.time.temporal.Temporal;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

import static org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.addLocalDateAndDuration;
import static org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.math;
import static org.kie.dmn.feel.util.EvalHelper.getBigDecimalOrNull;

public class AddExecutor implements InfixExecutor {

private static final AddExecutor INSTANCE = new AddExecutor();
private final Map<ClassIdentifierTuple, BiFunction<EvaluatedParameters, EvaluationContext, Object>> addFunctionsByClassesTuple;

private AddExecutor() {
addFunctionsByClassesTuple = getAddFunctionsByClassesTuple();
}

public static AddExecutor instance() {
return INSTANCE;
}

@Override
public Object evaluate(Object left, Object right, EvaluationContext ctx) {
return evaluate(new EvaluatedParameters(left, right), ctx);
return add(left, right, ctx);
}

@Override
public Object evaluate(InfixOpNode infixNode, EvaluationContext ctx) {
return evaluate(infixNode.getLeft().evaluate(ctx), infixNode.getRight().evaluate(ctx), ctx);
}

private Object evaluate(EvaluatedParameters params, EvaluationContext ctx) {
if (params.getLeft() == null || params.getRight() == null) {
private Object add(Object left, Object right, EvaluationContext ctx) {
if (left == null || right == null) {
return null;
}
ClassIdentifierTuple identifierTuple = new ClassIdentifierTuple(params.getLeft(), params.getRight());
if (addFunctionsByClassesTuple.containsKey(identifierTuple)) {
return addFunctionsByClassesTuple.get(identifierTuple).apply(params, ctx);
} else {
return math(params.getLeft(), params.getRight(), ctx, (l, r) -> l.add(r, MathContext.DECIMAL128));

if (left instanceof Number) {
BigDecimal leftNumber = getBigDecimalOrNull(left);
return leftNumber != null && right instanceof Number ?
leftNumber.add(getBigDecimalOrNull(right), MathContext.DECIMAL128) :
null;
}

if (left instanceof String) {
return right instanceof String ? left + (String) right : null;
}
}

private Map<ClassIdentifierTuple, BiFunction<EvaluatedParameters, EvaluationContext, Object>> getAddFunctionsByClassesTuple() {
Map<ClassIdentifierTuple, BiFunction<EvaluatedParameters, EvaluationContext, Object>> toReturn = new HashMap<>();
toReturn.put(new ClassIdentifierTuple(String.class, String.class), (parameters, ctx) -> parameters.getLeft() + (String) parameters.getRight());
toReturn.put(new ClassIdentifierTuple(ChronoPeriod.class, ChronoPeriod.class), (parameters, ctx) ->
new ComparablePeriod(((ChronoPeriod) parameters.getLeft()).plus((ChronoPeriod) parameters.getRight())));
toReturn.put(new ClassIdentifierTuple(Duration.class, Duration.class), (parameters, ctx) ->
((Duration) parameters.getLeft()).plus((Duration) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(ZonedDateTime.class, ChronoPeriod.class), (parameters, ctx) ->
((ZonedDateTime) parameters.getLeft()).plus((ChronoPeriod) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(OffsetDateTime.class, ChronoPeriod.class), (parameters, ctx) ->
((OffsetDateTime) parameters.getLeft()).plus((ChronoPeriod) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(LocalDateTime.class, ChronoPeriod.class), (parameters, ctx) ->
((LocalDateTime) parameters.getLeft()).plus((ChronoPeriod) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(LocalDate.class, ChronoPeriod.class), (parameters, ctx) ->
((LocalDate) parameters.getLeft()).plus((ChronoPeriod) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(ZonedDateTime.class, Duration.class), (parameters, ctx) ->
((ZonedDateTime) parameters.getLeft()).plus((Duration) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(OffsetDateTime.class, Duration.class), (parameters, ctx) ->
((OffsetDateTime) parameters.getLeft()).plus((Duration) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(LocalDateTime.class, Duration.class), (parameters, ctx) ->
((LocalDateTime) parameters.getLeft()).plus((Duration) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(LocalDate.class, Duration.class), (parameters, ctx) ->
addLocalDateAndDuration((LocalDate) parameters.getLeft(), (Duration) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(ChronoPeriod.class, ZonedDateTime.class), (parameters, ctx) ->
((ZonedDateTime) parameters.getRight()).plus((ChronoPeriod) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(ChronoPeriod.class, OffsetDateTime.class), (parameters, ctx) ->
((OffsetDateTime) parameters.getRight()).plus((ChronoPeriod) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(ChronoPeriod.class, LocalDateTime.class), (parameters, ctx) ->
((LocalDateTime) parameters.getRight()).plus((ChronoPeriod) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(ChronoPeriod.class, LocalDate.class), (parameters, ctx) ->
((LocalDate) parameters.getRight()).plus((ChronoPeriod) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(Duration.class, ZonedDateTime.class), (parameters, ctx) ->
((ZonedDateTime) parameters.getRight()).plus((Duration) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(Duration.class, OffsetDateTime.class), (parameters, ctx) ->
((OffsetDateTime) parameters.getRight()).plus((Duration) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(Duration.class, LocalDateTime.class), (parameters, ctx) ->
((LocalDateTime) parameters.getRight()).plus((Duration) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(Duration.class, LocalDate.class), (parameters, ctx) ->
addLocalDateAndDuration((LocalDate) parameters.getRight(), (Duration) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(LocalTime.class, Duration.class), (parameters, ctx) ->
((LocalTime) parameters.getLeft()).plus((Duration) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(Duration.class, LocalTime.class), (parameters, ctx) ->
((LocalTime) parameters.getRight()).plus((Duration) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(OffsetTime.class, Duration.class), (parameters, ctx) ->
((OffsetTime) parameters.getLeft()).plus((Duration) parameters.getRight()));
toReturn.put(new ClassIdentifierTuple(Duration.class, OffsetTime.class), (parameters, ctx) ->
((OffsetTime) parameters.getRight()).plus((Duration) parameters.getLeft()));
toReturn.put(new ClassIdentifierTuple(Temporal.class, Temporal.class), (parameters, ctx) -> {
ctx.notifyEvt(() -> new InvalidParametersEvent(FEELEvent.Severity.ERROR, Msg.OPERATION_IS_UNDEFINED_FOR_PARAMETERS.getMask()));
return null;
});
return toReturn;
if (left instanceof Duration) {
if (right instanceof LocalDate) {
return addLocalDateAndDuration((LocalDate) right, (Duration) left);
}
if (right instanceof Duration) {
return ((Duration) left).plus((Duration) right);
}
}
if (right instanceof Duration && left instanceof LocalDate) {
return addLocalDateAndDuration((LocalDate) left, (Duration) right);
}

if (left instanceof Temporal) {
if (right instanceof TemporalAmount) {
return ((Temporal) left).plus((TemporalAmount) right);
}
} else if (left instanceof TemporalAmount) {
if (right instanceof Temporal) {
return ((Temporal) right).plus((TemporalAmount) left);
}
if (right instanceof ChronoPeriod) {
return ((ChronoPeriod) right).plus((TemporalAmount) left);
}
}

ctx.notifyEvt(() -> new InvalidParametersEvent(FEELEvent.Severity.ERROR, Msg.OPERATION_IS_UNDEFINED_FOR_PARAMETERS.getMask()));
return null;
}


}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,28 @@
*/
package org.kie.dmn.feel.lang.ast.infixexecutors;

import org.kie.dmn.api.feel.runtime.events.FEELEvent;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.ast.InfixOpNode;
import org.kie.dmn.feel.lang.types.impl.ComparablePeriod;
import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
import org.kie.dmn.feel.util.EvalHelper;
import org.kie.dmn.feel.util.Msg;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.chrono.ChronoPeriod;
import java.time.temporal.TemporalAmount;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

import org.kie.dmn.api.feel.runtime.events.FEELEvent;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.ast.InfixOpNode;
import org.kie.dmn.feel.lang.types.impl.ComparablePeriod;
import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;
import org.kie.dmn.feel.util.Msg;

import static org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.math;
import static org.kie.dmn.feel.util.EvalHelper.getBigDecimalOrNull;

public class DivExecutor implements InfixExecutor {

private static final DivExecutor INSTANCE = new DivExecutor();
private final Map<ClassIdentifierTuple, BiFunction<EvaluatedParameters, EvaluationContext, Object>> sumFunctionsByClassesTuple;

private DivExecutor() {
sumFunctionsByClassesTuple = getSumFunctionsByClassesTuple();
}

public static DivExecutor instance() {
Expand All @@ -53,50 +48,55 @@ public static DivExecutor instance() {

@Override
public Object evaluate(Object left, Object right, EvaluationContext ctx) {
return evaluate(new EvaluatedParameters(left, right), ctx);
return div(left, right, ctx);
}

@Override
public Object evaluate(InfixOpNode infixNode, EvaluationContext ctx) {
return evaluate(infixNode.getLeft().evaluate(ctx), infixNode.getRight().evaluate(ctx), ctx);
}

private Object evaluate(EvaluatedParameters params, EvaluationContext ctx) {
if (params.getLeft() == null || params.getRight() == null) {
private Object div(Object left, Object right, EvaluationContext ctx) {
if (left == null || right == null) {
return null;
}
ClassIdentifierTuple identifierTuple = new ClassIdentifierTuple(params.getLeft(), params.getRight());
if (sumFunctionsByClassesTuple.containsKey(identifierTuple)) {
return sumFunctionsByClassesTuple.get(identifierTuple).apply(params, ctx);
} else {
return math(params.getLeft(), params.getRight(), ctx, (l, r) -> l.divide(r, MathContext.DECIMAL128));
}
}

private Map<ClassIdentifierTuple, BiFunction<EvaluatedParameters, EvaluationContext, Object>> getSumFunctionsByClassesTuple() {
Map<ClassIdentifierTuple, BiFunction<EvaluatedParameters, EvaluationContext, Object>> toReturn = new HashMap<>();
toReturn.put(new ClassIdentifierTuple(Duration.class, Number.class), (parameters, ctx) -> {
final BigDecimal durationNumericValue = BigDecimal.valueOf(((Duration) parameters.getLeft()).toNanos());
final BigDecimal rightDecimal = BigDecimal.valueOf(((Number) parameters.getRight()).doubleValue());
return Duration.ofNanos(durationNumericValue.divide(rightDecimal, 0, RoundingMode.HALF_EVEN).longValue());
});
toReturn.put(new ClassIdentifierTuple(Number.class, TemporalAmount.class), (parameters, ctx) -> {
ctx.notifyEvt(() -> new InvalidParametersEvent(FEELEvent.Severity.ERROR, Msg.OPERATION_IS_UNDEFINED_FOR_PARAMETERS.getMask()));
if (left instanceof Number) {
if (right instanceof Number) {
return math(left, right, ctx, (l, r) -> l.divide(r, MathContext.DECIMAL128));
}
if (right instanceof TemporalAmount) {
ctx.notifyEvt(() -> new InvalidParametersEvent(FEELEvent.Severity.ERROR, Msg.OPERATION_IS_UNDEFINED_FOR_PARAMETERS.getMask()));
}
return null;
});
toReturn.put(new ClassIdentifierTuple(Duration.class, Duration.class), (parameters, ctx) ->
EvalHelper.getBigDecimalOrNull(((Duration) parameters.getLeft()).getSeconds()).divide(EvalHelper.getBigDecimalOrNull(((Duration) parameters.getRight()).getSeconds()), MathContext.DECIMAL128));
toReturn.put(new ClassIdentifierTuple(ChronoPeriod.class, Number.class), (parameters, ctx) -> {
final BigDecimal rightDecimal = EvalHelper.getBigDecimalOrNull(parameters.getRight());
if (rightDecimal.compareTo(BigDecimal.ZERO) == 0) {
ctx.notifyEvt(() -> new InvalidParametersEvent(FEELEvent.Severity.ERROR, Msg.DIVISION_BY_ZERO.getMask()));
return null;
} else {
return ComparablePeriod.ofMonths(EvalHelper.getBigDecimalOrNull(ComparablePeriod.toTotalMonths((ChronoPeriod) parameters.getLeft())).divide(rightDecimal, MathContext.DECIMAL128).intValue());
}

if (left instanceof Duration) {
if (right instanceof Number) {
final BigDecimal durationNumericValue = BigDecimal.valueOf(((Duration) left).toNanos());
final BigDecimal rightDecimal = BigDecimal.valueOf(((Number) right).doubleValue());
return Duration.ofNanos(durationNumericValue.divide(rightDecimal, 0, RoundingMode.HALF_EVEN).longValue());
}
if (right instanceof Duration) {
return getBigDecimalOrNull(((Duration) left).getSeconds()).divide(getBigDecimalOrNull(((Duration) right).getSeconds()), MathContext.DECIMAL128);
}
});
toReturn.put(new ClassIdentifierTuple(ChronoPeriod.class, ChronoPeriod.class), (parameters, ctx) ->
EvalHelper.getBigDecimalOrNull(ComparablePeriod.toTotalMonths((ChronoPeriod) parameters.getLeft())).divide(EvalHelper.getBigDecimalOrNull(ComparablePeriod.toTotalMonths((ChronoPeriod) parameters.getRight())), MathContext.DECIMAL128));
return toReturn;
}

if (left instanceof ChronoPeriod) {
if (right instanceof Number) {
final BigDecimal rightDecimal = getBigDecimalOrNull(right);
if (rightDecimal.compareTo(BigDecimal.ZERO) == 0) {
ctx.notifyEvt(() -> new InvalidParametersEvent(FEELEvent.Severity.ERROR, Msg.DIVISION_BY_ZERO.getMask()));
return null;
} else {
return ComparablePeriod.ofMonths(getBigDecimalOrNull(ComparablePeriod.toTotalMonths((ChronoPeriod) left)).divide(rightDecimal, MathContext.DECIMAL128).intValue());
}
}
if (right instanceof ChronoPeriod) {
return getBigDecimalOrNull(ComparablePeriod.toTotalMonths((ChronoPeriod) left)).divide(getBigDecimalOrNull(ComparablePeriod.toTotalMonths((ChronoPeriod) right)), MathContext.DECIMAL128);
}
}

return null;
}
}
Loading
Loading