Xamarin Public Jenkins (auto-signing) 95fdb59ea6 Imported Upstream version 6.6.0.89
Former-commit-id: b39a328747c2f3414dc52e009fb6f0aa80ca2492
2019-09-24 08:53:40 +00:00

201 lines
8.0 KiB
C#

using System;
using System.Xml;
using System.Linq;
using StringList = System.Collections.Generic.List<string>;
using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
using Mono.Documentation.Updater.Frameworks;
namespace Mono.Documentation.Updater
{
public class DocumentationMember
{
public StringToStringMap MemberSignatures = new StringToStringMap ();
public string ReturnType;
public StringList Parameters;
public StringList TypeParameters;
public string MemberName;
public string MemberType;
/// <summary>Removes modreq and modopt from ReturnType, Parameters, and TypeParameters</summary>
private void CleanTypes ()
{
ReturnType = MemberFormatter.RemoveMod (ReturnType);
MemberType = MemberFormatter.RemoveMod (MemberType);
if (Parameters != null)
{
for (var i = 0; i < Parameters.Count; i++)
Parameters[i] = MemberFormatter.RemoveMod (Parameters[i]);
}
if (TypeParameters != null)
{
for (var i = 0; i < TypeParameters.Count; i++)
TypeParameters[i] = MemberFormatter.RemoveMod (TypeParameters[i]);
}
}
public DocumentationMember (XmlReader reader)
{
MemberName = reader.GetAttribute ("MemberName");
int depth = reader.Depth;
bool go = true;
StringList p = new StringList ();
StringList tp = new StringList ();
do
{
if (reader.NodeType != XmlNodeType.Element)
continue;
bool shouldUse = true;
try
{
string apistyle = reader.GetAttribute ("apistyle");
shouldUse = string.IsNullOrWhiteSpace (apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
}
catch (Exception) { }
switch (reader.Name)
{
case "MemberSignature":
if (shouldUse)
{
MemberSignatures[reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
}
break;
case "MemberType":
MemberType = reader.ReadElementString ();
break;
case "ReturnType":
if (reader.Depth == depth + 2 && shouldUse)
ReturnType = reader.ReadElementString ();
break;
case "Parameter":
if (reader.Depth == depth + 2 && shouldUse)
{
var ptype = reader.GetAttribute ("Type");
var reftypeAttribute = reader.GetAttribute ("RefType");
if (!ptype.EndsWith("&", StringComparison.Ordinal) &&
(reftypeAttribute == "out" || reftypeAttribute == "ref"))
{
// let's add the `&` back, for comparisons
ptype += '&';
}
p.Add (ptype);
}
break;
case "TypeParameter":
if (reader.Depth == depth + 2 && shouldUse)
tp.Add (reader.GetAttribute ("Name"));
break;
case "Docs":
if (reader.Depth == depth + 1)
go = false;
break;
}
} while (go && reader.Read () && reader.Depth >= depth);
if (p.Count > 0)
{
Parameters = p;
}
if (tp.Count > 0)
{
TypeParameters = tp;
}
else
{
DiscernTypeParameters ();
}
CleanTypes ();
}
public DocumentationMember (XmlNode node, FrameworkTypeEntry typeEntry)
{
MemberName = node.Attributes["MemberName"].Value;
foreach (XmlNode n in node.SelectNodes ("MemberSignature"))
{
XmlAttribute l = n.Attributes["Language"];
XmlAttribute v = n.Attributes["Value"];
XmlAttribute apistyle = n.Attributes["apistyle"];
bool shouldUse = apistyle == null || apistyle.Value == "classic";
if (l != null && v != null && shouldUse)
MemberSignatures[l.Value] = v.Value;
}
MemberType = node.SelectSingleNode ("MemberType").InnerText;
XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
if (rt != null)
ReturnType = rt.InnerText;
var p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']").Cast<XmlElement>().ToArray ();
if (p.Length > 0)
{
Func<string, string, string> processType = (reftype, typename) =>
!typename.EndsWith("&", StringComparison.Ordinal) && (reftype == "ref" || reftype == "out") ? typename + '&' : typename;
if (p.Any (para => para.HasAttribute ("Index")))
{
var pgroup = p
.Select (para => new
{
Index = para.GetAttribute ("Index"),
Type = para.GetAttribute ("Type"),
RefType = para.GetAttribute ("RefType")
})
.GroupBy (para => new
{
para.Index,
Type = processType (para.RefType, para.Type)
})
.ToArray ();
Parameters = new StringList (pgroup.Length);
Parameters.AddRange (pgroup.Select (pg => pg.Key.Type));
}
else {
var ptypes = p
.Select (para => new
{
Type = para.GetAttribute ("Type"),
RefType = para.GetAttribute ("RefType")
})
.Select (para => processType(para.RefType, para.Type))
.ToArray ();
Parameters = new StringList (ptypes);
}
}
XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
if (tp.Count > 0)
{
TypeParameters = new StringList (tp.Count);
for (int i = 0; i < tp.Count; ++i)
TypeParameters.Add (tp[i].Attributes["Name"].Value);
}
else
{
DiscernTypeParameters ();
}
CleanTypes ();
}
void DiscernTypeParameters ()
{
// see if we can discern the param list from the name
if (MemberName.Contains ("<") && MemberName.EndsWith (">"))
{
var starti = MemberName.IndexOf ("<") + 1;
var endi = MemberName.LastIndexOf (">");
var paramlist = MemberName.Substring (starti, endi - starti);
var tparams = paramlist.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
TypeParameters = new StringList (tparams);
}
}
public override string ToString ()
{
if (MemberSignatures.Count > 0)
return MemberSignatures.Values.First ();
else
return $"{MemberType}{ReturnType} {MemberName}<{TypeParameters.Count}> ({Parameters.Count})";
}
}
}