Third, plugin system
The backup book said the SHARPDevelop entry main function structure, ServiceManager.Service first calls AddintReSinglet in the initializeserviceubsystem method, and AddINtree is initialized here. This goen go to AddIntree to focus on the plugin system of SharpDevelop. In the case of the description, in order to facilitate the "plug-in" and plug-in specific "function module", the two words will not distinguish, and you can distinguish between the specific meaning from the context (in fact, SharpDevelop "plugin "It means .addin configuration file, each" plugin "may contain multiple" function modules ").
1, the configuration of the plug-in is since the plugin system, then let's take a look at the organization of the SharpDevelop plugin system. Many times, the same thing will come out from different angles to conclude that SharpDevelop's plugin system is also the case. Before watching the code of SharpDevelop, according to my understanding of the plugin, I think the so-called "plugin" is representing a functional module, the configuration of the plugin describes the plugin and specifies how this plugin is hung in the system. SharpDevelop's idea of plug-in tree, that is, every plugin has a path of extension points in the system. So according to the understanding of the plugin, write plugins to do,: a in
After this, I have written a plug-in addintreeView that looks at the plug-in tree and intends to hang it in SharpDevelop. According to the definition of SharpDevelop to the plugin, after I have implemented the specific plugin's AddInTreeViewCommand, write a configuration file addintreeView.Addin as follows:
<
Addin
Name
= "AddIntreeView"
Author
= "SimonLiu"
Copyright
= "GPL"
URL
= "Http://www.icsharpcode.net"
Description
= "Display AddIntree"
Version
= "1.0.0"
>
<
Runtime
>
<
Import
askSEMBLY
= "../../ bin / addintreeview.dll"
/>
Runtime
>
<
Extension
Path
= "/ SharpDevelop / Workbench / MainMenu / Tools"
>
<
Menuitem
id
= "AddIntreeView"
label
= "View addINtree"
Class
= "Addins.addintreeView.AddintreeViewCommand"
/>
Extension
>
Addin
>
In the configuration file, the Runtime section specifies the specific path of the library file addins.dll where the plug-in function module is located. Specify the extended point path / SharPdevelop / Workbench / MainMenu / Tools in the Extension section (I plan to hang it to the main menu " Under the Tools menu, then specify its Codon for MenuItem and specific ID, tags, and command class names in Extension. In this way, SharpDevelop is very good, my plugin appears under the Tools menu. After that, I wrote a SHARPDevelop's resource manager (ResourceEditor) plugin class resourceEditor.dll and hang it under the Tool menu. Similarly, I also wrote a resourceEdIitor.Addin file to correspond. The system is working very normal. If we write such a configuration file for each plugin, the plug-in library file (.dll), plugin configuration file (.addin) is one or one. But this brings a small problem, in such a plug-in-based system, each menu, toolbar button, form, panel are a plugin, then we need to write to each plugin File, this will have a lot of configuration files (it seems a bit too much, not well managed). SharpDevelop also thought of this problem, so it allows us to merge the configuration of multiple plugins in a plug-in configuration file. Therefore, I merged my two plug-in library files into an Addins.dll, and again wrote my plugin profile myaddins.addin as follows:
<
Addin
Name
= "MyAddins"
Author
= "SimonLiu"
Copyright
= "GPL"
URL
= "Http://www.icsharpcode.net"
Description
= "Display AddIntree"
Version
= "1.0.0"
>
<
Runtime
>
<
Import
askSEMBLY
= "../../ bin / addins.dll"
/>
Runtime
>
<
Extension
Path
= "/ SharpDevelop / Workbench / MainMenu / Tools"
>
<
Menuitem
id
= "ResourceEditor"
label
= "Resource Editor"
Class
= "Addins.ResourceEditor.command.ResourceEditorCommand"
/>
<
Menuitem
id
= "AddIntreeView"
label
= "View addINtree"
Class
= "Addins.addintreeView.AddintreeViewCommand"
/>
Extension
>
Addin
>
In this way, I use a plug-in functional module to configure it. Similarly, I can also merge dozens of functional modules into a plug-in configuration file. SharpDevelop called this plugin configuration file as "Addin", and packages the specific functional module to Codon and uses the Command class to package the specific function. SharpDevelop itself The core configuration of SharpDevelopCore.Addin contains all basic menus, toolbars, and PAD plug-in configuration. Let's take a look at it, now we have two trees. First, the plug-in tree itself is a tree structure. This tree is constructed according to the extension path of all Codon of all Codon of the system, indicating the location of each Codon in the plug-in tree, and you can write this small. Small AddintreeView to see the actual structure in SharpDevelop. Second, the plug-in configuration file itself has a tree structure. The root node of this tree structure is the various plug-in configuration files of the system, which is constructed according to the EXTENSION node in this configuration file, which describes each Codon with the extension node. We can take a look at the structure of this tree through Addinscout under SharpDevelop's Tools menu. In order to experiment, I streamline SharpDevelop's plug-in and constitute a simple small plug-in system. Below is a screenshot of two trees of this streamlined system. Everyone can understand the relationship between plug-in trees and plug-in configuration files through these two pairs (just two angles of the same problem, one is Codon's ExtensionPath, one is the content of the configuration file). Summarize the profile format of the SharpDevelop plugin. The first is the
public
Static
IaddinTree Addintree
{Get {if (addintree == null) {createaddintree ();} Return AddInTree;}}
AddintReesingleton is a Singleton in the plug-in tree (specific "design mode"), and addintreSingleton.AddIndree is an attribute that returns an IaddIntree interface. Here I noticed that AddINtreSinglet was inherited from DefaultAddIntree. Since it is a single mode, all the methods contain all the static methods, there is no instantiation, and the external is to access the plug-in tree through the AddINtree property, why should I inherit from DefaultAddintree? It seems that there is nothing necessary. This may be a small problem that is missing during the reconstruction process. Let's take a look at the content of the IaddIntree interface. It defines several contents: A. Attribute ConditionFactory ConditionFactory returns a factory class of constructor, where the conditions are the conditions in the plug-in configuration, and we will later in detail later. B, attribute CodonFactory CodonFactory Returns a factory class constructed for Codon. C, attribute addincollection addins Returns the root node AddIn (plugin) collection of the plug-in tree. D, Method IaddinTreenode gettreenode (String Path) Returns the corresponding tree node E, method void insertdin (address), adds a plug-in to the tree according to the extension path in Addin, method Void RemoveAddin (Addin Added a plug-in G, method assembly loadassembly (string assemblyfile) Reads the assembly specified by the Import in the plugin, and constructs the corresponding CodonFactory and CodonFactory classes.
AddintReSinglet calls the createAdDINTree method when calling AddINTree to perform initialization. The CreateAddintree method is implemented in this way:
Addintree
=
New
DefaultAddintree ();
The initialization plug-in tree is the instance of DefaultAddintree, which I feel the traces of reconstruction. First, DEFAULTADDINTREE looks from the name is the default plugin tree (since it is the default, then in other words can be other plug-in trees). But SharpDevelop did not provide an interface to the outside using custom plug-in trees (unless we modify the code here), that is, this name is not as implied as it itself. Second, according to Singleton's usual ways and previous mentioned inherited questions from DefaultAddintree, I guess the content of DEFAULTADDINTREE was originally implemented in AddintReesingleton, and later perhaps the code of code for code, put it out. , Forming a defaultaddintree class. As for the problem of inheriting the DEFAULTADDINTREE, maybe it is a base class of ADDINTREE here. This is the external question, and it has not been confirmed that you can't put it in your heart (interested in finding the old version of the old version of SharpDevelop). There are two lines of viewing code, one is the code of the constructor of the defaultadDree, which constructs Codon and Condtion factory classes in this constructor. The other is the code behind CreateAdd Andree, search the plugin file, and constructs AddIn according to the plugin file. Everyone can choose to take a branch line or choose to look at the main line first (but you will miss a lot of content). 2.1 Branch (DEFAULTADDINTREE constructor) We interrupt the CreateAdDintree code, and jump to the defaultaddintree constructor to see. DEFAULTADDINTREE defines in the /src/main/core/addins/defaultaddintree.cs file. In the constructor of the defaultaddintree, it is noted that it has a modifier INTERNAL, that is, this class only allows the class in this program to instantiate DEFAULTADDINTREE (really embarrassing). The code in the constructor is only one sentence:
Assembly.GetexecutingSemBly ());
Although there is only one line of code, but here is very delicate, it is the key to the overall situation, and I have to write it. First, obtain the ASSEMBLY of the entry program through the global assembly object object, in turn to the loadCodonsandConditions method. In this method, all data types in the incoming Assembly are enumerated. If it is not abstract, and is the subclass of AbstractCodon, and has the corresponding CodonNameAttribute property information, then establishes a corresponding CodonBuilder according to the name of this class and it is added to CodonFactory (the same operation is also made to Condition, we focus on the same operation. Look at the Codon section, Condition is basically the same as CODON). The CodonFactory class and the CodonBuilder class constitute a flexible foundation of the SharpDevelop plugin system, and you can see it carefully. We present examples with examples, with the AddIndreeViewCommand I wrote before. MenuItemcodon is searched in the assembly in the entrance, which is a subclass of AbstractCodon, a codon of the MenuItem (Menu Item) Command. Condition, execute CodonFactory.Addcodonbuilder (
New
CodonBuilder (Type.Fullname, Assembly);
First, CodonBuilder constructed according to the class name Menuitemcodon and Assembly. CodonBuilder defines in the /src/main/core/addins/codons/codonbuilder.cs file. The name MenuItem is obtained according to the MenuItemCodon's CodonNameAttribute property in the constructor of the CodonBuilder. CodonNameAttribute describes the name of the Codon, which is also the
public
ICodon BuildCodon (Addin Add)
{ICodon codon; try {// create instance (ignore case) codon = (ICodon) assembly.CreateInstance (ClassName, true); // set default values codon.AddIn = addIn;} catch (Exception) {codon = null;} Return Codon;
Obviously, BuildCodon creates a specific CODON instance based on the Assembly and type ClassName incorporated in the constructor, and associates with the specific addin. After that, CodonFactory calls the AddCodonBuilder method to add this CodonBuilder to its builder collection. Let's look up, see how CodonFactory uses this CodonBuilder. In file /src/main/core/addins/codons/codonfactory.cs, CodonFactory has only two ways. The AddConbuilder method adds the CodonBuilder to a HashTable indexed with CodonName. Another way is very important: public
ICodon Createcodon (Addin Add, XMLNode Codonnode)
{CodonBuilder builder = codonHashtable [codonNode.Name] as CodonBuilder; if (! Builder = null) {return builder.BuildCodon (addIn);} throw new CodonNotFoundException (String.Format ( "no codon builder found for <{0}>" , Codonnode.name);
Here, addin is the description of this configuration file (that is, plugin), and what is the CodonNode of this XMLNode type? Remember the labels of
<
Extension
Path
= "/ SharpDevelop / Workbench / MainMenu / Tools"
>
<
Menuitem
id
= "AddIntreeView"
label
= "View addINtree"
Class
= "Addins.addintreeView.AddintreeViewCommand"
/>
Extension
>
SHARPDevelop After reading the
CodonBuilder Builder
=
Codonhashtable [CodonNode.Name]
AS
Codonbuilder;
Find the corresponding CodonBuilder based on the name of the node (Menuitem). Remember the CodonBuilder in front to get Menuitemcodon's CodonName based on CodonnameAttribute? It is this Menuitem. CodonFactory found Codonbuilder's CodonBuilder (this is built in the DEFAULTADDINTREE constructor "to create and join CodonFactory, remember?), Then use this CodonBuilder to establish the corresponding Codon and return it to the caller . In this way, through CodonNameAttribute, SharpDevelop, the
Ok, see how the flexibility of the plugin in SharpDevelop is embodied. First, the Codon node name under the ExtensIn configuration is not linked in the code and the specific CODON class, but linked with Codon with CodonNameAttribute. The advantage of this is that SharpDevelop's Codon and XML tags have unlimited extension capabilities. Suppose we have to define a Codon class SplashFormCodon role to specify a form of a form as a system startup. It is very simple to do: First, use CodonNameAttribute to specify CodonName to SPLASH in SplashFormCodon and define the properties you need in SplashFormCodon. Then, write this in the address of the AddIn configuration file:
<
Extension
Path
= "/ Sharpdevelop /"
>
<
Splash
id
= "Mysplashform"
Class
= "Mysplashformclass"
/>
Extension
>
Is not it simple? In addition, the treatment of Condition is also the same, that is, we can also use similar methods to flexibly join your definition conditions.
Here I have a small question: I don't know if I understand the design pattern. I feel that the implementation of the CodonBuilder class does not seem to be as implied by its class name, but it seems Should be Proxy mode, so I think it is easier to understand if it is called CodonProxy? What do you think? In addition, although a little bit is slightly, I think the configuration will make us more easier to associate more than the code: <
Extension
Path
= "/ Sharpdevelop /"
>
<
Codon
Name
= "Splash"
id
= "Mysplashform"
Class
= "Mysplashformclass"
/>
Extension
>
2.2 The main line (AddIte "ingleton. Createaddintree), I am a bit tired. But let's continue the code of CreateAdDintree in AddintDreamingleton. After establishing the instance of defaultadDintree, AddINtreSinglet is searched for files for .addin in the plugin directory. I still remember that ADDINTREESINGLETON. SETDIRECTORIES was called in SharpDevelop's Main function. It is a search for this incoming directory. It seems that SharpDevelop as a plugin for all the suffixes in the plug-in directory as .addin.
FileUtilityService FileUtilityService
=
FileUtilityService) ServiceManager.Services.getService
Typeof
FileUtilityService)))
First learn how to get what you need from ServiceManager, in which a service is available in this way in SharpDevelop. Call GetService Incorporation of the type of service class to get as a parameter, returns an iService interface, and then converts into needed services.
After finding an Addin file, call INSERTDINS to add the configuration in this addin file to the directory tree.
Static
StringCollection Insertdins (StringCollection AddInfiles)
{StringCollection retryList = new StringCollection (); foreach (string addInFile in addInFiles) {AddIn addIn = new AddIn (); try {addIn.Initialize (addInFile); addInTree.InsertAddIn (addIn);} catch (CodonNotFoundException) {retryList.Add (addinfile) {RetryList.add (address);} catch (exception e) {throw new addinitializeException (addinfile, e);}}
INSERTADDINS establishes a corresponding Addin (plugin), calling the addintree's InsertDin method to hang it into the plugin tree. There is a small process here, since it is a class corresponding to the Codon's label in the ASSEMBLY search and the plug-in configuration, and the Assembly in the Codon class is imported through the Import tag. Therefore, when the Codon class corresponding to a Codon tag in the configuration, the file where the CODON class is located is imported in other addin files. At this time, I will find CodonBuilder in the front branch line to find CodonBuilder, so you have to find CodonBuilder correctly after the AddIN processing in the Codon class. This is a problem of depends on relationships. SharpDevelop is a relatively simple, when you call the InsertDins method, when CodonNotFoundException occurs, you will join a RetryList list to return. After CreateAddIntree processing all the addin files, recirculate the ADDIN in the Retrylist list. If you can't succeed in the retrorelist in a certain loop, you will be prompted to fail. Let's look back to see the processing of addIN.
2.2.1 addin.initialize (initialization of addin) After the instance of addIN, call the Initialize method for initialization. Addin is a package for a .addin file, defined in the /src/main/core/addins/addin.cs file. It contains the description of the root element
If the child node is the Runtime node, call the AddRuntiMelibralies method.
Foreach
(
Object
o
in
El.childNodes)
.? {XmlElement curEl = (XmlElement) o; string assemblyName = curEl.Attributes [ "assembly"] InnerText; string pathName = Path.IsPathRooted (assemblyName) assemblyName: fileUtilityService.GetDirectoryNameWithSeparator (path) assemblyName; Assembly asm = AddInTreeSingleton.AddInTree .Loadassembly (pathname); Runtimelibraries [assemblyname] = asm;}
By adding all Codon and Condition subclasses in Assembly into the corresponding Factory class via the addintAassembly method (call the loadCodonsandConditions method, we have seen this file and the corresponding Assembly in the defaultaddintree) In the Runtimelibralies list. If the child node is an Extension node, call the AddExtensions method.
Extension e
=
New
Extension (El.attributes "
"
Path
"
] .Innertext); AddCodonStoextension (E, EL,
New
ConditionCollection (); extensions.add (e);
The XML description of this extension is created in the ExtensIn's extensions list and adds the Codon included to the created Extension object through the AddCodonStoExtension method. The extension object is an inline embedded class, one of which is the list of CodonCollection. AddCodonStoExtension is to save the Codon that appears in the configuration to save it in this list.
Let's take a look at the AddCodonStoExtension method. In the code, I have slightly analyzed the processing of the Condition and some irrelevant parts, we focus on the processing of the plugin. The first is a Foreach (Object O IN EL.CHILDES) traversed all child nodes under
Xmlelement Curel
=
XMLELEMENT O;
Switch
(curel.name)
{(Processing conditions) default: ICodon codon = AddInTreeSingleton.AddInTree.CodonFactory.CreateCodon (this, curEl); AutoInitializeAttributes (codon, curEl); (codon.InsertBefore processing codon.InsertAfter and, mainly in the processing list codon The order of the order, this is more important for the process of MenuItemCodon) E.CODONCOLLECTION.ADD (CODON); if (curel.childnodes.count> 0) {EXTENSION NEWEXTENSION = New Extension (E.PATH '/' CODON.ID); AddCodonstoextension (NewExtension, Curel, Condition); extensions.add (newextension);} Break;
We have seen a long-awaited call
AddinTreeSingleton.addintree.codonfactory.createcodon
THIS
CUREL);
After the pavement in the above branch 2.1 code, SharpDevelop uses the created CodonFactory, call the CreateCodon method to construct the actual Codon object according to the nodes under
Addins.Add (addin);
Foreach
(Addin.extension Extension
in
Addin.extensions
{AddExtensions (extension);
In DefaultAddIntree, two lessons are saved. One is a tree formed according to the structure of the plugin file, each plugin file as a root node, and the down is an Extension, Codon node. Addins.Add (add); adds the plugin to this tree structure. Another tree is constructed as a path based on the Path Codon of Extension, and each tree node is an AddINTreenode class that contains the Codon object on this path. Codon nested in this node is accessed through its child node. You can specify a path to get a path for a certain node on a plug-in tree in DefaultAddIndreE. The AddExtensions method is very simple, traversed all Codon in Extension, creates all nodes on this path as the path, creates all nodes on this path, and connects Codon to this addINtreenode. Since Codon's ID is a globally unique, each ADDINTREENODE has a unique Codon.
3, the last km (Condon and Command) In the discussion of the plug-in tree, we associate the ADDIN-Extension-Codon configuration with the classes they correspond to the classes. However, we have not involved how Codon and how it contains how it is associated. Because of this association is to call in external plugins tree (remember telling SharpDevelop program entry Main function, method InitializeServicesSubsystem ServiceManager mention of it? AddServices ((IService []) AddInTreeSingleton.AddInTree.GetTreeNode (servicesPath) .BuildChildItems (this). ToARRAY (iService)))))))))))))))) This is called to call it here, so separately here. To achieve this association is the BuildChildItems and buildchildITEM methods of Addintreenode and the Codon's builditem method. BuildChildItem methods and methods BuildChildItems only one word, BuildChildItem is to find the node containing at Codon belongs AddInTreeNode child nodes according Codon specified ID and call BuildItem method Codon; and BuildChildItems is first traversed all belong AddInTreeNode The child node, call the Codon's builditem method of each child node, then call the Codon's builditem method of the ADDINTREENODE (that is, a tree is traversed). Focus on Codon's builditem method. In AbstractCodon, this method is an abstract method, and there is not clear that this method is made in the code annotation of SharpDevelop. But we can find a Codon instance to see. For example, Classcodon's buildItem: PublicItem: Public
Override
Object
BuildItem
Object
Owner, ArrayList Subitems, ConditionCollection Conditions
{System.diagnostics.debug.assert (Class! = Null && class.Length> 0); returniful.createObject (class);}
Call AddIn's CreateObject, incoming Class (class name) of Codon as a parameter, establish an instance of this class. For example this configuration
<
Extension
Path
= "/ Workspace / AutoStart"
>
<
Class
id
= "InitializeWorkBenchCommand"
Class
= "Icsharpcode.sharpDevelop.commands.initializeworkbenchcommand"
/>
Extension
>
The Class attribute in Codon is ICsharpcode.sharpDevelop.commands.initializeWorkBenchCommand. That is, Codon's class refers to the name of the Command class that implements the specific function module. When reading the
Next book, you should analyze the services in SharpDevelop at a request of a netizen.