// // ObjectModel.cs - stores user-friendly, DataFormat-agnostic in-memory representation // // Author: // Michael Becker // // Copyright (c) 2011-2020 Mike Becker's Software // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program 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. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System; using System.Collections.Generic; using MBS.Framework; namespace UniversalEditor { /// /// The in-memory representation of data serialized to and from an using a particular . /// public abstract class ObjectModel : ICloneable, References, ISupportsExtraData { /// /// Represents a collection of objects. /// public class ObjectModelCollection : System.Collections.ObjectModel.Collection { } /// /// Creates a for this and registers it for future use. /// /// The that provides metadata and other information about this . public ObjectModelReference MakeReference() { ObjectModelReference omr = MakeReferenceInternal(); ObjectModelReference.Register(omr); return omr; } /// /// Creates a new . The returned is not cached. It is recommended that subclasses /// override this method and cache their own personal instances of containing the appropriate metadata for their /// subclassed implementations. /// /// The that provides metadata and other information about this . protected virtual ObjectModelReference MakeReferenceInternal() { ObjectModelReference omr = new ObjectModelReference(GetType()); return omr; } /// /// The that was last used to read or write this . /// /// The accessor. [Obsolete("ObjectModels should be Accessor-agnostic and not rely on being able to communicate with the Accessor"), NonSerializedProperty] public Accessor Accessor { get; set; } /// /// Clears all data from this and returns it to a pristine state. /// public abstract void Clear(); /// /// Copies all data from this to the specified . /// /// The into which to copy the data of this . /// The conversion between this and the given is not supported. public abstract void CopyTo(ObjectModel where); /// /// Copies all data from this to the specified . /// /// The into which to copy the data of this . /// When false, the method is called on the destination before the copy is performed. /// The conversion between this and the given is not supported. public void CopyTo(ObjectModel where, bool append) { if (!append) where.Clear(); CopyTo(where); } protected virtual CriteriaResult[] FindInternal(CriteriaQuery query) { return null; } public CriteriaResult[] Find(CriteriaQuery query) { return FindInternal(query); } /// /// Copies all data from the given into this . /// /// The from which to copy the data. /// The conversion between this and the given is not supported. public void CopyFrom(ObjectModel where) { where.CopyTo(this); } /// /// Copies all data from the given into this . /// /// The from which to copy the data. /// When false, the method is called on this before the copy is performed. /// The conversion between this and the given is not supported. public void CopyFrom(ObjectModel where, bool append) { if (!append) Clear(); CopyFrom(where); } /// /// Creates a clone of this and returns it. This is normally implemented as creating a new instance of this /// , then calling the original instance's method passing in the new instance as the target. /// /// The cloned . public object Clone() { Type type = this.GetType(); ObjectModel clone = (type.Assembly.CreateInstance(type.FullName) as ObjectModel); CopyTo(clone); return clone; } /// /// Performs a simple find and replace on the public properties of this using reflection. For a more in-depth find and replace /// solution, individual authors should annotate individual parameters that should participate in the find-and-replace feature. /// /// The string to search for. /// The string with which the found string should be replaced. public virtual void Replace(string FindWhat, string ReplaceWith) { Type type = GetType(); System.Reflection.PropertyInfo[] pis = type.GetProperties(System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); foreach (System.Reflection.PropertyInfo pi in pis) { object obj = pi.GetValue(this, null); if (obj is string) { string str = (obj as string); pi.SetValue(this, str.Replace(FindWhat, ReplaceWith), null); } } } private Dictionary> _customProperties = new Dictionary>(); // TODO: should this use the MBS.Framework "ISupportsExtraData" interface? Or is this specifically UE functionality (with DataFormatReference)? /// /// Gets the value of the custom property with the specified name for the given . If no custom property with the given /// name is registered for the specified , return the value specified as . /// /// The value of the custom property with the specified name for the given . /// The for which to look up custom properties. /// The name of the custom property to search for. /// The value that should be returned if no custom property with the given name is registered for the specified . /// The type of custom property that should be returned. public T GetCustomProperty(DataFormatReference dfr, string name, T defaultValue = default(T)) { if (_customProperties.ContainsKey(dfr)) { if (_customProperties[dfr].ContainsKey(name)) { return (T)_customProperties[dfr][name]; } } return defaultValue; } /// /// Gets the value of the custom property with the specified name for the given . If no custom property with the given /// name is registered for the specified , return the value specified as . /// /// The value of the custom property with the specified name for the given . /// The for which to look up custom properties. /// The name of the custom property to search for. /// The value that should be returned if no custom property with the given name is registered for the specified . public object GetCustomProperty(DataFormatReference dfr, string name, object defaultValue = null) { return GetCustomProperty(dfr, name, defaultValue); } /// /// Sets the value of the custom property with the specified name for the given . If no custom property with the given /// name is registered for the specified , a new property is registered. /// /// The for which to look up or register custom properties. /// The name of the custom property to set. /// The value that should be assigned to the property. /// The type of custom property that should be set. public void SetCustomProperty(DataFormatReference dfr, string name, T value) { if (!_customProperties.ContainsKey(dfr)) { _customProperties.Add(dfr, new Dictionary()); } _customProperties[dfr][name] = value; } /// /// Sets the value of the custom property with the specified name for the given . If no custom property with the given /// name is registered for the specified , a new property is registered. /// /// The for which to look up or register custom properties. /// The name of the custom property to set. /// The value that should be assigned to the property. public void SetCustomProperty(DataFormatReference dfr, string name, object value) { SetCustomProperty(dfr, name, value); } protected virtual CriteriaObject[] GetCriteriaObjectsInternal() { return new CriteriaObject[0]; } public CriteriaObject[] GetCriteriaObjects() { return GetCriteriaObjectsInternal(); } // implementation of ISupportsExtraData public T GetExtraData(string key, T defaultValue = default(T)) { return (T)GetExtraData(key, (object)defaultValue); } public void SetExtraData(string key, T value) { SetExtraData(key, (object)value); } private Dictionary _extraData = new Dictionary(); public object GetExtraData(string key, object defaultValue = null) { if (_extraData.ContainsKey(key)) return _extraData[key]; return defaultValue; } public void SetExtraData(string key, object value) { _extraData[key] = value; } } }