2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.Data.Linq / src / DbMetal / Generator / Implementation / Processor.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 using System;\r
27 using System.Collections.Generic;\r
28 using System.Globalization;\r
29 using System.IO;\r
30 using System.Linq;\r
31 using DbLinq.Factory;\r
32 using DbLinq.Schema;\r
33 using DbLinq.Schema.Dbml;\r
34 using DbLinq.Util;\r
35 using DbLinq.Vendor;\r
36 using DbMetal.Schema;\r
37 \r
38 using Mono.Options;\r
39 \r
40 namespace DbMetal.Generator.Implementation\r
41 {\r
42 #if !MONO_STRICT\r
43     public\r
44 #endif\r
45     class Processor : IProcessor\r
46     {\r
47         private TextWriter log;\r
48         /// <summary>\r
49         /// Log output\r
50         /// </summary>\r
51         public TextWriter Log\r
52         {\r
53             get { return log ?? Console.Out; }\r
54             set\r
55             {\r
56                 log = value;\r
57                 SchemaLoaderFactory.Log = value;\r
58             }\r
59         }\r
60 \r
61         public ISchemaLoaderFactory SchemaLoaderFactory { get; set; }\r
62 \r
63         public Processor()\r
64         {\r
65             //for DbMetal for want to log to console\r
66             //for VisualMetal we want to log to log4net\r
67             //Logger = ObjectFactory.Get<ILogger>();\r
68             SchemaLoaderFactory = ObjectFactory.Get<ISchemaLoaderFactory>();\r
69         }\r
70 \r
71         public void Process(string[] args)\r
72         {\r
73             var parameters = new Parameters { Log = Log };\r
74 \r
75             if (args.Length == 0)\r
76                 PrintUsage(parameters);\r
77 \r
78             else\r
79             {\r
80                 parameters.WriteHeader();\r
81 \r
82                 try\r
83                 {\r
84                     parameters.Parse(args);\r
85                 }\r
86                 catch (Exception e)\r
87                 {\r
88                     Output.WriteErrorLine(Log, e.Message);\r
89                     PrintUsage(parameters);\r
90                     return;\r
91                 }\r
92 \r
93                 if (parameters.Help)\r
94                 {\r
95                     PrintUsage(parameters);\r
96                     return;\r
97                 }\r
98 \r
99                 ProcessSchema(parameters);\r
100 \r
101                 if (parameters.ReadLineAtExit)\r
102                 {\r
103                     // '-readLineAtExit' flag: useful when running from Visual Studio\r
104                     Console.ReadKey();\r
105                 }\r
106             }\r
107         }\r
108 \r
109         private void ProcessSchema(Parameters parameters)\r
110         {\r
111             try\r
112             {\r
113                 // we always need a factory, even if generating from a DBML file, because we need a namespace\r
114                 ISchemaLoader schemaLoader;\r
115                 // then we load the schema\r
116                 var dbSchema = ReadSchema(parameters, out schemaLoader);\r
117                 // the we write it (to DBML or code)\r
118                 WriteSchema(dbSchema, schemaLoader, parameters);\r
119             }\r
120             catch (Exception ex)\r
121             {\r
122                 string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;\r
123                 Output.WriteErrorLine(Log, assemblyName + " failed:" + ex);\r
124             }\r
125         }\r
126 \r
127         protected void WriteSchema(Database dbSchema, ISchemaLoader schemaLoader, Parameters parameters)\r
128         {\r
129             if (parameters.Dbml != null)\r
130             {\r
131                 //we are supposed to write out a DBML file and exit\r
132                 parameters.Write("<<< Writing file '{0}'", parameters.Dbml);\r
133                 using (Stream dbmlFile = File.Create(parameters.Dbml))\r
134                 {\r
135                     DbmlSerializer.Write(dbmlFile, dbSchema);\r
136                 }\r
137             }\r
138             else\r
139             {\r
140                 if (!parameters.Schema)\r
141                     RemoveSchemaFromTables(dbSchema);\r
142 \r
143                 // extract filename from output filename, database schema or schema name\r
144                 string filename = parameters.Code;\r
145                 if (string.IsNullOrEmpty(filename) && !string.IsNullOrEmpty(parameters.Database))\r
146                     filename = parameters.Database.Replace("\"", "");\r
147                 if (string.IsNullOrEmpty(filename))\r
148                     filename = dbSchema.Name;\r
149 \r
150                 // TODO: move such check to runtime.\r
151                 schemaLoader.CheckNamesSafety(dbSchema);\r
152 \r
153                 parameters.Write("<<< writing C# classes in file '{0}'", filename);\r
154                 GenerateCode(parameters, dbSchema, schemaLoader, filename);\r
155 \r
156             }\r
157         }\r
158 \r
159         protected void RemoveSchemaFromTables(Database schema)\r
160         {\r
161             foreach (var table in schema.Table)\r
162             {\r
163                 string[] nameAndSchema = table.Name.Split('.');\r
164                 table.Name = nameAndSchema[nameAndSchema.Length - 1];\r
165             }\r
166         }\r
167 \r
168         public virtual IEnumerable<ICodeGenerator> EnumerateCodeGenerators()\r
169         {\r
170             foreach (var codeGeneratorType in ObjectFactory.Current.GetImplementations(typeof(ICodeGenerator)))\r
171             {\r
172                 yield return (ICodeGenerator)ObjectFactory.Current.Get(codeGeneratorType);\r
173             }\r
174         }\r
175 \r
176         protected virtual ICodeGenerator FindCodeGeneratorByLanguage(string languageCode)\r
177         {\r
178             return (from codeGenerator in EnumerateCodeGenerators()\r
179                     where codeGenerator.LanguageCode == languageCode\r
180                     select codeGenerator).SingleOrDefault();\r
181         }\r
182 \r
183         protected virtual ICodeGenerator FindCodeGeneratorByExtension(string extension)\r
184         {\r
185             return EnumerateCodeGenerators().SingleOrDefault(gen => gen.Extension == extension);\r
186         }\r
187 \r
188         public virtual ICodeGenerator FindCodeGenerator(Parameters parameters, string filename)\r
189         {\r
190             if (!string.IsNullOrEmpty(parameters.Language))\r
191                 return FindCodeGeneratorByLanguage(parameters.Language);\r
192             return FindCodeGeneratorByExtension(Path.GetExtension(filename));\r
193         }\r
194 \r
195         public void GenerateCode(Parameters parameters, Database dbSchema, ISchemaLoader schemaLoader, string filename)\r
196         {\r
197             ICodeGenerator codeGenerator = FindCodeGenerator(parameters, filename);\r
198             if (codeGenerator == null)\r
199                 throw new ArgumentException("Please specify either a /language or a /code file");\r
200 \r
201             if (string.IsNullOrEmpty(filename))\r
202                 filename = dbSchema.Class;\r
203             if (String.IsNullOrEmpty(Path.GetExtension(filename)))\r
204                 filename += codeGenerator.Extension;\r
205 \r
206             using (var streamWriter = new StreamWriter(filename))\r
207             {\r
208                 var generationContext = new GenerationContext(parameters, schemaLoader);\r
209                 codeGenerator.Write(streamWriter, dbSchema, generationContext);\r
210             }\r
211         }\r
212 \r
213         public Database ReadSchema(Parameters parameters, out ISchemaLoader schemaLoader)\r
214         {\r
215             Database dbSchema;\r
216             var nameAliases = NameAliasesLoader.Load(parameters.Aliases);\r
217             if (parameters.SchemaXmlFile == null) // read schema from DB\r
218             {\r
219                 schemaLoader = SchemaLoaderFactory.Load(parameters);\r
220 \r
221                 parameters.Write(">>> Reading schema from {0} database", schemaLoader.Vendor.VendorName);\r
222                 dbSchema = schemaLoader.Load(parameters.Database, nameAliases,\r
223                     new NameFormat(parameters.Pluralize, GetCase(parameters), new CultureInfo(parameters.Culture)),\r
224                     parameters.Sprocs, parameters.Namespace, parameters.Namespace);\r
225                 dbSchema.Provider = parameters.Provider;\r
226                 dbSchema.Tables.Sort(new LambdaComparer<Table>((x, y) => (x.Type.Name.CompareTo(y.Type.Name))));\r
227                 foreach (var table in dbSchema.Tables)\r
228                     table.Type.Columns.Sort(new LambdaComparer<Column>((x, y) => (x.Member.CompareTo(y.Member))));\r
229                 dbSchema.Functions.Sort(new LambdaComparer<Function>((x, y) => (x.Method.CompareTo(y.Method))));\r
230                 //SchemaPostprocess.PostProcess_DB(dbSchema);\r
231             }\r
232             else // load DBML\r
233             {\r
234                 dbSchema = ReadSchema(parameters, parameters.SchemaXmlFile);\r
235                 schemaLoader = SchemaLoaderFactory.Load(dbSchema.Provider);\r
236             }\r
237 \r
238             if (schemaLoader == null)\r
239                 throw new ApplicationException("Please provide -Provider=MySql (or Oracle, OracleODP, PostgreSql, Sqlite - see app.config for provider listing)");\r
240 \r
241             return dbSchema;\r
242         }\r
243 \r
244         public Database ReadSchema(Parameters parameters, string filename)\r
245         {\r
246             parameters.Write(">>> Reading schema from DBML file '{0}'", filename);\r
247             using (Stream dbmlFile = File.OpenRead(filename))\r
248             {\r
249                 return DbmlSerializer.Read(dbmlFile);\r
250             }\r
251         }\r
252 \r
253         private void PrintUsage(Parameters parameters)\r
254         {\r
255             parameters.WriteHelp();\r
256         }\r
257 \r
258         private Case GetCase(Parameters parameters)\r
259         {\r
260             if (String.IsNullOrEmpty(parameters.Case))\r
261                 return Case.PascalCase;\r
262 \r
263             switch (parameters.Case.ToLowerInvariant())\r
264             {\r
265                 case "leave":\r
266                     return Case.Leave;\r
267                 case "camel":\r
268                     return Case.camelCase;\r
269                 case "pascal":\r
270                     return Case.PascalCase;\r
271                 default:\r
272                     return Case.NetCase;\r
273             }\r
274         }\r
275     }\r
276 }\r