~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/mono-addins/Mono.Addins/Mono.Addins/ExtensionNode.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// ExtensionNode.cs
 
3
//
 
4
// Author:
 
5
//   Lluis Sanchez Gual
 
6
//
 
7
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining
 
10
// a copy of this software and associated documentation files (the
 
11
// "Software"), to deal in the Software without restriction, including
 
12
// without limitation the rights to use, copy, modify, merge, publish,
 
13
// distribute, sublicense, and/or sell copies of the Software, and to
 
14
// permit persons to whom the Software is furnished to do so, subject to
 
15
// the following conditions:
 
16
// 
 
17
// The above copyright notice and this permission notice shall be
 
18
// included in all copies or substantial portions of the Software.
 
19
// 
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
27
//
 
28
 
 
29
 
 
30
using System;
 
31
using System.Collections;
 
32
using System.Collections.Generic;
 
33
using System.Xml;
 
34
using System.Reflection;
 
35
using Mono.Addins.Description;
 
36
 
 
37
namespace Mono.Addins
 
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>
 
51
        public class ExtensionNode
 
52
        {
 
53
                bool childrenLoaded;
 
54
                TreeNode treeNode;
 
55
                ExtensionNodeList childNodes;
 
56
                RuntimeAddin addin;
 
57
                string addinId;
 
58
                ExtensionNodeType nodeType;
 
59
                ModuleDescription module;
 
60
                AddinEngine addinEngine;
 
61
                event ExtensionNodeEventHandler extensionNodeChanged;
 
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>
 
72
                public string Id {
 
73
                        get { return treeNode != null ? treeNode.Id : string.Empty; }
 
74
                }
 
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>
 
83
                public string Path {
 
84
                        get { return treeNode != null ? treeNode.GetPath () : string.Empty; }
 
85
                }
 
86
                
 
87
                /// <summary>
 
88
                /// Parent node of this node.
 
89
                /// </summary>
 
90
                public ExtensionNode Parent {
 
91
                        get {
 
92
                                if (treeNode != null && treeNode.Parent != null)
 
93
                                        return treeNode.Parent.ExtensionNode;
 
94
                                else
 
95
                                        return null;
 
96
                        }
 
97
                }
 
98
                
 
99
                /// <summary>
 
100
                /// Extension context to which this node belongs
 
101
                /// </summary>
 
102
                public ExtensionContext ExtensionContext {
 
103
                        get { return treeNode.Context; }
 
104
                }
 
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>
 
115
                public bool HasId {
 
116
                        get { return !Id.StartsWith (ExtensionTree.AutoIdPrefix); }
 
117
                }
 
118
                
 
119
                internal void SetTreeNode (TreeNode node)
 
120
                {
 
121
                        treeNode = node;
 
122
                }
 
123
                
 
124
                internal void SetData (AddinEngine addinEngine, string plugid, ExtensionNodeType nodeType, ModuleDescription module)
 
125
                {
 
126
                        this.addinEngine = addinEngine;
 
127
                        this.addinId = plugid;
 
128
                        this.nodeType = nodeType;
 
129
                        this.module = module;
 
130
                }
 
131
                
 
132
                internal string AddinId {
 
133
                        get { return addinId; }
 
134
                }
 
135
                
 
136
                internal TreeNode TreeNode {
 
137
                        get { return treeNode; }
 
138
                }
 
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>
 
146
                public RuntimeAddin Addin {
 
147
                        get {
 
148
                                if (addin == null && addinId != null) {
 
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);
 
154
                                }
 
155
                                if (addin == null)
 
156
                                        throw new InvalidOperationException ("Add-in '" + addinId + "' could not be loaded.");
 
157
                                return addin; 
 
158
                        }
 
159
                }
 
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>
 
167
                public event ExtensionNodeEventHandler ExtensionNodeChanged {
 
168
                        add {
 
169
                                extensionNodeChanged += value;
 
170
                                foreach (ExtensionNode node in ChildNodes) {
 
171
                                        try {
 
172
                                                value (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node));
 
173
                                        } catch (Exception ex) {
 
174
                                                addinEngine.ReportError (null, node.Addin != null ? node.Addin.Id : null, ex, false);
 
175
                                        }
 
176
                                }
 
177
                        }
 
178
                        remove {
 
179
                                extensionNodeChanged -= value;
 
180
                        }
 
