Skip to content

Commit

Permalink
[KIE-781] Improve performance of InfixOpNode (#5634)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariofusco authored Dec 20, 2023
1 parent 044e4c0 commit 2094023
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 371 deletions.
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

0 comments on commit 2094023

Please sign in to comment.