Mercurial > pub > bltoolkit
comparison Source/EditableObjects/EditableXmlDocument.cs @ 0:f990fcb411a9
Копия текущей версии из github
| author | cin |
|---|---|
| date | Thu, 27 Mar 2014 21:46:09 +0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:f990fcb411a9 |
|---|---|
| 1 using System; | |
| 2 using System.Collections; | |
| 3 using System.Reflection; | |
| 4 using System.Xml; | |
| 5 | |
| 6 using BLToolkit.TypeBuilder; | |
| 7 | |
| 8 namespace BLToolkit.EditableObjects | |
| 9 { | |
| 10 [Serializable] | |
| 11 public class EditableXmlDocument: IEditable, ISetParent, IMemberwiseEditable, IPrintDebugState | |
| 12 { | |
| 13 private Stack _changedNodes; | |
| 14 private XmlDocument _original; | |
| 15 private XmlDocument _current; | |
| 16 private IPropertyChanged _parent; | |
| 17 private PropertyInfo _propertyInfo; | |
| 18 | |
| 19 public EditableXmlDocument() | |
| 20 : this(new XmlDocument()) | |
| 21 { | |
| 22 } | |
| 23 | |
| 24 public EditableXmlDocument(XmlDocument value) | |
| 25 { | |
| 26 _changedNodes = null; | |
| 27 _current = value; | |
| 28 _original = value; | |
| 29 | |
| 30 StartXmlDocTracking(); | |
| 31 } | |
| 32 | |
| 33 [GetValue, SetValue] | |
| 34 public XmlDocument Value | |
| 35 { | |
| 36 get { return _current; } | |
| 37 set | |
| 38 { | |
| 39 if (_current == value) | |
| 40 return; | |
| 41 | |
| 42 if (_current == _original) | |
| 43 StopXmlDocTracking(); | |
| 44 | |
| 45 // Drop changes, if any. | |
| 46 // | |
| 47 if (_changedNodes != null) | |
| 48 _changedNodes.Clear(); | |
| 49 | |
| 50 _current = value; | |
| 51 | |
| 52 if (_current == _original) | |
| 53 StartXmlDocTracking(); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 private void StartXmlDocTracking() | |
| 58 { | |
| 59 if (_current == null) | |
| 60 return; | |
| 61 | |
| 62 _current.NodeInserted += HandleNodeChanged; | |
| 63 _current.NodeRemoved += HandleNodeChanged; | |
| 64 _current.NodeChanged += HandleNodeChanged; | |
| 65 } | |
| 66 | |
| 67 private void StopXmlDocTracking() | |
| 68 { | |
| 69 if (_current == null) | |
| 70 return; | |
| 71 | |
| 72 _current.NodeInserted -= HandleNodeChanged; | |
| 73 _current.NodeRemoved -= HandleNodeChanged; | |
| 74 _current.NodeChanged -= HandleNodeChanged; | |
| 75 } | |
| 76 | |
| 77 private void HandleNodeChanged(object sender, XmlNodeChangedEventArgs ea) | |
| 78 { | |
| 79 if (ea.Action == XmlNodeChangedAction.Change && ea.NewValue == ea.OldValue) | |
| 80 { | |
| 81 // A void change can be ignored. | |
| 82 // | |
| 83 return; | |
| 84 } | |
| 85 | |
| 86 if (_changedNodes == null) | |
| 87 _changedNodes = new Stack(); | |
| 88 | |
| 89 _changedNodes.Push(new XmlNodeTrackBack(ea)); | |
| 90 | |
| 91 // Propagate changes to parent object, if set. | |
| 92 // | |
| 93 if (_parent != null) | |
| 94 _parent.OnPropertyChanged(_propertyInfo); | |
| 95 } | |
| 96 | |
| 97 #region IEditable Members | |
| 98 | |
| 99 public void AcceptChanges() | |
| 100 { | |
| 101 if (_original != _current) | |
| 102 { | |
| 103 _original = _current; | |
| 104 StartXmlDocTracking(); | |
| 105 } | |
| 106 else | |
| 107 { | |
| 108 // Let them go away. | |
| 109 // | |
| 110 if (_changedNodes != null) | |
| 111 _changedNodes.Clear(); | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 public void RejectChanges() | |
| 116 { | |
| 117 if (_original != _current) | |
| 118 { | |
| 119 _current = _original; | |
| 120 StartXmlDocTracking(); | |
| 121 } | |
| 122 else if (_changedNodes != null && _changedNodes.Count > 0) | |
| 123 { | |
| 124 // Don't fall into an infinite loop. | |
| 125 // | |
| 126 StopXmlDocTracking(); | |
| 127 | |
| 128 // A Stack enumerates from back to front. | |
| 129 // | |
| 130 foreach (XmlNodeTrackBack nodeTrackBack in _changedNodes) | |
| 131 { | |
| 132 switch (nodeTrackBack.Action) | |
| 133 { | |
| 134 case XmlNodeChangedAction.Insert: | |
| 135 if (nodeTrackBack.Node.NodeType == XmlNodeType.Attribute) | |
| 136 ((XmlElement)nodeTrackBack.Value).Attributes.Remove((XmlAttribute)nodeTrackBack.Node); | |
| 137 else | |
| 138 ((XmlNode)nodeTrackBack.Value).RemoveChild(nodeTrackBack.Node); | |
| 139 break; | |
| 140 case XmlNodeChangedAction.Remove: | |
| 141 // NB: The order of children nodes may change. | |
| 142 // | |
| 143 if (nodeTrackBack.Node.NodeType == XmlNodeType.Attribute) | |
| 144 ((XmlElement)nodeTrackBack.Value).Attributes.Append((XmlAttribute)nodeTrackBack.Node); | |
| 145 else | |
| 146 ((XmlNode)nodeTrackBack.Value).AppendChild(nodeTrackBack.Node); | |
| 147 break; | |
| 148 case XmlNodeChangedAction.Change: | |
| 149 nodeTrackBack.Node.Value = (string)nodeTrackBack.Value; | |
| 150 break; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 _changedNodes.Clear(); | |
| 155 StartXmlDocTracking(); | |
| 156 } | |
| 157 } | |
| 158 | |
| 159 public bool IsDirty | |
| 160 { | |
| 161 get | |
| 162 { | |
| 163 if (_current == _original) | |
| 164 return _changedNodes != null && _changedNodes.Count > 0; | |
| 165 | |
| 166 if (_current == null || _original == null) | |
| 167 return true; | |
| 168 | |
| 169 return _current.InnerXml.TrimEnd() != _original.InnerXml.TrimEnd(); | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 #endregion | |
| 174 | |
| 175 #region IMemberwiseEditable Members | |
| 176 | |
| 177 public bool AcceptMemberChanges(PropertyInfo propertyInfo, string memberName) | |
| 178 { | |
| 179 if (memberName != propertyInfo.Name) | |
| 180 return false; | |
| 181 | |
| 182 AcceptChanges(); | |
| 183 | |
| 184 return true; | |
| 185 } | |
| 186 | |
| 187 public bool RejectMemberChanges(PropertyInfo propertyInfo, string memberName) | |
| 188 { | |
| 189 if (memberName != propertyInfo.Name) | |
| 190 return false; | |
| 191 | |
| 192 RejectChanges(); | |
| 193 | |
| 194 return true; | |
| 195 } | |
| 196 | |
| 197 public bool IsDirtyMember(PropertyInfo propertyInfo, string memberName, ref bool isDirty) | |
| 198 { | |
| 199 if (memberName != propertyInfo.Name) | |
| 200 return false; | |
| 201 | |
| 202 isDirty = IsDirty; | |
| 203 | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 public void GetDirtyMembers(PropertyInfo propertyInfo, ArrayList list) | |
| 208 { | |
| 209 if (IsDirty) | |
| 210 list.Add(propertyInfo); | |
| 211 } | |
| 212 | |
| 213 #endregion | |
| 214 | |
| 215 #region IPrintDebugState Members | |
| 216 | |
| 217 public void PrintDebugState(PropertyInfo propertyInfo, ref string str) | |
| 218 { | |
| 219 str += string.Format("{0,-20} {1} {2,-80}\r\n", | |
| 220 propertyInfo.Name, IsDirty? "*": " ", _current != null? _current.OuterXml: "(null)"); | |
| 221 } | |
| 222 | |
| 223 #endregion | |
| 224 | |
| 225 #region ISetParent Members | |
| 226 | |
| 227 public void SetParent(object parent, PropertyInfo propertyInfo) | |
| 228 { | |
| 229 _parent = parent as IPropertyChanged; | |
| 230 _propertyInfo = propertyInfo; | |
| 231 } | |
| 232 | |
| 233 #endregion | |
| 234 | |
| 235 #region Inner types | |
| 236 | |
| 237 private struct XmlNodeTrackBack | |
| 238 { | |
| 239 public readonly XmlNode Node; | |
| 240 public readonly XmlNodeChangedAction Action; | |
| 241 public readonly object Value; | |
| 242 | |
| 243 public XmlNodeTrackBack(XmlNodeChangedEventArgs ea) | |
| 244 { | |
| 245 Node = ea.Node; | |
| 246 Action = ea.Action; | |
| 247 switch(ea.Action) | |
| 248 { | |
| 249 case XmlNodeChangedAction.Insert: | |
| 250 Value = ea.NewParent; | |
| 251 break; | |
| 252 case XmlNodeChangedAction.Remove: | |
| 253 Value = ea.OldParent; | |
| 254 break; | |
| 255 case XmlNodeChangedAction.Change: | |
| 256 Value = ea.OldValue; | |
| 257 break; | |
| 258 default: | |
| 259 throw new ArgumentOutOfRangeException("ea", ea.Action, string.Format("Unknown XmlNodeChangedAction")); | |
| 260 } | |
| 261 } | |
| 262 } | |
| 263 | |
| 264 #endregion | |
| 265 | |
| 266 } | |
| 267 } |
