7
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
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:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
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.
31
using System.Collections;
32
using System.Collections.Generic;
33
using System.Collections.Specialized;
34
using Mono.Addins.Description;
39
/// An extension context.
42
/// Extension contexts can be used to query the extension tree
43
/// using particular condition values. Extension points which
44
/// declare the availability of a condition type can only be
45
/// queryed using an extension context which provides an
46
/// evaluator for that condition.
48
public class ExtensionContext
50
Hashtable conditionTypes = new Hashtable ();
51
Hashtable conditionsToNodes = new Hashtable ();
52
List<WeakReference> childContexts;
53
ExtensionContext parentContext;
55
bool fireEvents = false;
57
ArrayList runTimeEnabledAddins;
58
ArrayList runTimeDisabledAddins;
61
/// Extension change event.
64
/// This event is fired when any extension point in the add-in system changes.
65
/// The event args object provides the path of the changed extension, although
66
/// it does not provide information about what changed. Hosts subscribing to
67
/// this event should get the new list of nodes using a query method such as
68
/// AddinManager.GetExtensionNodes() and then update whatever needs to be updated.
70
public event ExtensionEventHandler ExtensionChanged;
72
internal void Initialize (AddinEngine addinEngine)
75
tree = new ExtensionTree (addinEngine, this);
78
#pragma warning disable 1591
80
protected void Clear ()
83
#pragma warning restore 1591
86
internal void ClearContext ()
88
conditionTypes.Clear ();
89
conditionsToNodes.Clear ();
93
runTimeEnabledAddins = null;
94
runTimeDisabledAddins = null;
97
internal AddinEngine AddinEngine {
98
get { return tree.AddinEngine; }
101
void CleanDisposedChildContexts ()
103
if (childContexts != null)
104
childContexts.RemoveAll (w => w.Target == null);
107
internal virtual void ResetCachedData ()
109
tree.ResetCachedData ();
110
if (childContexts != null) {
111
foreach (WeakReference wref in childContexts) {
112
ExtensionContext ctx = wref.Target as ExtensionContext;
114
ctx.ResetCachedData ();
119
internal ExtensionContext CreateChildContext ()
121
lock (conditionTypes) {
122
if (childContexts == null)
123
childContexts = new List<WeakReference> ();
125
CleanDisposedChildContexts ();
126
ExtensionContext ctx = new ExtensionContext ();
127
ctx.Initialize (AddinEngine);
128
ctx.parentContext = this;
129
WeakReference wref = new WeakReference (ctx);
130
childContexts.Add (wref);
136
/// Registers a new condition in the extension context.
138
/// <param name="id">
139
/// Identifier of the condition.
141
/// <param name="type">
142
/// Condition evaluator.
145
/// The registered condition will be particular to this extension context.
146
/// Any event that might be fired as a result of changes in the condition will
147
/// only be fired in this context.
149
public void RegisterCondition (string id, ConditionType type)
152
ConditionInfo info = CreateConditionInfo (id);
153
ConditionType ot = info.CondType as ConditionType;
155
ot.Changed -= new EventHandler (OnConditionChanged);
156
info.CondType = type;
157
type.Changed += new EventHandler (OnConditionChanged);
161
/// Registers a new condition in the extension context.
163
/// <param name="id">
164
/// Identifier of the condition.
166
/// <param name="type">
167
/// Type of the condition evaluator. Must be a subclass of Mono.Addins.ConditionType.
170
/// The registered condition will be particular to this extension context. Any event
171
/// that might be fired as a result of changes in the condition will only be fired in this context.
173
public void RegisterCondition (string id, Type type)
175
// Allows delayed creation of condition types
176
ConditionInfo info = CreateConditionInfo (id);
177
ConditionType ot = info.CondType as ConditionType;
179
ot.Changed -= new EventHandler (OnConditionChanged);
180
info.CondType = type;
183
ConditionInfo CreateConditionInfo (string id)
185
ConditionInfo info = conditionTypes [id] as ConditionInfo;
187
info = new ConditionInfo ();
188
conditionTypes [id] = info;
193
internal bool FireEvents {
194
get { return fireEvents; }
197
internal ConditionType GetCondition (string id)
200
ConditionInfo info = (ConditionInfo) conditionTypes [id];
203
if (info.CondType is Type) {
204
// The condition was registered as a type, create an instance now
205
ct = (ConditionType) Activator.CreateInstance ((Type)info.CondType);
207
ct.Changed += new EventHandler (OnConditionChanged);
211
ct = info.CondType as ConditionType;
217
if (parentContext != null)
218
return parentContext.GetCondition (id);
223
internal void RegisterNodeCondition (TreeNode node, BaseCondition cond)
225
ArrayList list = (ArrayList) conditionsToNodes [cond];
227
list = new ArrayList ();
228
conditionsToNodes [cond] = list;
229
ArrayList conditionTypeIds = new ArrayList ();
230
cond.GetConditionTypes (conditionTypeIds);
232
foreach (string cid in conditionTypeIds) {
234
// Make sure the condition is properly created
237
ConditionInfo info = CreateConditionInfo (cid);
238
if (info.BoundConditions == null)
239
info.BoundConditions = new ArrayList ();
241
info.BoundConditions.Add (cond);
247
internal void UnregisterNodeCondition (TreeNode node, BaseCondition cond)
249
ArrayList list = (ArrayList) conditionsToNodes [cond];
254
if (list.Count == 0) {
255
conditionsToNodes.Remove (cond);
256
ArrayList conditionTypeIds = new ArrayList ();
257
cond.GetConditionTypes (conditionTypeIds);
258
foreach (string cid in conditionTypes.Keys) {
259
ConditionInfo info = conditionTypes [cid] as ConditionInfo;
260
if (info != null && info.BoundConditions != null)
261
info.BoundConditions.Remove (cond);
267
/// Returns the extension node in a path
269
/// <param name="path">
270
/// Location of the node.
273
/// The node, or null if not found.
275
public ExtensionNode GetExtensionNode (string path)
277
TreeNode node = GetNode (path);
281
if (node.Condition == null || node.Condition.Evaluate (this))
282
return node.ExtensionNode;
288
/// Returns the extension node in a path
290
/// <param name="path">
291
/// Location of the node.
294
/// The node, or null if not found.
296
public T GetExtensionNode<T> (string path) where T: ExtensionNode
298
return (T) GetExtensionNode (path);
302
/// Gets extension nodes registered in a path.
304
/// <param name="path">
305
/// An extension path.>
308
/// All nodes registered in the provided path.
310
public ExtensionNodeList GetExtensionNodes (string path)
312
return GetExtensionNodes (path, null);
316
/// Gets extension nodes registered in a path.
318
/// <param name="path">
319
/// An extension path.
325
/// This method returns all nodes registered under the provided path.
326
/// It will throw a InvalidOperationException if the type of one of
327
/// the registered nodes is not assignable to the provided type.
329
public ExtensionNodeList<T> GetExtensionNodes<T> (string path) where T: ExtensionNode
331
ExtensionNodeList nodes = GetExtensionNodes (path, typeof(T));
332
return new ExtensionNodeList<T> (nodes.list);
336
/// Gets extension nodes for a type extension point
338
/// <param name="instanceType">
339
/// Type defining the extension point
345
/// This method returns all extension nodes bound to the provided type.
347
public ExtensionNodeList GetExtensionNodes (Type instanceType)
349
return GetExtensionNodes (instanceType, typeof(ExtensionNode));
353
/// Gets extension nodes for a type extension point
355
/// <param name="instanceType">
356
/// Type defining the extension point
358
/// <param name="expectedNodeType">
359
/// Expected extension node type
365
/// This method returns all nodes registered for the provided type.
366
/// It will throw a InvalidOperationException if the type of one of
367
/// the registered nodes is not assignable to the provided node type.
369
public ExtensionNodeList GetExtensionNodes (Type instanceType, Type expectedNodeType)
371
string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
373
return new ExtensionNodeList (null);
374
return GetExtensionNodes (path, expectedNodeType);
378
/// Gets extension nodes for a type extension point
380
/// <param name="instanceType">
381
/// Type defining the extension point
387
/// This method returns all nodes registered for the provided type.
388
/// It will throw a InvalidOperationException if the type of one of
389
/// the registered nodes is not assignable to the specified node type argument.
391
public ExtensionNodeList<T> GetExtensionNodes<T> (Type instanceType) where T: ExtensionNode
393
string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
395
return new ExtensionNodeList<T> (null);
396
return new ExtensionNodeList<T> (GetExtensionNodes (path, typeof (T)).list);
400
/// Gets extension nodes registered in a path.
402
/// <param name="path">
403
/// An extension path.
405
/// <param name="expectedNodeType">
406
/// Expected node type.
412
/// This method returns all nodes registered under the provided path.
413
/// It will throw a InvalidOperationException if the type of one of
414
/// the registered nodes is not assignable to the provided type.
416
public ExtensionNodeList GetExtensionNodes (string path, Type expectedNodeType)
418
TreeNode node = GetNode (path);
419
if (node == null || node.ExtensionNode == null)
420
return ExtensionNodeList.Empty;
422
ExtensionNodeList list = node.ExtensionNode.ChildNodes;
424
if (expectedNodeType != null) {
425
bool foundError = false;
426
foreach (ExtensionNode cnode in list) {
427
if (!expectedNodeType.IsInstanceOfType (cnode)) {
429
AddinEngine.ReportError ("Error while getting nodes for path '" + path + "'. Expected subclass of node type '" + expectedNodeType + "'. Found '" + cnode.GetType (), null, null, false);
433
// Create a new list excluding the elements that failed the test
434
List<ExtensionNode> newList = new List<ExtensionNode> ();
435
foreach (ExtensionNode cnode in list) {
436
if (expectedNodeType.IsInstanceOfType (cnode))
439
return new ExtensionNodeList (newList);
446
/// Gets extension objects registered for a type extension point.
448
/// <param name="instanceType">
449
/// Type defining the extension point
452
/// A list of objects
454
public object[] GetExtensionObjects (Type instanceType)
456
return GetExtensionObjects (instanceType, true);
460
/// Gets extension objects registered for a type extension point.
463
/// A list of objects
466
/// The type argument of this generic method is the type that defines
467
/// the extension point.
469
public T[] GetExtensionObjects<T> ()
471
return GetExtensionObjects<T> (true);
475
/// Gets extension objects registered for a type extension point.
477
/// <param name="instanceType">
478
/// Type defining the extension point
480
/// <param name="reuseCachedInstance">
481
/// When set to True, it will return instances created in previous calls.
484
/// A list of extension objects.
486
public object[] GetExtensionObjects (Type instanceType, bool reuseCachedInstance)
488
string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
490
return (object[]) Array.CreateInstance (instanceType, 0);
491
return GetExtensionObjects (path, instanceType, reuseCachedInstance);
495
/// Gets extension objects registered for a type extension point.
497
/// <param name="reuseCachedInstance">
498
/// When set to True, it will return instances created in previous calls.
501
/// A list of extension objects.
504
/// The type argument of this generic method is the type that defines
505
/// the extension point.
507
public T[] GetExtensionObjects<T> (bool reuseCachedInstance)
509
string path = AddinEngine.GetAutoTypeExtensionPoint (typeof(T));
512
return GetExtensionObjects<T> (path, reuseCachedInstance);
516
/// Gets extension objects registered in a path
518
/// <param name="path">
519
/// An extension path.
522
/// An array of objects registered in the path.
525
/// This method can only be used if all nodes in the provided extension path
526
/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
527
/// by all objects created by calling the TypeExtensionNode.CreateInstance()
528
/// method for each node.
530
public object[] GetExtensionObjects (string path)
532
return GetExtensionObjects (path, typeof(object), true);
536
/// Gets extension objects registered in a path.
538
/// <param name="path">
539
/// An extension path.
541
/// <param name="reuseCachedInstance">
542
/// When set to True, it will return instances created in previous calls.
545
/// An array of objects registered in the path.
548
/// This method can only be used if all nodes in the provided extension path
549
/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
550
/// by all objects created by calling the TypeExtensionNode.CreateInstance()
551
/// method for each node (or TypeExtensionNode.GetInstance() if
552
/// reuseCachedInstance is set to true)
554
public object[] GetExtensionObjects (string path, bool reuseCachedInstance)
556
return GetExtensionObjects (path, typeof(object), reuseCachedInstance);
560
/// Gets extension objects registered in a path.
562
/// <param name="path">
563
/// An extension path.
565
/// <param name="arrayElementType">
566
/// Type of the return array elements.
569
/// An array of objects registered in the path.
572
/// This method can only be used if all nodes in the provided extension path
573
/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
574
/// by all objects created by calling the TypeExtensionNode.CreateInstance()
575
/// method for each node.
577
/// An InvalidOperationException exception is thrown if one of the found
578
/// objects is not a subclass of the provided type.
580
public object[] GetExtensionObjects (string path, Type arrayElementType)
582
return GetExtensionObjects (path, arrayElementType, true);
586
/// Gets extension objects registered in a path.
588
/// <param name="path">
589
/// An extension path.
592
/// An array of objects registered in the path.
595
/// This method can only be used if all nodes in the provided extension path
596
/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
597
/// by all objects created by calling the TypeExtensionNode.CreateInstance()
598
/// method for each node.
600
/// An InvalidOperationException exception is thrown if one of the found
601
/// objects is not a subclass of the provided type.
603
public T[] GetExtensionObjects<T> (string path)
605
return GetExtensionObjects<T> (path, true);
609
/// Gets extension objects registered in a path.
611
/// <param name="path">
612
/// An extension path.
614
/// <param name="reuseCachedInstance">
615
/// When set to True, it will return instances created in previous calls.
618
/// An array of objects registered in the path.
621
/// This method can only be used if all nodes in the provided extension path
622
/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
623
/// by all objects created by calling the TypeExtensionNode.CreateInstance()
624
/// method for each node (or TypeExtensionNode.GetInstance() if
625
/// reuseCachedInstance is set to true).
627
/// An InvalidOperationException exception is thrown if one of the found
628
/// objects is not a subclass of the provided type.
630
public T[] GetExtensionObjects<T> (string path, bool reuseCachedInstance)
632
ExtensionNode node = GetExtensionNode (path);
634
throw new InvalidOperationException ("Extension node not found in path: " + path);
635
return node.GetChildObjects<T> (reuseCachedInstance);
639
/// Gets extension objects registered in a path.
641
/// <param name="path">
642
/// An extension path.
644
/// <param name="arrayElementType">
645
/// Type of the return array elements.
647
/// <param name="reuseCachedInstance">
648
/// When set to True, it will return instances created in previous calls.
651
/// An array of objects registered in the path.
654
/// This method can only be used if all nodes in the provided extension path
655
/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
656
/// by all objects created by calling the TypeExtensionNode.CreateInstance()
657
/// method for each node (or TypeExtensionNode.GetInstance() if
658
/// reuseCachedInstance is set to true).
660
/// An InvalidOperationException exception is thrown if one of the found
661
/// objects is not a subclass of the provided type.
663
public object[] GetExtensionObjects (string path, Type arrayElementType, bool reuseCachedInstance)
665
ExtensionNode node = GetExtensionNode (path);
667
throw new InvalidOperationException ("Extension node not found in path: " + path);
668
return node.GetChildObjects (arrayElementType, reuseCachedInstance);
672
/// Register a listener of extension node changes.
674
/// <param name="path">
675
/// Path of the node.
677
/// <param name="handler">
678
/// A handler method.
681
/// Hosts can call this method to be subscribed to an extension change
682
/// event for a specific path. The event will be fired once for every
683
/// individual node change. The event arguments include the change type
684
/// (Add or Remove) and the extension node added or removed.
686
/// NOTE: The handler will be called for all nodes existing in the path at the moment of registration.
688
public void AddExtensionNodeHandler (string path, ExtensionNodeEventHandler handler)
690
ExtensionNode node = GetExtensionNode (path);
692
throw new InvalidOperationException ("Extension node not found in path: " + path);
693
node.ExtensionNodeChanged += handler;
697
/// Unregister a listener of extension node changes.
699
/// <param name="path">
700
/// Path of the node.
702
/// <param name="handler">
703
/// A handler method.
706
/// This method unregisters a delegate from the node change event of a path.
708
public void RemoveExtensionNodeHandler (string path, ExtensionNodeEventHandler handler)
710
ExtensionNode node = GetExtensionNode (path);
712
throw new InvalidOperationException ("Extension node not found in path: " + path);
713
node.ExtensionNodeChanged -= handler;
717
/// Register a listener of extension node changes.
719
/// <param name="instanceType">
720
/// Type defining the extension point
722
/// <param name="handler">
723
/// A handler method.
726
/// Hosts can call this method to be subscribed to an extension change
727
/// event for a specific type extension point. The event will be fired once for every
728
/// individual node change. The event arguments include the change type
729
/// (Add or Remove) and the extension node added or removed.
731
/// NOTE: The handler will be called for all nodes existing in the path at the moment of registration.
733
public void AddExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler)
735
string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
737
throw new InvalidOperationException ("Type '" + instanceType + "' not bound to an extension point.");
738
AddExtensionNodeHandler (path, handler);
742
/// Unregister a listener of extension node changes.
744
/// <param name="instanceType">
745
/// Type defining the extension point
747
/// <param name="handler">
748
/// A handler method.
750
public void RemoveExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler)
752
string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
754
throw new InvalidOperationException ("Type '" + instanceType + "' not bound to an extension point.");
755
RemoveExtensionNodeHandler (path, handler);
758
void OnConditionChanged (object s, EventArgs a)
760
ConditionType cond = (ConditionType) s;
761
NotifyConditionChanged (cond);
764
internal void NotifyConditionChanged (ConditionType cond)
769
ConditionInfo info = (ConditionInfo) conditionTypes [cond.Id];
770
if (info != null && info.BoundConditions != null) {
771
Hashtable parentsToNotify = new Hashtable ();
772
foreach (BaseCondition c in info.BoundConditions) {
773
ArrayList nodeList = (ArrayList) conditionsToNodes [c];
774
if (nodeList != null) {
775
foreach (TreeNode node in nodeList)
776
parentsToNotify [node.Parent] = null;
779
foreach (TreeNode node in parentsToNotify.Keys) {
780
if (node.NotifyChildrenChanged ())
781
NotifyExtensionsChanged (new ExtensionEventArgs (node.GetPath ()));
789
// Notify child contexts
790
lock (conditionTypes) {
791
if (childContexts != null) {
792
CleanDisposedChildContexts ();
793
foreach (WeakReference wref in childContexts) {
794
ExtensionContext ctx = wref.Target as ExtensionContext;
796
ctx.NotifyConditionChanged (cond);
803
internal void NotifyExtensionsChanged (ExtensionEventArgs args)
808
if (ExtensionChanged != null)
809
ExtensionChanged (this, args);
812
internal void NotifyAddinLoaded (RuntimeAddin ad)
814
tree.NotifyAddinLoaded (ad, true);
816
lock (conditionTypes) {
817
if (childContexts != null) {
818
CleanDisposedChildContexts ();
819
foreach (WeakReference wref in childContexts) {
820
ExtensionContext ctx = wref.Target as ExtensionContext;
822
ctx.NotifyAddinLoaded (ad);
828
internal void CreateExtensionPoint (ExtensionPoint ep)
830
TreeNode node = tree.GetNode (ep.Path, true);
831
if (node.ExtensionPoint == null) {
832
node.ExtensionPoint = ep;
833
node.ExtensionNodeSet = ep.NodeSet;
837
internal void ActivateAddinExtensions (string id)
839
// Looks for loaded extension points which are extended by the provided
840
// add-in, and adds the new nodes
845
Addin addin = AddinEngine.Registry.GetAddin (id);
847
AddinEngine.ReportError ("Required add-in not found", id, null, false);
850
// Take note that this add-in has been enabled at run-time
851
// Needed because loaded add-in descriptions may not include this add-in.
852
RegisterRuntimeEnabledAddin (id);
854
// Look for loaded extension points
855
Hashtable eps = new Hashtable ();
856
ArrayList newExtensions = new ArrayList ();
857
foreach (ModuleDescription mod in addin.Description.AllModules) {
858
foreach (Extension ext in mod.Extensions) {
859
if (!newExtensions.Contains (ext.Path))
860
newExtensions.Add (ext.Path);
861
ExtensionPoint ep = tree.FindLoadedExtensionPoint (ext.Path);
862
if (ep != null && !eps.Contains (ep))
868
ArrayList loadedNodes = new ArrayList ();
869
foreach (ExtensionPoint ep in eps.Keys) {
870
ExtensionLoadData data = GetAddinExtensions (id, ep);
872
foreach (Extension ext in data.Extensions) {
873
TreeNode node = GetNode (ext.Path);
874
if (node != null && node.ExtensionNodeSet != null) {
875
if (node.ChildrenLoaded)
876
LoadModuleExtensionNodes (ext, data.AddinId, node.ExtensionNodeSet, loadedNodes);
879
AddinEngine.ReportError ("Extension node not found or not extensible: " + ext.Path, id, null, false);
884
// Call the OnAddinLoaded method on nodes, if the add-in is already loaded
885
foreach (TreeNode nod in loadedNodes)
886
nod.ExtensionNode.OnAddinLoaded ();
888
// Global extension change event. Other events are fired by LoadModuleExtensionNodes.
889
// The event is called for all extensions, even for those not loaded. This is for coherence,
890
// although that something that it doesn't make much sense to do (subcribing the ExtensionChanged
891
// event without first getting the list of nodes that may change).
892
foreach (string newExt in newExtensions)
893
NotifyExtensionsChanged (new ExtensionEventArgs (newExt));
898
// Do the same in child contexts
900
lock (conditionTypes) {
901
if (childContexts != null) {
902
CleanDisposedChildContexts ();
903
foreach (WeakReference wref in childContexts) {
904
ExtensionContext ctx = wref.Target as ExtensionContext;
906
ctx.ActivateAddinExtensions (id);
912
internal void RemoveAddinExtensions (string id)
915
// Registers this add-in as disabled, so from now on extension from this
916
// add-in will be ignored
917
RegisterRuntimeDisabledAddin (id);
921
// This method removes all extension nodes added by the add-in
922
// Get all nodes created by the addin
923
ArrayList list = new ArrayList ();
924
tree.FindAddinNodes (id, list);
926
// Remove each node and notify the change
927
foreach (TreeNode node in list) {
928
if (node.ExtensionNode == null) {
929
// It's an extension point. Just remove it, no notifications are needed
933
node.ExtensionNode.OnAddinUnloaded ();
938
// Notify global extension point changes.
939
// The event is called for all extensions, even for those not loaded. This is for coherence,
940
// although that something that it doesn't make much sense to do (subcribing the ExtensionChanged
941
// event without first getting the list of nodes that may change).
943
// We get the runtime add-in because the add-in may already have been deleted from the registry
944
RuntimeAddin addin = AddinEngine.GetAddin (id);
946
ArrayList paths = new ArrayList ();
947
// Using addin.Module.ParentAddinDescription here because addin.Addin.Description may not
948
// have a valid reference (the description is lazy loaded and may already have been removed from the registry)
949
foreach (ModuleDescription mod in addin.Module.ParentAddinDescription.AllModules) {
950
foreach (Extension ext in mod.Extensions) {
951
if (!paths.Contains (ext.Path))
952
paths.Add (ext.Path);
955
foreach (string path in paths)
956
NotifyExtensionsChanged (new ExtensionEventArgs (path));
963
void RegisterRuntimeDisabledAddin (string addinId)
965
if (runTimeDisabledAddins == null)
966
runTimeDisabledAddins = new ArrayList ();
967
if (!runTimeDisabledAddins.Contains (addinId))
968
runTimeDisabledAddins.Add (addinId);
970
if (runTimeEnabledAddins != null)
971
runTimeEnabledAddins.Remove (addinId);
974
void RegisterRuntimeEnabledAddin (string addinId)
976
if (runTimeEnabledAddins == null)
977
runTimeEnabledAddins = new ArrayList ();
978
if (!runTimeEnabledAddins.Contains (addinId))
979
runTimeEnabledAddins.Add (addinId);
981
if (runTimeDisabledAddins != null)
982
runTimeDisabledAddins.Remove (addinId);
985
internal ICollection GetAddinsForPath (string path, List<string> col)
987
ArrayList newlist = null;
989
// Always consider add-ins which have been enabled at runtime since
990
// they may contain extensioin for this path.
991
// Ignore addins disabled at run-time.
993
if (runTimeEnabledAddins != null && runTimeEnabledAddins.Count > 0) {
994
newlist = new ArrayList ();
995
newlist.AddRange (col);
996
foreach (string s in runTimeEnabledAddins)
997
if (!newlist.Contains (s))
1001
if (runTimeDisabledAddins != null && runTimeDisabledAddins.Count > 0) {
1002
if (newlist == null) {
1003
newlist = new ArrayList ();
1004
newlist.AddRange (col);
1006
foreach (string s in runTimeDisabledAddins)
1010
return newlist != null ? (ICollection)newlist : (ICollection)col;
1013
// Load the extension nodes at the specified path. If the path
1014
// contains extension nodes implemented in an add-in which is
1015
// not loaded, the add-in will be automatically loaded
1017
internal void LoadExtensions (string requestedExtensionPath)
1019
TreeNode node = GetNode (requestedExtensionPath);
1021
throw new InvalidOperationException ("Extension point not defined: " + requestedExtensionPath);
1023
ExtensionPoint ep = node.ExtensionPoint;
1027
// Collect extensions to be loaded from add-ins. Before loading the extensions,
1028
// they must be sorted, that's why loading is split in two steps (collecting + loading).
1030
ArrayList loadData = new ArrayList ();
1032
foreach (string addin in GetAddinsForPath (ep.Path, ep.Addins)) {
1033
ExtensionLoadData ed = GetAddinExtensions (addin, ep);
1035
// Insert the addin data taking into account dependencies.
1036
// An add-in must be processed after all its dependencies.
1038
for (int n=0; n<loadData.Count; n++) {
1039
ExtensionLoadData other = (ExtensionLoadData) loadData [n];
1040
if (AddinEngine.Registry.AddinDependsOn (other.AddinId, ed.AddinId)) {
1041
loadData.Insert (n, ed);
1051
// Now load the extensions
1053
ArrayList loadedNodes = new ArrayList ();
1054
foreach (ExtensionLoadData data in loadData) {
1055
foreach (Extension ext in data.Extensions) {
1056
TreeNode cnode = GetNode (ext.Path);
1057
if (cnode != null && cnode.ExtensionNodeSet != null)
1058
LoadModuleExtensionNodes (ext, data.AddinId, cnode.ExtensionNodeSet, loadedNodes);
1060
AddinEngine.ReportError ("Extension node not found or not extensible: " + ext.Path, data.AddinId, null, false);
1063
// Call the OnAddinLoaded method on nodes, if the add-in is already loaded
1064
foreach (TreeNode nod in loadedNodes)
1065
nod.ExtensionNode.OnAddinLoaded ();
1067
NotifyExtensionsChanged (new ExtensionEventArgs (requestedExtensionPath));
1071
ExtensionLoadData GetAddinExtensions (string id, ExtensionPoint ep)
1075
// Root add-ins are not returned by GetInstalledAddin.
1076
RuntimeAddin addin = AddinEngine.GetAddin (id);
1078
pinfo = addin.Addin;
1080
pinfo = AddinEngine.Registry.GetAddin (id);
1082
if (pinfo == null) {
1083
AddinEngine.ReportError ("Required add-in not found", id, null, false);
1086
if (!pinfo.Enabled || pinfo.Version != Addin.GetIdVersion (id))
1089
// Loads extensions defined in each module
1091
ExtensionLoadData data = null;
1092
AddinDescription conf = pinfo.Description;
1093
GetAddinExtensions (conf.MainModule, id, ep, ref data);
1095
foreach (ModuleDescription module in conf.OptionalModules) {
1096
if (CheckOptionalAddinDependencies (conf, module))
1097
GetAddinExtensions (module, id, ep, ref data);
1100
data.Extensions.Sort ();
1105
void GetAddinExtensions (ModuleDescription module, string addinId, ExtensionPoint ep, ref ExtensionLoadData data)
1107
string basePath = ep.Path + "/";
1109
foreach (Extension extension in module.Extensions) {
1110
if (extension.Path == ep.Path || extension.Path.StartsWith (basePath)) {
1112
data = new ExtensionLoadData ();
1113
data.AddinId = addinId;
1114
data.Extensions = new ArrayList ();
1116
data.Extensions.Add (extension);
1121
void LoadModuleExtensionNodes (Extension extension, string addinId, ExtensionNodeSet nset, ArrayList loadedNodes)
1123
// Now load the extensions
1124
ArrayList addedNodes = new ArrayList ();
1125
tree.LoadExtension (addinId, extension, addedNodes);
1127
RuntimeAddin ad = AddinEngine.GetAddin (addinId);
1129
foreach (TreeNode nod in addedNodes) {
1130
// Don't call OnAddinLoaded here. Do it when the entire extension point has been loaded.
1131
if (nod.ExtensionNode != null)
1132
loadedNodes.Add (nod);
1137
bool CheckOptionalAddinDependencies (AddinDescription conf, ModuleDescription module)
1139
foreach (Dependency dep in module.Dependencies) {
1140
AddinDependency pdep = dep as AddinDependency;
1142
Addin pinfo = AddinEngine.Registry.GetAddin (Addin.GetFullId (conf.Namespace, pdep.AddinId, pdep.Version));
1143
if (pinfo == null || !pinfo.Enabled)
1151
TreeNode GetNode (string path)
1153
TreeNode node = tree.GetNode (path);
1154
if (node != null || parentContext == null)
1157
TreeNode supNode = parentContext.tree.GetNode (path);
1158
if (supNode == null)
1161
if (path.StartsWith ("/"))
1162
path = path.Substring (1);
1164
string[] parts = path.Split ('/');
1165
TreeNode srcNode = parentContext.tree;
1166
TreeNode dstNode = tree;
1168
foreach (string part in parts) {
1170
// Look for the node in the source tree
1172
int i = srcNode.Children.IndexOfNode (part);
1174
srcNode = srcNode.Children [i];
1178
// Now get the node in the target tree
1180
int j = dstNode.Children.IndexOfNode (part);
1182
dstNode = dstNode.Children [j];
1185
// Create if not found
1186
TreeNode newNode = new TreeNode (AddinEngine, part);
1187
dstNode.AddChildNode (newNode);
1190
// Copy extension data
1191
dstNode.ExtensionNodeSet = srcNode.ExtensionNodeSet;
1192
dstNode.ExtensionPoint = srcNode.ExtensionPoint;
1193
dstNode.Condition = srcNode.Condition;
1195
if (dstNode.Condition != null)
1196
RegisterNodeCondition (dstNode, dstNode.Condition);
1203
internal bool FindExtensionPathByType (IProgressStatus monitor, Type type, string nodeName, out string path, out string pathNodeName)
1205
return tree.FindExtensionPathByType (monitor, type, nodeName, out path, out pathNodeName);
1211
public object CondType;
1212
public ArrayList BoundConditions;
1217
/// Delegate to be used in extension point subscriptions
1219
public delegate void ExtensionEventHandler (object sender, ExtensionEventArgs args);
1222
/// Delegate to be used in extension point subscriptions
1224
public delegate void ExtensionNodeEventHandler (object sender, ExtensionNodeEventArgs args);
1227
/// Arguments for extension events.
1229
public class ExtensionEventArgs: EventArgs
1233
internal ExtensionEventArgs ()
1238
/// Creates a new instance.
1240
/// <param name="path">
1241
/// Path of the extension node that has changed.
1243
public ExtensionEventArgs (string path)
1249
/// Path of the extension node that has changed.
1251
public virtual string Path {
1252
get { return path; }
1256
/// Checks if a path has changed.
1258
/// <param name="pathToCheck">
1259
/// An extension path.
1262
/// 'true' if the path is affected by the extension change event.
1265
/// Checks if the specified path or any of its children paths is affected by the extension change event.
1267
public bool PathChanged (string pathToCheck)
1269
if (pathToCheck.EndsWith ("/"))
1270
return path.StartsWith (pathToCheck);
1272
return path.StartsWith (pathToCheck) && (pathToCheck.Length == path.Length || path [pathToCheck.Length] == '/');
1277
/// Arguments for extension node events.
1279
public class ExtensionNodeEventArgs: ExtensionEventArgs
1282
ExtensionChange change;
1285
/// Creates a new instance
1287
/// <param name="change">
1290
/// <param name="node">
1291
/// Node that has been added or removed.
1293
public ExtensionNodeEventArgs (ExtensionChange change, ExtensionNode node)
1296
this.change = change;
1300
/// Path of the extension that changed.
1302
public override string Path {
1303
get { return node.Path; }
1309
public ExtensionChange Change {
1310
get { return change; }
1314
/// Node that has been added or removed.
1316
public ExtensionNode ExtensionNode {
1317
get { return node; }
1321
/// Extension object that has been added or removed.
1323
public object ExtensionObject {
1325
InstanceExtensionNode tnode = node as InstanceExtensionNode;
1327
throw new InvalidOperationException ("Node is not an InstanceExtensionNode");
1328
return tnode.GetInstance ();
1334
/// Type of change in an extension change event.
1336
public enum ExtensionChange
1339
/// An extension node has been added.
1344
/// An extension node has been removed.
1350
internal class ExtensionLoadData
1352
public string AddinId;
1353
public ArrayList Extensions;