Fixing an issue with output parameters that are of type IntPtr
[castle.git] / MonoRail / Castle.MonoRail.Framework / Providers / DefaultControllerDescriptorProvider.cs
blob3d0f7d6a6b2458804c74265313cf6ad8b6e9eabf
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework.Providers
17 using System;
18 using System.Collections;
19 using System.Reflection;
20 using System.Threading;
21 using Castle.Core.Logging;
22 using Castle.MonoRail.Framework.Services.Utils;
23 using Descriptors;
24 using Providers;
26 /// <summary>
27 /// Constructs and caches all collected information
28 /// about a <see cref="IController"/> and its actions.
29 /// <seealso cref="ControllerMetaDescriptor"/>
30 /// </summary>
31 public class DefaultControllerDescriptorProvider : IControllerDescriptorProvider
33 /// <summary>
34 /// The logger instance
35 /// </summary>
36 private ILogger logger = NullLogger.Instance;
38 /// <summary>
39 /// Used to lock the cache
40 /// </summary>
41 private ReaderWriterLock locker = new ReaderWriterLock();
43 private Hashtable descriptorRepository = new Hashtable();
44 private IHelperDescriptorProvider helperDescriptorProvider;
45 private IFilterDescriptorProvider filterDescriptorProvider;
46 private ILayoutDescriptorProvider layoutDescriptorProvider;
47 private IRescueDescriptorProvider rescueDescriptorProvider;
48 private IResourceDescriptorProvider resourceDescriptorProvider;
49 private ITransformFilterDescriptorProvider transformFilterDescriptorProvider;
50 private IReturnBinderDescriptorProvider returnBinderDescriptorProvider;
52 /// <summary>
53 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
54 /// </summary>
55 public DefaultControllerDescriptorProvider()
59 /// <summary>
60 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
61 /// </summary>
62 /// <param name="helperDescriptorProvider">The helper descriptor provider.</param>
63 /// <param name="filterDescriptorProvider">The filter descriptor provider.</param>
64 /// <param name="layoutDescriptorProvider">The layout descriptor provider.</param>
65 /// <param name="rescueDescriptorProvider">The rescue descriptor provider.</param>
66 /// <param name="resourceDescriptorProvider">The resource descriptor provider.</param>
67 /// <param name="transformFilterDescriptorProvider">The transform filter descriptor provider.</param>
68 /// <param name="returnBinderDescriptorProvider">The return binder descriptor provider.</param>
69 public DefaultControllerDescriptorProvider(IHelperDescriptorProvider helperDescriptorProvider,
70 IFilterDescriptorProvider filterDescriptorProvider,
71 ILayoutDescriptorProvider layoutDescriptorProvider,
72 IRescueDescriptorProvider rescueDescriptorProvider,
73 IResourceDescriptorProvider resourceDescriptorProvider,
74 ITransformFilterDescriptorProvider transformFilterDescriptorProvider,
75 IReturnBinderDescriptorProvider returnBinderDescriptorProvider)
77 this.helperDescriptorProvider = helperDescriptorProvider;
78 this.filterDescriptorProvider = filterDescriptorProvider;
79 this.layoutDescriptorProvider = layoutDescriptorProvider;
80 this.rescueDescriptorProvider = rescueDescriptorProvider;
81 this.resourceDescriptorProvider = resourceDescriptorProvider;
82 this.transformFilterDescriptorProvider = transformFilterDescriptorProvider;
83 this.returnBinderDescriptorProvider = returnBinderDescriptorProvider;
86 #region IMRServiceEnabled implementation
88 /// <summary>
89 /// Services the specified service provider.
90 /// </summary>
91 /// <param name="serviceProvider">The service provider.</param>
92 public void Service(IMonoRailServices serviceProvider)
94 ILoggerFactory loggerFactory = (ILoggerFactory) serviceProvider.GetService(typeof(ILoggerFactory));
96 if (loggerFactory != null)
98 logger = loggerFactory.Create(typeof(DefaultControllerDescriptorProvider));
101 helperDescriptorProvider = serviceProvider.GetService<IHelperDescriptorProvider>();
102 filterDescriptorProvider = serviceProvider.GetService<IFilterDescriptorProvider>();
103 layoutDescriptorProvider = serviceProvider.GetService<ILayoutDescriptorProvider>();
104 rescueDescriptorProvider = serviceProvider.GetService<IRescueDescriptorProvider>();
105 resourceDescriptorProvider = serviceProvider.GetService<IResourceDescriptorProvider>();
106 transformFilterDescriptorProvider = serviceProvider.GetService<ITransformFilterDescriptorProvider>();
107 returnBinderDescriptorProvider = serviceProvider.GetService<IReturnBinderDescriptorProvider>();
110 #endregion
112 /// <summary>
113 /// Occurs when the providers needs to create a <see cref="ControllerMetaDescriptor"/>.
114 /// </summary>
115 public event MetaCreatorHandler Create;
117 /// <summary>
118 /// Occurs when the meta descriptor is about to the returned to the caller.
119 /// </summary>
120 public event ControllerMetaDescriptorHandler AfterProcess;
122 /// <summary>
123 /// Occurs when the providers needs to create a <see cref="ActionMetaDescriptor"/>.
124 /// </summary>
125 public event ActionMetaCreatorHandler ActionCreate;
127 /// <summary>
128 /// Occurs when the meta descriptor is about to be included on the <see cref="ControllerMetaDescriptor"/>.
129 /// </summary>
130 public event ActionMetaDescriptorHandler AfterActionProcess;
132 /// <summary>
133 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
134 /// </summary>
135 /// <remarks>
136 /// This implementation is also responsible for caching
137 /// constructed meta descriptors.
138 /// </remarks>
139 public ControllerMetaDescriptor BuildDescriptor(IController controller)
141 Type controllerType = controller.GetType();
143 return BuildDescriptor(controllerType);
146 /// <summary>
147 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
148 /// </summary>
149 /// <remarks>
150 /// This implementation is also responsible for caching
151 /// constructed meta descriptors.
152 /// </remarks>
153 public ControllerMetaDescriptor BuildDescriptor(Type controllerType)
155 ControllerMetaDescriptor desc;
157 locker.AcquireReaderLock(-1);
160 desc = (ControllerMetaDescriptor) descriptorRepository[controllerType];
162 if (desc != null)
164 return desc;
167 finally
169 locker.ReleaseReaderLock();
174 locker.UpgradeToWriterLock(-1);
176 // We need to recheck after getting the writer lock
177 desc = (ControllerMetaDescriptor) descriptorRepository[controllerType];
179 if (desc != null)
181 return desc;
184 desc = InternalBuildDescriptor(controllerType);
186 descriptorRepository[controllerType] = desc;
188 finally
190 locker.ReleaseWriterLock();
193 return desc;
196 /// <summary>
197 /// Builds the <see cref="ControllerMetaDescriptor"/> for the specified controller type
198 /// </summary>
199 /// <param name="controllerType">Type of the controller.</param>
200 /// <returns></returns>
201 private ControllerMetaDescriptor InternalBuildDescriptor(Type controllerType)
203 if (logger.IsDebugEnabled)
205 logger.DebugFormat("Building controller descriptor for {0}", controllerType);
208 ControllerMetaDescriptor descriptor = CreateMetaDescriptor();
210 descriptor.ControllerDescriptor = ControllerInspectionUtil.Inspect(controllerType);
212 CollectClassLevelAttributes(controllerType, descriptor);
214 CollectActions(controllerType, descriptor);
216 CollectActionLevelAttributes(descriptor);
218 if (AfterProcess != null)
220 AfterProcess(descriptor);
223 return descriptor;
226 #region Action data
228 /// <summary>
229 /// Collects the actions.
230 /// </summary>
231 /// <param name="controllerType">Type of the controller.</param>
232 /// <param name="desc">The desc.</param>
233 private void CollectActions(Type controllerType, ControllerMetaDescriptor desc)
235 // HACK: GetRealControllerType is a workaround for DYNPROXY-14 bug
236 // see: http://support.castleproject.org/browse/DYNPROXY-14
237 controllerType = GetRealControllerType(controllerType);
239 MethodInfo[] methods = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
241 foreach(MethodInfo method in methods)
243 Type declaringType = method.DeclaringType;
245 if (method.IsSpecialName)
247 continue;
250 if (declaringType == typeof(Object) ||
251 declaringType == typeof(IController) || declaringType == typeof(Controller))
252 // || declaringType == typeof(SmartDispatcherController))
254 continue;
257 if (desc.Actions.Contains(method.Name))
259 ArrayList list = desc.Actions[method.Name] as ArrayList;
261 if (list == null)
263 list = new ArrayList();
264 list.Add(desc.Actions[method.Name]);
266 desc.Actions[method.Name] = list;
269 list.Add(method);
271 else
273 desc.Actions[method.Name] = method;
277 MergeAsyncMethodPairsToSingleAction(desc);
280 private void MergeAsyncMethodPairsToSingleAction(ControllerMetaDescriptor desc)
282 foreach(string name in new ArrayList(desc.Actions.Keys))
284 // skip methods that are not named "BeginXyz", including "Begin"
285 if (name.StartsWith("Begin", StringComparison.InvariantCultureIgnoreCase) == false || name.Length == 5)
287 continue;
290 string actionName = name.Substring("Begin".Length);
292 ArrayList list = desc.Actions[name] as ArrayList;
293 if (list != null)
295 foreach(MethodInfo info in list)
297 if (info.ReturnType == typeof(IAsyncResult))
299 throw new MonoRailException("Action '" + actionName + "' on controller '" + desc.ControllerDescriptor.Name +
300 "' is an async action, but there are method overloads '" + name +
301 "(...)', which is not allowed on async actions.");
304 continue;
307 if (desc.Actions.Contains(actionName))
309 throw new MonoRailException("Found both async method '" + name + "' and sync method '" + actionName +
310 "' on controller '" + desc.ControllerDescriptor.Name +
311 "'. MonoRail doesn't support mixing sync and async methods for the same action");
314 string endActionName = "End" + actionName;
315 if (desc.Actions.Contains(endActionName) == false)
317 throw new MonoRailException("Found beginning of async pair '" + name + "' but not the end '" + endActionName +
318 "' on controller '" + desc.ControllerDescriptor.Name + "', did you forget to define " +
319 endActionName + "(IAsyncResult ar) ?");
322 if (desc.Actions[endActionName] is IList)
324 throw new MonoRailException("Found more than a single " + endActionName +
325 " method, for async action '" + actionName + "' on controller '" +
326 desc.ControllerDescriptor.Name + "', only a single " +
327 endActionName + " may be defined as part of an async action");
330 MethodInfo beginActionInfo = (MethodInfo) desc.Actions[name];
331 MethodInfo endActionInfo = (MethodInfo) desc.Actions[endActionName];
333 desc.Actions.Remove(name);
334 desc.Actions.Remove(endActionName);
335 desc.Actions[actionName] = new AsyncActionPair(actionName, beginActionInfo, endActionInfo);
339 /// <summary>
340 /// Collects the action level attributes.
341 /// </summary>
342 /// <param name="descriptor">The descriptor.</param>
343 private void CollectActionLevelAttributes(ControllerMetaDescriptor descriptor)
345 foreach(object action in descriptor.Actions.Values)
347 if (action is IList)
349 foreach(MethodInfo overloadedAction in (action as IList))
351 CollectActionAttributes(overloadedAction, descriptor);
354 continue;
357 MethodInfo methodInfo = action as MethodInfo;
358 if (methodInfo != null)
360 CollectActionAttributes(methodInfo, descriptor);
362 else
364 AsyncActionPair asyncActionPair = (AsyncActionPair) action;
365 CollectActionAttributes(asyncActionPair.BeginActionInfo, descriptor);
366 CollectActionAttributes(asyncActionPair.EndActionInfo, descriptor);
371 /// <summary>
372 /// Collects the action attributes.
373 /// </summary>
374 /// <param name="method">The method.</param>
375 /// <param name="descriptor">The descriptor.</param>
376 private void CollectActionAttributes(MethodInfo method, ControllerMetaDescriptor descriptor)
378 if (logger.IsDebugEnabled)
380 logger.DebugFormat("Collection attributes for action {0}", method.Name);
383 ActionMetaDescriptor actionDescriptor = descriptor.GetAction(method);
385 if (actionDescriptor == null)
387 actionDescriptor = CreateActionDescriptor();
389 descriptor.AddAction(method, actionDescriptor);
392 CollectResources(actionDescriptor, method);
393 CollectSkipFilter(actionDescriptor, method);
394 CollectRescues(actionDescriptor, method);
395 CollectAccessibleThrough(actionDescriptor, method);
396 CollectSkipRescue(actionDescriptor, method);
397 CollectLayout(actionDescriptor, method);
398 CollectCacheConfigure(actionDescriptor, method);
399 CollectTransformFilter(actionDescriptor, method);
400 CollectReturnTypeBinder(actionDescriptor, method);
402 if (method.IsDefined(typeof(AjaxActionAttribute), true))
404 descriptor.AjaxActions.Add(method);
407 if (method.IsDefined(typeof(DefaultActionAttribute), true))
409 if (descriptor.DefaultAction != null)
411 throw new MonoRailException(
412 "Cannot resolve a default action for {0}, DefaultActionAttribute was declared more than once.",
413 method.DeclaringType.FullName);
415 descriptor.DefaultAction = new DefaultActionAttribute(method.Name);
418 if (AfterActionProcess != null)
420 AfterActionProcess(actionDescriptor);
424 /// <summary>
425 /// Collects the skip rescue.
426 /// </summary>
427 /// <param name="actionDescriptor">The action descriptor.</param>
428 /// <param name="method">The method.</param>
429 private void CollectSkipRescue(ActionMetaDescriptor actionDescriptor, MethodInfo method)
431 object[] attributes = method.GetCustomAttributes(typeof(SkipRescueAttribute), true);
433 if (attributes.Length != 0)
435 actionDescriptor.SkipRescue = (SkipRescueAttribute) attributes[0];
439 /// <summary>
440 /// Collects the accessible through.
441 /// </summary>
442 /// <param name="actionDescriptor">The action descriptor.</param>
443 /// <param name="method">The method.</param>
444 private void CollectAccessibleThrough(ActionMetaDescriptor actionDescriptor, MethodInfo method)
446 object[] attributes = method.GetCustomAttributes(typeof(AccessibleThroughAttribute), true);
448 if (attributes.Length != 0)
450 actionDescriptor.AccessibleThrough = (AccessibleThroughAttribute) attributes[0];
454 /// <summary>
455 /// Collects the skip filter.
456 /// </summary>
457 /// <param name="actionDescriptor">The action descriptor.</param>
458 /// <param name="method">The method.</param>
459 private void CollectSkipFilter(ActionMetaDescriptor actionDescriptor, MethodInfo method)
461 object[] attributes = method.GetCustomAttributes(typeof(SkipFilterAttribute), true);
463 foreach(SkipFilterAttribute attr in attributes)
465 actionDescriptor.SkipFilters.Add(attr);
469 /// <summary>
470 /// Collects the resources.
471 /// </summary>
472 /// <param name="desc">The desc.</param>
473 /// <param name="memberInfo">The member info.</param>
474 private void CollectResources(BaseMetaDescriptor desc, MemberInfo memberInfo)
476 desc.Resources = resourceDescriptorProvider.CollectResources(memberInfo);
479 /// <summary>
480 /// Collects the transform filter.
481 /// </summary>
482 /// <param name="actionDescriptor">The action descriptor.</param>
483 /// <param name="method">The method.</param>
484 private void CollectTransformFilter(ActionMetaDescriptor actionDescriptor, MethodInfo method)
486 actionDescriptor.TransformFilters = transformFilterDescriptorProvider.CollectFilters((method));
487 Array.Sort(actionDescriptor.TransformFilters, TransformFilterDescriptorComparer.Instance);
490 /// <summary>
491 /// Collects the return type binder.
492 /// </summary>
493 /// <param name="actionDescriptor">The action descriptor.</param>
494 /// <param name="method">The method.</param>
495 private void CollectReturnTypeBinder(ActionMetaDescriptor actionDescriptor, MethodInfo method)
497 actionDescriptor.ReturnDescriptor = returnBinderDescriptorProvider.Collect(method);
500 /// <summary>
501 /// Gets the real controller type, instead of the proxy type.
502 /// </summary>
503 /// <remarks>
504 /// Workaround for DYNPROXY-14 bug. See: http://support.castleproject.org/browse/DYNPROXY-14
505 /// </remarks>
506 private Type GetRealControllerType(Type controllerType)
508 Type prev = controllerType;
510 // try to get the first non-proxy type
511 while(controllerType.Assembly.FullName.StartsWith("DynamicProxyGenAssembly2") ||
512 controllerType.Assembly.FullName.StartsWith("DynamicAssemblyProxyGen"))
514 controllerType = controllerType.BaseType;
516 if ( // controllerType == typeof(SmartDispatcherController) ||
517 controllerType == typeof(IController))
519 // oops, it's a pure-proxy controller. just let it go.
520 controllerType = prev;
521 break;
525 return controllerType;
528 #endregion
530 #region Controller data
532 /// <summary>
533 /// Collects the class level attributes.
534 /// </summary>
535 /// <param name="controllerType">Type of the controller.</param>
536 /// <param name="descriptor">The descriptor.</param>
537 private void CollectClassLevelAttributes(Type controllerType, ControllerMetaDescriptor descriptor)
539 CollectHelpers(descriptor, controllerType);
540 CollectResources(descriptor, controllerType);
541 CollectFilters(descriptor, controllerType);
542 CollectLayout(descriptor, controllerType);
543 CollectRescues(descriptor, controllerType);
544 CollectDefaultAction(descriptor, controllerType);
545 CollectScaffolding(descriptor, controllerType);
546 CollectDynamicAction(descriptor, controllerType);
547 CollectCacheConfigure(descriptor, controllerType);
550 /// <summary>
551 /// Collects the default action.
552 /// </summary>
553 /// <param name="descriptor">The descriptor.</param>
554 /// <param name="controllerType">Type of the controller.</param>
555 private void CollectDefaultAction(ControllerMetaDescriptor descriptor, Type controllerType)
557 object[] attributes = controllerType.GetCustomAttributes(typeof(DefaultActionAttribute), true);
559 if (attributes.Length != 0)
561 descriptor.DefaultAction = (DefaultActionAttribute) attributes[0];
565 /// <summary>
566 /// Collects the scaffolding.
567 /// </summary>
568 /// <param name="descriptor">The descriptor.</param>
569 /// <param name="controllerType">Type of the controller.</param>
570 private void CollectScaffolding(ControllerMetaDescriptor descriptor, Type controllerType)
572 object[] attributes = controllerType.GetCustomAttributes(typeof(ScaffoldingAttribute), false);
574 if (attributes.Length != 0)
576 foreach(ScaffoldingAttribute scaffolding in attributes)
578 descriptor.Scaffoldings.Add(scaffolding);
583 /// <summary>
584 /// Collects the dynamic action.
585 /// </summary>
586 /// <param name="descriptor">The descriptor.</param>
587 /// <param name="controllerType">Type of the controller.</param>
588 private void CollectDynamicAction(ControllerMetaDescriptor descriptor, Type controllerType)
590 object[] attributes = controllerType.GetCustomAttributes(typeof(DynamicActionProviderAttribute), true);
592 if (attributes.Length != 0)
594 foreach(DynamicActionProviderAttribute attr in attributes)
596 descriptor.ActionProviders.Add(attr.ProviderType);
601 /// <summary>
602 /// Collects the helpers.
603 /// </summary>
604 /// <param name="descriptor">The descriptor.</param>
605 /// <param name="controllerType">Type of the controller.</param>
606 private void CollectHelpers(ControllerMetaDescriptor descriptor, Type controllerType)
608 descriptor.Helpers = helperDescriptorProvider.CollectHelpers(controllerType);
611 /// <summary>
612 /// Collects the filters.
613 /// </summary>
614 /// <param name="descriptor">The descriptor.</param>
615 /// <param name="controllerType">Type of the controller.</param>
616 private void CollectFilters(ControllerMetaDescriptor descriptor, Type controllerType)
618 descriptor.Filters = filterDescriptorProvider.CollectFilters(controllerType);
620 Array.Sort(descriptor.Filters, FilterDescriptorComparer.Instance);
623 /// <summary>
624 /// Collects the layout.
625 /// </summary>
626 /// <param name="descriptor">The descriptor.</param>
627 /// <param name="memberInfo">The member info.</param>
628 private void CollectLayout(BaseMetaDescriptor descriptor, MemberInfo memberInfo)
630 descriptor.Layout = layoutDescriptorProvider.CollectLayout(memberInfo);
633 /// <summary>
634 /// Collects the rescues.
635 /// </summary>
636 /// <param name="descriptor">The descriptor.</param>
637 /// <param name="memberInfo">The member info.</param>
638 private void CollectRescues(BaseMetaDescriptor descriptor, MethodInfo memberInfo)
640 descriptor.Rescues = rescueDescriptorProvider.CollectRescues(memberInfo);
643 /// <summary>
644 /// Collects the rescues.
645 /// </summary>
646 /// <param name="descriptor">The descriptor.</param>
647 /// <param name="type">The type.</param>
648 private void CollectRescues(BaseMetaDescriptor descriptor, Type type)
650 descriptor.Rescues = rescueDescriptorProvider.CollectRescues(type);
653 /// <summary>
654 /// Collects the cache configures.
655 /// </summary>
656 /// <param name="descriptor">The descriptor.</param>
657 /// <param name="memberInfo">The member info.</param>
658 private void CollectCacheConfigure(BaseMetaDescriptor descriptor, MemberInfo memberInfo)
660 object[] configurers = memberInfo.GetCustomAttributes(typeof(ICachePolicyConfigurer), true);
662 if (configurers.Length != 0)
664 descriptor.CacheConfigurer = (ICachePolicyConfigurer) configurers[0];
668 #endregion
670 private ControllerMetaDescriptor CreateMetaDescriptor()
672 if (Create != null)
674 return Create();
676 else
678 return new ControllerMetaDescriptor();
682 private ActionMetaDescriptor CreateActionDescriptor()
684 if (ActionCreate != null)
686 return ActionCreate();
688 else
690 return new ActionMetaDescriptor();
694 /// <summary>
695 /// This <see cref="IComparer"/> implementation
696 /// is used to sort the filters based on their Execution Order.
697 /// </summary>
698 private class FilterDescriptorComparer : IComparer
700 private static readonly FilterDescriptorComparer instance = new FilterDescriptorComparer();
702 /// <summary>
703 /// Initializes a new instance of the <see cref="FilterDescriptorComparer"/> class.
704 /// </summary>
705 private FilterDescriptorComparer()
709 /// <summary>
710 /// Gets the instance.
711 /// </summary>
712 /// <value>The instance.</value>
713 public static FilterDescriptorComparer Instance
715 get { return instance; }
718 /// <summary>
719 /// Compares the specified left.
720 /// </summary>
721 /// <param name="left">The left.</param>
722 /// <param name="right">The right.</param>
723 /// <returns></returns>
724 public int Compare(object left, object right)
726 return ((FilterDescriptor) left).ExecutionOrder - ((FilterDescriptor) right).ExecutionOrder;
730 /// <summary>
731 /// This <see cref="IComparer"/> implementation
732 /// is used to sort the transformfilters based on their Execution Order.
733 /// </summary>
734 private class TransformFilterDescriptorComparer : IComparer
736 private static readonly TransformFilterDescriptorComparer instance = new TransformFilterDescriptorComparer();
738 /// <summary>
739 /// Initializes a new instance of the <see cref="TransformFilterDescriptorComparer"/> class.
740 /// </summary>
741 private TransformFilterDescriptorComparer()
745 /// <summary>
746 /// Gets the instance.
747 /// </summary>
748 /// <value>The instance.</value>
749 public static TransformFilterDescriptorComparer Instance
751 get { return instance; }
754 /// <summary>
755 /// Compares the specified left.
756 /// </summary>
757 /// <param name="left">The left.</param>
758 /// <param name="right">The right.</param>
759 /// <returns></returns>
760 public int Compare(object left, object right)
762 return ((TransformFilterDescriptor) right).ExecutionOrder - ((TransformFilterDescriptor) left).ExecutionOrder;