All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hisp.dhis.rules.RuleEngine Maven / Gradle / Ivy

package org.hisp.dhis.rules;

import org.hisp.dhis.antlr.Parser;
import org.hisp.dhis.rules.models.*;
import org.hisp.dhis.rules.parser.expression.CommonExpressionVisitor;
import org.hisp.dhis.rules.utils.RuleEngineUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.Callable;

import static org.hisp.dhis.antlr.AntlrParserUtils.castClass;
import static org.hisp.dhis.rules.parser.expression.ParserUtils.FUNCTION_FOR_DESCRIPTION;

// ToDo: logging
public final class RuleEngine
{
    private static final Logger log = LoggerFactory.getLogger( RuleEngine.class.getName() );

    @Nonnull
    private final RuleEngineContext ruleEngineContext;

    @Nonnull
    private final List ruleEvents;

    @Nullable
    private final RuleEnrollment ruleEnrollment;

    @Nullable
    private TriggerEnvironment triggerEnvironment;

    RuleEngine( @Nonnull RuleEngineContext ruleEngineContext,
        @Nonnull List ruleEvents,
        @Nullable RuleEnrollment ruleEnrollment, @Nullable TriggerEnvironment triggerEnvironment )
    {
        this.ruleEngineContext = ruleEngineContext;
        this.ruleEvents = ruleEvents;
        this.ruleEnrollment = ruleEnrollment;
        this.triggerEnvironment = triggerEnvironment;
    }

    @Nonnull
    public List events()
    {
        return ruleEvents;
    }

    @Nullable
    public RuleEnrollment enrollment()
    {
        return ruleEnrollment;
    }

    @Nullable
    public TriggerEnvironment triggerEnvironment()
    {
        return triggerEnvironment;
    }

    @Nonnull
    public RuleEngineContext executionContext()
    {
        return ruleEngineContext;
    }

    @Nonnull
    public Callable> evaluate( @Nonnull RuleEvent ruleEvent )
    {
        return evaluate( ruleEvent, ruleEngineContext.rules() );
    }

    @Nonnull
    public Callable> evaluate( @Nonnull RuleEvent ruleEvent, @Nonnull List rulesToEvaluate )
    {
        if ( ruleEvent == null )
        {
            throw new IllegalArgumentException( "ruleEvent == null" );
        }

        Map valueMap = RuleVariableValueMapBuilder.target( ruleEvent )
            .ruleVariables( ruleEngineContext.ruleVariables() )
            .ruleEnrollment( ruleEnrollment )
            .triggerEnvironment( triggerEnvironment )
            .ruleEvents( ruleEvents )
            .constantValueMap( ruleEngineContext.constantsValues() )
            .build();

        return new RuleEngineExecution( ruleEvent, rulesToEvaluate, valueMap, ruleEngineContext.supplementaryData() );
    }

    @Nonnull
    public Callable> evaluate( @Nonnull RuleEnrollment ruleEnrollment,
        @Nonnull List rulesToEvaluate )
    {
        if ( ruleEnrollment == null )
        {
            throw new IllegalArgumentException( "ruleEnrollment == null" );
        }

        Map valueMap = RuleVariableValueMapBuilder.target( ruleEnrollment )
            .ruleVariables( ruleEngineContext.ruleVariables() )
            .triggerEnvironment( triggerEnvironment )
            .ruleEvents( ruleEvents )
            .constantValueMap( ruleEngineContext.constantsValues() )
            .build();

        return new RuleEngineExecution( ruleEnrollment, rulesToEvaluate, valueMap, ruleEngineContext.supplementaryData() );
    }

