diff options
author | Gonzalo Paniagua Javier <gonzalo.mono@gmail.com> | 2009-04-08 05:49:51 +0400 |
---|---|---|
committer | Gonzalo Paniagua Javier <gonzalo.mono@gmail.com> | 2009-04-08 05:49:51 +0400 |
commit | 1ac8a9bda72652ff63ce1fb1e3de3be0df5478c8 (patch) | |
tree | 27327b5e2bf5d36f8d8cc1a596cc1ce4d8272aaf /webcompare | |
parent | 90a3857a62f2b85b4f0622ed0192d9bd5757aa9b (diff) |
2009-04-07 Gonzalo Paniagua Javier <gonzalo@novell.com>
* db/init-db.mysql: new filters table. And modifications to add the
detail level field.
* App_Code/NodeUtils.cs:
* App_Code/CompareParameters.cs: updated to add DetailLevel
* db/webcompare-db.cs: for every compare, generate 'raw' data and
'filtered' data. The filters are read from the DB.
* INSTALL: updated.
* main.css: 'detaillevel' CSS
* status.aspx: added a RadioButtonList to select between the normal
and the detailed view of a tree. Default is 'normal'.
svn path=/trunk/mono-tools/; revision=131269
Diffstat (limited to 'webcompare')
-rw-r--r-- | webcompare/App_Code/CompareParameters.cs | 6 | ||||
-rw-r--r-- | webcompare/App_Code/NodeUtils.cs | 10 | ||||
-rw-r--r-- | webcompare/INSTALL | 31 | ||||
-rw-r--r-- | webcompare/db/init-db.mysql | 73 | ||||
-rw-r--r-- | webcompare/db/webcompare-db.cs | 114 | ||||
-rw-r--r-- | webcompare/main.css | 14 | ||||
-rw-r--r-- | webcompare/status.aspx | 73 |
7 files changed, 292 insertions, 29 deletions
diff --git a/webcompare/App_Code/CompareParameters.cs b/webcompare/App_Code/CompareParameters.cs index a6283c7d..a4da134f 100644 --- a/webcompare/App_Code/CompareParameters.cs +++ b/webcompare/App_Code/CompareParameters.cs @@ -40,6 +40,7 @@ public class CompareParameters { Assembly = nvc ["assembly"] ?? "mscorlib"; InfoDir = nvc ["reference"] ?? "3.5"; profile = nvc ["profile"] ?? "2.0"; + detail_level = nvc ["detail_level"] ?? "normal"; Validate (profile); BinDir = "binary/" + profile; } @@ -60,6 +61,11 @@ public class CompareParameters { get { return profile; } } + string detail_level; + public string DetailLevel { + get { return detail_level; } + } + string assembly; public string Assembly { get { return assembly; } diff --git a/webcompare/App_Code/NodeUtils.cs b/webcompare/App_Code/NodeUtils.cs index bc26c9d0..f411e232 100644 --- a/webcompare/App_Code/NodeUtils.cs +++ b/webcompare/App_Code/NodeUtils.cs @@ -54,6 +54,7 @@ public class NodeUtils { string profile; string assembly; int master_id; + string detail_level; static NodeUtils () { @@ -61,7 +62,7 @@ public class NodeUtils { cnc_string = col ["WebCompareDB"]; } - public NodeUtils (string reference, string profile, string assembly) + public NodeUtils (string reference, string profile, string assembly, string detail_level) { if (String.IsNullOrEmpty (reference)) throw new ArgumentNullException ("reference"); @@ -69,10 +70,16 @@ public class NodeUtils { throw new ArgumentNullException ("profile"); if (String.IsNullOrEmpty (assembly)) throw new ArgumentNullException ("assembly"); + if (String.IsNullOrEmpty (detail_level)) + throw new ArgumentNullException ("detail_level"); + + if (detail_level != "normal" && detail_level != "detailed") + throw new ArgumentException ("detail_level", "Invalid value"); this.reference = reference; this.profile = profile; this.assembly = assembly; + this.detail_level = detail_level; master_id = -1; } @@ -85,6 +92,7 @@ public class NodeUtils { AddParameter (cmd, "reference", reference); AddParameter (cmd, "profile", profile); AddParameter (cmd, "assembly", assembly); + AddParameter (cmd, "detail_level", detail_level); master_id = Convert.ToInt32 (cmd.ExecuteScalar ()); return master_id; } diff --git a/webcompare/INSTALL b/webcompare/INSTALL index 992c6158..94fdd5e1 100644 --- a/webcompare/INSTALL +++ b/webcompare/INSTALL @@ -31,14 +31,33 @@ Prerequisites MySQL configuration -------------------- * Create a database (i.e. 'webcompare'). + + * Run the init-db.mysql script in MySql: + mysql> source init-db.mysql + * Create and assign privileges to the web application DB user like: - mysql> GRANT SELECT,EXECUTE on `webcompare`.* TO `webcompare`@`localhost`; - mysql> GRANT SELECT on `mysql`.`proc` TO `webcompare`@`localhost`; - mysql> SET PASSWORD FOR `webcompare`@`localhost` = PASSWORD ('thepassword'); + mysql> GRANT SELECT,EXECUTE on `webcompare`.* TO `webcompare`@`localhost`; + mysql> GRANT SELECT on `mysql`.`proc` TO `webcompare`@`localhost`; + mysql> SET PASSWORD FOR `webcompare`@`localhost` = PASSWORD ('thepassword'); + * Create the user for the webcompare-db.exe application: - mysql> GRANT ALL on `webcompare`.* TO `dbupdater`@`localhost`; - mysql> SET PASSWORD FOR `dbupdater`@`localhost` = PASSWORD ('apassword'); - mysql> GRANT FILE ON *.* TO `dbupdater`@`localhost`; + mysql> GRANT ALL on `webcompare`.* TO `dbupdater`@`localhost`; + mysql> SET PASSWORD FOR `dbupdater`@`localhost` = PASSWORD ('apassword'); + mysql> GRANT FILE ON *.* TO `dbupdater`@`localhost`; + + * Add any filters that you want on the full compare to generate the 'normal' view: + mysql> INSERT INTO filters VALUES (NULL, 0, 'name_str', 'typename_str'); + mysql> INSERT INTO filters VALUES (NULL, 0, 'name_str', NULL); + mysql> INSERT INTO filters VALUES (NULL, 0, NULL, 'typename_str'); + mysql> INSERT INTO filters VALUES (NULL, 1, 'regex_name', 'regex_typename'); + mysql> INSERT INTO filters VALUES (NULL, 1, 'regex_name', NULL); + mysql> INSERT INTO filters VALUES (NULL, 1, NULL, 'regex_typename'); + + -The first column should always be inserted as NULL. + -The second column is 0 for string and 1 for Regex. + -The third column is the string/Regex to match the 'name' + -The forth column is the string/Regex to match the 'typename' + -A NULL for the 3rd/4rd column matches a NULL in the input data. Web app configuration diff --git a/webcompare/db/init-db.mysql b/webcompare/db/init-db.mysql index f7b177ee..4ba70b7f 100644 --- a/webcompare/db/init-db.mysql +++ b/webcompare/db/init-db.mysql @@ -86,3 +86,76 @@ CREATE TABLE `nodes` ( ) ENGINE=MyISAM DEFAULT CHARSET=ascii COLLATE=ascii_bin; SET character_set_client = @saved_cs_client; +#### Changes 1###########3 +ALTER TABLE `webcompare`.`master` ADD COLUMN `detail_level` VARCHAR(25) NOT NULL AFTER `assembly`, +DROP INDEX `master_idx_1`, +ADD INDEX `master_idx_1` USING BTREE(`reference`, `profile`, `assembly`, `detail_level`); + +DELIMITER $$ + +DROP PROCEDURE IF EXISTS `webcompare`.`get_master_id`$$ +CREATE DEFINER=`root`@`localhost` PROCEDURE `get_master_id`(in reference varchar(100), in profile varchar (100), in assembly varchar(100), in detail_level varchar (25)) +BEGIN +SELECT m.id FROM master m +WHERE m.reference = reference AND m.profile = profile AND m.assembly = assembly AND m.detail_level = detail_level +ORDER BY last_updated DESC +LIMIT 1; + +END$$ + +DELIMITER ; + +DELIMITER $$ + +DROP PROCEDURE IF EXISTS `webcompare`.`insert_master`$$ +CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_master`( IN reference varchar(100), IN profile varchar (100), IN assembly varchar (100), IN detail_level VARCHAR(25), IN last_updated timestamp, OUT id int) +BEGIN +INSERT INTO master (reference, profile, assembly, detail_level, last_updated, active) VALUES (reference, profile, assembly, detail_level, last_updated, FALSE); +SET id = LAST_INSERT_ID(); +END$$ + +DELIMITER ; + + +DELIMITER $$ + +DROP PROCEDURE IF EXISTS `webcompare`.`update_active_master`$$ +CREATE DEFINER=`root`@`localhost` PROCEDURE `update_active_master`(IN master_id INT) +BEGIN +DECLARE reference varchar(128); +DECLARE profile varchar(128); +DECLARE assembly varchar(128); +DECLARE detail_level varchar(25); + +SELECT m.reference, m.profile, m.assembly, m.detail_level +FROM master m +WHERE m.id = master_id +INTO @reference, @profile, @assembly, @detail_level; + +UPDATE master m +SET m.active = TRUE +WHERE m.id = master_id; + +UPDATE master m +SET m.active = FALSE +WHERE m.id <> master_id AND m.reference = @reference AND m.profile = @profile AND m.assembly = @assembly AND m.detail_level = @detail_level; +END$$ + +DELIMITER ; + +CREATE TABLE `webcompare`.`filters` ( + `id` int NOT NULL AUTO_INCREMENT, + `is_rx` bit NOT NULL, + `name_filter` varchar(100) , + `typename_filter` varchar(100) , + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +INSERT INTO filters (is_rx, name_filter, typename_filter) VALUES + (0, 'System.ComponentModel.BrowsableAttribute', NULL), + (0, 'System.ComponentModel.EditorBrowsableAttribute', NULL), + (0, 'System.ComponentModel.DesignerSerializationVisibilityAttribute', NULL), + (0, 'System.ComponentModel.DesignerAttribute', NULL), + (1, '^System.Diagnostics.Debug.*', NULL); + diff --git a/webcompare/db/webcompare-db.cs b/webcompare/db/webcompare-db.cs index 5df16dfe..7c4c9775 100644 --- a/webcompare/db/webcompare-db.cs +++ b/webcompare/db/webcompare-db.cs @@ -34,6 +34,7 @@ using System.Data; using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using MySql.Data.MySqlClient; using GuiCompare; @@ -135,6 +136,12 @@ class Populate { state.UpdateLock.WaitOne (); if (state.Root != null) { Console.WriteLine ("Inserting {0} {1} {2}", state.Reference, state.Profile, state.Assembly); + state.DetailLevel = "detailed"; + InsertRoot (state); + state.Root.ResetCounts (); + FilterRoot (state.Root); + state.Root.PropagateCounts (); + state.DetailLevel = "normal"; InsertRoot (state); } else { Console.WriteLine ("No insertions for {0} {1} {2}", state.Reference, state.Profile, state.Assembly); @@ -145,7 +152,7 @@ class Populate { } } - static IDbConnection GetConnection () + public static IDbConnection GetConnection () { IDbConnection cnc = new MySqlConnection (); cnc.ConnectionString = cnc_string; @@ -204,6 +211,7 @@ class Populate { AddParameter (cmd, "reference", state.Reference); AddParameter (cmd, "profile", state.Profile); AddParameter (cmd, "assembly", state.Assembly); + AddParameter (cmd, "detail_level", state.DetailLevel); AddParameter (cmd, "last_updated", state.AssemblyLastWrite); IDataParameter p = AddOutputParameter (cmd, "id"); cmd.ExecuteNonQuery (); @@ -401,6 +409,44 @@ class Populate { return "\\N"; return str.Replace ("\t", "\\\t").Replace ('\n', ' ').Replace ('\r', ' '); } + + static void FilterRoot (ComparisonNode node) + { + Filters filters = null; + using (IDbConnection cnc = Populate.GetConnection ()) { + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandText = "SELECT is_rx, name_filter, typename_filter FROM filters"; + using (IDataReader reader = cmd.ExecuteReader ()) { + filters = new Filters (reader); + } + } + + FilterNode (filters, node); + } + + static bool FilterNode (Filters filters, ComparisonNode node) + { + if (filters.Filter (node.Name, node.TypeName)) { + Console.WriteLine ("OUT: '{0}' '{1}'", node.Name, node.TypeName); + return true; + } + + List<ComparisonNode> removed = null; + foreach (ComparisonNode child in node.Children) { + if (FilterNode (filters, child)) { + if (removed == null) + removed = new List<ComparisonNode> (); + removed.Add (child); + } + } + if (removed == null) + return false; + + foreach (ComparisonNode child in removed) + node.Children.Remove (child); + + return false; + } } class State { @@ -422,6 +468,8 @@ class State { public StreamWriter MessagesWriter; public int MasterId; + public string DetailLevel; + public State (string reference, string profile, string assembly, string info_file, string dll_file) { Reference = reference; @@ -439,3 +487,67 @@ class State { } } +class Filters { + const RegexOptions rx_options = RegexOptions.Compiled; + // -null matches null + // -"*" matches any string, including null + List<string []> static_filters; + + // -null matches null + List<Regex []> rx_filters; + + public Filters (IDataReader reader) + { + static_filters = new List<string[]> (); + rx_filters = new List<Regex []> (); + while (reader.Read ()) { + string name_filter = reader ["name_filter"] as string; + string typename_filter = reader ["typename_filter"] as string; + if (false == Convert.ToBoolean (reader ["is_rx"])) + static_filters.Add (new string [] { name_filter, typename_filter }); + else { + Regex [] rxs = new Regex [2]; + if (name_filter != null) + rxs [0] = new Regex (name_filter, rx_options); + if (typename_filter != null) + rxs [1] = new Regex (typename_filter, rx_options); + rx_filters.Add (rxs); + } + } + } + + public bool Filter (string name, string typename) + { + bool match = false; + foreach (string [] strs in static_filters) { + string s1 = strs [0]; + string s2 = strs [1]; + if (s2 == "*") + match = (s1 == strs [0]); + else if (s1 == "*") + match = (s2 == strs [1]); + else + match = (name == s1 && typename == s2); + + if (match) + return true; + } + + foreach (Regex [] rx in rx_filters) { + Regex rx1 = rx [0]; + Regex rx2 = rx [1]; + if (rx1 != null && rx2 != null && rx1.IsMatch (name) && rx2.IsMatch (typename)) + match = true; + else if (rx1 != null && typename == null && rx1.IsMatch (name)) + match = true; + else if (rx2 != null && name == null && rx2.IsMatch (typename)) + match = true; + + if (match) + return true; + } + + return false; + } +} + diff --git a/webcompare/main.css b/webcompare/main.css index 3ecd1c27..4bc33334 100644 --- a/webcompare/main.css +++ b/webcompare/main.css @@ -91,3 +91,17 @@ a:active { color:#ccf; text-decoration: none; } background-image: url(images/st.gif); } +#treeview { + float: left; +} +#detaillevel { + float: right; + margin: 20px 20px 20px 20px; + border-width: 1px 1px 2px 2px; + border-color: #4c83c4; + border-style: solid; + display: block; + background-color: #DDD; + padding: 1em; +} + diff --git a/webcompare/status.aspx b/webcompare/status.aspx index cf287848..5126e00c 100644 --- a/webcompare/status.aspx +++ b/webcompare/status.aspx @@ -24,7 +24,7 @@ NodeUtils db; NodeUtils DB { get { if (db == null) - db = new NodeUtils (Parameters.InfoDir, Parameters.Profile, Parameters.Assembly); + db = new NodeUtils (Parameters.InfoDir, Parameters.Profile, Parameters.Assembly, Parameters.DetailLevel); return db; } } @@ -32,15 +32,19 @@ NodeUtils DB { static string ImageTodo (ComparisonNode cn) { - return String.Format ("<img src='images/st.gif' border=0 align=absmiddle title='{0}'>", GetTodo (cn)); + string todo = GetTodo (cn); + if (!String.IsNullOrEmpty (todo)) + todo = HttpUtility.HtmlEncode (todo); + return String.Format ("<img src='images/st.gif' border=0 align=absmiddle title=\"{0}\">", todo); } static string Get (int count, string kind, string caption) { if (count == 0) return ""; - - return String.Format ("<div class='report' title='{0} {2}'><div class='icons suffix {1}'></div>{0}</div>", count, kind, caption); + + caption = HttpUtility.HtmlEncode (caption); + return String.Format ("<div class='report' title=\"{0} {2}\"><div class='icons suffix {1}'></div>{0}</div>", count, kind, caption); } static string GetStatus (ComparisonNode n) @@ -63,13 +67,22 @@ public void Page_Load () if (IsPostBack) return; + Header.Title = String.Format ("Mono {1} in {0} vs MS.NET {2}", Parameters.InfoDir, Parameters.Assembly, Parameters.Profile); + page_header.InnerText = Header.Title; + + string detail = Request.QueryString ["detail_level"]; + if (String.IsNullOrEmpty (detail) || detail != "detailed") + detail = "normal"; + + dlevel.SelectedValue = detail; var cp = Parameters; var n = DB.GetRootNode (); if (n == null) { tree.Visible = false; tree.Enabled = false; time_label.Text = "No data available for " + - String.Format ("Mono <b>{1}</b> in {0} vs MS.NET {2}", Parameters.InfoDir, Parameters.Assembly, Parameters.Profile); + HttpUtility.HtmlEncode (String.Format ("Mono <b>{1}</b> in {0} vs MS.NET {2}", + Parameters.InfoDir, Parameters.Assembly, Parameters.Profile)); return; } @@ -89,9 +102,7 @@ public void Page_Load () else t = String.Format ("{0} seconds", diff.Seconds); - time_label.Text = String.Format ("Assembly <b>{1}</b> last updated: {0} ago", t, Parameters.Assembly); - Header.Title = String.Format ("Mono {1} in {0} vs MS.NET {2}", Parameters.InfoDir, Parameters.Assembly, Parameters.Profile); - page_header.InnerText = Header.Title; + time_label.Text = String.Format ("Assembly <b>{1}</b> last updated: {0} ago", t, HttpUtility.HtmlEncode (Parameters.Assembly)); } static string GetTodo (ComparisonNode cn) @@ -123,17 +134,19 @@ static string GetMessages (ComparisonNode cn) static string ImagesFromCounts (ComparisonNode cn) { int x = (cn.Todo != 0 ? 2 : 0) | (cn.Warning != 0 ? 1 : 0); - switch (x){ + switch (x) { case 0: - return ""; + return null; case 1: return ImageWarning; case 2: return ImageTodo (cn); case 4: return ImageTodo (cn) + ImageWarning; + default: + break; } - return ""; + return null; } static string MemberStatus (ComparisonNode cn) @@ -145,16 +158,16 @@ static string MemberStatus (ComparisonNode cn) switch (cn.Status) { case ComparisonStatus.None: - return counts == "" ? ImageOk : ImageOk + counts; + return ImageOk + counts; case ComparisonStatus.Missing: return ImageMissing; case ComparisonStatus.Extra: - return counts == "" ? ImageExtra : ImageOk + counts; + return ImageExtra + counts; case ComparisonStatus.Error: - return counts == "" ? ImageError : ImageError + counts; + return ImageError + counts; default: return "Unknown status: " + cn.Status; @@ -311,6 +324,17 @@ void TreeNodePopulate (object sender, TreeNodeEventArgs e) e.Node.ChildNodes.Add (tn); } } + +void OnLevelChanged (object sender, EventArgs args) +{ + if (dlevel.SelectedIndex < 0) + return; + + string url = String.Format ("{0}?reference={1}&profile={2}&assembly={3}&detail_level={4}", Request.FilePath, + Parameters.InfoDir, Parameters.Profile, Parameters.Assembly, dlevel.SelectedValue); + Response.Redirect (url); +} + </script> <html> <head id="head1" runat="server"> @@ -322,21 +346,28 @@ void TreeNodePopulate (object sender, TreeNodeEventArgs e) <h1 runat="server" id="page_header">Mono Class Status Pages</h1> </div> <form id="form" runat="server"> - <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager> <div id="content"> - <br> - <asp:Label id="time_label" runat="server"/> - <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always"> - <ContentTemplate> + <div id="treeview"> + <br> + <asp:Label id="time_label" runat="server"/> <asp:TreeView ID="tree" Runat="server" OnTreeNodePopulate="TreeNodePopulate" EnableClientScript="true" PopulateNodesFromClient="true" ExpandDepth="1"> </asp:TreeView> - </ContentTemplate> - </asp:UpdatePanel> </div> + <div id="detaillevel"> + <div style="font-weight: bold; margin-bottom: 0.5em; text-align: center;">Detail Level</div> + <asp:RadioButtonList id="dlevel" runat="server" + AutoPostBack="true" + RepeatDirection="vertical" + OnSelectedIndexChanged="OnLevelChanged"> + <asp:ListItem Text="Normal" Value="normal" Selected="true" /> + <asp:ListItem Text="Detailed" Value="detailed" /> + </asp:RadioButtonList> + </div> + </div> </form> </body> </html> |