ExtensionMethodAddIn.cs :  » Development » Sandcastle » Microsoft » Ddue » Tools » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Development » Sandcastle 
Sandcastle » Microsoft » Ddue » Tools » ExtensionMethodAddIn.cs
// Copyright  Microsoft Corporation.
// This source file is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.XPath;

using System.Compiler;

using Microsoft.Ddue.Tools.Reflection;

namespace Microsoft.Ddue.Tools{

    // Extension method add in

    public class ExtensionMethodAddIn : MRefBuilderAddIn {

        private Dictionary < TypeNode, List < Method > > index = new Dictionary < TypeNode, List < Method > >();

        private bool isExtensionMethod = false;

        private ManagedReflectionWriter reflector;

        public ExtensionMethodAddIn(ManagedReflectionWriter reflector, XPathNavigator configuration) : base(reflector, configuration) {
            this.reflector = reflector;
            reflector.RegisterStartTagCallback("apis", new MRefBuilderCallback(RecordExtensionMethods));
            reflector.RegisterEndTagCallback("elements", new MRefBuilderCallback(AddExtensionMethods));
            reflector.RegisterStartTagCallback("apidata", new MRefBuilderCallback(AddExtensionSubsubgroup));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="type">the current type being extended</param>
        /// <param name="extensionMethodTemplate">A reference to the extension method. For generic methods, this is a reference to the 
        /// non-specialized method, e.g. System.Linq.Enumerable.Select``2.
        /// </param>
        /// <param name="specialization">When the current type implements or inherits from a specialization of a generic type,
        /// this parameter has a TypeNode for the type used as apecialization of the generic type's first template param. 
        /// </param>
        private void AddExtensionMethod(XmlWriter writer, TypeNode type, Method extensionMethodTemplate, TypeNode specialization)
        {
            // If this is a specialization of a generic method, construct a Method object that describes the specialization
            Method extensionMethodTemplate2 = extensionMethodTemplate;
            if (extensionMethodTemplate2.IsGeneric && (specialization != null))
            {
                // the specialization type is the first of the method's template arguments
                TypeNodeList templateArgs = new TypeNodeList();
                templateArgs.Add(specialization);
                // add any additional template arguments
                for (int i = 1; i < extensionMethodTemplate.TemplateParameters.Count; i++)
                    templateArgs.Add(extensionMethodTemplate.TemplateParameters[i]);
                extensionMethodTemplate2 = extensionMethodTemplate.GetTemplateInstance(type, templateArgs);
            }
            TypeNode extensionMethodTemplateReturnType = extensionMethodTemplate2.ReturnType;
            ParameterList extensionMethodTemplateParameters = extensionMethodTemplate2.Parameters;

            ParameterList extensionMethodParameters = new ParameterList();
            for (int i = 1; i < extensionMethodTemplateParameters.Count; i++)
            {
                Parameter extensionMethodParameter = extensionMethodTemplateParameters[i];
                extensionMethodParameters.Add(extensionMethodParameter);
            }
            Method extensionMethod = new Method(extensionMethodTemplate.DeclaringType, new AttributeList(), extensionMethodTemplate.Name, extensionMethodParameters, extensionMethodTemplate.ReturnType, null);
            extensionMethod.Flags = extensionMethodTemplate.Flags & ~MethodFlags.Static;

            // for generic methods, set the template args and params so the template data is included in the id and the method data
            if (extensionMethodTemplate2.IsGeneric)
            {
                extensionMethod.IsGeneric = true;
                if (specialization != null)
                {
                    // set the template args for the specialized generic method
                    extensionMethod.TemplateArguments = extensionMethodTemplate2.TemplateArguments;
                }
                else
                {
                    // set the generic template params for the non-specialized generic method
                    extensionMethod.TemplateParameters = extensionMethodTemplate2.TemplateParameters;
                }
            }

            // Get the id
            string extensionMethodTemplateId = reflector.ApiNamer.GetMemberName(extensionMethodTemplate);

            // write the element node
            writer.WriteStartElement("element");
            writer.WriteAttributeString("api", extensionMethodTemplateId);
            writer.WriteAttributeString("source", "extension");
            isExtensionMethod = true;
            reflector.WriteMember(extensionMethod);
            isExtensionMethod = false;
            writer.WriteEndElement();
        }

        private void AddExtensionMethods(XmlWriter writer, Object info)
        {

            MemberDictionary members = info as MemberDictionary;
            if (members == null) return;

            TypeNode type = members.Type;

            InterfaceList contracts = type.Interfaces;
            foreach (Interface contract in contracts)
            {
                List<Method> extensionMethods = null;
                if (index.TryGetValue(contract, out extensionMethods))
                {
                    foreach (Method extensionMethod in extensionMethods)
                        if (!IsExtensionMethodHidden(extensionMethod, members))
                            AddExtensionMethod(writer, type, extensionMethod, null);
                }
                if (contract.IsGeneric && (contract.TemplateArguments != null) && (contract.TemplateArguments.Count > 0))
                {
                    Interface templateContract = (Interface)ReflectionUtilities.GetTemplateType(contract);
                    TypeNode specialization = contract.TemplateArguments[0];
                    if (index.TryGetValue(templateContract, out extensionMethods))
                    {
                        foreach (Method extensionMethod in extensionMethods)
                        {
                            if (IsValidTemplateArgument(specialization, extensionMethod.TemplateParameters[0]))
                            {
                                if (!IsExtensionMethodHidden(extensionMethod, members))
                                    AddExtensionMethod(writer, type, extensionMethod, specialization);
                            }
                        }
                    }
                }
            }

            TypeNode comparisonType = type;
            while (comparisonType != null)
            {
                List<Method> extensionMethods = null;
                if (index.TryGetValue(comparisonType, out extensionMethods))
                {
                    foreach (Method extensionMethod in extensionMethods)
                        if (!IsExtensionMethodHidden(extensionMethod, members))
                            AddExtensionMethod(writer, type, extensionMethod, null);
                }
                if (comparisonType.IsGeneric && (comparisonType.TemplateArguments != null) && (comparisonType.TemplateArguments.Count > 0))
                {
                    TypeNode templateType = ReflectionUtilities.GetTemplateType(comparisonType);
                    TypeNode specialization = comparisonType.TemplateArguments[0];
                    if (index.TryGetValue(templateType, out extensionMethods))
                    {
                        foreach (Method extensionMethod in extensionMethods)
                        {
                            if (IsValidTemplateArgument(specialization, extensionMethod.TemplateParameters[0]))
                            {
                                if (!IsExtensionMethodHidden(extensionMethod, members))
                                    AddExtensionMethod(writer, type, extensionMethod, specialization);
                            }
                        }
                    }
                }
                comparisonType = comparisonType.BaseType;
            }
        }

        private void AddExtensionSubsubgroup(XmlWriter writer, Object data) {
            if (isExtensionMethod) writer.WriteAttributeString("subsubgroup", "extension");
        }

        private bool HasExtensionAttribute(Method method) {
            AttributeList attributes = method.Attributes;
            foreach (AttributeNode attribute in attributes) {
                if (attribute.Type.FullName == "System.Runtime.CompilerServices.ExtensionAttribute") return (true);
            }
            return (false);
        }

        private bool IsValidTemplateArgument(TypeNode type, TypeNode parameter) {
            if (type == null) throw new ArgumentNullException("type");
            if (parameter == null) throw new ArgumentNullException("parameter");

            // check that the parameter really is a type parameter

            ITypeParameter itp = parameter as ITypeParameter;
            if (itp == null) throw new ArgumentException("The 'parameter' argument is null or not an 'ITypeParameter'.");

            // test constraints

            bool reference = ((itp.TypeParameterFlags & TypeParameterFlags.ReferenceTypeConstraint) > 0);
            if (reference && type.IsValueType) return (false);

            bool value = ((itp.TypeParameterFlags & TypeParameterFlags.ValueTypeConstraint) > 0);
            if (value && !type.IsValueType) return (false);

            bool constructor = ((itp.TypeParameterFlags & TypeParameterFlags.DefaultConstructorConstraint) > 0);


            InterfaceList contracts = parameter.Interfaces;
            if (contracts != null) {
                foreach (Interface contract in contracts) {
                    if (!type.IsAssignableTo(contract)) return (false);
                }
            }

            TypeNode parent = parameter.BaseType;
            if ((parent != null) && !type.IsAssignableTo(parent)) return (false);

            // okay, passed all tests

            return (true);

        }

        private void RecordExtensionMethods(XmlWriter writer, Object info)
        {
            NamespaceList spaces = (NamespaceList)info;
            foreach (Namespace space in spaces)
            {
                TypeNodeList types = space.Types;
                foreach (TypeNode type in types)
                {

                    MemberList members = type.Members;

                    // go through the members, looking for fields signaling extension methods
                    foreach (Member member in members)
                    {

                        Method method = member as Method;
                        if (method == null) continue;

                        if (!reflector.ApiFilter.IsExposedMember(method)) continue;

                        if (!HasExtensionAttribute(method)) continue;

                        ParameterList parameters = method.Parameters;
                        TypeNode extendedType = parameters[0].Type;

                        // recognize generic extension methods where the extended type is a specialization of a generic type,  
                        // and the extended type's specialized template arg is a type parameter declared by the generic extension method 
                        // In this case, we need to save a TypeNode for the non-specialized type in the index, 
                        // because a TypeNode for the specialized type won't match correctly in AddExtensionMethods
                        // Note: we are not interested in extended types that are specialized by a specific type rather than by the extension method's template param.
                        if (method.IsGeneric && (method.TemplateParameters.Count > 0))
                        {
                            if (extendedType.IsGeneric && (extendedType.TemplateArguments != null) && (extendedType.TemplateArguments.Count == 1))
                            {
                                // is the extended type's template arg a template parameter, rather than a specialized type?
                                TypeNode arg = extendedType.TemplateArguments[0];
                                if (arg.IsTemplateParameter)
                                {
                                    // is the template parameter declared on the extension method
                                    ITypeParameter gtp = (ITypeParameter)arg;
                                    if ((gtp.DeclaringMember == method) && (gtp.ParameterListIndex == 0))
                                    {
                                        // get a TypeNode for the non-specialized type
                                        extendedType = ReflectionUtilities.GetTemplateType(extendedType);
                                    }
                                }
                            }
                        }

                        List<Method> methods = null;
                        if (!index.TryGetValue(extendedType, out methods))
                        {
                            methods = new List<Method>();
                            index.Add(extendedType, methods);
                        }
                        methods.Add(method);

                    }
                }

            }
        }

        /// <summary>
        /// Determines whether an extension method is hidden by a member that's already defined on the type being extended.
        /// The extension method is hidden if it has the same name, template params, and parameters as a defined method.
        /// </summary>
        /// <param name="extensionMethod">The extension method to compare.</param>
        /// <param name="members">A dictionary of the members defined on the type being extended.</param>
        /// <returns></returns>
        private bool IsExtensionMethodHidden(Method extensionMethod, MemberDictionary members)
        {
            if (!members.MemberNames.Contains(extensionMethod.Name.Name))
                return false;

            // get a list of members with the same name as the extension method
            List<Member> membersList = members[extensionMethod.Name.Name];
            foreach (Member member in membersList)
            {
                // the hiding member must be a method
                if (member.NodeType != NodeType.Method)
                    continue;
                Method method = (Method)member;

                // do the generic template parameters of both methods match?
                if (!method.TemplateParametersMatch(extensionMethod.TemplateParameters))
                    continue;

                // do both methods have the same number of parameters?
                // (not counting the extension method's first param, which identifies the extended type)
                if (method.Parameters.Count != (extensionMethod.Parameters.Count - 1))
                    continue;

                // do the parameter types of both methods match?
                if (DoParameterTypesMatch(extensionMethod.Parameters, method.Parameters))
                    return true;
            }
            return false;
        }

        private bool DoParameterTypesMatch(ParameterList extensionParams, ParameterList methodParams)
        {
            for (int i = 0; i < methodParams.Count; i++)
            {
                if (methodParams[i].Type.FullName != extensionParams[i + 1].Type.FullName)
                    return false;
            }
            return true;
        }



    }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.