// Copyright © Microsoft Corporation. // This source file is subject to the Microsoft Permissive License. // See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. // All other rights reserved. using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Schema; using System.Xml.XPath; namespace Microsoft.Ddue.Tools { // The basic object model here is this: // * Target objects represent files that can be targeted by a reference link // * Different child objects of Target represent different sorts of API targets: Namespace, Type, Member, etc. // * Targets are stored in a TargetCollection // To indicate relationships between targets (e.g. a Method takes a particular type parameter), we // introduce another set of classes: // * Reference objects refer to a specific target // * Objects like SpecializedTypeReference and ArrayTypeReference that represent decorated types // There are two ways to construct such objects: // * XML from a reflection information file defines Target and Reference objects. XmlUtilities does this. // * Code entity reference strings construct Reference objecs. CerUtilities does this. // Finally, we need a way to write the link text corresponding to a reference: // * LinkTextResolver contains routines that, given a reference, writes the corresponding link text // all arguments of public methods are verified // The fact that the creation methods (via XML or CER strings) for references and their rendering methods // are seperated from the declarations of the reference types goes against OO principals. (The consequent // absence of virtual methods also makes for a lot of ugly casting to figure out what method to call.) // But there is a reason for it: I wanted all the code that intrepreted XML together, all the code that // intrepreted CER strings together, and all the code that did link text renderig together, and I wanted // them all seperate from each other. I belive this is extremely important for maintainability. It may // be possible to leverage partial classes to do this in a more OO fashion. public class Test { public static void Main (string[] args) { TargetCollection targets = new TargetCollection(); XPathDocument document = new XPathDocument(args[0]); XPathNavigator node = document.CreateNavigator(); XmlTargetCollectionUtilities.AddTargets(targets, node, LinkType2.Local); Console.WriteLine(targets.Count); LinkTextResolver resolver = new LinkTextResolver(targets); // test writer XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; XmlWriter writer = XmlWriter.Create(Console.Out, settings); writer.WriteStartDocument(); writer.WriteStartElement("types"); XPathNodeIterator apiNodes = node.Select("/*/apis/api[not(apidata/@subgroup='enumeration')]//*[@display-api]"); foreach (XPathNavigator apiNode in apiNodes) { string api = apiNode.GetAttribute("display-api", String.Empty); if (api[1] != ':') continue; string id = (string) apiNode.Evaluate("string(ancestor::api[1]/@id)"); TextReferenceUtilities.SetGenericContext(id); Reference reference = TextReferenceUtilities.CreateReference(api); writer.WriteStartElement("test"); writer.WriteAttributeString("api", api); writer.WriteAttributeString("context", id); if (reference == null) { writer.WriteString("NULL REFERENCE"); } else { resolver.WriteReference(reference, DisplayOptions.ShowContainer | DisplayOptions.ShowTemplates | DisplayOptions.ShowParameters, writer); } writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteEndDocument(); writer.Close(); } } // contains a collection of targets public class TargetCollection { private Dictionary index = new Dictionary(); // read the collection public Target this[string id] { get { Target result; index.TryGetValue(id, out result); return (result); } } public bool Contains (string id) { return( index.ContainsKey(id) ); } public IEnumerable Ids { get { return (index.Keys); } } public IEnumerable Targets { get { return (index.Values); } } public int Count { get { return(index.Count); } } // change the collection public void Add (Target target) { index[target.Id] = target; //index.Add(target.Id, target); } public void Clear () { index.Clear(); } } // targets public class Target { internal string id; internal string container; internal string file; internal LinkType2 type; public string Id { get { return (id); } } public string Container { get { return (container); } } public string File { get { return(file); } } internal LinkType2 DefaultLinkType { get { return (type); } } public virtual void Add (TargetCollection targets) { targets.Add(this); } } public class NamespaceTarget : Target { internal string name; public string Name { get { return(name); } } } public class TypeTarget : Target { internal string name; internal NamespaceReference containingNamespace; internal SimpleTypeReference containingType; internal string[] templates; public string Name { get { return(name); } } public NamespaceReference Namespace { get { return (containingNamespace); } } public SimpleTypeReference OuterType { get { return (containingType); } } public string[] Templates { get { return(templates); } } } public class EnumerationTarget : TypeTarget { internal MemberTarget[] elements; public override void Add (TargetCollection targets) { base.Add(targets); foreach (MemberTarget element in elements) { element.Add(targets); } } } public class MemberTarget : Target { internal string name; internal SimpleTypeReference containingType; internal string overload; public string Name { get { return (name); } } public TypeReference Type { get { return (containingType); } } public string OverloadId { get { return (overload); } } } public class ConstructorTarget : MemberTarget { internal Parameter[] parameters; public Parameter[] Parameters { get { return (parameters); } } } public class ProcedureTarget : MemberTarget { internal bool conversionOperator; internal MemberReference explicitlyImplements = null; public bool ConversionOperator { get { return (conversionOperator); } } public MemberReference ExplicitlyImplements { get { return (explicitlyImplements); } } } public class EventTarget : ProcedureTarget { } public class PropertyTarget : ProcedureTarget { internal Parameter[] parameters; internal TypeReference returnType; public Parameter[] Parameters { get { return (parameters); } } } public class MethodTarget : ProcedureTarget { internal Parameter[] parameters; internal TypeReference returnType; internal string[] templates; public Parameter[] Parameters { get { return (parameters); } } public string[] Templates { get { return (templates); } } // property to hold specialized template arguments (used with extension methods) internal TypeReference[] templateArgs; public TypeReference[] TemplateArgs { get { return (templateArgs); } } } public class Parameter { private string name; private TypeReference type; public string Name { get { return (name); } } public TypeReference Type { get { return (type); } } internal Parameter (string name, TypeReference type) { this.name = name; this.type = type; } } // ***** Reference objects ***** public abstract class Reference { } public class NamespaceReference : Reference { private string namespaceId; public string Id { get { return(namespaceId); } } public Target Resolve (TargetCollection targets) { return(targets[namespaceId]); } internal NamespaceReference (string id) { this.namespaceId = id; } } public abstract class TypeReference : Reference {} public class SimpleTypeReference : TypeReference { private string typeId; public string Id { get { return(typeId); } } public Target Resolve (TargetCollection targets) { return(targets[typeId]); } internal SimpleTypeReference (string id) { this.typeId = id; } } public class SpecializedTypeReference : TypeReference { private Specialization[] specializations; public Specialization[] Specializations { get { return (specializations); } } internal SpecializedTypeReference (Specialization[] specializations) { if (specializations == null) throw new ArgumentNullException("specializations"); this.specializations = specializations; } public Dictionary GetSpecializationDictionary () { Dictionary dictionary = new Dictionary(); foreach (Specialization specialization in specializations) { for (int index=0; index /// Contains the information to generate the display string for an extension method link /// public class ExtensionMethodReference : Reference { private string methodName; public string Name { get { return (methodName); } } private Parameter[] parameters; public Parameter[] Parameters { get { return (parameters); } } private TypeReference[] templateArgs; public TypeReference[] TemplateArgs { get { return (templateArgs); } } internal ExtensionMethodReference(string methodName, Parameter[] parameters, TypeReference[] templateArgs) { if (methodName == null) throw new ArgumentNullException("methodName"); this.methodName = methodName; this.parameters = parameters; this.templateArgs = templateArgs; } } public class SimpleMemberReference : MemberReference { private string memberId; public string Id { get { return(memberId); } } public Target Resolve (TargetCollection targets) { return(targets[memberId]); } internal SimpleMemberReference (string id) { if (id == null) throw new ArgumentNullException("id"); this.memberId = id; } } public class SpecializedMemberReference : MemberReference { private SimpleMemberReference member; private SpecializedTypeReference type; public SimpleMemberReference TemplateMember { get { return(member); } } public SpecializedTypeReference SpecializedType { get { return (type); } } internal SpecializedMemberReference (SimpleMemberReference member, SpecializedTypeReference type) { if (member == null) throw new ArgumentNullException("member"); if (type == null) throw new ArgumentNullException("type"); this.member = member; this.type = type; } } public class SpecializedMemberWithParametersReference : MemberReference { private string prefix; private SpecializedTypeReference type; private string member; private TypeReference[] parameters; public string Prefix { get { return (prefix); } } public SpecializedTypeReference SpecializedType { get { return (type); } } public string MemberName { get { return (member); } } public TypeReference[] ParameterTypes { get { return(parameters); } } internal SpecializedMemberWithParametersReference (string prefix, SpecializedTypeReference type, string member, TypeReference[] parameters) { if (type == null) throw new ArgumentNullException("type"); if (parameters == null) throw new ArgumentNullException("parameters"); this.prefix = prefix; this.type = type; this.member = member; this.parameters = parameters; } } public class InvalidReference : Reference { private string id; public String Id { get { return (id); } } internal InvalidReference (string id) { this.id = id; } } // ***** Logic to construct Target & Reference objects from XML reflection data ***** // Anything that depends on specifics of the XML reflection data format lives here public static class XmlTargetCollectionUtilities { // XPath expressions for extracting data // topic data private static XPathExpression topicIdExpression = XPathExpression.Compile("string(@id)"); private static XPathExpression topicContainerExpression = XPathExpression.Compile("string(containers/library/@assembly)"); private static XPathExpression topicFileExpression = XPathExpression.Compile("string(file/@name)"); private static XPathExpression topicGroupExpression = XPathExpression.Compile("string(topicdata/@group)"); // api data private static XPathExpression apiNameExpression = XPathExpression.Compile("string(apidata/@name)"); private static XPathExpression apiGroupExpression = XPathExpression.Compile("string(apidata/@group)"); private static XPathExpression apiSubgroupExpression = XPathExpression.Compile("string(apidata/@subgroup)"); private static XPathExpression apiSubsubgroupExpression = XPathExpression.Compile("string(apidata/@subsubgroup)"); // member data private static XPathExpression apiOverloadIdExpression = XPathExpression.Compile("string(overload/@api | memberdata/@overload)"); // explicit implmentation data private static XPathExpression apiIsExplicitImplementationExpression = XPathExpression.Compile("boolean(memberdata/@visibility='private' and proceduredata/@virtual='true' and boolean(implements/member))"); private static XPathExpression apiImplementedMembersExpression = XPathExpression.Compile("implements/member"); // op_explicit and op_implicit data private static XPathExpression apiIsConversionOperatorExpression = XPathExpression.Compile("boolean((apidata/@subsubgroup='operator') and (apidata/@name='Explicit' or apidata/@name='Implicit'))"); // container data private static XPathExpression apiContainingNamespaceExpression = XPathExpression.Compile("(containers/namespace)[1]"); private static XPathExpression apiContainingTypeExpression = XPathExpression.Compile("(containers/type)[1]"); // reference data private static XPathExpression referenceApiExpression = XPathExpression.Compile("string(@api)"); // template data private static XPathExpression apiTemplatesExpression = XPathExpression.Compile("templates/template"); private static XPathExpression templateNameExpression = XPathExpression.Compile("string(@name)"); // extension method template data private static XPathExpression methodTemplateArgsExpression = XPathExpression.Compile("templates/*"); // Change the container public static string ContainerExpression { get { return (topicContainerExpression.Expression); } set { topicContainerExpression = XPathExpression.Compile(value); } } // super factory method public static void AddTargets (TargetCollection targets, XPathNavigator topicsNode, LinkType2 type) { XPathNodeIterator topicNodes = topicsNode.Select("/*/apis/api[not(topicdata/@notopic)]"); foreach (XPathNavigator topicNode in topicNodes) { Target target = CreateTarget(topicNode, type); if (target != null) target.Add(targets); } } // Target factory methods public static Target CreateTarget (XPathNavigator topic, LinkType2 type) { if (topic == null) throw new ArgumentNullException("topic"); bool isApiTarget = (bool)topic.Evaluate("boolean(apidata)"); Target target; if (isApiTarget) { target = CreateApiTarget(topic, type); } else { target = new Target(); } if (target == null) throw new XmlSchemaValidationException(String.Format("The target file '{0}' is not valid.", topic.BaseURI)); target.id = (string)topic.Evaluate(topicIdExpression); if (String.IsNullOrEmpty(target.id)) throw new XmlSchemaValidationException(String.Format("The target file '{0}' is not valid.", topic.BaseURI)); target.container = (string)topic.Evaluate(topicContainerExpression); target.file = (string)topic.Evaluate(topicFileExpression); if (String.IsNullOrEmpty(target.file)) throw new XmlSchemaValidationException(String.Format("The target file '{0}' is not valid.", topic.BaseURI)); target.type = type; return(target); } private static Target CreateApiTarget (XPathNavigator api, LinkType2 linkType) { string subGroup = (string) api.Evaluate(apiGroupExpression); if (subGroup == "namespace") { return( CreateNamespaceTarget(api) ); } else if (subGroup == "type") { return (CreateTypeTarget(api, linkType)); } else if (subGroup == "member") { return (CreateMemberTarget(api)); } else { return (null); } } private static NamespaceTarget CreateNamespaceTarget (XPathNavigator api) { NamespaceTarget target = new NamespaceTarget(); target.name = (string) api.Evaluate(apiNameExpression); if (String.IsNullOrEmpty(target.name)) target.name = "(Default Namespace)"; return(target); } private static TypeTarget CreateTypeTarget (XPathNavigator api, LinkType2 linkType) { string subgroup = (string)api.Evaluate(apiSubgroupExpression); TypeTarget target; if (subgroup == "enumeration") { target = CreateEnumerationTarget(api, linkType); } else { target = new TypeTarget(); } target.name = (string) api.Evaluate(apiNameExpression); // containing namespace XPathNavigator namespaceNode = api.SelectSingleNode(apiContainingNamespaceExpression); target.containingNamespace = CreateNamespaceReference(namespaceNode); // containing type, if any XPathNavigator typeNode = api.SelectSingleNode(apiContainingTypeExpression); if (typeNode == null) { target.containingType = null; } else { target.containingType = CreateSimpleTypeReference(typeNode); } // templates target.templates = GetTemplateNames(api); return(target); } private static string[] GetTemplateNames (XPathNavigator api) { List templates = new List(); XPathNodeIterator templateNodes = api.Select(apiTemplatesExpression); foreach (XPathNavigator templateNode in templateNodes) { templates.Add((string)templateNode.Evaluate(templateNameExpression)); } return(templates.ToArray()); } private static EnumerationTarget CreateEnumerationTarget (XPathNavigator api, LinkType2 linkType) { EnumerationTarget enumeration = new EnumerationTarget(); string typeId = (string) api.Evaluate(topicIdExpression); string file = (string) api.Evaluate(topicFileExpression); // Create tar List members = new List(); XPathNodeIterator elementNodes = api.Select("elements/element"); foreach (XPathNavigator elementNode in elementNodes) { string memberId = elementNode.GetAttribute("api", String.Empty); // try to get name from attribute on element node string memberName = elementNode.GetAttribute("name", String.Empty); if (String.IsNullOrEmpty(memberName)) { // if we can't do that, try to get the name by searching the file for the element of that member XPathNavigator memberApi = api.SelectSingleNode(String.Format("following-sibling::api[@id='{0}']", memberId)); if (memberApi != null) { memberName = (string) memberApi.Evaluate(apiNameExpression); } else { // if all else fails, get the name by parsing the identifier string arguments; string type; TextReferenceUtilities.DecomposeMemberIdentifier(memberId, out type, out memberName, out arguments); } } MemberTarget member = new MemberTarget(); member.id = memberId; // get Id from element member.file = file; // get file from type file member.type = linkType; member.name = memberName; // get name from element member.containingType = new SimpleTypeReference(typeId); // get containing type from this type members.Add(member); } enumeration.elements = members.ToArray(); return (enumeration); } public static MemberTarget CreateMemberTarget (XPathNavigator api) { string subgroup = (string)api.Evaluate(apiSubgroupExpression); MemberTarget target; if (subgroup == "method") { target = CreateMethodTarget(api); } else if (subgroup == "property") { target = CreatePropertyTarget(api); } else if (subgroup == "constructor") { target = CreateConstructorTarget(api); } else if (subgroup == "event") { target = CreateEventTarget(api); } else { target = new MemberTarget(); } target.name = (string) api.Evaluate(apiNameExpression); target.containingType = CreateSimpleTypeReference( api.SelectSingleNode(apiContainingTypeExpression) ); target.overload = (string) api.Evaluate(apiOverloadIdExpression); return (target); } private static MethodTarget CreateMethodTarget (XPathNavigator api) { MethodTarget target = new MethodTarget(); target.parameters = CreateParameterList(api); target.returnType = CreateReturnType(api); target.conversionOperator = (bool)api.Evaluate(apiIsConversionOperatorExpression); if ((bool)api.Evaluate(apiIsExplicitImplementationExpression)) { target.explicitlyImplements = CreateMemberReference(api.SelectSingleNode(apiImplementedMembersExpression)); } // this selects templates/template or templates/type, because extension methods can have a mix of generic and specialization XPathNodeIterator templateArgNodes = api.Select(methodTemplateArgsExpression); TypeReference[] templateArgumentReferences = null; if (templateArgNodes != null && templateArgNodes.Count > 0) { templateArgumentReferences = new TypeReference[templateArgNodes.Count]; int i = 0; foreach (XPathNavigator templateArgNode in templateArgNodes) { templateArgumentReferences[i] = CreateTypeReference(templateArgNode); i++; } } target.templateArgs = templateArgumentReferences; // get the short name of each template param target.templates = GetTemplateNames(api); return (target); } private static PropertyTarget CreatePropertyTarget (XPathNavigator api) { PropertyTarget target = new PropertyTarget(); target.parameters = CreateParameterList(api); target.returnType = CreateReturnType(api); if ((bool)api.Evaluate(apiIsExplicitImplementationExpression)) { target.explicitlyImplements = CreateMemberReference(api.SelectSingleNode(apiImplementedMembersExpression)); } return (target); } private static EventTarget CreateEventTarget (XPathNavigator api) { EventTarget target = new EventTarget(); if ((bool)api.Evaluate(apiIsExplicitImplementationExpression)) { target.explicitlyImplements = CreateMemberReference(api.SelectSingleNode(apiImplementedMembersExpression)); } return (target); } private static ConstructorTarget CreateConstructorTarget (XPathNavigator api) { ConstructorTarget target = new ConstructorTarget(); target.parameters = CreateParameterList(api); return (target); } private static Parameter[] CreateParameterList (XPathNavigator api) { List parameters = new List(); XPathNodeIterator parameterNodes = api.Select("parameters/parameter"); foreach (XPathNavigator parameterNode in parameterNodes) { string name = parameterNode.GetAttribute("name", String.Empty); XPathNavigator type = parameterNode.SelectSingleNode("*[1]"); Parameter parameter = new Parameter(name, CreateTypeReference(type)); parameters.Add(parameter); } return (parameters.ToArray()); } private static TypeReference CreateReturnType (XPathNavigator api) { XPathNavigator returnTypeNode = api.SelectSingleNode("returns/*[1]"); if (returnTypeNode == null) { return (null); } else { return (CreateTypeReference(returnTypeNode)); } } // reference factory public static Reference CreateReference (XPathNavigator node) { if (node == null) throw new ArgumentNullException("node"); if (node.NodeType == XPathNodeType.Element) { string tag = node.LocalName; if (tag == "namespace") return(CreateNamespaceReference(node)); if (tag == "member") return (CreateMemberReference(node)); return (CreateTypeReference(node)); } else { return (null); } } public static NamespaceReference CreateNamespaceReference (XPathNavigator namespaceElement) { if (namespaceElement == null) throw new ArgumentNullException("namespaceElement"); string api = (string) namespaceElement.Evaluate(referenceApiExpression); NamespaceReference reference = new NamespaceReference(api); return(reference); } public static TypeReference CreateTypeReference (XPathNavigator node) { if (node == null) throw new ArgumentNullException("reference"); string tag = node.LocalName; if (tag == "type") { bool isSpecialized = (bool)node.Evaluate("boolean(.//specialization)"); if (isSpecialized) { return (CreateSpecializedTypeReference(node)); } else { return (CreateSimpleTypeReference(node)); } } else if (tag == "arrayOf") { string rankValue = node.GetAttribute("rank", String.Empty); XPathNavigator elementNode = node.SelectSingleNode("*[1]"); return (new ArrayTypeReference(CreateTypeReference(elementNode), Convert.ToInt32(rankValue))); } else if (tag == "referenceTo") { XPathNavigator referedToNode = node.SelectSingleNode("*[1]"); return (new ReferenceTypeReference(CreateTypeReference(referedToNode))); } else if (tag == "pointerTo") { XPathNavigator pointedToNode = node.SelectSingleNode("*[1]"); return (new PointerTypeReference(CreateTypeReference(pointedToNode))); } else if (tag == "template") { string nameValue = node.GetAttribute("name", String.Empty); string indexValue = node.GetAttribute("index", String.Empty); string apiValue = node.GetAttribute("api", String.Empty); if ((!String.IsNullOrEmpty(apiValue)) && (!String.IsNullOrEmpty(indexValue))) { return (new IndexedTemplateTypeReference(apiValue, Convert.ToInt32(indexValue))); } else { return (new NamedTemplateTypeReference(nameValue)); } } throw new InvalidOperationException(String.Format("INVALID '{0}'", tag)); } public static SimpleTypeReference CreateSimpleTypeReference (XPathNavigator node) { if (node == null) throw new ArgumentNullException("node"); string api = node.GetAttribute("api", String.Empty); SimpleTypeReference reference = new SimpleTypeReference(api); return(reference); } private static SpecializedTypeReference CreateSpecializedTypeReference (XPathNavigator node) { Stack specializations = new Stack(); XPathNavigator typeNode = node.Clone(); while (typeNode != null) { specializations.Push(CreateSpecialization(typeNode)); typeNode = typeNode.SelectSingleNode("type"); } SpecializedTypeReference reference = new SpecializedTypeReference(specializations.ToArray()); return (reference); } private static Specialization CreateSpecialization (XPathNavigator node) { SimpleTypeReference template = CreateSimpleTypeReference(node); List arguments = new List(); XPathNodeIterator specializationNodes = node.Select("specialization/*"); foreach (XPathNavigator specializationNode in specializationNodes) { arguments.Add(CreateTypeReference(specializationNode)); } Specialization specialization = new Specialization(template, arguments.ToArray()); return(specialization); } public static MemberReference CreateMemberReference (XPathNavigator node) { string api = node.GetAttribute("api", String.Empty); SimpleMemberReference member = new SimpleMemberReference(api); bool isSpecialized = (bool)node.Evaluate("boolean(./type//specialization)"); if (isSpecialized) { XPathNavigator typeNode = node.SelectSingleNode("type"); SpecializedTypeReference type = CreateSpecializedTypeReference(typeNode); return( new SpecializedMemberReference(member, type) ); } else { return(member); } } /// /// Create an object to store the information to generate the display string for an extension method /// /// xml node containing the extension method data /// public static ExtensionMethodReference CreateExtensionMethodReference(XPathNavigator node) { string methodName = (string)node.Evaluate(apiNameExpression); Parameter[] parameters = CreateParameterList(node); TypeReference[] templateArgumentReferences = null; // List templateNames = new List(); // this selects templates/template or templates/type, because extension methods can have a mix of generic and specialization // get the short name of each template param or template arg XPathNodeIterator templateNodes = node.Select(methodTemplateArgsExpression); if (templateNodes != null && templateNodes.Count > 0) { templateArgumentReferences = new TypeReference[templateNodes.Count]; int i = 0; foreach (XPathNavigator templateNode in templateNodes) { templateArgumentReferences[i] = CreateTypeReference(templateNode); i++; } } ExtensionMethodReference extMethod = new ExtensionMethodReference(methodName, parameters, templateArgumentReferences); return extMethod; } } // ***** Logic for constructing references from code entity reference strings ***** // Anything that depends on the specific form the ID strings lives here public static class TextReferenceUtilities { public static Reference CreateReference (string api) { if (String.IsNullOrEmpty(api)) throw new ArgumentException("api"); Reference reference = null; char start = api[0]; if (start == 'N') { reference = CreateNamespaceReference(api); } else if (start == 'T') { reference = CreateTypeReference(api); } else { //Console.WriteLine("Creating member reference"); reference = CreateMemberReference(api); } if (reference == null) { return (new InvalidReference(api)); } else { return (reference); } } public static NamespaceReference CreateNamespaceReference (string api) { if (ValidNamespace.IsMatch(api)) { return( new NamespaceReference(api) ); } else { return(null); } } public static TypeReference CreateTypeReference (string api) { if (ValidSimpleType.IsMatch(api)) { // this is a reference to a "normal" simple type return (CreateSimpleTypeReference(api)); } else if (ValidSpecializedType.IsMatch(api)) { // this is a reference to a specialized type return (CreateSpecializedTypeReference(api)); } else if (ValidDecoratedType.IsMatch(api)) { // this is a reference to a type that is decorated or is a template // process array, reference, and pointer decorations char lastCharacter = api[api.Length-1]; if (lastCharacter == ']') { // arrays int lastBracketPosition = api.LastIndexOf('['); int rank = api.Length - lastBracketPosition - 1; string elementApi = api.Substring(0, lastBracketPosition); TypeReference elementReference = CreateTypeReference(elementApi); return( new ArrayTypeReference(elementReference, rank) ); } else if (lastCharacter == '@') { // references string referedApi = api.Substring(0, api.Length - 1); TypeReference referedReference = CreateTypeReference(referedApi); return (new ReferenceTypeReference(referedReference)); } else if (lastCharacter == '*') { // pointers string pointedApi = api.Substring(0, api.Length - 1); TypeReference pointedReference = CreateTypeReference(pointedApi); return (new PointerTypeReference(pointedReference)); } // process templates if (api.StartsWith("T:``")) { int position = Convert.ToInt32(api.Substring(4)); if (genericTypeContext == null) { return (new NamedTemplateTypeReference("UMP")); } else { return (new IndexedTemplateTypeReference(genericTypeContext.Id, position)); } } else if (api.StartsWith("T:`")) { int position = Convert.ToInt32(api.Substring(3)); if (genericTypeContext == null) { return (new NamedTemplateTypeReference("UTP")); } else { return (new IndexedTemplateTypeReference(genericTypeContext.Id, position)); } } // we shouldn't get here, because one of those test should have been satisfied if the regex matched throw new InvalidOperationException("Could not parse valid type expression"); } else { return (null); } } private static SimpleTypeReference CreateSimpleTypeReference (string api) { return (new SimpleTypeReference(api)); } private static SpecializedTypeReference CreateSpecializedTypeReference (string api) { List specializations = new List(); string text = String.Copy(api); // at the moment we are only handling one specialization; need to iterate int specializationStart = text.IndexOf('{'); int specializationEnd = FindMatchingEndBracket(text, specializationStart); string list = text.Substring(specializationStart + 1, specializationEnd - specializationStart - 1); string[] types = SeperateTypes(list); string template = text.Substring(0, specializationStart) + String.Format("`{0}", types.Length); SimpleTypeReference templateReference = CreateSimpleTypeReference(template); TypeReference[] argumentReferences = new TypeReference[types.Length]; for (int i = 0; i < types.Length; i++) { argumentReferences[i] = CreateTypeReference(types[i]); } Specialization specialization = new Specialization(templateReference, argumentReferences); specializations.Add(specialization); // end iteration return (new SpecializedTypeReference(specializations.ToArray())); } //private static Regex tr = new Regex(@"^(M:([_a-zA-Z0-9]+\.)*([_a-zA-Z0-9]+(\{.+\})?\.)*[_a-zA-Z0-9]+(\{.+\})?\.([_a-zA-Z0-9]+(\{[^\.]+\})?#)*[_a-zA-Z0-9]+(``\d+)?(\((((([_a-zA-Z0-9]+\.)*([_a-zA-Z0-9]+(\{.+\})?\.)*[_a-zA-Z0-9]+(\{.+\})?)|(`\d+)|(``\d+))(@|\*|(\[\]))*,)*((([_a-zA-Z0-9]+\.)*([_a-zA-Z0-9]+(\{.+\})?\.)*[_a-zA-Z0-9]+(\{.+\})?)|(`\d+)|(``\d+))(@|\*|(\[\]))*\))?(~((([_a-zA-Z0-9]+\.)*([_a-zA-Z0-9]+(\{.+\})?\.)*[_a-zA-Z0-9]+(\{.+\})?)|(`\d+)|(``\d+))(@|\*|(\[\]))*)?)$", RegexOptions.Compiled); //private static Regex tr = new Regex(@"^(M:([_a-zA-Z0-9]+\.)*([_a-zA-Z0-9]+(\{[^:\(\)\s]+\})?\.)*[_a-zA-Z0-9]+(\{[^:\(\)\s]+\})?\.[_a-zA-Z0-9]+(``\d+)?(\((((([_a-zA-Z0-9]+\.)*([_a-zA-Z0-9]+(\{[^:\(\)\s]+\})?\.)*[_a-zA-Z0-9]+(\{[^:\(\)\s]+\})?)|(`\d+)|(``\d+))(@|\*|(\[\]))*,)*((([_a-zA-Z0-9]+\.)*([_a-zA-Z0-9]+(\{[^:\(\)\s]+\})?\.)*[_a-zA-Z0-9]+(\{[^:\(\)\s]+\})?)|(`\d+)|(``\d+))(@|\*|(\[\]))*\))?)$", RegexOptions.Compiled); private static Regex tr = new Regex(@"^(M:([_a-zA-Z0-9]+\.)*[_a-zA-Z0-9]+\.[_a-zA-Z0-9]+(``\d+)?(\((((([_a-zA-Z0-9]+\.)*[_a-zA-Z0-9]+)|(`\d+)|(``\d+))(@|\*|(\[\]))*,)*((([_a-zA-Z0-9]+\.)*[_a-zA-Z0-9]+)|(`\d+)|(``\d+))(@|\*|(\[\]))*\))?)$", RegexOptions.Compiled); public static MemberReference CreateMemberReference (string api) { //Console.WriteLine("Testing"); //Console.WriteLine(tr.ToString()); //Console.WriteLine(api); //Console.WriteLine(tr.IsMatch(api)); //Console.WriteLine("Tested"); if (ValidSimpleMember.IsMatch(api)) { //Console.WriteLine("Is valid simple member"); // this is just a normal member of a simple type return (new SimpleMemberReference(api)); } else if (ValidSpecializedMember.IsMatch(api)) { //Console.WriteLine("Is valid specialized member"); //Console.WriteLine("cer = {0}", api); // this is a member of a specialized type; we need to extract: // (1) the underlying specialized type, (2) the member name, (3) the arguments //Console.WriteLine("Extracting data"); // seperate the member prefix int colonPosition = api.IndexOf(':'); string prefix = api.Substring(0, colonPosition); string text = api.Substring(colonPosition + 1); // get the arguments string arguments = String.Empty; int startParenthesisPosition = text.IndexOf('('); if (startParenthesisPosition > 0) { int endParenthesisPosition = text.LastIndexOf(')'); arguments = text.Substring(startParenthesisPosition + 1, endParenthesisPosition - startParenthesisPosition - 1); text = text.Substring(0, startParenthesisPosition); } // seperate the type and member name int lastDotPosition; int firstHashPosition = text.IndexOf('#'); if (firstHashPosition > 0) { // if this is an EII, the boundry is at the last dot before the hash lastDotPosition = text.LastIndexOf('.', firstHashPosition); } else { // otherwise, the boundry is at the last dot lastDotPosition = text.LastIndexOf('.'); } string name = text.Substring(lastDotPosition+1); text = text.Substring(0, lastDotPosition); //Console.WriteLine("type = {0}", "T:" + text); // text now contains a specialized generic type; use it to create a reference SpecializedTypeReference type = CreateSpecializedTypeReference("T:" + text); // If there are no arguments... // we simply create a reference to a member whoose identifier we construct in the specialized type if (String.IsNullOrEmpty(arguments)) { string typeId = type.Specializations[type.Specializations.Length - 1].TemplateType.Id; string memberId = String.Format("{0}:{1}.{2}", prefix, typeId.Substring(2), name); SimpleMemberReference member = new SimpleMemberReference(memberId); return (new SpecializedMemberReference(member, type)); } // If there are arguments... life is not so simple. We can't be sure we can identify the // corresponding member of the template type because any particular type that appears in // the argument might have come from the template or it might have come from the specialization. // We need to create a special kind of reference to handle this situation. //Console.WriteLine("Specialized member with arguments '{0}'", api); string[] parameterTypeCers = SeperateTypes(arguments); TypeReference[] parameterTypes = new TypeReference[parameterTypeCers.Length]; for (int i = 0; i < parameterTypeCers.Length; i++) { parameterTypes[i] = CreateTypeReference(parameterTypeCers[i]); } return (new SpecializedMemberWithParametersReference(prefix, type, name, parameterTypes)); } else { //Console.WriteLine("No match"); return (null); //throw new InvalidOperationException(String.Format("Invalid member '{0}'", api)); } } // Template context logic private static SimpleTypeReference genericTypeContext = null; private static SimpleMemberReference genericMemberContext = null; public static void SetGenericContext (string cer) { //Console.WriteLine("context = {0}", cer); // re-set the context genericTypeContext = null; genericMemberContext = null; // get the new context Reference context = CreateReference(cer); if (context == null) return; // if it is a type context, set it to be the type context SimpleTypeReference typeContext = context as SimpleTypeReference; if (typeContext != null) { genericTypeContext = typeContext; return; } // if it is a member context, set it to be the member context and use it to obtain a type context, too SimpleMemberReference memberContext = context as SimpleMemberReference; if (memberContext != null) { genericMemberContext = memberContext; string typeId, memberName, arguments; DecomposeMemberIdentifier(memberContext.Id, out typeId, out memberName, out arguments); genericTypeContext = CreateSimpleTypeReference(typeId); return; } } public static SimpleTypeReference GenericContext { get { return (genericTypeContext); } } // Code entity reference validation logic // iterate -> specializedTypePattern -> decoratedTypePattern -> decoratedTypeListPattern // to get a patterns that enforce the contents of specialization brackets static TextReferenceUtilities () { string namePattern = @"[_a-zA-Z0-9]+"; // namespace patterns string namespacePattern = String.Format(@"({0}\.)*({0})?", namePattern); string optionalNamespacePattern = String.Format(@"({0}\.)*", namePattern); // type patterns string simpleTypePattern = String.Format(@"{0}({1}(`\d+)?\.)*{1}(`\d+)?", optionalNamespacePattern, namePattern); //string specializedTypePattern = String.Format(@"{0}({1}(\{{.+\}})?\.)*{1}(\{{.+\}})?", optionalNamespacePattern, namePattern); string specializedTypePattern = String.Format(@"({0}(\{{.+\}})?\.)*{0}(\{{.+\}})?", namePattern); string baseTypePattern = String.Format(@"({0})|({1})", simpleTypePattern, specializedTypePattern); string decoratedTypePattern = String.Format(@"(({0})|(`\d+)|(``\d+))(@|\*|(\[\]))*", specializedTypePattern); string decoratedTypeListPattern = String.Format(@"({0},)*{0}", decoratedTypePattern); string explicitInterfacePattern = String.Format(@"({0}(\{{[^\.]+\}})?#)*", namePattern); // members of non-specialized types string simpleFieldPattern = String.Format(@"{0}\.{1}", simpleTypePattern, namePattern); string simpleEventPattern = String.Format(@"{0}\.{1}{2}", simpleTypePattern, explicitInterfacePattern, namePattern); string simplePropertyPattern = String.Format(@"{0}\.{1}{2}(\({3}\))?", simpleTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern); string simpleMethodPattern = String.Format(@"{0}\.{1}{2}(``\d+)?(\({3}\))?(~{4})?", simpleTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern, decoratedTypePattern); string simpleConstructorPattern = String.Format(@"{0}\.#ctor(\({1}\))?", simpleTypePattern, decoratedTypeListPattern); string simpleOverloadPattern = String.Format(@"{0}\.{1}{2}", simpleTypePattern, explicitInterfacePattern, namePattern); string simpleConstructorOverloadPattern = String.Format(@"{0}\.#ctor", simpleTypePattern); // members of specialized types string specializedFieldPattern = String.Format(@"{0}\.{1}", specializedTypePattern, namePattern); string specializedEventPattern = String.Format(@"{0}\.{1}{2}", specializedTypePattern, explicitInterfacePattern, namePattern); string specializedPropertyPattern = String.Format(@"{0}\.{1}{2}(\({3}\))?", specializedTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern); string specializedMethodPattern = String.Format(@"{0}\.{1}{2}(``\d+)?(\({3}\))?(~{4})?", specializedTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern, decoratedTypePattern); string specializedOverloadPattern = String.Format(@"{0}\.{1}{2}", specializedTypePattern, explicitInterfacePattern, namePattern); // create regexes using this patterns ValidNamespace = new Regex( String.Format(@"^N:{0}$", namespacePattern), RegexOptions.Compiled ); ValidSimpleType = new Regex(String.Format(@"^T:{0}$", simpleTypePattern), RegexOptions.Compiled); ValidDecoratedType = new Regex(String.Format(@"^T:{0}$", decoratedTypePattern), RegexOptions.Compiled); ValidSpecializedType = new Regex(String.Format(@"^T:{0}$", specializedTypePattern), RegexOptions.Compiled); ValidSimpleMember = new Regex(String.Format(@"^((M:{0})|(M:{1})|(P:{2})|(F:{3})|(E:{4})|(Overload:{5})|(Overload:{6}))$", simpleMethodPattern, simpleConstructorPattern, simplePropertyPattern, simpleFieldPattern, simpleEventPattern, simpleOverloadPattern, simpleConstructorOverloadPattern)); ValidSpecializedMember = new Regex(String.Format(@"^((M:{0})|(P:{1})|(F:{2})|(E:{3})|(Overload:{4}))$", specializedMethodPattern, specializedPropertyPattern, specializedFieldPattern, specializedEventPattern, specializedOverloadPattern)); ValidTest = new Regex(String.Format(@"^M:{0}\.{1}$", simpleTypePattern, namePattern)); } private static Regex ValidNamespace; private static Regex ValidSimpleType; private static Regex ValidDecoratedType; private static Regex ValidSpecializedType; private static Regex ValidSimpleMember; private static Regex ValidSpecializedMember; private static Regex ValidTest; // Code entity reference string manipulation utilities internal static string[] SeperateTypes (string typelist) { List types = new List(); int start = 0; int specializationCount = 0; for (int index = 0; index < typelist.Length; index++) { switch (typelist[index]) { case '{': case '[': specializationCount++; break; case '}': case ']': specializationCount--; break; case ',': if (specializationCount == 0) { types.Add("T:" + typelist.Substring(start, index - start).Trim()); start = index + 1; } break; } } types.Add("T:" + typelist.Substring(start).Trim()); return (types.ToArray()); } internal static void DecomposeMemberIdentifier (string memberCer, out string typeCer, out string memberName, out string arguments) { // drop the member prefix int colonPosition = memberCer.IndexOf(':'); string text = memberCer.Substring(colonPosition + 1); // get the arguments arguments = String.Empty; int startParenthesisPosition = text.IndexOf('('); if (startParenthesisPosition > 0) { int endParenthesisPosition = text.LastIndexOf(')'); arguments = text.Substring(startParenthesisPosition + 1, endParenthesisPosition - startParenthesisPosition - 1); text = text.Substring(0, startParenthesisPosition); } // seperate the type and member name int lastDotPosition; int firstHashPosition = text.IndexOf('#'); if (firstHashPosition > 0) { // if this is an EII, the boundry is at the last dot before the hash lastDotPosition = text.LastIndexOf('.', firstHashPosition); } else { // otherwise, the boundry is at the last dot lastDotPosition = text.LastIndexOf('.'); } memberName = text.Substring(lastDotPosition + 1); typeCer = "T:" + text.Substring(0, lastDotPosition); } private static int FindMatchingEndBracket (string text, int position) { if (text == null) throw new ArgumentNullException("text"); if ((position < 0) || (position >= text.Length)) throw new ArgumentOutOfRangeException("position", String.Format("The position {0} is not within the given text string.", position)); if (text[position] != '{') throw new InvalidOperationException(String.Format("Position {0} of the string '{1}' does not contain and ending curly bracket.", position, text)); int count = 1; for (int index = position + 1; index < text.Length; index++) { if (text[index] == '{') { count++; } else if (text[index] == '}') { count--; } if (count == 0) return (index); } throw new FormatException("No opening brace matches the closing brace."); } // Writing link text for unresolved simple references internal static void WriteNamespaceReference (NamespaceReference space, DisplayOptions options, XmlWriter writer) { writer.WriteString(space.Id.Substring(2)); } internal static void WriteSimpleTypeReference (SimpleTypeReference type, DisplayOptions options, XmlWriter writer) { // this logic won't correctly deal with nested types, but type cer strings simply don't include that // infomation, so this is out best guess under the assumption of a non-nested type string cer = type.Id; // get the name string name; int lastDotPosition = cer.LastIndexOf('.'); if (lastDotPosition > 0) { // usually, the name will start after the last dot name = cer.Substring(lastDotPosition + 1); } else { // but if there is no dot, this is a type in the default namespace and the name is everything after the colon name = cer.Substring(2); } // remove any generic tics from the name int tickPosition = name.IndexOf('`'); if (tickPosition > 0) name = name.Substring(0, tickPosition); if ((options & DisplayOptions.ShowContainer) > 0) { // work out namespace } writer.WriteString(name); if ((options & DisplayOptions.ShowTemplates) > 0) { // work out templates } } internal static void WriteSimpleMemberReference (SimpleMemberReference member, DisplayOptions options, XmlWriter writer, LinkTextResolver resolver) { string cer = member.Id; string typeCer, memberName, arguments; DecomposeMemberIdentifier(cer, out typeCer, out memberName, out arguments); if ((options & DisplayOptions.ShowContainer) > 0) { SimpleTypeReference type = CreateSimpleTypeReference(typeCer); WriteSimpleTypeReference(type, options & ~DisplayOptions.ShowContainer, writer); } // change this so that we deal with EII names correctly, too writer.WriteString(memberName); if ((options & DisplayOptions.ShowParameters) > 0) { string[] parameterTypeCers; if (String.IsNullOrEmpty(arguments)) { Parameter[] parameters = new Parameter[0]; resolver.WriteMethodParameters(parameters, writer); } else { parameterTypeCers = SeperateTypes(arguments); Parameter[] parameters = new Parameter[parameterTypeCers.Length]; for (int i = 0; i < parameterTypeCers.Length; i++) { TypeReference parameterType = CreateTypeReference(parameterTypeCers[i]); if (parameterType == null) { parameterType = new NamedTemplateTypeReference("UAT"); } parameters[i] = new Parameter(String.Empty, parameterType); } resolver.WriteMethodParameters(parameters, writer); } } } } // ***** Link text writing logic ***** public class LinkTextResolver { public LinkTextResolver (TargetCollection targets) { this.targets = targets; } private TargetCollection targets; public void WriteTarget (Target target, DisplayOptions options, XmlWriter writer) { if (target == null) throw new ArgumentNullException("target"); if (writer == null) throw new ArgumentNullException("writer"); NamespaceTarget space = target as NamespaceTarget; if (space != null) { WriteNamespaceTarget(space, options, writer); return; } TypeTarget type = target as TypeTarget; if (type != null) { WriteTypeTarget(type, options, writer); return; } MemberTarget member = target as MemberTarget; if (member != null) { WriteMemberTarget(member, options, writer); return; } throw new InvalidOperationException(); } public void WriteNamespaceTarget (NamespaceTarget space, DisplayOptions options, XmlWriter writer) { if (space == null) throw new ArgumentNullException("target"); if (writer == null) throw new ArgumentNullException("writer"); writer.WriteString(space.Name); } public void WriteTypeTarget (TypeTarget type, DisplayOptions options, XmlWriter writer) { if (type == null) throw new ArgumentNullException("type"); if (writer == null) throw new ArgumentNullException("writer"); WriteTypeTarget(type, options, true, writer); } private void WriteTypeTarget (TypeTarget type, DisplayOptions options, bool showOuterType, XmlWriter writer) { // write namespace, if containers are requested if ((options & DisplayOptions.ShowContainer) > 0) { WriteNamespace(type.Namespace, DisplayOptions.Default, writer); WriteSeperator(writer); } // write outer type, if one exists if (showOuterType && (type.OuterType != null)) { WriteSimpleType(type.OuterType, DisplayOptions.Default, writer); WriteSeperator(writer); } // write the type name writer.WriteString(type.Name); // write if template parameters, if they exist and we are requested if ((options & DisplayOptions.ShowTemplates) > 0) { WriteTemplateParameters(type.Templates, writer); } } public void WriteMemberTarget (MemberTarget target, DisplayOptions options, XmlWriter writer) { WriteMemberTarget(target, options, writer, null); } private void WriteMemberTarget (MemberTarget target, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { if (target == null) throw new ArgumentNullException("target"); if (writer == null) throw new ArgumentNullException("writer"); if ((options & DisplayOptions.ShowContainer) > 0) { TypeReference type = target.Type; WriteType(type, options & ~DisplayOptions.ShowContainer, writer); MethodTarget methodTarget = target as MethodTarget; if (methodTarget != null) { if (methodTarget.conversionOperator) { writer.WriteString(" "); } else { WriteSeperator(writer); } } else { WriteSeperator(writer); } } // special logic for writing methods MethodTarget method = target as MethodTarget; if (method != null) { WriteMethod(method, options, writer, dictionary); return; } // special logic for writing properties PropertyTarget property = target as PropertyTarget; if (property != null) { WriteProperty(property, options, writer); return; } // special logic for writing constructors ConstructorTarget constructor = target as ConstructorTarget; if (constructor != null) { WriteConstructor(constructor, options, writer); return; } // special logic for writing events EventTarget trigger = target as EventTarget; if (trigger != null) { WriteEvent(trigger, options, writer); return; } // by default, just write name writer.WriteString(target.Name); } public void WriteReference (Reference reference, DisplayOptions options, XmlWriter writer) { if (reference == null) throw new ArgumentNullException("reference"); if (writer == null) throw new ArgumentNullException("writer"); NamespaceReference space = reference as NamespaceReference; if (space != null) { WriteNamespace(space, options, writer); return; } TypeReference type = reference as TypeReference; if (type != null) { WriteType(type, options, writer); return; } MemberReference member = reference as MemberReference; if (member != null) { WriteMember(member, options, writer); return; } ExtensionMethodReference extMethod = reference as ExtensionMethodReference; if (extMethod != null) { WriteExtensionMethod(extMethod, options, writer); return; } InvalidReference invalid = reference as InvalidReference; if (invalid != null) { WriteInvalid(invalid, options, writer); return; } throw new InvalidOperationException(); } public void WriteNamespace (NamespaceReference spaceReference, DisplayOptions options, XmlWriter writer) { if (spaceReference == null) throw new ArgumentNullException("spaceReference"); if (writer == null) throw new ArgumentNullException("writer"); NamespaceTarget spaceTarget = spaceReference.Resolve(targets) as NamespaceTarget; if (spaceTarget != null) { WriteNamespaceTarget(spaceTarget, options, writer); } else { TextReferenceUtilities.WriteNamespaceReference(spaceReference, options, writer); } } public void WriteType (TypeReference type, DisplayOptions options, XmlWriter writer) { WriteType(type, options, writer, null); } private void WriteType (TypeReference type, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { if (type == null) throw new ArgumentNullException("type"); if (writer == null) throw new ArgumentNullException("writer"); //Console.WriteLine("type {0}", type.GetType().FullName); SimpleTypeReference simple = type as SimpleTypeReference; if (simple != null) { WriteSimpleType(simple, options, writer); return; } SpecializedTypeReference specialized = type as SpecializedTypeReference; if (specialized != null) { WriteSpecializedType(specialized, options, writer); return; } ArrayTypeReference array = type as ArrayTypeReference; if (array != null) { WriteArrayType(array, options, writer, dictionary); return; } ReferenceTypeReference reference = type as ReferenceTypeReference; if (reference != null) { WriteReferenceType(reference, options, writer, dictionary); return; } PointerTypeReference pointer = type as PointerTypeReference; if (pointer != null) { WritePointerType(pointer, options, writer, dictionary); return; } TemplateTypeReference template = type as TemplateTypeReference; if (template != null) { WriteTemplateType(template, options, writer, dictionary); return; } throw new InvalidOperationException("Unknown type reference type"); } public void WriteSimpleType (SimpleTypeReference simple, DisplayOptions options, XmlWriter writer) { WriteSimpleType(simple, options, true, writer); } private void WriteSimpleType (SimpleTypeReference simple, DisplayOptions options, bool showOuterType, XmlWriter writer) { TypeTarget type = simple.Resolve(targets) as TypeTarget; if (type != null) { WriteTypeTarget(type, options, showOuterType, writer); } else { TextReferenceUtilities.WriteSimpleTypeReference(simple, options, writer); } } private static void WriteTemplateParameters (string[] templates, XmlWriter writer) { if (templates.Length == 0) return; writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("<"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString("(Of "); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("<"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString("("); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString("<'"); writer.WriteEndElement(); writer.WriteEndElement(); for (int i = 0; i < templates.Length; i++) { if (i > 0) writer.WriteString(", "); writer.WriteString(templates[i]); } writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString(">"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString(")"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString(">"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString(")"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString(">"); writer.WriteEndElement(); writer.WriteEndElement(); } private void WriteSpecializedType (SpecializedTypeReference special, DisplayOptions options, XmlWriter writer) { Specialization[] specializations = special.Specializations; for (int i = 0; i < specializations.Length; i++) { if (i == 0) { WriteSpecialization(specializations[0], options, writer); } else { WriteSeperator(writer); WriteSpecialization(specializations[i], options & ~DisplayOptions.ShowContainer, writer); } } } private void WriteSpecialization (Specialization specialization, DisplayOptions options, XmlWriter writer) { // write the type itself (without outer types, because those will be written be other calls to this routine) WriteSimpleType(specialization.TemplateType, (options & ~DisplayOptions.ShowTemplates), false, writer); // then write the template arguments WriteTemplateArguments(specialization.Arguments, writer); } private void WriteTemplateArguments (TypeReference[] specialization, XmlWriter writer) { if (specialization.Length == 0) return; writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("<"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString("(Of "); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("<"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString("<'"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString("("); writer.WriteEndElement(); writer.WriteEndElement(); for (int i = 0; i < specialization.Length; i++) { if (i > 0) writer.WriteString(", "); WriteType(specialization[i], DisplayOptions.Default, writer); } writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString(">"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString(")"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString(">"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString(">"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString(")"); writer.WriteEndElement(); writer.WriteEndElement(); } private void WriteArrayType (ArrayTypeReference reference, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); // C++ array notation (left) writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("array<"); writer.WriteEndElement(); writer.WriteEndElement(); // end of element // the underlying type WriteType(reference.ElementType, options, writer, dictionary); // C++ array notation (right) writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); if (reference.Rank > 1) { writer.WriteString("," + reference.Rank.ToString()); } writer.WriteString(">"); writer.WriteEndElement(); // C# array notation writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("["); for (int i = 1; i < reference.Rank; i++) { writer.WriteString(","); } writer.WriteString("]"); writer.WriteEndElement(); // VB array notation writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString("("); for (int i = 1; i < reference.Rank; i++) { writer.WriteString(","); } writer.WriteString(")"); writer.WriteEndElement(); // neutral array notation writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString("["); for (int i = 1; i < reference.Rank; i++) { writer.WriteString(","); } writer.WriteString("]"); writer.WriteEndElement(); // F# array notation writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString("["); for (int i = 1; i < reference.Rank; i++) { writer.WriteString(","); } writer.WriteString("]"); writer.WriteEndElement(); writer.WriteEndElement(); // end of element } private static void WriteSeperator (XmlWriter writer) { writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); // C# seperator writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("."); writer.WriteEndElement(); // VB seperator writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString("."); writer.WriteEndElement(); // C++ seperator writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("::"); writer.WriteEndElement(); // neutral seperator writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString("."); writer.WriteEndElement(); // F# seperator writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString("."); writer.WriteEndElement(); writer.WriteEndElement(); } private void WritePointerType (PointerTypeReference pointer, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { WriteType(pointer.PointedToType, options, writer, dictionary); writer.WriteString("*"); } private void WriteReferenceType (ReferenceTypeReference reference, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { WriteType(reference.ReferedToType, options, writer, dictionary); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); // add % in C++ writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("%"); writer.WriteEndElement(); writer.WriteEndElement(); } private void WriteTemplateType (TemplateTypeReference template, DisplayOptions options, XmlWriter writer) { WriteTemplateType(template, options, writer, null); } private void WriteTemplateType (TemplateTypeReference template, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { /* Console.WriteLine("template type {0}", template.GetType().FullName); if (dictionary != null) { foreach (IndexedTemplateTypeReference it in dictionary.Keys) { Console.WriteLine("index = {0}, api = {1} ({3}) -> {2}", it.Index, it.TemplateId, dictionary[it].GetType().FullName, it.GetHashCode()); } } */ // if we have the name, just write it NamedTemplateTypeReference namedTemplate = template as NamedTemplateTypeReference; if (namedTemplate != null) { writer.WriteString(namedTemplate.Name); return; } IndexedTemplateTypeReference indexedTemplate = template as IndexedTemplateTypeReference; if (indexedTemplate != null) { //Console.WriteLine("index = {0}, api = {1} ({2})", indexedTemplate.Index, indexedTemplate.TemplateId, indexedTemplate.GetHashCode()); //if ((dictionary != null) && (!dictionary.ContainsKey(indexedTemplate))) Console.WriteLine("not in dictionary"); if ((dictionary != null) && (dictionary.ContainsKey(indexedTemplate))) { //Console.WriteLine("Substituted {0}", dictionary[indexedTemplate].GetType().FullName); WriteType(dictionary[indexedTemplate], options, writer); } else { //Console.WriteLine("Getting name for id='{0}' and index={1}", indexedTemplate.TemplateId, indexedTemplate.Index); writer.WriteString(GetTemplateName(indexedTemplate.TemplateId, indexedTemplate.Index)); } return; } TypeTemplateTypeReference typeTemplate = template as TypeTemplateTypeReference; if (typeTemplate != null) { TypeReference value = null; if (dictionary != null) { IndexedTemplateTypeReference key = new IndexedTemplateTypeReference(typeTemplate.TemplateType.Id, typeTemplate.Position); if (dictionary.ContainsKey(key)) value = dictionary[key]; } if (value == null) { writer.WriteString(GetTypeTemplateName(typeTemplate.TemplateType, typeTemplate.Position)); } else { WriteType(value, options, writer); } return; } throw new InvalidOperationException(); } private string GetTemplateName (string templateId, int position) { Target target = targets[templateId]; if (target == null) { return ("UTT"); } else { TypeTarget type = target as TypeTarget; if (type != null) { string[] templates = type.Templates; if (templates.Length > position) { return(templates[position]); } else { return ("UTT"); } } MethodTarget method = target as MethodTarget; if (method != null) { string[] templates = method.Templates; if (templates.Length > position) { return (templates[position]); } else { return ("UTT"); } } return ("UTT"); } } private string GetTypeTemplateName (SimpleTypeReference type, int position) { TypeTarget target = type.Resolve(targets) as TypeTarget; if (target != null) { string[] templates = target.Templates; if (templates.Length > position) { return (templates[position]); } else if (target.OuterType != null) { return (GetTypeTemplateName(target.OuterType, position)); } else { return ("UTT"); } } else { throw new InvalidOperationException(String.Format("Unknown type reference '{0}'", type.Id)); } } public void WriteExtensionMethod(ExtensionMethodReference extMethod, DisplayOptions options, XmlWriter writer) { if (extMethod == null) throw new ArgumentNullException("extMethod"); if (writer == null) throw new ArgumentNullException("writer"); // write the unqualified method name writer.WriteString(extMethod.Name); // if this is a generic method, write any template params or args if (extMethod.TemplateArgs != null && extMethod.TemplateArgs.Length > 0) { WriteTemplateArguments(extMethod.TemplateArgs, writer); } // write parameters if ((options & DisplayOptions.ShowParameters) > 0) { Parameter[] parameters = extMethod.Parameters; WriteMethodParameters(extMethod.Parameters, writer); } } public void WriteMember (MemberReference member, DisplayOptions options, XmlWriter writer) { if (member == null) throw new ArgumentNullException("member"); if (writer == null) throw new ArgumentNullException("writer"); SimpleMemberReference simple = member as SimpleMemberReference; if (simple != null) { WriteSimpleMember(simple, options, writer); return; } SpecializedMemberReference special = member as SpecializedMemberReference; if (special != null) { WriteSpecializedMember(special, options, writer); return; } SpecializedMemberWithParametersReference ugly = member as SpecializedMemberWithParametersReference; if (ugly != null) { WriteSpecializedMemberWithParameters(ugly, options, writer); return; } throw new InvalidOperationException(); } private void WriteSpecializedMember (SpecializedMemberReference member, DisplayOptions options, XmlWriter writer) { if ((options & DisplayOptions.ShowContainer) > 0) { WriteType(member.SpecializedType, options & ~DisplayOptions.ShowContainer, writer); WriteSeperator(writer); } Dictionary dictionary = member.SpecializedType.GetSpecializationDictionary(); WriteSimpleMember(member.TemplateMember, options & ~DisplayOptions.ShowContainer, writer, dictionary); } private void WriteSimpleMember (SimpleMemberReference member, DisplayOptions options, XmlWriter writer) { WriteSimpleMember(member, options, writer, null); } private void WriteSimpleMember (SimpleMemberReference member, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { MemberTarget target = member.Resolve(targets) as MemberTarget; if (target != null) { WriteMemberTarget(target, options, writer, dictionary); } else { TextReferenceUtilities.WriteSimpleMemberReference(member, options, writer, this); //throw new InvalidOperationException(String.Format("Unknown member target '{0}'", member.Id)); } } private void WriteProcedureName (ProcedureTarget target, DisplayOptions options, XmlWriter writer) { MemberReference implements = target.ExplicitlyImplements; if (implements == null) { if (target.conversionOperator) { WriteConversionOperator(target, writer); } else { writer.WriteString(target.Name); } } else { WriteMember(implements, DisplayOptions.ShowContainer, writer); } } private void WriteMethod (MethodTarget target, DisplayOptions options, XmlWriter writer, Dictionary dictionary) { WriteProcedureName(target, options, writer); if ((options & DisplayOptions.ShowTemplates) > 0) { // if this is a generic method, write any template params or args if (target.TemplateArgs != null && target.TemplateArgs.Length > 0) { WriteTemplateArguments(target.TemplateArgs, writer); } } if ((options & DisplayOptions.ShowParameters) > 0) { Parameter[] parameters = target.Parameters; if (target.ConversionOperator) { TypeReference returns = target.returnType; WriteConversionOperatorParameters(parameters, returns, writer, dictionary); } else { WriteMethodParameters(parameters, writer, dictionary); } } } private void WriteConversionOperator(ProcedureTarget target, XmlWriter writer) { writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString(target.Name); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); if (target.name == "Explicit") { writer.WriteString("Narrowing"); } else if (target.name == "Implicit") { writer.WriteString("Widening"); } writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString(target.name); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString(target.name); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString(target.name); writer.WriteEndElement(); writer.WriteEndElement(); } internal void WriteMethodParameters (Parameter[] parameters, XmlWriter writer) { WriteMethodParameters(parameters, writer, null); } private void WriteMethodParameters (Parameter[] parameters, XmlWriter writer, Dictionary dictionary) { if (parameters.Length > 0) { writer.WriteString("("); // show parameters // we need to deal with type template substitutions! for (int i = 0; i < parameters.Length; i++) { if (i > 0) writer.WriteString(", "); WriteType(parameters[i].Type, DisplayOptions.Default, writer, dictionary); } writer.WriteString(")"); } else { writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); // when there are no parameters, VB shows no parenthesis writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteEndElement(); } } private void WriteConversionOperatorParameters(Parameter[] parameters, TypeReference returns, XmlWriter writer, Dictionary dictionary) { if (parameters.Length > 0 || returns != null) writer.WriteString("("); if (parameters.Length > 0) WriteType(parameters[0].Type, DisplayOptions.Default, writer, dictionary); if (parameters.Length > 0 && returns != null) writer.WriteString(" to "); if (returns != null) WriteType(returns, DisplayOptions.Default, writer, dictionary); if (parameters.Length > 0 || returns != null) writer.WriteString(")"); if (parameters.Length == 0 && returns == null) { writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); // when there are no parameters, VB shows no parenthesis writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString("()"); writer.WriteEndElement(); writer.WriteEndElement(); } } private void WriteProperty (PropertyTarget target, DisplayOptions options, XmlWriter writer) { WriteProcedureName(target, options, writer); if ((options & DisplayOptions.ShowParameters) > 0) { Parameter[] parameters = target.Parameters; // VB only shows parenthesis when there are parameters if (parameters.Length > 0) { writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("["); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString("("); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("["); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString("("); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString(" "); writer.WriteEndElement(); writer.WriteEndElement(); // show parameters // we need to deal with type template substitutions! for (int i = 0; i < parameters.Length; i++) { if (i > 0) writer.WriteString(", "); WriteType(parameters[i].Type, DisplayOptions.Default, writer); } writer.WriteStartElement("span"); writer.WriteAttributeString("class", "languageSpecificText"); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cs"); writer.WriteString("]"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "vb"); writer.WriteString(")"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "cpp"); writer.WriteString("]"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nu"); writer.WriteString(")"); writer.WriteEndElement(); writer.WriteStartElement("span"); writer.WriteAttributeString("class", "fs"); writer.WriteString(" "); writer.WriteEndElement(); writer.WriteEndElement(); } } } private void WriteEvent (EventTarget trigger, DisplayOptions options, XmlWriter writer) { WriteProcedureName(trigger, options, writer); } private void WriteConstructor (ConstructorTarget constructor, DisplayOptions options, XmlWriter writer) { WriteType(constructor.Type, options & ~DisplayOptions.ShowContainer, writer); if ((options & DisplayOptions.ShowParameters) > 0) { Parameter[] parameters = constructor.Parameters; WriteMethodParameters(parameters, writer); } } private void WriteSpecializedMemberWithParameters (SpecializedMemberWithParametersReference ugly, DisplayOptions options, XmlWriter writer) { if ((options & DisplayOptions.ShowContainer) > 0) { WriteSpecializedType(ugly.SpecializedType, options & ~DisplayOptions.ShowContainer, writer); WriteSeperator(writer); } writer.WriteString(ugly.MemberName); if ((options & DisplayOptions.ShowParameters) > 0) { writer.WriteString("("); TypeReference[] parameterTypes = ugly.ParameterTypes; for (int i = 0; i < parameterTypes.Length; i++) { //Console.WriteLine("i = {0}, type = {1}", i, parameterTypes[i].GetType().FullName); if (i > 0) writer.WriteString(", "); WriteType(parameterTypes[i], DisplayOptions.Default, writer); } writer.WriteString(")"); } } private static void WriteInvalid (InvalidReference invalid, DisplayOptions options, XmlWriter writer) { writer.WriteString("[" + invalid.Id + "]"); } } [Flags] public enum DisplayOptions { ShowContainer = 1, ShowTemplates = 2, ShowParameters = 4, Default = 6 } public enum LinkType2 { None, Self, Local, Index, LocalOrIndex, Msdn, Id } }