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