// Copyright (c) 2015-2017 schick Informatik // Description: Klasse kapselt eine vom ENI-2 verwendete Validierungsregel using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Reflection; using log4net; namespace bsmd.database { public class ValidationRule : DatabaseEntity { private static readonly ILog _log = LogManager.GetLogger("ValidationRule"); private static List _validationFields = null; private static Dictionary _validationFieldDict = null; private static readonly Dictionary _lookupDict = new Dictionary(); private static readonly Dictionary> _lookupListDict = new Dictionary>(); public ValidationRule() { this.tablename = "[dbo].[ValidationRule]"; } #region Properties public string Name { get; set; } public string Context { get; set; } public string Rule { get; set; } public DateTime Created { get; set; } public string CreatedBy { get; set; } public DateTime? Changed { get; set; } public string ChangedBy { get; set; } public bool? IsActive { get; set; } #endregion #region static Properties /// /// Validierungsregeln werden aus diesen Feldern konstruiert. Mit dieser Liste wird die Auswahlbox befüllt. /// public static List ValidationFields { get { if(_validationFields == null) { _validationFields = new List(); _validationFieldDict = new Dictionary(); _validationFields.AddRange(GetFieldsForClass(typeof(AGNT), false, Message.NotificationClass.AGNT)); _validationFields.AddRange(GetFieldsForClass(typeof(ATA), false, Message.NotificationClass.ATA)); _validationFields.AddRange(GetFieldsForClass(typeof(ATD), false, Message.NotificationClass.ATD)); _validationFields.AddRange(GetFieldsForClass(typeof(BRKA), true, Message.NotificationClass.BKRA)); _validationFields.AddRange(GetFieldsForClass(typeof(BRKD), true, Message.NotificationClass.BKRD)); _validationFields.AddRange(GetFieldsForClass(typeof(BPOL), false, Message.NotificationClass.BPOL)); _validationFields.AddRange(GetFieldsForClass(typeof(CallPurpose), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(CREW), true, Message.NotificationClass.CREW)); _validationFields.AddRange(GetFieldsForClass(typeof(Customer), false, null)); _validationFields.AddRange(GetFieldsForClass(typeof(HAZ), false, Message.NotificationClass.HAZA)); _validationFields.AddRange(GetFieldsForClass(typeof(IBCPosition), true, null, "HAZA")); _validationFields.AddRange(GetFieldsForClass(typeof(IGCPosition), true, null, "HAZA")); _validationFields.AddRange(GetFieldsForClass(typeof(IMDGPosition), true, null, "HAZA")); _validationFields.AddRange(GetFieldsForClass(typeof(IMSBCPosition), true, null, "HAZA")); _validationFields.AddRange(GetFieldsForClass(typeof(MARPOL_Annex_I_Position), true, null, "HAZA")); _validationFields.AddRange(GetFieldsForClass(typeof(HAZ), false, Message.NotificationClass.HAZD)); _validationFields.AddRange(GetFieldsForClass(typeof(IBCPosition), true, null, "HAZD")); _validationFields.AddRange(GetFieldsForClass(typeof(IGCPosition), true, null, "HAZD")); _validationFields.AddRange(GetFieldsForClass(typeof(IMDGPosition), true, null, "HAZD")); _validationFields.AddRange(GetFieldsForClass(typeof(IMSBCPosition), true, null, "HAZD")); _validationFields.AddRange(GetFieldsForClass(typeof(MARPOL_Annex_I_Position), true, null, "HAZD")); _validationFields.AddRange(GetFieldsForClass(typeof(InfectedArea), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(INFO), false, Message.NotificationClass.INFO)); _validationFields.AddRange(GetFieldsForClass(typeof(LADG), true, Message.NotificationClass.LADG)); _validationFields.AddRange(GetFieldsForClass(typeof(LastTenPortFacilitiesCalled), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(MDH), false, Message.NotificationClass.MDH)); _validationFields.AddRange(GetFieldsForClass(typeof(NAME), false, Message.NotificationClass.NAME)); _validationFields.AddRange(GetFieldsForClass(typeof(NOA_NOD), false, Message.NotificationClass.NOA_NOD)); _validationFields.AddRange(GetFieldsForClass(typeof(PAS), true, Message.NotificationClass.PAS)); _validationFields.AddRange(GetFieldsForClass(typeof(POBA), false, Message.NotificationClass.POBA)); _validationFields.AddRange(GetFieldsForClass(typeof(POBD), false, Message.NotificationClass.POBD)); _validationFields.AddRange(GetFieldsForClass(typeof(PortArea), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(PortOfCallLast30Days), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(PortOfCallLast30DaysCrewJoinedShip), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(PortOfItinerary),true ,null)); _validationFields.AddRange(GetFieldsForClass(typeof(PRE72H), false, Message.NotificationClass.PRE72H)); _validationFields.AddRange(GetFieldsForClass(typeof(ReportingParty), false, null)); _validationFields.AddRange(GetFieldsForClass(typeof(SanitaryMeasuresDetail), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(SEC), false, Message.NotificationClass.SEC)); _validationFields.AddRange(GetFieldsForClass(typeof(SERV), true, Message.NotificationClass.SERV)); _validationFields.AddRange(GetFieldsForClass(typeof(ShipToShipActivitiesDuringLastTenPortFacilitiesCalled), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(STAT), false, Message.NotificationClass.STAT)); _validationFields.AddRange(GetFieldsForClass(typeof(STO), true, Message.NotificationClass.STO)); _validationFields.AddRange(GetFieldsForClass(typeof(StowawaysJoiningLocation), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(SubsidiaryRisks), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(TIEFA), false, Message.NotificationClass.TIEFA)); _validationFields.AddRange(GetFieldsForClass(typeof(TIEFD), false, Message.NotificationClass.TIEFD)); _validationFields.AddRange(GetFieldsForClass(typeof(TOWA), true, Message.NotificationClass.TOWA)); _validationFields.AddRange(GetFieldsForClass(typeof(TOWD), true, Message.NotificationClass.TOWD)); _validationFields.AddRange(GetFieldsForClass(typeof(WAS), false, Message.NotificationClass.WAS)); _validationFields.AddRange(GetFieldsForClass(typeof(Waste), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(WasteDisposalServiceProvider), true, null)); _validationFields.AddRange(GetFieldsForClass(typeof(MessageCore), false, null)); // Ergebnis sortiert nach Klassenname - Propertyname _validationFields.Sort(delegate (ValidationField c1, ValidationField c2) { int result = c1.NotificationClassText.CompareTo(c2.NotificationClassText); if (result == 0) result = c1.PropertyName.CompareTo(c2.PropertyName); return result; }); // Dictionary aufladen foreach (ValidationField vf in _validationFields) _validationFieldDict[vf.FullName] = vf; } return _validationFields; } } public static Dictionary ValidationFieldDict { get { return _validationFieldDict; } } #endregion #region private static private static List GetFieldsForClass(Type objType, bool isList, Message.NotificationClass? notificationClass, string extraInfo = null) { List result = new List(); var props = objType.GetProperties().Where( prop => Attribute.IsDefined(prop, typeof(ENI2ValidationAttribute))); foreach (PropertyInfo property in props) { ValidationField vField = new ValidationField(property); vField.NotificationClassText = objType.Name; vField.PropertyName = property.Name; vField.IsInListType = isList; vField.NotificationClass = notificationClass; vField.ExtraInfo = extraInfo; result.Add(vField); } return result; } /// /// Hilfsmethode, die eine Liste von object der untergeordneten Listenklasse zurückliefert. Da es hier verschiedene Ebene/Namen etc. gibt /// wird das einfach über den NotificationClassText aufgelöst, der beim Erzeugen der ValidationFields angelegt wird (aus dem Klassennamen) /// Da nicht klar ist in welcher Meldeklasse das Objekt steckt ist das hier der Einfachheit halber fest verdrahtet /// private static List GetListForSublistField(ValidationField field, Dictionary messageDict) { List result = null; switch(field.NotificationClassText) { case "CallPurpose": { NOA_NOD nn = messageDict[Message.NotificationClass.NOA_NOD].Elements[0] as NOA_NOD; result = nn.CallPurposes.Cast().ToList(); break; } case "IBCPosition": { HAZ haz = (field.ExtraInfo == "HAZA") ? (messageDict[Message.NotificationClass.HAZA].Elements[0] as HAZ) : (messageDict[Message.NotificationClass.HAZD].Elements[0] as HAZ); result = haz.IBCPositions.Cast().ToList(); break; } case "IGCPosition": { HAZ haz = (field.ExtraInfo == "HAZA") ? (messageDict[Message.NotificationClass.HAZA].Elements[0] as HAZ) : (messageDict[Message.NotificationClass.HAZD].Elements[0] as HAZ); result = haz.IGCPositions.Cast().ToList(); break; } case "IMDGPosition": { HAZ haz = (field.ExtraInfo == "HAZA") ? (messageDict[Message.NotificationClass.HAZA].Elements[0] as HAZ) : (messageDict[Message.NotificationClass.HAZD].Elements[0] as HAZ); result = haz.IMDGPositions.Cast().ToList(); break; } case "IMSBCPosition": { HAZ haz = (field.ExtraInfo == "HAZA") ? (messageDict[Message.NotificationClass.HAZA].Elements[0] as HAZ) : (messageDict[Message.NotificationClass.HAZD].Elements[0] as HAZ); result = haz.IMSBCPositions.Cast().ToList(); break; } case "MARPOL_Annex_I_Position": { HAZ haz = (field.ExtraInfo == "HAZA") ? (messageDict[Message.NotificationClass.HAZA].Elements[0] as HAZ) : (messageDict[Message.NotificationClass.HAZD].Elements[0] as HAZ); result = haz.MARPOLPositions.Cast().ToList(); break; } case "InfectedArea": { MDH mdh = messageDict[Message.NotificationClass.MDH].Elements[0] as MDH; result = mdh.InfectedAreas.Cast().ToList(); break; } case "SanitaryMeasuresDetail": { MDH mdh = messageDict[Message.NotificationClass.MDH].Elements[0] as MDH; result = mdh.SanitaryMeasuresDetails.Cast().ToList(); break; } case "PortOfCallLast30Days": { MDH mdh = messageDict[Message.NotificationClass.MDH].Elements[0] as MDH; result = mdh.PortOfCallLast30Days.Cast().ToList(); break; } case "StowawaysJoiningLocation": { MDH mdh = messageDict[Message.NotificationClass.MDH].Elements[0] as MDH; result = mdh.StowawaysJoiningLocations.Cast().ToList(); break; } case "LastTenPortFacilitiesCalled": { SEC sec = messageDict[Message.NotificationClass.SEC].Elements[0] as SEC; result = sec.LastTenPortFacilitesCalled.Cast().ToList(); break; } case "ShipToShipActivitiesDuringLastTenPortFacilitiesCalled": { SEC sec = messageDict[Message.NotificationClass.SEC].Elements[0] as SEC; result = sec.ShipToShipActivitiesDuringLastTenPortFacilitiesCalled.Cast().ToList(); break; } case "Waste": { WAS was = messageDict[Message.NotificationClass.WAS].Elements[0] as WAS; result = was.Waste.Cast().ToList(); break; } case "WasteDisposalServiceProvider": { WAS was = messageDict[Message.NotificationClass.WAS].Elements[0] as WAS; result = was.WasteDisposalServiceProvider.Cast().ToList(); break; } case "PortOfItinerary": { BPOL bpol = messageDict[Message.NotificationClass.BPOL].Elements[0] as BPOL; result = bpol.PortOfItineraries.Cast().ToList(); break; } default: _log.WarnFormat("unknown list class {0}", field.NotificationClassText); break; } return result; } #endregion #region public static methods public static object GetValueFromData(ValidationField field) { if (_lookupDict.ContainsKey(field)) return field.PropertyInfo.GetValue(_lookupDict[field]); return null; } public static List GetValueListFromData(ValidationField field) { List result = new List(); if(_lookupListDict.ContainsKey(field)) { foreach (object o in _lookupListDict[field]) result.Add(field.PropertyInfo.GetValue(o)); } return result; } public static void PrepareNameLookupDict(MessageCore core, List messages) { _lookupDict.Clear(); _lookupListDict.Clear(); Dictionary messageLookup = new Dictionary(); foreach (Message message in messages) { messageLookup[message.MessageNotificationClass] = message; } try { foreach (ValidationField field in _validationFields) { if (!field.IsInListType) { if (field.NotificationClass.HasValue) { _lookupDict.Add(field, messageLookup[field.NotificationClass.Value].Elements[0]); } else { // "fixed" type wie Core / Customer usw. switch(field.NotificationClassText) { case "Customer": _lookupDict.Add(field, core.Customer); break; case "MessageCore": _lookupDict.Add(field, core); break; case "ReportingParty": if(core.DefaultReportingPartyId.HasValue) _lookupDict.Add(field, DBManager.Instance.GetReportingPartyDict()[core.DefaultReportingPartyId.Value]); break; default: _log.WarnFormat("Unknown class type {0}", field.NotificationClassText); break; } } } else { if (field.NotificationClass.HasValue) { // Feld gehört zu einer Liste auf "Top-Level" Ebene if(messageLookup.ContainsKey(field.NotificationClass.Value)) _lookupListDict.Add(field, messageLookup[field.NotificationClass.Value].Elements.Cast().ToList()); } else { // Feld gehört zu einer untergeordneten Liste // Trickster: Komplizierte Logik vermeiden und den Namen statisch hinterlegen? _lookupListDict.Add(field, GetListForSublistField(field, messageLookup)); } } } } catch(Exception ex) { _log.ErrorFormat("Error preparing lookup dict: {0}", ex.Message); } } #endregion #region overrides public override string ToString() { return this.Name; } #endregion #region DatabaseEntity implementation public override List LoadList(IDataReader reader) { List result = new List(); while (reader.Read()) { ValidationRule vr = new ValidationRule(); vr.id = reader.GetGuid(0); if (!reader.IsDBNull(1)) vr.Name = reader.GetString(1); if (!reader.IsDBNull(2)) vr.Context = reader.GetString(2); if (!reader.IsDBNull(3)) vr.Rule = reader.GetString(3); if (!reader.IsDBNull(4)) vr.Changed = reader.GetDateTime(4); if (!reader.IsDBNull(5)) vr.ChangedBy = reader.GetString(5); if (!reader.IsDBNull(6)) vr.Created = reader.GetDateTime(6); if (!reader.IsDBNull(7)) vr.CreatedBy = reader.GetString(7); if (!reader.IsDBNull(8)) vr.IsActive = reader.GetBoolean(8); result.Add(vr); } reader.Close(); return result; } public override void PrepareLoadCommand(IDbCommand cmd, Message.LoadFilter filter, params object[] criteria) { string query = string.Format("SELECT Id, Name, Context, [Rule], Changed, ChangedBy, Created, CreatedBy, IsActive FROM {0} ORDER BY Name", this.Tablename); switch (filter) { case Message.LoadFilter.ALL: default: break; } cmd.CommandText = query; } public override void PrepareSave(IDbCommand cmd) { SqlCommand scmd = cmd as SqlCommand; scmd.Parameters.AddWithNullableValue("@P1", this.Name); scmd.Parameters.AddWithNullableValue("@P2", this.Context); scmd.Parameters.AddWithNullableValue("@P3", this.Rule); scmd.Parameters.AddWithNullableValue("@P4", this.Changed); scmd.Parameters.AddWithNullableValue("@P5", this.ChangedBy); scmd.Parameters.AddWithNullableValue("@P6", this.Created); scmd.Parameters.AddWithNullableValue("@P7", this.CreatedBy); scmd.Parameters.AddWithNullableValue("@P8", this.IsActive); if (this.IsNew) { this.CreateId(); scmd.Parameters.AddWithValue("@ID", this.Id); scmd.CommandText = string.Format("INSERT INTO {0} (Id, Name, Context, [Rule], Changed, ChangedBy, Created, CreatedBy, IsActive) " + "VALUES ( @ID, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8 )", this.Tablename); } else { scmd.Parameters.AddWithValue(@"ID", this.Id); scmd.CommandText = string.Format("UPDATE {0} SET Name = @P1, Context = @P2, [Rule] = @P3, Changed = @P4, ChangedBy = @P5, " + "Created = @P6, CreatedBy = @P7, IsActive = @P8 WHERE Id = @ID", this.Tablename); } } #endregion } }