1 //
2 // Extension.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 definition.
39 	/// </summary>
40 	/// <remarks>
41 	/// An Extension is a collection of nodes which have to be registered in an extension point.
42 	/// The target extension point is specified in the <see cref="Mono.Addins.Description.Extension"/>.Path property.
43 	/// </remarks>
44 	public class Extension: ObjectDescription, IComparable
45 	{
46 		string path;
47 		ExtensionNodeDescriptionCollection nodes;
48 
49 		/// <summary>
50 		/// Initializes a new instance of the <see cref="Mono.Addins.Description.Extension"/> class.
51 		/// </summary>
Extension()52 		public Extension ()
53 		{
54 		}
55 
56 		/// <summary>
57 		/// Initializes a new instance of the <see cref="Mono.Addins.Description.Extension"/> class.
58 		/// </summary>
59 		/// <param name='path'>
60 		/// Path that identifies the extension point being extended
61 		/// </param>
Extension(string path)62 		public Extension (string path)
63 		{
64 			this.path = path;
65 		}
66 
67 		/// <summary>
68 		/// Gets the object extended by this extension
69 		/// </summary>
70 		/// <returns>
71 		/// The extended object can be an <see cref="Mono.Addins.Description.ExtensionPoint"/> or
72 		/// an <see cref="Mono.Addins.Description.ExtensionNodeDescription"/>.
73 		/// </returns>
74 		/// <remarks>
75 		/// This method only works when the add-in description to which the extension belongs has been
76 		/// loaded from an add-in registry.
77 		/// </remarks>
GetExtendedObject()78 		public ObjectDescription GetExtendedObject ()
79 		{
80 			AddinDescription desc = ParentAddinDescription;
81 			if (desc == null)
82 				return null;
83 			ExtensionPoint ep = FindExtensionPoint (desc, path);
84 			if (ep == null && desc.OwnerDatabase != null) {
85 				foreach (Dependency dep in desc.MainModule.Dependencies) {
86 					AddinDependency adep = dep as AddinDependency;
87 					if (adep == null) continue;
88 					Addin ad = desc.OwnerDatabase.GetInstalledAddin (ParentAddinDescription.Domain, adep.FullAddinId);
89 					if (ad != null && ad.Description != null) {
90 						ep = FindExtensionPoint (ad.Description, path);
91 						if (ep != null)
92 							break;
93 					}
94 				}
95 			}
96 			if (ep != null) {
97 				string subp = path.Substring (ep.Path.Length).Trim ('/');
98 				if (subp.Length == 0)
99 					return ep; // The extension is directly extending the extension point
100 
101 				// The extension is extending a node of the extension point
102 
103 				return desc.FindExtensionNode (path, true);
104 			}
105 			return null;
106 		}
107 
108 		/// <summary>
109 		/// Gets the node types allowed in this extension.
110 		/// </summary>
111 		/// <returns>
112 		/// The allowed node types.
113 		/// </returns>
114 		/// <remarks>
115 		/// This method only works when the add-in description to which the extension belongs has been
116 		/// loaded from an add-in registry.
117 		/// </remarks>
GetAllowedNodeTypes()118 		public ExtensionNodeTypeCollection GetAllowedNodeTypes ()
119 		{
120 			ObjectDescription ob = GetExtendedObject ();
121 			ExtensionPoint ep = ob as ExtensionPoint;
122 			if (ep != null)
123 				return ep.NodeSet.GetAllowedNodeTypes ();
124 
125 			ExtensionNodeDescription node = ob as ExtensionNodeDescription;
126 			if (node != null) {
127 				ExtensionNodeType nt = node.GetNodeType ();
128 				if (nt != null)
129 					return nt.GetAllowedNodeTypes ();
130 			}
131 			return new ExtensionNodeTypeCollection ();
132 		}
133 
FindExtensionPoint(AddinDescription desc, string path)134 		ExtensionPoint FindExtensionPoint (AddinDescription desc, string path)
135 		{
136 			foreach (ExtensionPoint ep in desc.ExtensionPoints) {
137 				if (ep.Path == path || path.StartsWith (ep.Path + "/"))
138 					return ep;
139 			}
140 			return null;
141 		}
142 
Verify(string location, StringCollection errors)143 		internal override void Verify (string location, StringCollection errors)
144 		{
145 			VerifyNotEmpty (location + "Extension", errors, path, "path");
146 			ExtensionNodes.Verify (location + "Extension (" + path + ")/", errors);
147 
148 			foreach (ExtensionNodeDescription cnode in ExtensionNodes)
149 				VerifyNode (location, cnode, errors);
150 		}
151 
VerifyNode(string location, ExtensionNodeDescription node, StringCollection errors)152 		void VerifyNode (string location, ExtensionNodeDescription node, StringCollection errors)
153 		{
154 			string id = node.GetAttribute ("id");
155 			if (id.Length > 0)
156 				id = "(" + id + ")";
157 			if (node.NodeName == "Condition" && node.GetAttribute ("id").Length == 0) {
158 				errors.Add (location + node.NodeName + id + ": Missing 'id' attribute in Condition element.");
159 			}
160 			if (node.NodeName == "ComplexCondition") {
161 				if (node.ChildNodes.Count > 0) {
162 					VerifyConditionNode (location, node.ChildNodes[0], errors);
163 					for (int n=1; n<node.ChildNodes.Count; n++)
164 						VerifyNode (location + node.NodeName + id + "/", node.ChildNodes[n], errors);
165 				}
166 				else
167 					errors.Add (location + "ComplexCondition: Missing child condition in ComplexCondition element.");
168 			}
169 			foreach (ExtensionNodeDescription cnode in node.ChildNodes)
170 				VerifyNode (location + node.NodeName + id + "/", cnode, errors);
171 		}
172 
VerifyConditionNode(string location, ExtensionNodeDescription node, StringCollection errors)173 		void VerifyConditionNode (string location, ExtensionNodeDescription node, StringCollection errors)
174 		{
175 			string nodeName = node.NodeName;
176 			if (nodeName != "Or" && nodeName != "And" && nodeName != "Not" && nodeName != "Condition") {
177 				errors.Add (location + "ComplexCondition: Invalid condition element: " + nodeName);
178 				return;
179 			}
180 			foreach (ExtensionNodeDescription cnode in node.ChildNodes)
181 				VerifyConditionNode (location, cnode, errors);
182 		}
183 
184 		/// <summary>
185 		/// Initializes a new instance of the <see cref="Mono.Addins.Description.Extension"/> class.
186 		/// </summary>
187 		/// <param name='element'>
188 		/// XML that describes the extension.
189 		/// </param>
Extension(XmlElement element)190 		public Extension (XmlElement element)
191 		{
192 			Element = element;
193 			path = element.GetAttribute ("path");
194 		}
195 
196 		/// <summary>
197 		/// Gets or sets the path that identifies the extension point being extended.
198 		/// </summary>
199 		/// <value>
200 		/// The path.
201 		/// </value>
202 		public string Path {
203 			get { return path; }
204 			set { path = value; }
205 		}
206 
SaveXml(XmlElement parent)207 		internal override void SaveXml (XmlElement parent)
208 		{
209 			if (Element == null) {
210 				Element = parent.OwnerDocument.CreateElement ("Extension");
211 				parent.AppendChild (Element);
212 			}
213 			Element.SetAttribute ("path", path);
214 			if (nodes != null)
215 				nodes.SaveXml (Element);
216 		}
217 
218 		/// <summary>
219 		/// Gets the extension nodes.
220 		/// </summary>
221 		/// <value>
222 		/// The extension nodes.
223 		/// </value>
224 		public ExtensionNodeDescriptionCollection ExtensionNodes {
225 			get {
226 				if (nodes == null) {
227 					nodes = new ExtensionNodeDescriptionCollection (this);
228 					if (Element != null) {
229 						foreach (XmlNode node in Element.ChildNodes) {
230 							XmlElement e = node as XmlElement;
231 							if (e != null)
232 								nodes.Add (new ExtensionNodeDescription (e));
233 						}
234 					}
235 				}
236 				return nodes;
237 			}
238 		}
239 
IComparable.CompareTo(object obj)240 		int IComparable.CompareTo (object obj)
241 		{
242 			Extension other = (Extension) obj;
243 			return Path.CompareTo (other.Path);
244 		}
245 
Write(BinaryXmlWriter writer)246 		internal override void Write (BinaryXmlWriter writer)
247 		{
248 			writer.WriteValue ("path", path);
249 			writer.WriteValue ("Nodes", ExtensionNodes);
250 		}
251 
Read(BinaryXmlReader reader)252 		internal override void Read (BinaryXmlReader reader)
253 		{
254 			path = reader.ReadStringValue ("path");
255 			nodes = (ExtensionNodeDescriptionCollection) reader.ReadValue ("Nodes", new ExtensionNodeDescriptionCollection (this));
256 		}
257 	}
258 }
259