git_bsmd/nsw/Source/bsmd.database/ValidationCondition.cs

438 lines
14 KiB
C#

// Copyright (c) 2015-2017 schick Informatik
// Description: Validierungsbedingung, die (als Gruppe) als Validierungsregel serialisiert und
// ausgewertet wird
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using log4net;
using System.Collections.ObjectModel;
using System.Collections;
using System.Windows.Data;
using System.ComponentModel;
namespace bsmd.database
{
[Serializable]
public class ValidationCondition : ICloneable
{
private static ILog log = LogManager.GetLogger(typeof(ValidationCondition));
public ValidationCondition() { }
public enum ConditionOperatorEnum
{
[Description("==")]
EQUAL,
[Description("!=")]
NOT_EQUAL,
[Description(">")]
GREATER,
[Description("<")]
LESS,
[Description("IS NULL")]
NULL,
[Description("IS NOT NULL")]
NOT_NULL
}
#region Properties
public string FieldName { get; set; }
public ConditionOperatorEnum ConditionOperator { get; set; }
public string Value { get; set; }
public string ErrorMessage { get; set; }
#endregion
#region static methods
public static bool? CheckConditions(MessageCore core, List<Message> messages, ConditionGroup cGroup, out string message)
{
bool? result = null;
message = "";
bool conditionResult = false;
try
{
foreach (ValidationCondition condition in cGroup.Conditions)
{
object[] otherargs = null;
// switch case type
string errorMessage = condition.ErrorMessage;
if (otherargs != null)
errorMessage = string.Format(errorMessage, otherargs);
result = ValidationCondition.CombineConditions(result, conditionResult, cGroup.GroupOperator);
// Fehlermeldung hinzufügen falls Bedingung verletzt wurde
if (!conditionResult && ((cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.AND) || (cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.OR)))
message += string.Format("- {0}{1}", errorMessage, Environment.NewLine);
//if (conditionResult && ((cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.NAND) || (cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.NOR)))
// message += string.Format("- {0}{1}", errorMessage, Environment.NewLine);
}
// check internal groups recursively
foreach (ConditionGroup subGroup in cGroup.SubGroups)
{
string subMessage = "";
bool? subResult = ValidationCondition.CheckConditions(core, messages, subGroup, out subMessage);
if (!subResult.HasValue) // an error occurred evaluating this element
{
return subResult;
}
else
{
result = ValidationCondition.CombineConditions(result, subResult.Value, cGroup.GroupOperator);
// Fehlermeldung hinzufügen falls Bedingung verletzt wurde
if (!subResult.Value && ((cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.AND) || (cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.OR)))
message += string.Format("- {0}{1}", subMessage, Environment.NewLine);
//if (subResult.Value && ((cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.NAND) || (cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.NOR)))
// message += string.Format("- {0}{1}", subMessage, Environment.NewLine);
}
}
// falls in einer "OR" Gruppe eine Bedingung fehlschlägt aber die Condition nach true evaluiert
// wird die Fehlermeldung zwecks Verwirrungsvermeidung gedroppt. Dasselbe natürl. auch für NAND
if ((cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.OR) && result.Value) message = "";
//if ((cGroup.GroupOperator == ConditionGroup.GroupOperatorEnum.NAND) && !result.Value) message = "";
}
catch (Exception ex)
{
message = string.Format("Validation threw exception: {0}", ex.ToString());
log.Error(message);
return false;
}
return result;
}
#region Serialization
public static ConditionGroup LoadFromString(string serializedConditions)
{
if ((serializedConditions == null) ||
(serializedConditions.Trim().Length == 0))
return null;
using (StringReader sr = new StringReader(serializedConditions))
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(ConditionGroup));
ConditionGroup container = serializer.Deserialize(sr) as ConditionGroup;
container.CopyToObservable();
return container;
}
catch (Exception)
{
return null;
}
}
}
public static string SaveToString(ConditionGroup group)
{
if (group == null) return null;
using (StringWriter sw = new StringWriter())
{
try
{
Type theType = Type.GetType("bsmd.database.ConditionGroup, bsmd.database");
XmlSerializer serializer = new XmlSerializer(theType);
group.CopyToXML();
serializer.Serialize(sw, group);
return sw.ToString();
}
catch (Exception)
{
return null;
}
}
}
#endregion
/// <summary>
/// evaluate logical group operator
/// </summary>
private static bool CombineConditions(bool? c1, bool c2, ConditionGroup.GroupOperatorEnum op)
{
if (!c1.HasValue) return c2;
switch (op)
{
case ConditionGroup.GroupOperatorEnum.AND:
return c1.Value & c2;
case ConditionGroup.GroupOperatorEnum.OR:
return c1.Value | c2;
case ConditionGroup.GroupOperatorEnum.NOT:
return !(c1.Value);
case ConditionGroup.GroupOperatorEnum.XOR:
return (c1.Value ^ c2);
}
return false;
}
private static bool IsConditionTrue(string val1, string val2, ValidationCondition.ConditionOperatorEnum condition)
{
switch (condition)
{
case ValidationCondition.ConditionOperatorEnum.EQUAL:
return val1.Equals(val2);
default:
return !val1.Equals(val2);
}
}
private static bool IsConditionTrue(bool val1, bool val2, ValidationCondition.ConditionOperatorEnum condition)
{
switch (condition)
{
case ValidationCondition.ConditionOperatorEnum.EQUAL:
return val1.Equals(val2);
default:
return !val1.Equals(val2);
}
}
private static bool IsConditionTrue(DateTime val1, DateTime val2, ValidationCondition.ConditionOperatorEnum condition)
{
switch (condition)
{
case ValidationCondition.ConditionOperatorEnum.EQUAL:
return val1 == val2;
case ValidationCondition.ConditionOperatorEnum.GREATER:
return val1 > val2;
case ValidationCondition.ConditionOperatorEnum.LESS:
return val1 < val2;
case ValidationCondition.ConditionOperatorEnum.NOT_EQUAL:
return val1 != val2;
}
return true;
}
private static bool IsConditionTrue(TimeSpan val1, TimeSpan val2, ValidationCondition.ConditionOperatorEnum condition)
{
switch (condition)
{
case ValidationCondition.ConditionOperatorEnum.EQUAL:
return val1 == val2;
case ValidationCondition.ConditionOperatorEnum.GREATER:
return val1 > val2;
case ValidationCondition.ConditionOperatorEnum.LESS:
return val1 < val2;
case ValidationCondition.ConditionOperatorEnum.NOT_EQUAL:
return val1 != val2;
}
return true;
}
private static bool IsConditionTrue(double val1, double val2, ValidationCondition.ConditionOperatorEnum condition)
{
switch (condition)
{
case ValidationCondition.ConditionOperatorEnum.EQUAL:
return val1 == val2;
case ValidationCondition.ConditionOperatorEnum.GREATER:
return val1 > val2;
case ValidationCondition.ConditionOperatorEnum.LESS:
return val1 < val2;
case ValidationCondition.ConditionOperatorEnum.NOT_EQUAL:
return val1 != val2;
}
return true;
}
#endregion
#region ICloneable implementation
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
#region class ConditionGroup
/// <summary>
/// Diese Klasse benötigt (blöderweise) für eine Sache 2 Collections: einmal für die XML Serialisierung und einmal
/// für die Hierarchie im WPF Treecontrol (ObservableCollection). Daher müssen die Inhalte beim Laden und Speichern hin und herkopiert
/// werden. Was für ein Scheiß.
/// </summary>
[Serializable]
public class ConditionGroup
{
public enum GroupOperatorEnum
{
AND,
OR,
NOT,
XOR
}
public ConditionGroup() {
Conditions = new ObservableCollection<ValidationCondition>();
SubGroups = new ObservableCollection<ConditionGroup>();
XMLConditions = new List<ValidationCondition>();
XMLSubGroups = new List<ConditionGroup>();
}
#region Properties
[XmlIgnore]
public ObservableCollection<ValidationCondition> Conditions
{
get;
set;
}
public List<ValidationCondition> XMLConditions { get; set; }
public GroupOperatorEnum GroupOperator
{
get;
set;
}
[XmlIgnore]
public ObservableCollection<ConditionGroup> SubGroups
{
get;
set;
}
public List<ConditionGroup> XMLSubGroups { get; set; }
[XmlIgnore]
public IList Children
{
get
{
return new CompositeCollection()
{
new CollectionContainer() { Collection = SubGroups },
new CollectionContainer() { Collection = Conditions }
};
}
}
public string Name
{
get { return this.GroupOperator.ToString(); }
}
#endregion
#region public recursive methods
public void Remove(ConditionGroup group)
{
if(this.SubGroups.Contains(group))
{
this.SubGroups.Remove(group);
return;
}
// try recursive
foreach (ConditionGroup subGroup in this.SubGroups)
subGroup.Remove(group);
}
public void RemoveCondition(ValidationCondition condition)
{
if(this.Conditions.Contains(condition))
{
this.Conditions.Remove(condition);
return;
}
// try recursive
foreach (ConditionGroup subGroup in this.SubGroups)
subGroup.RemoveCondition(condition);
}
public void CopyToObservable()
{
this.SubGroups.Clear();
foreach (ConditionGroup cg in this.XMLSubGroups)
this.SubGroups.Add(cg);
this.Conditions.Clear();
foreach (ValidationCondition vc in this.XMLConditions)
this.Conditions.Add(vc);
foreach (ConditionGroup subGroup in this.XMLSubGroups)
subGroup.CopyToObservable();
}
public void CopyToXML()
{
this.XMLSubGroups.Clear();
this.XMLSubGroups.AddRange(this.SubGroups);
this.XMLConditions.Clear();
this.XMLConditions.AddRange(this.Conditions);
foreach (ConditionGroup subGroup in this.SubGroups)
subGroup.CopyToXML();
}
#endregion
}
#endregion
#region class ValidationField
/// <summary>
/// Diese Klasse wird beim Bearbeiten im Regel-Editor verwendet (ENI-2)
/// </summary>
public class ValidationField
{
public string PropertyName { get; set; }
public string NotificationClassText { get; set; }
public Message.NotificationClass NotificationClass { get; set; }
public bool IsInListType { get; set; }
public override string ToString()
{
return string.Format("{0}.{1}", NotificationClassText, PropertyName);
}
}
#endregion
#region struct KeyValuePairS
[Serializable]
[XmlType(TypeName = "schickKeyValuePair")]
public struct KeyValuePairS<K, V>
{
public K Key { get; set; }
public V Value { get; set; }
}
#endregion
}