diff options
-rw-r--r-- | webcompare/ChangeLog | 9 | ||||
-rw-r--r-- | webcompare/Makefile | 3 | ||||
-rw-r--r-- | webcompare/db/DataAccess.cs | 254 | ||||
-rw-r--r-- | webcompare/db/Makefile | 23 | ||||
-rw-r--r-- | webcompare/db/MySqlDataAccess.cs | 77 | ||||
-rw-r--r-- | webcompare/db/PostgresDataAccess.cs | 140 | ||||
-rw-r--r-- | webcompare/db/webcompare-db.cs | 835 |
7 files changed, 856 insertions, 485 deletions
diff --git a/webcompare/ChangeLog b/webcompare/ChangeLog index 2d5ee1cd..9bf046c3 100644 --- a/webcompare/ChangeLog +++ b/webcompare/ChangeLog @@ -1,3 +1,12 @@ +2009-04-15 Gonzalo Paniagua Javier <gonzalo@novell.com> + + * db/Makefile: + * Makefile: + * db/DataAccess.cs: + * db/MySqlDataAccess.cs: + * db/PostgresDataAccess.cs: + * db/webcompare-db.cs: added support for postgres. + 2009-04-12 Gonzalo Paniagua Javier <gonzalo@novell.com> * main.css: diff --git a/webcompare/Makefile b/webcompare/Makefile index 03c96ce8..df1b1c82 100644 --- a/webcompare/Makefile +++ b/webcompare/Makefile @@ -6,6 +6,9 @@ DB_SYNCFILES= \ db/Makefile \ db/init-db.mysql \ db/webcompare-db.cs \ + db/DataAccess.cs \ + db/MySqlDataAccess.cs \ + db/PostgresDataAccess.cs \ db/webcompare-db.exe.config-EDITME \ db/web.config \ db/*.dll \ diff --git a/webcompare/db/DataAccess.cs b/webcompare/db/DataAccess.cs new file mode 100644 index 00000000..f1ff2427 --- /dev/null +++ b/webcompare/db/DataAccess.cs @@ -0,0 +1,254 @@ +// +// DataAccess.cs +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo@novell.com) +// +// Copyright (c) Copyright 2009 Novell, Inc +// +// 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. +// + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; +using System.Data; +using System.IO; +using System.Text; +using GuiCompare; + +namespace Mono.WebCompareDB { + abstract class DataAccess { + static string cnc_string; + protected static string [] default_delete_tables = { "master", "nodes", "messages" }; + + static DataAccess () + { + NameValueCollection col = ConfigurationManager.AppSettings; + cnc_string = col ["WebCompareDB"]; + if (String.IsNullOrEmpty (cnc_string)) + throw new ApplicationException ("Missing connection string from configuration file."); + } + + public DataAccess () + { + } + + protected string ConnectionString { + get { return cnc_string; } + } + + protected abstract IDbConnection GetConnection (); + protected abstract void LoadFile (IDbConnection cnc, string table, string file_name); + protected abstract void EnableNewMaster (IDbConnection cnc, int new_master_id); + protected abstract int InsertMaster (IDbConnection cnc, State state); + + public void InsertRoot (State state) + { + using (IDbConnection cnc = GetConnection ()) { + state.MasterId = InsertMaster (cnc, state); + using (state.NodesWriter = new StreamWriter (state.NodesFileName)) { + using (state.MessagesWriter = new StreamWriter (state.MessagesFileName)) { + InsertTree (cnc, state, state.Root, null, 0); + } + } + LoadFile (cnc, "nodes", state.NodesFileName); + LoadFile (cnc, "messages", state.MessagesFileName); + File.Delete (state.NodesFileName); + File.Delete (state.MessagesFileName); + EnableNewMaster (cnc, state.MasterId); + } + DeleteInactive (state); + } + + + protected virtual string GetParameterNameForQuery (string pname) + { + return "@" + pname; + } + + protected virtual void InsertTree (IDbConnection cnc, State state, ComparisonNode node, string base_name, int node_id) + { + StringBuilder sb = new StringBuilder (); + string node_name = GetNodeName (base_name, node_id); + AppendValue (sb, node_name); // node_name + AppendValue (sb, state.MasterId); // master_id + AppendValue (sb, node_id); // child_id + AppendValue (sb, base_name == null ? "-" : base_name); // parent_name + AppendValue (sb, (int) node.Type); // comparison_type + AppendValue (sb, (int) node.Status); // status + AppendValue (sb, node.Extra); // extras + AppendValue (sb, node.Missing); // missing + AppendValue (sb, node.Present); // present + AppendValue (sb, node.Warning); // warning + AppendValue (sb, node.Todo); // todo + AppendValue (sb, node.Niex); // niex + AppendValue (sb, node.ThrowsNIE); // throwsnie + AppendValue (sb, node.Children.Count > 0); // has_children + AppendValue (sb, node.Messages.Count > 0 || node.Todos.Count > 0); // has_messages + AppendValue (sb, node.Name); // name + AppendValue (sb, node.TypeName); // typename + sb.Length--; // remove trailing \t + state.NodesWriter.WriteLine (sb); + + InsertMessages (state, node_name, node.Messages, false); + InsertMessages (state, node_name, node.Todos, true); + int counter = 0; + foreach (ComparisonNode n in node.Children) { + InsertTree (cnc, state, n, node_name, counter); + counter++; + } + } + + protected virtual void InsertMessages (State state, string node_name, List<string> strs, bool is_todo) + { + if (strs == null || strs.Count == 0) + return; + + StringBuilder sb = new StringBuilder (); + foreach (string s in strs) { + sb.AppendFormat ("{0}\t{1}\t{2}\t{3}", node_name, state.MasterId, (is_todo) ? 1 : 0, FormatString (s)); + state.MessagesWriter.WriteLine (sb); + sb.Length = 0; + } + } + + public void DeleteTables () + { + DeleteTables (default_delete_tables); + } + + protected virtual void DeleteTables (string [] tables) + { + using (IDbConnection cnc = GetConnection ()) { + foreach (string tbl in tables) { + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandText = String.Format ("DELETE FROM {0}", tbl); + cmd.ExecuteNonQuery (); + } + } + } + + public virtual Filters GetFilters () + { + Filters filters = null; + using (IDbConnection cnc = 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); + } + } + return filters; + } + + protected virtual void DeleteInactive (State state) + { + using (IDbConnection cnc = GetConnection ()) { + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandText = String.Format ( + "SELECT id FROM master WHERE active = FALSE AND reference = {0} AND profile = {1} AND assembly = {2}", + GetParameterNameForQuery ("reference"), + GetParameterNameForQuery ("profile"), + GetParameterNameForQuery ("assembly")); + AddParameter (cmd, "reference", state.Reference); + AddParameter (cmd, "profile", state.Profile); + AddParameter (cmd, "assembly", state.Assembly); + List<int> ids = new List<int> (); + using (IDataReader reader = cmd.ExecuteReader ()) { + while (reader.Read ()) { + ids.Add (Convert.ToInt32 (reader [0])); + } + } + CleanupTables (cnc, ids); + } + } + + protected virtual void CleanupTables (IDbConnection cnc, List<int> ids) + { + if (ids.Count == 0) + return; + + StringBuilder sb = new StringBuilder (); + sb.Append ('('); + foreach (int i in ids) + sb.AppendFormat ("{0},", i); + sb.Length--; + sb.Append (')'); + string str = sb.ToString (); + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandText = "DELETE FROM messages WHERE master_id IN " + str; + cmd.ExecuteNonQuery (); + cmd = cnc.CreateCommand (); + cmd.CommandText = "DELETE FROM nodes WHERE master_id IN " + str; + cmd.ExecuteNonQuery (); + cmd = cnc.CreateCommand (); + cmd.CommandText = "DELETE FROM master WHERE id IN " + str; + cmd.ExecuteNonQuery (); + } + + protected static IDataParameter AddParameter (IDbCommand cmd, string name, object val) + { + IDataParameter p = cmd.CreateParameter (); + p.ParameterName = name; + p.Value = val; + cmd.Parameters.Add (p); + return p; + } + + protected static IDataParameter AddOutputParameter (IDbCommand cmd, string name) + { + IDataParameter p = cmd.CreateParameter (); + p.ParameterName = name; + p.Direction = ParameterDirection.Output; + cmd.Parameters.Add (p); + return p; + } + + protected static string GetNodeName (string base_name, int node_id) + { + if (base_name == null) + return node_id.ToString (); + return String.Format ("{0}-{1}", base_name, node_id); + } + + protected virtual string FormatString (string str) + { + if (String.IsNullOrEmpty (str)) + return "\\N"; + + return str.Replace ("\t", "\\\t").Replace ('\n', ' ').Replace ('\r', ' ').Replace ("\0", ""); + } + + protected virtual void AppendValue (StringBuilder sb, object o) + { + if (!((o is string) || (o is DBNull))) { + if (o is bool) + sb.AppendFormat ("{0}\t", ((bool) o) ? 1 : 0); + else + sb.AppendFormat ("{0}\t", o); + } else { + sb.AppendFormat ("{0}\t", FormatString (o as string)); + } + } + } +} + diff --git a/webcompare/db/Makefile b/webcompare/db/Makefile index 1321ddb2..e3dba55d 100644 --- a/webcompare/db/Makefile +++ b/webcompare/db/Makefile @@ -1,6 +1,6 @@ base = ../../gui-compare -SOURCES = \ +LIB_SOURCES = \ $(base)/AssemblyResolver.cs \ $(base)/CompareContext.cs \ $(base)/Comparison.cs \ @@ -9,13 +9,26 @@ SOURCES = \ $(base)/Metadata.cs \ $(base)/Masterinfo.cs +SOURCES = \ + webcompare-db.cs \ + DataAccess.cs \ + MySqlDataAccess.cs +# PostgresDataAccess.cs + +REFS = \ + -r:System.Configuration.dll \ + -r:System.Core.dll \ + -r:MySql.Data.dll \ + -r:System.Data.dll +# -r:Npgsql + all: webcompare-db.exe -webcompare-db.exe: webcompare-db.cs Mono.Cecil.dll Mono.Api.CompareFull.dll - gmcs -debug -r:System.Configuration.dll -r:System.Core.dll -r:MySql.Data.dll -r:System.Data.dll -r:Mono.Api.CompareFull.dll -r:Mono.Cecil.dll webcompare-db.cs +webcompare-db.exe: $(SOURCES) Mono.Cecil.dll Mono.Api.CompareFull.dll + gmcs -debug $(REFS) -r:Mono.Api.CompareFull.dll -r:Mono.Cecil.dll $(SOURCES) -Mono.Api.CompareFull.dll: $(SOURCES) Mono.Cecil.dll Makefile - gmcs -nowarn:169,414 -debug -target:library -r:Mono.Cecil.dll -out:Mono.Api.CompareFull.dll $(SOURCES) +Mono.Api.CompareFull.dll: $(LIB_SOURCES) Mono.Cecil.dll Makefile + gmcs -nowarn:169,414 -debug -target:library -r:Mono.Cecil.dll -out:Mono.Api.CompareFull.dll $(LIB_SOURCES) Mono.Cecil.dll: cp `pkg-config --variable Libraries cecil` . diff --git a/webcompare/db/MySqlDataAccess.cs b/webcompare/db/MySqlDataAccess.cs new file mode 100644 index 00000000..41b0f3ba --- /dev/null +++ b/webcompare/db/MySqlDataAccess.cs @@ -0,0 +1,77 @@ +// +// MySqlDataAccess.cs +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo@novell.com) +// +// Copyright (c) Copyright 2009 Novell, Inc +// +// 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. +// + +using System; +using System.Data; +using MySql.Data.MySqlClient; +using GuiCompare; + +namespace Mono.WebCompareDB { + class MySqlDataAccess : DataAccess { + protected override IDbConnection GetConnection () + { + IDbConnection cnc = new MySqlConnection (); + cnc.ConnectionString = ConnectionString; + cnc.Open (); + return cnc; + } + + protected override int InsertMaster (IDbConnection cnc, State state) + { + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandText = "insert_master"; + cmd.CommandType = CommandType.StoredProcedure; + 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 (); + return Convert.ToInt32 (p.Value); + } + + protected override void LoadFile (IDbConnection cnc, string table, string file_name) + { + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandType = CommandType.Text; + cmd.CommandText = String.Format ("LOAD DATA INFILE '{0}' INTO TABLE {1}", file_name, table); + cmd.ExecuteNonQuery (); + } + + protected override void EnableNewMaster (IDbConnection cnc, int new_master_id) + { + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandText = "update_active_master"; + cmd.CommandType = CommandType.StoredProcedure; + AddParameter (cmd, "master_id", new_master_id); + cmd.ExecuteNonQuery (); + } + } +} + diff --git a/webcompare/db/PostgresDataAccess.cs b/webcompare/db/PostgresDataAccess.cs new file mode 100644 index 00000000..c054fc34 --- /dev/null +++ b/webcompare/db/PostgresDataAccess.cs @@ -0,0 +1,140 @@ +// +// PostgresDataAccess.cs +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo@novell.com) +// +// Copyright (c) Copyright 2009 Novell, Inc +// +// 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. +// + +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using Npgsql; +using GuiCompare; + +namespace Mono.WebCompareDB { + class PostgresDataAccess : DataAccess { + protected override IDbConnection GetConnection () + { + IDbConnection cnc = new NpgsqlConnection (); + //cnc.ConnectionString = ConnectionString; + cnc.ConnectionString = "Server=127.0.0.1;Port=5432;User Id=gonzalo;Password=gonz;Database=webcompare;"; + cnc.Open (); + return cnc; + } + + protected override string GetParameterNameForQuery (string pname) + { + return ":" + pname; + } + + protected override int InsertMaster (IDbConnection cnc, State state) + { + IDbTransaction trans = cnc.BeginTransaction (); + try { + IDbCommand cmd = cnc.CreateCommand (); + cmd.Transaction = trans; + cmd.CommandText = "INSERT INTO master (reference, profile, assembly, detail_level, last_updated, active) VALUES (:reference, :profile, :assembly, :detail_level, CURRENT_TIMESTAMP, FALSE);"; + + AddParameter (cmd, "reference", state.Reference); + AddParameter (cmd, "profile", state.Profile); + AddParameter (cmd, "assembly", state.Assembly); + AddParameter (cmd, "detail_level", state.DetailLevel); + if (cmd.ExecuteNonQuery () != 1) + throw new Exception ("Error inserting into 'master'"); + + cmd = cnc.CreateCommand (); + cmd.Transaction = trans; + cmd.CommandText = "SELECT CURRVAL('seq_master_id')"; + int result = Convert.ToInt32 (cmd.ExecuteScalar ()); + trans.Commit (); + trans = null; + return result; + } finally { + if (trans != null) { + trans.Rollback (); + trans = null; + } + } + } + + protected override void LoadFile (IDbConnection cnc, string table, string file_name) + { + IDbCommand cmd = cnc.CreateCommand (); + cmd.CommandType = CommandType.Text; + string col_names = null; + if (table == "messages") + col_names = "(node_name, master_id, is_todo, message)"; + cmd.CommandText = String.Format ("COPY {1} {2} FROM '{0}'", file_name, table, col_names); + cmd.ExecuteNonQuery (); + } + + protected override void EnableNewMaster (IDbConnection cnc, int new_master_id) + { + IDbTransaction trans = cnc.BeginTransaction (); + try { + string reference, profile, assembly, detail_level; + + IDbCommand cmd = cnc.CreateCommand (); + cmd.Transaction = trans; + cmd.CommandText = "SELECT reference, profile, assembly, detail_level FROM master WHERE id = :master_id"; + AddParameter (cmd, "master_id", new_master_id); + using (IDataReader reader = cmd.ExecuteReader ()) { + if (!reader.Read ()) + throw new Exception ("MasterID not found"); + reference = reader [0] as string; + profile = reader [1] as string; + assembly = reader [2] as string; + detail_level = reader [3] as string; + } + cmd = cnc.CreateCommand (); + cmd.Transaction = trans; + cmd.CommandText = "UPDATE master SET active = TRUE WHERE id = :master_id"; + AddParameter (cmd, "master_id", new_master_id); + if (cmd.ExecuteNonQuery () != 1) + throw new Exception ("Error activating masterID"); + + cmd = cnc.CreateCommand (); + cmd.Transaction = trans; + cmd.CommandText = "UPDATE master SET active = FALSE " + + "WHERE id <> :master_id AND reference = :reference AND " + + "profile = :profile AND assembly = :assembly AND detail_level = :detail_level"; + AddParameter (cmd, "master_id", new_master_id); + AddParameter (cmd, "reference", reference); + AddParameter (cmd, "profile", profile); + AddParameter (cmd, "assembly", assembly); + AddParameter (cmd, "detail_level", detail_level); + cmd.ExecuteNonQuery (); + trans.Commit (); + trans = null; + } finally { + if (trans != null) { + trans.Rollback (); + trans = null; + } + } + } + } +} + diff --git a/webcompare/db/webcompare-db.cs b/webcompare/db/webcompare-db.cs index 295471b9..1a1642db 100644 --- a/webcompare/db/webcompare-db.cs +++ b/webcompare/db/webcompare-db.cs @@ -36,541 +36,416 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; -using MySql.Data.MySqlClient; using GuiCompare; -class Populate { - static void Help () - { - Console.WriteLine (" Compares masterinfos to Mono assemblies and stores the data in a DB."); - Console.WriteLine (" The masterinfos are expected to be in ../masterinfos and the assemblies"); - Console.WriteLine (" in ../binary."); - Console.WriteLine (); - Console.WriteLine (" When invoked with no arguments it is equivalent to:"); - Console.WriteLine (); - Console.WriteLine (" webcompare-db.exe '3.5 2.0' 'SL2 2.1' '2.0 2.0' '1.1 1.0'"); - Console.WriteLine (); - Console.WriteLine (" The first argument of each pair is a directory in ../masterinfos."); - Console.WriteLine (" The second argument of each pair is a directory in ../binary."); - Console.WriteLine (); - Console.WriteLine (" --help: displays this help"); - Console.WriteLine (" --delete-tables: delete ALL the data in ALL the tables and exits."); - Console.WriteLine (" --assemblies A1[,A2,...]: comma-separated list of assemblies to compare."); - Console.WriteLine (" All other assemblies are ignored."); - Console.WriteLine (); - } - - static string [] default_compares = new string [] { "3.5 2.0", "SL2 2.1", "2.0 2.0", "1.1 1.0" }; - static string cnc_string; - - static int Main (string [] args) - { - NameValueCollection col = ConfigurationManager.AppSettings; - cnc_string = col ["WebCompareDB"]; - if (String.IsNullOrEmpty (cnc_string)) { - Console.Error.WriteLine ("Missing connection string from configuration file."); - return 1; - } - List<string> compares = new List<string>(); - List<string> include_list = new List<string> (); - bool got_assemblies = false; - foreach (string arg in args) { - if (got_assemblies) { - string [] strs = arg.Split (','); - foreach (string s in strs) - include_list.Add (s); - got_assemblies = false; - continue; - } - if (arg == "--help") { - Help (); - return 0; - } - if (arg == "--delete-tables") { - DeleteTables (); - Console.WriteLine ("Tables deleted"); - return 0; - } - if (arg == "--assemblies") { - got_assemblies = true; - continue; - } - - string [] compare = arg.Split (); - if (compare.Length != 2) { - Console.Error.WriteLine ("Invalid argument: {0}", arg); - return 1; - } - compares.Add (arg); +namespace Mono.WebCompareDB { + class Populate { + static DataAccess GetDataAccess () + { + return new MySqlDataAccess (); + //return new PostgresDataAccess (); } - if (got_assemblies) { - Console.Error.WriteLine ("Assembly list not provided for --assemblies"); - return 1; + static void Help () + { + Console.WriteLine (" Compares masterinfos to Mono assemblies and stores the data in a DB."); + Console.WriteLine (" The masterinfos are expected to be in ../masterinfos and the assemblies"); + Console.WriteLine (" in ../binary."); + Console.WriteLine (); + Console.WriteLine (" When invoked with no arguments it is equivalent to:"); + Console.WriteLine (); + Console.WriteLine (" webcompare-db.exe '3.5 2.0' 'SL2 2.1' '2.0 2.0' '1.1 1.0'"); + Console.WriteLine (); + Console.WriteLine (" The first argument of each pair is a directory in ../masterinfos."); + Console.WriteLine (" The second argument of each pair is a directory in ../binary."); + Console.WriteLine (); + Console.WriteLine (" --help: displays this help"); + Console.WriteLine (" --delete-tables: delete ALL the data in ALL the tables and exits."); + Console.WriteLine (" --assemblies A1[,A2,...]: comma-separated list of assemblies to compare."); + Console.WriteLine (" All other assemblies are ignored."); + Console.WriteLine (); } - string [] actual_compares = null; - if (compares.Count == 0) - actual_compares = default_compares; - else - actual_compares = compares.ToArray (); - - CreateWorkItems (actual_compares, include_list); - Thread [] comparers = new Thread [1]; - for (int i = comparers.Length - 1; i >= 0; i--) { - comparers [i]= new Thread (PerformComparison); - comparers [i].Start (); - } - Thread [] dbupdaters = new Thread [2]; - for (int i = dbupdaters.Length - 1; i >= 0; i--) { - dbupdaters [i]= new Thread (UpdateDB); - dbupdaters [i].Start (); - } - for (int i = comparers.Length - 1; i >= 0; i--) - comparers [i].Join (); - for (int i = dbupdaters.Length - 1; i >= 0; i--) - dbupdaters [i].Join (); - - return 0; - } + static string [] default_compares = new string [] { "3.5 2.0", "SL2 2.1", "2.0 2.0", "1.1 1.0" }; + + static int Main (string [] args) + { + List<string> compares = new List<string>(); + List<string> include_list = new List<string> (); + bool got_assemblies = false; + foreach (string arg in args) { + if (got_assemblies) { + string [] strs = arg.Split (','); + foreach (string s in strs) + include_list.Add (s); + got_assemblies = false; + continue; + } + if (arg == "--help") { + Help (); + return 0; + } + if (arg == "--delete-tables") { + DeleteTables (); + Console.WriteLine ("Tables deleted"); + return 0; + } + if (arg == "--assemblies") { + got_assemblies = true; + continue; + } - static void PerformComparison () - { - int end = work_items.Count; - for (int i = 0; i < end; i++) { - State state = work_items [i]; - if (Interlocked.CompareExchange (ref state.AlreadyComparing, 1, 0) == 1) - continue; - Console.WriteLine ("Comparing {0} {1} {2}", state.Reference, state.Profile, state.Assembly); - state.Root = MakeComparisonNode (state.InfoFile, state.DllFile); - state.UpdateLock.Set (); - } - } + string [] compare = arg.Split (); + if (compare.Length != 2) { + Console.Error.WriteLine ("Invalid argument: {0}", arg); + return 1; + } + compares.Add (arg); + } - static void UpdateDB () - { - int end = work_items.Count; - for (int i = 0; i < end; i++) { - State state = work_items [i]; - if (Interlocked.CompareExchange (ref state.LockInUse, 1, 0) == 1) - continue; - 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); + if (got_assemblies) { + Console.Error.WriteLine ("Assembly list not provided for --assemblies"); + return 1; } - state.UpdateLock.Close (); - state.UpdateLock = null; - state.Root = null; - } - } - public static IDbConnection GetConnection () - { - IDbConnection cnc = new MySqlConnection (); - cnc.ConnectionString = cnc_string; - cnc.Open (); - return cnc; - } + string [] actual_compares = null; + if (compares.Count == 0) + actual_compares = default_compares; + else + actual_compares = compares.ToArray (); - static string [] tables = { "master", "nodes", "messages" }; - static void DeleteTables () - { - using (IDbConnection cnc = GetConnection ()) { - foreach (string tbl in tables) { - IDbCommand cmd = cnc.CreateCommand (); - cmd.CommandText = String.Format ("TRUNCATE TABLE {0}", tbl); - cmd.ExecuteNonQuery (); + CreateWorkItems (actual_compares, include_list); + Thread [] comparers = new Thread [1]; + for (int i = comparers.Length - 1; i >= 0; i--) { + comparers [i]= new Thread (PerformComparison); + comparers [i].Start (); } - } - } - - static List<State> work_items = new List<State> (); - static void CreateWorkItems (string [] compares, List<string> include_list) - { - foreach (string str in compares) { - string [] s = str.Split (); - string reference = s [0]; - string profile = s [1]; - string mpath = "../masterinfos/" + reference; - string bpath = "../binary/" + profile; - if (!Directory.Exists (mpath)) - continue; - - if (!Directory.Exists (bpath)) - continue; - - var infos = from p in Directory.GetFiles (mpath) - select Path.GetFileNameWithoutExtension (p); + Thread [] dbupdaters = new Thread [2]; + for (int i = dbupdaters.Length - 1; i >= 0; i--) { + dbupdaters [i]= new Thread (UpdateDB); + dbupdaters [i].Start (); + } + for (int i = comparers.Length - 1; i >= 0; i--) + comparers [i].Join (); + for (int i = dbupdaters.Length - 1; i >= 0; i--) + dbupdaters [i].Join (); - var dlls = from p in Directory.GetFiles (bpath) - select Path.GetFileNameWithoutExtension (p); + return 0; + } - foreach (var assembly in (from p in infos.Intersect (dlls) orderby p select p)) { - if (include_list.Count > 0 && include_list.IndexOf (assembly) == -1) + static void PerformComparison () + { + int end = work_items.Count; + for (int i = 0; i < end; i++) { + State state = work_items [i]; + if (Interlocked.CompareExchange (ref state.AlreadyComparing, 1, 0) == 1) continue; - string info_file = Path.Combine (mpath, assembly + ".xml"); - string dll_file = Path.Combine (bpath, assembly + ".dll"); - State state = new State (reference, profile, assembly, info_file, dll_file); - work_items.Add (state); + Console.WriteLine ("Comparing {0} {1} {2}", state.Reference, state.Profile, state.Assembly); + state.Root = MakeComparisonNode (state.InfoFile, state.DllFile); + state.UpdateLock.Set (); } } - } - static void InsertRoot (State state) - { - using (IDbConnection cnc = GetConnection ()) { - IDbCommand cmd = cnc.CreateCommand (); - cmd.CommandText = "insert_master"; - cmd.CommandType = CommandType.StoredProcedure; - 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 (); - state.MasterId = (int) p.Value; - using (state.NodesWriter = new StreamWriter (state.NodesFileName)) { - using (state.MessagesWriter = new StreamWriter (state.MessagesFileName)) { - InsertTree (cnc, state, state.Root, null, 0); + static void UpdateDB () + { + DataAccess da = GetDataAccess (); + int end = work_items.Count; + for (int i = 0; i < end; i++) { + State state = work_items [i]; + if (Interlocked.CompareExchange (ref state.LockInUse, 1, 0) == 1) + continue; + state.UpdateLock.WaitOne (); + if (state.Root != null) { + Console.WriteLine ("Inserting {0} {1} {2}", state.Reference, state.Profile, state.Assembly); + state.DetailLevel = "detailed"; + da.InsertRoot (state); + state.Root.ResetCounts (); + FilterRoot (state.Root); + state.Root.PropagateCounts (); + state.DetailLevel = "normal"; + da.InsertRoot (state); + } else { + Console.WriteLine ("No insertions for {0} {1} {2}", state.Reference, state.Profile, state.Assembly); } + state.UpdateLock.Close (); + state.UpdateLock = null; + state.Root = null; } - LoadFile (cnc, "nodes", state.NodesFileName); - LoadFile (cnc, "messages", state.MessagesFileName); - File.Delete (state.NodesFileName); - File.Delete (state.MessagesFileName); - EnableNewMaster (cnc, state.MasterId); } - DeleteInactive (state); - } - static void LoadFile (IDbConnection cnc, string table, string file_name) - { - IDbCommand cmd = cnc.CreateCommand (); - cmd.CommandType = CommandType.Text; - cmd.CommandText = String.Format ("LOAD DATA INFILE '{0}' INTO TABLE {1}", file_name, table); - cmd.ExecuteNonQuery (); - } + static void DeleteTables () + { + DataAccess da = GetDataAccess (); + da.DeleteTables (); + } - static void EnableNewMaster (IDbConnection cnc, int new_master_id) - { - IDbCommand cmd = cnc.CreateCommand (); - cmd.CommandText = "update_active_master"; - cmd.CommandType = CommandType.StoredProcedure; - AddParameter (cmd, "master_id", new_master_id); - cmd.ExecuteNonQuery (); - } + static List<State> work_items = new List<State> (); + static void CreateWorkItems (string [] compares, List<string> include_list) + { + foreach (string str in compares) { + string [] s = str.Split (); + string reference = s [0]; + string profile = s [1]; + string mpath = "../masterinfos/" + reference; + string bpath = "../binary/" + profile; + if (!Directory.Exists (mpath)) + continue; - static void DeleteInactive (State state) - { - using (IDbConnection cnc = GetConnection ()) { - IDbCommand cmd = cnc.CreateCommand (); - cmd.CommandText = "SELECT id FROM master WHERE active = 0 AND reference = @reference AND profile = @profile AND assembly = @assembly"; - AddParameter (cmd, "reference", state.Reference); - AddParameter (cmd, "profile", state.Profile); - AddParameter (cmd, "assembly", state.Assembly); - List<int> ids = new List<int> (); - using (IDataReader reader = cmd.ExecuteReader ()) { - while (reader.Read ()) { - ids.Add (Convert.ToInt32 (reader [0])); + if (!Directory.Exists (bpath)) + continue; + + var infos = from p in Directory.GetFiles (mpath) + select Path.GetFileNameWithoutExtension (p); + + var dlls = from p in Directory.GetFiles (bpath) + select Path.GetFileNameWithoutExtension (p); + + foreach (var assembly in (from p in infos.Intersect (dlls) orderby p select p)) { + if (include_list.Count > 0 && include_list.IndexOf (assembly) == -1) + continue; + string info_file = Path.Combine (mpath, assembly + ".xml"); + string dll_file = Path.Combine (bpath, assembly + ".dll"); + State state = new State (reference, profile, assembly, info_file, dll_file); + work_items.Add (state); } } - CleanupTables (cnc, ids); } - } - - static void CleanupTables (IDbConnection cnc, List<int> ids) - { - if (ids.Count == 0) - return; - StringBuilder sb = new StringBuilder (); - sb.Append ('('); - foreach (int i in ids) - sb.AppendFormat ("{0},", i); - sb.Length--; - sb.Append (')'); - string str = sb.ToString (); - IDbCommand cmd = cnc.CreateCommand (); - cmd.CommandText = "DELETE FROM messages WHERE master_id IN " + str; - cmd.ExecuteNonQuery (); - cmd = cnc.CreateCommand (); - cmd.CommandText = "DELETE FROM nodes WHERE master_id IN " + str; - cmd.ExecuteNonQuery (); - cmd = cnc.CreateCommand (); - cmd.CommandText = "DELETE FROM master WHERE id IN " + str; - cmd.ExecuteNonQuery (); - } - - static IDataParameter AddParameter (IDbCommand cmd, string name, object val) - { - IDataParameter p = cmd.CreateParameter (); - p.ParameterName = name; - p.Value = val; - cmd.Parameters.Add (p); - return p; - } - - static IDataParameter AddOutputParameter (IDbCommand cmd, string name) - { - IDataParameter p = cmd.CreateParameter (); - p.ParameterName = name; - p.Direction = ParameterDirection.Output; - cmd.Parameters.Add (p); - return p; - } - static ComparisonNode MakeComparisonNode (string info_file, string dll_file) - { - if (!File.Exists (info_file)) { - Console.Error.WriteLine ("{0} does not exist", info_file); - return null; - } - if (!File.Exists (dll_file)) { - Console.Error.WriteLine ("{0} does not exist", dll_file); - return null; + static ComparisonNode MakeComparisonNode (string info_file, string dll_file) + { + if (!File.Exists (info_file)) { + Console.Error.WriteLine ("{0} does not exist", info_file); + return null; + } + if (!File.Exists (dll_file)) { + Console.Error.WriteLine ("{0} does not exist", dll_file); + return null; + } + + CompareContext cc = new CompareContext ( + () => new MasterAssembly (info_file), + () => new CecilAssembly (dll_file)); + + cc.ProgressChanged += delegate (object sender, CompareProgressChangedEventArgs a){ + //Console.Error.WriteLine (a.Message); + }; + bool have_error = false; + cc.Error += delegate (object sender, CompareErrorEventArgs args) { + have_error = true; + Console.Error.WriteLine ("Error loading {0}: {1}", info_file, args.Message.Split (Environment.NewLine.ToCharArray ())[0]); + }; + ManualResetEvent r = new ManualResetEvent (false); + cc.Finished += delegate { r.Set (); }; + cc.Compare (); + r.WaitOne (); + if (have_error) + return null; + cc.Comparison.PropagateCounts (); + return cc.Comparison; } - - CompareContext cc = new CompareContext ( - () => new MasterAssembly (info_file), - () => new CecilAssembly (dll_file)); - - cc.ProgressChanged += delegate (object sender, CompareProgressChangedEventArgs a){ - //Console.Error.WriteLine (a.Message); - }; - bool have_error = false; - cc.Error += delegate (object sender, CompareErrorEventArgs args) { - have_error = true; - Console.Error.WriteLine ("Error loading {0}: {1}", info_file, args.Message.Split (Environment.NewLine.ToCharArray ())[0]); - }; - ManualResetEvent r = new ManualResetEvent (false); - cc.Finished += delegate { r.Set (); }; - cc.Compare (); - r.WaitOne (); - if (have_error) - return null; - cc.Comparison.PropagateCounts (); - return cc.Comparison; - } - static string GetNodeName (string base_name, int node_id) - { - if (base_name == null) - return node_id.ToString (); - return String.Format ("{0}-{1}", base_name, node_id); - } - - static void AppendValue (StringBuilder sb, object o) - { - if (!((o is string) || (o is DBNull))) { - if (o is bool) - sb.AppendFormat ("{0}\t", ((bool) o) ? 1 : 0); - else - sb.AppendFormat ("{0}\t", o); - } else { - sb.AppendFormat ("{0}\t", FormatString (o as string)); + static string GetNodeName (string base_name, int node_id) + { + if (base_name == null) + return node_id.ToString (); + return String.Format ("{0}-{1}", base_name, node_id); } - } - static void InsertTree (IDbConnection cnc, State state, ComparisonNode node, string base_name, int node_id) - { - StringBuilder sb = new StringBuilder (); - string node_name = GetNodeName (base_name, node_id); - AppendValue (sb, node_name); // node_name - AppendValue (sb, state.MasterId); // master_id - AppendValue (sb, node_id); // child_id - AppendValue (sb, base_name == null ? "-" : base_name); // parent_name - AppendValue (sb, (int) node.Type); // comparison_type - AppendValue (sb, (int) node.Status); // status - AppendValue (sb, node.Extra); // extras - AppendValue (sb, node.Missing); // missing - AppendValue (sb, node.Present); // present - AppendValue (sb, node.Warning); // warning - AppendValue (sb, node.Todo); // todo - AppendValue (sb, node.Niex); // niex - AppendValue (sb, node.ThrowsNIE); // throwsnie - AppendValue (sb, node.Children.Count > 0); // has_children - AppendValue (sb, node.Messages.Count > 0 || node.Todos.Count > 0); // has_messages - AppendValue (sb, node.Name); // name - AppendValue (sb, node.TypeName); // typename - sb.Length--; // remove trailing \t - state.NodesWriter.WriteLine (sb); - - InsertMessages (state, node_name, node.Messages, false); - InsertMessages (state, node_name, node.Todos, true); - int counter = 0; - foreach (ComparisonNode n in node.Children) { - InsertTree (cnc, state, n, node_name, counter); - counter++; + static void AppendValue (StringBuilder sb, object o) + { + if (!((o is string) || (o is DBNull))) { + if (o is bool) + sb.AppendFormat ("{0}\t", ((bool) o) ? 1 : 0); + else + sb.AppendFormat ("{0}\t", o); + } else { + sb.AppendFormat ("{0}\t", FormatString (o as string)); + } } - } - - static void InsertMessages (State state, string node_name, List<string> strs, bool is_todo) - { - if (strs == null || strs.Count == 0) - return; - StringBuilder sb = new StringBuilder (); - foreach (string s in strs) { - sb.AppendFormat ("{0}\t{1}\t{2}\t{3}", node_name, state.MasterId, (is_todo) ? 1 : 0, FormatString (s)); - state.MessagesWriter.WriteLine (sb); - sb.Length = 0; + static void InsertTree (IDbConnection cnc, State state, ComparisonNode node, string base_name, int node_id) + { + StringBuilder sb = new StringBuilder (); + string node_name = GetNodeName (base_name, node_id); + AppendValue (sb, node_name); // node_name + AppendValue (sb, state.MasterId); // master_id + AppendValue (sb, node_id); // child_id + AppendValue (sb, base_name == null ? "-" : base_name); // parent_name + AppendValue (sb, (int) node.Type); // comparison_type + AppendValue (sb, (int) node.Status); // status + AppendValue (sb, node.Extra); // extras + AppendValue (sb, node.Missing); // missing + AppendValue (sb, node.Present); // present + AppendValue (sb, node.Warning); // warning + AppendValue (sb, node.Todo); // todo + AppendValue (sb, node.Niex); // niex + AppendValue (sb, node.ThrowsNIE); // throwsnie + AppendValue (sb, node.Children.Count > 0); // has_children + AppendValue (sb, node.Messages.Count > 0 || node.Todos.Count > 0); // has_messages + AppendValue (sb, node.Name); // name + AppendValue (sb, node.TypeName); // typename + sb.Length--; // remove trailing \t + state.NodesWriter.WriteLine (sb); + + InsertMessages (state, node_name, node.Messages, false); + InsertMessages (state, node_name, node.Todos, true); + int counter = 0; + foreach (ComparisonNode n in node.Children) { + InsertTree (cnc, state, n, node_name, counter); + counter++; + } } - } - static string FormatString (string str) - { - if (String.IsNullOrEmpty (str)) - return "\\N"; - return str.Replace ("\t", "\\\t").Replace ('\n', ' ').Replace ('\r', ' '); - } + static void InsertMessages (State state, string node_name, List<string> strs, bool is_todo) + { + if (strs == null || strs.Count == 0) + return; - 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); + StringBuilder sb = new StringBuilder (); + foreach (string s in strs) { + sb.AppendFormat ("{0}\t{1}\t{2}\t{3}", node_name, state.MasterId, (is_todo) ? 1 : 0, FormatString (s)); + state.MessagesWriter.WriteLine (sb); + sb.Length = 0; } } - FilterNode (filters, node); - } + static string FormatString (string str) + { + if (String.IsNullOrEmpty (str)) + return "\\N"; + return str.Replace ("\t", "\\\t").Replace ('\n', ' ').Replace ('\r', ' '); + } - 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; + static void FilterRoot (ComparisonNode node) + { + Filters filters = GetDataAccess ().GetFilters (); + FilterNode (filters, node); } - List<ComparisonNode> removed = null; - foreach (ComparisonNode child in node.Children) { - if (FilterNode (filters, child)) { - if (removed == null) - removed = new List<ComparisonNode> (); - removed.Add (child); + 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; } - } - if (removed == null) - return false; - foreach (ComparisonNode child in removed) - node.Children.Remove (child); + 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; + return false; + } } -} -class State { - public readonly string Reference; - public readonly string Profile; - public readonly string Assembly; - public readonly string InfoFile; - public readonly string DllFile; - public readonly DateTime AssemblyLastWrite; - public readonly string NodesFileName; - public readonly string MessagesFileName; - - public ComparisonNode Root; - public int AlreadyComparing; - public int LockInUse; - public ManualResetEvent UpdateLock; - - public StreamWriter NodesWriter; - 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; - Profile = profile; - Assembly = assembly; - InfoFile = info_file; - DllFile = dll_file; - UpdateLock = new ManualResetEvent (false); - long ticks = DateTime.UtcNow.Ticks; - NodesFileName = Path.Combine (Path.GetTempPath (), String.Format ("tmpnodes{0}{1}{2}{3}", reference, profile, assembly, ticks)); - MessagesFileName = Path.Combine (Path.GetTempPath (), String.Format ("tmpmessages{0}{1}{2}{3}", reference, profile, assembly, ticks + 1)); - File.Delete (NodesFileName); - File.Delete (MessagesFileName); - AssemblyLastWrite = new FileInfo (dll_file).LastWriteTimeUtc; + class State { + public readonly string Reference; + public readonly string Profile; + public readonly string Assembly; + public readonly string InfoFile; + public readonly string DllFile; + public readonly DateTime AssemblyLastWrite; + public readonly string NodesFileName; + public readonly string MessagesFileName; + + public ComparisonNode Root; + public int AlreadyComparing; + public int LockInUse; + public ManualResetEvent UpdateLock; + + public StreamWriter NodesWriter; + 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; + Profile = profile; + Assembly = assembly; + InfoFile = info_file; + DllFile = dll_file; + UpdateLock = new ManualResetEvent (false); + long ticks = DateTime.UtcNow.Ticks; + NodesFileName = Path.Combine (Path.GetTempPath (), String.Format ("tmpnodes{0}{1}{2}{3}", reference, profile, assembly, ticks)); + MessagesFileName = Path.Combine (Path.GetTempPath (), String.Format ("tmpmessages{0}{1}{2}{3}", reference, profile, assembly, ticks + 1)); + File.Delete (NodesFileName); + File.Delete (MessagesFileName); + AssemblyLastWrite = new FileInfo (dll_file).LastWriteTimeUtc; + } } -} -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); + 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); + 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; + } - 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; + } - 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; } - - return false; } } - |