Exemple #1
0
        /// <summary>
        /// Assigns the <paramref name="variable"/> with <see cref="Type"/> corresponding to the <paramref name="typeName"/>.
        /// When the variable already contains the type information, it must match with the <paramref name="typeName"/>
        /// </summary>
        /// <param name="variable">Variable to check or update. If null, function returns without an exception</param>
        /// <param name="typeName">Type name to assign. If null or empty, function returns without an exception (assuming that the type is not know yet)</param>
        /// <exception cref="DmnParserException">Type for variable don't match the one assigned before</exception>
        protected static void CheckAndUpdateVariableType(DmnVariableDefinition variable, string typeName)
        {
            if (string.IsNullOrWhiteSpace(typeName))
            {
                return;
            }
            if (variable == null)
            {
                return;
            }

            var parsedType = ParseTypeName(typeName);

            if (variable.Type == null)
            {
                //set (update)
                variable.SetRecognizedType(parsedType);
            }
            else
            {
                //check
                if (variable.Type != parsedType)
                {
                    throw Logger.Fatal <DmnParserException>($"Types for variable {variable.Name} don't match: {variable.Type} vs. {parsedType}");
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Validates the inputs of the <see cref="DmnModel"/> (<paramref name="source"/> and populates <see cref="InputData"/> and related <see cref="Variables"/>
        /// </summary>
        /// <param name="source">Source DMN Model parsed from XML</param>
        /// <param name="inputDataById">Temporary dictionary of input data (by ID) - input data are populated here</param>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="inputDataById"/> is null.</exception>
        /// <exception cref="DmnParserException">Missing input id</exception>
        /// <exception cref="DmnParserException">Duplicate input data name</exception>
        protected void ProcessInputDataSource(DmnModel source, IDictionary <string, DmnVariableDefinition> inputDataById)
        {
            if (source == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(source)} is null");
            }
            if (inputDataById == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(inputDataById)} is null");
            }
            if (source.InputData == null || source.InputData.Count == 0)
            {
                return;                                                         //it's not common, but OK to have no input data
            }
            //TODO    ?Input name in form varName:varType for (complex) input types
            foreach (var sourceInput in source.InputData)
            {
                if (string.IsNullOrWhiteSpace(sourceInput.Id))
                {
                    throw Logger.Fatal <DmnParserException>($"Missing input id");
                }

                var inputName    = !string.IsNullOrWhiteSpace(sourceInput.Name) ? sourceInput.Name : sourceInput.Id;
                var variableName = DmnVariableDefinition.NormalizeVariableName(inputName);
                if (InputData.ContainsKey(variableName))
                {
                    throw Logger.Fatal <DmnParserException>($"Duplicate input data name {variableName} (from {inputName})");
                }

                var variable = new DmnVariableDefinition(variableName, inputName);
                InputData.Add(variableName, variable);
                Variables.Add(variableName, variable);
                inputDataById.Add(sourceInput.Id, variable);
            }
        }
Exemple #3
0
        /// <summary>
        /// Validates the <paramref name="sourceDecision"/> within the <see cref="DmnModel"/>
        /// and creates <see cref="DmnExpressionDecision"/>
        /// </summary>
        /// <param name="sourceDecision">Decision source parsed from XML</param>
        /// <param name="decisionName">Unique name of decision</param>
        /// <param name="requiredInputs">Inputs required by decision</param>
        /// <param name="requiredDecisions">Decisions, the decision table depends on</param>
        ///<exception cref="ArgumentNullException"><paramref name="sourceDecision"/>, <paramref name="decisionName"/>, <paramref name="requiredInputs"/> or <paramref name="requiredDecisions"/> is null.</exception>
        ///<exception cref="ArgumentException"><paramref name="decisionName"/> is empty</exception>
        /// <exception cref="DmnParserException">Missing expression for expression decision</exception>
        /// <exception cref="DmnParserException">Missing output variable for expression decision</exception>
        protected DmnExpressionDecision CreateExpressionDecision(
            Decision sourceDecision,
            string decisionName,
            IReadOnlyCollection <IDmnVariable> requiredInputs,
            IReadOnlyCollection <IDmnDecision> requiredDecisions)
        {
            if (sourceDecision == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(sourceDecision)} is null");
            }
            if (decisionName == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(decisionName)} is null");
            }
            if (string.IsNullOrWhiteSpace(decisionName))
            {
                throw Logger.Fatal <ArgumentException>($"{nameof(decisionName)} is empty");
            }
            if (requiredInputs == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(requiredInputs)} is null");
            }
            if (requiredDecisions == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(requiredDecisions)} is null");
            }

            //prepare output variable
            if (sourceDecision.OutputVariable == null)
            {
                throw Logger.Fatal <DmnParserException>($"Missing output variable for expression decision {decisionName}");
            }
            var sourceVariableName = !string.IsNullOrWhiteSpace(sourceDecision.OutputVariable.Name)
                ? sourceDecision.OutputVariable.Name
                : sourceDecision.OutputVariable.Id;
            var variableName = DmnVariableDefinition.NormalizeVariableName(sourceVariableName);

            if (!Variables.TryGetValue(variableName, out var variable))
            {
                //create variable
                variable = new DmnVariableDefinition(variableName);
                Variables.Add(variableName, variable);
            }

            variable.AddValueSetter($"Expression Decision {decisionName}");
            CheckAndUpdateVariableType(variable, sourceDecision.OutputVariable.TypeRef);

            //prepare expression
            if (string.IsNullOrWhiteSpace(sourceDecision.Expression.Text))
            {
                throw Logger.Fatal <DmnParserException>($"Missing expression for expression decision {decisionName}");
            }
            var expr = $"{sourceDecision.Expression.Text}";

            //create
            var expressionDecision = new DmnExpressionDecision(decisionName, expr, variable, requiredInputs, requiredDecisions);

            return(expressionDecision);
        }