181
                }
 
182
                
 
183
                /// <summary>
 
184
                /// Child nodes of this extension node.
 
185
                /// </summary>
 
186
                public ExtensionNodeList ChildNodes {
 
187
                        get {
 
188
                                if (childrenLoaded)
 
189
                                        return childNodes;
 
190
                                
 
191
                                try {
 
192
                                        if (treeNode.Children.Count == 0) {
 
193
                                                childNodes = ExtensionNodeList.Empty;
 
194
                                                return childNodes;
 
195
                                        }
 
196
                                }
 
197
                                catch (Exception ex) {
 
198
                                        addinEngine.ReportError (null, null, ex, false);
 
199
                                        childNodes = ExtensionNodeList.Empty;
 
200
                                        return childNodes;
 
201
                                } finally {
 
202
                                        childrenLoaded = true;
 
203
                                }
 
204
 
 
205
                                List<ExtensionNode> list = new List<ExtensionNode> ();
 
206
                                foreach (TreeNode cn in treeNode.Children) {
 
207
                                        
 
208
                                        // For each node check if it is visible for the current context.
 
209
                                        // If something fails while evaluating the condition, just ignore the node.
 
210
                                        
 
211
                                        try {
 
212
                                                if (cn.ExtensionNode != null && cn.IsEnabled)
 
213
                                                        list.Add (cn.ExtensionNode);
 
214
                                        } catch (Exception ex) {
 
215
                                                addinEngine.ReportError (null, null, ex, false);
 
216
                                        }
 
217
                                }
 
218
                                if (list.Count > 0)
 
219
                                        childNodes = new ExtensionNodeList (list);
 
220
                                else
 
221
                                        childNodes = ExtensionNodeList.Empty;
 
222
                        
 
223
                                return childNodes;
 
224
                        }
 
225
                }
 
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>
 
238
                public object[] GetChildObjects ()
 
239
                {
 
240
                        return GetChildObjects (typeof(object), true);
 
241
                }
 
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>
 
257
                public object[] GetChildObjects (bool reuseCachedInstance)
 
258
                {
 
259
                        return GetChildObjects (typeof(object), reuseCachedInstance);
 
260
                }
 
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>
 
279
                public object[] GetChildObjects (Type arrayElementType)
 
280
                {
 
281
                        return GetChildObjects (arrayElementType, true);
 
282
                }
 
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>
 
319
                public object[] GetChildObjects (Type arrayElementType, bool reuseCachedInstance)
 
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
                {
 
345
                        ArrayList list = new ArrayList (ChildNodes.Count);
 
346
                        
 
347
                        for (int n=0; n<ChildNodes.Count; n++) {
 
348
                                InstanceExtensionNode node = ChildNodes [n] as InstanceExtensionNode;
 
349
                                if (node == null) {
 
350
                                        addinEngine.ReportError ("Error while getting object for node in path '" + Path + "'. Extension node is not a subclass of InstanceExtensionNode.", null, null, false);
 
351
                                        continue;
 
352
                                }
 
353
                                
 
354
                                try {
 
355
                                        if (reuseCachedInstance)
 
356
                                                list.Add (node.GetInstance (arrayElementType));
 
357
                                        else
 
358
                                                list.Add (node.CreateInstance (arrayElementType));
 
359
                                }
 
360
                                catch (Exception ex) {
 
361
                                        addinEngine.ReportError ("Error while getting object for node in path '" + Path + "'.", node.AddinId, ex, false);
 
362
                                }
 
363
                        }
 
364
                        return list.ToArray (arrayElementType);
 
365
                }
 
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>
 
378
                internal protected virtual void Read (NodeElement elem)
 
379
                {
 
380
                        if (nodeType == null)
 
381
                                return;
 
382
 
 
383
                        NodeAttribute[] attributes = elem.Attributes;
 
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);
 
