New tests.
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Vendor / Implementation / SchemaLoader.cs
index 627d3ef2ef7ea3b2c95765fe45f607d0c354192c..ef05fc55baa17f3bd07991298be8f18743091364 100644 (file)
@@ -26,6 +26,7 @@
 using System;\r
 using System.Collections.Generic;\r
 using System.Data;\r
+using System.IO;\r
 using System.Linq;\r
 \r
 #if MONO_STRICT\r
@@ -35,26 +36,53 @@ using DbLinq.Data.Linq;
 #endif\r
 \r
 using DbLinq.Factory;\r
-using DbLinq.Logging;\r
 using DbLinq.Schema;\r
 using DbLinq.Schema.Dbml;\r
+using System.Text.RegularExpressions;\r
 \r
 namespace DbLinq.Vendor.Implementation\r
 {\r
-#if MONO_STRICT\r
-    internal\r
-#else\r
+#if !MONO_STRICT\r
     public\r
 #endif\r
- abstract partial class SchemaLoader : ISchemaLoader\r
   abstract partial class SchemaLoader : ISchemaLoader\r
     {\r
-        public virtual string VendorName { get { return Vendor.VendorName; } }\r
-        public abstract IVendor Vendor { get; }\r
-        public abstract System.Type DataContextType { get; }\r
+        /// <summary>\r
+        /// Underlying vendor\r
+        /// </summary>\r
+        /// <value></value>\r
+        public abstract IVendor Vendor { get; set; }\r
+        /// <summary>\r
+        /// Connection used to read schema\r
+        /// </summary>\r
+        /// <value></value>\r
         public IDbConnection Connection { get; set; }\r
+        /// <summary>\r
+        /// Gets or sets the name formatter.\r
+        /// </summary>\r
+        /// <value>The name formatter.</value>\r
         public INameFormatter NameFormatter { get; set; }\r
-        public ILogger Logger { get; set; }\r
 \r
+        private TextWriter log;\r
+        /// <summary>\r
+        /// Log output\r
+        /// </summary>\r
+        public TextWriter Log\r
+        {\r
+            get { return log ?? Console.Out; }\r
+            set { log = value; }\r
+        }\r
+\r
+        /// <summary>\r
+        /// Loads database schema\r
+        /// </summary>\r
+        /// <param name="databaseName"></param>\r
+        /// <param name="nameAliases"></param>\r
+        /// <param name="nameFormat"></param>\r
+        /// <param name="loadStoredProcedures"></param>\r
+        /// <param name="contextNamespace"></param>\r
+        /// <param name="entityNamespace"></param>\r
+        /// <returns></returns>\r
         public virtual Database Load(string databaseName, INameAliases nameAliases, NameFormat nameFormat,\r
             bool loadStoredProcedures, string contextNamespace, string entityNamespace)\r
         {\r
@@ -69,12 +97,14 @@ namespace DbLinq.Vendor.Implementation
             if (string.IsNullOrEmpty(databaseName))\r
                 throw new ArgumentException("A database name is required. Please specify /database=<databaseName>");\r
 \r
+            databaseName = GetDatabaseNameAliased(databaseName, nameAliases);\r
+\r
             var schemaName = NameFormatter.GetSchemaName(databaseName, GetExtraction(databaseName), nameFormat);\r
             var names = new Names();\r
             var schema = new Database\r
                              {\r
                                  Name = schemaName.DbName,\r
-                                 Class = schemaName.ClassName,\r
+                                 Class = GetRuntimeClassName(schemaName.ClassName, nameAliases),\r
                                  BaseType = typeof(DataContext).FullName,\r
                                  ContextNamespace = contextNamespace,\r
                                  EntityNamespace = entityNamespace,\r
@@ -97,23 +127,73 @@ namespace DbLinq.Vendor.Implementation
             //CheckNamesSafety(schema);\r
 \r
             // generate backing fields name (since we have here correct names)\r
-            GenerateStorageFields(schema);\r
+            GenerateStorageAndMemberFields(schema);\r
 \r
             return schema;\r
         }\r
 \r
+        /// <summary>\r
+        /// Gets a usable name for the database.\r
+        /// </summary>\r
+        /// <param name="databaseName">Name of the database.</param>\r
+        /// <returns></returns>\r
+        protected virtual string GetDatabaseName(string databaseName)\r
+        {\r
+            return databaseName;\r
+        }\r
+\r
+        protected virtual string GetDatabaseNameAliased(string databaseName, INameAliases nameAliases)\r
+        {\r
+            string databaseNameAliased = nameAliases != null ? nameAliases.GetDatabaseNameAlias(databaseName) : null;\r
+            return (databaseNameAliased != null) ? databaseNameAliased : GetDatabaseName(databaseName);\r
+        }\r
+\r
+        /// <summary>\r
+        /// Gets a usable name for the database class.\r
+        /// </summary>\r
+        /// <param name="databaseName">Name of the clas.</param>\r
+        /// <returns></returns>\r
+        protected virtual string GetRuntimeClassName(string className, INameAliases nameAliases)\r
+        {\r
+            string classNameAliased = nameAliases != null ? nameAliases.GetClassNameAlias(className) : null;\r
+            return (classNameAliased != null) ? classNameAliased : className;\r
+        }\r
+\r
+        /// <summary>\r
+        /// Writes an error line.\r
+        /// </summary>\r
+        /// <param name="format">The format.</param>\r
+        /// <param name="arg">The arg.</param>\r
+        protected void WriteErrorLine(string format, params object[] arg)\r
+        {\r
+            var o = Log;\r
+            if (o == Console.Out)\r
+                o = Console.Error;\r
+            o.WriteLine(format, arg);\r
+        }\r
+\r
         protected SchemaLoader()\r
         {\r
-            Logger = ObjectFactory.Get<ILogger>();\r
             NameFormatter = ObjectFactory.Create<INameFormatter>(); // the Pluralize property is set dynamically, so no singleton\r
         }\r
 \r
+        /// <summary>\r
+        /// Gets the extraction type from a columnname.\r
+        /// </summary>\r
+        /// <param name="dbColumnName">Name of the db column.</param>\r
+        /// <returns></returns>\r
         protected virtual WordsExtraction GetExtraction(string dbColumnName)\r
         {\r
             bool isMixedCase = dbColumnName != dbColumnName.ToLower() && dbColumnName != dbColumnName.ToUpper();\r
             return isMixedCase ? WordsExtraction.FromCase : WordsExtraction.FromDictionary;\r
         }\r
 \r
+        /// <summary>\r
+        /// Gets the full name of a name and schema.\r
+        /// </summary>\r
+        /// <param name="dbName">Name of the db.</param>\r
+        /// <param name="dbSchema">The db schema.</param>\r
+        /// <returns></returns>\r
         protected virtual string GetFullDbName(string dbName, string dbSchema)\r
         {\r
             string fullDbName;\r
@@ -124,6 +204,15 @@ namespace DbLinq.Vendor.Implementation
             return fullDbName;\r
         }\r
 \r
+        /// <summary>\r
+        /// Creates the name of the table given a name and schema\r
+        /// </summary>\r
+        /// <param name="dbTableName">Name of the db table.</param>\r
+        /// <param name="dbSchema">The db schema.</param>\r
+        /// <param name="nameAliases">The name aliases.</param>\r
+        /// <param name="nameFormat">The name format.</param>\r
+        /// <param name="extraction">The extraction.</param>\r
+        /// <returns></returns>\r
         protected virtual TableName CreateTableName(string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat, WordsExtraction extraction)\r
         {\r
             // if we have an alias, use it, and don't try to analyze it (a human probably already did the job)\r
@@ -149,6 +238,17 @@ namespace DbLinq.Vendor.Implementation
             return CreateTableName(dbTableName, dbSchema, nameAliases, nameFormat, GetExtraction(dbTableName));\r
         }\r
 \r
+        Regex startsWithNumber = new Regex(@"^\d", RegexOptions.Compiled);\r
+\r
+        /// <summary>\r
+        /// Creates the name of the column.\r
+        /// </summary>\r
+        /// <param name="dbColumnName">Name of the db column.</param>\r
+        /// <param name="dbTableName">Name of the db table.</param>\r
+        /// <param name="dbSchema">The db schema.</param>\r
+        /// <param name="nameAliases">The name aliases.</param>\r
+        /// <param name="nameFormat">The name format.</param>\r
+        /// <returns></returns>\r
         protected virtual ColumnName CreateColumnName(string dbColumnName, string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat)\r
         {\r
             var columnNameAlias = nameAliases != null ? nameAliases.GetColumnMemberAlias(dbColumnName, dbTableName, dbSchema) : null;\r
@@ -168,10 +268,21 @@ namespace DbLinq.Vendor.Implementation
             var tableName = CreateTableName(dbTableName, dbSchema, nameAliases, nameFormat);\r
             if (columnName.PropertyName == tableName.ClassName)\r
                 columnName.PropertyName = columnName.PropertyName + "1";\r
+\r
+            if (startsWithNumber.IsMatch(columnName.PropertyName))\r
+                columnName.PropertyName = "_" + columnName.PropertyName;\r
+\r
             columnName.DbName = dbColumnName;\r
             return columnName;\r
         }\r
 \r
+        /// <summary>\r
+        /// Creates the name of the procedure.\r
+        /// </summary>\r
+        /// <param name="dbProcedureName">Name of the db procedure.</param>\r
+        /// <param name="dbSchema">The db schema.</param>\r
+        /// <param name="nameFormat">The name format.</param>\r
+        /// <returns></returns>\r
         protected virtual ProcedureName CreateProcedureName(string dbProcedureName, string dbSchema, NameFormat nameFormat)\r
         {\r
             var procedureName = NameFormatter.GetProcedureName(dbProcedureName, GetExtraction(dbProcedureName), nameFormat);\r
@@ -179,6 +290,17 @@ namespace DbLinq.Vendor.Implementation
             return procedureName;\r
         }\r
 \r
+        /// <summary>\r
+        /// Creates the name of the association.\r
+        /// </summary>\r
+        /// <param name="dbManyName">Name of the db many.</param>\r
+        /// <param name="dbManySchema">The db many schema.</param>\r
+        /// <param name="dbOneName">Name of the db one.</param>\r
+        /// <param name="dbOneSchema">The db one schema.</param>\r
+        /// <param name="dbConstraintName">Name of the db constraint.</param>\r
+        /// <param name="foreignKeyName">Name of the foreign key.</param>\r
+        /// <param name="nameFormat">The name format.</param>\r
+        /// <returns></returns>\r
         protected virtual AssociationName CreateAssociationName(string dbManyName, string dbManySchema,\r
             string dbOneName, string dbOneSchema, string dbConstraintName, string foreignKeyName, NameFormat nameFormat)\r
         {\r
@@ -188,6 +310,13 @@ namespace DbLinq.Vendor.Implementation
             return associationName;\r
         }\r
 \r
+        /// <summary>\r
+        /// Creates the name of the schema.\r
+        /// </summary>\r
+        /// <param name="databaseName">Name of the database.</param>\r
+        /// <param name="connection">The connection.</param>\r
+        /// <param name="nameFormat">The name format.</param>\r
+        /// <returns></returns>\r
         protected virtual SchemaName CreateSchemaName(string databaseName, IDbConnection connection, NameFormat nameFormat)\r
         {\r
             if (string.IsNullOrEmpty(databaseName))\r
@@ -222,6 +351,15 @@ namespace DbLinq.Vendor.Implementation
             }\r
         }\r
 \r
+        /// <summary>\r
+        /// Loads the tables in the given schema.\r
+        /// </summary>\r
+        /// <param name="schema">The schema.</param>\r
+        /// <param name="schemaName">Name of the schema.</param>\r
+        /// <param name="conn">The conn.</param>\r
+        /// <param name="nameAliases">The name aliases.</param>\r
+        /// <param name="nameFormat">The name format.</param>\r
+        /// <param name="names">The names.</param>\r
         protected virtual void LoadTables(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names)\r
         {\r
             var tables = ReadTables(conn, schemaName.DbName);\r
@@ -238,6 +376,15 @@ namespace DbLinq.Vendor.Implementation
             }\r
         }\r
 \r
+        /// <summary>\r
+        /// Loads the columns.\r
+        /// </summary>\r
+        /// <param name="schema">The schema.</param>\r
+        /// <param name="schemaName">Name of the schema.</param>\r
+        /// <param name="conn">The conn.</param>\r
+        /// <param name="nameAliases">The name aliases.</param>\r
+        /// <param name="nameFormat">The name format.</param>\r
+        /// <param name="names">The names.</param>\r
         protected void LoadColumns(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names)\r
         {\r
             var columnRows = ReadColumns(conn, schemaName.DbName);\r
@@ -251,7 +398,7 @@ namespace DbLinq.Vendor.Implementation
                 DbLinq.Schema.Dbml.Table tableSchema = schema.Tables.FirstOrDefault(tblSchema => fullColumnDbName == tblSchema.Name);\r
                 if (tableSchema == null)\r
                 {\r
-                    Logger.Write(Level.Error, "ERROR L46: Table '" + columnRow.TableName + "' not found for column " + columnRow.ColumnName);\r
+                    WriteErrorLine("ERROR L46: Table '" + columnRow.TableName + "' not found for column " + columnRow.ColumnName);\r
                     continue;\r
                 }\r
                 var column = new Column();\r
@@ -262,8 +409,15 @@ namespace DbLinq.Vendor.Implementation
                 if (columnRow.PrimaryKey.HasValue)\r
                     column.IsPrimaryKey = columnRow.PrimaryKey.Value;\r
 \r
-                if (columnRow.Generated.HasValue)\r
-                    column.IsDbGenerated = columnRow.Generated.Value;\r
+                bool? generated = (nameAliases != null) ? nameAliases.GetColumnGenerated(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null;\r
+                if (!generated.HasValue)\r
+                    generated = columnRow.Generated;\r
+                if (generated.HasValue)\r
+                    column.IsDbGenerated = generated.Value;\r
+\r
+                AutoSync? autoSync = (nameAliases != null) ? nameAliases.GetColumnAutoSync(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null;\r
+                if (autoSync.HasValue)\r
+                    column.AutoSync = autoSync.Value;\r
 \r
                 // the Expression can originate from two sources:\r
                 // 1. DefaultValue\r
@@ -274,6 +428,7 @@ namespace DbLinq.Vendor.Implementation
 \r
                 column.CanBeNull = columnRow.Nullable;\r
 \r
+                string columnTypeAlias = nameAliases != null ? nameAliases.GetColumnForcedType(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null;\r
                 var columnType = MapDbType(columnName.DbName, columnRow);\r
 \r
                 var columnEnumType = columnType as EnumType;\r
@@ -286,6 +441,8 @@ namespace DbLinq.Vendor.Implementation
                         enumType[enumValue.Key] = enumValue.Value;\r
                     }\r
                 }\r
+                else if (columnTypeAlias != null)\r
+                    column.Type = columnTypeAlias;\r
                 else\r
                     column.Type = columnType.ToString();\r
 \r