PageRenderTime 1ms CodeModel.GetById 2ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/System.Data.Linq/src/DbMetal/Generator/Implementation/CodeTextGenerator/CodeGenerator.cs

https://github.com/ekovalenko-softheme/mono
C# | 466 lines | 358 code | 45 blank | 63 comment | 47 complexity | 4ad39ac20c811d785348b9508a302bdf MD5 | raw file
  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
 26using System;
 27using System.Collections.Generic;
 28using System.IO;
 29using System.Linq;
 30using DbLinq.Data.Linq;
 31using DbLinq.Schema;
 32using DbLinq.Schema.Dbml;
 33using DbLinq.Schema.Dbml.Adapter;
 34using DbLinq.Util;
 35using Type = System.Type;
 36
 37#if MONO_STRICT
 38using System.Data.Linq;
 39#endif
 40
 41namespace DbMetal.Generator.Implementation.CodeTextGenerator
 42{
 43#if !MONO_STRICT
 44    public
 45#endif
 46    abstract partial class CodeGenerator : ICodeGenerator
 47    {
 48        public abstract string LanguageCode { get; }
 49        public abstract string Extension { get; }
 50
 51        protected class MassDisposer : IDisposable
 52        {
 53            public IList<IDisposable> Disposables = new List<IDisposable>();
 54
 55            public void Dispose()
 56            {
 57                for (int index = Disposables.Count - 1; index > 0; index--)
 58                {
 59                    Disposables[index].Dispose();
 60                }
 61            }
 62        }
 63
 64        protected abstract CodeWriter CreateCodeWriter(TextWriter textWriter);
 65
 66        public void Write(TextWriter textWriter, Database dbSchema, GenerationContext context)
 67        {
 68            if (dbSchema == null || dbSchema.Tables == null)
 69            {
 70                //Logger.Write(Level.Error, "CodeGenAll ERROR: incomplete dbSchema, cannot start generating code");
 71                return;
 72            }
 73
 74            context["namespace"] = string.IsNullOrEmpty(context.Parameters.Namespace)
 75                                       ? dbSchema.ContextNamespace
 76                                       : context.Parameters.Namespace;
 77            context["database"] = dbSchema.Name;
 78            context["generationTime"] = context.Parameters.GenerateTimestamps
 79                ? DateTime.Now.ToString("u")
 80                : "[TIMESTAMP]";
 81            context["class"] = dbSchema.Class;
 82
 83            using (var codeWriter = CreateCodeWriter(textWriter))
 84            {
 85                WriteBanner(codeWriter, context);
 86                WriteUsings(codeWriter, context);
 87
 88                string contextNamespace = context.Parameters.Namespace;
 89                if (string.IsNullOrEmpty(contextNamespace))
 90                    contextNamespace = dbSchema.ContextNamespace;
 91
 92                string entityNamespace = context.Parameters.Namespace;
 93                if (string.IsNullOrEmpty(entityNamespace))
 94                    entityNamespace = dbSchema.EntityNamespace;
 95
 96                bool generateDataContext = true;
 97                var types = context.Parameters.GenerateTypes;
 98                if (types.Count > 0)
 99                    generateDataContext = types.Contains(dbSchema.Class);
100
101                if (contextNamespace == entityNamespace)
102                {
103                    using (WriteNamespace(codeWriter, contextNamespace))
104                    {
105                        if (generateDataContext)
106                            WriteDataContext(codeWriter, dbSchema, context);
107                        WriteClasses(codeWriter, dbSchema, context);
108                    }
109                }
110                else
111                {
112                    if (generateDataContext)
113                        using (WriteNamespace(codeWriter, contextNamespace))
114                            WriteDataContext(codeWriter, dbSchema, context);
115                    using (WriteNamespace(codeWriter, entityNamespace))
116                        WriteClasses(codeWriter, dbSchema, context);
117                }
118            }
119        }
120
121        private void WriteBanner(CodeWriter writer, GenerationContext context)
122        {
123            using (writer.WriteRegion(context.Evaluate("Auto-generated classes for ${database} database on ${generationTime}")))
124            {
125                // http://www.network-science.de/ascii/
126                // 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 
127                writer.WriteCommentLines(
128                    @"
129 ____  _     __  __      _        _ 
130|  _ \| |__ |  \/  | ___| |_ __ _| |
131| | | | '_ \| |\/| |/ _ \ __/ _` | |
132| |_| | |_) | |  | |  __/ || (_| | |
133|____/|_.__/|_|  |_|\___|\__\__,_|_|
134");
135                writer.WriteCommentLines(context.Evaluate("Auto-generated from ${database} on ${generationTime}"));
136                writer.WriteCommentLines("Please visit http://linq.to/db for more information");
137            }
138        }
139
140        private void WriteUsings(CodeWriter writer, GenerationContext context)
141        {
142            writer.WriteUsingNamespace("System");
143            writer.WriteUsingNamespace("System.Data");
144            writer.WriteUsingNamespace("System.Data.Linq.Mapping");
145            writer.WriteUsingNamespace("System.Diagnostics");
146            writer.WriteUsingNamespace("System.Reflection");
147
148#if MONO_STRICT
149            writer.WriteUsingNamespace("System.Data.Linq");
150#else
151            writer.WriteLine("#if MONO_STRICT");
152            writer.WriteUsingNamespace("System.Data.Linq");
153            writer.WriteLine("#else   // MONO_STRICT");
154            writer.WriteUsingNamespace("DbLinq.Data.Linq");
155            writer.WriteUsingNamespace("DbLinq.Vendor");
156            writer.WriteLine("#endif  // MONO_STRICT");
157#endif
158
159            //            writer.WriteUsingNamespace("System");
160            //            writer.WriteUsingNamespace("System.Collections.Generic");
161            //            writer.WriteUsingNamespace("System.ComponentModel");
162            //            writer.WriteUsingNamespace("System.Data");
163            //            writer.WriteUsingNamespace("System.Data.Linq.Mapping");
164            //            writer.WriteUsingNamespace("System.Diagnostics");
165            //            writer.WriteUsingNamespace("System.Linq");
166            //            writer.WriteUsingNamespace("System.Reflection");
167            //            writer.WriteUsingNamespace("System.Text");
168            //#if MONO_STRICT
169            //            writer.WriteUsingNamespace("System.Data.Linq");
170            //#else
171            //            writer.WriteUsingNamespace("DbLinq.Data.Linq");
172            //            writer.WriteUsingNamespace("DbLinq.Data.Linq.Mapping");
173            //#endif
174
175            // now, we write usings required by implemented interfaces
176            foreach (var implementation in context.Implementations())
177                implementation.WriteHeader(writer, context);
178
179            // write namespaces for members attributes
180            foreach (var memberAttribute in context.Parameters.MemberAttributes)
181                WriteUsingNamespace(writer, GetNamespace(memberAttribute));
182
183            writer.WriteLine();
184        }
185
186        /// <summary>
187        /// Writes a using, if given namespace is not null or empty
188        /// </summary>
189        /// <param name="writer"></param>
190        /// <param name="nameSpace"></param>
191        protected virtual void WriteUsingNamespace(CodeWriter writer, string nameSpace)
192        {
193            if (!string.IsNullOrEmpty(nameSpace))
194                writer.WriteUsingNamespace(nameSpace);
195        }
196
197        protected virtual string GetNamespace(string fullName)
198        {
199            var namePartIndex = fullName.LastIndexOf('.');
200            // if we have a dot, we have a namespace
201            if (namePartIndex < 0)
202                return null;
203            return fullName.Substring(0, namePartIndex);
204        }
205
206        private IDisposable WriteNamespace(CodeWriter writer, string nameSpace)
207        {
208            if (!string.IsNullOrEmpty(nameSpace))
209                return writer.WriteNamespace(nameSpace);
210            return null;
211        }
212
213        private void WriteDataContext(CodeWriter writer, Database schema, GenerationContext context)
214        {
215            if (schema.Tables.Count == 0)
216            {
217                writer.WriteCommentLine("L69 no tables found");
218                return;
219            }
220
221
222            string contextBase = schema.BaseType;
223            var contextBaseType = string.IsNullOrEmpty(contextBase)
224                ? typeof(DataContext)
225                : TypeLoader.Load(contextBase);
226
227            // in all cases, get the literal type name from loaded type
228            contextBase = writer.GetLiteralType(contextBaseType);
229
230            var specifications = SpecificationDefinition.Partial;
231            if (schema.AccessModifierSpecified)
232                specifications |= GetSpecificationDefinition(schema.AccessModifier);
233            else
234                specifications |= SpecificationDefinition.Public;
235            if (schema.ModifierSpecified)
236                specifications |= GetSpecificationDefinition(schema.Modifier);
237            using (writer.WriteClass(specifications, schema.Class, contextBase))
238            {
239                WriteDataContextExtensibilityDeclarations(writer, schema, context);
240                WriteDataContextCtors(writer, schema, contextBaseType, context);
241                WriteDataContextTables(writer, schema, context);
242                WriteDataContextProcedures(writer, schema, context);
243            }
244        }
245
246        private void WriteDataContextTables(CodeWriter writer, Database schema, GenerationContext context)
247        {
248            foreach (var table in schema.Tables)
249                WriteDataContextTable(writer, table);
250            writer.WriteLine();
251        }
252
253        protected abstract void WriteDataContextTable(CodeWriter writer, Table table);
254
255        protected virtual Type GetType(string literalType, bool canBeNull)
256        {
257            bool isNullable = literalType.EndsWith("?");
258            if (isNullable)
259                literalType = literalType.Substring(0, literalType.Length - 1);
260            bool isArray = literalType.EndsWith("[]");
261            if (isArray)
262                literalType = literalType.Substring(0, literalType.Length - 2);
263            Type type = GetSimpleType(literalType);
264            if (type == null)
265                return type;
266            if (isArray)
267                type = type.MakeArrayType();
268            if (isNullable)
269                type = typeof(Nullable<>).MakeGenericType(type);
270            else if (canBeNull)
271            {
272                if (type.IsValueType)
273                    type = typeof(Nullable<>).MakeGenericType(type);
274            }
275            return type;
276        }
277
278        private Type GetSimpleType(string literalType)
279        {
280            switch (literalType)
281            {
282            case "string":
283                return typeof(string);
284            case "long":
285                return typeof(long);
286            case "short":
287                return typeof(short);
288            case "int":
289                return typeof(int);
290            case "char":
291                return typeof(char);
292            case "byte":
293                return typeof(byte);
294            case "float":
295                return typeof(float);
296            case "double":
297                return typeof(double);
298            case "decimal":
299                return typeof(decimal);
300            case "bool":
301                return typeof(bool);
302            case "DateTime":
303                return typeof(DateTime);
304            case "object":
305                return typeof(object);
306            default:
307                return Type.GetType(literalType);
308            }
309        }
310
311        protected string GetAttributeShortName<T>()
312            where T : Attribute
313        {
314            string literalAttribute = typeof(T).Name;
315            string end = "Attribute";
316            if (literalAttribute.EndsWith(end))
317                literalAttribute = literalAttribute.Substring(0, literalAttribute.Length - end.Length);
318            return literalAttribute;
319        }
320
321        protected AttributeDefinition NewAttributeDefinition<T>()
322            where T : Attribute
323        {
324            return new AttributeDefinition(GetAttributeShortName<T>());
325        }
326
327        protected IDisposable WriteAttributes(CodeWriter writer, params AttributeDefinition[] definitions)
328        {
329            var massDisposer = new MassDisposer();
330            foreach (var definition in definitions)
331                massDisposer.Disposables.Add(writer.WriteAttribute(definition));
332            return massDisposer;
333        }
334
335        protected IDisposable WriteAttributes(CodeWriter writer, params string[] definitions)
336        {
337            var attributeDefinitions = new List<AttributeDefinition>();
338            foreach (string definition in definitions)
339                attributeDefinitions.Add(new AttributeDefinition(definition));
340            return WriteAttributes(writer, attributeDefinitions.ToArray());
341        }
342
343        protected virtual SpecificationDefinition GetSpecificationDefinition(AccessModifier accessModifier)
344        {
345            switch (accessModifier)
346            {
347            case AccessModifier.Public:
348                return SpecificationDefinition.Public;
349            case AccessModifier.Internal:
350                return SpecificationDefinition.Internal;
351            case AccessModifier.Protected:
352                return SpecificationDefinition.Protected;
353            case AccessModifier.ProtectedInternal:
354                return SpecificationDefinition.Protected | SpecificationDefinition.Internal;
355            case AccessModifier.Private:
356                return SpecificationDefinition.Private;
357            default:
358                throw new ArgumentOutOfRangeException("accessModifier");
359            }
360        }
361
362        protected virtual SpecificationDefinition GetSpecificationDefinition(ClassModifier classModifier)
363        {
364            switch (classModifier)
365            {
366            case ClassModifier.Sealed:
367                return SpecificationDefinition.Sealed;
368            case ClassModifier.Abstract:
369                return SpecificationDefinition.Abstract;
370            default:
371                throw new ArgumentOutOfRangeException("classModifier");
372            }
373        }
374
375        protected virtual SpecificationDefinition GetSpecificationDefinition(MemberModifier memberModifier)
376        {
377            switch (memberModifier)
378            {
379            case MemberModifier.Virtual:
380                return SpecificationDefinition.Virtual;
381            case MemberModifier.Override:
382                return SpecificationDefinition.Override;
383            case MemberModifier.New:
384                return SpecificationDefinition.New;
385            case MemberModifier.NewVirtual:
386                return SpecificationDefinition.New | SpecificationDefinition.Virtual;
387            default:
388                throw new ArgumentOutOfRangeException("memberModifier");
389            }
390        }
391
392        /// <summary>
393        /// The "custom types" are types related to a class
394        /// Currently, we only support enums (non-standard)
395        /// </summary>
396        /// <param name="writer"></param>
397        /// <param name="table"></param>
398        /// <param name="schema"></param>
399        /// <param name="context"></param>
400        protected virtual void WriteCustomTypes(CodeWriter writer, Table table, Database schema, GenerationContext context)
401        {
402            // detect required custom types
403            foreach (var column in table.Type.Columns)
404            {
405                var extendedType = column.ExtendedType;
406                var enumType = extendedType as EnumType;
407                if (enumType != null)
408                {
409                    context.ExtendedTypes[column] = new GenerationContext.ExtendedTypeAndName
410                    {
411                        Type = column.ExtendedType,
412                        Table = table
413                    };
414                }
415            }
416
417            var customTypesNames = new List<string>();
418
419            // create names and avoid conflits
420            foreach (var extendedTypePair in context.ExtendedTypes)
421            {
422                if (extendedTypePair.Value.Table != table)
423                    continue;
424
425                if (string.IsNullOrEmpty(extendedTypePair.Value.Type.Name))
426                {
427                    string name = extendedTypePair.Key.Member + "Type";
428                    for (; ; )
429                    {
430                        if ((from t in context.ExtendedTypes.Values where t.Type.Name == name select t).FirstOrDefault() == null)
431                        {
432                            extendedTypePair.Value.Type.Name = name;
433                            break;
434                        }
435                        // at 3rd loop, it will look ugly, however we will never go there
436                        name = extendedTypePair.Value.Table.Type.Name + name;
437                    }
438                }
439                customTypesNames.Add(extendedTypePair.Value.Type.Name);
440            }
441
442            // write custom types
443            if (customTypesNames.Count > 0)
444            {
445                using (writer.WriteRegion(string.Format("Custom type definition for {0}", string.Join(", ", customTypesNames.ToArray()))))
446                {
447                    // write types
448                    foreach (var extendedTypePair in context.ExtendedTypes)
449                    {
450                        if (extendedTypePair.Value.Table != table)
451                            continue;
452
453                        var extendedType = extendedTypePair.Value.Type;
454                        var enumValue = extendedType as EnumType;
455
456                        if (enumValue != null)
457                        {
458                            writer.WriteEnum(GetSpecificationDefinition(extendedTypePair.Key.AccessModifier),
459                                             enumValue.Name, enumValue);
460                        }
461                    }
462                }
463            }
464        }
465    }
466}