using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using Artemis.Core.Modules; namespace Artemis.Core.DataModelExpansions { public abstract class DataModel { private readonly Dictionary _dynamicDataModels = new Dictionary(); /// /// Gets the plugin info this data model belongs to /// [DataModelIgnore] public PluginInfo PluginInfo { get; internal set; } /// /// Gets the describing this data model /// [DataModelIgnore] public DataModelPropertyAttribute DataModelDescription { get; internal set; } /// /// Gets the is expansion status indicating whether this data model expands the main data model /// [DataModelIgnore] public bool IsExpansion { get; internal set; } /// /// Gets an read-only dictionary of all dynamic data models /// [DataModelIgnore] public ReadOnlyDictionary DynamicDataModels => new ReadOnlyDictionary(_dynamicDataModels); /// /// Returns a read-only collection of all properties in this datamodel that are to be ignored /// /// public ReadOnlyCollection GetHiddenProperties() { if (PluginInfo.Instance is ProfileModule profileModule) return profileModule.HiddenProperties; if (PluginInfo.Instance is BaseDataModelExpansion dataModelExpansion) return dataModelExpansion.HiddenProperties; return new List().AsReadOnly(); } /// /// Adds a dynamic data model to this data model /// /// The dynamic data model to add /// The key of the child, must be unique to this data model /// An optional name, if not provided the key will be used in a humanized form /// An optional description public void AddDynamicChild(DataModel dynamicDataModel, string key, string name = null, string description = null) { if (_dynamicDataModels.ContainsKey(key)) { throw new ArtemisCoreException($"Cannot add a dynamic data model with key '{key}' " + "because the key is already in use on this data model."); } if (_dynamicDataModels.ContainsValue(dynamicDataModel)) { var existingKey = _dynamicDataModels.First(kvp => kvp.Value == dynamicDataModel).Key; throw new ArtemisCoreException($"Cannot add a dynamic data model with key '{key}' " + $"because the dynamic data model is already added with key '{existingKey}."); } _dynamicDataModels.Add(key, dynamicDataModel); } /// /// Removes a dynamic data model from the data model by its key /// /// The key of the dynamic data model to remove public void RemoveDynamicChildByKey(string key) { _dynamicDataModels.Remove(key); } /// /// Removes a dynamic data model from this data model /// /// The dynamic data model to remove public void RemoveDynamicChild(DataModel dynamicDataModel) { var keys = _dynamicDataModels.Where(kvp => kvp.Value == dynamicDataModel).Select(kvp => kvp.Key).ToList(); foreach (var key in keys) _dynamicDataModels.Remove(key); } /// /// Gets a dynamic data model of type by its key /// /// The type of data model you expect /// The unique key of the dynamic data model /// If found, the dynamic data model public T GetDynamicChild(string key) where T : DataModel { _dynamicDataModels.TryGetValue(key, out var value); return value as T; } internal bool ContainsPath(string path) { var parts = path.Split('.'); var current = GetType(); foreach (var part in parts) { var property = current?.GetProperty(part); current = property?.PropertyType; if (property == null) return false; } return true; } internal Type GetTypeAtPath(string path) { if (!ContainsPath(path)) return null; var parts = path.Split('.'); var current = GetType(); Type result = null; foreach (var part in parts) { var property = current.GetProperty(part); current = property.PropertyType; result = property.PropertyType; } return result; } internal Type GetListTypeInPath(string path) { if (!ContainsPath(path)) return null; var parts = path.Split('.'); var current = GetType(); var index = 0; foreach (var part in parts) { // Only return a type if the path CONTAINS a list, not if it points TO a list if (index == parts.Length - 1) return null; var property = current.GetProperty(part); // For lists, look into the list type instead of the list itself if (typeof(IList).IsAssignableFrom(property.PropertyType)) return property.PropertyType.GetGenericArguments()[0]; current = property.PropertyType; index++; } return null; } internal Type GetListTypeAtPath(string path) { if (!ContainsPath(path)) return null; var child = GetTypeAtPath(path); return child.GenericTypeArguments.Length > 0 ? child.GenericTypeArguments[0] : null; } } }