Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono-tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastien Pouliot <sebastien@ximian.com>2008-02-15 22:51:21 +0300
committerSebastien Pouliot <sebastien@ximian.com>2008-02-15 22:51:21 +0300
commit7714ce1c5622ad329f110fffe754eb0a4cda27b7 (patch)
treed713ac070f0a463b196145397fa4d9b286048514 /gendarme/console
parent2282e366e0e10c29b45c81a8d86f3badc5a9014e (diff)
parentb502282bb675feba4f68136a9f1e49f9b8a008e7 (diff)
2008-02-15 Sebastien Pouliot <sebastien@ximian.com>
* ConsoleRunner.cs: Updated runner for the new framework (still a work in progress). * gendarme.xsl: Updated XSLT to match new XML format. * HtmlResultWriter.cs: Produce HTML reports using gendarme.xsl and the XML writer. * Makefile.am: Updated with changes. * Options.cs: Jonathan Pryor Getopt::Long-inspired option parsing library for C# * ResultWriter.cs: Renamed from IResultWriter. Now an abstract class. * TextResultWriter.cs: Updated for display more information. * XmlResultWriter.cs: Updated xml format for results. svn path=/trunk/mono-tools/; revision=95808
Diffstat (limited to 'gendarme/console')
-rw-r--r--gendarme/console/ChangeLog14
-rw-r--r--gendarme/console/ConsoleRunner.cs533
-rw-r--r--gendarme/console/HtmlResultWriter.cs60
-rw-r--r--gendarme/console/Makefile.am6
-rw-r--r--gendarme/console/Options.cs1032
-rw-r--r--gendarme/console/ResultWriter.cs (renamed from gendarme/console/IResultWriter.cs)49
-rw-r--r--gendarme/console/TextResultWriter.cs79
-rw-r--r--gendarme/console/XmlResultWriter.cs151
-rw-r--r--gendarme/console/gendarme.xsl307
9 files changed, 1643 insertions, 588 deletions
diff --git a/gendarme/console/ChangeLog b/gendarme/console/ChangeLog
index bd48921a..237a30c2 100644
--- a/gendarme/console/ChangeLog
+++ b/gendarme/console/ChangeLog
@@ -1,3 +1,17 @@
+2008-02-15 Sebastien Pouliot <sebastien@ximian.com>
+
+ * ConsoleRunner.cs: Updated runner for the new framework (still a
+ work in progress).
+ * gendarme.xsl: Updated XSLT to match new XML format.
+ * HtmlResultWriter.cs: Produce HTML reports using gendarme.xsl and
+ the XML writer.
+ * Makefile.am: Updated with changes.
+ * Options.cs: Jonathan Pryor Getopt::Long-inspired option parsing
+ library for C#
+ * ResultWriter.cs: Renamed from IResultWriter. Now an abstract class.
+ * TextResultWriter.cs: Updated for display more information.
+ * XmlResultWriter.cs: Updated xml format for results.
+
2008-02-03 Sebastien Pouliot <sebastien@ximian.com>
* gendarme.xsl: Display the correct message (not the first one) for
diff --git a/gendarme/console/ConsoleRunner.cs b/gendarme/console/ConsoleRunner.cs
index 3c9da508..c27b41fd 100644
--- a/gendarme/console/ConsoleRunner.cs
+++ b/gendarme/console/ConsoleRunner.cs
@@ -27,363 +27,300 @@
//
using System;
-using System.Collections;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using Mono.Cecil;
+
using Gendarme.Framework;
-using Gendarme.Console.Writers;
-class ConsoleRunner : Runner {
+using NDesk.Options;
- private const string defaultConfiguration = "rules.xml";
- private const string defaultRuleSet = "default";
+namespace Gendarme {
- private string config;
- private string set;
- private Hashtable assemblies;
- private string format;
- private string output;
+ public class ConsoleRunner : Runner {
- private static Assembly assembly;
- private bool quiet;
+ private string config_file = "rules.xml";
+ private string rule_set = "*";
+ private string html_file;
+ private string log_file;
+ private string xml_file;
+ private bool help;
+ private bool quiet;
+ private List<string> assembly_names;
- static Assembly Assembly {
- get {
- if (assembly == null)
- assembly = Assembly.GetExecutingAssembly ();
- return assembly;
+ int Parse (string [] args)
+ {
+ var p = new OptionSet () {
+ { "config=", v => config_file = v },
+ { "set=", v => rule_set = v },
+ { "log=", v => log_file = v },
+ { "xml=", v => xml_file = v },
+ { "html=", v => html_file = v },
+ { "v|verbose", v => ++VerbosityLevel },
+ { "quiet", v => quiet = v != null },
+ { "h|?|help", v => help = v != null },
+ };
+ assembly_names = p.Parse (args);
+ return (assembly_names.Count > 0) ? 0 : 1;
}
- }
- static string GetFullPath (string filename)
- {
- if (Path.GetDirectoryName (filename) != String.Empty)
- return filename;
- return Path.Combine (Path.GetDirectoryName (Assembly.Location), filename);
- }
+ // name can be
+ // - a filename (a single assembly)
+ // - a mask (*, ?) for multiple assemblies
+ // - a special file (@) containing a list of assemblies
+ int AddFiles (string name)
+ {
+ if (String.IsNullOrEmpty (name))
+ return 0;
- static string GetNext (string[] args, int index, string defaultValue)
- {
- if ((args == null) || (index < 0) || (index >= args.Length))
- return defaultValue;
- return args [index];
- }
-
- // name can be
- // - a filename (a single assembly)
- // - a mask (*, ?) for multiple assemblies
- // - a special file (@) containing a list of assemblies
- void AddFiles (string name)
- {
- if ((name == null) || (name.Length == 0))
- return;
-
- if (name.StartsWith ("@")) {
- // note: recursive (can contains @, masks and filenames)
- using (StreamReader sr = File.OpenText (name.Substring (1))) {
- while (sr.Peek () >= 0) {
- AddFiles (sr.ReadLine ());
+ if (name.StartsWith ("@", StringComparison.OrdinalIgnoreCase)) {
+ // note: recursive (can contains @, masks and filenames)
+ using (StreamReader sr = File.OpenText (name.Substring (1))) {
+ while (sr.Peek () >= 0) {
+ AddFiles (sr.ReadLine ());
+ }
}
+ } else if (name.IndexOfAny (new char [] { '*', '?' }) >= 0) {
+ string dirname = Path.GetDirectoryName (name);
+ if (dirname.Length == 0)
+ dirname = "."; // assume current directory
+ string [] files = Directory.GetFiles (dirname, Path.GetFileName (name));
+ foreach (string file in files) {
+ AddAssembly (file);
+ }
+ } else {
+ AddAssembly (name);
}
- } else if (name.IndexOfAny (new char[] { '*', '?' }) >= 0) {
- string dirname = Path.GetDirectoryName (name);
- if (dirname.Length == 0)
- dirname = "."; // assume current directory
- string [] files = Directory.GetFiles (dirname, Path.GetFileName (name));
- foreach (string file in files) {
- assemblies.Add (Path.GetFullPath (file), null);
- }
- } else {
- assemblies.Add (Path.GetFullPath (name), null);
+ return 0;
}
- }
- bool ParseOptions (string[] args)
- {
- // defaults
- config = GetFullPath (defaultConfiguration);
- set = defaultRuleSet;
- assemblies = new Hashtable ();
-
- // TODO - we probably want (i.e. later) the possibility to
- // include/exclude certain rules from executing
- for (int i=0; i < args.Length; i++) {
- switch (args [i]) {
- case "--config":
- config = GetNext (args, ++i, defaultConfiguration);
- break;
- case "--set":
- set = GetNext (args, ++i, defaultRuleSet);
- break;
- case "--debug":
- debug = true;
- break;
- case "--quiet":
- quiet = true;
- break;
- case "--help":
- return false;
- case "--log":
- format = "text";
- output = GetNext (args, ++i, String.Empty);
- break;
- case "--xml":
- format = "xml";
- output = GetNext (args, ++i, String.Empty);
- break;
- case "--html":
- format = "html";
- output = GetNext (args, ++i, String.Empty);
- break;
- default:
- AddFiles (args[i]);
- break;
- }
+ void AddAssembly (string filename)
+ {
+ string assembly_name = Path.GetFullPath (filename);
+ AssemblyDefinition ad = AssemblyFactory.GetAssembly (assembly_name);
+ (ad as IAnnotationProvider).Annotations.Add ("filename", assembly_name);
+ Assemblies.Add (ad);
}
- return (assemblies.Count > 0);
- }
- bool LoadCustomParameters (XmlElement ruleset) {
- foreach (XmlElement parameter in ruleset.SelectNodes ("parameter")) {
- try {
- if (!parameter.HasAttribute ("name"))
- throw new XmlException ("The attribute name can't be found");
- if (!parameter.HasAttribute ("value"))
- throw new XmlException ("The attribute value can't be found");
- if (!parameter.HasAttribute ("rule"))
- throw new XmlException ("The attribute rule can't be found");
-
- string name = GetAttribute (parameter, "name", String.Empty);
- int value = 0;
- try {
- value = Int32.Parse (GetAttribute (parameter, "value", String.Empty));
- }
- catch (Exception exception) {
- throw new XmlException ("The value for the value field should be an integer.", exception);
- }
- string ruleName = GetAttribute (parameter, "rule", String.Empty);
+ static string GetFullPath (string filename)
+ {
+ if (Path.GetDirectoryName (filename).Length > 0)
+ return filename;
+ return Path.Combine (Path.GetDirectoryName (Assembly.Location), filename);
+ }
+
+ static string GetAttribute (XmlElement xel, string name, string defaultValue)
+ {
+ XmlAttribute xa = xel.Attributes [name];
+ if (xa == null)
+ return defaultValue;
+ return xa.Value;
+ }
- ApplyCustomParameterToRule (ruleName, name, value);
+ private static bool IsContainedInRuleSet (string rule, string mask)
+ {
+ string [] ruleSet = mask.Split ('|');
+ foreach (string entry in ruleSet) {
+ if (String.Compare (rule, entry.Trim ()) == 0)
+ return true;
}
- catch (Exception e) {
- Console.WriteLine ("Error reading parameters{0}Details: {1}", Environment.NewLine, e);
- return false;
+ return false;
+ }
+
+ public static bool RuleFilter (Type type, object interfaceName)
+ {
+ return (type.ToString () == (interfaceName as string));
+ }
+
+ public int LoadRulesFromAssembly (string assembly, string includeMask, string excludeMask)
+ {
+ int total = 0;
+ Assembly a = Assembly.LoadFile (Path.GetFullPath (assembly));
+ foreach (Type t in a.GetTypes ()) {
+ if (t.IsAbstract || t.IsInterface)
+ continue;
+
+ if (includeMask != "*")
+ if (!IsContainedInRuleSet (t.Name, includeMask))
+ continue;
+
+ if ((excludeMask != null) && (excludeMask.Length > 0))
+ if (IsContainedInRuleSet (t.Name, excludeMask))
+ continue;
+
+ if (t.FindInterfaces (new TypeFilter (RuleFilter), "Gendarme.Framework.IRule").Length > 0) {
+ Rules.Add ((IRule) Activator.CreateInstance (t));
+ total++;
+ }
}
+ return total;
}
- return true;
- }
- static string GetAttribute (XmlElement xel, string name, string defaultValue)
- {
- XmlAttribute xa = xel.Attributes [name];
- if (xa == null)
- return defaultValue;
- return xa.Value;
- }
+ bool LoadConfiguration ()
+ {
+ XmlDocument doc = new XmlDocument ();
+ doc.Load (config_file);
+ if (doc.DocumentElement.Name != "gendarme")
+ return false;
- bool LoadConfiguration ()
- {
- XmlDocument doc = new XmlDocument ();
- doc.Load (config);
- if (doc.DocumentElement.Name != "gendarme")
- return false;
+ bool result = false;
+ foreach (XmlElement ruleset in doc.DocumentElement.SelectNodes ("ruleset")) {
+ if (ruleset.Attributes ["name"].Value != rule_set)
+ continue;
+ foreach (XmlElement assembly in ruleset.SelectNodes ("rules")) {
+ string include = GetAttribute (assembly, "include", "*");
+ string exclude = GetAttribute (assembly, "exclude", String.Empty);
+ string from = GetFullPath (GetAttribute (assembly, "from", String.Empty));
- bool result = false;
- foreach (XmlElement ruleset in doc.DocumentElement.SelectNodes("ruleset")) {
- if (ruleset.Attributes["name"].Value != set)
- continue;
- foreach (XmlElement assembly in ruleset.SelectNodes("rules")) {
- string include = GetAttribute (assembly, "include", "*");
- string exclude = GetAttribute (assembly, "exclude", String.Empty);
- string from = GetFullPath (GetAttribute (assembly, "from", String.Empty));
- try {
int n = LoadRulesFromAssembly (from, include, exclude);
result = (result || (n > 0));
}
- catch (Exception e) {
- Console.WriteLine ("Error reading rules{1}Details: {0}", e, Environment.NewLine);
- return false;
- }
}
- if (!LoadCustomParameters (ruleset))
- return false;
+ return result;
}
- return result;
- }
- void ApplyCustomParameterToRule (string ruleName, string name, int value)
- {
- IRule rule = GetRule (ruleName);
- if (rule == null)
- throw new ArgumentException (String.Format ("The rule name {0} can't be found in the rules collection", ruleName), "rule");
- PropertyInfo property = rule.GetType ().GetProperty (name);
- if (property == null)
- throw new ArgumentException (String.Format ("The property {0} can't be found in the rule {1}", name, ruleName), "name");
- if (!property.CanWrite)
- throw new ArgumentException (String.Format ("The property {0} can't be written in the rule {1}", name, ruleName), "name");
- object result = property.GetSetMethod ().Invoke (rule, new object[] {value});
- }
+ int Report ()
+ {
+ // generate text report (default, to console, if xml and html aren't specified)
+ if ((log_file != null) || ((xml_file == null) && (html_file == null))) {
+ using (TextResultWriter writer = new TextResultWriter (this, log_file)) {
+ writer.Report ();
+ }
+ }
- IRule GetRule (string name)
- {
- IRule result;
- result = GetRuleFromSet (name, Rules.Assembly);
- if (result == null) {
- result = GetRuleFromSet (name, Rules.Module);
- if (result == null) {
- result = GetRuleFromSet (name, Rules.Type);
- if (result == null) {
- result = GetRuleFromSet (name, Rules.Method);
+ // generate XML report
+ if (xml_file != null) {
+ using (XmlResultWriter writer = new XmlResultWriter (this, xml_file)) {
+ writer.Report ();
}
}
- }
- return result;
- }
- static IRule GetRuleFromSet (string name, RuleCollection rules)
- {
- foreach (IRule rule in rules) {
- if (String.Compare (name, rule.GetType ().FullName) == 0)
- return rule;
+ // generate HTML report
+ if (html_file != null) {
+ using (HtmlResultWriter writer = new HtmlResultWriter (this, html_file)) {
+ writer.Report ();
+ }
+ }
+ return 0;
}
- return null;
- }
- void Header ()
- {
- if (quiet)
- return;
-
- Assembly a = Assembly.GetExecutingAssembly();
- Version v = a.GetName ().Version;
- if (v.ToString () != "0.0.0.0") {
- Console.WriteLine ("Gendarme v{0}", v);
- object[] attr = a.GetCustomAttributes (typeof (AssemblyCopyrightAttribute), false);
- if (attr.Length > 0)
- Console.WriteLine (((AssemblyCopyrightAttribute) attr [0]).Copyright);
- } else {
- Console.WriteLine ("Gendarme - Development Snapshot");
- }
- Console.WriteLine ();
- }
+ int Execute (string [] args)
+ {
+ try {
+ int result = Parse (args);
+ if (result != 0) {
+ if (help) {
+ Help ();
+ }
+ return result;
+ }
- static void Help ()
- {
- Console.WriteLine ("Usage: gendarme [--config file] [--set ruleset] [--{log|xml|html} file] assembly");
- Console.WriteLine ("Where");
- Console.WriteLine (" --config file\t\tSpecify the configuration file. Default is 'rules.xml'.");
- Console.WriteLine (" --set ruleset\t\tSpecify the set of rules to verify. Default is '*'.");
- Console.WriteLine (" --log file\t\tSave the text output to the specified file.");
- Console.WriteLine (" --xml file\t\tSave the output, as XML, to the specified file.");
- Console.WriteLine (" --html file\t\tSave the output, as HTML, to the specified file.");
- Console.WriteLine (" --quiet\t\tDisplay minimal output (results) from the runner.");
- Console.WriteLine (" --debug\t\tEnable debugging output.");
- Console.WriteLine (" assembly\t\tSpecify the assembly to verify.");
- Console.WriteLine ();
- }
+ Header ();
- void Write (string text)
- {
- if (!quiet)
- Console.Write (text);
- }
+ // load configuration, including rules, and continue if
+ // there's at least one rule to execute
+ if (!LoadConfiguration () || (Rules.Count < 1))
+ return 3;
- void WriteLine (string text)
- {
- if (!quiet)
- Console.WriteLine (text);
- }
-
- void WriteLine (string text, params object[] args)
- {
- if (!quiet)
- Console.WriteLine (text, args);
- }
-
- static void ProcessRules (ConsoleRunner runner)
- {
- runner.Header ();
- string[] assemblies = new string [runner.assemblies.Count];
- runner.assemblies.Keys.CopyTo (assemblies, 0);
- DateTime total = DateTime.UtcNow;
- foreach (string assembly in assemblies) {
- DateTime start = DateTime.UtcNow;
- runner.Write (assembly);
- try {
- AssemblyDefinition ad = AssemblyFactory.GetAssembly (assembly);
- try {
- runner.Process (ad);
- runner.assemblies [assembly] = ad;
- runner.WriteLine (" - completed ({0} seconds).", (DateTime.UtcNow - start).TotalSeconds);
- }
- catch (Exception e) {
- runner.WriteLine (" - error executing rules{0}Details: {1}", Environment.NewLine, e);
+ foreach (string name in assembly_names) {
+ result = AddFiles (name);
+ if (result != 0)
+ return result;
}
+
+ // now that all rules and assemblies are know, time to initialize
+ Initialize ();
+ // before analizing the assemblies with the rules
+ Run ();
+
+ return Report ();
}
catch (Exception e) {
- runner.WriteLine (" - error processing{0}\tDetails: {1}", Environment.NewLine, e);
+ Console.WriteLine ("Uncatched exception occured. Please fill a bug report.");
+ Console.WriteLine ("Rule:\t{0}", CurrentRule);
+ Console.WriteLine ("Target:\t{0}", CurrentTarget);
+ Console.WriteLine ("Stack trace: {0}", e);
+ return 4;
}
}
- runner. WriteLine ("{0}{1} assemblies processed in {2} seconds.{0}", Environment.NewLine, runner.assemblies.Count,
- (DateTime.UtcNow - total).TotalSeconds);
- }
- static void Report (ConsoleRunner runner)
- {
- IResultWriter writer;
- switch (runner.format) {
- case "xml":
- writer = new XmlResultWriter (runner.output);
- break;
- case "html":
- writer = new HtmlResultWriter (runner.output);
- break;
- default:
- writer = new TextResultWriter (runner.output);
- break;
+ public override void Run ()
+ {
+ DateTime start = DateTime.UtcNow;
+ base.Run ();
+ Console.WriteLine ("{0}{1} assemblies processed in {2} seconds.{0}",
+ Environment.NewLine, Assemblies.Count, (DateTime.UtcNow - start).TotalSeconds);
}
- writer.Start ();
- writer.Write (runner.assemblies);
- writer.Write (runner.Rules);
- foreach (Violation v in runner.Violations) {
- writer.Write (v);
+ protected override void OnAssembly (RunnerEventArgs e)
+ {
+ DateTime start = DateTime.UtcNow;
+ base.OnAssembly (e);
+ Console.WriteLine ("{0}: {1} seconds.",
+ (e.CurrentAssembly as IAnnotationProvider).Annotations ["filename"],
+ (DateTime.UtcNow - start).TotalSeconds);
}
- writer.End ();
- }
- static int Main (string[] args)
- {
- ConsoleRunner runner = new ConsoleRunner ();
+ void Header ()
+ {
+ if (quiet)
+ return;
- // runner options and configuration
-
- try {
- if (!runner.ParseOptions (args)) {
- Help ();
- return 1;
+ Assembly a = Assembly.GetExecutingAssembly ();
+ Version v = a.GetName ().Version;
+ if (v.ToString () != "0.0.0.0") {
+ Console.WriteLine ("Gendarme v{0}", v);
+ object [] attr = a.GetCustomAttributes (typeof (AssemblyCopyrightAttribute), false);
+ if (attr.Length > 0)
+ Console.WriteLine (((AssemblyCopyrightAttribute) attr [0]).Copyright);
+ } else {
+ Console.WriteLine ("Gendarme - Development Snapshot");
}
- if (!runner.LoadConfiguration ()) {
- Console.WriteLine ("No assembly file were specified.");
- return 1;
+ Console.WriteLine ();
+ }
+
+ private static Assembly assembly;
+
+ static Assembly Assembly {
+ get {
+ if (assembly == null)
+ assembly = Assembly.GetExecutingAssembly ();
+ return assembly;
}
}
- catch (Exception e) {
- Console.WriteLine (e);
- return 1;
+
+ static void Help ()
+ {
+ Console.WriteLine ("Usage: gendarme [--config file] [--set ruleset] [--{log|xml|html} file] assembly");
+ Console.WriteLine ("Where");
+ Console.WriteLine (" --config file\t\tSpecify the configuration file. Default is 'rules.xml'.");
+ Console.WriteLine (" --set ruleset\t\tSpecify the set of rules to verify. Default is '*'.");
+ Console.WriteLine (" --log file\t\tSave the text output to the specified file.");
+ Console.WriteLine (" --xml file\t\tSave the output, as XML, to the specified file.");
+ Console.WriteLine (" --html file\t\tSave the output, as HTML, to the specified file.");
+ Console.WriteLine (" --quiet\t\tDisplay minimal output (results) from the runner.");
+ Console.WriteLine (" --v\t\tEnable debugging output (use multiple time to augment verbosity).");
+ Console.WriteLine (" assembly\t\tSpecify the assembly to verify.");
+ Console.WriteLine ();
}
- ProcessRules (runner);
- Report (runner);
-
- if (runner.Violations.Count == 0) {
- runner.WriteLine ("No rule's violation were found.");
- return 0;
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="args"></param>
+ /// <returns>0 for success,
+ /// 1 if some defects are found,
+ /// 2 if some parameters are bad,
+ /// 3 if a problem is related to the xml configuration file
+ /// 4 if an uncatched exception occured</returns>
+ static int Main (string [] args)
+ {
+ return new ConsoleRunner ().Execute (args);
}
- return 1;
}
}
diff --git a/gendarme/console/HtmlResultWriter.cs b/gendarme/console/HtmlResultWriter.cs
index 9b844971..35e0c764 100644
--- a/gendarme/console/HtmlResultWriter.cs
+++ b/gendarme/console/HtmlResultWriter.cs
@@ -27,7 +27,6 @@
//
using System;
-using System.Collections;
using System.IO;
using System.Reflection;
using System.Xml;
@@ -35,56 +34,53 @@ using System.Xml.Xsl;
using Gendarme.Framework;
-namespace Gendarme.Console.Writers {
+namespace Gendarme {
- public class HtmlResultWriter : IResultWriter {
+ public class HtmlResultWriter : ResultWriter, IDisposable {
- private XmlResultWriter writer;
private string temp_filename;
- private string final_filename;
- public HtmlResultWriter (string output)
+ public HtmlResultWriter (IRunner runner, string fileName)
+ : base (runner, fileName)
{
- final_filename = output;
temp_filename = Path.GetTempFileName ();
- writer = new XmlResultWriter (temp_filename);
}
- public void Start ()
+ protected override void Write()
{
- writer.Start ();
- }
-
- public void End ()
- {
- try {
- writer.End ();
- // load XSL file from embedded resource
- using (Stream s = Assembly.GetExecutingAssembly ().GetManifestResourceStream ("gendarme.xsl")) {
- // process the XML result with the XSL file
- XslCompiledTransform xslt = new XslCompiledTransform ();
- xslt.Load (new XmlTextReader (s));
- xslt.Transform (temp_filename, final_filename);
- }
- }
- finally {
- File.Delete (temp_filename);
+ using (XmlResultWriter writer = new XmlResultWriter (Runner, temp_filename)) {
+ writer.Report ();
}
}
- public void Write (IDictionary assemblies)
+ protected override void Finish ()
{
- writer.Write (assemblies);
+ // load XSL file from embedded resource
+ Assembly a = Assembly.GetExecutingAssembly ();
+ string[] resources = a.GetManifestResourceNames ();
+ if (resources.Length != 1)
+ throw new InvalidDataException ("Could not locate XSL style sheet");
+
+ using (Stream s = a.GetManifestResourceStream (resources [0])) {
+ // process the XML result with the XSL file
+ XslCompiledTransform xslt = new XslCompiledTransform ();
+ xslt.Load (new XmlTextReader (s));
+ xslt.Transform (temp_filename, FileName);
+ }
}
- public void Write (Rules rules)
+ public void Dispose ()
{
- writer.Write (rules);
+ Dispose (true);
+ GC.SuppressFinalize (this);
}
- public void Write (Violation v)
+ protected virtual void Dispose (bool disposing)
{
- writer.Write (v);
+ if (disposing) {
+ if (File.Exists (temp_filename))
+ File.Delete (temp_filename);
+ }
}
}
}
diff --git a/gendarme/console/Makefile.am b/gendarme/console/Makefile.am
index 7c612a67..da90479a 100644
--- a/gendarme/console/Makefile.am
+++ b/gendarme/console/Makefile.am
@@ -6,15 +6,15 @@ DISTCLEANFILES = Makefile.in
gendarme_sources_in = ../AssemblyInfo.cs.in
gendarme_generated_sources = $(gendarme_sources_in:.in=)
-gendarme_sources = ConsoleRunner.cs IResultWriter.cs TextResultWriter.cs XmlResultWriter.cs HtmlResultWriter.cs
+gendarme_sources = ConsoleRunner.cs ResultWriter.cs TextResultWriter.cs XmlResultWriter.cs HtmlResultWriter.cs Options.cs
gendarme_resources = gendarme.xsl
gendarme_build_sources = $(addprefix $(srcdir)/, $(gendarme_sources))
gendarme_build_sources += $(gendarme_generated_sources)
../bin/gendarme.exe: $(gendarme_build_sources) $(gendarme_resources)
- $(GMCS) -debug -r:$(top_builddir)/gendarme/bin/Mono.Cecil.dll -r:../bin/Gendarme.Framework.dll -r:System.Xml.dll -out:$@ $(gendarme_build_sources) \
- -resource:gendarme.xsl
+ $(GMCS) -debug -d:LINQ -r:$(top_builddir)/gendarme/bin/Mono.Cecil.dll -r:../bin/Gendarme.Framework.dll -r:System.Xml.dll \
+ -out:$@ $(gendarme_build_sources) -resource:gendarme.xsl
self-test: ../bin/gendarme.exe
mono --debug ../bin/gendarme.exe ../bin/gendarme.exe
diff --git a/gendarme/console/Options.cs b/gendarme/console/Options.cs
new file mode 100644
index 00000000..540ac51f
--- /dev/null
+++ b/gendarme/console/Options.cs
@@ -0,0 +1,1032 @@
+//
+// Options.cs
+//
+// Authors:
+// Jonathan Pryor <jpryor@novell.com>
+//
+// Copyright (C) 2008 Novell (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+// Compile With:
+// gmcs -debug+ -d:TEST -r:System.Core Options.cs
+// gmcs -debug+ -d:LINQ -d:TEST -r:System.Core Options.cs
+
+//
+// A Getopt::Long-inspired option parsing library for C#.
+//
+// NDesk.Options.OptionSet is built upon a key/value table, where the
+// key is a option format string and the value is an Action<string>
+// delegate that is invoked when the format string is matched.
+//
+// Option format strings:
+// BNF Grammar: ( name [=:]? ) ( '|' name [=:]? )+
+//
+// Each '|'-delimited name is an alias for the associated action. If the
+// format string ends in a '=', it has a required value. If the format
+// string ends in a ':', it has an optional value. If neither '=' or ':'
+// is present, no value is supported.
+//
+// Options are extracted either from the current option by looking for
+// the option name followed by an '=' or ':', or is taken from the
+// following option IFF:
+// - The current option does not contain a '=' or a ':'
+// - The following option is not a registered named option
+//
+// The `name' used in the option format string does NOT include any leading
+// option indicator, such as '-', '--', or '/'. All three of these are
+// permitted/required on any named option.
+//
+// Option bundling is permitted so long as:
+// - '-' is used to start the option group
+// - all of the bundled options do not require values
+// - all of the bundled options are a single character
+//
+// This allows specifying '-a -b -c' as '-abc'.
+//
+// Option processing is disabled by specifying "--". All options after "--"
+// are returned by OptionSet.Parse() unchanged and unprocessed.
+//
+// Unprocessed options are returned from OptionSet.Parse().
+//
+// Examples:
+// int verbose = 0;
+// OptionSet p = new OptionSet ()
+// .Add ("v", v => ++verbose)
+// .Add ("name=|value=", v => Console.WriteLine (v));
+// p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"});
+//
+// The above would parse the argument string array, and would invoke the
+// lambda expression three times, setting `verbose' to 3 when complete.
+// It would also print out "A" and "B" to standard output.
+// The returned array would contain the string "extra".
+//
+// C# 3.0 collection initializers are supported:
+// var p = new OptionSet () {
+// { "h|?|help", v => ShowHelp () },
+// };
+//
+// System.ComponentModel.TypeConverter is also supported, allowing the use of
+// custom data types in the callback type; TypeConverter.ConvertFromString()
+// is used to convert the value option to an instance of the specified
+// type:
+//
+// var p = new OptionSet () {
+// { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) },
+// };
+//
+// Random other tidbits:
+// - Boolean options (those w/o '=' or ':' in the option format string)
+// are explicitly enabled if they are followed with '+', and explicitly
+// disabled if they are followed with '-':
+// string a = null;
+// var p = new OptionSet () {
+// { "a", s => a = s },
+// };
+// p.Parse (new string[]{"-a"}); // sets v != null
+// p.Parse (new string[]{"-a+"}); // sets v != null
+// p.Parse (new string[]{"-a-"}); // sets v == null
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Text.RegularExpressions;
+
+#if LINQ
+using System.Linq;
+#endif
+
+#if TEST
+using NDesk.Options;
+#endif
+
+#if !LINQ
+namespace System {
+ public delegate void Action<T1, T2> (T1 a, T2 b);
+}
+#endif
+
+namespace NDesk.Options {
+
+ public enum OptionValueType {
+ None,
+ Optional,
+ Required,
+ }
+
+ public class OptionContext {
+ public OptionContext ()
+ {
+ }
+
+ public Option Option { get; set; }
+ public string OptionName { get; set; }
+ public int OptionIndex { get; set; }
+ public string OptionValue { get; set; }
+ }
+
+ public abstract class Option {
+ string prototype, description;
+ string [] names;
+ OptionValueType type;
+
+ public Option (string prototype, string description)
+ {
+ if (prototype == null)
+ throw new ArgumentNullException ("prototype");
+ if (prototype.Length == 0)
+ throw new ArgumentException ("Cannot be the empty string.", "prototype");
+
+ this.prototype = prototype;
+ this.names = prototype.Split ('|');
+ this.description = description;
+ this.type = ValidateNames ();
+ }
+
+ public string Prototype { get { return prototype; } }
+ public string Description { get { return description; } }
+ public OptionValueType OptionValueType { get { return type; } }
+
+ public string [] GetNames ()
+ {
+ return (string []) names.Clone ();
+ }
+
+ internal string [] Names { get { return names; } }
+
+ static readonly char [] NameTerminator = new char [] { '=', ':' };
+ private OptionValueType ValidateNames ()
+ {
+ char type = '\0';
+ for (int i = 0; i < names.Length; ++i) {
+ string name = names [i];
+ if (name.Length == 0)
+ throw new ArgumentException ("Empty option names are not supported.", "prototype");
+
+ int end = name.IndexOfAny (NameTerminator);
+ if (end > 0) {
+ names [i] = name.Substring (0, end);
+ if (type == '\0' || type == name [end])
+ type = name [end];
+ else
+ throw new ArgumentException (
+ string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]),
+ "prototype");
+ }
+ }
+ if (type == '\0')
+ return OptionValueType.None;
+ return type == '=' ? OptionValueType.Required : OptionValueType.Optional;
+ }
+
+ public void Invoke (OptionContext c)
+ {
+ OnParseComplete (c);
+ c.OptionName = null;
+ c.OptionValue = null;
+ c.Option = null;
+ }
+
+ protected abstract void OnParseComplete (OptionContext c);
+
+ public override string ToString ()
+ {
+ return Prototype;
+ }
+ }
+
+ [Serializable]
+ public class OptionException : Exception {
+ private string option;
+
+ public OptionException (string message, string optionName)
+ : base (message)
+ {
+ this.option = optionName;
+ }
+
+ public OptionException (string message, string optionName, Exception innerException)
+ : base (message, innerException)
+ {
+ this.option = optionName;
+ }
+
+ protected OptionException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ this.option = info.GetString ("OptionName");
+ }
+
+ public string OptionName
+ {
+ get { return this.option; }
+ }
+
+ public override void GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData (info, context);
+ info.AddValue ("OptionName", option);
+ }
+ }
+
+ public class OptionSet : Collection<Option> {
+ public OptionSet ()
+ : this (f => f)
+ {
+ }
+
+ public OptionSet (Converter<string, string> localizer)
+ {
+ this.localizer = localizer;
+ }
+
+ Dictionary<string, Option> options = new Dictionary<string, Option> ();
+ Converter<string, string> localizer;
+
+ protected Option GetOptionForName (string option)
+ {
+ if (option == null)
+ throw new ArgumentNullException ("option");
+ Option v;
+ if (options.TryGetValue (option, out v))
+ return v;
+ return null;
+ }
+
+ protected override void ClearItems ()
+ {
+ this.options.Clear ();
+ }
+
+ protected override void InsertItem (int index, Option item)
+ {
+ Add (item);
+ base.InsertItem (index, item);
+ }
+
+ protected override void RemoveItem (int index)
+ {
+ Option p = Items [index];
+ foreach (string name in p.Names) {
+ this.options.Remove (name);
+ }
+ base.RemoveItem (index);
+ }
+
+ protected override void SetItem (int index, Option item)
+ {
+ RemoveItem (index);
+ Add (item);
+ base.SetItem (index, item);
+ }
+
+ class ActionOption : Option {
+ Action<string, OptionContext> action;
+
+ public ActionOption (string prototype, string description, Action<string, OptionContext> action)
+ : base (prototype, description)
+ {
+ if (action == null)
+ throw new ArgumentNullException ("action");
+ this.action = action;
+ }
+
+ protected override void OnParseComplete (OptionContext c)
+ {
+ action (c.OptionValue, c);
+ }
+ }
+
+ public new OptionSet Add (Option option)
+ {
+ if (option == null)
+ throw new ArgumentNullException ("option");
+ List<string> added = new List<string> ();
+ try {
+ foreach (string name in option.Names) {
+ this.options.Add (name, option);
+ }
+ }
+ catch (Exception e) {
+ foreach (string name in added)
+ this.options.Remove (name);
+ throw;
+ }
+ return this;
+ }
+
+ public OptionSet Add (string options, Action<string> action)
+ {
+ return Add (options, null, action);
+ }
+
+ public OptionSet Add (string options, Action<string, OptionContext> action)
+ {
+ return Add (options, null, action);
+ }
+
+ public OptionSet Add (string options, string description, Action<string> action)
+ {
+ if (action == null)
+ throw new ArgumentNullException ("action");
+ return Add (options, description, (v, c) => { action (v); });
+ }
+
+ public OptionSet Add (string options, string description, Action<string, OptionContext> action)
+ {
+ Option p = new ActionOption (options, description, action);
+ base.Add (p);
+ return this;
+ }
+
+ public OptionSet Add<T> (string options, Action<T> action)
+ {
+ return Add (options, null, action);
+ }
+
+ public OptionSet Add<T> (string options, Action<T, OptionContext> action)
+ {
+ return Add (options, null, action);
+ }
+
+ public OptionSet Add<T> (string options, string description, Action<T> action)
+ {
+ return Add (options, description, (T v, OptionContext c) => { action (v); });
+ }
+
+ public OptionSet Add<T> (string options, string description, Action<T, OptionContext> action)
+ {
+ TypeConverter conv = TypeDescriptor.GetConverter (typeof (T));
+ Action<string, OptionContext> a = delegate (string s, OptionContext c) {
+ T t = default (T);
+ try {
+ if (s != null)
+ t = (T) conv.ConvertFromString (s);
+ }
+ catch (Exception e) {
+ throw new OptionException (
+ string.Format (
+ localizer ("Could not convert string `{0}' to type {1} for option `{2}'."),
+ s, typeof (T).Name, c.OptionName),
+ c.OptionName, e);
+ }
+ action (t, c);
+ };
+ return Add (options, description, a);
+ }
+
+ protected virtual OptionContext CreateOptionContext ()
+ {
+ return new OptionContext ();
+ }
+
+#if LINQ
+ public List<string> Parse (IEnumerable<string> options)
+ {
+ bool process = true;
+ OptionContext c = CreateOptionContext ();
+ c.OptionIndex = -1;
+ var unprocessed =
+ from option in options
+ where ++c.OptionIndex >= 0 && process
+ ? option == "--"
+ ? (process = false)
+ : !Parse (option, c)
+ : true
+ select option;
+ List<string> r = unprocessed.ToList ();
+ if (c.Option != null)
+ NoValue (c);
+ return r;
+ }
+#else
+ public List<string> Parse (IEnumerable<string> options)
+ {
+ OptionContext c = CreateOptionContext ();
+ c.OptionIndex = -1;
+ bool process = true;
+ List<string> unprocessed = new List<string> ();
+ foreach (string option in options) {
+ ++c.OptionIndex;
+ if (option == "--") {
+ process = false;
+ continue;
+ }
+ if (!process) {
+ unprocessed.Add (option);
+ continue;
+ }
+ if (!Parse (option, c))
+ unprocessed.Add (option);
+ }
+ if (c.Option != null)
+ NoValue (c);
+ return unprocessed;
+ }
+#endif
+
+ private readonly Regex ValueOption = new Regex (
+ @"^(?<flag>--|-|/)(?<name>[^:=]+)([:=](?<value>.*))?$");
+
+ protected bool GetOptionParts (string option, out string flag, out string name, out string value)
+ {
+ Match m = ValueOption.Match (option);
+ if (!m.Success) {
+ flag = name = value = null;
+ return false;
+ }
+ flag = m.Groups ["flag"].Value;
+ name = m.Groups ["name"].Value;
+ value = !m.Groups ["value"].Success ? null : m.Groups ["value"].Value;
+ return true;
+ }
+
+ protected virtual bool Parse (string option, OptionContext c)
+ {
+ if (c.Option != null) {
+ c.OptionValue = option;
+ c.Option.Invoke (c);
+ return true;
+ }
+
+ string f, n, v;
+ if (!GetOptionParts (option, out f, out n, out v))
+ return false;
+
+ Option p;
+ if (this.options.TryGetValue (n, out p)) {
+ c.OptionName = f + n;
+ c.Option = p;
+ switch (p.OptionValueType) {
+ case OptionValueType.None:
+ c.OptionValue = n;
+ c.Option.Invoke (c);
+ break;
+ case OptionValueType.Optional:
+ case OptionValueType.Required:
+ if (v != null) {
+ c.OptionValue = v;
+ c.Option.Invoke (c);
+ }
+ break;
+ }
+ return true;
+ }
+ // no match; is it a bool option?
+ if (ParseBool (option, n, c))
+ return true;
+ // is it a bundled option?
+ if (ParseBundled (f, n, c))
+ return true;
+
+ return false;
+ }
+
+ private bool ParseBool (string option, string n, OptionContext c)
+ {
+ Option p;
+ if (n.Length >= 1 && (n [n.Length - 1] == '+' || n [n.Length - 1] == '-') &&
+ this.options.TryGetValue (n.Substring (0, n.Length - 1), out p)) {
+ string v = n [n.Length - 1] == '+' ? option : null;
+ c.OptionName = option;
+ c.OptionValue = v;
+ c.Option = p;
+ p.Invoke (c);
+ return true;
+ }
+ return false;
+ }
+
+ private bool ParseBundled (string f, string n, OptionContext c)
+ {
+ Option p;
+ if (f == "-" && this.options.TryGetValue (n [0].ToString (), out p)) {
+ int i = 0;
+ do {
+ string opt = "-" + n [i].ToString ();
+ if (p.OptionValueType != OptionValueType.None) {
+ throw new OptionException (string.Format (
+ localizer ("Cannot bundle option '{0}' that requires a value."), opt),
+ opt);
+ }
+ c.OptionName = opt;
+ c.OptionValue = n;
+ c.Option = p;
+ p.Invoke (c);
+ } while (++i < n.Length && this.options.TryGetValue (n [i].ToString (), out p));
+ return true;
+ }
+ return false;
+ }
+
+ private void NoValue (OptionContext c)
+ {
+ c.OptionValue = null;
+ Option p = c.Option;
+ if (p != null && p.OptionValueType == OptionValueType.Optional) {
+ p.Invoke (c);
+ } else if (p != null && p.OptionValueType == OptionValueType.Required) {
+ throw new OptionException (string.Format (
+ localizer ("Missing required value for option '{0}'."), c.OptionName),
+ c.OptionName);
+ }
+ }
+
+ private const int OptionWidth = 29;
+
+ public void WriteOptionDescriptions (TextWriter o)
+ {
+ foreach (Option p in this) {
+ List<string> names = new List<string> (p.Names);
+
+ int written = 0;
+ if (names [0].Length == 1) {
+ Write (o, ref written, " -");
+ Write (o, ref written, names [0]);
+ } else {
+ Write (o, ref written, " --");
+ Write (o, ref written, names [0]);
+ }
+
+ for (int i = 1; i < names.Count; ++i) {
+ Write (o, ref written, ", ");
+ Write (o, ref written, names [i].Length == 1 ? "-" : "--");
+ Write (o, ref written, names [i]);
+ }
+
+ if (p.OptionValueType == OptionValueType.Optional)
+ Write (o, ref written, localizer ("[=VALUE]"));
+ else if (p.OptionValueType == OptionValueType.Required)
+ Write (o, ref written, localizer ("=VALUE"));
+
+ if (written < OptionWidth)
+ o.Write (new string (' ', OptionWidth - written));
+ else {
+ o.WriteLine ();
+ o.Write (new string (' ', OptionWidth));
+ }
+
+ o.WriteLine (localizer (p.Description));
+ }
+ }
+
+ static void Write (TextWriter o, ref int n, string s)
+ {
+ n += s.Length;
+ o.Write (s);
+ }
+ }
+}
+
+#if TEST
+namespace Tests.NDesk.Options {
+
+ using System.Linq;
+
+ class FooConverter : TypeConverter {
+ public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType)
+ {
+ if (sourceType == typeof (string))
+ return true;
+ return base.CanConvertFrom (context, sourceType);
+ }
+
+ public override object ConvertFrom (ITypeDescriptorContext context,
+ CultureInfo culture, object value)
+ {
+ string v = value as string;
+ if (v != null) {
+ switch (v) {
+ case "A": return Foo.A;
+ case "B": return Foo.B;
+ }
+ }
+
+ return base.ConvertFrom (context, culture, value);
+ }
+ }
+
+ [TypeConverter (typeof(FooConverter))]
+ class Foo {
+ public static readonly Foo A = new Foo ("A");
+ public static readonly Foo B = new Foo ("B");
+ string s;
+ Foo (string s) { this.s = s; }
+ public override string ToString () {return s;}
+ }
+
+ class Test {
+ public static void Main (string[] args)
+ {
+ var tests = new Dictionary<string, Action> () {
+ { "boolean", () => CheckBoolean () },
+ { "bundling", () => CheckOptionBundling () },
+ { "context", () => CheckOptionContext () },
+ { "descriptions", () => CheckWriteOptionDescriptions () },
+ { "exceptions", () => CheckExceptions () },
+ { "halt", () => CheckHaltProcessing () },
+ { "localization", () => CheckLocalization () },
+ { "many", () => CheckMany () },
+ { "optional", () => CheckOptional () },
+ { "required", () => CheckRequired () },
+ { "derived-type", () => CheckDerivedType () },
+ };
+ bool run = true;
+ bool help = false;
+ var p = new OptionSet () {
+ { "t|test=",
+ "Run the specified test. Valid tests:\n" + new string (' ', 32) +
+ string.Join ("\n" + new string (' ', 32), tests.Keys.OrderBy (s => s).ToArray ()),
+ v => { run = false; Console.WriteLine (v); tests [v] (); } },
+ { "h|?|help", "Show this message and exit", (v) => help = v != null },
+ };
+ p.Parse (args);
+ if (help) {
+ Console.WriteLine ("usage: Options.exe [OPTION]+\n");
+ Console.WriteLine ("Options unit test program.");
+ Console.WriteLine ("Valid options include:");
+ p.WriteOptionDescriptions (Console.Out);
+ } else if (run) {
+ foreach (Action a in tests.Values)
+ a ();
+ }
+ }
+
+ static IEnumerable<string> _ (params string[] a)
+ {
+ return a;
+ }
+
+ static void CheckRequired ()
+ {
+ string a = null;
+ int n = 0;
+ OptionSet p = new OptionSet () {
+ { "a=", v => a = v },
+ { "n=", (int v) => n = v },
+ };
+ List<string> extra = p.Parse (_("a", "-a", "s", "-n=42", "n"));
+ Assert (extra.Count, 2);
+ Assert (extra [0], "a");
+ Assert (extra [1], "n");
+ Assert (a, "s");
+ Assert (n, 42);
+
+ extra = p.Parse (_("-a="));
+ Assert (extra.Count, 0);
+ Assert (a, "");
+ }
+
+ static void CheckOptional ()
+ {
+ string a = null;
+ int n = -1;
+ Foo f = null;
+ OptionSet p = new OptionSet () {
+ { "a:", v => a = v },
+ { "n:", (int v) => n = v },
+ { "f:", (Foo v) => f = v },
+ };
+ p.Parse (_("-a=s"));
+ Assert (a, "s");
+ p.Parse (_("-a"));
+ Assert (a, null);
+ p.Parse (_("-a="));
+ Assert (a, "");
+
+ p.Parse (_("-f", "A"));
+ Assert (f, Foo.A);
+ p.Parse (_("-f"));
+ Assert (f, null);
+
+ p.Parse (_("-n", "42"));
+ Assert (n, 42);
+ p.Parse (_("-n"));
+ Assert (n, 0);
+ }
+
+ static void CheckBoolean ()
+ {
+ bool a = false;
+ OptionSet p = new OptionSet () {
+ { "a", v => a = v != null },
+ };
+ p.Parse (_("-a"));
+ Assert (a, true);
+ p.Parse (_("-a+"));
+ Assert (a, true);
+ p.Parse (_("-a-"));
+ Assert (a, false);
+ }
+
+ static void CheckMany ()
+ {
+ int a = -1, b = -1;
+ string av = null, bv = null;
+ Foo f = null;
+ int help = 0;
+ int verbose = 0;
+ OptionSet p = new OptionSet () {
+ { "a=", v => { a = 1; av = v; } },
+ { "b", "desc", v => {b = 2; bv = v;} },
+ { "f=", (Foo v) => f = v },
+ { "v", v => { ++verbose; } },
+ { "h|?|help", (v) => { switch (v) {
+ case "h": help |= 0x1; break;
+ case "?": help |= 0x2; break;
+ case "help": help |= 0x4; break;
+ } } },
+ };
+ List<string> e = p.Parse (new string[]{"foo", "-v", "-a=42", "/b-",
+ "-a", "64", "bar", "--f", "B", "/h", "-?", "--help", "-v"});
+
+ Assert (e.Count, 2);
+ Assert (e[0], "foo");
+ Assert (e[1], "bar");
+ Assert (a, 1);
+ Assert (av, "64");
+ Assert (b, 2);
+ Assert (bv, null);
+ Assert (verbose, 2);
+ Assert (help, 0x7);
+ Assert (f, Foo.B);
+ }
+
+ static void Assert<T>(T actual, T expected)
+ {
+ if (!object.Equals (actual, expected))
+ throw new InvalidOperationException (
+ string.Format ("Assertion failed: {0} != {1}", actual, expected));
+ }
+
+ class DefaultOption : Option {
+ public DefaultOption (string prototypes, string description)
+ : base (prototypes, description)
+ {
+ }
+
+ protected override void OnParseComplete (OptionContext c)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+
+ static void CheckExceptions ()
+ {
+ string a = null;
+ var p = new OptionSet () {
+ { "a=", v => a = v },
+ { "c", v => { } },
+ { "n=", (int v) => { } },
+ { "f=", (Foo v) => { } },
+ };
+ // missing argument
+ AssertException (typeof(OptionException),
+ "Missing required value for option '-a'.",
+ p, v => { v.Parse (_("-a")); });
+ // another named option while expecting one -- follow Getopt::Long
+ AssertException (null, null,
+ p, v => { v.Parse (_("-a", "-a")); });
+ Assert (a, "-a");
+ // no exception when an unregistered named option follows.
+ AssertException (null, null,
+ p, v => { v.Parse (_("-a", "-b")); });
+ Assert (a, "-b");
+ AssertException (typeof(ArgumentNullException),
+ "Argument cannot be null.\nParameter name: option",
+ p, v => { v.Add (null); });
+
+ // bad type
+ AssertException (typeof(OptionException),
+ "Could not convert string `value' to type Int32 for option `-n'.",
+ p, v => { v.Parse (_("-n", "value")); });
+ AssertException (typeof(OptionException),
+ "Could not convert string `invalid' to type Foo for option `--f'.",
+ p, v => { v.Parse (_("--f", "invalid")); });
+
+ // try to bundle with an option requiring a value
+ AssertException (typeof(OptionException),
+ "Cannot bundle option '-a' that requires a value.",
+ p, v => { v.Parse (_("-ca", "value")); });
+
+ AssertException (typeof(ArgumentNullException),
+ "Argument cannot be null.\nParameter name: prototype",
+ p, v => { new DefaultOption (null, null); });
+ AssertException (typeof(ArgumentException),
+ "Cannot be the empty string.\nParameter name: prototype",
+ p, v => { new DefaultOption ("", null); });
+ AssertException (typeof(ArgumentException),
+ "Empty option names are not supported.\nParameter name: prototype",
+ p, v => { new DefaultOption ("a|b||c=", null); });
+ AssertException (typeof(ArgumentException),
+ "Conflicting option types: '=' vs. ':'.\nParameter name: prototype",
+ p, v => { new DefaultOption ("a=|b:", null); });
+ AssertException (typeof(ArgumentNullException),
+ "Argument cannot be null.\nParameter name: action",
+ p, v => { v.Add ("foo", (Action<string>) null); });
+ AssertException (typeof(ArgumentNullException),
+ "Argument cannot be null.\nParameter name: action",
+ p, v => { v.Add ("foo", (Action<string, OptionContext>) null); });
+ }
+
+ static void AssertException<T> (Type exception, string message, T a, Action<T> action)
+ {
+ Type actualType = null;
+ string stack = null;
+ string actualMessage = null;
+ try {
+ action (a);
+ }
+ catch (Exception e) {
+ actualType = e.GetType ();
+ actualMessage = e.Message;
+ if (!object.Equals (actualType, exception))
+ stack = e.ToString ();
+ }
+ if (!object.Equals (actualType, exception)) {
+ throw new InvalidOperationException (
+ string.Format ("Assertion failed: Expected Exception Type {0}, got {1}.\n" +
+ "Actual Exception: {2}", exception, actualType, stack));
+ }
+ if (!object.Equals (actualMessage, message))
+ throw new InvalidOperationException (
+ string.Format ("Assertion failed:\n\tExpected: {0}\n\t Actual: {1}",
+ message, actualMessage));
+ }
+
+ static void CheckWriteOptionDescriptions ()
+ {
+ var p = new OptionSet () {
+ { "p|indicator-style=", "append / indicator to directories", v => {} },
+ { "color:", "controls color info", v => {} },
+ { "h|?|help", "show help text", v => {} },
+ { "version", "output version information and exit", v => {} },
+ };
+
+ StringWriter expected = new StringWriter ();
+ expected.WriteLine (" -p, --indicator-style=VALUE");
+ expected.WriteLine (" append / indicator to directories");
+ expected.WriteLine (" --color[=VALUE] controls color info");
+ expected.WriteLine (" -h, -?, --help show help text");
+ expected.WriteLine (" --version output version information and exit");
+
+ StringWriter actual = new StringWriter ();
+ p.WriteOptionDescriptions (actual);
+
+ Assert (actual.ToString (), expected.ToString ());
+ }
+
+ static void CheckOptionBundling ()
+ {
+ string a, b, c;
+ a = b = c = null;
+ var p = new OptionSet () {
+ { "a", v => a = "a" },
+ { "b", v => b = "b" },
+ { "c", v => c = "c" },
+ };
+ p.Parse (_ ("-abc"));
+ Assert (a, "a");
+ Assert (b, "b");
+ Assert (c, "c");
+ }
+
+ static void CheckHaltProcessing ()
+ {
+ var p = new OptionSet () {
+ { "a", v => {} },
+ { "b", v => {} },
+ };
+ List<string> e = p.Parse (_ ("-a", "-b", "--", "-a", "-b"));
+ Assert (e.Count, 2);
+ Assert (e [0], "-a");
+ Assert (e [1], "-b");
+ }
+
+ static void CheckLocalization ()
+ {
+ var p = new OptionSet (f => "hello!") {
+ { "n=", (int v) => { } },
+ };
+ AssertException (typeof(OptionException), "hello!",
+ p, v => { v.Parse (_("-n=value")); });
+
+ StringWriter expected = new StringWriter ();
+ expected.WriteLine (" -nhello! hello!");
+
+ StringWriter actual = new StringWriter ();
+ p.WriteOptionDescriptions (actual);
+
+ Assert (actual.ToString (), expected.ToString ());
+ }
+
+ class CiOptionSet : OptionSet {
+ protected override void InsertItem (int index, Option item)
+ {
+ if (item.Prototype.ToLower () != item.Prototype)
+ throw new ArgumentException ("prototypes must be null!");
+ base.InsertItem (index, item);
+ }
+
+ protected override bool Parse (string option, OptionContext c)
+ {
+ if (c.Option != null)
+ return base.Parse (option, c);
+ string f, n, v;
+ if (!GetOptionParts (option, out f, out n, out v)) {
+ return base.Parse (option, c);
+ }
+ return base.Parse (f + n.ToLower () + (v != null ? "=" + v : ""), c);
+ }
+
+ public new Option GetOptionForName (string n)
+ {
+ return base.GetOptionForName (n);
+ }
+ }
+
+ static void CheckDerivedType ()
+ {
+ bool help = false;
+ var p = new CiOptionSet () {
+ { "h|help", v => help = v != null },
+ };
+ p.Parse (_("-H"));
+ Assert (help, true);
+ help = false;
+ p.Parse (_("-HELP"));
+ Assert (help, true);
+
+ Assert (p.GetOptionForName ("h"), p [0]);
+ Assert (p.GetOptionForName ("help"), p [0]);
+ Assert (p.GetOptionForName ("invalid"), null);
+
+ AssertException (typeof(ArgumentException), "prototypes must be null!",
+ p, v => { v.Add ("N|NUM=", (int n) => {}); });
+ AssertException (typeof(ArgumentNullException),
+ "Argument cannot be null.\nParameter name: option",
+ p, v => { v.GetOptionForName (null); });
+ }
+
+ static void CheckOptionContext ()
+ {
+ var p = new OptionSet () {
+ { "a=", "a desc", (v,c) => {
+ Assert (v, "a-val");
+ Assert (c.Option.Description, "a desc");
+ Assert (c.OptionName, "/a");
+ Assert (c.OptionIndex, 1);
+ Assert (c.OptionValue, v);
+ } },
+ { "b", "b desc", (v, c) => {
+ Assert (v, "--b+");
+ Assert (c.Option.Description, "b desc");
+ Assert (c.OptionName, "--b+");
+ Assert (c.OptionIndex, 2);
+ Assert (c.OptionValue, v);
+ } },
+ { "c=", "c desc", (v, c) => {
+ Assert (v, "C");
+ Assert (c.Option.Description, "c desc");
+ Assert (c.OptionName, "--c");
+ Assert (c.OptionIndex, 3);
+ Assert (c.OptionValue, v);
+ } },
+ { "d", "d desc", (v, c) => {
+ Assert (v, null);
+ Assert (c.Option.Description, "d desc");
+ Assert (c.OptionName, "/d-");
+ Assert (c.OptionIndex, 4);
+ Assert (c.OptionValue, v);
+ } },
+ };
+ p.Parse (_("/a", "a-val", "--b+", "--c=C", "/d-"));
+ }
+ }
+}
+#endif
+
diff --git a/gendarme/console/IResultWriter.cs b/gendarme/console/ResultWriter.cs
index 281990de..70124aa7 100644
--- a/gendarme/console/IResultWriter.cs
+++ b/gendarme/console/ResultWriter.cs
@@ -1,5 +1,5 @@
//
-// IResultWriter interface
+// ResultWriter base class
//
// Authors:
// Christian Birkl <christian.birkl@gmail.com>
@@ -28,20 +28,47 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System;
-using System.Collections;
-
using Gendarme.Framework;
-namespace Gendarme.Console.Writers {
+namespace Gendarme {
+
+ abstract public class ResultWriter {
+
+ private IRunner runner;
+ private string filename;
+
+ protected ResultWriter (IRunner runner, string fileName)
+ {
+ this.runner = runner;
+ this.filename = fileName;
+ }
+
+ protected IRunner Runner {
+ get { return runner; }
+ }
+
+ protected string FileName {
+ get { return filename; }
+ set { filename = value; }
+ }
+
+ protected virtual void Start ()
+ {
+ }
- public interface IResultWriter {
+ protected virtual void Write ()
+ {
+ }
- void Start ();
- void End ();
+ protected virtual void Finish ()
+ {
+ }
- void Write (IDictionary assemblies);
- void Write (Rules rules);
- void Write (Violation v);
+ public void Report ()
+ {
+ Start ();
+ Write ();
+ Finish ();
+ }
}
}
diff --git a/gendarme/console/TextResultWriter.cs b/gendarme/console/TextResultWriter.cs
index 67dc8d55..4c22c1d9 100644
--- a/gendarme/console/TextResultWriter.cs
+++ b/gendarme/console/TextResultWriter.cs
@@ -29,70 +29,75 @@
//
using System;
-using System.Collections;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
+
+using Mono.Cecil;
using Gendarme.Framework;
-namespace Gendarme.Console.Writers {
+namespace Gendarme {
- public class TextResultWriter : IResultWriter {
+ public class TextResultWriter : ResultWriter, IDisposable {
private TextWriter writer;
private bool need_closing;
- private int index;
- public TextResultWriter (string output)
+ public TextResultWriter (IRunner runner, string fileName)
+ : base (runner, fileName)
{
- if ((output == null) || (output.Length == 0))
+ if ((fileName == null) || (fileName.Length == 0))
writer = System.Console.Out;
else {
- writer = new StreamWriter (output);
+ writer = new StreamWriter (fileName);
need_closing = true;
}
}
- public void Start ()
+ protected override void Write ()
{
- index = 0;
- }
+ int index = 0;
- public void End ()
- {
- if (need_closing)
- writer.Close ();
- }
+ var query = from n in Runner.Defects
+ orderby n.Severity
+ select n;
- public void Write (IDictionary assemblies)
- {
+ foreach (Defect defect in query) {
+ IRule rule = defect.Rule;
+
+ writer.WriteLine ("{0}. {1}", ++index, rule.Name);
+ writer.WriteLine ();
+ writer.WriteLine ("Problem: {0}", rule.Problem);
+ writer.WriteLine ();
+ writer.WriteLine ("Details [Severity: {0}, Confidence: {1}]", defect.Severity, defect.Confidence);
+ writer.WriteLine ("* Target: {0}", defect.Target);
+ writer.WriteLine ("* Location: {0}", defect.Location);
+ if (!String.IsNullOrEmpty (defect.Text))
+ writer.WriteLine ("* {0}", defect.Text);
+ writer.WriteLine ();
+ writer.WriteLine ("Solution: {0}", rule.Solution);
+ writer.WriteLine ();
+ writer.WriteLine ("More info available at: {0}", rule.Uri.ToString ());
+ writer.WriteLine ();
+ writer.WriteLine ();
+ }
}
- public void Write (Rules rules)
+ public void Dispose ()
{
+ Dispose (true);
+ GC.SuppressFinalize (this);
}
- public void Write (Violation v)
+ protected virtual void Dispose (bool disposing)
{
- RuleInformation ri = RuleInformationManager.GetRuleInformation (v.Rule);
- writer.WriteLine ("{0}. {1}", ++index, ri.Name);
- writer.WriteLine ();
- writer.WriteLine ("Problem: {0}", String.Format (ri.Problem, v.Violator));
- writer.WriteLine ();
- if (v.Messages != null && v.Messages.Count > 0) {
- writer.WriteLine ("Details:");
- foreach (Message message in v.Messages) {
- writer.WriteLine (" {0}", message);
+ if (disposing) {
+ if (need_closing) {
+ writer.Close ();
+ writer.Dispose ();
}
- writer.WriteLine ();
- }
- writer.WriteLine ("Solution: {0}", String.Format (ri.Solution, v.Violator));
- writer.WriteLine ();
- string url = ri.Uri;
- if (url.Length > 0) {
- writer.WriteLine ("More info available at: {0}", url);
- writer.WriteLine ();
}
- writer.WriteLine ();
}
}
}
diff --git a/gendarme/console/XmlResultWriter.cs b/gendarme/console/XmlResultWriter.cs
index be34fb25..0c5788bc 100644
--- a/gendarme/console/XmlResultWriter.cs
+++ b/gendarme/console/XmlResultWriter.cs
@@ -1,5 +1,5 @@
//
-// ResultWriter
+// XmlResultWriter
//
// Authors:
// Christian Birkl <christian.birkl@gmail.com>
@@ -29,29 +29,31 @@
//
using System;
-using System.Collections;
+using System.Linq;
using System.Text;
using System.Xml;
-using System.Xml.Serialization;
-using Gendarme.Framework;
using Mono.Cecil;
-namespace Gendarme.Console.Writers {
+using Gendarme.Framework;
+using Gendarme.Framework.Rocks;
+
+namespace Gendarme {
- public class XmlResultWriter : IResultWriter {
+ public class XmlResultWriter : ResultWriter, IDisposable {
private XmlTextWriter writer;
- public XmlResultWriter (string output)
+ public XmlResultWriter (IRunner runner, string fileName)
+ : base (runner, fileName)
{
- if ((output == null) || (output.Length == 0))
+ if ((fileName == null) || (fileName.Length == 0))
writer = new XmlTextWriter (System.Console.Out);
else
- writer = new XmlTextWriter (output, Encoding.UTF8);
+ writer = new XmlTextWriter (fileName, Encoding.UTF8);
}
- public void Start ()
+ protected override void Start ()
{
writer.Formatting = Formatting.Indented;
writer.WriteProcessingInstruction ("xml", "version='1.0'");
@@ -59,74 +61,101 @@ namespace Gendarme.Console.Writers {
writer.WriteAttributeString ("date", DateTime.UtcNow.ToString ());
}
- public void End ()
+ protected override void Write ()
{
+ writer.WriteStartElement ("files");
+ foreach (AssemblyDefinition assembly in Runner.Assemblies) {
+ writer.WriteStartElement ("file");
+ writer.WriteAttributeString ("Name", assembly.Name.ToString ());
+ IAnnotationProvider provider = (assembly as IAnnotationProvider);
+ if (provider.Annotations.Contains ("filename")) {
+ writer.WriteString (provider.Annotations ["filename"] as string);
+ }
+ writer.WriteEndElement ();
+ }
writer.WriteEndElement ();
- writer.Flush ();
- writer.Close ();
- writer = null;
- }
- public void Write (IDictionary assemblies)
- {
- foreach (DictionaryEntry de in assemblies) {
- writer.WriteStartElement ("input");
- AssemblyDefinition ad = (de.Value as AssemblyDefinition);
- if (ad != null)
- writer.WriteAttributeString ("Name", ad.Name.ToString ());
- writer.WriteString ((string) de.Key);
+ writer.WriteStartElement ("rules");
+ foreach (IRule rule in Runner.Rules) {
+ if (rule is IAssemblyRule)
+ WriteRule (rule, "Assembly");
+ if (rule is ITypeRule)
+ WriteRule (rule, "Type");
+ if (rule is IMethodRule)
+ WriteRule (rule, "Method");
+ }
+ writer.WriteEndElement ();
+
+ var query = from n in Runner.Defects
+ orderby n.Assembly.Name.FullName, n.Rule.Name
+ group n by n.Rule into a
+ select new {
+ Rule = a.Key,
+ Value = from o in a
+ group o by o.Target into r
+ select new {
+ Target = r.Key,
+ Value = r
+ }
+ };
+
+ writer.WriteStartElement ("results");
+ foreach (var value in query) {
+ writer.WriteStartElement ("rule");
+ writer.WriteAttributeString ("Name", value.Rule.Name);
+ writer.WriteAttributeString ("Uri", value.Rule.Uri.ToString ());
+ writer.WriteElementString ("problem", value.Rule.Problem);
+ writer.WriteElementString ("solution", value.Rule.Solution);
+ foreach (var v2 in value.Value) {
+ writer.WriteStartElement ("target");
+ writer.WriteAttributeString ("Name", v2.Target.ToString ());
+ writer.WriteAttributeString ("Assembly", v2.Target.GetAssembly ().Name.FullName);
+ foreach (var v3 in v2.Value) {
+ writer.WriteStartElement ("defect");
+ writer.WriteAttributeString ("Severity", v3.Severity.ToString ());
+ writer.WriteAttributeString ("Confidence", v3.Confidence.ToString ());
+ writer.WriteAttributeString ("Location", v3.Location.ToString ());
+ writer.WriteAttributeString ("Source", v3.Source);
+ writer.WriteString (v3.Text);
+ writer.WriteEndElement ();
+ }
+ writer.WriteEndElement ();
+ }
writer.WriteEndElement ();
}
+ writer.WriteEndElement ();
}
- public void Write (Rules rules)
+ protected override void Finish ()
{
- writer.WriteStartElement ("rules");
- Rules ("Assembly", rules.Assembly);
- Rules ("Module", rules.Module);
- Rules ("Type", rules.Type);
- Rules ("Method", rules.Method);
writer.WriteEndElement ();
+ writer.Flush ();
}
- private void Rules (string type, RuleCollection rules)
+ private void WriteRule (IRule rule, string type)
{
- foreach (IRule rule in rules) {
- RuleInformation info = RuleInformationManager.GetRuleInformation (rule);
- writer.WriteStartElement ("rule");
- writer.WriteAttributeString ("Name", info.Name);
- writer.WriteAttributeString ("Type", type);
- writer.WriteAttributeString ("Uri", info.Uri);
- writer.WriteString (rule.GetType ().FullName);
- writer.WriteEndElement ();
- }
+ writer.WriteStartElement ("rule");
+ writer.WriteAttributeString ("Name", rule.Name);
+ writer.WriteAttributeString ("Type", type);
+ writer.WriteAttributeString ("Uri", rule.Uri.ToString ());
+ writer.WriteString (rule.GetType ().FullName);
+ writer.WriteEndElement ();
}
- public void Write (Violation v)
+ public void Dispose ()
{
- RuleInformation ri = RuleInformationManager.GetRuleInformation (v.Rule);
-
- writer.WriteStartElement ("violation");
- writer.WriteAttributeString ("Assembly", v.Assembly.ToString ());
- writer.WriteAttributeString ("Name", ri.Name);
- writer.WriteAttributeString ("Uri", ri.Uri);
- writer.WriteElementString ("problem", String.Format (ri.Problem, v.Violator));
- writer.WriteElementString ("solution", String.Format (ri.Solution, v.Violator));
-
- if ((v.Messages != null) && (v.Messages.Count > 0)) {
- writer.WriteStartElement ("messages");
- foreach (Message message in v.Messages) {
- writer.WriteStartElement ("message");
- if (message.Location != null)
- writer.WriteAttributeString ("Location", message.Location.ToString ());
- writer.WriteAttributeString ("Type", message.Type.ToString ());
- writer.WriteString (message.Text);
- writer.WriteEndElement ();
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (disposing) {
+ if (writer != null) {
+ writer.Close ();
+ writer = null;
}
- writer.WriteEndElement ();
}
-
- writer.WriteEndElement ();
}
}
}
diff --git a/gendarme/console/gendarme.xsl b/gendarme/console/gendarme.xsl
index ac7cfc7f..1c328128 100644
--- a/gendarme/console/gendarme.xsl
+++ b/gendarme/console/gendarme.xsl
@@ -1,166 +1,181 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
- <xsl:output method="html" encoding="iso-8859-1" />
- <xsl:template name="print-defect-rules">
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:output method="html" encoding="iso-8859-1" />
+ <xsl:template name="print-defect-rules">
<xsl:param name="name" />
- : <xsl:value-of select="count(//violation[@Name = $name])" /> defects
- </xsl:template>
- <xsl:template name="print-rules">
- <xsl:param name="type" />
- <p>
- <b><xsl:value-of select="$type" /></b>:
- <xsl:choose>
- <xsl:when test="count(rules/rule[@Type = $type]) = 0">
- <ul>
- <li>None</li>
- </ul>
- </xsl:when>
- <xsl:otherwise>
- <ul>
- <xsl:for-each select="rules/rule[@Type = $type]">
+ : <xsl:value-of select="count(//rule[@Name = $name]/target/defect)" /> defects
+ </xsl:template>
+ <xsl:template name="print-rules">
+ <xsl:param name="type" />
+ <p>
+ <b><xsl:value-of select="$type" /></b>:
+ <xsl:choose>
+ <xsl:when test="count(rules/rule[@Type = $type]) = 0">
+ <ul>
+ <li>None</li>
+ </ul>
+ </xsl:when>
+ <xsl:otherwise>
+ <ul>
+ <xsl:for-each select="rules/rule[@Type = $type]">
<li>
<a href="{@Uri}" target="{@Name}"><xsl:value-of select="text()" /></a>
- <xsl:call-template name="print-defect-rules">
+ <xsl:call-template name="print-defect-rules">
<xsl:with-param name="name">
<xsl:value-of select="@Name" />
- </xsl:with-param>
- </xsl:call-template>
- </li>
- </xsl:for-each>
- </ul>
- </xsl:otherwise>
- </xsl:choose>
- </p>
- </xsl:template>
- <xsl:template match="/">
- <xsl:for-each select="gendarme-output">
- <html>
- <head>
- <title>Gendarme Report</title>
- </head>
- <style type="text/css">
- h1, h2, h3 {
- font-family: Verdana;
- color: #68892F;
- }
- h2 {
- font-size: 14pt;
- }
-
- p, li, b {
- font-family: Verdana;
- font-size: 11pt;
- }
- p.where, p.problem, p.found, p.solution {
- background-color: #F6F6F6;
- border: 1px solid #DDDDDD;
- padding: 10px;
+ </xsl:with-param>
+ </xsl:call-template>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:otherwise>
+ </xsl:choose>
+ </p>
+ </xsl:template>
+ <xsl:template match="/">
+ <xsl:for-each select="gendarme-output">
+ <html>
+ <head>
+ <title>Gendarme Report</title>
+ </head>
+ <style type="text/css">
+ h1, h2, h3 {
+ font-family: Verdana;
+ color: #68892F;
+ }
+ h2 {
+ font-size: 14pt;
+ }
+
+ p, li, b {
+ font-family: Verdana;
+ font-size: 11pt;
+ }
+ p.where, p.problem, p.found, p.solution {
+ background-color: #F6F6F6;
+ border: 1px solid #DDDDDD;
+ padding: 10px;
}
span.found {
- padding: 10px;
- }
- div.toc {
- background-color: #F6F6F6;
- border: 1px solid #DDDDDD;
- padding: 10px;
- float: right;
- width: 300px;
- }
- a:link, a:active, a:hover, a:visited {
- color: #9F75AD;
- font-weight: bold;
- text-decoration: none;
- }
- </style>
- <body>
-
- <h1>Gendarme Report</h1>
+ margin-left: 10px;
+ }
+ div.toc {
+ background-color: #F6F6F6;
+ border: 1px solid #DDDDDD;
+ padding: 10px;
+ float: right;
+ width: 300px;
+ }
+ a:link, a:active, a:hover, a:visited {
+ color: #9F75AD;
+ font-weight: bold;
+ text-decoration: none;
+ }
+ </style>
+ <body>
+
+ <h1>Gendarme Report</h1>
<p>Produced on <xsl:value-of select="@date" /> UTC.</p>
-
- <div class="toc">
- <div align="center">
- <b style="font-size: 10pt;">Table of contents</b>
- </div>
- <p style="font-size: 10pt;">
- <a href="#s1">1&#160;&#160;Summary</a><br />
- <a href="#s1_1">&#160;&#160;1.1&#160;&#160;List of assemblies searched</a><br />
- <a href="#s1_2">&#160;&#160;1.2&#160;&#160;List of rules used</a><br />
- <a href="#s2">2&#160;&#160;Reported defects</a><br />
- </p>
- </div>
- <h1><a name="s1">Summary</a></h1>
-
- <p>
- <h2>List of assemblies analyzed</h2>
- <ul>
- <xsl:for-each select="input">
- <xsl:variable name="assembly"><xsl:value-of select="@Name" /></xsl:variable>
-
- <li><xsl:value-of select="text()" />: <xsl:value-of select="count(//violation[@Assembly = $assembly])" /> defects</li>
- </xsl:for-each>
- </ul>
- </p>
-
- <p>
- <h2>List of rules used</h2>
-
- <xsl:call-template name="print-rules">
- <xsl:with-param name="type">Assembly</xsl:with-param>
- </xsl:call-template>
-
- <xsl:call-template name="print-rules">
- <xsl:with-param name="type">Module</xsl:with-param>
- </xsl:call-template>
-
- <xsl:call-template name="print-rules">
- <xsl:with-param name="type">Type</xsl:with-param>
- </xsl:call-template>
-
- <xsl:call-template name="print-rules">
- <xsl:with-param name="type">Method</xsl:with-param>
- </xsl:call-template>
- </p>
-
- <h1><a name="s2">Reported Defects</a></h1>
-
- <p>
- <xsl:for-each select="violation">
+
+ <div class="toc">
+ <div align="center">
+ <b style="font-size: 10pt;">Table of contents</b>
+ </div>
+ <p style="font-size: 10pt;">
+ <a href="#s1">1&#160;&#160;Summary</a><br />
+ <a href="#s1_1">&#160;&#160;1.1&#160;&#160;List of assemblies searched</a><br />
+ <a href="#s1_2">&#160;&#160;1.2&#160;&#160;List of rules used</a><br />
+ <a href="#s2">2&#160;&#160;Reported defects</a><br />
+ <xsl:for-each select="results/rule">
+ <a href="#{@Name}">&#160;&#160;2.<xsl:value-of select="position()" />&#160;<xsl:value-of select="@Name" /><br />
+ </a>
+ </xsl:for-each>
+ </p>
+ </div>
+ <h1><a name="s1">Summary</a></h1>
+ <p>
+ <a href="http://www.mono-project.com/Gendarme">Gendarme</a> found <xsl:value-of select="count(//rule/target/defect)" /> defects using <xsl:value-of select="count(//rules/rule)" /> rules.
+ </p>
+ <p>
+ <h2>List of assemblies analyzed</h2>
+ <ul>
+ <xsl:for-each select="files/file">
+ <xsl:variable name="file">
+ <xsl:value-of select="@Name" />
+ </xsl:variable>
+
+ <li><xsl:value-of select="text()" />: <xsl:value-of select="count(//target[@Assembly = $file])" /> defects</li>
+ </xsl:for-each>
+ </ul>
+ </p>
+
+ <p>
+ <h2>List of rules used</h2>
+
+ <xsl:call-template name="print-rules">
+ <xsl:with-param name="type">Assembly</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="print-rules">
+ <xsl:with-param name="type">Type</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="print-rules">
+ <xsl:with-param name="type">Method</xsl:with-param>
+ </xsl:call-template>
+ </p>
+
+ <h1><a name="s2">Reported Defects</a></h1>
+
+ <p>
+ <xsl:for-each select="results/rule">
<h3><xsl:value-of select="position()" />&#160;
+ <a name="{@Name}" />
<a href="{@Uri}" target="{@Name}">
<xsl:value-of select="@Name" />
</a>
</h3>
- <b>Problem:</b>
- <p class="problem">
- <xsl:value-of select="problem" />
+ <b>Problem:</b>
+ <p class="problem">
+ <xsl:value-of select="problem" />
</p>
<b>Found in:</b>
- <p class="found">
- Assembly Qualified Name: <i><xsl:value-of select="@Assembly" /></i><br/>
- <xsl:if test="count(messages/message) != 0">
- <xsl:for-each select="messages/message">
- <br/>
+ <xsl:if test="count(target) != 0">
+ <xsl:for-each select="target">
+ <p class="found">
+ <b>Target:</b>&#160;<xsl:value-of select="@Name" /><br/>
+ <b>Assembly:</b>&#160;<xsl:value-of select="@Assembly" /><br/>
+ <xsl:for-each select="defect">
<!-- FIXME: use different color/style for warnings versus errors -->
- <b>Location:</b>&#160;<xsl:value-of select="@Location" />
- <br/>
- <span class="found">
- <xsl:value-of select="." />
- </span>
- <br/>
- </xsl:for-each>
- </xsl:if>
- </p>
+ <span class="found">
+ <br/>
+ <b>Severity:</b>&#160;<xsl:value-of select="@Severity" />&#160;
+ <b>Confidence:</b>&#160;<xsl:value-of select="@Confidence" /><br/>
+ <xsl:if test="@Location != (../@Name)">
+ <b>Location:</b>&#160;<xsl:value-of select="@Location" /><br/>
+ </xsl:if>
+ <xsl:if test="string-length(@Source) &gt; 0">
+ <b>Source:</b>&#160;<xsl:value-of select="@Source" /><br/>
+ </xsl:if>
+ <xsl:if test="string-length(.) &gt; 0">
+ <b>Details:</b>&#160;<xsl:value-of select="." /><br/>
+ </xsl:if>
+ </span>
+ </xsl:for-each>
+ </p>
+ </xsl:for-each>
+ </xsl:if>
- <b>Solution:</b>
- <p class="solution">
- <xsl:value-of select="solution" />
- </p>
- </xsl:for-each>
- </p>
- </body>
- </html>
- </xsl:for-each>
- </xsl:template>
+ <b>Solution:</b>
+ <p class="solution">
+ <xsl:value-of select="solution" />
+ </p>
+ </xsl:for-each>
+ </p>
+ </body>
+ </html>
+ </xsl:for-each>
+ </xsl:template>
</xsl:stylesheet>