400
                        
 
401
                        foreach (NodeAttribute at in attributes) {
 
402
                                
 
403
                                ExtensionNodeType.FieldData f;
 
404
                                if (!fields.TryGetValue (at.name, out f))
 
405
                                        continue;
 
406
                                
 
407
                                fields.Remove (at.name);
 
408
                                        
 
409
                                object val;
 
410
                                Type memberType = f.MemberType;
 
411
 
 
412
                                if (memberType == typeof(string)) {
 
413
                                        if (f.Localizable)
 
414
                                                val = Addin.Localizer.GetString (at.value);
 
415
                                        else
 
416
                                                val = at.value;
 
417
                                }
 
418
                                else if (memberType == typeof(string[])) {
 
419
                                        string[] ss = at.value.Split (',');
 
420
                                        if (ss.Length == 0 && ss[0].Length == 0)
 
421
                                                val = new string [0];
 
422
                                        else {
 
423
                                                for (int n=0; n<ss.Length; n++)
 
424
                                                        ss [n] = ss[n].Trim ();
 
425
                                                val = ss;
 
426
                                        }
 
427
                                }
 
428
                                else if (memberType.IsEnum) {
 
429
                                        val = Enum.Parse (memberType, at.value);
 
430
                                }
 
431
                                else {
 
432
                                        try {
 
433
                                                val = Convert.ChangeType (at.Value, memberType);
 
434
                                        } catch (InvalidCastException) {
 
435
                                                throw new InvalidOperationException ("Property type not supported by [NodeAttribute]: " + f.Member.DeclaringType + "." + f.Member.Name);
 
436
                                        }
 
437
                                }
 
438
                                
 
439
                                f.SetValue (ob, val);
 
440
                        }
 
441
                        
 
442
                        if (fields.Count > 0) {
 
443
                                // Check if one of the remaining fields is mandatory
 
444
                                foreach (KeyValuePair<string,ExtensionNodeType.FieldData> e in fields) {
 
445
                                        ExtensionNodeType.FieldData f = e.Value;
 
446
                                        if (f.Required)
 
447
                                                throw new InvalidOperationException ("Required attribute '" + e.Key + "' not found.");
 
448
                                }
 
449
                        }
 
450
                }
 
451
                
 
452
                internal bool NotifyChildChanged ()
 
453
                {
 
454
                        if (!childrenLoaded)
 
455
                                return false;
 
456
 
 
457
                        ExtensionNodeList oldList = childNodes;
 
458
                        childrenLoaded = false;
 
459
                        
 
460
                        bool changed = false;
 
461
                        
 
462
                        foreach (ExtensionNode nod in oldList) {
 
463
                                if (ChildNodes [nod.Id] == null) {
 
464
                                        changed = true;
 
465
                                        OnChildNodeRemoved (nod);
 
466
                                }
 
467
                        }
 
468
                        foreach (ExtensionNode nod in ChildNodes) {
 
469
                                if (oldList [nod.Id] == null) {
 
470
                                        changed = true;
 
471
                                        OnChildNodeAdded (nod);
 
472
                                }
 
473
                        }
 
474
                        if (changed)
 
475
                                OnChildrenChanged ();
 
476
                        return changed;
 
477
                }
 
478
                
 
479
                /// <summary>
 
480
                /// Called when the add-in that defined this extension node is actually loaded in memory.
 
481
                /// </summary>
 
482
                internal protected virtual void OnAddinLoaded ()
 
483
                {
 
484
                }
 
485
                
 
486
                /// <summary>
 
487
                /// Called when the add-in that defined this extension node is being
 
488
                /// unloaded from memory.
 
489
                /// </summary>
 
490
                internal protected virtual void OnAddinUnloaded ()
 
491
                {
 
492
                }
 
493
                
 
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>
 
498
                protected virtual void OnChildrenChanged ()
 
499
                {
 
500
                }
 
501
                
 
502
                /// <summary>
 
503
                /// Called when a child node is added
 
504
                /// </summary>
 
505
                /// <param name="node">
 
506
                /// Added node.
 
507
                /// </param>
 
508
                protected virtual void OnChildNodeAdded (ExtensionNode node)
 
509
                {
 
510
                        if (extensionNodeChanged != null)
 
511
                                extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node));
 
512
                }
 
513
                
 
514
                /// <summary>
 
515
                /// Called when a child node is removed
 
516
                /// </summary>
 
517
                /// <param name="node">
 
518
                /// Removed node.
 
519
                /// </param>
 
520
                protected virtual void OnChildNodeRemoved (ExtensionNode node)
 
521
                {
 
522
                        if (extensionNodeChanged != null)
 
523
                                extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Remove, node));
 
524
                }
 
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
        }
 
546
}