31
31
using System.Collections;
32
using System.Collections.Generic;
33
34
using System.Reflection;
34
35
using Mono.Addins.Description;
36
37
namespace Mono.Addins
40
/// A node of the extension model.
43
/// An extension node is an element registered by an add-in in an extension point.
44
/// A host can get nodes registered in an extension point using methods such as
45
/// AddinManager.GetExtensionNodes(string), which returns a collection of ExtensionNode objects.
47
/// ExtensionNode will normally be used as a base class of more complex extension point types.
48
/// The most common subclass is Mono.Addins.TypeExtensionNode, which allows registering a class
49
/// implemented in an add-in.
38
51
public class ExtensionNode
40
53
bool childrenLoaded;
43
56
RuntimeAddin addin;
45
58
ExtensionNodeType nodeType;
59
ModuleDescription module;
60
AddinEngine addinEngine;
46
61
event ExtensionNodeEventHandler extensionNodeChanged;
64
/// Identifier of the node.
67
/// It is not mandatory to specify an 'id' for a node. When none is provided,
68
/// the add-in manager will automatically generate an unique id for the node.
69
/// The ExtensionNode.HasId property can be used to know if the 'id' has been
70
/// specified by the developer or not.
49
73
get { return treeNode != null ? treeNode.Id : string.Empty; }
77
/// Location of this node in the extension tree.
80
/// The node path is composed by the path of the extension point where it is defined,
81
/// the identifiers of its parent nodes, and its own identifier.
52
83
public string Path {
53
84
get { return treeNode != null ? treeNode.GetPath () : string.Empty; }
88
/// Parent node of this node.
56
90
public ExtensionNode Parent {
58
92
if (treeNode != null && treeNode.Parent != null)
89
137
get { return treeNode; }
141
/// The add-in that registered this extension node.
144
/// This property provides access to the resources and types of the add-in that created this extension node.
92
146
public RuntimeAddin Addin {
94
148
if (addin == null && addinId != null) {
95
if (!AddinManager.SessionService.IsAddinLoaded (addinId))
96
AddinManager.SessionService.LoadAddin (null, addinId, true);
97
addin = AddinManager.SessionService.GetAddin (addinId);
149
if (!addinEngine.IsAddinLoaded (addinId))
150
addinEngine.LoadAddin (null, addinId, true);
151
addin = addinEngine.GetAddin (addinId);
153
addin = addin.GetModule (module);
99
155
if (addin == null)
100
156
throw new InvalidOperationException ("Add-in '" + addinId + "' could not be loaded.");
228
/// Returns the child objects of a node.
231
/// An array of child objects.
234
/// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode.
235
/// The returned array is composed by all objects created by calling the
236
/// TypeExtensionNode.GetInstance() method for each node.
162
238
public object[] GetChildObjects ()
164
240
return GetChildObjects (typeof(object), true);
244
/// Returns the child objects of a node.
246
/// <param name="reuseCachedInstance">
247
/// True if the method can reuse instances created in previous calls.
250
/// An array of child objects.
253
/// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode.
254
/// The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance()
255
/// method for each node (or TypeExtensionNode.GetInstance() if reuseCachedInstance is set to true).
167
257
public object[] GetChildObjects (bool reuseCachedInstance)
169
259
return GetChildObjects (typeof(object), reuseCachedInstance);
263
/// Returns the child objects of a node (with type check).
265
/// <param name="arrayElementType">
266
/// Type of the return array elements.
269
/// An array of child objects.
272
/// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode.
273
/// The returned array is composed by all objects created by calling the
274
/// TypeExtensionNode.GetInstance(Type) method for each node.
276
/// An InvalidOperationException exception is thrown if one of the found child objects is not a
277
/// subclass of the provided type.
172
279
public object[] GetChildObjects (Type arrayElementType)
174
281
return GetChildObjects (arrayElementType, true);
285
/// Returns the child objects of a node (casting to the specified type)
288
/// An array of child objects.
291
/// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode.
292
/// The returned array is composed by all objects created by calling the
293
/// TypeExtensionNode.GetInstance() method for each node.
295
public T[] GetChildObjects<T> ()
297
return (T[]) GetChildObjectsInternal (typeof(T), true);
300
/// Returns the child objects of a node (with type check).
302
/// <param name="arrayElementType">
303
/// Type of the return array elements.
305
/// <param name="reuseCachedInstance">
306
/// True if the method can reuse instances created in previous calls.
309
/// An array of child objects.
312
/// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode.
313
/// The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance(Type)
314
/// method for each node (or TypeExtensionNode.GetInstance(Type) if reuseCachedInstance is set to true).
316
/// An InvalidOperationException exception will be thrown if one of the found child objects is not a subclass
317
/// of the provided type.
177
319
public object[] GetChildObjects (Type arrayElementType, bool reuseCachedInstance)
321
return (object[]) GetChildObjectsInternal (arrayElementType, reuseCachedInstance);
325
/// Returns the child objects of a node (casting to the specified type).
327
/// <param name="reuseCachedInstance">
328
/// True if the method can reuse instances created in previous calls.
331
/// An array of child objects.
334
/// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode.
335
/// The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance()
336
/// method for each node (or TypeExtensionNode.GetInstance() if reuseCachedInstance is set to true).
338
public T[] GetChildObjects<T> (bool reuseCachedInstance)
340
return (T[]) GetChildObjectsInternal (typeof(T), reuseCachedInstance);
343
Array GetChildObjectsInternal (Type arrayElementType, bool reuseCachedInstance)
179
345
ArrayList list = new ArrayList (ChildNodes.Count);
181
347
for (int n=0; n<ChildNodes.Count; n++) {
182
348
InstanceExtensionNode node = ChildNodes [n] as InstanceExtensionNode;
183
349
if (node == null) {
184
AddinManager.ReportError ("Error while getting object for node in path '" + Path + "'. Extension node is not a subclass of InstanceExtensionNode.", null, null, false);
350
addinEngine.ReportError ("Error while getting object for node in path '" + Path + "'. Extension node is not a subclass of InstanceExtensionNode.", null, null, false);
192
358
list.Add (node.CreateInstance (arrayElementType));
194
360
catch (Exception ex) {
195
AddinManager.ReportError ("Error while getting object for node in path '" + Path + "'.", null, ex, false);
361
addinEngine.ReportError ("Error while getting object for node in path '" + Path + "'.", node.AddinId, ex, false);
198
return (object[]) list.ToArray (arrayElementType);
364
return list.ToArray (arrayElementType);
368
/// Reads the extension node data
370
/// <param name='elem'>
371
/// The element containing the extension data
374
/// This method can be overriden to provide a custom method for reading extension node data from an element.
375
/// The default implementation reads the attributes if the element and assigns the values to the fields
376
/// and properties of the extension node that have the corresponding [NodeAttribute] decoration.
201
378
internal protected virtual void Read (NodeElement elem)
203
if (nodeType == null || nodeType.Fields == null)
380
if (nodeType == null)
206
383
NodeAttribute[] attributes = elem.Attributes;
207
Hashtable fields = (Hashtable) nodeType.Fields.Clone ();
384
ReadObject (this, attributes, nodeType.Fields);
386
if (nodeType.CustomAttributeMember != null) {
387
object att = Activator.CreateInstance (nodeType.CustomAttributeMember.MemberType);
388
ReadObject (att, attributes, nodeType.CustomAttributeFields);
389
nodeType.CustomAttributeMember.SetValue (this, att);
393
void ReadObject (object ob, NodeAttribute[] attributes, Dictionary<string,ExtensionNodeType.FieldData> fields)
398
// Make a copy because we are going to remove fields that have been used
399
fields = new Dictionary<string,ExtensionNodeType.FieldData> (fields);
209
401
foreach (NodeAttribute at in attributes) {
211
ExtensionNodeType.FieldData f = (ExtensionNodeType.FieldData) fields [at.name];
403
ExtensionNodeType.FieldData f;
404
if (!fields.TryGetValue (at.name, out f))
215
407
fields.Remove (at.name);
410
Type memberType = f.MemberType;
219
if (f.Field.FieldType == typeof(string)) {
412
if (memberType == typeof(string)) {
220
413
if (f.Localizable)
221
414
val = Addin.Localizer.GetString (at.value);
225
else if (f.Field.FieldType == typeof(string[])) {
418
else if (memberType == typeof(string[])) {
226
419
string[] ss = at.value.Split (',');
227
420
if (ss.Length == 0 && ss[0].Length == 0)
228
421
val = new string [0];
235
else if (f.Field.FieldType.IsEnum) {
236
val = Enum.Parse (f.Field.FieldType, at.value);
428
else if (memberType.IsEnum) {
429
val = Enum.Parse (memberType, at.value);
240
val = Convert.ChangeType (at.Value, f.Field.FieldType);
433
val = Convert.ChangeType (at.Value, memberType);
241
434
} catch (InvalidCastException) {
242
throw new InvalidOperationException ("Property type not supported by [NodeAttribute]: " + f.Field.DeclaringType + "." + f.Field.Name);
435
throw new InvalidOperationException ("Property type not supported by [NodeAttribute]: " + f.Member.DeclaringType + "." + f.Member.Name);
246
f.Field.SetValue (this, val);
439
f.SetValue (ob, val);
249
442
if (fields.Count > 0) {
250
443
// Check if one of the remaining fields is mandatory
251
foreach (DictionaryEntry e in fields) {
252
ExtensionNodeType.FieldData f = (ExtensionNodeType.FieldData) e.Value;
444
foreach (KeyValuePair<string,ExtensionNodeType.FieldData> e in fields) {
445
ExtensionNodeType.FieldData f = e.Value;
254
447
throw new InvalidOperationException ("Required attribute '" + e.Key + "' not found.");
286
// Called when the add-in that defined this extension node is actually
480
/// Called when the add-in that defined this extension node is actually loaded in memory.
288
482
internal protected virtual void OnAddinLoaded ()
292
// Called when the add-in that defined this extension node is being
293
// unloaded from memory.
487
/// Called when the add-in that defined this extension node is being
488
/// unloaded from memory.
294
490
internal protected virtual void OnAddinUnloaded ()
298
// Called when the children list of this node has changed. It may be due to add-ins
299
// being loaded/unloaded, or to conditions being changed.
495
/// Called when the children list of this node has changed. It may be due to add-ins
496
/// being loaded/unloaded, or to conditions being changed.
300
498
protected virtual void OnChildrenChanged ()
503
/// Called when a child node is added
505
/// <param name="node">
304
508
protected virtual void OnChildNodeAdded (ExtensionNode node)
306
510
if (extensionNodeChanged != null)
307
511
extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node));
515
/// Called when a child node is removed
517
/// <param name="node">
310
520
protected virtual void OnChildNodeRemoved (ExtensionNode node)
312
522
if (extensionNodeChanged != null)
313
523
extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Remove, node));
528
/// An extension node with custom metadata
531
/// This is the default type for extension nodes bound to a custom extension attribute.
533
public class ExtensionNode<T>: ExtensionNode where T:CustomExtensionAttribute
538
/// The custom attribute containing the extension metadata
543
internal set { data = value; }