예제 #1
0
        public static string GetTrackerClassName(TypeSyntax type)
        {
            // NOTE: it's naive approach because we don't know semantic type information here.
            var genericType = type as GenericNameSyntax;

            if (genericType == null)
            {
                if (type.ToString().StartsWith("Trackable"))
                {
                    return($"TrackablePocoTracker<I{type.ToString().Substring(9)}>");
                }
            }
            else if (CodeAnalaysisExtensions.CompareTypeName(genericType.Identifier.ToString(),
                                                             "TrackableData.TrackableDictionary"))
            {
                return($"TrackableDictionaryTracker{genericType.TypeArgumentList}");
            }
            else if (CodeAnalaysisExtensions.CompareTypeName(genericType.Identifier.ToString(),
                                                             "TrackableData.TrackableSet"))
            {
                return($"TrackableSetTracker{genericType.TypeArgumentList}");
            }
            else if (CodeAnalaysisExtensions.CompareTypeName(genericType.Identifier.ToString(),
                                                             "TrackableData.TrackableList"))
            {
                return($"TrackableListTracker{genericType.TypeArgumentList}");
            }

            throw new Exception("Cannot resolve tracker class of " + type);
        }
        private void GenerateTrackablePocoCode(InterfaceDeclarationSyntax idecl, CodeWriter.CodeWriter w,
                                               bool useProtoContract)
        {
            var typeName  = idecl.GetTypeName();
            var className = "Trackable" + typeName.Substring(1);

            var properties          = idecl.GetProperties();
            var trackableProperties = Utility.GetTrackableProperties(properties);

            if (useProtoContract)
            {
                w._($"[ProtoContract]");
            }

            using (w.B($"public partial class {className} : {typeName}"))
            {
                // Tracker

                w._($"[IgnoreDataMember]",
                    $"public IPocoTracker<{typeName}> Tracker {{ get; set; }}");
                w._();

                // Clone

                using (w.B($"public {className} Clone()"))
                {
                    w._($"var o = new {className}();");
                    foreach (var p in properties)
                    {
                        if (Utility.IsTrackableType(p.Type))
                        {
                            w._($"o._{p.Identifier} = _{p.Identifier}?.Clone();");
                        }
                        else
                        {
                            w._($"o._{p.Identifier} = _{p.Identifier};");
                        }
                    }
                    w._($"return o;");
                }

                // ITrackable.Changed

                w._("[IgnoreDataMember]",
                    "public bool Changed { get { return Tracker != null && Tracker.HasChange; } }");
                w._();

                // ITrackable.Tracker

                using (w.B($"ITracker ITrackable.Tracker"))
                {
                    using (w.b($"get"))
                    {
                        w._($"return Tracker;");
                    }
                    using (w.b($"set"))
                    {
                        w._($"var t = (IPocoTracker<{typeName}>)value;",
                            $"Tracker = t;");
                    }
                }

                // ITrackable<T>.Tracker

                using (w.B($"ITracker<{typeName}> ITrackable<{typeName}>.Tracker"))
                {
                    using (w.b($"get"))
                    {
                        w._($"return Tracker;");
                    }
                    using (w.b($"set"))
                    {
                        w._($"var t = (IPocoTracker<{typeName}>)value;",
                            $"Tracker = t;");
                    }
                }

                // ITrackable.Clone

                using (w.B($"ITrackable ITrackable.Clone()"))
                {
                    w._($"return Clone();");
                }

                // ITrackable.GetChildTrackable

                using (w.B($"public ITrackable GetChildTrackable(object name)"))
                {
                    using (w.B($"switch ((string)name)"))
                    {
                        foreach (var p in trackableProperties)
                        {
                            w._($"case \"{p.Identifier}\":",
                                $"    return {p.Identifier} as ITrackable;");
                        }
                        w._($"default:",
                            $"    return null;");
                    }
                }

                // ITrackable.GetChildTrackables

                using (w.B($"public IEnumerable<KeyValuePair<object, ITrackable>> " + $"" +
                           $"GetChildTrackables(bool changedOnly = false)"))
                {
                    if (trackableProperties.Any())
                    {
                        foreach (var p in trackableProperties)
                        {
                            var id = p.Identifier;
                            w._($"var trackable{id} = {id} as ITrackable;",
                                $"if (trackable{id} != null && (changedOnly == false || trackable{id}.Changed))",
                                $"    yield return new KeyValuePair<object, ITrackable>(`{id}`, trackable{id});");
                        }
                    }
                    else
                    {
                        w._($"yield break;");
                    }
                }

                // Property Table

                using (w.B($"public static class PropertyTable"))
                {
                    foreach (var p in properties)
                    {
                        w._($"public static readonly PropertyInfo {p.Identifier} = " +
                            $"typeof({typeName}).GetProperty(\"{p.Identifier}\");");
                    }
                }

                // Property Accessors

                foreach (var p in properties)
                {
                    var propertyType = p.Type.ToString();
                    var propertyName = p.Identifier.ToString();

                    w._();
                    w._($"private {propertyType} _{p.Identifier};");
                    w._();

                    var protoMemberAttr = p.AttributeLists.GetAttribute("ProtoMemberAttribute");
                    if (protoMemberAttr != null)
                    {
                        w._($"[ProtoMember{protoMemberAttr?.ArgumentList}] ");
                    }

                    using (w.B($"public {propertyType} {propertyName}"))
                    {
                        using (w.b($"get"))
                        {
                            w._($"return _{propertyName};");
                        }
                        using (w.b($"set"))
                        {
                            var compare = $"{propertyName} != value";

                            // Because DateTime ignores Kind for checking equal, compare it additionally.
                            if (CodeAnalaysisExtensions.CompareTypeName(propertyType, "System.DateTime"))
                            {
                                compare = $"({propertyName} != value || {propertyName}.Kind != value.Kind)";
                            }
                            if (CodeAnalaysisExtensions.CompareTypeName(propertyType, "System.DateTime?"))
                            {
                                compare = $"({propertyName} != value || ({propertyName}.HasValue && {propertyName}.Value.Kind != value.Value.Kind))";
                            }

                            w._($"if (Tracker != null && {compare})",
                                $"    Tracker.TrackSet(PropertyTable.{propertyName}, _{propertyName}, value);",
                                $"_{propertyName} = value;");
                        }
                    }
                }
            }
        }
