The AddIn tree is a simple tree data structure. There is only one AddIn tree for each instance of
SharpDevelop. This is the reason for implementing it by using the singleton design pattern
This diagram will give us the basic idea of add-ins; add-ins just plug into the tree and the tree contains the
whole system. Physically, an add-in is defined by an XML file and a set of DLLs that are referenced by the
XML. The DLLs provide the code, while the XML defines how and where it plugs into the AddIn tree.
The AddIn tree is a tree that "binds them all".The add-ins are the nodes of the AddIn tree
All SharpDevelop parts that use the AddIn tree must know the path from which they can obtain the required
nodes.
In other words, the
add-in that defines a particular path also gets to define the (runtime) interface of the nodes that can be
inserted below it.
All visible elements in
SharpDevelop (and most invisible ones too; for example, keyboard commands in the text editor, like cursor
keys, are implemented as nodes in the add-in tree) are defined by nodes.
All SharpDevelop parts that use the AddIn tree must know the path from which they can obtain the required
nodes.
In other words, the
add-in that defines a particular path also gets to define the (runtime) interface of the nodes that can be
inserted below it.
All visible elements in
SharpDevelop (and most invisible ones too; for example, keyboard commands in the text editor, like cursor
keys, are implemented as nodes in the add-in tree) are defined by nodes.
Advantages of Using the AddIn Tree
- It allows extension of existing add-ins by other add-ins.
- Another advantage of the AddIn tree is that the assembly files containing the executable
code need not reside in one directory. - Also with this approach, the add-ins don't have to implement their own add-in structure, as all
the add-ins are based on only one system – The AddIn tree.
good extensibility for any further use of other toolkits. The GUI layer is only implemented when is actually needed by the application.
The AddIn Tree Superstructure
public interface IAddInTree
{
// these factories:ConditionFactory and CodonFactory create our AddIn tree node contents.
///
/// Returns the default condition factory.
///
ConditionFactory ConditionFactory
{
get ;
}
///
/// Returns the default codon factory.
///
CodonFactory CodonFactory
{
get ;
}
///
/// Returns a collection of all loaded add-ins.
///
AddInCollection AddIns
{
get ;
}
///
/// Returns a TreeNode corresponding to
/// This is the only method needed by the add-ins to use the AddIn tree.
///
///
IAddInTreeNode GetTreeNode(string path);
// The InsertAddIn and RemoveAddIn methods may be useful for implementing an add-in manager in theIDE
///
/// Inserts an AddIn into the AddInTree.
///
///
void InsertAddIn(AddIn addIn);
///
/// Removes an AddIn from the AddInTree.
///
void RemoveAddIn(AddIn addIn);
///
/// This method does load all codons and conditions in the given assembly.
///
{
// these factories:ConditionFactory and CodonFactory create our AddIn tree node contents.
///
/// Returns the default condition factory.
///
ConditionFactory ConditionFactory
{
get ;
}
///
/// Returns the default codon factory.
///
CodonFactory CodonFactory
{
get ;
}
///
/// Returns a collection of all loaded add-ins.
///
AddInCollection AddIns
{
get ;
}
///
/// Returns a TreeNode corresponding to
path ./// This is the only method needed by the add-ins to use the AddIn tree.
///
///
IAddInTreeNode GetTreeNode(string path);
// The InsertAddIn and RemoveAddIn methods may be useful for implementing an add-in manager in theIDE
///
/// Inserts an AddIn into the AddInTree.
///
///
void InsertAddIn(AddIn addIn);
///
/// Removes an AddIn from the AddInTree.
///
void RemoveAddIn(AddIn addIn);
///
/// This method does load all codons and conditions in the given assembly.
///
Assembly LoadAssembly(string assemblyFile);
}
}
Add-in Definition
< AddIn name = "Typed Collection Wizard"
author = "Mike Krueger"
copyright = "GPL"
url = "unknown"
description = "Creates a typed collection"
version = "1.0.0">
<Runtime>
<Import assembly="TypedCollectionWizard.dll" />
Runtime>
<Extension path = "/SharpDevelop/Templates/File/TypedCollection" >
<DialogPanel id = "CollectionGenerator"
label = "Typed Collection"
class = "TypedCollectionGenerator.TypedCollectionWizardPanel"/>
Extension>
AddIn>
public interface ICodon
{
// returns the add-in in which this codon object was declared
AddIn AddIn
{
get;
set;
}
// returns the name of the xml node of this codon. (it is the same
// for each type of codon (the name of the XML tag inside
// the add-in file))
//Each codon class must have a unique name.
string Name
{
get;
}
///
/// The ID is the name of the codon object inside the tree (codons are referenced by their ID), therefore no two
/// codons can have the same ID when they are stored under the same AddIn tree path. This ID comes from the
/// XML attribute CollectionGenerator of the DialogPanel, which we saw before.
/// returns the ID of this codon object.
///
string ID
{
get;
}
// returns the Class which is used in the action corresponding to
// this codon (may return null, if no action for this codon is
// given)
string Class
{
get;
}
// Insert this codon after all the codons defined in this string
// array
string[] InsertAfter
{
get;
set;
}
// Insert this codon before the codons defined in this string array
string[] InsertBefore
{
get;
}
//The only method that a codon must have is the BuildItem method.
// Creates an item (=object) with the specified sub items and
// the current Condition status for this item.
object BuildItem( object owner, ArrayList subItems,
ConditionFailedAction action);
}
{
// returns the add-in in which this codon object was declared
AddIn AddIn
{
get;
set;
}
// returns the name of the xml node of this codon. (it is the same
// for each type of codon (the name of the XML tag inside
// the add-in file))
//Each codon class must have a unique name.
string Name
{
get;
}
///
/// The ID is the name of the codon object inside the tree (codons are referenced by their ID), therefore no two
/// codons can have the same ID when they are stored under the same AddIn tree path. This ID comes from the
/// XML attribute CollectionGenerator of the DialogPanel, which we saw before.
/// returns the ID of this codon object.
///
string ID
{
get;
}
// returns the Class which is used in the action corresponding to
// this codon (may return null, if no action for this codon is
// given)
string Class
{
get;
}
// Insert this codon after all the codons defined in this string
// array
string[] InsertAfter
{
get;
set;
}
// Insert this codon before the codons defined in this string array
string[] InsertBefore
{
get;
}
//The only method that a codon must have is the BuildItem method.
// Creates an item (=object) with the specified sub items and
// the current Condition status for this item.
object BuildItem( object owner, ArrayList subItems,
ConditionFailedAction action);
}
Codons can define additional attributes, the values of which they obtain from the XML definition, and can
apply them to the objects that they build. The AddIn tree puts these build codons together so that they can be used outside the tree structure.
Now we will take a look at the manner in which a codon class flags the add-in system that it has attributes that need to be read from the codon XML node. flagging fields by using attributes.
We use the custom attributes feature of C# to define which attributes a codon has, because we need to make it explicit when a codon has mandatory attributes.
public abstract class AbstractCodon : ICodon
{
[XmlMemberAttributeAttribute( "id", IsRequired = true)]
string id = null;
[XmlMemberAttributeAttribute( "class")]
string myClass = null ;
[XmlMemberArrayAttribute("insertafter" )]
string[] insertafter = null;
[XmlMemberArrayAttribute("insertbefore " )]
string[] insertbefore = null;
// Canonical get/set properties for all attributes seen above are taken out.
// Creates an item with the specified sub items and the current Condition status for this item.
public abstract object BuildItem(object owner, ArrayList subItems, ConditionFailedAction action);
}
{
[XmlMemberAttributeAttribute( "id", IsRequired = true)]
string id = null;
[XmlMemberAttributeAttribute( "class")]
string myClass = null ;
[XmlMemberArrayAttribute("insertafter" )]
string[] insertafter = null;
[XmlMemberArrayAttribute("insertbefore " )]
string[] insertbefore = null;
// Canonical get/set properties for all attributes seen above are taken out.
// Creates an item with the specified sub items and the current Condition status for this item.
public abstract object BuildItem(object owner, ArrayList subItems, ConditionFailedAction action);
}
Codons have a class attribute, CodonNameAttribute, applied that has only one parameter – the codon name. This is the name of the codon XML node.
From Tree Node to Running Object (74)
public interface IAddInTreeNode
{
// A hash table containing the child nodes.
Hashtable ChildNodes {
get;
}
// A codon defined in this node
ICodon Codon {
get;
}
// All conditions for this TreeNode.
ConditionCollection ConditionCollection {
get;
}
// Gets the current ConditionFailedAction
ConditionFailedAction GetCurrentConditionFailedAction(object caller);
// Builds all child items of this node using the <code>BuildItem</code>
// method of each codon in the child tree.
ArrayList BuildChildItems(object caller);
// Builds one child item of this node using the <code>BuildItem</code>
// method of the codon in the child tree. The sub item with the ID
object BuildChildItem(string childItemID, object caller);
}
{
// A hash table containing the child nodes.
Hashtable ChildNodes {
get;
}
// A codon defined in this node
ICodon Codon {
get;
}
// All conditions for this TreeNode.
ConditionCollection ConditionCollection {
get;
}
// Gets the current ConditionFailedAction
ConditionFailedAction GetCurrentConditionFailedAction(object caller);
// Builds all child items of this node using the <code>BuildItem</code>
// method of each codon in the child tree.
ArrayList BuildChildItems(object caller);
// Builds one child item of this node using the <code>BuildItem</code>
// method of the codon in the child tree. The sub item with the ID
object BuildChildItem(string childItemID, object caller);
}
The BuildChildItems method calls BuildItem on all child codons (if any) and returns them as an ArrayList.
Note that the subItems are created recursively with this method (called on the child) and that the codon BuildItem method must handle the sub-items itself. Each tree node can have sub-items and during the object creation these sub-items must be handled.
Codon Creation
The system allows new codons to be defined by add-ins. This ensures much more flexibility than defining a static set of codons inside the core assembly. When an assembly is imported by the add-in subsystem, it is scanned for types that have the CodonNameAttribute attached and are a subclass of the AbstractCodon class.
The codon responsible for a specific XML node is determined by the node name. This node name and the codon name, which is given by the CodonNameAttribute, must be equal. The core locates the codon that has a CodonNameAttribute, which matches the XML node name.
Conditions
Currently there are only three possible options for a condition – Nothing, Exclude, and Disable. These actions occur only when the condition evaluates to false.
- Nothing does nothing, if the condition fails. If we choose this, it will be similar to not using a condition. This is only included for the sake of completeness.
- If no action attribute is specified, Exclude is the default action.It merely removes the item virtually from the AddIn tree when the BuildItem method is called.
- Disable tries to disable the item. The codon object must handle the disable case by itself. Only the object knows whether it can be disabled and how this happens.
Happy day, happy life!
No comments:
Post a Comment