Imported Upstream version 6.6.0.89

Former-commit-id: b39a328747c2f3414dc52e009fb6f0aa80ca2492
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-09-24 08:53:40 +00:00
parent cf815e07e0
commit 95fdb59ea6
2556 changed files with 138145 additions and 47453 deletions

View File

@@ -0,0 +1,228 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using Mono.Options;
namespace DocStat
{
public static class CommandUtils
{
public static List<string> ProcessFileArgs(IEnumerable<string> args,
ref string rootdir,
ref string omitlist,
ref string processlist,
ref string pattern)
{
// Take that, compiler!
string _rd = "";
string _ol = "";
string _pl = "";
string _pa = "";
List<string> extras;
var opt = new OptionSet {
{ "d|dir|directory=",
(string d) => _rd = d },
// Provide a file that contains a list of files to omit. User may use more than one -o
{ "e|exceptlist=",
(m) => _ol = m },
// List a file that contains a list of files to process
{ "p|processlist=",
(f) => _pl = f},
{ "n|namematches=",
(n) => _pa = n }
};
extras = opt.Parse(args);
// And that!
rootdir = String.IsNullOrEmpty(_rd) ? rootdir : _rd;
omitlist = String.IsNullOrEmpty(_ol) ? omitlist : _ol;
processlist =String.IsNullOrEmpty(_pl) ? processlist : _pl;
pattern = String.IsNullOrEmpty(_pa) ? pattern : _pa;
return extras;
}
public static IEnumerable<string> GetFileList(string processListFileName,
string omitListFileName,
string rootDir,
string pattern,
bool recurse = true,
bool skipNsAndIndex = true)
{
IEnumerable<string> toProcess = Enumerable.Empty<string>();
// Build search predicates
Func<string, bool> fileMatches;
Func<string, bool> omitFile;
if (!String.IsNullOrEmpty(pattern))
{
Regex fm = new Regex(pattern);
fileMatches = fm.IsMatch;
}
else
{
fileMatches = (string s) => true;
}
if (String.IsNullOrEmpty(omitListFileName))
{
omitFile = (string s) => false;
}
else
{
if (File.Exists(omitListFileName))
{
IEnumerable<string> toOmit = FileNamesIn(omitListFileName);
omitFile = toOmit.Contains;
}
else
throw new ArgumentException("Omission file does not exist: " + omitListFileName);
}
// Process any user-supplied file lists
if (!String.IsNullOrEmpty(processListFileName))
{
if (File.Exists(processListFileName))
{
toProcess = toProcess.Union(
FileNamesIn(processListFileName)
.Where((p) => fileMatches(Path.GetFileName(p)) && !omitFile(p)));
}
else
{
throw new FileNotFoundException("Process list file does not exist: " + processListFileName);
}
}
if (String.IsNullOrEmpty(rootDir) && !String.IsNullOrEmpty(processListFileName))
return toProcess; // they gave us a list only, so they're happy
if (String.IsNullOrEmpty(rootDir))
rootDir = Directory.GetCurrentDirectory(); // no list or rootdir, so they want the default
if (!Directory.Exists(rootDir)) //They gave us something, but it was a boo-boo
throw new ArgumentException("The provided root directory was required and does not exist: " + rootDir);
// We have a good root directory, and we want to use it.
Func<string, bool> isNsOrIndex;
if (skipNsAndIndex)
{
isNsOrIndex = (string fName) => {
string barename = Path.GetFileName(fName);
return barename.StartsWith("ns-") || barename.StartsWith("index");
};
}
else
{
isNsOrIndex = (string arg) => false;
}
return toProcess.Union(Directory.GetFiles(rootDir,
"*.xml",
recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
.Where(p => fileMatches(Path.GetFileName(p)) && !omitFile(p) && !isNsOrIndex(p));
}
public static void ThrowOnFiniteExtras(List<string> extras)
{
if (extras.Count > 1)
{
StringBuilder s = new StringBuilder("The following options were not recoginzed:\n");
List<string> sl = new List<string>();
for (int i = 1; i < extras.Count; i++)
{
sl.Add(extras[i]);
}
s.Append(String.Join("\n", sl));
throw new Exception(s.ToString());
}
}
public static XmlDocument ToXmlDocument(XDocument xDocument)
{
var xmlDocument = new XmlDocument();
using (var reader = xDocument.CreateReader())
{
xmlDocument.Load(reader);
}
var xDeclaration = xDocument.Declaration;
if (xDeclaration != null)
{
var xmlDeclaration = xmlDocument.CreateXmlDeclaration(
xDeclaration.Version,
xDeclaration.Encoding,
xDeclaration.Standalone);
xmlDocument.InsertBefore(xmlDeclaration, xmlDocument.FirstChild);
}
return xmlDocument;
}
public static void WriteXDocument(XDocument xdoc, string file)
{
if (!File.Exists(file))
throw new FileNotFoundException("File not found: " + file);
XmlDocument xmldoc = CommandUtils.ToXmlDocument(xdoc);
TextWriter xdout = new StreamWriter(file);
// Write back
XmlTextWriter writer = new XmlTextWriter(xdout)
{
Formatting = Formatting.Indented,
IndentChar = ' ',
Indentation = 2
};
xmldoc.WriteTo(writer);
xdout.WriteLine();
xdout.Flush();
}
private static IEnumerable<string> FileNamesIn(string fileListPath)
{
return File.ReadLines(fileListPath)
.Where(s =>
!String.IsNullOrEmpty(s) &&
Uri.IsWellFormedUriString(s, UriKind.Absolute) &&
File.Exists(s)
);
}
public static string CSVFormatString(int numColumns, int[] order = null)
{
if (null != order && order.Length != numColumns)
throw new ArgumentException(String.Format("Column order array had {0} entries, but {1} columns are needed.",
order.Length.ToString(),
numColumns.ToString()));
Func<int, int> indexFor = null;
if (null != order)
{
indexFor = (int i) => order[i];
}
else
{
indexFor = (int i) => i;
}
string[] cols = new string[numColumns];
for (int i = 0; i < numColumns; i++)
cols[i] = "\"{" + indexFor(i) + "}\"";
return String.Join(",", cols);
}
}
}

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{EF899D5E-28F7-4CEE-A47A-80C4B4995B81}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>DocStat</RootNamespace>
<AssemblyName>DocStat</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Compile Include="apistat.cs" />
<Compile Include="CommandUtils.cs" />
<Compile Include="comparefix.cs" />
<Compile Include="internalize.cs" />
<Compile Include="obsolete.cs" />
<Compile Include="EcmaXmlHelper.cs" />
<Compile Include="remaining.cs" />
<Compile Include="..\..\..\mdoc\Options.cs">
<Link>Options.cs</Link>
</Compile>
<Compile Include="comparereport.cs" />
<Compile Include="fixsummaries.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
<Reference Include="System" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace DocStat
{
public static class EcmaXmlHelper
{
public static void Fix(XElement toFix, XElement forReference)
{
if (null == forReference)
{
return;
}
if (XNode.DeepEquals(toFix, forReference))
{
return;
}
toFix.ReplaceWith(forReference);
}
// Sometimes, the identifying attribute lives in a child. This means that
// we need to return a predicate that finds the piece to match.
// So:
// Return a function that takes an XElement with a known unique identifier
// and return a predicate that gets the XAttribute that identifies it
public static Func<XElement, XAttribute> IdentifyingAttributePredicateFor(XElement el)
{
switch (el.Name.ToString())
{
case "typeparam":
case "param":
return (XElement e) => e.Attribute("name");
case "Member":
// The ILAsm signature is unique, and always present
return (XElement e) => e.Elements("MemberSignature")
.First((a) => a.Attribute("Language").Value == "ILAsm")
.Attribute("Value");
case "related":
return (XElement e) => e.Attribute("href"); ;
default:
throw new Exception("Encountered plural node of type " + el.Name);
}
}
// Walk the hierarchy to get a selector that can be used to find the current element
// This selector can then be used on the parallel XDocument to retrieve the equivalent
// XElement. The selector returns null early if any selector returns null
public static Func<XDocument, XElement> GetSelectorFor(XElement toSelect)
{
List<Func<XElement, XElement>> toRun = new List<Func<XElement, XElement>>();
XElement current = toSelect;
Func<XElement, XElement> selector;
while (current.Parent != null)
{
XName currentName = current.Name;
if (current.Parent.Elements(currentName).Count() > 1)
{
// Function that finds the element-specific unique attribute.
Func<XElement, XAttribute> uniquePredicate
= IdentifyingAttributePredicateFor(current);
// Get the unique attribute value in *this* xml tree.
// (We know this exists.)
string uniqueAttr = uniquePredicate(current).Value;
selector = (XElement arg) => arg.Elements(currentName)
.FirstOrDefault((XElement a) =>
uniquePredicate(a).Value == uniqueAttr);
}
else
{
selector = (XElement arg) => arg.Element(currentName);
}
toRun.Add(selector);
current = current.Parent;
}
return (XDocument doc) =>
{
XElement _current = doc.Root;
foreach (var _selector in ((IEnumerable<Func<XElement, XElement>>)toRun).Reverse())
{
_current = _selector(_current);
if (_current == null)
return null;
}
return _current;
};
}
public static string GetParallelFilePathFor(string pathToTypeToFix,
string rootOfReferenceFiles,
string rootOfFilesToFix,
Func<string, string> referenceRootTransform = null,
Func<string, string> referencePathTransform = null)
{
string fullFixPath = Path.GetFullPath(pathToTypeToFix);
string fullFixRoot = Path.GetFullPath(rootOfFilesToFix);
rootOfReferenceFiles =
null == referenceRootTransform ? rootOfReferenceFiles : referenceRootTransform(rootOfReferenceFiles);
string fullRefRoot = Path.GetFullPath(rootOfReferenceFiles);
string fullReferencePath = fullFixPath.Replace(fullFixRoot, fullRefRoot);
fullReferencePath =
null == referencePathTransform ? fullReferencePath : referencePathTransform(fullReferencePath);
return fullReferencePath;
}
public static XDocument GetParallelXDocFor(string parallelFilePath,
HashSet<string> refPaths = null)
{
if (!File.Exists(parallelFilePath))
return null;
if ((null != refPaths) && !refPaths.Contains(parallelFilePath))
return null;
return XDocument.Load(parallelFilePath);
}
public static IEnumerable<XElement> ElementsOfInterest(XDocument ecmaXmlDoc)
{
// (1) Yield type-level summary and remarks:
yield return ecmaXmlDoc.Element("Type").Element("Docs").Element("summary");
yield return ecmaXmlDoc.Element("Type").Element("Docs").Element("remarks");
var members = ecmaXmlDoc.Element("Type").Element("Members");
if (null != members)
{
foreach (XElement m in members.Elements())
{
// (2) Yield summary, remarks, return values, parameters, and typeparams
XElement docsElement = m.Element("Docs");
yield return docsElement.Element("summary");
XElement remarks = docsElement.Element("remarks");
if (null != remarks)
yield return remarks;
XElement returns = docsElement.Element("returns");
if (null != returns)
yield return returns;
if (docsElement.Elements("param").Any())
{
IEnumerable<XElement> _params = docsElement.Elements("param");
foreach (XElement p in _params)
{
yield return p;
}
}
if (docsElement.Elements("typeparam").Any())
{
IEnumerable<XElement> typeparams = docsElement.Elements("typeparam");
foreach (XElement p in typeparams)
{
yield return p;
}
}
}
}
}
public static IEnumerable<XElement> Members(XDocument ecmaXmlDoc)
{
var members = ecmaXmlDoc.Element("Type").Element("Members");
if (null != members)
{
foreach (var m in members.Elements("Member"))
yield return m;
}
yield break;
}
public static IEnumerable<XElement> NewMembers(XDocument newXml, XDocument oldXml)
{
if (null == Members(newXml))
{ yield break; }
if (null == oldXml)
{
foreach (var e in Members(newXml))
yield return e;
}
else
{
foreach (var e in Members(newXml))
{
if (null == GetSelectorFor(e)(oldXml))
yield return e;
}
}
}
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Options;
namespace DocStat
{
public class apistat
{
private static void Main(string[] args)
{
apistat a = new apistat();
try
{
a.Run(args);
}
catch (Exception e)
{
Console.Error.WriteLine("apistat: {0}", e.Message);
Environment.ExitCode = 1;
}
}
internal Dictionary<string, ApiCommand> subcommands;
private void Run (string[] args) {
subcommands = new Dictionary<string, ApiCommand>() {
{"internalize", new InternalizeCommand() },
{"remaining", new RemainingCommand() },
{"obsolete", new ObsoleteCommand() },
{"comparefix", new CompareFixCommand()},
{"reportnew", new CompareReportCommand()},
{"fixsummaries", new FixSummariesCommand()}
};
GetCommand(args.First()).Run(args);
}
internal ApiCommand GetCommand(string command)
{
ApiCommand a;
if (!subcommands.TryGetValue(command, out a))
{
throw new Exception(String.Format("Unknown command: {0}.", command));
}
return a;
}
}
public abstract class ApiCommand {
public abstract void Run(IEnumerable<string> args);
protected List<string> Parse(OptionSet p, IEnumerable<string> args,
string command, string prototype, string description)
{
bool showHelp = false;
p.Add("h|?|help",
"Show this message and exit.",
v => showHelp = v != null);
List<string> extra = null;
if (args != null)
{
extra = p.Parse(args.Skip(1));
}
if (args == null || showHelp)
{
Console.WriteLine("usage: mdoc {0} {1}",
args == null ? command : args.First(), prototype);
Console.WriteLine();
Console.WriteLine(description);
Console.WriteLine();
Console.WriteLine("Available Options:");
p.WriteOptionDescriptions(Console.Out);
return null;
}
return extra;
}
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Mono.Options;
namespace DocStat
{
public class CompareFixCommand : ApiCommand
{
public override void Run(IEnumerable<string> args)
{
string filesToFixDir = "";
string omitlist = "";
string processlist = "";
string pattern = "";
List<string> extras = CommandUtils.ProcessFileArgs(args,
ref filesToFixDir,
ref omitlist,
ref processlist,
ref pattern);
// must have
string filesToUseAsRefDir = "";
// should have
bool doSummaries = true;
bool doParameters = true;
bool doReturns = true;
bool doRemarks = true;
bool doTypes = true;
// nice to have
bool dryRun = false;
bool reportChanges = false;
var opts = new OptionSet {
{"f|fix=", (f) => filesToFixDir = f},
{"u|using=", (u) => filesToUseAsRefDir = u},
{"s|summaries", (s) => doSummaries = s != null},
{"a|params", (p) => doParameters = p != null },
{"r|retvals", (r) => doReturns = r != null },
{"m|remarks", (r) => doRemarks = r != null },
{"t|typesummaries", (t) => doTypes = t != null },
{"y|dryrun", (d) => dryRun = d != null },
{"c|reportchanges", (c) => reportChanges = c != null },
};
extras = opts.Parse(extras);
CommandUtils.ThrowOnFiniteExtras(extras);
if (String.IsNullOrEmpty(filesToUseAsRefDir))
throw new ArgumentException("You must supply a parallel directory from which to source new content with '[u|using]'=.");
IEnumerable<string> filesToFix = CommandUtils.GetFileList(processlist, omitlist, filesToFixDir, pattern);
HashSet<string> filesToUseAsReference = new HashSet<string>(CommandUtils.GetFileList("", "", filesToUseAsRefDir, ""));
filesToFix =
filesToFix.Where((f) =>
filesToUseAsReference.Contains(EcmaXmlHelper.GetParallelFilePathFor(f,
filesToUseAsRefDir,
filesToFixDir)));
foreach (var f in filesToFix)
{
XDocument currentRefXDoc = EcmaXmlHelper.GetParallelXDocFor(
EcmaXmlHelper.GetParallelFilePathFor(f, filesToUseAsRefDir, filesToFixDir),
filesToUseAsReference
);
if (null == currentRefXDoc)
continue;
Action<XElement> fix =
(XElement e) => EcmaXmlHelper.Fix(e, EcmaXmlHelper.GetSelectorFor(e)(currentRefXDoc));
bool changed = false;
XDocument currentXDocToFix = XDocument.Load(f);
EventHandler<XObjectChangeEventArgs> SetTrueIfChanged = null;
SetTrueIfChanged =
new EventHandler<XObjectChangeEventArgs>((sender, e) => { currentXDocToFix.Changed -= SetTrueIfChanged; changed = true; });
currentXDocToFix.Changed += SetTrueIfChanged;
foreach (XElement e in EcmaXmlHelper.ElementsOfInterest(currentXDocToFix))
fix(e);
if (changed)
{
CommandUtils.WriteXDocument(currentXDocToFix, f);
}
}
}
}
}

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Mono.Options;
namespace DocStat
{
public class CompareReportCommand : ApiCommand
{
public override void Run(IEnumerable<string> args)
{
// throw new NotImplementedException();
string updatedDir = "";
string omitlist = "";
string processlist = "";
string pattern = "";
List<string> extras = CommandUtils.ProcessFileArgs(args,
ref updatedDir,
ref omitlist,
ref processlist,
ref pattern);
string oldFilesDir = "";
bool typeOnly = false;
string reportFile = "";
bool nosigil = false;
var options = new OptionSet {
{"previous=", (p) => oldFilesDir = p},
{"typeonly", (t) => typeOnly = t != null},
{"reportfile=", (r) => reportFile = r},
{ "no-check-TBA", (t) => nosigil = t != null }
};
extras = options.Parse(extras);
CommandUtils.ThrowOnFiniteExtras(extras);
if (String.IsNullOrEmpty(oldFilesDir))
throw new ArgumentException("You must supply a parallel directory from which to source new content with 'previous='.");
if (String.IsNullOrEmpty(reportFile) || !reportFile.EndsWith(".csv"))
throw new ArgumentException("'reportfile=' must be used, and its value must end with '.csv'.");
string bareReportDir = Path.GetDirectoryName(Path.GetFullPath(reportFile));
if (!Directory.Exists(bareReportDir))
throw new ArgumentException(bareReportDir + " does not exist.");
IEnumerable<string> updated = CommandUtils.GetFileList(processlist, omitlist, updatedDir, pattern);
StreamWriter reportStream = new StreamWriter(reportFile);
reportStream.WriteLine(String.Format(CommandUtils.CSVFormatString(5), "File Name", "Type", "Member","Need file summary", "Need member summary"));
Func<XElement, bool> hasSigil = null;
if (nosigil)
{
hasSigil = (XElement e) => true;
}
else
{
hasSigil = (XElement e) => e.Element("Docs").Element("summary").Value == "To be added.";
}
//Func<XElement, bool> needSummary = (XElement e) => e.Element("Docs").Element("summary").Value == "To be added.";
Func<XElement, string> MemberLine = (XElement e) => {
return string.Format(
CommandUtils.CSVFormatString(5),
"",
"",
e.Attribute("MemberName").Value,
"",
hasSigil(e) ? "y" : "n");
};
List<string> toWrite = new List<string>();
foreach (string updatedXMLFile in updated)
{
bool fileLineAdded = false;
XDocument updatedXDoc = XDocument.Load(updatedXMLFile);
Func<string> FileLine = () =>
{
return string.Format(
CommandUtils.CSVFormatString(5),
updatedXMLFile,
updatedXDoc.Element("Type").Attribute("FullName").Value,
"",
hasSigil(updatedXDoc.Element("Type")) ? "y" : "n",
"");
};
string oldXMLFile = EcmaXmlHelper.GetParallelFilePathFor(updatedXMLFile, oldFilesDir, updatedDir);
XDocument oldXDoc = File.Exists(oldXMLFile) ? XDocument.Load(oldXMLFile) : null;
if (null == oldXDoc && hasSigil(updatedXDoc.Element("Type")))
{
toWrite.Add(FileLine());
fileLineAdded = true;
}
IEnumerable<XElement> newMembers = EcmaXmlHelper.NewMembers(updatedXDoc, oldXDoc);
if (null != newMembers && newMembers.Where((f) => hasSigil(f)).Any())
{
if (!fileLineAdded)
toWrite.Add(FileLine());
foreach (XElement e in newMembers.Where((f) => hasSigil(f)))
{
toWrite.Add(MemberLine(e));
}
}
// If toWrite isn't empty, write all lines
if (toWrite.Any())
{
foreach (string s in toWrite)
reportStream.WriteLine(s);
}
toWrite.Clear();
}
reportStream.Flush();
reportStream.Close();
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Mono.Options;
namespace DocStat
{
public class FixSummariesCommand : ApiCommand
{
public FixSummariesCommand()
{
}
public override void Run(IEnumerable<string> args)
{
string rootdir = "";
string omitlist = "";
string processlist = "";
string pattern = "";
List<string> extras = CommandUtils.ProcessFileArgs(args,
ref rootdir,
ref omitlist,
ref processlist,
ref pattern
);
CommandUtils.ThrowOnFiniteExtras(extras);
foreach (string file in CommandUtils.GetFileList(processlist, omitlist, rootdir, pattern))
{
bool changed = false;
XDocument xdoc = new XDocument(XElement.Load(file));
XElement memberRoot = xdoc.Element("Type").Element("Members");
if (memberRoot == null || !memberRoot.Descendants().Any())
{
continue;
}
foreach (XElement m in memberRoot.Elements("Member"))
{
XElement summary = m.Element("Docs").Element("summary");
if (null == summary)
{
continue;
}
if (summary.IsEmpty || (summary.Value.Length == 0 && summary.Descendants().Count() == 0))
{
summary.Value = "To be added.";
changed = true;
continue;
}
IEnumerable<XElement> mistakeParams = summary.Descendants("param");
if (mistakeParams.Count() == 0)
{
continue;
}
mistakeParams.ToList().ForEach(e => e.Name = "paramref");
changed = true;
}
if (changed)
{
CommandUtils.WriteXDocument(xdoc, file);
}
}
}
}
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Mono.Options;
namespace DocStat
{
public class InternalizeCommand : ApiCommand
{
public override void Run(IEnumerable<string> args)
{
string rootdir = "";
string omitlist = "";
string processlist = "";
string pattern = "";
List<string> extras = CommandUtils.ProcessFileArgs(args,
ref rootdir,
ref omitlist,
ref processlist,
ref pattern
);
string message = "For internal use only.";
string sigil = "To be added.";
bool nocheck = false;
bool nosigil = false;
var opt = new OptionSet {
{ "m|message=", (m) => message = m },
{ "s|sigil=", (s) => sigil = s },
{ "no-check-browsable", (n) => nocheck = n != null},
{ "no-check-TBA", (t) => nosigil = t != null }
};
extras = opt.Parse(extras);
CommandUtils.ThrowOnFiniteExtras(extras);
Func<XElement, bool> hassigil;
Func<XDocument, bool> typehassigil;
Func<XElement, bool> qualifies;
if (nosigil)
{
// Mark types and members internal, regardless of whether the summaries are filled out
hassigil = (x) => true;
typehassigil = (x) => true;
}
else
{
hassigil = (e) => e.Element("Docs").Element("summary").Value == sigil;
typehassigil = (t) => t.Element("Type").Element("Docs").Element("summary").Value == sigil;
}
if (!nocheck)
{
qualifies = (e) =>
{
return e.Elements("Attributes")
.Any((XElement child) => child.Elements("Attribute")
.Any((XElement name) => name.Value.Contains("EditorBrowsableState.Never")))
&& hassigil(e);
};
}
else
{
qualifies = hassigil;
}
foreach (string file in CommandUtils.GetFileList(processlist, omitlist, rootdir, pattern))
{
XDocument xdoc = new XDocument(XElement.Load(file));
// Find every member that has the internal marker and summary="To be added." (or the provided sigil)
XElement memberRoot = xdoc.Element("Type").Element("Members");
if (memberRoot == null || !memberRoot.Descendants().Any())
continue;
IEnumerable<XElement> hits = memberRoot.Elements("Member").Where(s => qualifies(s));
foreach (XElement x in hits)
{
x.Element("Docs").Element("summary").Value = message;
}
if (typehassigil(xdoc))
xdoc.Element("Type").Element("Docs").Element("summary").Value = message;
CommandUtils.WriteXDocument(xdoc, file);
}
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Mono.Options;
namespace DocStat
{
public class ObsoleteCommand : ApiCommand
{
public override void Run(IEnumerable<string> args)
{
string rootdir = "";
string omitlist = "";
string processlist = "";
string pattern = "";
List<string> extras = CommandUtils.ProcessFileArgs(args,
ref rootdir,
ref omitlist,
ref processlist,
ref pattern);
string obsoleteMarker = "System.Obsolete";
string sigil = "To be added.";
bool skipSigil = false;
string message = "Deprecated. Do not use.";
var opt = new OptionSet {
{"a|attribute",
(x) => obsoleteMarker = x },
{ "s|sigil=", (s) => sigil = s },
{ "no-check-TBA", (s) => skipSigil = s != null},
{ "m|message=", (m) => message = m}
};
extras = opt.Parse(extras);
CommandUtils.ThrowOnFiniteExtras(extras);
Func<XElement, bool> sigilCheck;
Func<XElement, bool> obsoleteCheck;
if (skipSigil)
{
sigilCheck = (e) => true;
}
else
{
sigilCheck = (e) => e.Element("Docs").Element("summary").Value == sigil;
}
obsoleteCheck = (e) => e.Elements("Attribute").Any((arg) =>
arg.Elements("Attribute").Any((arg2) =>
arg2.Value.StartsWith(obsoleteMarker))); ; ;
foreach (string file in CommandUtils.GetFileList(processlist, omitlist, rootdir, pattern))
{
// find all the ones that have attributes that start with the provided attribute
XDocument xdoc = XDocument.Load(file);
XElement memberRoot = xdoc.Element("Type").Element("Members");
if (memberRoot == null || !memberRoot.Descendants().Any())
continue;
foreach (XElement toMark in memberRoot.Elements("Member")
.Where((e) => obsoleteCheck(e) && sigilCheck(e)))
{
toMark.Element("Docs").Element("summary").Value = message;
}
CommandUtils.WriteXDocument(xdoc, file);
}
}
}
}

View File

@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Mono.Options;
namespace DocStat
{
public class RemainingCommand : ApiCommand
{
public override void Run(IEnumerable<string> args)
{
string rootdir = "";
string omitlist = "";
string processlist = "";
string pattern = "";
List<string> extras = CommandUtils.ProcessFileArgs(args,
ref rootdir,
ref omitlist,
ref processlist,
ref pattern);
string sigil = "To be added.";
string outputfile = "";
var opt = new OptionSet
{
{"s|sigil=", (s) => sigil = s},
{"o|output|ofile=", (o) => outputfile = o.EndsWith(@"csv") ? o : outputfile}
};
extras = opt.Parse(extras);
if (String.IsNullOrEmpty(outputfile))
throw new ArgumentException("You must supply an output file, and it must end with '.csv'");
CommandUtils.ThrowOnFiniteExtras(extras);
List<XElement> results = new List<XElement>();
IEnumerable<string> files = CommandUtils.GetFileList(processlist, omitlist, rootdir, pattern);
List<string> fileList = files.ToList();
foreach (string file in fileList)
{
AddResults(file, results, sigil);
}
WriteResults(results, outputfile);
}
internal void AddResults(string file, List<XElement> results, string sigil)
{
// Add results
// <QueryResults>
// <Type name="..." filename="....">
// <Member name="...">
// ....
// </Type ... >
// <Type>
// ....
// <QueryResults>
XElement top = XElement.Load(file);
if (top.Name == "Type")
{
// We got a live one!
IEnumerable<XElement> qres =
from member in top.Descendants("Member")
where (string)member.Element("Docs").Element("summary") == sigil
select member;
List<XElement> le = new List<XElement>(qres);
if (le.Any())
{
string typeName = top.Attribute("FullName").Value;
XElement t = new XElement("Type");
t.Add(new XAttribute("name", typeName));
t.Add(new XAttribute("filename", file));
// add member name node for each node
foreach (XElement m in le)
{
XElement mres = new XElement("Member");
mres.Add(new XAttribute("name", m.Attribute("MemberName").Value));
t.Add(mres);
}
results.Add(t);
}
}
}
internal void WriteResults(List<XElement> results, string outputFileName)
{
if (null == results || results.Count == 0)
{
return;
}
StreamWriter ofile = new StreamWriter(outputFileName);
int typeCount = 0;
int memberCount = 0;
ofile.WriteLine("Type,Count,File Name,Member");
string typeFormat = "\"{0}\",\"{1}\",\"{2}\",";
string memberFormat = ",,,\"{0}\"";
string rollupFormat = "Types:,\"{0}\",Members:,\"{1}\"";
foreach (XElement e in results)
{
//List<XElement> countable = new List<XElement>(e.Elements());
ofile.WriteLine(typeFormat,
e.Attribute("name").Value,
e.Elements().Count(),
e.Attribute("filename").Value.Replace("`", "\\`"));
typeCount++;
foreach (XElement x in e.Elements())
{
ofile.WriteLine(memberFormat, x.Attribute("name").Value);
memberCount++;
}
}
ofile.WriteLine(rollupFormat, typeCount, memberCount);
ofile.Flush();
ofile.Close();
}
}
}