~ubuntu-branches/ubuntu/trusty/mono-addins/trusty-proposed

« back to all changes in this revision

Viewing changes to Mono.Addins/Mono.Addins/ExtensionNode.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-04-25 11:11:33 UTC
  • mfrom: (4.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20110425111133-t05u5p7o5fxx70fu
Tags: 0.6-2
Upload to Unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
 
30
30
using System;
31
31
using System.Collections;
 
32
using System.Collections.Generic;
32
33
using System.Xml;
33
34
using System.Reflection;
34
35
using Mono.Addins.Description;
35
36
 
36
37
namespace Mono.Addins
37
38
{
 
39
        /// <summary>
 
40
        /// A node of the extension model.
 
41
        /// </summary>
 
42
        /// <remarks>
 
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.
 
46
        /// 
 
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.
 
50
        /// </remarks>
38
51
        public class ExtensionNode
39
52
        {
40
53
                bool childrenLoaded;
43
56
                RuntimeAddin addin;
44
57
                string addinId;
45
58
                ExtensionNodeType nodeType;
 
59
                ModuleDescription module;
 
60
                AddinEngine addinEngine;
46
61
                event ExtensionNodeEventHandler extensionNodeChanged;
47
62
                
 
63
                /// <summary>
 
64
                /// Identifier of the node.
 
65
                /// </summary>
 
66
                /// <remarks>
 
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.
 
71
                /// </remarks>
48
72
                public string Id {
49
73
                        get { return treeNode != null ? treeNode.Id : string.Empty; }
50
74
                }
51
75
                
 
76
                /// <summary>
 
77
                /// Location of this node in the extension tree.
 
78
                /// </summary>
 
79
                /// <remarks>
 
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.
 
82
                /// </remarks>
52
83
                public string Path {
53
84
                        get { return treeNode != null ? treeNode.GetPath () : string.Empty; }
54
85
                }
55
86
                
 
87
                /// <summary>
 
88
                /// Parent node of this node.
 
89
                /// </summary>
56
90
                public ExtensionNode Parent {
57
91
                        get {
58
92
                                if (treeNode != null && treeNode.Parent != null)
62
96
                        }
63
97
                }
64
98
                
 
99
                /// <summary>
 
100
                /// Extension context to which this node belongs
 
101
                /// </summary>
65
102
                public ExtensionContext ExtensionContext {
66
103
                        get { return treeNode.Context; }
67
104
                }
68
105
                
 
106
                /// <summary>
 
107
                /// Specifies whether the extension node has as an Id or not.
 
108
                /// </summary>
 
109
                /// <remarks>
 
110
                /// It is not mandatory to specify an 'id' for a node. When none is provided,
 
111
                /// the add-in manager will automatically generate an unique id for the node.
 
112
                /// This property will return true if an 'id' was provided for the node, and
 
113
                /// false if the id was assigned by the add-in manager.
 
114
                /// </remarks>
69
115
                public bool HasId {
70
116
                        get { return !Id.StartsWith (ExtensionTree.AutoIdPrefix); }
71
117
                }
75
121
                        treeNode = node;
76
122
                }
77
123
                
78
 
                internal void SetData (string plugid, ExtensionNodeType nodeType)
 
124
                internal void SetData (AddinEngine addinEngine, string plugid, ExtensionNodeType nodeType, ModuleDescription module)
79
125
                {
 
126
                        this.addinEngine = addinEngine;
80
127
                        this.addinId = plugid;
81
128
                        this.nodeType = nodeType;
 
129
                        this.module = module;
82
130
                }
83
131
                
84
132
                internal string AddinId {
89
137
                        get { return treeNode; }
90
138
                }
91
139
                
 
140
                /// <summary>
 
141
                /// The add-in that registered this extension node.
 
142
                /// </summary>
 
143
                /// <remarks>
 
144
                /// This property provides access to the resources and types of the add-in that created this extension node.
 
145
                /// </remarks>
92
146
                public RuntimeAddin Addin {
93
147
                        get {
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);
 
152
                                        if (addin != null)
 
153
                                                addin = addin.GetModule (module);
98
154
                                }
99
155
                                if (addin == null)
100
156
                                        throw new InvalidOperationException ("Add-in '" + addinId + "' could not be loaded.");
102
158
                        }
103
159
                }
