/// <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}"); } } }
/// <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); } }
/// <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); }
/// <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); } }
/// <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); }