Exemple #4
0
        /// <summary>
        /// Validates the inputs of the <see cref="DmnModel"/> (<paramref name="source"/> and populates <see cref="InputData"/> and related <see cref="Variables"/>
        /// </summary>
        /// <param name="source">Source DMN Model parsed from XML</param>
        /// <param name="inputDataById">Temporary dictionary of input data (by ID) - input data are populated here</param>
        ///<exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="inputDataById"/> is null.</exception>
        /// <exception cref="DmnParserException">Missing input id</exception>
        /// <exception cref="DmnParserException">Duplicate input data name</exception>
        protected void ProcessInputDataSource(DmnModel source, IDictionary <string, DmnVariableDefinition> inputDataById)
        {
            if (source == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(source)} is null");
            }
            if (inputDataById == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(inputDataById)} is null");
            }
            if (source.InputData == null || source.InputData.Count == 0)
            {
                return;                                                         //it's not common, but OK to have no input data
            }
            var inputExpressionTypeByNames = source.Decisions?.Where(d => d.DecisionTable?.Inputs != null).SelectMany(d => d.DecisionTable?.Inputs)?
                                             .ToDictionary(i => string.IsNullOrEmpty(i.InputExpression?.Text) ? i.Label : i.InputExpression.Text, i => i.InputExpression?.TypeRef) ?? new Dictionary <string, string>();

            //TODO    ?Input name in form varName:varType for (complex) input types
            //TODO ?Required input parameters check for null??
            foreach (var sourceInput in source.InputData)
            {
                if (string.IsNullOrWhiteSpace(sourceInput.Id))
                {
                    throw Logger.Fatal <DmnParserException>($"Missing input id");
                }

                var inputName    = !string.IsNullOrWhiteSpace(sourceInput.Name) ? sourceInput.Name : sourceInput.Id;
                var variableName = NormalizeVariableName(inputName);
                if (InputData.ContainsKey(variableName))
                {
                    throw Logger.Fatal <DmnParserException>($"Duplicate input data name {variableName} (from {inputName})");
                }

                var variable = new DmnVariableDefinition(variableName)
                {
                    IsInputParameter = true, HasValueSetter = true
                };

                // try to evaluate variable type from expression type
                if (inputExpressionTypeByNames.TryGetValue(variableName, out var expressionType))
                {
                    CheckAndUpdateVariableType(variable, expressionType);
                }

                variable.ValueSetters.Add($"Input: {inputName}");
                InputData.Add(variableName, variable);
                Variables.Add(variableName, variable);
                inputDataById.Add(sourceInput.Id, variable);
            }
        }