예제 #3
0
        private static void WriteTrackableData(Options options, InterfaceDeclarationSyntax[] interfaceDeclarations)
        {
            Console.WriteLine("- Generate trackable data code");

            var settings = new CodeWriterSettings(CodeWriterSettings.CSharpDefault);

            settings.TranslationMapping["`"] = "\"";

            var w = new CodeWriter.CodeWriter(settings);
            var relatedSourceTrees = new HashSet <SyntaxNode>();

            w.HeadLines = new List <string>
            {
                "// ------------------------------------------------------------------------------",
                "// <auto-generated>",
                "//     This code was generated by TrackableData.CodeGenerator.Core.",
                "//",
                "//     Changes to this file may cause incorrect behavior and will be lost if",
                "//     the code is regenerated.",
                "// </auto-generated>",
                "// ------------------------------------------------------------------------------",
                "",
                "using System;",
                "using System.Collections.Generic;",
                "using System.Reflection;",
                "using System.Runtime.Serialization;",
                "using System.Linq;",
                "using System.Text;",
                "using TrackableData;",
                ""
            };

            // TrackablePoco

            var pocoCodeGen = new TrackablePocoCodeGenerator()
            {
                Options = options
            };

            foreach (var idecl in interfaceDeclarations)
            {
                var baseType = idecl.GetGenericBase("TrackableData.ITrackablePoco");
                if (baseType != null)
                {
                    var pocoType = baseType.TypeArgumentList.Arguments[0].ToString();
                    if (CodeAnalaysisExtensions.CompareTypeName(idecl.Identifier.ToString(), pocoType) == false)
                    {
                        throw new Exception($"Invalid base type of ITrackablePoco<{pocoType}>");
                    }

                    pocoCodeGen.GenerateCode(idecl, w);
                    relatedSourceTrees.Add(idecl.GetRootNode());
                }
            }

            // TrackableContainer

            var containerCodeGen = new TrackableContainerCodeGenerator()
            {
                Options = options
            };

            foreach (var idecl in interfaceDeclarations)
            {
                var baseType = idecl.GetGenericBase("TrackableData.ITrackableContainer");
                if (baseType != null)
                {
                    var containerType = baseType.TypeArgumentList.Arguments[0].ToString();
                    if (CodeAnalaysisExtensions.CompareTypeName(idecl.Identifier.ToString(), containerType) == false)
                    {
                        throw new Exception($"Invalid base type of ITrackableContainer<{containerType}>");
                    }

                    containerCodeGen.GenerateCode(idecl, w);
                    relatedSourceTrees.Add(idecl.GetRootNode());
                }
            }

            // Resolve referenced using

            var usingDirectives = new HashSet <string>(relatedSourceTrees.SelectMany(
                                                           st => st.DescendantNodes().OfType <UsingDirectiveSyntax>()).Select(x => x.Name.ToString()));

            foreach (var usingDirective in usingDirectives)
            {
                EnsureUsing(w, usingDirective);
            }

            // Save generated code
            var targetPath = GetTargetPath(options.Path, options.TargetFile);

            Console.WriteLine("- Save code: " + targetPath);

            if (w.WriteAllText(targetPath, true) == false)
            {
                Console.WriteLine("Nothing changed. Skip writing.");
            }
        }
