1 #region MIT license
2 //
3 // MIT license
4 //
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 // THE SOFTWARE.
24 //
25 #endregion
26 using System;
27 using System.CodeDom;
28 using System.CodeDom.Compiler;
29 using System.Collections.Generic;
30 using System.ComponentModel;
31 using System.IO;
32 using System.Linq;
33 using System.Reflection;
34 using System.Text;
35 
36 using Microsoft.CSharp;
37 using Microsoft.VisualBasic;
38 
39 using DbLinq.Schema.Dbml;
40 
41 namespace DbMetal.Generator.Implementation.CodeDomGenerator
42 {
43 #if !MONO_STRICT
44     public
45 #endif
46     abstract class AbstractCodeDomGenerator : ICodeGenerator
47     {
48         public abstract string LanguageCode { get; }
49         public abstract string Extension { get; }
Write(TextWriter textWriter, Database dbSchema, GenerationContext context)50         public abstract void Write(TextWriter textWriter, Database dbSchema, GenerationContext context);
51 
52         /// <summary>
53         /// Generates a C# source code wrapper for the database schema objects.
54         /// </summary>
55         /// <param name="database"></param>
56         /// <param name="filename"></param>
GenerateCSharp(Database database, string filename)57         public void GenerateCSharp(Database database, string filename)
58         {
59             using (Stream stream = File.Open(filename, FileMode.Create))
60             {
61                 using (StreamWriter writer = new StreamWriter(stream))
62                 {
63                     new CSharpCodeProvider().CreateGenerator(writer).GenerateCodeFromNamespace(GenerateCodeDomModel(database), writer, new CodeGeneratorOptions() { BracingStyle = "C" });
64                 }
65             }
66         }
67 
68         /// <summary>
69         /// Generates a Visual Basic source code wrapper for the database schema objects.
70         /// </summary>
71         /// <param name="database"></param>
72         /// <param name="filename"></param>
GenerateVisualBasic(Database database, string filename)73         public void GenerateVisualBasic(Database database, string filename)
74         {
75             using (Stream stream = File.Open(filename, FileMode.Create))
76             {
77                 using (StreamWriter writer = new StreamWriter(stream))
78                 {
79                     new VBCodeProvider().CreateGenerator(writer).GenerateCodeFromNamespace(GenerateCodeDomModel(database), writer, new CodeGeneratorOptions() { BracingStyle = "C" });
80                 }
81             }
82         }
83 
84         CodeThisReferenceExpression thisReference = new CodeThisReferenceExpression();
85 
GenerateCodeDomModel(Database database)86         protected virtual CodeNamespace GenerateCodeDomModel(Database database)
87         {
88             CodeNamespace _namespace = new CodeNamespace(database.ContextNamespace);
89 
90             _namespace.Imports.Add(new CodeNamespaceImport("System"));
91             _namespace.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));
92             _namespace.Imports.Add(new CodeNamespaceImport("System.Data"));
93             _namespace.Imports.Add(new CodeNamespaceImport("System.Data.Linq.Mapping"));
94             _namespace.Imports.Add(new CodeNamespaceImport("System.Diagnostics"));
95             _namespace.Imports.Add(new CodeNamespaceImport("DbLinq.Linq"));
96             _namespace.Imports.Add(new CodeNamespaceImport("DbLinq.Linq.Mapping"));
97 
98             _namespace.Comments.Add(new CodeCommentStatement(GenerateCommentBanner(database)));
99 
100             _namespace.Types.Add(GenerateContextClass(database));
101 
102             foreach (Table table in database.Tables)
103                 _namespace.Types.Add(GenerateTableClass(table));
104             return _namespace;
105         }
106 
GenerateCommentBanner(Database database)107         protected virtual string GenerateCommentBanner(Database database)
108         {
109             var result = new StringBuilder();
110 
111             // http://www.network-science.de/ascii/
112             // http://www.network-science.de/ascii/ascii.php?TEXT=MetalSequel&x=14&y=14&FONT=_all+fonts+with+your+text_&RICH=no&FORM=left&STRE=no&WIDT=80
113             result.Append(
114                 @"
115   ____  _     __  __      _        _
116  |  _ \| |__ |  \/  | ___| |_ __ _| |
117  | | | | '_ \| |\/| |/ _ \ __/ _` | |
118  | |_| | |_) | |  | |  __/ || (_| | |
119  |____/|_.__/|_|  |_|\___|\__\__,_|_|
120 
121 ");
122             result.AppendLine(String.Format(" Auto-generated from {0} on {1}.", database.Name, DateTime.Now));
123             result.AppendLine(" Please visit http://linq.to/db for more information.");
124 
125             return result.ToString();
126         }
127 
GenerateContextClass(Database database)128         protected virtual CodeTypeDeclaration GenerateContextClass(Database database)
129         {
130             var _class = new CodeTypeDeclaration() { IsClass = true, IsPartial = true, Name = database.Class, TypeAttributes = TypeAttributes.Public };
131 
132             if (database.BaseType != null)
133                 _class.BaseTypes.Add(database.BaseType);
134             else // TODO: something with less risk
135                 _class.BaseTypes.Add(String.Format("DbLinq.{0}.{0}DataContext", database.Provider));
136 
137             // CodeDom does not currently support partial methods.  This will be a problem for VB.  Will probably be fixed in .net 4
138             _class.Members.Add(new CodeSnippetTypeMember("\tpartial void OnCreated();"));
139 
140             // Implement Constructor
141             var constructor = new CodeConstructor() { Attributes = MemberAttributes.Public, Parameters = { new CodeParameterDeclarationExpression("IDbConnection", "connection") } };
142             constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("connection"));
143             constructor.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, "OnCreated")));
144             _class.Members.Add(constructor);
145 
146             // todo: override other constructors
147 
148             foreach (Table table in database.Tables)
149             {
150                 var tableType = new CodeTypeReference(table.Member);
151                 var property = new CodeMemberProperty() { Type = new CodeTypeReference("Table", tableType), Name = table.Member, Attributes = MemberAttributes.Public | MemberAttributes.Final };
152                 property.GetStatements.Add
153                     (
154                     new CodeMethodReturnStatement
155                         (
156                         new CodeMethodInvokeExpression
157                             (
158                             new CodeMethodReferenceExpression(thisReference, "GetTable", tableType)
159                             )
160                         )
161                     );
162                 _class.Members.Add(property);
163             }
164 
165             return _class;
166         }
167 
GenerateTableClass(Table table)168         protected virtual CodeTypeDeclaration GenerateTableClass(Table table)
169         {
170             var _class = new CodeTypeDeclaration() { IsClass = true, IsPartial = true, Name = table.Member, TypeAttributes = TypeAttributes.Public };
171 
172             _class.CustomAttributes.Add(new CodeAttributeDeclaration("Table", new CodeAttributeArgument("Name", new CodePrimitiveExpression(table.Name))));
173 
174             // Implement Constructor
175             var constructor = new CodeConstructor() { Attributes = MemberAttributes.Public };
176             constructor.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, "OnCreated")));
177             _class.Members.Add(constructor);
178 
179             // todo: implement INotifyPropertyChanging
180 
181             // Implement INotifyPropertyChanged
182             _class.BaseTypes.Add(typeof(INotifyPropertyChanged));
183 
184             var propertyChangedEvent = new CodeMemberEvent() { Type = new CodeTypeReference(typeof(PropertyChangedEventHandler)), Name = "PropertyChanged", Attributes = MemberAttributes.Public };
185             _class.Members.Add(propertyChangedEvent);
186 
187             var sendPropertyChangedMethod = new CodeMemberMethod() { Attributes = MemberAttributes.Family, Name = "SendPropertyChanged", Parameters = { new CodeParameterDeclarationExpression(typeof(System.String), "propertyName") } };
188             sendPropertyChangedMethod.Statements.Add
189                 (
190                 new CodeConditionStatement
191                     (
192                     new CodeSnippetExpression(propertyChangedEvent.Name + " != null"), // todo: covert this to CodeBinaryOperatorExpression
193                     new CodeExpressionStatement
194                         (
195                         new CodeMethodInvokeExpression
196                             (
197                             new CodeMethodReferenceExpression(thisReference, propertyChangedEvent.Name),
198                             thisReference,
199                             new CodeObjectCreateExpression(typeof(PropertyChangedEventArgs), new CodeArgumentReferenceExpression("propertyName"))
200                             )
201                         )
202                     )
203                 );
204             _class.Members.Add(sendPropertyChangedMethod);
205 
206             // CodeDom does not currently support partial methods.  This will be a problem for VB.  Will probably be fixed in .net 4
207             _class.Members.Add(new CodeSnippetTypeMember("\tpartial void OnCreated();"));
208 
209             // todo: add these when the actually get called
210             //partial void OnLoaded();
211             //partial void OnValidate(System.Data.Linq.ChangeAction action);
212 
213             // columns
214             foreach (Column column in table.Type.Columns)
215             {
216                 var type = new CodeTypeReference(column.Type);
217                 var columnMember = column.Member ?? column.Name;
218 
219                 var field = new CodeMemberField(type, "_" + columnMember);
220                 _class.Members.Add(field);
221                 var fieldReference = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
222 
223                 // CodeDom does not currently support partial methods.  This will be a problem for VB.  Will probably be fixed in .net 4
224                 string onChangingPartialMethodName = String.Format("On{0}Changing", columnMember);
225                 _class.Members.Add(new CodeSnippetTypeMember(String.Format("\tpartial void {0}({1} instance);", onChangingPartialMethodName, column.Type)));
226                 string onChangedPartialMethodName = String.Format("On{0}Changed", columnMember);
227                 _class.Members.Add(new CodeSnippetTypeMember(String.Format("\tpartial void {0}();", onChangedPartialMethodName)));
228 
229                 var property = new CodeMemberProperty();
230                 property.Type = type;
231                 property.Name = columnMember;
232                 property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
233                 property.CustomAttributes.Add
234                     (
235                     new CodeAttributeDeclaration
236                         (
237                         "Column",
238                         new CodeAttributeArgument("Storage", new CodePrimitiveExpression(column.Storage)),
239                         new CodeAttributeArgument("Name", new CodePrimitiveExpression(column.Name)),
240                         new CodeAttributeArgument("DbType", new CodePrimitiveExpression(column.DbType)),
241                         new CodeAttributeArgument("CanBeNull", new CodePrimitiveExpression(column.CanBeNull)),
242                         new CodeAttributeArgument("IsPrimaryKey", new CodePrimitiveExpression(column.IsPrimaryKey))
243                         )
244                     );
245                 property.CustomAttributes.Add(new CodeAttributeDeclaration("DebuggerNonUserCode"));
246                 property.GetStatements.Add(new CodeMethodReturnStatement(fieldReference));
247                 property.SetStatements.Add
248                     (
249                     new CodeConditionStatement
250                         (
251                         new CodeSnippetExpression(field.Name + " != value"), // todo: covert this to CodeBinaryOperatorExpression
252                         new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, onChangingPartialMethodName, new CodePropertySetValueReferenceExpression())),
253                         new CodeAssignStatement(fieldReference, new CodePropertySetValueReferenceExpression()),
254                         new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, sendPropertyChangedMethod.Name, new CodePrimitiveExpression(property.Name))),
255                         new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, onChangedPartialMethodName))
256                         )
257                     );
258                 _class.Members.Add(property);
259             }
260 
261             // TODO: implement associations
262 
263             // TODO: implement functions / procedures
264 
265             // TODO: Override Equals and GetHashCode
266 
267             return _class;
268         }
269     }
270 }
271