1 //
2 // ExtensionContext.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.Collections.Specialized;
34 using Mono.Addins.Description;
35 
36 namespace Mono.Addins
37 {
38 	/// <summary>
39 	/// An extension context.
40 	/// </summary>
41 	/// <remarks>
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.
47 	/// </remarks>
48 	public class ExtensionContext
49 	{
50 		internal object LocalLock = new object ();
51 
52 		Hashtable conditionTypes = new Hashtable ();
53 		Hashtable conditionsToNodes = new Hashtable ();
54 		List<WeakReference> childContexts;
55 		ExtensionContext parentContext;
56 		ExtensionTree tree;
57 		bool fireEvents = false;
58 
59 		ArrayList runTimeEnabledAddins;
60 		ArrayList runTimeDisabledAddins;
61 
62 		/// <summary>
63 		/// Extension change event.
64 		/// </summary>
65 		/// <remarks>
66 		/// This event is fired when any extension point in the add-in system changes.
67 		/// The event args object provides the path of the changed extension, although
68 		/// it does not provide information about what changed. Hosts subscribing to
69 		/// this event should get the new list of nodes using a query method such as
70 		/// AddinManager.GetExtensionNodes() and then update whatever needs to be updated.
71 		/// </remarks>
72 		public event ExtensionEventHandler ExtensionChanged;
73 
Initialize(AddinEngine addinEngine)74 		internal void Initialize (AddinEngine addinEngine)
75 		{
76 			fireEvents = false;
77 			tree = new ExtensionTree (addinEngine, this);
78 		}
79 
80 #pragma warning disable 1591
81 		[ObsoleteAttribute]
Clear()82 		protected void Clear ()
83 		{
84 		}
85 #pragma warning restore 1591
86 
87 
ClearContext()88 		internal void ClearContext ()
89 		{
90 			conditionTypes.Clear ();
91 			conditionsToNodes.Clear ();
92 			childContexts = null;
93 			parentContext = null;
94 			tree = null;
95 			runTimeEnabledAddins = null;
96 			runTimeDisabledAddins = null;
97 		}
98 
99 		internal AddinEngine AddinEngine {
100 			get { return tree.AddinEngine; }
101 		}
102 
CleanDisposedChildContexts()103 		void CleanDisposedChildContexts ()
104 		{
105 			if (childContexts != null)
106 				childContexts.RemoveAll (w => w.Target == null);
107 		}
108 
ResetCachedData()109 		internal virtual void ResetCachedData ()
110 		{
111 			tree.ResetCachedData ();
112 			if (childContexts != null) {
113 				foreach (WeakReference wref in childContexts) {
114 					ExtensionContext ctx = wref.Target as ExtensionContext;
115 					if (ctx != null)
116 						ctx.ResetCachedData ();
117 				}
118 			}
119 		}
120 
CreateChildContext()121 		internal ExtensionContext CreateChildContext ()
122 		{
123 			lock (conditionTypes) {
124 				if (childContexts == null)
125 					childContexts = new List<WeakReference> ();
126 				else
127 					CleanDisposedChildContexts ();
128 				ExtensionContext ctx = new ExtensionContext ();
129 				ctx.Initialize (AddinEngine);
130 				ctx.parentContext = this;
131 				WeakReference wref = new WeakReference (ctx);
132 				childContexts.Add (wref);
133 				return ctx;
134 			}
135 		}
136 
137 		/// <summary>
138 		/// Registers a new condition in the extension context.
139 		/// </summary>
140 		/// <param name="id">
141 		/// Identifier of the condition.
142 		/// </param>
143 		/// <param name="type">
144 		/// Condition evaluator.
145 		/// </param>
146 		/// <remarks>
147 		/// The registered condition will be particular to this extension context.
148 		/// Any event that might be fired as a result of changes in the condition will
149 		/// only be fired in this context.
150 		/// </remarks>
RegisterCondition(string id, ConditionType type)151 		public void RegisterCondition (string id, ConditionType type)
152 		{
153 			type.Id = id;
154 			ConditionInfo info = CreateConditionInfo (id);
155 			ConditionType ot = info.CondType as ConditionType;
156 			if (ot != null)
157 				ot.Changed -= new EventHandler (OnConditionChanged);
158 			info.CondType = type;
159 			type.Changed += new EventHandler (OnConditionChanged);
160 		}
161 
162 		/// <summary>
163 		/// Registers a new condition in the extension context.
164 		/// </summary>
165 		/// <param name="id">
166 		/// Identifier of the condition.
167 		/// </param>
168 		/// <param name="type">
169 		/// Type of the condition evaluator. Must be a subclass of Mono.Addins.ConditionType.
170 		/// </param>
171 		/// <remarks>
172 		/// The registered condition will be particular to this extension context. Any event
173 		/// that might be fired as a result of changes in the condition will only be fired in this context.
174 		/// </remarks>
RegisterCondition(string id, Type type)175 		public void RegisterCondition (string id, Type type)
176 		{
177 			// Allows delayed creation of condition types
178 			ConditionInfo info = CreateConditionInfo (id);
179 			ConditionType ot = info.CondType as ConditionType;
180 			if (ot != null)
181 				ot.Changed -= new EventHandler (OnConditionChanged);
182 			info.CondType = type;
183 		}
184 
CreateConditionInfo(string id)185 		ConditionInfo CreateConditionInfo (string id)
186 		{
187 			ConditionInfo info = conditionTypes [id] as ConditionInfo;
188 			if (info == null) {
189 				info = new ConditionInfo ();
190 				conditionTypes [id] = info;
191 			}
192 			return info;
193 		}
194 
195 		internal bool FireEvents {
196 			get { return fireEvents; }
197 		}
198 
GetCondition(string id)199 		internal ConditionType GetCondition (string id)
200 		{
201 			ConditionType ct;
202 			ConditionInfo info = (ConditionInfo) conditionTypes [id];
203 
204 			if (info != null) {
205 				if (info.CondType is Type) {
206 					// The condition was registered as a type, create an instance now
207 					ct = (ConditionType) Activator.CreateInstance ((Type)info.CondType);
208 					ct.Id = id;
209 					ct.Changed += new EventHandler (OnConditionChanged);
210 					info.CondType = ct;
211 				}
212 				else
213 					ct = info.CondType as ConditionType;
214 
215 				if (ct != null)
216 					return ct;
217 			}
218 
219 			if (parentContext != null)
220 				return parentContext.GetCondition (id);
221 			else
222 				return null;
223 		}
224 
RegisterNodeCondition(TreeNode node, BaseCondition cond)225 		internal void RegisterNodeCondition (TreeNode node, BaseCondition cond)
226 		{
227 			ArrayList list = (ArrayList) conditionsToNodes [cond];
228 			if (list == null) {
229 				list = new ArrayList ();
230 				conditionsToNodes [cond] = list;
231 				ArrayList conditionTypeIds = new ArrayList ();
232 				cond.GetConditionTypes (conditionTypeIds);
233 
234 				foreach (string cid in conditionTypeIds) {
235 
236 					// Make sure the condition is properly created
237 					GetCondition (cid);
238 
239 					ConditionInfo info = CreateConditionInfo (cid);
240 					if (info.BoundConditions == null)
241 						info.BoundConditions = new ArrayList ();
242 
243 					info.BoundConditions.Add (cond);
244 				}
245 			}
246 			list.Add (node);
247 		}
248 
UnregisterNodeCondition(TreeNode node, BaseCondition cond)249 		internal void UnregisterNodeCondition (TreeNode node, BaseCondition cond)
250 		{
251 			ArrayList list = (ArrayList) conditionsToNodes [cond];
252 			if (list == null)
253 				return;
254 
255 			list.Remove (node);
256 			if (list.Count == 0) {
257 				conditionsToNodes.Remove (cond);
258 				ArrayList conditionTypeIds = new ArrayList ();
259 				cond.GetConditionTypes (conditionTypeIds);
260 				foreach (string cid in conditionTypes.Keys) {
261 					ConditionInfo info = conditionTypes [cid] as ConditionInfo;
262 					if (info != null && info.BoundConditions != null)
263 						info.BoundConditions.Remove (cond);
264 				}
265 			}
266 		}
267 
268 		/// <summary>
269 		/// Returns the extension node in a path
270 		/// </summary>
271 		/// <param name="path">
272 		/// Location of the node.
273 		/// </param>
274 		/// <returns>
275 		/// The node, or null if not found.
276 		/// </returns>
GetExtensionNode(string path)277 		public ExtensionNode GetExtensionNode (string path)
278 		{
279 			TreeNode node = GetNode (path);
280 			if (node == null)
281 				return null;
282 
283 			if (node.Condition == null || node.Condition.Evaluate (this))
284 				return node.ExtensionNode;
285 			else
286 				return null;
287 		}
288 
289 		/// <summary>
290 		/// Returns the extension node in a path
291 		/// </summary>
292 		/// <param name="path">
293 		/// Location of the node.
294 		/// </param>
295 		/// <returns>
296 		/// The node, or null if not found.
297 		/// </returns>
298 		public T GetExtensionNode<T> (string path) where T: ExtensionNode
299 		{
300 			return (T) GetExtensionNode (path);
301 		}
302 
303 		/// <summary>
304 		/// Gets extension nodes registered in a path.
305 		/// </summary>
306 		/// <param name="path">
307 		/// An extension path.>
308 		/// </param>
309 		/// <returns>
310 		/// All nodes registered in the provided path.
311 		/// </returns>
GetExtensionNodes(string path)312 		public ExtensionNodeList GetExtensionNodes (string path)
313 		{
314 			return GetExtensionNodes (path, null);
315 		}
316 
317 		/// <summary>
318 		/// Gets extension nodes registered in a path.
319 		/// </summary>
320 		/// <param name="path">
321 		/// An extension path.
322 		/// </param>
323 		/// <returns>
324 		/// A list of nodes
325 		/// </returns>
326 		/// <remarks>
327 		/// This method returns all nodes registered under the provided path.
328 		/// It will throw a InvalidOperationException if the type of one of
329 		/// the registered nodes is not assignable to the provided type.
330 		/// </remarks>
331 		public ExtensionNodeList<T> GetExtensionNodes<T> (string path) where T: ExtensionNode
332 		{
333 			ExtensionNodeList nodes = GetExtensionNodes (path, typeof(T));
334 			return new ExtensionNodeList<T> (nodes.list);
335 		}
336 
337 		/// <summary>
338 		/// Gets extension nodes for a type extension point
339 		/// </summary>
340 		/// <param name="instanceType">
341 		/// Type defining the extension point
342 		/// </param>
343 		/// <returns>
344 		/// A list of nodes
345 		/// </returns>
346 		/// <remarks>
347 		/// This method returns all extension nodes bound to the provided type.
348 		/// </remarks>
GetExtensionNodes(Type instanceType)349 		public ExtensionNodeList GetExtensionNodes (Type instanceType)
350 		{
351 			return GetExtensionNodes (instanceType, typeof(ExtensionNode));
352 		}
353 
354 		/// <summary>
355 		/// Gets extension nodes for a type extension point
356 		/// </summary>
357 		/// <param name="instanceType">
358 		/// Type defining the extension point
359 		/// </param>
360 		/// <param name="expectedNodeType">
361 		/// Expected extension node type
362 		/// </param>
363 		/// <returns>
364 		/// A list of nodes
365 		/// </returns>
366 		/// <remarks>
367 		/// This method returns all nodes registered for the provided type.
368 		/// It will throw a InvalidOperationException if the type of one of
369 		/// the registered nodes is not assignable to the provided node type.
370 		/// </remarks>
GetExtensionNodes(Type instanceType, Type expectedNodeType)371 		public ExtensionNodeList GetExtensionNodes (Type instanceType, Type expectedNodeType)
372 		{
373 			string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
374 			if (path == null)
375 				return new ExtensionNodeList (null);
376 			return GetExtensionNodes (path, expectedNodeType);
377 		}
378 
379 		/// <summary>
380 		/// Gets extension nodes for a type extension point
381 		/// </summary>
382 		/// <param name="instanceType">
383 		/// Type defining the extension point
384 		/// </param>
385 		/// <returns>
386 		/// A list of nodes
387 		/// </returns>
388 		/// <remarks>
389 		/// This method returns all nodes registered for the provided type.
390 		/// It will throw a InvalidOperationException if the type of one of
391 		/// the registered nodes is not assignable to the specified node type argument.
392 		/// </remarks>
393 		public ExtensionNodeList<T> GetExtensionNodes<T> (Type instanceType) where T: ExtensionNode
394 		{
395 			string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
396 			if (path == null)
397 				return new ExtensionNodeList<T> (null);
398 			return new ExtensionNodeList<T> (GetExtensionNodes (path, typeof (T)).list);
399 		}
400 
401 		/// <summary>
402 		/// Gets extension nodes registered in a path.
403 		/// </summary>
404 		/// <param name="path">
405 		/// An extension path.
406 		/// </param>
407 		/// <param name="expectedNodeType">
408 		/// Expected node type.
409 		/// </param>
410 		/// <returns>
411 		/// A list of nodes
412 		/// </returns>
413 		/// <remarks>
414 		/// This method returns all nodes registered under the provided path.
415 		/// It will throw a InvalidOperationException if the type of one of
416 		/// the registered nodes is not assignable to the provided type.
417 		/// </remarks>
GetExtensionNodes(string path, Type expectedNodeType)418 		public ExtensionNodeList GetExtensionNodes (string path, Type expectedNodeType)
419 		{
420 			TreeNode node = GetNode (path);
421 			if (node == null || node.ExtensionNode == null)
422 				return ExtensionNodeList.Empty;
423 
424 			ExtensionNodeList list = node.ExtensionNode.ChildNodes;
425 
426 			if (expectedNodeType != null) {
427 				bool foundError = false;
428 				foreach (ExtensionNode cnode in list) {
429 					if (!expectedNodeType.IsInstanceOfType (cnode)) {
430 						foundError = true;
431 						AddinEngine.ReportError ("Error while getting nodes for path '" + path + "'. Expected subclass of node type '" + expectedNodeType + "'. Found '" + cnode.GetType (), null, null, false);
432 					}
433 				}
434 				if (foundError) {
435 					// Create a new list excluding the elements that failed the test
436 					List<ExtensionNode> newList = new List<ExtensionNode> ();
437 					foreach (ExtensionNode cnode in list) {
438 						if (expectedNodeType.IsInstanceOfType (cnode))
439 							newList.Add (cnode);
440 					}
441 					return new ExtensionNodeList (newList);
442 				}
443 			}
444 			return list;
445 		}
446 
447 		/// <summary>
448 		/// Gets extension objects registered for a type extension point.
449 		/// </summary>
450 		/// <param name="instanceType">
451 		/// Type defining the extension point
452 		/// </param>
453 		/// <returns>
454 		/// A list of objects
455 		/// </returns>
GetExtensionObjects(Type instanceType)456 		public object[] GetExtensionObjects (Type instanceType)
457 		{
458 			return GetExtensionObjects (instanceType, true);
459 		}
460 
461 		/// <summary>
462 		/// Gets extension objects registered for a type extension point.
463 		/// </summary>
464 		/// <returns>
465 		/// A list of objects
466 		/// </returns>
467 		/// <remarks>
468 		/// The type argument of this generic method is the type that defines
469 		/// the extension point.
470 		/// </remarks>
GetExtensionObjects()471 		public T[] GetExtensionObjects<T> ()
472 		{
473 			return GetExtensionObjects<T> (true);
474 		}
475 
476 		/// <summary>
477 		/// Gets extension objects registered for a type extension point.
478 		/// </summary>
479 		/// <param name="instanceType">
480 		/// Type defining the extension point
481 		/// </param>
482 		/// <param name="reuseCachedInstance">
483 		/// When set to True, it will return instances created in previous calls.
484 		/// </param>
485 		/// <returns>
486 		/// A list of extension objects.
487 		/// </returns>
GetExtensionObjects(Type instanceType, bool reuseCachedInstance)488 		public object[] GetExtensionObjects (Type instanceType, bool reuseCachedInstance)
489 		{
490 			string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
491 			if (path == null)
492 				return (object[]) Array.CreateInstance (instanceType, 0);
493 			return GetExtensionObjects (path, instanceType, reuseCachedInstance);
494 		}
495 
496 		/// <summary>
497 		/// Gets extension objects registered for a type extension point.
498 		/// </summary>
499 		/// <param name="reuseCachedInstance">
500 		/// When set to True, it will return instances created in previous calls.
501 		/// </param>
502 		/// <returns>
503 		/// A list of extension objects.
504 		/// </returns>
505 		/// <remarks>
506 		/// The type argument of this generic method is the type that defines
507 		/// the extension point.
508 		/// </remarks>
GetExtensionObjects(bool reuseCachedInstance)509 		public T[] GetExtensionObjects<T> (bool reuseCachedInstance)
510 		{
511 			string path = AddinEngine.GetAutoTypeExtensionPoint (typeof(T));
512 			if (path == null)
513 				return new T[0];
514 			return GetExtensionObjects<T> (path, reuseCachedInstance);
515 		}
516 
517 		/// <summary>
518 		/// Gets extension objects registered in a path
519 		/// </summary>
520 		/// <param name="path">
521 		/// An extension path.
522 		/// </param>
523 		/// <returns>
524 		/// An array of objects registered in the path.
525 		/// </returns>
526 		/// <remarks>
527 		/// This method can only be used if all nodes in the provided extension path
528 		/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
529 		/// by all objects created by calling the TypeExtensionNode.CreateInstance()
530 		/// method for each node.
531 		/// </remarks>
GetExtensionObjects(string path)532 		public object[] GetExtensionObjects (string path)
533 		{
534 			return GetExtensionObjects (path, typeof(object), true);
535 		}
536 
537 		/// <summary>
538 		/// Gets extension objects registered in a path.
539 		/// </summary>
540 		/// <param name="path">
541 		/// An extension path.
542 		/// </param>
543 		/// <param name="reuseCachedInstance">
544 		/// When set to True, it will return instances created in previous calls.
545 		/// </param>
546 		/// <returns>
547 		/// An array of objects registered in the path.
548 		/// </returns>
549 		/// <remarks>
550 		/// This method can only be used if all nodes in the provided extension path
551 		/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
552 		/// by all objects created by calling the TypeExtensionNode.CreateInstance()
553 		/// method for each node (or TypeExtensionNode.GetInstance() if
554 		/// reuseCachedInstance is set to true)
555 		/// </remarks>
GetExtensionObjects(string path, bool reuseCachedInstance)556 		public object[] GetExtensionObjects (string path, bool reuseCachedInstance)
557 		{
558 			return GetExtensionObjects (path, typeof(object), reuseCachedInstance);
559 		}
560 
561 		/// <summary>
562 		/// Gets extension objects registered in a path.
563 		/// </summary>
564 		/// <param name="path">
565 		/// An extension path.
566 		/// </param>
567 		/// <param name="arrayElementType">
568 		/// Type of the return array elements.
569 		/// </param>
570 		/// <returns>
571 		/// An array of objects registered in the path.
572 		/// </returns>
573 		/// <remarks>
574 		/// This method can only be used if all nodes in the provided extension path
575 		/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
576 		/// by all objects created by calling the TypeExtensionNode.CreateInstance()
577 		/// method for each node.
578 		///
579 		/// An InvalidOperationException exception is thrown if one of the found
580 		/// objects is not a subclass of the provided type.
581 		/// </remarks>
GetExtensionObjects(string path, Type arrayElementType)582 		public object[] GetExtensionObjects (string path, Type arrayElementType)
583 		{
584 			return GetExtensionObjects (path, arrayElementType, true);
585 		}
586 
587 		/// <summary>
588 		/// Gets extension objects registered in a path.
589 		/// </summary>
590 		/// <param name="path">
591 		/// An extension path.
592 		/// </param>
593 		/// <returns>
594 		/// An array of objects registered in the path.
595 		/// </returns>
596 		/// <remarks>
597 		/// This method can only be used if all nodes in the provided extension path
598 		/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
599 		/// by all objects created by calling the TypeExtensionNode.CreateInstance()
600 		/// method for each node.
601 		///
602 		/// An InvalidOperationException exception is thrown if one of the found
603 		/// objects is not a subclass of the provided type.
604 		/// </remarks>
GetExtensionObjects(string path)605 		public T[] GetExtensionObjects<T> (string path)
606 		{
607 			return GetExtensionObjects<T> (path, true);
608 		}
609 
610 		/// <summary>
611 		/// Gets extension objects registered in a path.
612 		/// </summary>
613 		/// <param name="path">
614 		/// An extension path.
615 		/// </param>
616 		/// <param name="reuseCachedInstance">
617 		/// When set to True, it will return instances created in previous calls.
618 		/// </param>
619 		/// <returns>
620 		/// An array of objects registered in the path.
621 		/// </returns>
622 		/// <remarks>
623 		/// This method can only be used if all nodes in the provided extension path
624 		/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
625 		/// by all objects created by calling the TypeExtensionNode.CreateInstance()
626 		/// method for each node (or TypeExtensionNode.GetInstance() if
627 		/// reuseCachedInstance is set to true).
628 		///
629 		/// An InvalidOperationException exception is thrown if one of the found
630 		/// objects is not a subclass of the provided type.
631 		/// </remarks>
GetExtensionObjects(string path, bool reuseCachedInstance)632 		public T[] GetExtensionObjects<T> (string path, bool reuseCachedInstance)
633 		{
634 			ExtensionNode node = GetExtensionNode (path);
635 			if (node == null)
636 				throw new InvalidOperationException ("Extension node not found in path: " + path);
637 			return node.GetChildObjects<T> (reuseCachedInstance);
638 		}
639 
640 		/// <summary>
641 		/// Gets extension objects registered in a path.
642 		/// </summary>
643 		/// <param name="path">
644 		/// An extension path.
645 		/// </param>
646 		/// <param name="arrayElementType">
647 		/// Type of the return array elements.
648 		/// </param>
649 		/// <param name="reuseCachedInstance">
650 		/// When set to True, it will return instances created in previous calls.
651 		/// </param>
652 		/// <returns>
653 		/// An array of objects registered in the path.
654 		/// </returns>
655 		/// <remarks>
656 		/// This method can only be used if all nodes in the provided extension path
657 		/// are of type Mono.Addins.TypeExtensionNode. The returned array is composed
658 		/// by all objects created by calling the TypeExtensionNode.CreateInstance()
659 		/// method for each node (or TypeExtensionNode.GetInstance() if
660 		/// reuseCachedInstance is set to true).
661 		///
662 		/// An InvalidOperationException exception is thrown if one of the found
663 		/// objects is not a subclass of the provided type.
664 		/// </remarks>
GetExtensionObjects(string path, Type arrayElementType, bool reuseCachedInstance)665 		public object[] GetExtensionObjects (string path, Type arrayElementType, bool reuseCachedInstance)
666 		{
667 			ExtensionNode node = GetExtensionNode (path);
668 			if (node == null)
669 				throw new InvalidOperationException ("Extension node not found in path: " + path);
670 			return node.GetChildObjects (arrayElementType, reuseCachedInstance);
671 		}
672 
673 		/// <summary>
674 		/// Register a listener of extension node changes.
675 		/// </summary>
676 		/// <param name="path">
677 		/// Path of the node.
678 		/// </param>
679 		/// <param name="handler">
680 		/// A handler method.
681 		/// </param>
682 		/// <remarks>
683 		/// Hosts can call this method to be subscribed to an extension change
684 		/// event for a specific path. The event will be fired once for every
685 		/// individual node change. The event arguments include the change type
686 		/// (Add or Remove) and the extension node added or removed.
687 		///
688 		/// NOTE: The handler will be called for all nodes existing in the path at the moment of registration.
689 		/// </remarks>
AddExtensionNodeHandler(string path, ExtensionNodeEventHandler handler)690 		public void AddExtensionNodeHandler (string path, ExtensionNodeEventHandler handler)
691 		{
692 			ExtensionNode node = GetExtensionNode (path);
693 			if (node == null)
694 				throw new InvalidOperationException ("Extension node not found in path: " + path);
695 			node.ExtensionNodeChanged += handler;
696 		}
697 
698 		/// <summary>
699 		/// Unregister a listener of extension node changes.
700 		/// </summary>
701 		/// <param name="path">
702 		/// Path of the node.
703 		/// </param>
704 		/// <param name="handler">
705 		/// A handler method.
706 		/// </param>
707 		/// <remarks>
708 		/// This method unregisters a delegate from the node change event of a path.
709 		/// </remarks>
RemoveExtensionNodeHandler(string path, ExtensionNodeEventHandler handler)710 		public void RemoveExtensionNodeHandler (string path, ExtensionNodeEventHandler handler)
711 		{
712 			ExtensionNode node = GetExtensionNode (path);
713 			if (node == null)
714 				throw new InvalidOperationException ("Extension node not found in path: " + path);
715 			node.ExtensionNodeChanged -= handler;
716 		}
717 
718 		/// <summary>
719 		/// Register a listener of extension node changes.
720 		/// </summary>
721 		/// <param name="instanceType">
722 		/// Type defining the extension point
723 		/// </param>
724 		/// <param name="handler">
725 		/// A handler method.
726 		/// </param>
727 		/// <remarks>
728 		/// Hosts can call this method to be subscribed to an extension change
729 		/// event for a specific type extension point. The event will be fired once for every
730 		/// individual node change. The event arguments include the change type
731 		/// (Add or Remove) and the extension node added or removed.
732 		///
733 		/// NOTE: The handler will be called for all nodes existing in the path at the moment of registration.
734 		/// </remarks>
AddExtensionNodeHandler(Type instanceType, ExtensionNodeEventHandler handler)735 		public void AddExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler)
736 		{
737 			string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
738 			if (path == null)
739 				throw new InvalidOperationException ("Type '" + instanceType + "' not bound to an extension point.");
740 			AddExtensionNodeHandler (path, handler);
741 		}
742 
743 		/// <summary>
744 		/// Unregister a listener of extension node changes.
745 		/// </summary>
746 		/// <param name="instanceType">
747 		/// Type defining the extension point
748 		/// </param>
749 		/// <param name="handler">
750 		/// A handler method.
751 		/// </param>
RemoveExtensionNodeHandler(Type instanceType, ExtensionNodeEventHandler handler)752 		public void RemoveExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler)
753 		{
754 			string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType);
755 			if (path == null)
756 				throw new InvalidOperationException ("Type '" + instanceType + "' not bound to an extension point.");
757 			RemoveExtensionNodeHandler (path, handler);
758 		}
759 
OnConditionChanged(object s, EventArgs a)760 		void OnConditionChanged (object s, EventArgs a)
761 		{
762 			ConditionType cond = (ConditionType) s;
763 			NotifyConditionChanged (cond);
764 		}
765 
NotifyConditionChanged(ConditionType cond)766 		internal void NotifyConditionChanged (ConditionType cond)
767 		{
768 			try {
769 				fireEvents = true;
770 
771 				ConditionInfo info = (ConditionInfo) conditionTypes [cond.Id];
772 				if (info != null && info.BoundConditions != null) {
773 					Hashtable parentsToNotify = new Hashtable ();
774 					foreach (BaseCondition c in info.BoundConditions) {
775 						ArrayList nodeList = (ArrayList) conditionsToNodes [c];
776 						if (nodeList != null) {
777 							foreach (TreeNode node in nodeList)
778 								parentsToNotify [node.Parent] = null;
779 						}
780 					}
781 					foreach (TreeNode node in parentsToNotify.Keys) {
782 						if (node.NotifyChildrenChanged ())
783 							NotifyExtensionsChanged (new ExtensionEventArgs (node.GetPath ()));
784 					}
785 				}
786 			}
787 			finally {
788 				fireEvents = false;
789 			}
790 
791 			// Notify child contexts
792 			lock (conditionTypes) {
793 				if (childContexts != null) {
794 					CleanDisposedChildContexts ();
795 					foreach (WeakReference wref in childContexts) {
796 						ExtensionContext ctx = wref.Target as ExtensionContext;
797 						if (ctx != null)
798 							ctx.NotifyConditionChanged (cond);
799 					}
800 				}
801 			}
802 		}
803 
804 
NotifyExtensionsChanged(ExtensionEventArgs args)805 		internal void NotifyExtensionsChanged (ExtensionEventArgs args)
806 		{
807 			if (!fireEvents)
808 				return;
809 
810 			if (ExtensionChanged != null)
811 				ExtensionChanged (this, args);
812 		}
813 
NotifyAddinLoaded(RuntimeAddin ad)814 		internal void NotifyAddinLoaded (RuntimeAddin ad)
815 		{
816 			tree.NotifyAddinLoaded (ad, true);
817 
818 			lock (conditionTypes) {
819 				if (childContexts != null) {
820 					CleanDisposedChildContexts ();
821 					foreach (WeakReference wref in childContexts) {
822 						ExtensionContext ctx = wref.Target as ExtensionContext;
823 						if (ctx != null)
824 							ctx.NotifyAddinLoaded (ad);
825 					}
826 				}
827 			}
828 		}
829 
CreateExtensionPoint(ExtensionPoint ep)830 		internal void CreateExtensionPoint (ExtensionPoint ep)
831 		{
832 			TreeNode node = tree.GetNode (ep.Path, true);
833 			if (node.ExtensionPoint == null) {
834 				node.ExtensionPoint = ep;
835 				node.ExtensionNodeSet = ep.NodeSet;
836 			}
837 		}
838 
ActivateAddinExtensions(string id)839 		internal void ActivateAddinExtensions (string id)
840 		{
841 			// Looks for loaded extension points which are extended by the provided
842 			// add-in, and adds the new nodes
843 
844 			try {
845 				fireEvents = true;
846 
847 				Addin addin = AddinEngine.Registry.GetAddin (id);
848 				if (addin == null) {
849 					AddinEngine.ReportError ("Required add-in not found", id, null, false);
850 					return;
851 				}
852 				// Take note that this add-in has been enabled at run-time
853 				// Needed because loaded add-in descriptions may not include this add-in.
854 				RegisterRuntimeEnabledAddin (id);
855 
856 				// Look for loaded extension points
857 				Hashtable eps = new Hashtable ();
858 				ArrayList newExtensions = new ArrayList ();
859 				foreach (ModuleDescription mod in addin.Description.AllModules) {
860 					foreach (Extension ext in mod.Extensions) {
861 						if (!newExtensions.Contains (ext.Path))
862 							newExtensions.Add (ext.Path);
863 						ExtensionPoint ep = tree.FindLoadedExtensionPoint (ext.Path);
864 						if (ep != null && !eps.Contains (ep))
865 							eps.Add (ep, ep);
866 					}
867 				}
868 
869 				// Add the new nodes
870 				ArrayList loadedNodes = new ArrayList ();
871 				foreach (ExtensionPoint ep in eps.Keys) {
872 					ExtensionLoadData data = GetAddinExtensions (id, ep);
873 					if (data != null) {
874 						foreach (Extension ext in data.Extensions) {
875 							TreeNode node = GetNode (ext.Path);
876 							if (node != null && node.ExtensionNodeSet != null) {
877 								if (node.ChildrenLoaded)
878 									LoadModuleExtensionNodes (ext, data.AddinId, node.ExtensionNodeSet, loadedNodes);
879 							}
880 							else
881 								AddinEngine.ReportError ("Extension node not found or not extensible: " + ext.Path, id, null, false);
882 						}
883 					}
884 				}
885 
886 				// Call the OnAddinLoaded method on nodes, if the add-in is already loaded
887 				foreach (TreeNode nod in loadedNodes)
888 					nod.ExtensionNode.OnAddinLoaded ();
889 
890 				// Global extension change event. Other events are fired by LoadModuleExtensionNodes.
891 				// The event is called for all extensions, even for those not loaded. This is for coherence,
892 				// although that something that it doesn't make much sense to do (subscribing the ExtensionChanged
893 				// event without first getting the list of nodes that may change).
894 				foreach (string newExt in newExtensions)
895 					NotifyExtensionsChanged (new ExtensionEventArgs (newExt));
896 			}
897 			finally {
898 				fireEvents = false;
899 			}
900 			// Do the same in child contexts
901 
902 			lock (conditionTypes) {
903 				if (childContexts != null) {
904 					CleanDisposedChildContexts ();
905 					foreach (WeakReference wref in childContexts) {
906 						ExtensionContext ctx = wref.Target as ExtensionContext;
907 						if (ctx != null)
908 							ctx.ActivateAddinExtensions (id);
909 					}
910 				}
911 			}
912 		}
913 
RemoveAddinExtensions(string id)914 		internal void RemoveAddinExtensions (string id)
915 		{
916 			try {
917 				// Registers this add-in as disabled, so from now on extension from this
918 				// add-in will be ignored
919 				RegisterRuntimeDisabledAddin (id);
920 
921 				fireEvents = true;
922 
923 				// This method removes all extension nodes added by the add-in
924 				// Get all nodes created by the addin
925 				ArrayList list = new ArrayList ();
926 				tree.FindAddinNodes (id, list);
927 
928 				// Remove each node and notify the change
929 				foreach (TreeNode node in list) {
930 					if (node.ExtensionNode == null) {
931 						// It's an extension point. Just remove it, no notifications are needed
932 						node.Remove ();
933 					}
934 					else {
935 						node.ExtensionNode.OnAddinUnloaded ();
936 						node.Remove ();
937 					}
938 				}
939 
940 				// Notify global extension point changes.
941 				// The event is called for all extensions, even for those not loaded. This is for coherence,
942 				// although that something that it doesn't make much sense to do (subscribing the ExtensionChanged
943 				// event without first getting the list of nodes that may change).
944 
945 				// We get the runtime add-in because the add-in may already have been deleted from the registry
946 				RuntimeAddin addin = AddinEngine.GetAddin (id);
947 				if (addin != null) {
948 					ArrayList paths = new ArrayList ();
949 					// Using addin.Module.ParentAddinDescription here because addin.Addin.Description may not
950 					// have a valid reference (the description is lazy loaded and may already have been removed from the registry)
951 					foreach (ModuleDescription mod in addin.Module.ParentAddinDescription.AllModules) {
952 						foreach (Extension ext in mod.Extensions) {
953 							if (!paths.Contains (ext.Path))
954 								paths.Add (ext.Path);
955 						}
956 					}
957 					foreach (string path in paths)
958 						NotifyExtensionsChanged (new ExtensionEventArgs (path));
959 				}
960 			} finally {
961 				fireEvents = false;
962 			}
963 		}
964 
RegisterRuntimeDisabledAddin(string addinId)965 		void RegisterRuntimeDisabledAddin (string addinId)
966 		{
967 			if (runTimeDisabledAddins == null)
968 				runTimeDisabledAddins = new ArrayList ();
969 			if (!runTimeDisabledAddins.Contains (addinId))
970 				runTimeDisabledAddins.Add (addinId);
971 
972 			if (runTimeEnabledAddins != null)
973 				runTimeEnabledAddins.Remove (addinId);
974 		}
975 
RegisterRuntimeEnabledAddin(string addinId)976 		void RegisterRuntimeEnabledAddin (string addinId)
977 		{
978 			if (runTimeEnabledAddins == null)
979 				runTimeEnabledAddins = new ArrayList ();
980 			if (!runTimeEnabledAddins.Contains (addinId))
981 				runTimeEnabledAddins.Add (addinId);
982 
983 			if (runTimeDisabledAddins != null)
984 				runTimeDisabledAddins.Remove (addinId);
985 		}
986 
GetAddinsForPath(string path, List<string> col)987 		internal ICollection GetAddinsForPath (string path, List<string> col)
988 		{
989 			ArrayList newlist = null;
990 
991 			// Always consider add-ins which have been enabled at runtime since
992 			// they may contain extension for this path.
993 			// Ignore addins disabled at run-time.
994 
995 			if (runTimeEnabledAddins != null && runTimeEnabledAddins.Count > 0) {
996 				newlist = new ArrayList ();
997 				newlist.AddRange (col);
998 				foreach (string s in runTimeEnabledAddins)
999 					if (!newlist.Contains (s))
1000 						newlist.Add (s);
1001 			}
1002 
1003 			if (runTimeDisabledAddins != null && runTimeDisabledAddins.Count > 0) {
1004 				if (newlist == null) {
1005 					newlist = new ArrayList ();
1006 					newlist.AddRange (col);
1007 				}
1008 				foreach (string s in runTimeDisabledAddins)
1009 					newlist.Remove (s);
1010 			}
1011 
1012 			return newlist != null ? (ICollection)newlist : (ICollection)col;
1013 		}
1014 
1015 		// Load the extension nodes at the specified path. If the path
1016 		// contains extension nodes implemented in an add-in which is
1017 		// not loaded, the add-in will be automatically loaded
1018 
LoadExtensions(string requestedExtensionPath)1019 		internal void LoadExtensions (string requestedExtensionPath)
1020 		{
1021 			TreeNode node = GetNode (requestedExtensionPath);
1022 			if (node == null)
1023 				throw new InvalidOperationException ("Extension point not defined: " + requestedExtensionPath);
1024 
1025 			ExtensionPoint ep = node.ExtensionPoint;
1026 
1027 			if (ep != null) {
1028 
1029 				// Collect extensions to be loaded from add-ins. Before loading the extensions,
1030 				// they must be sorted, that's why loading is split in two steps (collecting + loading).
1031 
1032 				ArrayList loadData = new ArrayList ();
1033 
1034 				foreach (string addin in GetAddinsForPath (ep.Path, ep.Addins)) {
1035 					ExtensionLoadData ed = GetAddinExtensions (addin, ep);
1036 					if (ed != null) {
1037 						// Insert the addin data taking into account dependencies.
1038 						// An add-in must be processed after all its dependencies.
1039 						bool added = false;
1040 						for (int n=0; n<loadData.Count; n++) {
1041 							ExtensionLoadData other = (ExtensionLoadData) loadData [n];
1042 							if (AddinEngine.Registry.AddinDependsOn (other.AddinId, ed.AddinId)) {
1043 								loadData.Insert (n, ed);
1044 								added = true;
1045 								break;
1046 							}
1047 						}
1048 						if (!added)
1049 							loadData.Add (ed);
1050 					}
1051 				}
1052 
1053 				// Now load the extensions
1054 
1055 				ArrayList loadedNodes = new ArrayList ();
1056 				foreach (ExtensionLoadData data in loadData) {
1057 					foreach (Extension ext in data.Extensions) {
1058 						TreeNode cnode = GetNode (ext.Path);
1059 						if (cnode != null && cnode.ExtensionNodeSet != null)
1060 							LoadModuleExtensionNodes (ext, data.AddinId, cnode.ExtensionNodeSet, loadedNodes);
1061 						else
1062 							AddinEngine.ReportError ("Extension node not found or not extensible: " + ext.Path, data.AddinId, null, false);
1063 					}
1064 				}
1065 				// Call the OnAddinLoaded method on nodes, if the add-in is already loaded
1066 				foreach (TreeNode nod in loadedNodes)
1067 					nod.ExtensionNode.OnAddinLoaded ();
1068 
1069 				NotifyExtensionsChanged (new ExtensionEventArgs (requestedExtensionPath));
1070 			}
1071 		}
1072 
GetAddinExtensions(string id, ExtensionPoint ep)1073 		ExtensionLoadData GetAddinExtensions (string id, ExtensionPoint ep)
1074 		{
1075 			Addin pinfo = null;
1076 
1077 			// Root add-ins are not returned by GetInstalledAddin.
1078 			RuntimeAddin addin = AddinEngine.GetAddin (id);
1079 			if (addin != null)
1080 				pinfo = addin.Addin;
1081 			else
1082 				pinfo = AddinEngine.Registry.GetAddin (id);
1083 
1084 			if (pinfo == null) {
1085 				AddinEngine.ReportError ("Required add-in not found", id, null, false);
1086 				return null;
1087 			}
1088 			if (!pinfo.Enabled || pinfo.Version != Addin.GetIdVersion (id))
1089 				return null;
1090 
1091 			// Loads extensions defined in each module
1092 
1093 			ExtensionLoadData data = null;
1094 			AddinDescription conf = pinfo.Description;
1095 			GetAddinExtensions (conf.MainModule, id, ep, ref data);
1096 
1097 			foreach (ModuleDescription module in conf.OptionalModules) {
1098 				if (CheckOptionalAddinDependencies (conf, module))
1099 					GetAddinExtensions (module, id, ep, ref data);
1100 			}
1101 			if (data != null)
1102 				data.Extensions.Sort ();
1103 
1104 			return data;
1105 		}
1106 
GetAddinExtensions(ModuleDescription module, string addinId, ExtensionPoint ep, ref ExtensionLoadData data)1107 		void GetAddinExtensions (ModuleDescription module, string addinId, ExtensionPoint ep, ref ExtensionLoadData data)
1108 		{
1109 			string basePath = ep.Path + "/";
1110 
1111 			foreach (Extension extension in module.Extensions) {
1112 				if (extension.Path == ep.Path || extension.Path.StartsWith (basePath)) {
1113 					if (data == null) {
1114 						data = new ExtensionLoadData ();
1115 						data.AddinId = addinId;
1116 						data.Extensions = new ArrayList ();
1117 					}
1118 					data.Extensions.Add (extension);
1119 				}
1120 			}
1121 		}
1122 
LoadModuleExtensionNodes(Extension extension, string addinId, ExtensionNodeSet nset, ArrayList loadedNodes)1123 		void LoadModuleExtensionNodes (Extension extension, string addinId, ExtensionNodeSet nset, ArrayList loadedNodes)
1124 		{
1125 			// Now load the extensions
1126 			ArrayList addedNodes = new ArrayList ();
1127 			tree.LoadExtension (addinId, extension, addedNodes);
1128 
1129 			RuntimeAddin ad = AddinEngine.GetAddin (addinId);
1130 			if (ad != null) {
1131 				foreach (TreeNode nod in addedNodes) {
1132 					// Don't call OnAddinLoaded here. Do it when the entire extension point has been loaded.
1133 					if (nod.ExtensionNode != null)
1134 						loadedNodes.Add (nod);
1135 				}
1136 			}
1137 		}
1138 
CheckOptionalAddinDependencies(AddinDescription conf, ModuleDescription module)1139 		bool CheckOptionalAddinDependencies (AddinDescription conf, ModuleDescription module)
1140 		{
1141 			foreach (Dependency dep in module.Dependencies) {
1142 				AddinDependency pdep = dep as AddinDependency;
1143 				if (pdep != null) {
1144 					Addin pinfo = AddinEngine.Registry.GetAddin (Addin.GetFullId (conf.Namespace, pdep.AddinId, pdep.Version));
1145 					if (pinfo == null || !pinfo.Enabled)
1146 						return false;
1147 				}
1148 			}
1149 			return true;
1150 		}
1151 
1152 
GetNode(string path)1153 		TreeNode GetNode (string path)
1154 		{
1155 			TreeNode node = tree.GetNode (path);
1156 			if (node != null || parentContext == null)
1157 				return node;
1158 
1159 			TreeNode supNode = parentContext.tree.GetNode (path);
1160 			if (supNode == null)
1161 				return null;
1162 
1163 			if (path.StartsWith ("/"))
1164 				path = path.Substring (1);
1165 
1166 			string[] parts = path.Split ('/');
1167 			TreeNode srcNode = parentContext.tree;
1168 			TreeNode dstNode = tree;
1169 
1170 			foreach (string part in parts) {
1171 
1172 				// Look for the node in the source tree
1173 
1174 				int i = srcNode.Children.IndexOfNode (part);
1175 				if (i != -1)
1176 					srcNode = srcNode.Children [i];
1177 				else
1178 					return null;
1179 
1180 				// Now get the node in the target tree
1181 
1182 				int j = dstNode.Children.IndexOfNode (part);
1183 				if (j != -1) {
1184 					dstNode = dstNode.Children [j];
1185 				}
1186 				else {
1187 					// Create if not found
1188 					TreeNode newNode = new TreeNode (AddinEngine, part);
1189 					dstNode.AddChildNode (newNode);
1190 					dstNode = newNode;
1191 
1192 					// Copy extension data
1193 					dstNode.ExtensionNodeSet = srcNode.ExtensionNodeSet;
1194 					dstNode.ExtensionPoint = srcNode.ExtensionPoint;
1195 					dstNode.Condition = srcNode.Condition;
1196 
1197 					if (dstNode.Condition != null)
1198 						RegisterNodeCondition (dstNode, dstNode.Condition);
1199 				}
1200 			}
1201 
1202 			return dstNode;
1203 		}
1204 
FindExtensionPathByType(IProgressStatus monitor, Type type, string nodeName, out string path, out string pathNodeName)1205 		internal bool FindExtensionPathByType (IProgressStatus monitor, Type type, string nodeName, out string path, out string pathNodeName)
1206 		{
1207 			return tree.FindExtensionPathByType (monitor, type, nodeName, out path, out pathNodeName);
1208 		}
1209 	}
1210 
1211 	class ConditionInfo
1212 	{
1213 		public object CondType;
1214 		public ArrayList BoundConditions;
1215 	}
1216 
1217 
1218 	/// <summary>
1219 	/// Delegate to be used in extension point subscriptions
1220 	/// </summary>
ExtensionEventHandler(object sender, ExtensionEventArgs args)1221 	public delegate void ExtensionEventHandler (object sender, ExtensionEventArgs args);
1222 
1223 	/// <summary>
1224 	/// Delegate to be used in extension point subscriptions
1225 	/// </summary>
ExtensionNodeEventHandler(object sender, ExtensionNodeEventArgs args)1226 	public delegate void ExtensionNodeEventHandler (object sender, ExtensionNodeEventArgs args);
1227 
1228 	/// <summary>
1229 	/// Arguments for extension events.
1230 	/// </summary>
1231 	public class ExtensionEventArgs: EventArgs
1232 	{
1233 		string path;
1234 
ExtensionEventArgs()1235 		internal ExtensionEventArgs ()
1236 		{
1237 		}
1238 
1239 		/// <summary>
1240 		/// Creates a new instance.
1241 		/// </summary>
1242 		/// <param name="path">
1243 		/// Path of the extension node that has changed.
1244 		/// </param>
ExtensionEventArgs(string path)1245 		public ExtensionEventArgs (string path)
1246 		{
1247 			this.path = path;
1248 		}
1249 
1250 		/// <summary>
1251 		/// Path of the extension node that has changed.
1252 		/// </summary>
1253 		public virtual string Path {
1254 			get { return path; }
1255 		}
1256 
1257 		/// <summary>
1258 		/// Checks if a path has changed.
1259 		/// </summary>
1260 		/// <param name="pathToCheck">
1261 		/// An extension path.
1262 		/// </param>
1263 		/// <returns>
1264 		/// 'true' if the path is affected by the extension change event.
1265 		/// </returns>
1266 		/// <remarks>
1267 		/// Checks if the specified path or any of its children paths is affected by the extension change event.
1268 		/// </remarks>
PathChanged(string pathToCheck)1269 		public bool PathChanged (string pathToCheck)
1270 		{
1271 			if (pathToCheck.EndsWith ("/"))
1272 				return path.StartsWith (pathToCheck);
1273 			else
1274 				return path.StartsWith (pathToCheck) && (pathToCheck.Length == path.Length || path [pathToCheck.Length] == '/');
1275 		}
1276 	}
1277 
1278 	/// <summary>
1279 	/// Arguments for extension node events.
1280 	/// </summary>
1281 	public class ExtensionNodeEventArgs: ExtensionEventArgs
1282 	{
1283 		ExtensionNode node;
1284 		ExtensionChange change;
1285 
1286 		/// <summary>
1287 		/// Creates a new instance
1288 		/// </summary>
1289 		/// <param name="change">
1290 		/// Type of change.
1291 		/// </param>
1292 		/// <param name="node">
1293 		/// Node that has been added or removed.
1294 		/// </param>
ExtensionNodeEventArgs(ExtensionChange change, ExtensionNode node)1295 		public ExtensionNodeEventArgs (ExtensionChange change, ExtensionNode node)
1296 		{
1297 			this.node = node;
1298 			this.change = change;
1299 		}
1300 
1301 		/// <summary>
1302 		/// Path of the extension that changed.
1303 		/// </summary>
1304 		public override string Path {
1305 			get { return node.Path; }
1306 		}
1307 
1308 		/// <summary>
1309 		/// Type of change.
1310 		/// </summary>
1311 		public ExtensionChange Change {
1312 			get { return change; }
1313 		}
1314 
1315 		/// <summary>
1316 		/// Node that has been added or removed.
1317 		/// </summary>
1318 		public ExtensionNode ExtensionNode {
1319 			get { return node; }
1320 		}
1321 
1322 		/// <summary>
1323 		/// Extension object that has been added or removed.
1324 		/// </summary>
1325 		public object ExtensionObject {
1326 			get {
1327 				InstanceExtensionNode tnode = node as InstanceExtensionNode;
1328 				if (tnode == null)
1329 					throw new InvalidOperationException ("Node is not an InstanceExtensionNode");
1330 				return tnode.GetInstance ();
1331 			}
1332 		}
1333 	}
1334 
1335 	/// <summary>
1336 	/// Type of change in an extension change event.
1337 	/// </summary>
1338 	public enum ExtensionChange
1339 	{
1340 		/// <summary>
1341 		/// An extension node has been added.
1342 		/// </summary>
1343 		Add,
1344 
1345 		/// <summary>
1346 		/// An extension node has been removed.
1347 		/// </summary>
1348 		Remove
1349 	}
1350 
1351 
1352 	internal class ExtensionLoadData
1353 	{
1354 		public string AddinId;
1355 		public ArrayList Extensions;
1356 	}
1357 }
1358