1 //
2 // ExtensionNodeDescription.cs
3 //
4 // Author:
5 //   Lluis Sanchez Gual
6 //
7 // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 
29 
30 using System;
31 using System.Xml;
32 using System.Collections.Specialized;
33 using Mono.Addins.Serialization;
34 
35 namespace Mono.Addins.Description
36 {
37 	/// <summary>
38 	/// An extension node definition.
39 	/// </summary>
40 	public class ExtensionNodeDescription: ObjectDescription, NodeElement
41 	{
42 		ExtensionNodeDescriptionCollection childNodes;
43 		string[] attributes;
44 		string nodeName;
45 
46 		/// <summary>
47 		/// Initializes a new instance of the <see cref="Mono.Addins.Description.ExtensionNodeDescription"/> class.
48 		/// </summary>
49 		/// <param name='nodeName'>
50 		/// Node name.
51 		/// </param>
ExtensionNodeDescription(string nodeName)52 		public ExtensionNodeDescription (string nodeName)
53 		{
54 			this.nodeName = nodeName;
55 		}
56 
ExtensionNodeDescription(XmlElement elem)57 		internal ExtensionNodeDescription (XmlElement elem)
58 		{
59 			Element = elem;
60 			nodeName = elem.LocalName;
61 		}
62 
ExtensionNodeDescription()63 		internal ExtensionNodeDescription ()
64 		{
65 		}
66 
67 		/// <summary>
68 		/// Gets the type of the node.
69 		/// </summary>
70 		/// <returns>
71 		/// The node type.
72 		/// </returns>
73 		/// <remarks>
74 		/// This method only works when the add-in description to which the node belongs has been
75 		/// loaded from an add-in registry.
76 		/// </remarks>
GetNodeType()77 		public ExtensionNodeType GetNodeType ()
78 		{
79 			if (Parent is Extension) {
80 				Extension ext = (Extension) Parent;
81 				object ob = ext.GetExtendedObject ();
82 				if (ob is ExtensionPoint) {
83 					ExtensionPoint ep = (ExtensionPoint) ob;
84 					return ep.NodeSet.GetAllowedNodeTypes () [NodeName];
85 				} else if (ob is ExtensionNodeDescription) {
86 					ExtensionNodeDescription pn = (ExtensionNodeDescription) ob;
87 					ExtensionNodeType pt = ((ExtensionNodeDescription) pn).GetNodeType ();
88 					if (pt != null)
89 						return pt.GetAllowedNodeTypes () [NodeName];
90 				}
91 			}
92 			else if (Parent is ExtensionNodeDescription) {
93 				ExtensionNodeType pt = ((ExtensionNodeDescription) Parent).GetNodeType ();
94 				if (pt != null)
95 					return pt.GetAllowedNodeTypes () [NodeName];
96 			}
97 			return null;
98 		}
99 
100 		/// <summary>
101 		/// Gets the extension path under which this node is registered
102 		/// </summary>
103 		/// <returns>
104 		/// The parent path.
105 		/// </returns>
106 		/// <remarks>
107 		/// For example, if the id of the node is 'ThisNode', and the node is a child of another node with id 'ParentNode', and
108 		/// that parent node is defined in an extension with the path '/Core/MainExtension', then the parent path is 'Core/MainExtension/ParentNode'.
109 		/// </remarks>
GetParentPath()110 		public string GetParentPath ()
111 		{
112 			if (Parent is Extension)
113 				return ((Extension)Parent).Path;
114 			else if (Parent is ExtensionNodeDescription) {
115 				ExtensionNodeDescription pn = (ExtensionNodeDescription) Parent;
116 				return pn.GetParentPath () + "/" + pn.Id;
117 			}
118 			else
119 				return string.Empty;
120 		}
121 
Verify(string location, StringCollection errors)122 		internal override void Verify (string location, StringCollection errors)
123 		{
124 			if (nodeName == null || nodeName.Length == 0)
125 				errors.Add (location + "Node: NodeName can't be empty.");
126 			ChildNodes.Verify (location + NodeName + "/", errors);
127 		}
128 
129 		/// <summary>
130 		/// Gets or sets the name of the node.
131 		/// </summary>
132 		/// <value>
133 		/// The name of the node.
134 		/// </value>
135 		public string NodeName {
136 			get { return nodeName; }
137 			internal set {
138 				if (Element != null)
139 					throw new InvalidOperationException ("Can't change node name of xml element");
140 				nodeName = value;
141 			}
142 		}
143 
144 		/// <summary>
145 		/// Gets or sets the identifier of the node.
146 		/// </summary>
147 		/// <value>
148 		/// The identifier.
149 		/// </value>
150 		public string Id {
151 			get { return GetAttribute ("id"); }
152 			set { SetAttribute ("id", value); }
153 		}
154 
155 		/// <summary>
156 		/// Gets or sets the identifier of the node after which this node has to be inserted
157 		/// </summary>
158 		/// <value>
159 		/// The identifier of the reference node
160 		/// </value>
161 		public string InsertAfter {
162 			get { return GetAttribute ("insertafter"); }
163 			set {
164 				if (value == null || value.Length == 0)
165 					RemoveAttribute ("insertafter");
166 				else
167 					SetAttribute ("insertafter", value);
168 			}
169 		}
170 
171 		/// <summary>
172 		/// Gets or sets the identifier of the node before which this node has to be inserted
173 		/// </summary>
174 		/// <value>
175 		/// The identifier of the reference node
176 		/// </value>
177 		public string InsertBefore {
178 			get { return GetAttribute ("insertbefore"); }
179 			set {
180 				if (value == null || value.Length == 0)
181 					RemoveAttribute ("insertbefore");
182 				else
183 					SetAttribute ("insertbefore", value);
184 			}
185 		}
186 
187 		/// <summary>
188 		/// Gets a value indicating whether this node is a condition.
189 		/// </summary>
190 		/// <value>
191 		/// <c>true</c> if this node is a condition; otherwise, <c>false</c>.
192 		/// </value>
193 		public bool IsCondition {
194 			get { return nodeName == "Condition" || nodeName == "ComplexCondition"; }
195 		}
196 
SaveXml(XmlElement parent)197 		internal override void SaveXml (XmlElement parent)
198 		{
199 			if (Element == null) {
200 				Element = parent.OwnerDocument.CreateElement (nodeName);
201 				parent.AppendChild (Element);
202 				if (attributes != null) {
203 					for (int n=0; n<attributes.Length; n+=2)
204 						Element.SetAttribute (attributes[n], attributes[n+1]);
205 				}
206 				ChildNodes.SaveXml (Element);
207 			}
208 		}
209 
210 		/// <summary>
211 		/// Gets the value of an attribute.
212 		/// </summary>
213 		/// <returns>
214 		/// The value of the attribute, or an empty string if the attribute is not defined.
215 		/// </returns>
216 		/// <param name='key'>
217 		/// Name of the attribute.
218 		/// </param>
GetAttribute(string key)219 		public string GetAttribute (string key)
220 		{
221 			if (Element != null)
222 				return Element.GetAttribute (key);
223 
224 			if (attributes == null)
225 				return string.Empty;
226 			for (int n=0; n<attributes.Length; n+=2) {
227 				if (attributes [n] == key)
228 					return attributes [n+1];
229 			}
230 			return string.Empty;
231 		}
232 
233 		/// <summary>
234 		/// Sets the value of an attribute.
235 		/// </summary>
236 		/// <param name='key'>
237 		/// Name of the attribute
238 		/// </param>
239 		/// <param name='value'>
240 		/// The value.
241 		/// </param>
SetAttribute(string key, string value)242 		public void SetAttribute (string key, string value)
243 		{
244 			if (Element != null) {
245 				Element.SetAttribute (key, value);
246 				return;
247 			}
248 
249 			if (value == null)
250 				value = string.Empty;
251 
252 			if (attributes == null) {
253 				attributes = new string [2];
254 				attributes [0] = key;
255 				attributes [1] = value;
256 				return;
257 			}
258 
259 			for (int n=0; n<attributes.Length; n+=2) {
260 				if (attributes [n] == key) {
261 					attributes [n+1] = value;
262 					return;
263 				}
264 			}
265 			string[] newList = new string [attributes.Length + 2];
266 			attributes.CopyTo (newList, 0);
267 			attributes = newList;
268 			attributes [attributes.Length - 2] = key;
269 			attributes [attributes.Length - 1] = value;
270 		}
271 
272 		/// <summary>
273 		/// Removes an attribute.
274 		/// </summary>
275 		/// <param name='name'>
276 		/// Name of the attribute to remove.
277 		/// </param>
RemoveAttribute(string name)278 		public void RemoveAttribute (string name)
279 		{
280 			if (Element != null) {
281 				Element.RemoveAttribute (name);
282 				return;
283 			}
284 
285 			if (attributes == null)
286 				return;
287 
288 			for (int n=0; n<attributes.Length; n+=2) {
289 				if (attributes [n] == name) {
290 					string[] newar = new string [attributes.Length - 2];
291 					Array.Copy (attributes, 0, newar, 0, n);
292 					Array.Copy (attributes, n+2, newar, n, attributes.Length - n - 2);
293 					attributes = newar;
294 					break;
295 				}
296 			}
297 		}
298 
299 		/// <summary>
300 		/// Gets the attributes of the node.
301 		/// </summary>
302 		/// <value>
303 		/// The attributes.
304 		/// </value>
305 		public NodeAttribute[] Attributes {
306 			get {
307 				if (Element != null)
308 					SaveXmlAttributes ();
309 				if (attributes == null)
310 					return new NodeAttribute [0];
311 				NodeAttribute[] ats = new NodeAttribute [attributes.Length / 2];
312 				for (int n=0; n<ats.Length; n++) {
313 					NodeAttribute at = new NodeAttribute ();
314 					at.name = attributes [n*2];
315 					at.value = attributes [n*2 + 1];
316 					ats [n] = at;
317 				}
318 				return ats;
319 			}
320 		}
321 
322 		/// <summary>
323 		/// Gets the child nodes.
324 		/// </summary>
325 		/// <value>
326 		/// The child nodes.
327 		/// </value>
328 		public ExtensionNodeDescriptionCollection ChildNodes {
329 			get {
330 				if (childNodes == null) {
331 					childNodes = new ExtensionNodeDescriptionCollection (this);
332 					if (Element != null) {
333 						foreach (XmlNode nod in Element.ChildNodes) {
334 							if (nod is XmlElement)
335 								childNodes.Add (new ExtensionNodeDescription ((XmlElement)nod));
336 						}
337 					}
338 				}
339 				return childNodes;
340 			}
341 		}
342 
343 		NodeElementCollection NodeElement.ChildNodes {
344 			get { return ChildNodes; }
345 		}
346 
SaveXmlAttributes()347 		void SaveXmlAttributes ()
348 		{
349 			attributes = new string [Element.Attributes.Count * 2];
350 			for (int n=0; n<attributes.Length; n+=2) {
351 				XmlAttribute at = Element.Attributes [n/2];
352 				attributes [n] = at.LocalName;
353 				attributes [n+1] = at.Value;
354 			}
355 		}
356 
Write(BinaryXmlWriter writer)357 		internal override void Write (BinaryXmlWriter writer)
358 		{
359 			if (Element != null)
360 				SaveXmlAttributes ();
361 
362 			writer.WriteValue ("nodeName", nodeName);
363 			writer.WriteValue ("attributes", attributes);
364 			writer.WriteValue ("ChildNodes", ChildNodes);
365 		}
366 
Read(BinaryXmlReader reader)367 		internal override void Read (BinaryXmlReader reader)
368 		{
369 			nodeName = reader.ReadStringValue ("nodeName");
370 			attributes = (string[]) reader.ReadValue ("attributes");
371 			childNodes = (ExtensionNodeDescriptionCollection) reader.ReadValue ("ChildNodes", new ExtensionNodeDescriptionCollection (this));
372 		}
373 	}
374 }
375