SharpDevelop源码剖析(二)————插件结构

SharpDevelop的每个插件都是自成体系的,通过addin文件进行自我描述。当SharpDevelop启动时,会在指定位置寻找并加读入所有的addin文件,根据文件中的内容构造一个全局唯一的静态的插件树型结构,当需要用到插件树中的某个对象时,给出路径,就可以得到.

addin文件可以AddIns文件下的任何一个工程中找到,ICSharpCode.SharpDevelop工程下有的ICSharpCode.SharpDevelop.addin文件是SharpDevelop主界面的插件文件。

<AddIn name        = "SharpDevelop"
       author      = "Mike Krueger"
       copyright   = "prj:///doc/copyright.txt"
       url         = "http://www.icsharpcode.net"
       description = "SharpDevelop main module"
       addInManagerHidden = "true">
       <Manifest>
		<Identity name = "SharpDevelop" version = "@SharpDevelopCoreVersion"/>
	</Manifest>
</AddIn>
这是所有addin文件内容的根节点,提供插件的基本信息。
<Runtime>
		<Import assembly=":ICSharpCode.SharpDevelop">
			<ConditionEvaluator name="ActiveContentExtension" 
class="ICSharpCode.SharpDevelop.ActiveContentExtensionConditionEvaluator"/> <Doozer name="TaskBoundAdditionalLogger"
                         class="ICSharpCode.SharpDevelop.Project.TaskBoundAdditionalLoggerDoozer"/>
		</Import>
		<Import assembly=":ICSharpCode.TextEditor"/>
</Runtime>

Runtime节点指定一些插件需要加载的程序集和一些在运需要加入到插件树的类型。当需要创建一个类型时,程序和查询每个插件中的Runtime对象中通过Import标签指定的程序集。这里需要注意,每个Import标签都对应一个Runtime对象,而不是Runtime标签与Runtime对象对应。

<Path name = "/SharpDevelop/Workbench/Ambiences">
		<Class id    = ".NET"
		       class = "ICSharpCode.SharpDevelop.NetAmbience"/>
</Path>

Path节点的作用是将其子节点作为插件树的节点放入其name属性指定的路径下。而其子节点一般就是一些具体功能,如类型,菜单项等。

关于Addin文件的格式,可以参考ICSharpCode.Core工程下,src\AddInTree\AddIn\AddIn.xsd文件,但是这个文件的格式不是严格的,只是用在一些XML工具中可以得到一定代码提示信息。

下面我们看一下插件树结构:

	public static class AddInTree
	{
		/// <summary>
		/// Gets the list of loaded AddIns.
		/// </summary>
		public static IList<AddIn> AddIns {
			get {
				return addIns.AsReadOnly();
			}
		}
		
		/// <summary>
		/// Gets a dictionary of registered doozers.
		/// </summary>
		public static Dictionary<string, IDoozer> Doozers {
			get {
				return doozers;
			}
		}
		
		/// <summary>
		/// Gets a dictionary of registered condition evaluators.
		/// </summary>
		public static Dictionary<string, IConditionEvaluator> ConditionEvaluators {
			get {
				return conditionEvaluators;
			}
		}
		public static void Load(List<string> addInFiles, List<string> disabledAddIns);
		public static void InsertAddIn(AddIn addIn);

AddInTree是一个静态的全局唯一的类型,在启动应用程序的时候将所有需要添加的addin文件做为参数调用其Load方法。通过解析addin文件Load方法为每个addin文件生成一个AddIn对象,通过调用InsertAddIn将其保存AddInTree中。InsertAddIn中,首先根据Path节点的定义,将其每一个子节点放入一个Codon对象中,然后按Path指定路径,以rootNode为根节点将这些Codon对像保存起来。然后将Runtime节点下的ConditionEvaluator和Doozer会别放入ConditionEvaluators ,Doozers。这个两个属性都是Dictionary类型,便于查找。前在提到的每个对象,如Codon,Doozer,其结构中都保留了对Addin对象的引用,在使用这些对象时可以方便的得到其所属的Addin对象中的其它信息。

从addin文件的节点名称我们大致可以判断每个节点对应的对像和其作用,下面我介绍两个不太好理解的类型:

Doozer: 实现IDoozer接口

public interface IDoozer
	{
		/// <summary>
		/// Gets if the doozer handles codon conditions on its own.
		/// If this property return false, the item is excluded when the condition is not met.
		/// </summary>
		bool HandleConditions { get; }
		
		/// <summary>
		/// Construct the item.
		/// </summary>
		/// <param name="caller">The caller passed to <see cref="AddInTree.BuildItem"/>.</param>
		/// <param name="codon">The codon to build.</param>
		/// <param name="subItems">The list of objects created by (other) doozers for the sub items.</param>
		/// <returns>The constructed item.</returns>
		object BuildItem(object caller, Codon codon, ArrayList subItems);
	}
 

在addin文件中,Doozer标签有name,class两个属性。通过查看代码我们可以了解到,每个Path节点下面的子节点的标签名,如Class,MenuItem都可以找到有对应的name属性的Doozer的定义。当需要生成Class,MenuItem等对应的对象时,就查找AddInTree中的Doozers,如果有对像的Doozer,就把Codon对像传入,调用BuildItem方法,就可得到所需要的对象。

Codon:定义见Codon.cs

每个Codon对象对应的是Path节点下的一个子节点,节点名对应一个Doozer,其它属性用于给Doozer构建对象时提供相应信息。由此可见,在定义addin文件时,Path节点的子节点是可以自定义的,只有要相应的Doozer定义就可以。

以上是我对构造插件树过程的一些理解,我不想贴太多的代码,我认为代码可以自己去读,所以这里只讲了一下我读代码后构想出来的思路,和一些不好理解的地方,然希望大家读过后能给点意见,我会改正,能真正的这个过程讲清楚。谢谢!

原文地址:https://www.cnblogs.com/SharpDeveloper/p/1738386.html