Exemple #5
0
        /// <summary>
        /// Validates the <paramref name="sourceTable"/> within the <see cref="DmnModel"/>
        /// and creates <see cref="DmnDecisionTable"/>
        /// </summary>
        /// <param name="sourceTable">Decision source parsed from XML</param>
        /// <param name="decisionName">Unique name of decision</param>
        /// <param name="requiredInputs">Inputs required by decision</param>
        /// <param name="requiredDecisions">Decisions, the decision table depends on</param>
        ///<exception cref="ArgumentNullException"><paramref name="sourceTable"/>, <paramref name="decisionName"/>, <paramref name="requiredInputs"/> or <paramref name="requiredDecisions"/> is null.</exception>
        ///<exception cref="ArgumentException"><paramref name="decisionName"/> is empty</exception>
        /// <exception cref="DmnParserException">No outputs for decision table</exception>
        /// <exception cref="DmnParserException">No inputs for decision table</exception>
        /// <exception cref="DmnParserException">Input variable for decision table not found</exception>
        /// <exception cref="DmnParserException">No rules for decision table</exception>
        /// <exception cref="DmnParserException">Number of rule input entries doesn't match the number of inputs for decision table</exception>
        /// <exception cref="DmnParserException">Number of rule output entries doesn't match the number of outputs for decision table</exception>
        protected DmnDecisionTable CreateDecisionTable(
            DecisionTable sourceTable,
            string decisionName,
            List <IDmnVariable> requiredInputs,
            List <IDmnDecision> requiredDecisions)
        {
            if (sourceTable == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(sourceTable)} is null");
            }
            if (decisionName == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(decisionName)} is null");
            }
            if (string.IsNullOrWhiteSpace(decisionName))
            {
                throw Logger.Fatal <ArgumentException>($"{nameof(decisionName)} is empty");
            }
            if (requiredInputs == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(requiredInputs)} is null");
            }
            if (requiredDecisions == null)
            {
                throw Logger.Fatal <ArgumentNullException>($"{nameof(requiredDecisions)} is null");
            }

            //prepare outputs
            if (sourceTable.Outputs == null || sourceTable.Outputs.Count < 1)
            {
                throw Logger.Fatal <DmnParserException>($"No outputs for decision table {decisionName}");
            }

            var outputs   = new List <DmnDecisionTableOutput>();
            var outputIdx = 0;

            foreach (var sourceOutput in sourceTable.Outputs)
            {
                //Label, Name, Id
                var sourceVariableName =
                    !string.IsNullOrWhiteSpace(sourceOutput.Label) ? sourceOutput.Label :
                    !string.IsNullOrWhiteSpace(sourceOutput.Name) ? sourceOutput.Name :
                    sourceOutput.Id;

                var variableName = DmnVariableDefinition.NormalizeVariableName(sourceVariableName);
                if (!Variables.TryGetValue(variableName, out var variable))
                {
                    //create variable
                    variable = new DmnVariableDefinition(variableName);
                    Variables.Add(variableName, variable);
                }

                variable.AddValueSetter($"Table Decision {decisionName}");
                CheckAndUpdateVariableType(variable, sourceOutput.TypeRef);

                var allowedValues = sourceOutput.AllowedOutputValues?.Values;
                outputs.Add(new DmnDecisionTableOutput(outputIdx, variable, allowedValues));
                outputIdx++;
            }

            //prepare inputs
            if (sourceTable.Inputs == null || sourceTable.Inputs.Count < 1)
            {
                throw Logger.Fatal <DmnParserException>($"No inputs for decision table {decisionName}");
            }

            var inputs   = new List <DmnDecisionTableInput>();
            var inputIdx = 0;

            foreach (var sourceInput in sourceTable.Inputs)
            {
                //Expression or  variable by Label,  Id
                DmnVariableDefinition variable = null;
                string expression = null;

                if (string.IsNullOrWhiteSpace(sourceInput.InputExpression?.Text))
                {
                    var sourceVariableName = !string.IsNullOrWhiteSpace(sourceInput.Label) ? sourceInput.Label : sourceInput.Id;
                    var variableName       = DmnVariableDefinition.NormalizeVariableName(sourceVariableName);

                    if (!Variables.TryGetValue(variableName, out variable))
                    {
                        throw Logger.Fatal <DmnParserException>(
                                  $"Input variable {sourceVariableName} ({variableName}) for decision table {decisionName} not found");
                    }

                    CheckAndUpdateVariableType(variable, sourceInput.InputExpression?.TypeRef);
                }
                else
                {
                    expression = sourceInput.InputExpression.Text;
                }

                var allowedValues = sourceInput.AllowedInputValues?.Values;
                inputs.Add(new DmnDecisionTableInput(inputIdx, variable, expression, allowedValues));
                inputIdx++;
            }

            //prepare rules
            if (sourceTable.Rules == null || sourceTable.Rules.Count < 1)
            {
                throw Logger.Fatal <DmnParserException>($"No rules for decision table {decisionName}");
            }

            var rules   = new List <DmnDecisionTableRule>();
            var ruleIdx = 0;

            foreach (var sourceRule in sourceTable.Rules)
            {
                if (sourceRule.InputEntries == null || sourceRule.InputEntries.Count != inputs.Count)
                {
                    throw Logger.Fatal <DmnParserException>($"Number of input entries doesn't match the number of inputs for decision table {decisionName}. Rule Id:{sourceRule.Id} ");
                }

                if (sourceRule.OutputEntries == null || sourceRule.OutputEntries.Count != outputs.Count)
                {
                    throw Logger.Fatal <DmnParserException>($"Number of output entries doesn't match the number of outputs for decision table {decisionName}. Rule Id:{sourceRule.Id} ");
                }

                var ruleInputs  = new List <DmnDecisionTableRuleInput>();
                var ruleOutputs = new List <DmnDecisionTableRuleOutput>();
                var ruleName    = string.IsNullOrWhiteSpace(sourceRule.Label) ? sourceRule.Id : sourceRule.Label;

                //inputs
                var ruleInputIdx = 0;
                foreach (var sourceRuleInputEntry in sourceRule.InputEntries)
                {
                    if (!string.IsNullOrWhiteSpace(sourceRuleInputEntry.Text) && sourceRuleInputEntry.Text != "-") //don't create rule inputs with no condition - can be also represented as single dash
                    {
                        var ruleInput = new DmnDecisionTableRuleInput(inputs[ruleInputIdx], sourceRuleInputEntry.Text);
                        ruleInputs.Add(ruleInput);
                    }
                    ruleInputIdx++;
                }

                //outputs
                var ruleOutputIdx = 0;
                foreach (var sourceRuleOutputEntry in sourceRule.OutputEntries)
                {
                    var ruleOutput = new DmnDecisionTableRuleOutput(outputs[ruleOutputIdx], sourceRuleOutputEntry.Text);
                    ruleOutputs.Add(ruleOutput);
                    ruleOutputIdx++;
                }

                rules.Add(new DmnDecisionTableRule(ruleIdx, ruleName, ruleInputs.ToArray(), ruleOutputs.ToArray(), sourceRule.Description));
                ruleIdx++;
            }

            var decisionTable = new DmnDecisionTable(
                decisionName,
                sourceTable.HitPolicy, sourceTable.Aggregation,
                inputs.ToArray(), outputs.ToArray(), rules.ToArray(),
                requiredInputs, requiredDecisions);

            return(decisionTable);
        }