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 }