// // Class: RuleEngine // Current CLR: 4.0.30319.34209 // System: Microsoft Visual Studio 10.0 // Author: dani // Created: 9/7/2015 8:16:42 AM // // Copyright (c) 2015 Informatikbüro Daniel Schick. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.IO; using System.Reflection; using System.Text.RegularExpressions; using log4net; namespace bsmd.database { public class RuleEngine { private static ILog log = LogManager.GetLogger(typeof(RuleEngine)); private static Dictionary errorTextList = null; private static Dictionary violationTextList = null; private Dictionary> errorDict = new Dictionary>(); private Dictionary> violationDict = new Dictionary>(); public RuleEngine() { if (RuleEngine.errorTextList == null) RuleEngine.errorTextList = DBManager.Instance.LoadErrorTexts(); if (RuleEngine.violationTextList == null) RuleEngine.violationTextList = DBManager.Instance.LoadViolationTexts(); } public Dictionary> ErrorDict { get { return this.errorDict; } } public Dictionary> ViolationDict { get { return this.violationDict; } } #region public static property validation /// /// Test function checks decorated properties on an entity for errors (only errors, violations cannot /// happen here) /// /// /// public static void ValidateProperties(DatabaseEntity entity, List errors) { Type objType = entity.GetType(); Type attribType = typeof(Validation1Attribute); if (entity.GetValidationBlock() == DatabaseEntity.ValidationBlock.BLOCK2) attribType = typeof(Validation2Attribute); List props = new List(); // add "generic" validation properties to check list props.AddRange(objType.GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(ValidationAttribute)))); // add "block" validation properties to check list props.AddRange(objType.GetProperties().Where( prop => Attribute.IsDefined(prop, attribType)) ); foreach (PropertyInfo property in props) { object propValue = property.GetValue(entity, null); string value = (propValue == null) ? string.Empty : propValue.ToString(); ValidationCode validationCode = ValidationCode.NONE; if (Attribute.IsDefined(property, attribType)) { if (entity.GetValidationBlock() == DatabaseEntity.ValidationBlock.BLOCK1) { Validation1Attribute validationAttribute = Attribute.GetCustomAttribute(property, typeof(Validation1Attribute)) as Validation1Attribute; validationCode = validationAttribute.Code; } else { Validation2Attribute validationAttribute = Attribute.GetCustomAttribute(property, typeof(Validation2Attribute)) as Validation2Attribute; validationCode = validationAttribute.Code; } } if (validationCode == ValidationCode.NONE) { if (Attribute.IsDefined(property, typeof(ValidationAttribute))) { ValidationAttribute validationAttribute = Attribute.GetCustomAttribute(property, typeof(ValidationAttribute)) as ValidationAttribute; validationCode = validationAttribute.Code; } } // check properties switch (validationCode) { case ValidationCode.NOT_NULL: if (value.Length == 0) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); break; case ValidationCode.LOCODE: { Regex rgx = new Regex("[A-Z]{2}[A-Z0-9]{3}"); if (!rgx.IsMatch(value)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.LOCODE_GER: { // TODO: Jan nach der Liste anhauen Regex rgx = new Regex("[A-Z]{2}[A-Z0-9]{3}"); if (!rgx.IsMatch(value)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.INT_GT_ZERO: { int intVal = 0; if (!Int32.TryParse(value, out intVal) || intVal <= 0) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.DOUBLE_GT_ZERO: { double dVal = 0; if (!Double.TryParse(value, out dVal) || dVal <= 0) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.GISIS: { Regex rgx = new Regex("[0-9]{4}"); if (!rgx.IsMatch(value)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.FLAG_CODE: { Regex rgx = new Regex("[A-Z]{2}"); if(!rgx.IsMatch(value)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.TWO_DIGIT: { Regex rgx = new Regex("[0-9]{2}"); if (!rgx.IsMatch(value)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; default: break; } } } #endregion #region public methods /// /// Diese Funktion wird für Nachrichtenklassen (MDH, SEC,.. usw.) aufgerufen. Error in eingebetteten /// Klassen werden dann der Nachrichtenklasse zugeordnet (können dann logischerweise mehrfach auftreten) /// public void Validate(DatabaseEntity entity) { if (!(entity is Message)) return; List errors = new List(); List violations = new List(); this.errorDict[entity] = errors; this.violationDict[entity] = violations; foreach (DatabaseEntity derivedEntity in ((Message)entity).Elements) { // individuelle Fehler nach Nachrichtenklasse prüfen derivedEntity.MessageCore = entity.MessageCore; // some instance we need info from core (NOA / Transit) RuleEngine.ValidateProperties(derivedEntity, errors); derivedEntity.Validate(errors, violations); } foreach (MessageError error in errors) { error.MessageHeaderId = entity.Id.Value; DBManager.Instance.Save(error); } foreach (MessageViolation violation in violations) { violation.MessageHeaderId = entity.Id.Value; DBManager.Instance.Save(violation); } log.InfoFormat("Msg Id {0} Type {1} has {2} errors and {3} violations", entity.Id, entity.MessageNotificationClass, errors.Count, violations.Count); } #endregion #region private helper internal static MessageError CreateError(ValidationCode validationCode, string p, string value) { MessageError error = new MessageError(); error.ErrorCode = (int)validationCode; if (errorTextList.ContainsKey((int)validationCode)) { error.ErrorText = string.Format(errorTextList[(int)validationCode], p, value); } else { error.ErrorText = p; if (value != null) error.ErrorText += " - " + value; } return error; } internal static MessageViolation CreateViolation(ValidationCode validationCode, string p, string value) { MessageViolation violation = new MessageViolation(); violation.ViolationCode = (int)validationCode; if (violationTextList.ContainsKey((int)validationCode)) { violation.ViolationText = string.Format(violationTextList[(int)validationCode], p, value); } else { violation.ViolationText = p; if (value != null) violation.ViolationText += " - " + value; } return violation; } #endregion } }