Mercurial > pub > bltoolkit
view Source/EditableObjects/EditableXmlDocument.cs @ 9:1e85f66cf767 default tip
update bltoolkit
author | nickolay |
---|---|
date | Thu, 05 Apr 2018 20:53:26 +0300 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections; using System.Reflection; using System.Xml; using BLToolkit.TypeBuilder; namespace BLToolkit.EditableObjects { [Serializable] public class EditableXmlDocument: IEditable, ISetParent, IMemberwiseEditable, IPrintDebugState { private Stack _changedNodes; private XmlDocument _original; private XmlDocument _current; private IPropertyChanged _parent; private PropertyInfo _propertyInfo; public EditableXmlDocument() : this(new XmlDocument()) { } public EditableXmlDocument(XmlDocument value) { _changedNodes = null; _current = value; _original = value; StartXmlDocTracking(); } [GetValue, SetValue] public XmlDocument Value { get { return _current; } set { if (_current == value) return; if (_current == _original) StopXmlDocTracking(); // Drop changes, if any. // if (_changedNodes != null) _changedNodes.Clear(); _current = value; if (_current == _original) StartXmlDocTracking(); } } private void StartXmlDocTracking() { if (_current == null) return; _current.NodeInserted += HandleNodeChanged; _current.NodeRemoved += HandleNodeChanged; _current.NodeChanged += HandleNodeChanged; } private void StopXmlDocTracking() { if (_current == null) return; _current.NodeInserted -= HandleNodeChanged; _current.NodeRemoved -= HandleNodeChanged; _current.NodeChanged -= HandleNodeChanged; } private void HandleNodeChanged(object sender, XmlNodeChangedEventArgs ea) { if (ea.Action == XmlNodeChangedAction.Change && ea.NewValue == ea.OldValue) { // A void change can be ignored. // return; } if (_changedNodes == null) _changedNodes = new Stack(); _changedNodes.Push(new XmlNodeTrackBack(ea)); // Propagate changes to parent object, if set. // if (_parent != null) _parent.OnPropertyChanged(_propertyInfo); } #region IEditable Members public void AcceptChanges() { if (_original != _current) { _original = _current; StartXmlDocTracking(); } else { // Let them go away. // if (_changedNodes != null) _changedNodes.Clear(); } } public void RejectChanges() { if (_original != _current) { _current = _original; StartXmlDocTracking(); } else if (_changedNodes != null && _changedNodes.Count > 0) { // Don't fall into an infinite loop. // StopXmlDocTracking(); // A Stack enumerates from back to front. // foreach (XmlNodeTrackBack nodeTrackBack in _changedNodes) { switch (nodeTrackBack.Action) { case XmlNodeChangedAction.Insert: if (nodeTrackBack.Node.NodeType == XmlNodeType.Attribute) ((XmlElement)nodeTrackBack.Value).Attributes.Remove((XmlAttribute)nodeTrackBack.Node); else ((XmlNode)nodeTrackBack.Value).RemoveChild(nodeTrackBack.Node); break; case XmlNodeChangedAction.Remove: // NB: The order of children nodes may change. // if (nodeTrackBack.Node.NodeType == XmlNodeType.Attribute) ((XmlElement)nodeTrackBack.Value).Attributes.Append((XmlAttribute)nodeTrackBack.Node); else ((XmlNode)nodeTrackBack.Value).AppendChild(nodeTrackBack.Node); break; case XmlNodeChangedAction.Change: nodeTrackBack.Node.Value = (string)nodeTrackBack.Value; break; } } _changedNodes.Clear(); StartXmlDocTracking(); } } public bool IsDirty { get { if (_current == _original) return _changedNodes != null && _changedNodes.Count > 0; if (_current == null || _original == null) return true; return _current.InnerXml.TrimEnd() != _original.InnerXml.TrimEnd(); } } #endregion #region IMemberwiseEditable Members public bool AcceptMemberChanges(PropertyInfo propertyInfo, string memberName) { if (memberName != propertyInfo.Name) return false; AcceptChanges(); return true; } public bool RejectMemberChanges(PropertyInfo propertyInfo, string memberName) { if (memberName != propertyInfo.Name) return false; RejectChanges(); return true; } public bool IsDirtyMember(PropertyInfo propertyInfo, string memberName, ref bool isDirty) { if (memberName != propertyInfo.Name) return false; isDirty = IsDirty; return true; } public void GetDirtyMembers(PropertyInfo propertyInfo, ArrayList list) { if (IsDirty) list.Add(propertyInfo); } #endregion #region IPrintDebugState Members public void PrintDebugState(PropertyInfo propertyInfo, ref string str) { str += string.Format("{0,-20} {1} {2,-80}\r\n", propertyInfo.Name, IsDirty? "*": " ", _current != null? _current.OuterXml: "(null)"); } #endregion #region ISetParent Members public void SetParent(object parent, PropertyInfo propertyInfo) { _parent = parent as IPropertyChanged; _propertyInfo = propertyInfo; } #endregion #region Inner types private struct XmlNodeTrackBack { public readonly XmlNode Node; public readonly XmlNodeChangedAction Action; public readonly object Value; public XmlNodeTrackBack(XmlNodeChangedEventArgs ea) { Node = ea.Node; Action = ea.Action; switch(ea.Action) { case XmlNodeChangedAction.Insert: Value = ea.NewParent; break; case XmlNodeChangedAction.Remove: Value = ea.OldParent; break; case XmlNodeChangedAction.Change: Value = ea.OldValue; break; default: throw new ArgumentOutOfRangeException("ea", ea.Action, string.Format("Unknown XmlNodeChangedAction")); } } } #endregion } }