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 } |