104
160
                
 
161
                /// <summary>
 
162
                /// Notifies that a child node of this node has been added or removed.
 
163
                /// </summary>
 
164
                /// <remarks>
 
165
                /// The first time the event is subscribed, the handler will be called for each existing node.
 
166
                /// </remarks>
105
167
                public event ExtensionNodeEventHandler ExtensionNodeChanged {
106
168
                        add {
107
169
                                extensionNodeChanged += value;
109
171
                                        try {
110
172
                                                value (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node));
111
173
                                        } catch (Exception ex) {
112
 
                                                AddinManager.ReportError (null, node.Addin != null ? node.Addin.Id : null, ex, false);
 
174
                                                addinEngine.ReportError (null, node.Addin != null ? node.Addin.Id : null, ex, false);
113
175
                                        }
114
176
                                }
115
177
                        }
118
180
                        }
119
181
                }
120
182
                
 
183
                /// <summary>
 
184
                /// Child nodes of this extension node.
 
185
                /// </summary>
121
186
                public ExtensionNodeList ChildNodes {
122
187
                        get {
123
188
                                if (childrenLoaded)
130
195
                                        }
131
196
                                }
132
197
                                catch (Exception ex) {
133
 
                                        AddinManager.ReportError (null, null, ex, false);
 
198
                                        addinEngine.ReportError (null, null, ex, false);
134
199
                                        childNodes = ExtensionNodeList.Empty;
135
200
                                        return childNodes;
136
201
                                } finally {
137
202
                                        childrenLoaded = true;
138
203
                                }
139
204
 
140
 
                                ArrayList list = new ArrayList ();
 
205
                                List<ExtensionNode> list = new List<ExtensionNode> ();
141
206
                                foreach (TreeNode cn in treeNode.Children) {
142
207
                                        
143
208
                                        // For each node check if it is visible for the current context.
147
212
                                                if (cn.ExtensionNode != null && cn.IsEnabled)
148
213
                                                        list.Add (cn.ExtensionNode);
149
214
                                        } catch (Exception ex) {
150
 
                                                AddinManager.ReportError (null, null, ex, false);
 
215
                                                addinEngine.ReportError (null, null, ex, false);
151
216
                                        }
152
217
                                }
153
218
                                if (list.Count > 0)
159
224
                        }
160
225
                }
161
226
                
 
227
                /// <summary>
 
228
                /// Returns the child objects of a node.
 
229
                /// </summary>
 
230
                /// <returns>
 
231
                /// An array of child objects.
 
232
                /// </returns>
 
233
                /// <remarks>
 
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.
 
237
                /// </remarks>
162
238
                public object[] GetChildObjects ()
163
239
                {
164
240
                        return GetChildObjects (typeof(object), true);
165
241
                }
166
242
                
 
243
                /// <summary>
 
244
                /// Returns the child objects of a node.
 
245
                /// </summary>
 
246
                /// <param name="reuseCachedInstance">
 
247
                /// True if the method can reuse instances created in previous calls.
 
248
                /// </param>
 
249
                /// <returns>
 
250
                /// An array of child objects.
 
251
                /// </returns>
 
252
                /// <remarks>
 
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).
 
256
                /// </remarks>
167
257
                public object[] GetChildObjects (bool reuseCachedInstance)
168
258
                {
169
259
                        return GetChildObjects (typeof(object), reuseCachedInstance);
170
260
                }
171
261
                
 
262
                /// <summary>
 
263
                /// Returns the child objects of a node (with type check).
 
264
                /// </summary>
 
265
                /// <param name="arrayElementType">
 
266
                /// Type of the return array elements.
 
267
                /// </param>
 
268
                /// <returns>
 
269
                /// An array of child objects.
 
270
                /// </returns>
 
271
                /// <remarks>
 
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.
 
