diff options
Diffstat (limited to 'tools/Sandcastle/Source/Reflection')
23 files changed, 2806 insertions, 0 deletions
diff --git a/tools/Sandcastle/Source/Reflection/AllDocumentedFilter.cs b/tools/Sandcastle/Source/Reflection/AllDocumentedFilter.cs new file mode 100644 index 0000000..a171b52 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/AllDocumentedFilter.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +//using System; +using System.Compiler; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools.Reflection { + + // exposes all apis, including internal apis, for which documentation is written + + // this includes all members except for property and event accessors (e.g. get_ methods) and delegate members (e.g. Invoke). + + // enumeration members are included + + public class AllDocumentedFilter : ApiFilter { + + public AllDocumentedFilter() : base() { } + + public AllDocumentedFilter(XPathNavigator configuration) : base(configuration) { } + + + + public override bool IsExposedMember(Member member) { + + // member of delegates are not exposed + TypeNode type = member.DeclaringType; + if (type.NodeType == NodeType.DelegateNode) return (false); + + // accessor methods for properties and events are not exposed + if (member.IsSpecialName && (member.NodeType == NodeType.Method)) { + string name = member.Name.Name; + if (NameContains(name, "get_")) return (false); + if (NameContains(name, "set_")) return (false); + if (NameContains(name, "add_")) return (false); + if (NameContains(name, "remove_")) return (false); + if (NameContains(name, "raise_")) return (false); + } + + // the value field of enumerations is not exposed + if (member.IsSpecialName && (type.NodeType == NodeType.EnumNode) && (member.NodeType == NodeType.Field)) { + string name = member.Name.Name; + if (name == "value__") return (false); + } + + // members marked as compiler-generated are not exposed + if (ListContainsAttribute(member.Attributes, "System.Runtime.CompilerServices.CompilerGeneratedAttribute")) { + return (false); + } + + // okay, passed all tests, so member is exposed + return (base.IsExposedMember(member)); + + } + + // all namespace and all types are exposed + + /** <summary>Check the given type and all parent types for compiler attributes. + * If none are found look for any filters to determine if it is exposed.</summary> */ + public override bool IsExposedType(TypeNode type) { + + // don't include compiler-generated types + // check this and all parents for compiler attributes + TypeNode curType = type; //cursor + while (curType != null) + { + if (ListContainsAttribute(curType.Attributes, "System.Runtime.CompilerServices.CompilerGeneratedAttribute")) + return false; + + curType = curType.DeclaringType; //check the next parent + } + + //continue on with checking if the type is exposed + return base.IsExposedType(type); + } + + private static bool ListContainsAttribute(AttributeList attributes, string name) { + for (int i = 0; i < attributes.Count; i++) { + if (attributes[i].Type.FullName == name) return (true); + } + return (false); + } + + private static bool NameContains(string name, string substring) { + return (name.Contains(substring)); + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/AllTopicFilter.cs b/tools/Sandcastle/Source/Reflection/AllTopicFilter.cs new file mode 100644 index 0000000..b92bba6 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/AllTopicFilter.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + // exposes all apis for which a topic exists + // this includes all documented members (see DocumentFilter) except for enumeration members + + public class AllTopicFilter : AllDocumentedFilter { + + public override bool IsExposedMember(Member member) { + // don't expose members of enumerations + if (member.DeclaringType.NodeType == NodeType.EnumNode) return (false); + // otherwise, agree with DocumentedFilter + return (base.IsExposedMember(member)); + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/ApiFilter.cs b/tools/Sandcastle/Source/Reflection/ApiFilter.cs new file mode 100644 index 0000000..201878f --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/ApiFilter.cs @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class ApiFilter { + +#region Member Variables + + private RootFilter apiFilter = new RootFilter(true); + + private RootFilter attributeFilter = new RootFilter(true); + + private Dictionary < string, bool > namespaceCache = new Dictionary < string, bool >(); + +#endregion + +#region Constructors + + // stored filters + + public ApiFilter() { + // apiFilters.Add(new UserFilter("System.Reflection", "", "", true)); + // apiFilters.Add(new UserFilter("System", "Object", "", true)); + // apiFilters.Add(new UserFilter("", "", "", false)); + + // attributeFilters.Add(new UserFilter("System.Runtime.InteropServices", "ComVisibleAttribute", "", false)); + } + + public ApiFilter(XPathNavigator configuration) { + + if (configuration == null) throw new ArgumentNullException("configuration"); + + // API filter nodes + XPathNavigator apiFilterNode = configuration.SelectSingleNode("apiFilter"); + if (apiFilterNode != null) { + XmlReader configurationReader = apiFilterNode.ReadSubtree(); + configurationReader.MoveToContent(); + apiFilter = new RootFilter(configurationReader); + configurationReader.Close(); + } + + // Attribute filter nodes + XPathNavigator attributeFilterNode = configuration.SelectSingleNode("attributeFilter"); + if (attributeFilterNode != null) { + XmlReader configurationReader = attributeFilterNode.ReadSubtree(); + configurationReader.MoveToContent(); + attributeFilter = new RootFilter(configurationReader); + configurationReader.Close(); + } + + } + +#endregion + +#region Public API + + public virtual bool HasExposedMembers(TypeNode type) + { + if (type == null) throw new ArgumentNullException("type"); + return (apiFilter.HasExposedMembers(type)); + } + + // exposure logic for artibrary APIs + // call the appropriate particular exposure logic + + public virtual bool IsExposedApi(Member api) { + + Namespace space = api as Namespace; + if (space != null) return (IsExposedNamespace(space)); + + TypeNode type = api as TypeNode; + if (type != null) return (IsExposedType(type)); + + return (IsExposedMember(api)); + } + + public virtual bool IsExposedAttribute(AttributeNode attribute) { + if (attribute == null) throw new ArgumentNullException("attribute"); + + // check whether attribte type is exposed + TypeNode attributeType = attribute.Type; + if (!IsExposedType(attributeType)) return (false); + + // check whether expressions used to instantiate attribute are exposed + ExpressionList expressions = attribute.Expressions; + for (int i = 0; i < expressions.Count; i++) { + if (!IsExposedExpression(expressions[i])) return (false); + } + + // apply user filters to attribute + return (attributeFilter.IsExposedType(attributeType)); + } + + public virtual bool IsExposedMember(Member member) { + if (member == null) throw new ArgumentNullException("member"); + return (apiFilter.IsExposedMember(member)); + } + + // namespce logic + // a namespace is exposed if any type in it is exposed + + public virtual bool IsExposedNamespace(Namespace space) { + if (space == null) throw new ArgumentNullException("space"); + string name = space.Name.Name; + + // look in cache to see if namespace exposure is already determined + bool exposed; + if (!namespaceCache.TryGetValue(name, out exposed)) { + // it is not; determine exposure now + + // the namespace is exposed if any types in it are exposed + exposed = NamespaceContainesExposedTypes(space) ?? false; + + // the namespace is also exposed if it contains exposed members, even if all types are hidden + if (!exposed) + { + exposed = NamespaceContainsExposedMembers(space); + } + + // cache the result + namespaceCache.Add(name, exposed); + + } + return (exposed); + } + + // type and member logic + // by default, types and members are exposed if a user filter says it, or if no user filter forbids it + + public virtual bool IsExposedType(TypeNode type) { + if (type == null) throw new ArgumentNullException("type"); + return (apiFilter.IsExposedType(type)); + } + +#endregion + +#region Implementation + + private bool IsExposedExpression(Expression expression) { + if (expression.NodeType == NodeType.Literal) { + Literal literal = (Literal)expression; + TypeNode type = literal.Type; + if (!IsExposedType(type)) return (false); + if (type.FullName == "System.Type") { + // if the value is itself a type, we need to test whether that type is visible + TypeNode value = literal.Value as TypeNode; + if ((value != null) && !IsExposedType(value)) return (false); + } + return (true); + } else if (expression.NodeType == NodeType.NamedArgument) { + NamedArgument assignment = (NamedArgument)expression; + return (IsExposedExpression(assignment.Value)); + } else { + throw new InvalidOperationException("Encountered unrecognized expression"); + } + } + + private bool? NamespaceContainesExposedTypes(Namespace space) + { + TypeNodeList types = space.Types; + + for (int i = 0; i < types.Count; i++) + { + TypeNode type = types[i]; + if (IsExposedType(type)) return (true); + } + + if (apiFilter.NamespaceFilterCount < 1) + { + return null; //this apiFilter does not contain any namespaces + } + + return (false); + } + + + /** <summary>Check for any exposed members in any of the types. + * Returns true if the type has an exposed memeber filter and + * it is matched. This is used to determine if the namespace + * should be visited if the namespace and all types are set to + * false for exposed, we still want to visit them if any members + * are set to true. + * </summary> */ + private bool NamespaceContainsExposedMembers(Namespace space) + { + TypeNodeList types = space.Types; + for (int i = 0; i < types.Count; i++) + { + TypeNode type = types[i]; + + if (HasExposedMembers(type)) return true; + } + return (false); + } + +#endregion + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/ApiNamer.cs b/tools/Sandcastle/Source/Reflection/ApiNamer.cs new file mode 100644 index 0000000..ade218f --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/ApiNamer.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public abstract class ApiNamer { + + public virtual string GetApiName(Member api) { + + Namespace space = api as Namespace; + if (space != null) return (GetNamespaceName(space)); + + TypeNode type = api as TypeNode; + if (type != null) return (GetTypeName(type)); + + return (GetMemberName(api)); + + } + + public abstract string GetMemberName(Member member); + + public abstract string GetNamespaceName(Namespace space); + + public abstract string GetTypeName(TypeNode type); + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/ApiVisitor.cs b/tools/Sandcastle/Source/Reflection/ApiVisitor.cs new file mode 100644 index 0000000..7b5d9af --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/ApiVisitor.cs @@ -0,0 +1,339 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +using System.Compiler; + + +namespace Microsoft.Ddue.Tools.Reflection { + + public class ApiVisitor : IDisposable { + + private static Comparison < Member > memberComparison = new Comparison < Member >(CompareMembers); + + // Sorting logic + + private static Comparison < Namespace > namespaceComparison = new Comparison < Namespace >(CompareMembers); + + private static Comparison < TypeNode > typeComparison = new Comparison < TypeNode >(CompareMembers); + + private List < AssemblyNode > accessoryAssemblies = new List < AssemblyNode >(); + + // Disposal logic + + private List < AssemblyNode > assemblies = new List < AssemblyNode >(); + + // object model store + + private Dictionary < string, Namespace > catalog = new Dictionary < string, Namespace >(); + + private ApiFilter filter; + + // Keep track of any metadata load errors + + private Dictionary < string, Exception > loadErrors = new Dictionary < string, Exception >(); + + // Revised assembly storage + + private AssemblyResolver resolver = new AssemblyResolver(); + + protected ApiVisitor(ApiFilter filter) { + this.filter = filter; + } + + protected ApiVisitor() : this(new NoFilter()) { } + + public AssemblyNode[] AccessoryAssemblies { + get { + return (accessoryAssemblies.ToArray()); + } + } + + public ApiFilter ApiFilter { + get { + return (filter); + } + set { + filter = value; + } + } + + public AssemblyNode[] Assemblies { + get { + return (assemblies.ToArray()); + } + } + + public Dictionary < string, Exception > LoadErrors { + get { + return (loadErrors); + } + } + + public AssemblyResolver Resolver { + get { + return (resolver); + } + set { + resolver = value; + } + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + if (disposing) { + foreach (AssemblyNode assembly in assemblies) { + // Console.WriteLine(loadedModule.Name); + assembly.Dispose(); + } + foreach (AssemblyNode accessoryAssembly in accessoryAssemblies) { + accessoryAssembly.Dispose(); + } + } + } + + public void LoadAccessoryAssemblies(string filePattern) { + + // get the full path to the relevent directory + string directoryPath = Path.GetDirectoryName(filePattern); + if ((directoryPath == null) || (directoryPath.Length == 0)) directoryPath = Environment.CurrentDirectory; + directoryPath = Path.GetFullPath(directoryPath); + + // get the file name, which may contain wildcards + string filePath = Path.GetFileName(filePattern); + + // look up the files and load them + string[] files = Directory.GetFiles(directoryPath, filePath); + foreach (string file in files) { + LoadAccessoryAssembly(file); + } + } + + // Accessory modules + + //private IDictionary cache = new Hashtable(); + + public void LoadAccessoryAssembly(string filePath) { + AssemblyNode assembly = AssemblyNode.GetAssembly(filePath, null, false, false, false, false); // this causes non-classes to register as classes + if (assembly != null) { + if (assembly.Name == "mscorlib") ResetMscorlib(assembly); + + resolver.Add(assembly); + //assembly.AssemblyReferenceResolutionAfterProbingFailed += unresolvedModuleHandler; + accessoryAssemblies.Add(assembly); + } + } + + public void LoadAssemblies(string filePattern) { + + // get the full path to the relevent directory + string directoryPath = Path.GetDirectoryName(filePattern); + if ((directoryPath == null) || (directoryPath.Length == 0)) directoryPath = Environment.CurrentDirectory; + directoryPath = Path.GetFullPath(directoryPath); + + // get the file name, which may contain wildcards + string filePath = Path.GetFileName(filePattern); + + // look up the files and load them + string[] files = Directory.GetFiles(directoryPath, filePath); + foreach (string file in files) { + LoadAssembly(file); + } + + } + + // Parsing logic + + public void LoadAssembly(string filePath) { + //Console.WriteLine("loading {0}", filePath); + //AssemblyNode assembly = AssemblyNode.GetAssembly(filePath); // this causes mscorlib to be missing members + //AssemblyNode assembly = AssemblyNode.GetAssembly(filePath, cache); // this causes compact framework non-classes to register as classes + //AssemblyNode assembly = AssemblyNode.GetAssembly(filePath, cache, false, false, true, false); // this causes missing mscorlib members + //AssemblyNode assembly = AssemblyNode.GetAssembly(filePath, cache, false, false, false, false); // this causes non-classes to register as classes + //AssemblyNode assembly = AssemblyNode.GetAssembly(filePath, null, false, false, true, false); // this causes missing mscorlib members + AssemblyNode assembly = AssemblyNode.GetAssembly(filePath, null, false, false, false, false); // this causes non-classes to register as classes + + if (assembly != null) { + if (assembly.Name == "mscorlib") ResetMscorlib(assembly); + + // Console.WriteLine("assembly = {0}", assembly.Name); + resolver.Add(assembly); + //assembly.AssemblyReferenceResolutionAfterProbingFailed += unresolvedModuleHandler; + assemblies.Add(assembly); + //Console.WriteLine("{0} has {1} types", assembly.Name, assembly.ExportedTypes.Count); + //StoreTypes(assembly.Types); + } + + } + + // Visit Object Model + + public void VisitApis() { + + // store types + // we have to do this after all assemblies are registered because the resolution may not work unless + // all the assemblies we need are in the resolver cache + //Console.WriteLine("storing types"); + foreach (AssemblyNode assembly in assemblies) { + //Console.WriteLine("assembly {0}", assembly.Name); + //Console.WriteLine("has {0} types", assembly.Types.Count); + StoreTypes(assembly.Types); + //Console.WriteLine("done with assembly"); + } + //Console.WriteLine("done storing types"); + + //Console.WriteLine("visiting namespaces"); + NamespaceList spaces = new NamespaceList(); + foreach (Namespace space in catalog.Values) { + if (filter.IsExposedNamespace(space)) spaces.Add(space); + } + VisitNamespaces(spaces); + } + + protected virtual void VisitEntity(Member entity) { + // inherit and insert logic here + } + + protected virtual void VisitMember(Member member) { + VisitEntity(member); + } + + protected virtual void VisitMembers(MemberList members) { + // sort members by name + Member[] sorted_members = new Member[members.Count]; + for (int i = 0; i < members.Count; i++) sorted_members[i] = members[i]; + Array.Sort < Member >(sorted_members, memberComparison); + // visit them + foreach (Member member in sorted_members) { + // don't visit nested types, as they are already visited + if (member is TypeNode) continue; + if (filter.IsExposedMember(member)) + { + VisitMember(member); + } + } + } + + protected virtual void VisitNamespace(Namespace space) { + //Console.WriteLine("Visit Entity {0}",space.FullName); + VisitEntity(space); + TypeNodeList types = space.Types; + VisitTypes(types); + } + + protected virtual void VisitNamespaces(NamespaceList spaces) { + // sort namespaces by name + Namespace[] sorted_spaces = new Namespace[spaces.Count]; + for (int i = 0; i < spaces.Count; i++) sorted_spaces[i] = spaces[i]; + Array.Sort < Namespace >(sorted_spaces, namespaceComparison); + // visit them + foreach (Namespace space in sorted_spaces) { + if (filter.IsExposedNamespace(space)) VisitNamespace(space); + } + } + + protected virtual void VisitType(TypeNode type) { + //Console.WriteLine(type.FullName); + VisitEntity(type); + MemberList members = type.Members; + //Console.WriteLine("{0}: {1}", type.FullName, members.Length); + VisitMembers(members); + } + + protected virtual void VisitTypes(TypeNodeList types) { + // sort types by name + TypeNode[] sorted_types = new TypeNode[types.Count]; + for (int i = 0; i < types.Count; i++) sorted_types[i] = types[i]; + Array.Sort < TypeNode >(sorted_types, typeComparison); + // visit them + foreach (TypeNode type in sorted_types) { + //Console.WriteLine("visiting {0}", type.Name); + //visit this type if it is exposed, or has members that are set as exposed + if (filter.IsExposedType(type) || filter.HasExposedMembers(type)) + VisitType(type); //visit type and members + } + } + + private static int CompareMembers(Member a, Member b) { + return (String.Compare(a.FullName, b.FullName)); + } + + private static int CompareNamespaces(Namespace a, Namespace b) { + return (String.Compare(a.Name.Name, b.Name.Name)); + } + + private static int CompareTypes(TypeNode a, TypeNode b) { + return (String.Compare(a.Name.Name, b.Name.Name)); + } + + private static string GetNamespaceName(TypeNode type) { + TypeNode parent = type.DeclaringType; + if (parent != null) { + return (GetNamespaceName(parent)); + } else { + if (type.Namespace == null) { + return (String.Empty); + } else { + return (type.Namespace.Name); + } + } + } + + private void ResetMscorlib(AssemblyNode assembly) { + TargetPlatform.Clear(); + CoreSystemTypes.Clear(); + CoreSystemTypes.SystemAssembly = assembly; + CoreSystemTypes.Initialize(true, false); + } + + private void StoreType(TypeNode type) { + //Console.WriteLine("type: {0} ", type.Name); + /* + if (type.Name == null) { + // CCI seems to occasionally construct corrupted, phantom types, which we should reject + // Console.WriteLine("unidentified type rejected"); + return; + } + */ + string spaceName = GetNamespaceName(type); + //Console.WriteLine("in space: {0}", spaceName); + Namespace space; + if (!catalog.TryGetValue(spaceName, out space)) { + space = new Namespace(new Identifier(spaceName)); + catalog.Add(spaceName, space); + } + if (space.Types == null) Console.WriteLine("null type list"); + space.Types.Add(type); + + //Console.WriteLine("getting members"); + MemberList members = type.Members; + //Console.WriteLine("got {0} members", members.Count); + for (int i = 0; i < members.Count; i++) { + TypeNode nestedType = members[i] as TypeNode; + if (nestedType != null) { + //Console.WriteLine("nested type {0}", type.FullName); + StoreType(nestedType); + } + } + //Console.WriteLine("done storing type"); + } + + private void StoreTypes(TypeNodeList types) { + //Console.WriteLine("{0} types", types.Length); + for (int i = 0; i < types.Count; i++) { + StoreType(types[i]); + } + } + + } + + +} diff --git a/tools/Sandcastle/Source/Reflection/AssemblyReferenceEventArgs.cs b/tools/Sandcastle/Source/Reflection/AssemblyReferenceEventArgs.cs new file mode 100644 index 0000000..feaa117 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/AssemblyReferenceEventArgs.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class AssemblyReferenceEventArgs : EventArgs { + + private Module module; + + private AssemblyReference reference; + + public AssemblyReferenceEventArgs(AssemblyReference reference, Module module) { + this.reference = reference; + this.module = module; + } + + public AssemblyReference Reference { + get { + return (reference); + } + } + + public Module Referrer { + get { + return (module); + } + } + } + +} diff --git a/tools/Sandcastle/Source/Reflection/AssemblyResolver.cs b/tools/Sandcastle/Source/Reflection/AssemblyResolver.cs new file mode 100644 index 0000000..116dcc8 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/AssemblyResolver.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class AssemblyResolver { + + private Dictionary < string, AssemblyNode > cache = new Dictionary < string, AssemblyNode >(); + + private bool useGac = true; + + public AssemblyResolver() { + + } + + public AssemblyResolver(XPathNavigator configuration) { + + string useGacValue = configuration.GetAttribute("use-gac", String.Empty); + if (!String.IsNullOrEmpty(useGacValue)) useGac = Convert.ToBoolean(useGacValue); + + } + + public event EventHandler < AssemblyReferenceEventArgs > UnresolvedAssemblyReference; + + public bool UseGac { + get { + return (useGac); + } + set { + useGac = value; + } + } + + public virtual void Add(AssemblyNode assembly) { + if (assembly == null) throw new ArgumentNullException("assembly"); + string name = assembly.StrongName; + assembly.AssemblyReferenceResolution += new Module.AssemblyReferenceResolver(ResolveReference); + assembly.AssemblyReferenceResolutionAfterProbingFailed += new Module.AssemblyReferenceResolver(UnresolvedReference); + cache[name] = assembly; + //Console.WriteLine("added {0}; cache now contains {1}", name, cache.Count); + } + + public virtual AssemblyNode ResolveReference(AssemblyReference reference, Module module) { + + if (reference == null) throw new ArgumentNullException("reference"); + + //Console.WriteLine("resolving {0}", reference.StrongName); + + // try to get it from the cache + string name = reference.StrongName; + if (cache.ContainsKey(name)) return (cache[name]); + + // try to get it from the gac + if (useGac) { + string location = GlobalAssemblyCache.GetLocation(reference); + if (location != null) { + AssemblyNode assembly = AssemblyNode.GetAssembly(location, null, false, false, false, false); + if (assembly != null) { + Add(assembly); + return (assembly); + } + } + } + + // couldn't find it; return null + // Console.WriteLine("returning null on request for {0}", reference.StrongName); + //OnUnresolvedAssemblyReference(reference, module); + return (null); + + } + + protected virtual void OnUnresolvedAssemblyReference(AssemblyReference reference, Module module) { + if (UnresolvedAssemblyReference != null) UnresolvedAssemblyReference(this, new AssemblyReferenceEventArgs(reference, module)); + } + + private AssemblyNode UnresolvedReference(AssemblyReference reference, Module module) { + OnUnresolvedAssemblyReference(reference, module); + return (null); + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/ExternalDocumentedFilter.cs b/tools/Sandcastle/Source/Reflection/ExternalDocumentedFilter.cs new file mode 100644 index 0000000..e7f0cf7 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/ExternalDocumentedFilter.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Xml.XPath; +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + // exposes all apis for which documentation is written + + // this includes all visible members except for property and event accessors (e.g. get_ methods) and delegate members (e.g. Invoke). + + // enumeration members are included + + public class ExternalDocumentedFilter : ApiFilter { + + public ExternalDocumentedFilter() : base() { } + + public ExternalDocumentedFilter(XPathNavigator configuration) : base(configuration) { } + + public override bool IsExposedMember(Member member) { + if (member == null) throw new ArgumentNullException("member"); + // if the member isn't visible, we certainly won't expose it... + if (!member.IsVisibleOutsideAssembly) return (false); + // ...but there are also some visible members we won't expose. + TypeNode type = member.DeclaringType; + // member of delegates are not exposed + if (type.NodeType == NodeType.DelegateNode) return (false); + // accessor methods for properties and events are not exposed + if (member.IsSpecialName && (member.NodeType == NodeType.Method)) { + string name = member.Name.Name; + if (NameContains(name, "get_")) return (false); + if (NameContains(name, "set_")) return (false); + if (NameContains(name, "add_")) return (false); + if (NameContains(name, "remove_")) return (false); + if (NameContains(name, "raise_")) return (false); + } + + // the value field of enumerations is not exposed + if (member.IsSpecialName && (type.NodeType == NodeType.EnumNode) && (member.NodeType == NodeType.Field)) { + string name = member.Name.Name; + if (name == "value__") return (false); + } + + // protected members of sealed types are not exposed + // change of plan -- yes they are + // if (type.IsSealed && (member.IsFamily || member.IsFamilyOrAssembly)) return(false); + + // One more test to deal with a case: a private method is an explicit implementation for + // a property accessor, but is not marked with the special name flag. To find these, test for + // the accessibility of the methods they implement + if (member.IsPrivate && member.NodeType == NodeType.Method) { + Method method = (Method)member; + MethodList implements = method.ImplementedInterfaceMethods; + if ((implements.Count > 0) && (!IsExposedMember(implements[0]))) return (false); + } + + // okay, passed all tests, the member is exposed as long as the filters allow it + return (base.IsExposedMember(member)); + } + + // we are satistied with the default namespace expose test, so don't override it + + public override bool IsExposedType(TypeNode type) { + if (type == null) throw new ArgumentNullException("type"); + // expose any visible types allowed by the base filter + if (type.IsVisibleOutsideAssembly) { + return (base.IsExposedType(type)); + } else { + return (false); + } + // return(type.IsVisibleOutsideAssembly); + } + + private static bool NameContains(string name, string substring) { + return (name.Contains(substring)); + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/ExternalFilter.cs b/tools/Sandcastle/Source/Reflection/ExternalFilter.cs new file mode 100644 index 0000000..ff99bef --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/ExternalFilter.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + // exposes all APIs that are visible from outside the assembly + + // this includes property and event accessors (e.g. get_), delegate methods (e.g. Invoke), as well as enumeration members + + public class ExternalFilter : ApiFilter { + + public override bool IsExposedMember(Member member) { + if (member == null) throw new ArgumentNullException("member"); + // expose all visible members + return (member.IsVisibleOutsideAssembly); + } + + // we are satistied with the default namespace expose test, so don't override it + + public override bool IsExposedType(TypeNode type) { + if (type == null) throw new ArgumentNullException("type"); + // expose all visible types + return (type.IsVisibleOutsideAssembly); + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/ExternalTopicFilter.cs b/tools/Sandcastle/Source/Reflection/ExternalTopicFilter.cs new file mode 100644 index 0000000..ed6eae7 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/ExternalTopicFilter.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + // exposes all apis for which a topic exists + // this includes all documented members (see DocumentFilter) except for enumeration members + + public class ExternalTopicFilter : ExternalDocumentedFilter { + + public override bool IsExposedMember(Member member) { + // don't expose members of enumerations + if (member.DeclaringType.NodeType == NodeType.EnumNode) return (false); + // otherwise, agree with DocumentedFilter + return (base.IsExposedMember(member)); + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/GlobalSuppressions.cs b/tools/Sandcastle/Source/Reflection/GlobalSuppressions.cs new file mode 100644 index 0000000..aff5272 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/GlobalSuppressions.cs @@ -0,0 +1,55 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click +// "In Project Suppression File". +// You do not need to add suppressions to this file manually. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ddue", Scope = "namespace", Target = "Microsoft.Ddue.Tools.Reflection")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Namer", Scope = "type", Target = "Microsoft.Ddue.Tools.Reflection.ApiNamer")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1307:SpecifyStringComparison", MessageId = "System.String.Compare(System.String,System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#CompareMembers(System.Compiler.Member,System.Compiler.Member)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1307:SpecifyStringComparison", MessageId = "System.String.Compare(System.String,System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#CompareNamespaces(System.Compiler.Namespace,System.Compiler.Namespace)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1307:SpecifyStringComparison", MessageId = "System.String.Compare(System.String,System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#CompareTypes(System.Compiler.TypeNode,System.Compiler.TypeNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#CompareNamespaces(System.Compiler.Namespace,System.Compiler.Namespace)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#CompareTypes(System.Compiler.TypeNode,System.Compiler.TypeNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#ResetMscorlib(System.Compiler.AssemblyNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#Assemblies")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ApiVisitor.#AccessoryAssemblies")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer.#GetName(System.Compiler.Member)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ReflectionUtilities.#TypeMatch(System.Compiler.TypeNode,System.Compiler.TypeNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#GetName(System.Compiler.Member)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "argument", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer.#WriteType(System.Compiler.TypeNode,System.IO.TextWriter)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "arguments", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ReflectionUtilities.#GetTemplateType(System.Compiler.TypeNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "argument", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#WriteType(System.Compiler.TypeNode,System.IO.TextWriter)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer.#WriteType(System.Compiler.TypeNode,System.IO.TextWriter)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#WriteType(System.Compiler.TypeNode,System.IO.TextWriter)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Module", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.AssemblyResolver.#ResolveReference(System.Compiler.AssemblyReference,System.Compiler.Module)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Module", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.AssemblyResolver.#OnUnresolvedAssemblyReference(System.Compiler.AssemblyReference,System.Compiler.Module)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Namer", Scope = "type", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Namer", Scope = "type", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gac", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.AssemblyResolver.#UseGac")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Convert.ToBoolean(System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.AssemblyResolver.#.ctor(System.Xml.XPath.XPathNavigator)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Convert.ToBoolean(System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.MemberFilter.#.ctor(System.Xml.XmlReader)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Convert.ToBoolean(System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.NamespaceFilter.#.ctor(System.Xml.XmlReader)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Convert.ToBoolean(System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.RootFilter.#.ctor(System.Xml.XmlReader)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Convert.ToBoolean(System.String)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.TypeFilter.#.ctor(System.Xml.XmlReader)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.ReflectionUtilities.#GetTemplateType(System.Compiler.TypeNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#GetMemberName(System.Compiler.Member)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#GetName(System.Compiler.Member)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#GetNamespaceName(System.Compiler.Namespace)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#GetTypeName(System.Compiler.TypeNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#WriteEvent(System.Compiler.Event,System.IO.TextWriter)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#WriteMethod(System.Compiler.Method,System.IO.TextWriter)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.OrcasNamer.#WriteProperty(System.Compiler.Property,System.IO.TextWriter)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer.#GetMemberName(System.Compiler.Member)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer.#GetName(System.Compiler.Member)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer.#GetNamespaceName(System.Compiler.Namespace)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.WhidbeyNamer.#GetTypeName(System.Compiler.TypeNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.RootFilter.#NamespaceFilters")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.NamespaceFilter.#TypeFilters")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.NamespaceFilter.#IsExposedNamespace(System.Compiler.Namespace)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Scope = "member", Target = "Microsoft.Ddue.Tools.Reflection.TypeFilter.#IsExposedType(System.Compiler.TypeNode)")] diff --git a/tools/Sandcastle/Source/Reflection/MemberFilter.cs b/tools/Sandcastle/Source/Reflection/MemberFilter.cs new file mode 100644 index 0000000..051d589 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/MemberFilter.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class MemberFilter + { + +#region Member Variables + + private bool exposed; + + private string name; + +#endregion + +#region Constructors + public MemberFilter(string name, bool exposed) { + if (name == null) throw new ArgumentNullException("name"); + this.name = name; + this.exposed = exposed; + } + + public MemberFilter(XmlReader configuration) { + if ((configuration.NodeType != XmlNodeType.Element) || (configuration.Name != "member")) throw new InvalidOperationException(); + name = configuration.GetAttribute("name"); + exposed = Convert.ToBoolean(configuration.GetAttribute("expose")); + } +#endregion + +#region Public API + public bool? IsExposedMember(Member member) { + if (member.Name.Name == name) { + return (exposed); + } else { + return (null); + } + } +#endregion + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/NamespaceFilter.cs b/tools/Sandcastle/Source/Reflection/NamespaceFilter.cs new file mode 100644 index 0000000..9a8956c --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/NamespaceFilter.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class NamespaceFilter + { + +#region Member Variables + + private bool exposed; + private string name; + + private List < TypeFilter > typeFilters = new List < TypeFilter >(); +#endregion + +#region Constructors + public NamespaceFilter(string name, bool exposed) { + this.name = name; + this.exposed = exposed; + } + + public NamespaceFilter(XmlReader configuration) { + if (configuration.Name != "namespace") throw new InvalidOperationException(); + name = configuration.GetAttribute("name"); + exposed = Convert.ToBoolean(configuration.GetAttribute("expose")); + XmlReader subtree = configuration.ReadSubtree(); + while (subtree.Read()) { + if ((subtree.NodeType == XmlNodeType.Element) && (subtree.Name == "type")) { + TypeFilter typeFilter = new TypeFilter(subtree); + typeFilters.Add(typeFilter); + } + } + subtree.Close(); + } +#endregion + +#region Public API + + /// <summary> + /// Gets the number of type filters + /// </summary> + public int TypeFilterCount + { + get + { + return typeFilters.Count; + } + } + + public List < TypeFilter > TypeFilters { + get { + return (typeFilters); + { + } + } + } + + + //Find out if any are exposed incase this class is not exposed + public bool HasExposedMembers(TypeNode type) + { + Namespace space = ReflectionUtilities.GetNamespace(type); + if (IsExposedNamespace(space) != null) + { + foreach (TypeFilter typeFilter in typeFilters) + { + bool? result = typeFilter.IsExposedType(type); + if (result != null) //matched + { + return typeFilter.HasExposedMembers(type); + } + } + } + + return false; + } + + public bool? IsExposedMember(Member member) { + //Console.WriteLine("DEBUG: namespaceFilter.isExposedMemeber"); + TypeNode type = ReflectionUtilities.GetTemplateType(member.DeclaringType); + Namespace space = ReflectionUtilities.GetNamespace(type); + if (IsExposedNamespace(space) != null) { + foreach (TypeFilter typeFilter in typeFilters) { + bool? result = typeFilter.IsExposedMember(member); + if (result != null) return (result); + } + + //no filters matched this method, check if the type is exposed + bool? typeIsExposed = IsExposedType(type); + if (typeIsExposed != null) return typeIsExposed; + + return (exposed); //if the namespace is exposed + } else { + return (null); + } + } + + public bool? IsExposedNamespace(Namespace space) { + if (space.Name.Name == name) { + return (exposed); + } else { + return (null); + } + } + + public bool? IsExposedType(TypeNode type) { + Namespace space = ReflectionUtilities.GetNamespace(type); + if (IsExposedNamespace(space) != null) { + foreach (TypeFilter typeFilter in typeFilters) { + bool? result = typeFilter.IsExposedType(type); + if (result != null) return (result); + } + + //no filter matches for this type, check the parents since it could be nested + TypeNode parent = type.DeclaringType; + while (parent != null) + { + bool? parentExposed = IsExposedType(parent); + + if (parentExposed != null) + return parentExposed; + + parent = type.DeclaringType; + } + + //no answer for the parents either, the top parent should pass this back above + return (exposed); + } else { + return (null); + } + } + +#endregion + } +} diff --git a/tools/Sandcastle/Source/Reflection/NoFilter.cs b/tools/Sandcastle/Source/Reflection/NoFilter.cs new file mode 100644 index 0000000..0e7b7f5 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/NoFilter.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class NoFilter : ApiFilter { + + public override bool IsExposedApi(Member api) { + return (true); + } + + public override bool IsExposedMember(Member member) { + return (true); + } + + public override bool IsExposedNamespace(Namespace space) { + return (true); + } + + public override bool IsExposedType(TypeNode type) { + return (true); + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/Notes.txt b/tools/Sandcastle/Source/Reflection/Notes.txt new file mode 100644 index 0000000..e5ab15c --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/Notes.txt @@ -0,0 +1,215 @@ +Entity + Name : string + Visiblity : + +Namespace : Entity + Types : Type[] + +Type : Entity + Namespace : Namespace + Interfaces : Interface[] + Members : Member[] + TemplateArguments : Type[] + CustomAttributes : Attribute[] + SecurityAttributes : + ContainingType : Type + Assembly + +Class : Type + BaseClass : Class + DerivedClasses : Class[] + IsAbstract : bool + IsSealed : bool + +Structure : Type + +Interface : Type + +Delegate : Type + Parameters : Parameter[] + ReturnType : Type + +Enumeration : Type + UnderlyingType : Type + +TemplateParameter : Type + IsValueType : bool + +TypeArray : Type + Rank : int + UnderlyingType : Type + +TypePointer : Type + UnderlyingType : Type + +TypeReference : Type + UnderlyingType : Type + +Member : Entity + IsStatic : bool + IsSpecialName : bool + DeclaringType : Type + Overrides : Member + Attributes : Attribute[] + +Field : Member + Type : Type + IsLiteral : bool + IsInitOnly : bool + IsVolitile : bool + +Method : Member + Parameters : Parameter[] + ReturnType : Type + IsAbstract : bool + IsVirtual : bool + IsFinal : bool + IsExtern : bool + Implements : Member[] + +Constructor : Member + Parameters : Parameter[] + +Property : Member + Parameters : Parameter[] + Type : Type + IsAbstract : bool + IsVirtual : bool + IsFinal : bool + Implements : Property[] + Getter : Method + Setter : Method + +Event : Member + Handler : Delegate + IsAbstract : bool + IsVirtual : bool + IsFinal : bool + Implements : Event[] + Adder: Method + Remover : Method + Raiser : Method + +Parameter + Name : string + Type : Type + CustomAttributes : Attribute[] + +Attribute + Type : Type + Constructor : Constructor + Arguments : Object[] + + +Namespace: + <apidata name="System.Web" group="namespace" /> + <elements /> + +Type: + <apidata name="System.String" group="type" subgroup="class" /> + <typedata visibility="public" abstract="false" sealed="false" serializable="true" /> + <containers namespace="N:System" assembly="mscorlib" /> + <implements /> + <elements /> + <attributes /> + +Class: + <classdata parent="T:System.Object" /> + +Delegate: + <parameters /> + <value /> + +Enumeration: + <enumdata base /> + +Member: + <apidata name="System.String.Length" group="member" subgroup="property" /> + <memberdata visibility="public" static="false" special="false" /> + <proceduredata abstract="false" virtual="false" final="false" /> + <propertydata getter="M:System.String.get_Length" getter-visibility setter setter-visibility /> + <containers namespace="N:System" type="T:System.String" assembly="mscorlib" /> + +Field: + <fielddata literal initonly volative serialized /> + <value /> + +Method: + <templates /> + <parameters /> + <value /> + +Event: + <eventdata handler adder remover args /> + + + +Namespace: +<entityProperties name="System.Web" group="namespace" /> + +Class: +<entityProperties name="Console" visibility="public" group="type" subgroup="class" /> +<typeProperties namespace="N:System" abstract="true" sealed="true" /> + +Method: +<entityProperties name="AsReadOnly" visiblity="public" group="member" subgroup="method" /> +<memberProperties type="T:System.Array" static="true" special="false" abstract="false" virtual="false" final="true" /> + + +entityProperties + name + group + subgroup + subsubgroup + +typeProperties + namespace + assembly + abstract + sealed + parent + container + +memberProperties + type + static + special + literal + initonly + volitile + abstract + virtual + final + extern + + +<entityInfo name="Boo" visiblity="protected" group="type" /> +<typeInfo namespace="N:Foo" container="T:Foo.Barrel" assembly="mscorlib.dll" subgroup="class" /> +<classInfo base="T:System.Object" abstract="false" sealed="false" /> +<templates> + <template name="TKey" /> + <template name="TValue" /> +</templates> +<interfaces> + <interface type="T:Foo.IBoo" /> +</interfaces> +<attributes> + <attribute type="T:Foo.MyAttribute" constructor="T:Foo.MyAttribute.#ctor(System.Boolean)" /> + <parameter name="visible" type="T:System.Boolean" value="true" /> + </attribute> +</attributes> +<members> + <member member="M:Foo.Boo.#ctor" /> + <member member="P:Foo.Boo.Value" /> +</members> + +<entity name="Moo" visibility="protected" group="method" /> +<memberInfo type="T:Foo.Boo" static="false" special="false" subgroup="method" /> +<methodnfo abstract="false" virtual="true" /> +<parameters> + <parameter name="x" type="T:`0" /> +<parameters> +<value type="T:System.Int32" /> +<implements> + <implement type="T:Foo.IBoo" member="M:Foo.IBoo.Choo" /> +</implements> diff --git a/tools/Sandcastle/Source/Reflection/OrcasNamer.cs b/tools/Sandcastle/Source/Reflection/OrcasNamer.cs new file mode 100644 index 0000000..806c7a2 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/OrcasNamer.cs @@ -0,0 +1,376 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +// using System.Collections.Generic; +using System.IO; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class OrcasNamer : ApiNamer { + + public override string GetMemberName(Member member) { + + using (TextWriter writer = new StringWriter()) { + + switch (member.NodeType) { + case NodeType.Field: + writer.Write("F:"); + WriteField((Field)member, writer); + break; + case NodeType.Property: + writer.Write("P:"); + WriteProperty((Property)member, writer); + break; + case NodeType.Method: + writer.Write("M:"); + WriteMethod((Method)member, writer); + break; + case NodeType.InstanceInitializer: + writer.Write("M:"); + WriteConstructor((InstanceInitializer)member, writer); + break; + case NodeType.StaticInitializer: + writer.Write("M:"); + WriteStaticConstructor((StaticInitializer)member, writer); + break; + case NodeType.Event: + writer.Write("E:"); + WriteEvent((Event)member, writer); + break; + } + + return (writer.ToString()); + + } + + } + + public override string GetNamespaceName(Namespace space) { + using (TextWriter writer = new StringWriter()) { + writer.Write("N:"); + WriteNamespace(space, writer); + return (writer.ToString()); + } + } + + public override string GetTypeName(TypeNode type) { + using (TextWriter writer = new StringWriter()) { + writer.Write("T:"); + WriteType(type, writer); + return (writer.ToString()); + } + } + + + private static string GetName(Member entity) { + + using (TextWriter writer = new StringWriter()) { + + TypeNode type = entity as TypeNode; + if (type != null) { + writer.Write("T:"); + WriteType(type, writer); + return (writer.ToString()); + } + + switch (entity.NodeType) { + case NodeType.Namespace: + writer.Write("N:"); + WriteNamespace(entity as Namespace, writer); + break; + case NodeType.Field: + writer.Write("F:"); + WriteField(entity as Field, writer); + break; + case NodeType.Property: + writer.Write("P:"); + WriteProperty(entity as Property, writer); + break; + case NodeType.Method: + writer.Write("M:"); + WriteMethod(entity as Method, writer); + break; + case NodeType.InstanceInitializer: + writer.Write("M:"); + WriteConstructor(entity as InstanceInitializer, writer); + break; + case NodeType.StaticInitializer: + writer.Write("M:"); + WriteStaticConstructor(entity as StaticInitializer, writer); + break; + case NodeType.Event: + writer.Write("E:"); + WriteEvent(entity as Event, writer); + break; + } + + return (writer.ToString()); + + } + + } + + private static void WriteConstructor(InstanceInitializer constructor, TextWriter writer) { + WriteType(constructor.DeclaringType, writer); + writer.Write(".#ctor"); + WriteParameters(constructor.Parameters, writer); + } + + private static void WriteEvent(Event trigger, TextWriter writer) { + WriteType(trigger.DeclaringType, writer); + + Event eiiTrigger = null; + if (trigger.IsPrivate && trigger.IsVirtual) { + Event[] eiiTriggers = ReflectionUtilities.GetImplementedEvents(trigger); + if (eiiTriggers.Length > 0) eiiTrigger = eiiTriggers[0]; + } + + if (eiiTrigger != null) { + TypeNode eiiType = eiiTrigger.DeclaringType; + TextWriter eiiWriter = new StringWriter(); + + if (eiiType != null && eiiType.Template != null) + { + writer.Write("."); + WriteTemplate(eiiType, writer); + } + else + { + WriteType(eiiType, eiiWriter); + writer.Write("."); + writer.Write(eiiWriter.ToString().Replace('.', '#')); + } + + writer.Write("#"); + writer.Write(eiiTrigger.Name.Name); + } else { + writer.Write(".{0}", trigger.Name.Name); + } + } + + private static void WriteField(Field field, TextWriter writer) { + WriteType(field.DeclaringType, writer); + writer.Write(".{0}", field.Name.Name); + } + + private static void WriteMethod(Method method, TextWriter writer) { + string name = method.Name.Name; + WriteType(method.DeclaringType, writer); + + Method eiiMethod = null; + if (method.IsPrivate && method.IsVirtual) { + MethodList eiiMethods = method.ImplementedInterfaceMethods; + if (eiiMethods.Count > 0) eiiMethod = eiiMethods[0]; + } + if (eiiMethod != null) { //explicitly implemented interface + TypeNode eiiType = eiiMethod.DeclaringType; + TextWriter eiiWriter = new StringWriter(); + + + //we need to keep the param names instead of turning them into numbers + //get the template to the right format + if (eiiType != null && eiiType.Template != null) + { + writer.Write("."); + WriteTemplate(eiiType, writer); + } + else //revert back to writing the type the old way if there is no template + { + WriteType(eiiType, eiiWriter); + writer.Write("."); + writer.Write(eiiWriter.ToString().Replace('.', '#')); + } + + writer.Write("#"); + writer.Write(eiiMethod.Name.Name); + } else { + writer.Write(".{0}", name); + } + if (method.IsGeneric) { + TypeNodeList genericParameters = method.TemplateParameters; + if (genericParameters != null) { + writer.Write("``{0}", genericParameters.Count); + } + } + WriteParameters(method.Parameters, writer); + // add ~ for conversion operators + if ((name == "op_Implicit") || (name == "op_Explicit")) { + writer.Write("~"); + WriteType(method.ReturnType, writer); + } + + } + + // The actual logic to construct names + + private static void WriteNamespace(Namespace space, TextWriter writer) { + writer.Write(space.Name); + } + + private static void WriteParameters(ParameterList parameters, TextWriter writer) { + if ((parameters == null) || (parameters.Count == 0)) return; + writer.Write("("); + for (int i = 0; i < parameters.Count; i++) { + if (i > 0) writer.Write(","); + WriteType(parameters[i].Type, writer); + } + writer.Write(")"); + } + + private static void WriteProperty(Property property, TextWriter writer) { + WriteType(property.DeclaringType, writer); + //Console.WriteLine( "{0}::{1}", property.DeclaringType.FullName, property.Name ); + + Property eiiProperty = null; + if (property.IsPrivate && property.IsVirtual) { + Property[] eiiProperties = ReflectionUtilities.GetImplementedProperties(property); + if (eiiProperties.Length > 0) eiiProperty = eiiProperties[0]; + } + + + + if (eiiProperty != null) { + TypeNode eiiType = eiiProperty.DeclaringType; + TextWriter eiiWriter = new StringWriter(); + + + if (eiiType != null && eiiType.Template != null) + { + writer.Write("."); + WriteTemplate(eiiType, writer); + } + else + { + WriteType(eiiType, eiiWriter); + writer.Write("."); + writer.Write(eiiWriter.ToString().Replace('.', '#')); + } + + writer.Write("#"); + writer.Write(eiiProperty.Name.Name); + } else { + writer.Write(".{0}", property.Name.Name); + } + ParameterList parameters = property.Parameters; + WriteParameters(parameters, writer); + } + + private static void WriteStaticConstructor(StaticInitializer constructor, TextWriter writer) { + WriteType(constructor.DeclaringType, writer); + writer.Write(".#cctor"); + WriteParameters(constructor.Parameters, writer); + } + + /// <summary> + /// Used for explicitly implemented interfaces to convert the template to the + /// format used in the comments file. + /// </summary> + /// <param name="type">EII Type</param> + /// <param name="writer"></param> + private static void WriteTemplate(TypeNode eiiType, TextWriter writer) + { + string eiiClean = eiiType.Template.ToString(); + eiiClean = eiiClean.Replace('.', '#'); + eiiClean = eiiClean.Replace(',', '@'); //change the seperator between params + eiiClean = eiiClean.Replace('<', '{'); //change the parameter brackets + eiiClean = eiiClean.Replace('>', '}'); + writer.Write(eiiClean); + } + + private static void WriteType(TypeNode type, TextWriter writer) { + switch (type.NodeType) { + case NodeType.ArrayType: + ArrayType array = type as ArrayType; + WriteType(array.ElementType, writer); + writer.Write("["); + if (array.Rank > 1) { + for (int i = 0; i < array.Rank; i++) { + if (i > 0) writer.Write(","); + writer.Write("0:"); + } + } + writer.Write("]"); + break; + case NodeType.Reference: + Reference reference = type as Reference; + TypeNode referencedType = reference.ElementType; + WriteType(referencedType, writer); + writer.Write("@"); + break; + case NodeType.Pointer: + Pointer pointer = type as Pointer; + WriteType(pointer.ElementType, writer); + writer.Write("*"); + break; + case NodeType.OptionalModifier: + TypeModifier optionalModifierClause = type as TypeModifier; + WriteType(optionalModifierClause.ModifiedType, writer); + writer.Write("!"); + WriteType(optionalModifierClause.Modifier, writer); + break; + case NodeType.RequiredModifier: + TypeModifier requiredModifierClause = type as TypeModifier; + WriteType(requiredModifierClause.ModifiedType, writer); + writer.Write("|"); + WriteType(requiredModifierClause.Modifier, writer); + break; + default: + if (type.IsTemplateParameter) { + ITypeParameter gtp = (ITypeParameter)type; + if (gtp.DeclaringMember is TypeNode) { + writer.Write("`"); + } else if (gtp.DeclaringMember is Method) { + writer.Write("``"); + } else { + throw new InvalidOperationException("Generic parameter not on type or method."); + } + writer.Write(gtp.ParameterListIndex); + } else { + // namespace + TypeNode declaringType = type.DeclaringType; + if (declaringType != null) { + // names of nested types begin with outer type name + WriteType(declaringType, writer); + writer.Write("."); + } else { + // otherwise just prepend the namespace + Identifier space = type.Namespace; + if ((space != null) && !String.IsNullOrEmpty(space.Name)) { + //string space = type.Namespace.Name; + //if (space != null && space.Length > 0) { + writer.Write(space.Name); + writer.Write("."); + } + } + // name + writer.Write(type.GetUnmangledNameWithoutTypeParameters()); + // generic parameters + if (type.IsGeneric) { + // number of parameters + TypeNodeList parameters = type.TemplateParameters; + if (parameters != null) { + writer.Write("`{0}", parameters.Count); + } + // arguments + TypeNodeList arguments = type.TemplateArguments; + if ((arguments != null) && (arguments.Count > 0)) { + writer.Write("{"); + for (int i = 0; i < arguments.Count; i++) { + TypeNode argument = arguments[i]; + if (i > 0) writer.Write(","); + WriteType(arguments[i], writer); + } + writer.Write("}"); + } + } + } + break; + } + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/Properties/AssemblyInfo.cs b/tools/Sandcastle/Source/Reflection/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a18dd70 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Reflection")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Reflection")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: System.CLSCompliant(false)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("649c6153-4fdf-419d-96d3-236bae5d5749")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("2.5.10626.00")] +[assembly: AssemblyFileVersion("2.5.10626.00")] diff --git a/tools/Sandcastle/Source/Reflection/Reflection.cs b/tools/Sandcastle/Source/Reflection/Reflection.cs new file mode 100644 index 0000000..e646cc6 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/Reflection.cs @@ -0,0 +1,298 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public static class ReflectionUtilities { + + public static Event[] GetImplementedEvents(Event trigger) { + List < Event > list = new List < Event >(); + + // get the adder + Method adder = trigger.HandlerAdder; + + // get interface methods corresponding to this adder + Method[] implementedAdders = GetImplementedMethods(adder); + + // get the events corresponding to the implemented adders + foreach (Method implementedAdder in implementedAdders) { + Event implementedTrigger = GetEventFromAdder(implementedAdder); + if (implementedTrigger != null) list.Add(implementedTrigger); + } + + return (list.ToArray()); + + } + + public static Method[] GetImplementedMethods(Method method) { + List < Method > list = new List < Method >(); + + // Explicit implementations + MethodList explicitImplementations = method.ImplementedInterfaceMethods; + if (explicitImplementations != null) { + for (int i = 0; i < explicitImplementations.Count; i++) { + Method explicitImplementation = explicitImplementations[i]; + list.Add(explicitImplementation); + } + } + + // Implicit implementations + MethodList implicitImplementations = method.ImplicitlyImplementedInterfaceMethods; + if (implicitImplementations != null) { + for (int i = 0; i < implicitImplementations.Count; i++) { + Method implicitImplementation = implicitImplementations[i]; + list.Add(implicitImplementation); + } + } + + return (list.ToArray()); + } + + public static Property[] GetImplementedProperties(Property property) { + List < Property > list = new List < Property >(); + + // get an accessor + Method accessor = property.Getter; + if (accessor == null) accessor = property.Setter; + if (accessor == null) return (new Property[0]); + + // get the interface methods corresponding to this accessor + Method[] methods = GetImplementedMethods(accessor); + + // look for properties corresponding to these methods + for (int i = 0; i < methods.Length; i++) { + Method method = methods[i]; + Property entry = GetPropertyFromAccessor(method); + if (entry != null) list.Add(entry); + } + + return (list.ToArray()); + } + + public static Namespace GetNamespace(TypeNode type) { + if (type.DeclaringType != null) { + return (GetNamespace(type.DeclaringType)); + } else { + return (new Namespace(type.Namespace)); + } + } + + public static Member GetTemplateMember(Member member) { + + if (member == null) throw new ArgumentNullException("member"); + + // if the containing type isn't generic, the member is the template member + TypeNode type = member.DeclaringType; + if (!type.IsGeneric) return (member); + + // if the containing type isn't specialized, the member is the template member + if (!IsSpecialized(type)) return (member); + + // get the template type, and look for members with the same name + TypeNode template = ReflectionUtilities.GetTemplateType(member.DeclaringType); + Identifier name = member.Name; + MemberList candidates = template.GetMembersNamed(name); + + // if no candidates, say so (this shouldn't happen) + if (candidates.Count == 0) throw new InvalidOperationException("No members in the template had the name found in the specialization. This is not possible, but apparently it happened."); + + // if only one candidate, return it + if (candidates.Count == 1) return (candidates[0]); + + // multiple candidates, so now we need to compare parameters + ParameterList parameters = GetParameters(member); + + for (int i = 0; i < candidates.Count; i++) { + Member candidate = candidates[i]; + + // candidate must be same kind of node + if (candidate.NodeType != member.NodeType) continue; + + // if parameters match, this is the one + if (ParametersMatch(parameters, GetParameters(candidate))) return (candidate); + + } + + Console.WriteLine(member.DeclaringType.FullName); + Console.WriteLine(member.FullName); + throw new InvalidOperationException("No members in the template matched the parameters of the specialization. This is not possible."); + } + + public static TypeNode GetTemplateType(TypeNode type) { + + if (type == null) throw new ArgumentNullException("type"); + // Console.WriteLine(type.FullName); + + // only generic types have templates + if (!type.IsGeneric) return (type); + + if (type.DeclaringType == null) { + // if the type is not nested, life is simpler + + // if the type is not specified, the type is the template + if (type.TemplateArguments == null) return (type); + + // otherwise, construct the template type identifier and use it to fetch the template type + Module templateModule = type.DeclaringModule; + Identifier name = new Identifier(String.Format("{0}`{1}", type.GetUnmangledNameWithoutTypeParameters(), type.TemplateArguments.Count)); + Identifier space = type.Namespace; + TypeNode template = templateModule.GetType(space, name); + return (template); + } else { + // if the type is nested, life is harder; we have to walk up the chain, constructing + // un-specialized identifiers as we go, then walk back down the chain, fetching + // template types as we go + + // create a stack to keep track of identifiers + Stack < Identifier > identifiers = new Stack < Identifier >(); + + // populate the stack with the identifiers of all the types up to the outermost type + TypeNode current = type; + while (true) { + int count = 0; + if ((current.TemplateArguments != null) && (current.TemplateArguments.Count > count)) count = current.TemplateArguments.Count; + if ((current.TemplateParameters != null) && (current.TemplateParameters.Count > count)) count = current.TemplateParameters.Count; + TypeNodeList arguments = current.TemplateParameters; + if (count == 0) { + identifiers.Push(new Identifier(current.GetUnmangledNameWithoutTypeParameters())); + } else { + identifiers.Push(new Identifier(String.Format("{0}`{1}", current.GetUnmangledNameWithoutTypeParameters(), count))); + } + // Console.WriteLine("U {0} {1}", identifiers.Peek(), CountArguments(current)); + if (current.DeclaringType == null) break; + current = current.DeclaringType; + } + + // fetch a TypeNode representing that outermost type + Module module = current.DeclaringModule; + Identifier space = current.Namespace; + current = module.GetType(space, identifiers.Pop()); + + // move down the stack to the inner type we want + while (identifiers.Count > 0) { + current = (TypeNode)current.GetMembersNamed(identifiers.Pop())[0]; + // Console.WriteLine("D {0} {1}", current.GetFullUnmangledNameWithTypeParameters(), CountArguments(current)); + } + + // whew, finally we've got it + return (current); + } + + } + + public static bool IsDefaultMember(Member member) { + + if (member == null) throw new ArgumentNullException("member"); + TypeNode type = member.DeclaringType; + + MemberList defaultMembers = type.DefaultMembers; + for (int i = 0; i < defaultMembers.Count; i++) { + Member defaultMember = defaultMembers[i]; + if (member == defaultMember) return (true); + } + return (false); + + } + + private static Event GetEventFromAdder(Method adder) { + if (adder == null) throw new ArgumentNullException("adder"); + TypeNode type = adder.DeclaringType; + MemberList members = type.Members; + foreach (Member member in members) { + if (member.NodeType != NodeType.Event) continue; + Event trigger = member as Event; + if (trigger.HandlerAdder == adder) return (trigger); + } + return (null); + } + + private static ParameterList GetParameters(Member member) { + Method method = member as Method; + if (method != null) return (method.Parameters); + + Property property = member as Property; + if (property != null) return (property.Parameters); + + return (new ParameterList()); + } + + private static Property GetPropertyFromAccessor(Method accessor) { + if (accessor == null) throw new ArgumentNullException("accessor"); + TypeNode type = accessor.DeclaringType; + MemberList members = type.Members; + foreach (Member member in members) { + if (member.NodeType != NodeType.Property) continue; + Property property = member as Property; + if (property.Getter == accessor) return (property); + if (property.Setter == accessor) return (property); + } + return (null); + } + + private static bool IsSpecialized(TypeNode type) { + for (TypeNode t = type; t != null; t = t.DeclaringType) { + TypeNodeList templates = t.TemplateArguments; + if ((templates != null) && (templates.Count > 0)) return (true); + } + return (false); + } + + // parameters1 should be fully specialized; parameters2 + + private static bool ParametersMatch(ParameterList parameters1, ParameterList parameters2) { + + if (parameters1.Count != parameters2.Count) return (false); + + for (int i = 0; i < parameters1.Count; i++) { + TypeNode type1 = parameters1[i].Type; + TypeNode type2 = parameters2[i].Type; + + // we can't determine the equivilence of template parameters; this is probably not good + if (type1.IsTemplateParameter || type2.IsTemplateParameter) continue; + + // the node type must be the same; this is probably a fast check + if (type1.NodeType != type2.NodeType) return (false); + + // if they are "normal" types, we will compare them + // comparing arrays, pointers, etc. is dangerous, because the types they contian may be template parameters + if ((type1.NodeType == NodeType.Class) || (type1.NodeType == NodeType.Struct) || (type1.NodeType == NodeType.Interface) || + (type1.NodeType == NodeType.EnumNode) || (type1.NodeType == NodeType.DelegateNode)) { + type1 = GetTemplateType(type1); + type2 = GetTemplateType(type2); + if (!type2.IsStructurallyEquivalentTo(type1)) { + // Console.WriteLine("{0} !~ {1}", type1.FullName, type2.FullName); + return (false); + } else { + // Console.WriteLine("{0} ~ {1}", type1.FullName, type2.FullName); + } + } + + } + + return (true); + } + + private static bool TypeMatch(TypeNode type1, TypeNode type2) { + + // the two types must be of the same kind + if (type1.NodeType != type2.NodeType) return (false); + + if (type1.NodeType == NodeType.ArrayType) { + // they are arrays, so check elements + ArrayType array1 = (ArrayType)type1; + ArrayType array2 = (ArrayType)type2; + return (TypeMatch(array1.ElementType, array2.ElementType)); + } else { + // they are normal types + return (type1.IsStructurallyEquivalentTo(type2)); + } + } + + } + +} diff --git a/tools/Sandcastle/Source/Reflection/Reflection.csproj b/tools/Sandcastle/Source/Reflection/Reflection.csproj new file mode 100644 index 0000000..9a88afa --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/Reflection.csproj @@ -0,0 +1,107 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>9.0.30729</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{74F5EB3F-DC99-4FBE-9495-EE378FC60F65}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Reflection</RootNamespace> + <AssemblyName>Reflection</AssemblyName> + <SccProjectName> + </SccProjectName> + <SccLocalPath> + </SccLocalPath> + <SccAuxPath> + </SccAuxPath> + <SccProvider> + </SccProvider> + <SignAssembly>false</SignAssembly> + <AssemblyOriginatorKeyFile>../../key.snk</AssemblyOriginatorKeyFile> + <FileUpgradeFlags> + </FileUpgradeFlags> + <OldToolsVersion>2.0</OldToolsVersion> + <UpgradeBackupLocation> + </UpgradeBackupLocation> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'WebDocsDebug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\WebDocsDebug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression> + <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile> + <ErrorReport>prompt</ErrorReport> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Sandcastle|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\Sandcastle\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression> + <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile> + <ErrorReport>prompt</ErrorReport> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="AllDocumentedFilter.cs" /> + <Compile Include="AllTopicFilter.cs" /> + <Compile Include="ApiFilter.cs" /> + <Compile Include="ApiNamer.cs" /> + <Compile Include="ApiVisitor.cs" /> + <Compile Include="AssemblyReferenceEventArgs.cs" /> + <Compile Include="AssemblyResolver.cs" /> + <Compile Include="ExternalDocumentedFilter.cs" /> + <Compile Include="ExternalFilter.cs" /> + <Compile Include="ExternalTopicFilter.cs" /> + <Compile Include="GlobalSuppressions.cs" /> + <Compile Include="MemberFilter.cs" /> + <Compile Include="NamespaceFilter.cs" /> + <Compile Include="NoFilter.cs" /> + <Compile Include="OrcasNamer.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Reflection.cs" /> + <Compile Include="RootFilter.cs" /> + <Compile Include="TestResolver.cs" /> + <Compile Include="TypeFilter.cs" /> + <Compile Include="WhidbeyNamer.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\CCI\CCI.csproj"> + <Project>{4CB332D6-976E-44F6-A320-A515A9D1D1D3}</Project> + <Name>CCI</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <!-- Copy the output assemblies to a common binaries directory (ProductionTools). --> + <Target Name="AfterBuild"> + <CreateItem Include="$(OutputPath)\$(AssemblyName).*"> + <Output TaskParameter="Include" ItemName="ProductionFiles" /> + </CreateItem> + <Copy SourceFiles="@(ProductionFiles)" DestinationFolder="..\..\ProductionTools" /> + </Target> +</Project>
\ No newline at end of file diff --git a/tools/Sandcastle/Source/Reflection/RootFilter.cs b/tools/Sandcastle/Source/Reflection/RootFilter.cs new file mode 100644 index 0000000..63401a5 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/RootFilter.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class RootFilter + { + +#region Member Variables + private bool exposed; + + private List < NamespaceFilter > namespaceFilters = new List < NamespaceFilter >(); +#endregion + +#region Constructors + + public RootFilter(bool exposed) { + this.exposed = exposed; + } + + public RootFilter(XmlReader configuration) { + exposed = Convert.ToBoolean(configuration.GetAttribute("expose")); + XmlReader subtree = configuration.ReadSubtree(); + while (subtree.Read()) { + if ((subtree.NodeType == XmlNodeType.Element) && (subtree.Name == "namespace")) { + NamespaceFilter namespaceFilter = new NamespaceFilter(subtree); + namespaceFilters.Add(namespaceFilter); + } + } + subtree.Close(); + } +#endregion + +#region Public API + + /// <summary> + /// Gets the exposed value from the config + /// </summary> + public bool ExposedFilterSetting + { + get + { + return exposed; + } + } + + /// <summary> + /// Gets the number of namespace filters + /// </summary> + public int NamespaceFilterCount + { + get + { + return namespaceFilters.Count; + } + } + + public List < NamespaceFilter > NamespaceFilters { + get { + return (namespaceFilters); + } + } + + //Find out if any are exposed incase this class is not exposed + public bool HasExposedMembers(TypeNode type) + { + foreach (NamespaceFilter namespaceFilter in namespaceFilters) + { + bool? result = namespaceFilter.IsExposedType(type); + if (result != null) + { + return namespaceFilter.HasExposedMembers(type); + } + } + return false; + } + + public bool IsExposedApi(Member api) { + + Namespace space = api as Namespace; + if (space != null) return (IsExposedNamespace(space)); + + TypeNode type = api as TypeNode; + if (type != null) return (IsExposedType(type)); + + return (IsExposedMember(api)); + + } + + + public bool IsExposedMember(Member member) { + //Console.WriteLine("DEBUG: root.IsExposedMember"); + foreach (NamespaceFilter namespaceFilter in namespaceFilters) { + bool? result = namespaceFilter.IsExposedMember(member); + if (result != null) return ((bool)result); + } + return (exposed); + } + + public bool IsExposedNamespace(Namespace space) { + foreach (NamespaceFilter namespaceFilter in namespaceFilters) { + bool? result = namespaceFilter.IsExposedNamespace(space); + if (result != null) return ((bool)result); + } + return (exposed); + } + + public bool IsExposedType(TypeNode type) { + foreach (NamespaceFilter namespaceFilter in namespaceFilters) { + bool? result = namespaceFilter.IsExposedType(type); + if (result != null) return ((bool)result); + } + + return (exposed); + } + +#endregion + } +} diff --git a/tools/Sandcastle/Source/Reflection/TestResolver.cs b/tools/Sandcastle/Source/Reflection/TestResolver.cs new file mode 100644 index 0000000..5ad9c46 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/TestResolver.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class TestResolver : AssemblyResolver { + + public TestResolver(XPathNavigator configuration) : base(configuration) { } + + public override AssemblyNode ResolveReference(AssemblyReference reference, Module module) { + Console.WriteLine("test resolver: {0}", reference.StrongName); + return (base.ResolveReference(reference, module)); + } + } + +} diff --git a/tools/Sandcastle/Source/Reflection/TypeFilter.cs b/tools/Sandcastle/Source/Reflection/TypeFilter.cs new file mode 100644 index 0000000..de7a3fa --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/TypeFilter.cs @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class TypeFilter + { + +#region Member Variables + + private bool exposed; + + private List < MemberFilter > memberFilters = new List < MemberFilter >(); + + private string name; + +#endregion + +#region Constructors + public TypeFilter(string name, bool exposed) { + if (name == null) throw new ArgumentNullException("name"); + this.name = name; + this.exposed = exposed; + } + + public TypeFilter(XmlReader configuration) { + if ((configuration.NodeType != XmlNodeType.Element) || (configuration.Name != "type")) throw new InvalidOperationException(); + name = configuration.GetAttribute("name"); + exposed = Convert.ToBoolean(configuration.GetAttribute("expose")); + XmlReader subtree = configuration.ReadSubtree(); + while (subtree.Read()) { + if ((subtree.NodeType == XmlNodeType.Element) && (subtree.Name == "member")) { + MemberFilter memberFilter = new MemberFilter(subtree); + memberFilters.Add(memberFilter); + } + } + subtree.Close(); + } +#endregion + +#region Public API + + //Find out if any are exposed incase this class is not exposed + public bool HasExposedMembers(TypeNode type) + { + foreach (Member member in type.Members) + foreach (MemberFilter memberFilter in memberFilters) + if (memberFilter.IsExposedMember(member) == true) + return true; + + return false; + } + + + public bool? IsExposedMember(Member member) { + //Console.WriteLine("DEBUG: typeFilter.IsExposedMember"); + TypeNode type = ReflectionUtilities.GetTemplateType(member.DeclaringType); + if (IsExposedType(type) != null) { + foreach (MemberFilter memberFilter in memberFilters) { + bool? result = memberFilter.IsExposedMember(member); + if (result != null) return (result); + } + + return (exposed); //return the type's exposed setting + } else { + return (null); + } + } + + /** + * <summary>IsExposedType compares the given type to itself. If this filter + * contains a '.' designating it as for a nested class it will skip + * non-nested classes. Classes with no declaring types will not be compared to + * filters with a '.' + * </summary> + */ + public bool? IsExposedType(TypeNode type) + { + bool? typeIsExposed = null; + + //check if the type was nested + if (type.DeclaringType == null) + { + if (type.Name.Name == name) + typeIsExposed = exposed; + } + //if we are nested then check if this filter is for a nested class + //check that nothing used is null also. + //if the name attribute is not there in the config name can be null here. + else if (name != null && name.Contains(".")) + { + //Get a stack of declaring type names + Stack < string > parentNames = new Stack < string >(); + parentNames.Push(type.Name.Name); //start with this one + + TypeNode parent = type.DeclaringType; + while (parent != null) + { + parentNames.Push(parent.Name.Name); + parent = parent.DeclaringType; + } + + //put them back in the correct order and check the name + if (name.Equals(String.Join(".", parentNames.ToArray()))) + typeIsExposed = exposed; + } + + return typeIsExposed; + } + +#endregion + } +} diff --git a/tools/Sandcastle/Source/Reflection/WhidbeyNamer.cs b/tools/Sandcastle/Source/Reflection/WhidbeyNamer.cs new file mode 100644 index 0000000..f6234a8 --- /dev/null +++ b/tools/Sandcastle/Source/Reflection/WhidbeyNamer.cs @@ -0,0 +1,275 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.IO; + +using System.Compiler; + +namespace Microsoft.Ddue.Tools.Reflection { + + public class WhidbeyNamer : ApiNamer { + + public override string GetMemberName(Member member) { + + using (TextWriter writer = new StringWriter()) { + + switch (member.NodeType) { + case NodeType.Field: + writer.Write("F:"); + WriteField((Field)member, writer); + break; + case NodeType.Property: + writer.Write("P:"); + WriteProperty((Property)member, writer); + break; + case NodeType.Method: + writer.Write("M:"); + WriteMethod((Method)member, writer); + break; + case NodeType.InstanceInitializer: + writer.Write("M:"); + WriteConstructor((InstanceInitializer)member, writer); + break; + case NodeType.StaticInitializer: + writer.Write("M:"); + WriteStaticConstructor((StaticInitializer)member, writer); + break; + case NodeType.Event: + writer.Write("E:"); + WriteEvent((Event)member, writer); + break; + } + + return (writer.ToString()); + + } + + } + + public override string GetNamespaceName(Namespace space) { + using (TextWriter writer = new StringWriter()) { + writer.Write("N:"); + WriteNamespace(space, writer); + return (writer.ToString()); + } + } + + public override string GetTypeName(TypeNode type) { + using (TextWriter writer = new StringWriter()) { + writer.Write("T:"); + WriteType(type, writer); + return (writer.ToString()); + } + } + + + private static string GetName(Member entity) { + + using (TextWriter writer = new StringWriter()) { + + TypeNode type = entity as TypeNode; + if (type != null) { + writer.Write("T:"); + WriteType(type, writer); + return (writer.ToString()); + } + + switch (entity.NodeType) { + case NodeType.Namespace: + writer.Write("N:"); + WriteNamespace(entity as Namespace, writer); + break; + case NodeType.Field: + writer.Write("F:"); + WriteField(entity as Field, writer); + break; + case NodeType.Property: + writer.Write("P:"); + WriteProperty(entity as Property, writer); + break; + case NodeType.Method: + writer.Write("M:"); + WriteMethod(entity as Method, writer); + break; + case NodeType.InstanceInitializer: + writer.Write("M:"); + WriteConstructor(entity as InstanceInitializer, writer); + break; + case NodeType.StaticInitializer: + writer.Write("M:"); + WriteStaticConstructor(entity as StaticInitializer, writer); + break; + case NodeType.Event: + writer.Write("E:"); + WriteEvent(entity as Event, writer); + break; + } + + return (writer.ToString()); + + } + + } + + private static void WriteConstructor(InstanceInitializer constructor, TextWriter writer) { + WriteType(constructor.DeclaringType, writer); + writer.Write(".#ctor"); + WriteParameters(constructor.Parameters, writer); + } + + private static void WriteEvent(Event trigger, TextWriter writer) { + WriteType(trigger.DeclaringType, writer); + writer.Write(".{0}", trigger.Name.Name); + } + + private static void WriteField(Field field, TextWriter writer) { + WriteType(field.DeclaringType, writer); + writer.Write(".{0}", field.Name.Name); + } + + private static void WriteMethod(Method method, TextWriter writer) { + string name = method.Name.Name; + WriteType(method.DeclaringType, writer); + writer.Write(".{0}", name); + if (method.IsGeneric) { + TypeNodeList genericParameters = method.TemplateParameters; + if (genericParameters != null) { + writer.Write("``{0}", genericParameters.Count); + } + } + WriteParameters(method.Parameters, writer); + // add ~ for conversion operators + if ((name == "op_Implicit") || (name == "op_Explicit")) { + writer.Write("~"); + WriteType(method.ReturnType, writer); + } + + } + + // The actual logic to construct names + + private static void WriteNamespace(Namespace space, TextWriter writer) { + writer.Write(space.Name); + } + + private static void WriteParameters(ParameterList parameters, TextWriter writer) { + if ((parameters == null) || (parameters.Count == 0)) return; + writer.Write("("); + for (int i = 0; i < parameters.Count; i++) { + if (i > 0) writer.Write(","); + WriteType(parameters[i].Type, writer); + } + writer.Write(")"); + } + + private static void WriteProperty(Property property, TextWriter writer) { + WriteType(property.DeclaringType, writer); + writer.Write(".{0}", property.Name.Name); + ParameterList parameters = property.Parameters; + WriteParameters(parameters, writer); + } + + private static void WriteStaticConstructor(StaticInitializer constructor, TextWriter writer) { + WriteType(constructor.DeclaringType, writer); + writer.Write(".#cctor"); + WriteParameters(constructor.Parameters, writer); + } + + private static void WriteType(TypeNode type, TextWriter writer) { + switch (type.NodeType) { + case NodeType.ArrayType: + ArrayType array = type as ArrayType; + WriteType(array.ElementType, writer); + writer.Write("["); + for (int i = 1; i < array.Rank; i++) writer.Write(","); + writer.Write("]"); + break; + case NodeType.Reference: + Reference reference = type as Reference; + TypeNode referencedType = reference.ElementType; + WriteType(referencedType, writer); + + // DocStudio fails to add @ to template parameters or arrays of template + // parameters, so we have to mirror this bizarre behavior here + bool writeAt = true; + if (referencedType.IsTemplateParameter) writeAt = false; + if (referencedType.NodeType == NodeType.ArrayType) { + ArrayType referencedArray = referencedType as ArrayType; + if (referencedArray.ElementType.IsTemplateParameter) writeAt = false; + } + if (writeAt) writer.Write("@"); + break; + case NodeType.Pointer: + Pointer pointer = type as Pointer; + WriteType(pointer.ElementType, writer); + writer.Write("*"); + break; + case NodeType.OptionalModifier: + TypeModifier optionalModifierClause = type as TypeModifier; + WriteType(optionalModifierClause.ModifiedType, writer); + writer.Write("!"); + WriteType(optionalModifierClause.Modifier, writer); + break; + case NodeType.RequiredModifier: + TypeModifier requiredModifierClause = type as TypeModifier; + WriteType(requiredModifierClause.ModifiedType, writer); + writer.Write("|"); + WriteType(requiredModifierClause.Modifier, writer); + break; + default: + if (type.IsTemplateParameter) { + ITypeParameter gtp = (ITypeParameter)type; + if (gtp.DeclaringMember is TypeNode) { + writer.Write("`"); + } else if (gtp.DeclaringMember is Method) { + writer.Write("``"); + } else { + throw new InvalidOperationException("Generic parameter not on type or method."); + } + writer.Write(gtp.ParameterListIndex); + } else { + // namespace + TypeNode declaringType = type.DeclaringType; + if (declaringType != null) { + // names of nested types begin with outer type name + WriteType(declaringType, writer); + writer.Write("."); + } else { + // otherwise just prepend the namespace + string space = type.Namespace.Name; + if (space != null && space.Length > 0) { + writer.Write(space); + writer.Write("."); + } + } + // name + writer.Write(type.GetUnmangledNameWithoutTypeParameters()); + // generic parameters + if (type.IsGeneric) { + // number of parameters + TypeNodeList parameters = type.TemplateParameters; + if (parameters != null) { + writer.Write("`{0}", parameters.Count); + } + // arguments + TypeNodeList arguments = type.TemplateArguments; + if ((arguments != null) && (arguments.Count > 0)) { + writer.Write("{"); + for (int i = 0; i < arguments.Count; i++) { + TypeNode argument = arguments[i]; + if (i > 0) writer.Write(","); + WriteType(arguments[i], writer); + } + writer.Write("}"); + } + } + } + break; + } + } + + } + +} |