static int GenericTypeBacktickSearch(IList <Node> childNodes, EcmaDesc desc) { /* Our strategy is to search for the non-generic variant of the type * (which in most case should fail) and then use the closest index * to linearily search for the generic variant with the right generic arg number */ var searchNode = new Node() { Caption = desc.TypeName }; int index = childNodes.BinarySearch(searchNode, EcmaTypeNodeComparer.Instance); // Place the index in the right start position if (index < 0) { index = ~index; } for (int i = index; i < childNodes.Count; i++) { var currentNode = childNodes[i]; // Find the index of the generic argument list int genericIndex = currentNode.Caption.IndexOf('<'); // If we are not on the same base type name anymore, there is no point int captionSlice = genericIndex != -1 ? genericIndex : currentNode.Caption.LastIndexOf(' '); if (string.Compare(searchNode.Caption, 0, currentNode.Caption, 0, Math.Max(captionSlice, searchNode.Caption.Length), StringComparison.Ordinal) != 0) { break; } var numGenerics = CountTypeGenericArguments(currentNode.Caption, genericIndex); if (numGenerics == desc.GenericTypeArguments.Count) { // Simple comparison if we are not looking for an inner type if (desc.NestedType == null) { return(i); } // If more complicated, we fallback to using EcmaUrlParser var caption = currentNode.Caption; caption = "T:" + caption.Substring(0, caption.LastIndexOf(' ')).Replace('.', '+'); EcmaDesc otherDesc; var parser = new EcmaUrlParser(); if (parser.TryParse(caption, out otherDesc) && desc.NestedType.Equals(otherDesc.NestedType)) { return(i); } } } return(-1); }
void RewriteCrefsIfNecessary (XDocument doc, string path) { // we also have to rewrite crefs var sees = doc.Descendants ().Where (d => d.Name.LocalName == "see").ToArray (); foreach (var see in sees) { var cref = see.Attribute ("cref"); if (cref == null) { continue; } EcmaUrlParser parser = new EcmaUrlParser (); EcmaDesc reference; if (!parser.TryParse (cref.Value, out reference)) { continue; } if ((new EcmaDesc.Kind[] { EcmaDesc.Kind.Constructor, EcmaDesc.Kind.Method }).Any (k => k == reference.DescKind)) { string ns = reference.Namespace; string type = reference.TypeName; string memberName = reference.MemberName; if (reference.MemberArguments != null) { XDocument refDoc = FindReferenceDoc (path, doc, ns, type); if (refDoc == null) { continue; } // look in the refDoc for the memberName, and match on parameters and # of type parameters var overloads = refDoc.XPathSelectElements ("//Member[@MemberName='" + memberName + "']").ToArray (); // Do some initial filtering to find members that could potentially match (based on parameter and typeparam counts) var members = overloads.Where (e => reference.MemberArgumentsCount == e.XPathSelectElements ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']").Count () && reference.GenericMemberArgumentsCount == e.XPathSelectElements ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']").Count ()).Select (m => new { Node = m, AllParameters = m.XPathSelectElements ("Parameters/Parameter").ToArray (), Parameters = m.XPathSelectElements ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']").ToArray (), NewParameters = m.XPathSelectElements ("Parameters/Parameter[@apistyle='unified']").ToArray () }).ToArray (); // now find the member that matches on types var member = members.FirstOrDefault (m => reference.MemberArguments.All (r => m.Parameters.Any (mp => mp.Attribute ("Type").Value.Contains (r.TypeName)))); if (member == null || member.NewParameters.Length == 0) continue; foreach (var arg in reference.MemberArguments) { // find the "classic" parameter var oldParam = member.Parameters.First (p => p.Attribute ("Type").Value.Contains (arg.TypeName)); var newParam = member.NewParameters.FirstOrDefault (p => oldParam.Attribute ("Name").Value == p.Attribute ("Name").Value); if (newParam != null) { // this means there was a change made, and we should try to convert this cref arg.TypeName = NativeTypeManager.ConvertToNativeType (arg.TypeName); } } var rewrittenReference = reference.ToEcmaCref (); Console.WriteLine ("From {0} to {1}", cref.Value, rewrittenReference); cref.Value = rewrittenReference; } } } }
void RewriteCrefsIfNecessary(XDocument doc, string path) { // we also have to rewrite crefs var sees = doc.Descendants().Where(d => d.Name.LocalName == "see").ToArray(); foreach (var see in sees) { var cref = see.Attribute("cref"); if (cref == null) { continue; } EcmaUrlParser parser = new EcmaUrlParser(); EcmaDesc reference; if (!parser.TryParse(cref.Value, out reference)) { continue; } if ((new EcmaDesc.Kind[] { EcmaDesc.Kind.Constructor, EcmaDesc.Kind.Method }).Any(k => k == reference.DescKind)) { string ns = reference.Namespace; string type = reference.TypeName; string memberName = reference.MemberName; if (reference.MemberArguments != null) { XDocument refDoc = FindReferenceDoc(path, doc, ns, type); if (refDoc == null) { continue; } // look in the refDoc for the memberName, and match on parameters and # of type parameters var overloads = refDoc.XPathSelectElements("//Member[@MemberName='" + memberName + "']").ToArray(); // Do some initial filtering to find members that could potentially match (based on parameter and typeparam counts) var members = overloads.Where(e => reference.MemberArgumentsCount == e.XPathSelectElements("Parameters/Parameter[not(@apistyle) or @apistyle='classic']").Count() && reference.GenericMemberArgumentsCount == e.XPathSelectElements("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']").Count()).Select(m => new { Node = m, AllParameters = m.XPathSelectElements("Parameters/Parameter").ToArray(), Parameters = m.XPathSelectElements("Parameters/Parameter[not(@apistyle) or @apistyle='classic']").ToArray(), NewParameters = m.XPathSelectElements("Parameters/Parameter[@apistyle='unified']").ToArray() }).ToArray(); // now find the member that matches on types var member = members.FirstOrDefault(m => reference.MemberArguments.All(r => m.Parameters.Any(mp => mp.Attribute("Type").Value.Contains(r.TypeName)))); if (member == null || member.NewParameters.Length == 0) { continue; } foreach (var arg in reference.MemberArguments) { // find the "classic" parameter var oldParam = member.Parameters.First(p => p.Attribute("Type").Value.Contains(arg.TypeName)); var newParam = member.NewParameters.FirstOrDefault(p => oldParam.Attribute("Name").Value == p.Attribute("Name").Value); if (newParam != null) { // this means there was a change made, and we should try to convert this cref arg.TypeName = NativeTypeManager.ConvertToNativeType(arg.TypeName); } } var rewrittenReference = reference.ToEcmaCref(); Console.WriteLine("From {0} to {1}", cref.Value, rewrittenReference); cref.Value = rewrittenReference; } } } }
public static Node MatchNodeWithEcmaUrl(string url, Tree tree) { Node result = null; EcmaDesc desc; if (!parser.TryParse(url, out desc)) { return(null); } // Namespace search Node currentNode = tree.RootNode; Node searchNode = new Node() { Caption = desc.Namespace }; int index = currentNode.ChildNodes.BinarySearch(searchNode, EcmaGenericNodeComparer.Instance); if (index >= 0) { result = currentNode.ChildNodes[index]; } if (desc.DescKind == EcmaDesc.Kind.Namespace || index < 0) { return(result); } // Type search currentNode = result; result = null; searchNode.Caption = desc.ToCompleteTypeName(); if (!desc.GenericTypeArgumentsIsNumeric) { index = currentNode.ChildNodes.BinarySearch(searchNode, EcmaTypeNodeComparer.Instance); } else { index = GenericTypeBacktickSearch(currentNode.ChildNodes, desc); } if (index >= 0) { result = currentNode.ChildNodes[index]; } if ((desc.DescKind == EcmaDesc.Kind.Type && !desc.IsEtc) || index < 0) { return(result); } // Member selection currentNode = result; result = null; var caption = desc.IsEtc ? EtcKindToCaption(desc.Etc) : MemberKindToCaption(desc.DescKind); currentNode = FindNodeForCaption(currentNode.ChildNodes, caption); if (currentNode == null || (desc.IsEtc && desc.DescKind == EcmaDesc.Kind.Type && string.IsNullOrEmpty(desc.EtcFilter))) { return(currentNode); } // Member search result = null; var format = desc.DescKind == EcmaDesc.Kind.Constructor ? EcmaDesc.Format.WithArgs : EcmaDesc.Format.WithoutArgs; searchNode.Caption = desc.ToCompleteMemberName(format); index = currentNode.ChildNodes.BinarySearch(searchNode, EcmaGenericNodeComparer.Instance); if (index < 0) { return(null); } result = currentNode.ChildNodes[index]; if (result.ChildNodes.Count == 0 || desc.IsEtc) { return(result); } // Overloads search currentNode = result; searchNode.Caption = desc.ToCompleteMemberName(EcmaDesc.Format.WithArgs); index = currentNode.ChildNodes.BinarySearch(searchNode, EcmaGenericNodeComparer.Instance); if (index < 0) { return(result); } result = result.ChildNodes[index]; return(result); }
public Node InternalMatchNode(string url) { Node result = null; //Console.WriteLine ("Ecma-hs MatchNode with {0}", url); EcmaDesc desc; if (!parser.TryParse(url, out desc)) { return(null); } //Console.WriteLine ("EcmaDesc: {0}", desc.ToString ()); // Namespace search Node currentNode = Tree.RootNode; Node searchNode = new Node() { Caption = desc.Namespace }; int index = currentNode.Nodes.BinarySearch(searchNode, EcmaGenericNodeComparer.Instance); if (index >= 0) { result = currentNode.Nodes[index]; } if (desc.DescKind == EcmaDesc.Kind.Namespace || index < 0) { return(result); } //Console.WriteLine ("Post NS"); // Type search currentNode = result; result = null; searchNode.Caption = desc.ToCompleteTypeName(); //Console.WriteLine ("Type search: {0}", searchNode.Caption); index = currentNode.Nodes.BinarySearch(searchNode, EcmaTypeNodeComparer.Instance); if (index >= 0) { result = currentNode.Nodes[index]; } if ((desc.DescKind == EcmaDesc.Kind.Type && !desc.IsEtc) || index < 0) { return(result); } //Console.WriteLine ("Post Type"); // Member selection currentNode = result; result = null; var caption = desc.IsEtc ? EtcKindToCaption(desc.Etc) : MemberKindToCaption(desc.DescKind); currentNode = FindNodeForCaption(currentNode.Nodes, caption); if (currentNode == null || (desc.IsEtc && desc.DescKind == EcmaDesc.Kind.Type && string.IsNullOrEmpty(desc.EtcFilter))) { return(currentNode); } //Console.WriteLine ("Post caption"); // Member search result = null; var format = desc.DescKind == EcmaDesc.Kind.Constructor ? EcmaDesc.Format.WithArgs : EcmaDesc.Format.WithoutArgs; searchNode.Caption = desc.ToCompleteMemberName(format); //Console.WriteLine ("Member caption {0}", searchNode.Caption); index = currentNode.Nodes.BinarySearch(searchNode, EcmaGenericNodeComparer.Instance); if (index < 0) { //foreach (var n in currentNode.Nodes) // Console.WriteLine (n.Caption); return(null); } result = currentNode.Nodes[index]; //Console.WriteLine ("Member result: {0} {1} {2}", result.Caption, result.Nodes.Count, desc.IsEtc); if (result.Nodes.Count == 0 || desc.IsEtc) { return(result); } //Console.WriteLine ("Post member"); // Overloads search currentNode = result; searchNode.Caption = desc.ToCompleteMemberName(EcmaDesc.Format.WithArgs); //Console.WriteLine ("Overload caption: {0}", searchNode.Caption); //Console.WriteLine ("Candidates: {0}", string.Join (", ", currentNode.Nodes.Select (n => n.Caption))); index = currentNode.Nodes.BinarySearch(searchNode, EcmaGenericNodeComparer.Instance); if (index < 0) { return(result); } result = result.Nodes[index]; return(result); }