/// <summary> /// Updates a give template to reflect this one. Elements that /// share a name and datatype are transferred to the best ability. /// Elements that are not present in this one are removed. /// </summary> /// <param name="template"></param> public bool ReconcileElementProperties(BaseElement oldElement, bool reconcileOverrides = false) { //This assumes we already have an an element that matches this element's Guid if (!this.Guid.Equals(oldElement.Guid)) { return(false); } Type sourceType = this.GetType(); Type oldType = oldElement.GetType(); //first, let's do the easy stuff and update existing properties for both templates foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var targetProperty = oldType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance); if (targetProperty != null && targetProperty.CanWrite && targetProperty.GetCustomAttribute <ReconcilableProp>(true) != null && property.GetCustomAttribute <ReconcilableProp>(true) != null) { if (targetProperty.GetCustomAttribute <ReconcilableProp>(true).Override == reconcileOverrides) { //edit property to reflect template changes targetProperty.SetValue(oldElement, property.GetValue(this, null), null); } } } //TODO: We must have an 'intelligent' mechinism here that can attempt to compare //properties of different Guids but the same ID. If IDs match, it tries to convert //the data from the old element format to the new one. Otherwise it must delete that data. //TODO: We must remove elements from the card that were removed from the master template //This will be dangerous and requires mitigation from the above mechanism as well as plenty //of warnings to the user about what they are doing. //now we must compare each child in this element with each child //of the other element. If one of them matches, we need to reconcile them foreach (BaseElement child in _Children) { //update: this will automagically compare Guids within the call now foreach (BaseElement targetChild in oldElement.Children) { child.ReconcileElementProperties(targetChild); } } return(true); }
/// <summary> /// Copies this element to another element using shallow copying /// of all basic properties. Child elements however, are still /// deeply copied using recursion. This will not handle indexed properties. /// </summary> /// <returns></returns> public BaseElement GetShallowClone() { BaseElement dest = Activator.CreateInstance(this.GetType()) as BaseElement; if (dest == null) { return(null); } Type sourceType = this.GetType(); Type targetType = dest.GetType(); //copy all values of all elements marked with a clone attribute foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var targetProperty = targetType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance); if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType) && targetProperty.GetCustomAttribute <ShallowElementCloneAttribute>(true) != null && property.GetCustomAttribute <ShallowElementCloneAttribute>(true) != null) { targetProperty.SetValue(dest, property.GetValue(this, null), null); } else if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType) && targetProperty.GetCustomAttribute <DeepElementCloneAttribute>(true) != null && property.GetCustomAttribute <DeepElementCloneAttribute>(true) != null) { try { targetProperty.SetValue(dest, Utils.ObjectHelper.Clone(property.GetValue(this, null)), null); } catch (ArgumentException e) { MessageBox.Show("Failed to deep copy the property '" + property.Name + "' of the element '" + this._Name + "'.\n" + e.Message); } } else if ( targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType) && targetProperty.GetCustomAttribute <ContentElementCloneAttribute>(true) != null && property.GetCustomAttribute <ContentElementCloneAttribute>(true) != null) { this.DeepClone(property, this, targetProperty, dest); } } //manually copy InstanceProperties //We need to generate the InstanceEditProperty controls and bind them here. dest.BindInstanceEditProps(); //TODO: set default values to binding property //now deep shallow-copy of the children foreach (BaseElement child in _Children) { dest.Children.Add(child.GetShallowClone()); } //we use reflection to get the private Guid value. //We do this in an effort to avoid letting overriding //classes mess with the Guid FieldInfo sourceGuid = sourceType.GetField("_Guid", BindingFlags.NonPublic | BindingFlags.Instance); FieldInfo destGuid = targetType.GetField("_Guid", BindingFlags.NonPublic | BindingFlags.Instance); destGuid.SetValue(dest, sourceGuid.GetValue(this)); return(dest); }