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;"); } } } } }
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."); } }
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); } }