#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved //----------------------------------------------------------------------------- // Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved // Web: https://www.technosoftware.com // // The source code in this file is covered under a dual-license scenario: // - Owner of a purchased license: SCLA 1.0 // - GPL V3: everybody else // // SCLA license terms accompanied with this source code. // See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf // // GNU General Public License as published by the Free Software Foundation; // version 3 of the License are accompanied with this source code. // See https://technosoftware.com/license/GPLv3License.txt // // This source code is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. //----------------------------------------------------------------------------- #endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved #region Using Directives using System; using System.Xml; using System.Collections; using System.Text; #endregion namespace Technosoftware.DaAeHdaClient { /// /// Defines various functions used to convert types. /// public class OpcConvert { #region Public Methods /// /// Checks whether the array contains any useful data. /// public static bool IsValid(Array array) { return (array != null && array.Length > 0); } /// /// Checks whether the array contains any useful data. /// public static bool IsEmpty(Array array) { return (array == null || array.Length == 0); } /// /// Checks whether the string contains any useful data. /// public static bool IsValid(string target) { return !string.IsNullOrEmpty(target); } /// /// Checks whether the string contains any useful data. /// public static bool IsEmpty(string target) { return string.IsNullOrEmpty(target); } /// /// Performs a deep copy of an object if possible. /// public static object Clone(object source) { if (source == null) return null; if (source.GetType().IsValueType) return source; if (source.GetType().IsArray || source.GetType() == typeof(Array)) { var array = (Array)((Array)source).Clone(); for (var ii = 0; ii < array.Length; ii++) { array.SetValue(Clone(array.GetValue(ii)), ii); } return array; } try { return ((ICloneable)source).Clone(); } catch { throw new NotSupportedException("Object cannot be cloned."); } } /// /// Does a deep comparison between two objects. /// public static bool Compare(object a, object b) { if (a == null || b == null) return (a == null && b == null); var type1 = a.GetType(); var type2 = b.GetType(); if (type1 != type2) return false; if (type1.IsArray && type2.IsArray) { var array1 = (Array)a; var array2 = (Array)b; if (array1.Length != array2.Length) return false; for (var ii = 0; ii < array1.Length; ii++) { if (!Compare(array1.GetValue(ii), array2.GetValue(ii))) return false; } return true; } return a.Equals(b); } /// /// Converts an object to the specified type and returns a deep copy. /// public static object ChangeType(object source, Type newType) { // check for null source object. if (source == null) { if (newType != null && newType.IsValueType) { return Activator.CreateInstance(newType); } return null; } // check for null type or 'object' type. if (newType == null || newType == typeof(object) || newType == source.GetType()) { return Clone(source); } var type = source.GetType(); // convert between array types. if (type.IsArray && newType.IsArray) { var array = new ArrayList(((Array)source).Length); foreach (var element in (Array)source) { array.Add(ChangeType(element, newType.GetElementType())); } return array.ToArray(newType.GetElementType() ?? throw new InvalidOperationException()); } // convert scalar value to an array type. if (!type.IsArray && newType.IsArray) { var array = new ArrayList(1) {ChangeType(source, newType.GetElementType())}; return array.ToArray(newType.GetElementType() ?? throw new InvalidOperationException()); } // convert single element array type to scalar type. if (type.IsArray && !newType.IsArray && ((Array)source).Length == 1) { return Convert.ChangeType(((Array)source).GetValue(0), newType); } // convert array type to string. if (type.IsArray && newType == typeof(string)) { var buffer = new StringBuilder(); buffer.Append("{ "); var count = 0; foreach (var element in (Array)source) { buffer.AppendFormat("{0}", ChangeType(element, typeof(string))); count++; if (count < ((Array)source).Length) { buffer.Append(" | "); } } buffer.Append(" }"); return buffer.ToString(); } // convert to enumerated type. if (newType.IsEnum) { if (type == typeof(string)) { // check for an integer passed as a string. if (((string)source).Length > 0 && char.IsDigit((string)source, 0)) { return Enum.ToObject(newType, Convert.ToInt32(source)); } // parse a string value. return Enum.Parse(newType, (string)source); } else { // convert numerical value to an enum. return Enum.ToObject(newType, source); } } // convert to boolean type. if (newType == typeof(bool)) { // check for an integer passed as a string. if (source is string text) { if (text.Length > 0 && (text[0] == '+' || text[0] == '-' || char.IsDigit(text, 0))) { return Convert.ToBoolean(Convert.ToInt32(source)); } } return Convert.ToBoolean(source); } // use default conversion. return Convert.ChangeType(source, newType); } /// /// Formats an item or property value as a string. /// public static string ToString(object source) { // check for null if (source == null) return ""; var type = source.GetType(); // check for invalid values in date times. if (type == typeof(DateTime)) { if (((DateTime)source) == DateTime.MinValue) { return string.Empty; } var date = (DateTime)source; if (date.Millisecond > 0) { return date.ToString("yyyy-MM-dd HH:mm:ss.fff"); } else { return date.ToString("yyyy-MM-dd HH:mm:ss"); } } // use only the local name for qualified names. if (type == typeof(XmlQualifiedName)) { return ((XmlQualifiedName)source).Name; } // use only the name for system types. if (type.FullName == "System.RuntimeType") { return ((Type)source).Name; } // treat byte arrays as a special case. if (type == typeof(byte[])) { var bytes = (byte[])source; var buffer = new StringBuilder(bytes.Length * 3); foreach (var character in bytes) { buffer.Append(character.ToString("X2")); buffer.Append(" "); } return buffer.ToString(); } // show the element type and length for arrays. if (type.IsArray) { return $"{type.GetElementType()?.Name}[{((Array)source).Length}]"; } // instances of array are always treated as arrays of objects. if (type == typeof(Array)) { return $"Object[{((Array)source).Length}]"; } // default behavior. return source.ToString(); } /// /// Tests if the specified string matches the specified pattern. /// public static bool Match(string target, string pattern, bool caseSensitive) { // an empty pattern always matches. if (string.IsNullOrEmpty(pattern)) { return true; } // an empty string never matches. if (string.IsNullOrEmpty(target)) { return false; } // check for exact match if (caseSensitive) { if (target == pattern) { return true; } } else { if (target.ToLower() == pattern.ToLower()) { return true; } } var pIndex = 0; var tIndex = 0; while (tIndex < target.Length && pIndex < pattern.Length) { var p = ConvertCase(pattern[pIndex++], caseSensitive); if (pIndex > pattern.Length) { return (tIndex >= target.Length); // if end of string true } char c; switch (p) { // match zero or more char. case '*': { while (tIndex < target.Length) { if (Match(target.Substring(tIndex++), pattern.Substring(pIndex), caseSensitive)) { return true; } } return Match(target, pattern.Substring(pIndex), caseSensitive); } // match any one char. case '?': { // check if end of string when looking for a single character. if (tIndex >= target.Length) { return false; } // check if end of pattern and still string data left. if (pIndex >= pattern.Length && tIndex < target.Length - 1) { return false; } tIndex++; break; } // match char set case '[': { c = ConvertCase(target[tIndex++], caseSensitive); if (tIndex > target.Length) { return false; // syntax } var l = '\0'; // match a char if NOT in set [] if (pattern[pIndex] == '!') { ++pIndex; p = ConvertCase(pattern[pIndex++], caseSensitive); while (pIndex < pattern.Length) { if (p == ']') // if end of char set, then { break; // no match found } if (p == '-') { // check a range of chars? p = ConvertCase(pattern[pIndex], caseSensitive); // get high limit of range if (pIndex > pattern.Length || p == ']') { return false; // syntax } if (c >= l && c <= p) { return false; // if in range, return false } } l = p; if (c == p) // if char matches this element { return false; // return false } p = ConvertCase(pattern[pIndex++], caseSensitive); } } // match if char is in set [] else { p = ConvertCase(pattern[pIndex++], caseSensitive); while (pIndex < pattern.Length) { if (p == ']') // if end of char set, then no match found { return false; } if (p == '-') { // check a range of chars? p = ConvertCase(pattern[pIndex], caseSensitive); // get high limit of range if (pIndex > pattern.Length || p == ']') { return false; // syntax } if (c >= l && c <= p) { break; // if in range, move on } } l = p; if (c == p) // if char matches this element move on { break; } p = ConvertCase(pattern[pIndex++], caseSensitive); } while (pIndex < pattern.Length && p != ']') // got a match in char set skip to end of set { p = pattern[pIndex++]; } } break; } // match digit. case '#': { c = target[tIndex++]; if (!char.IsDigit(c)) { return false; // not a digit } break; } // match exact char. default: { c = ConvertCase(target[tIndex++], caseSensitive); if (c != p) // check for exact char { return false; // not a match } // check if end of pattern and still string data left. if (pIndex >= pattern.Length && tIndex < target.Length - 1) { return false; } break; } } } return true; } #endregion #region Private Methods private static char ConvertCase(char c, bool caseSensitive) { return (caseSensitive) ? c : char.ToUpper(c); } #endregion } }