예제 #4
0
        private static int Process(Options options)
        {
            try
            {
                Console.WriteLine("Start Process!");

                // Resolve options

                var basePath = Path.GetFullPath(options.Path ?? ".");
                var sources  =
                    options.Sources.Where(p => string.IsNullOrWhiteSpace(p) == false &&
                                          p.ToLower().IndexOf(".codegen.cs") == -1)
                    .Select(p => MakeFullPath(p, basePath))
                    .ToArray();
                var references =
                    options.References.Where(p => string.IsNullOrWhiteSpace(p) == false)
                    .Select(p => MakeFullPath(p, basePath))
                    .ToArray();
                var targetDefaultPath = @".\Properties\TrackableData.CodeGen.cs";
                var targetPath        = MakeFullPath(options.TargetFile ?? targetDefaultPath, basePath);

                // Parse sources and extract interfaces

                Console.WriteLine("- Parse sources");

                var syntaxTrees = sources.Select(
                    file => CSharpSyntaxTree.ParseText(File.ReadAllText(file), path: file)).ToArray();
                var interfaceDeclarations = syntaxTrees.SelectMany(
                    st => st.GetRoot().DescendantNodes().OfType <InterfaceDeclarationSyntax>()).ToArray();

                // Generate code

                Console.WriteLine("- Generate code");

                var settings = new CodeWriterSettings(CodeWriterSettings.CSharpDefault);
                settings.TranslationMapping["`"] = "\"";

                var w = new CodeWriter.CodeWriter(settings);
                var relatedSourceTrees = new HashSet <SyntaxNode>();

                w.HeadLines = new List <string>
                {
                    "// ------------------------------------------------------------------------------",
                    "// <auto-generated>",
                    "//     This code was generated by TrackableData.CodeGenerator.",
                    "//",
                    "//     Changes to this file may cause incorrect behavior and will be lost if",
                    "//     the code is regenerated.",
                    "// </auto-generated>",
                    "// ------------------------------------------------------------------------------",
                    "",
                    "using System;",
                    "using System.Collections.Generic;",
                    "using System.Reflection;",
                    "using System.Runtime.Serialization;",
                    "using System.Linq;",
                    "using System.Text;",
                    "using TrackableData;",
                    ""
                };

                // TrackablePoco

                var pocoCodeGen = new TrackablePocoCodeGenerator()
                {
                    Options = options
                };
                foreach (var idecl in interfaceDeclarations)
                {
                    var baseType = idecl.GetGenericBase("TrackableData.ITrackablePoco");
                    if (baseType != null)
                    {
                        var pocoType = baseType.TypeArgumentList.Arguments[0].ToString();
                        if (CodeAnalaysisExtensions.CompareTypeName(idecl.Identifier.ToString(), pocoType) == false)
                        {
                            throw new Exception($"Invalid base type of ITrackablePoco<{pocoType}>");
                        }

                        pocoCodeGen.GenerateCode(idecl, w);
                        relatedSourceTrees.Add(idecl.GetRootNode());
                    }
                }

                // TrackableContainer

                var containerCodeGen = new TrackableContainerCodeGenerator()
                {
                    Options = options
                };
                foreach (var idecl in interfaceDeclarations)
                {
                    var baseType = idecl.GetGenericBase("TrackableData.ITrackableContainer");
                    if (baseType != null)
                    {
                        var containerType = baseType.TypeArgumentList.Arguments[0].ToString();
                        if (CodeAnalaysisExtensions.CompareTypeName(idecl.Identifier.ToString(), containerType) == false)
                        {
                            throw new Exception($"Invalid base type of ITrackableContainer<{containerType}>");
                        }

                        containerCodeGen.GenerateCode(idecl, w);
                        relatedSourceTrees.Add(idecl.GetRootNode());
                    }
                }

                // Resolve referenced using

                var usingDirectives = new HashSet <string>(relatedSourceTrees.SelectMany(
                                                               st => st.DescendantNodes().OfType <UsingDirectiveSyntax>()).Select(x => x.Name.ToString()));
                foreach (var usingDirective in usingDirectives)
                {
                    EnsureUsing(w, usingDirective);
                }

                // Save generated code

                Console.WriteLine("- Save code");

                if (w.WriteAllText(targetPath, true) == false)
                {
                    Console.WriteLine("Nothing changed. Skip writing.");
                }

                return(0);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception in processing:\n" + e);
                return(1);
            }
        }