275
                /// 
 
276
                /// An InvalidOperationException exception is thrown if one of the found child objects is not a
 
277
                /// subclass of the provided type.
 
278
                /// </remarks>
172
279
                public object[] GetChildObjects (Type arrayElementType)
173
280
                {
174
281
                        return GetChildObjects (arrayElementType, true);
175
282
                }
176
283
                
 
284
                /// <summary>
 
285
                /// Returns the child objects of a node (casting to the specified type)
 
286
                /// </summary>
 
287
                /// <returns>
 
288
                /// An array of child objects.
 
289
                /// </returns>
 
290
                /// <remarks>
 
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.
 
294
                /// </remarks>
 
295
                public T[] GetChildObjects<T> ()
 
296
                {
 
297
                        return (T[]) GetChildObjectsInternal (typeof(T), true);
 
298
                }
 
299
                /// <summary>
 
300
                /// Returns the child objects of a node (with type check).
 
301
                /// </summary>
 
302
                /// <param name="arrayElementType">
 
303
                /// Type of the return array elements.
 
304
                /// </param>
 
305
                /// <param name="reuseCachedInstance">
 
306
                /// True if the method can reuse instances created in previous calls.
 
307
                /// </param>
 
308
                /// <returns>
 
309
                /// An array of child objects.
 
310
                /// </returns>
 
311
                /// <remarks>
 
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).
 
315
                /// 
 
316
                /// An InvalidOperationException exception will be thrown if one of the found child objects is not a subclass
 
317
                /// of the provided type.
 
318
                /// </remarks>
177
319
                public object[] GetChildObjects (Type arrayElementType, bool reuseCachedInstance)
178
320
                {
 
321
                        return (object[]) GetChildObjectsInternal (arrayElementType, reuseCachedInstance);
 
322
                }
 
323
                
 
324
                /// <summary>
 
325
                /// Returns the child objects of a node (casting to the specified type).
 
326
                /// </summary>
 
327
                /// <param name="reuseCachedInstance">
 
328
                /// True if the method can reuse instances created in previous calls.
 
329
                /// </param>
 
330
                /// <returns>
 
331
                /// An array of child objects.
 
332
                /// </returns>
 
333
                /// <remarks>
 
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).
 
337
                /// </remarks>
 
338
                public T[] GetChildObjects<T> (bool reuseCachedInstance)
 
339
                {
 
340
                        return (T[]) GetChildObjectsInternal (typeof(T), reuseCachedInstance);
 
341
                }
 
342
                
 
343
                Array GetChildObjectsInternal (Type arrayElementType, bool reuseCachedInstance)
 
344
                {
179
345
                        ArrayList list = new ArrayList (ChildNodes.Count);
180
346
                        
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);
185
351
                                        continue;
186
352
                                }
187
353
                                
192
358
                                                list.Add (node.CreateInstance (arrayElementType));
193
359
                                }
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);
196
362
                                }
197
363
                        }
198
 
                        return (object[]) list.ToArray (arrayElementType);
 
364
                        return list.ToArray (arrayElementType);
199
365
                }
200
366
                
 
367
                /// <summary>
 
368
                /// Reads the extension node data
 
369
                /// </summary>
 
370
                /// <param name='elem'>
 
371
                /// The element containing the extension data
 
372
                /// </param>
 
373
                /// <remarks>
 
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.
 
377
                /// </remarks>
201
378
                internal protected virtual void Read (NodeElement elem)
202
379
                {
203
 
                        if (nodeType == null || nodeType.Fields == null)
 
380
                        if (nodeType == null)
204
381
                                return;
205
382
 
206
383
                        NodeAttribute[] attributes = elem.Attributes;
207
 
                        Hashtable fields = (Hashtable) nodeType.Fields.Clone ();
 
384
                        ReadObject (this, attributes, nodeType.Fields);
 
385
                        
 
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);
 
390
                        }
 
391
                }
 
392
                
 
393
                void ReadObject (object ob, NodeAttribute[] attributes, Dictionary<string,ExtensionNodeType.FieldData> fields)
 