    @Nonnull
    public Callable> evaluate()
    {
        RuleVariableValueMap valueMap = RuleVariableValueMapBuilder.target()
            .ruleVariables( ruleEngineContext.ruleVariables() )
            .ruleEnrollment( ruleEnrollment )
            .triggerEnvironment( triggerEnvironment )
            .ruleEvents( ruleEvents )
            .constantValueMap( ruleEngineContext.constantsValues() )
            .multipleBuild();

        return new RuleEngineMultipleExecution( ruleEngineContext.rules(), valueMap,
            ruleEngineContext.supplementaryData() );
    }

    @Nonnull
    public Callable> evaluate( @Nonnull RuleEnrollment ruleEnrollment )
    {
        return evaluate( ruleEnrollment, ruleEngineContext.rules() );
    }

    @Nonnull
    public RuleValidationResult evaluate( String expression )
    {
        // Rule condition expression should be evaluated against Boolean
        return getExpressionDescription( expression, Boolean.class );
    }

    @Nonnull
    public RuleValidationResult evaluateDataFieldExpression( String expression )
    {
        // Rule action data field field should be evaluated against all i.e Boolean, String, Date and Numerical value
        return getExpressionDescription( expression, null );
    }

    private RuleValidationResult getExpressionDescription( String expression, Class klass )
    {
        Map itemDescriptions = new HashMap<>();

        CommonExpressionVisitor visitor = CommonExpressionVisitor.newBuilder()
                .withIteamStore( ruleEngineContext.getDataItemStore() )
                .withFunctionMethod( FUNCTION_FOR_DESCRIPTION )
                .withFunctionMap( RuleEngineUtils.FUNCTIONS )
                .withItemDescriptions( itemDescriptions )
                .validateAndBuildForDescription();

        RuleValidationResult result;

        try
        {
            if ( klass == null )
            {
                Parser.visit( expression, visitor );
            }
            else
            {
                castClass( klass, Parser.visit( expression, visitor ) );
            }

            String description = expression;

            for ( Map.Entry entry : itemDescriptions.entrySet() )
            {
                description = description.replace( entry.getKey(), entry.getValue() );
            }

            result = RuleValidationResult.builder().isValid( true ).description( description ).build();
        }
        catch ( IllegalStateException e )
        {
            result = RuleValidationResult.builder().isValid( false )
                    .errorMessage( e.getMessage() )
                    .exception( e )
                    .build();
            log.debug( e.getMessage(), e );
        }

        return result;
    }

    public static class Builder
    {
        @Nonnull
        private final RuleEngineContext ruleEngineContext;

        @Nullable
        private List ruleEvents;

        @Nullable
        private RuleEnrollment ruleEnrollment;

        @Nullable
        private TriggerEnvironment triggerEnvironment;

        Builder( @Nonnull RuleEngineContext ruleEngineContext )
        {
            this.ruleEngineContext = ruleEngineContext;
        }

        @Nonnull
        public Builder events( @Nonnull List ruleEvents )
        {
            if ( ruleEvents == null )
            {
                throw new IllegalArgumentException( "ruleEvents == null" );
            }

            this.ruleEvents = Collections.unmodifiableList( new ArrayList<>( ruleEvents ) );
            return this;
        }

        @Nonnull
        public Builder enrollment( @Nonnull RuleEnrollment ruleEnrollment )
        {
            if ( ruleEnrollment == null )
            {
                throw new IllegalArgumentException( "ruleEnrollment == null" );
            }

            this.ruleEnrollment = ruleEnrollment;
            return this;
        }

        @Nonnull
        public Builder triggerEnvironment( @Nonnull TriggerEnvironment triggerEnvironment )
        {
            if ( triggerEnvironment == null )
            {
                throw new IllegalArgumentException( "triggerEnvironment == null" );
            }

            this.triggerEnvironment = triggerEnvironment;
            return this;
        }

        @Nonnull
        public RuleEngine build()
        {
            if ( ruleEvents == null )
            {
                ruleEvents = Collections.unmodifiableList( new ArrayList() );
            }

            return new RuleEngine( ruleEngineContext, ruleEvents, ruleEnrollment, triggerEnvironment );
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy