// // 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 { #region german LOCODE's static definition private static List gerLocodeList = new List() { "DE001", "DEAND", "DEBDF", "DEBZR", "DEBMK", "DEBKE", "DEBRE", "DEBRV", "DEBRB", "DE651", "DEBUM", "DEBUZ", "DECUX", "DEDMG", "DEDTM", "DEDUI", "DEDUS", "DEECK", "DEELS", "DEEME", "DEEMM", "DEFLF", "DEGEK", "DEGLU", "DEGRD", "DEHAM", "DEHHF", "DEHGL", "DEHUS", "DEITZ", "DEKEL", "DE136", "DEKCH", "DEKLE", "DECGN", "DEKRE", "DE241", "DELEE", "DELEW", "DELIS", "DELBC", "DELBM", "DE002", "DEMHG", "DEMOZ", "DEMUK", "DEMUH", "DENSS", "DENHO", "DENHA", "DEOLO", "DETRD", "DEPAP", "DEPEF", "DEPUT", "DEREC", "DEREN", "DERHB", "DERSK", "DESAS", "DESTL", "DEUCK", "DE585", "DEVRD", "DEWED", "DEWVN", "DEWIS", "DE003", "DEWOL", "DEWYK" }; #endregion 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; int maxlen = 0; 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; maxlen = validationAttribute.MaxLen; } } // 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: { if(!RuleEngine.gerLocodeList.Contains(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; case ValidationCode.STRING_EXACT_LEN: { if (value.Length != maxlen) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.STRING_MAXLEN: { if (value.Length > maxlen) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.STRING_IMOCLASS: { Regex rgx = new Regex(@"[1-9]{1}(\.[1-9]{1})?"); if (!rgx.IsMatch(value)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.STRING_UNNUMBER: { Regex rgx = new Regex("[0-9]{4}"); if (!rgx.IsMatch(value)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.DRAUGHT_IMPLAUSIBLE: { double dVal = 0; if (!Double.TryParse(value, out dVal) || dVal <= 0) errors.Add(RuleEngine.CreateError(ValidationCode.DOUBLE_GT_ZERO, property.Name, value)); else if ((dVal < 20) || (dVal > 150)) errors.Add(RuleEngine.CreateError(validationCode, property.Name, value)); } break; case ValidationCode.TIME_IMPLAUSIBLE: { DateTime aTime; if (value.Length == 0) errors.Add(RuleEngine.CreateError(ValidationCode.NOT_NULL, property.Name, value)); if (DateTime.TryParse(value, out aTime)) { if ((aTime - DateTime.UtcNow).Minutes > 30) 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); if (errors.Count > 0) { ((Message)entity).InternalStatus = Message.BSMDStatus.ERROR; DBManager.Instance.Save(entity); } else if (violations.Count > 0) { ((Message)entity).InternalStatus = Message.BSMDStatus.VIOLATION; DBManager.Instance.Save(entity); } } #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 } }