394
                {
 
395
                        if (fields == null)
 
396
                                return;
 
397
                        
 
398
                        // Make a copy because we are going to remove fields that have been used
 
399
                        fields = new Dictionary<string,ExtensionNodeType.FieldData> (fields);
208
400
                        
209
401
                        foreach (NodeAttribute at in attributes) {
210
402
                                
211
 
                                ExtensionNodeType.FieldData f = (ExtensionNodeType.FieldData) fields [at.name];
212
 
                                if (f == null)
 
403
                                ExtensionNodeType.FieldData f;
 
404
                                if (!fields.TryGetValue (at.name, out f))
213
405
                                        continue;
214
406
                                
215
407
                                fields.Remove (at.name);
216
408
                                        
217
409
                                object val;
 
410
                                Type memberType = f.MemberType;
218
411
 
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);
222
415
                                        else
223
416
                                                val = at.value;
224
417
                                }
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];
232
425
                                                val = ss;
233
426
                                        }
234
427
                                }
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);
237
430
                                }
238
431
                                else {
239
432
                                        try {
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);
243
436
                                        }
244
437
                                }
245
 
                                        
246
 
                                f.Field.SetValue (this, val);
 
438
                                
 
439
                                f.SetValue (ob, val);
247
440
                        }
248
441
                        
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;
253
446
                                        if (f.Required)
254
447
                                                throw new InvalidOperationException ("Required attribute '" + e.Key + "' not found.");
255
448
                                }
283
476
                        return changed;
284
477
                }
285
478
                
286
 
                // Called when the add-in that defined this extension node is actually
287
 
                // loaded in memory.
 
479
                /// <summary>
 
480
                /// Called when the add-in that defined this extension node is actually loaded in memory.
 
481
                /// </summary>
288
482
                internal protected virtual void OnAddinLoaded ()
289
483
                {
290
484
                }
291
485
                
292
 
                // Called when the add-in that defined this extension node is being
293
 
                // unloaded from memory.
 
486
                /// <summary>
 
487
                /// Called when the add-in that defined this extension node is being
 
488
                /// unloaded from memory.
 
489
                /// </summary>
294
490
                internal protected virtual void OnAddinUnloaded ()
295
491
                {
296
492
                }
297
493
                
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.
 
494
                /// <summary>
 
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.
 
497
                /// </summary>
300
498
                protected virtual void OnChildrenChanged ()
301
499
                {
302
500
                }
303
501
                
 
502
                /// <summary>
 
503
                /// Called when a child node is added
 
504
                /// </summary>
 
505
                /// <param name="node">
 
506
                /// Added node.
 
507
                /// </param>
304
508
                protected virtual void OnChildNodeAdded (ExtensionNode node)
305
509
                {
306
510
                        if (extensionNodeChanged != null)
307
511
                                extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node));
308
512
                }
309
513
                
 
514
                /// <summary>
 
515
                /// Called when a child node is removed
 
516
                /// </summary>
 
517
                /// <param name="node">
 
518
                /// Removed node.
 
519
                /// </param>
310
520
                protected virtual void OnChildNodeRemoved (ExtensionNode node)
311
521
                {
312
522
                        if (extensionNodeChanged != null)
313
523
                                extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Remove, node));
314
524
                }
315
525
        }
 
526
        
 
527
        /// <summary>
 
528
        /// An extension node with custom metadata
 
529
        /// </summary>
 
530
        /// <remarks>
 
531
        /// This is the default type for extension nodes bound to a custom extension attribute.
 
532
        /// </remarks>
 
533
        public class ExtensionNode<T>: ExtensionNode where T:CustomExtensionAttribute
 
534
        {
 
535
                T data;
 
536
                
 
537
                /// <summary>
 
538
                /// The custom attribute containing the extension metadata
 
539
                /// </summary>
 
540
                [NodeAttribute]
 
541
                public T Data {
 
542
                        get { return data; }
 
543
                        internal set { data = value; }
 
544
                }
 
545
        }
316
546
}