public static void Generate() { checkCanGenerate(); Debug.Log("Generating..."); var codeGenerators = GetCodeGenerators(); var codeGeneratorNames = codeGenerators.Select(cg => cg.Name).ToArray(); var config = new CodeGeneratorConfig(EntitasPreferences.LoadConfig(), codeGeneratorNames); var enabledCodeGeneratorNames = config.enabledCodeGenerators; var enabledCodeGenerators = codeGenerators .Where(type => enabledCodeGeneratorNames.Contains(type.Name)) .Select(type => (ICodeGenerator)Activator.CreateInstance(type)) .ToArray(); var blueprintNames = BinaryBlueprintInspector.FindAllBlueprints() .Select(b => b.Deserialize().name) .ToArray(); var assembly = Assembly.GetAssembly(typeof(Entity)); var generatedFiles = TypeReflectionCodeGenerator.Generate(assembly, config.contexts, blueprintNames, config.generatedFolderPath, enabledCodeGenerators); foreach(var file in generatedFiles) { Debug.Log(file.generatorName + ": " + file.fileName); } var totalGeneratedFiles = generatedFiles.Select(file => file.fileName).Distinct().Count(); Debug.Log("Generated " + totalGeneratedFiles + " files."); AssetDatabase.Refresh(); }
static void printPluginStatus(Type[] types, CodeGeneratorConfig config) { var unavailableDataProviders = CodeGeneratorUtil.GetUnavailable <ICodeGeneratorDataProvider>(types, config.dataProviders); var unavailableCodeGenerators = CodeGeneratorUtil.GetUnavailable <ICodeGenerator>(types, config.codeGenerators); var unavailablePostProcessors = CodeGeneratorUtil.GetUnavailable <ICodeGenFilePostProcessor>(types, config.postProcessors); var availableDataProviders = CodeGeneratorUtil.GetAvailable <ICodeGeneratorDataProvider>(types, config.dataProviders); var availableCodeGenerators = CodeGeneratorUtil.GetAvailable <ICodeGenerator>(types, config.codeGenerators); var availablePostProcessors = CodeGeneratorUtil.GetAvailable <ICodeGenFilePostProcessor>(types, config.postProcessors); printUnavailable(unavailableDataProviders); printUnavailable(unavailableCodeGenerators); printUnavailable(unavailablePostProcessors); printAvailable(availableDataProviders); printAvailable(availableCodeGenerators); printAvailable(availablePostProcessors); }
public void IgnoreGetOnlyPropertiesInUpdateProxy() { // assemble var target = MakeTarget(); var maps = MakeMaps(typeof(ClassWithGetOnlyProperty)); var config = new CodeGeneratorConfig(); // act var results = target.GenerateProxies(config, maps); // assert Assert.NotNull(results); var updateProxy = results.ProxyTypes.Single(ctd => ctd.Name == typeof(ClassWithGetOnlyProperty).Name + config.UpdateClassSuffix); var members = new CodeTypeMember[updateProxy.Members.Count]; updateProxy.Members.CopyTo(members, 0); Assert.False(members.Any(m => m.Name == "Id")); }
public void IgnoreNonVirtualEntityPropertiesInFkProxy() { // assemble var target = MakeTarget(); var maps = MakeMaps(typeof(ClassWithGetOnlyProperty), typeof(ClassWithNonVirtualProperty)); var config = new CodeGeneratorConfig(); // act var results = target.GenerateProxies(config, maps); // assert Assert.NotNull(results); var fkProxy = results.ProxyTypes.Single(ctd => ctd.Name == typeof(ClassWithNonVirtualProperty).Name + config.ForeignKeyAccessClassSuffix); var members = new CodeTypeMember[fkProxy.Members.Count]; fkProxy.Members.CopyTo(members, 0); Assert.False(members.Any(m => m.Name == "NonVirtualProperty")); }
public void Initialize(EntitasPreferencesConfig config) { _availableGeneratorNames = UnityCodeGenerator.GetCodeGenerators() .Select(cg => cg.Name) .OrderBy(generatorName => generatorName) .ToArray(); _codeGeneratorConfig = new CodeGeneratorConfig(config, _availableGeneratorNames); _contexts = new List<string>(_codeGeneratorConfig.contexts); _contextList = new UnityEditorInternal.ReorderableList(_contexts, typeof(string), true, true, true, true); _contextList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Contexts"); _contextList.drawElementCallback = (rect, index, isActive, isFocused) => { rect.width -= 20; _contexts[index] = EditorGUI.TextField(rect, _contexts[index]); }; _contextList.onAddCallback = list => list.list.Add("New Context"); _contextList.onCanRemoveCallback = list => true; _contextList.onChangedCallback = list => GUI.changed = true; }
public ProxyGeneratorResult GenerateProxies( CodeGeneratorConfig codeGeneratorConfig, IDictionary <Type, IMap> mapDictionary) { var maps = mapDictionary.Values; var parallelMaps = maps.AsParallel(); // create code doms for the proxy classes var trackingClasses = parallelMaps.Select(m => this.CreateTrackingClass(m, codeGeneratorConfig)); var foreignKeyClasses = parallelMaps.Select(m => this.CreateFkClass(m, mapDictionary, codeGeneratorConfig)); var updateClasses = parallelMaps.Select( m => this.CreateUpdateClass(m, mapDictionary, codeGeneratorConfig)); // extract metadata from maps var typeHierarchy = maps.Select(m => m.Type) .Union(maps.SelectMany(m => m.Type.GetAncestorTypes())) .ToArray(); var namespaces = typeHierarchy.Select(m => m.Namespace) .Distinct() .Select(ns => new CodeNamespaceImport(ns)); var references = typeHierarchy.Select(t => t.Assembly) .Distinct() .Select(a => a.Location); return(new ProxyGeneratorResult { ProxyTypes = trackingClasses.Concat(foreignKeyClasses) .Concat(updateClasses) .ToArray(), NamespaceImports = namespaces.ToArray(), ReferencedAssemblyLocations = references.ToArray() }); }
protected override void GenerateCodes() { Log((object)"Generating..."); var recordpath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, JennyPropertyPath); LogError("recordpath " + recordpath); var text = File.ReadAllText(recordpath); var preference = new Preferences(recordpath, recordpath); ICodeGenerationPlugin[] instances = CodeGeneratorUtil.LoadFromPlugins(preference); CodeGeneratorConfig andConfigure = preference.CreateAndConfigure <CodeGeneratorConfig>(); AssemblyResolver assemblyResolver = new AssemblyResolver(false, andConfigure.searchPaths); foreach (string plugin in andConfigure.plugins) { Log("load plugin " + plugin); assemblyResolver.Load(plugin); } DesperateDevs.CodeGeneration.CodeGenerator.CodeGenerator codeGenerator = CodeGeneratorFromPreferences(preference); codeGenerator.OnProgress += (GeneratorProgress)((title, info, progress) => { Log("progress " + (progress)); }); CodeGenFile[] codeGenFileArray1 = new CodeGenFile[0]; CodeGenFile[] codeGenFileArray2; try { codeGenFileArray2 = codeGenerator.Generate(); } catch (Exception ex) { codeGenFileArray1 = new CodeGenFile[0]; codeGenFileArray2 = new CodeGenFile[0]; LogError("Error" + ex.Message + ex.StackTrace); } UpdateOutputProjectFile(); LogGenInfo(codeGenFileArray2, codeGenFileArray1); }
void autoImport() { var config = new CodeGeneratorConfig(); config.Configure(_preferences); var plugins = config.searchPaths .SelectMany(path => Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories)) .Where(path => path.ToLower().EndsWith(".plugins.dll")); config.searchPaths = config.searchPaths .Concat(plugins.Select(path => Path.GetDirectoryName(path))) .Distinct() .ToArray(); config.plugins = plugins .Select(path => Path.GetFileNameWithoutExtension(path)) .Distinct() .ToArray(); _preferences.Save(); }
public override void Run(string[] args) { string text = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString() + "Entitas.properties"; if (ArgsExtension.isForce(args) || !File.Exists(text)) { CodeGeneratorConfig codeGeneratorConfig = new CodeGeneratorConfig(); Properties properties = new Properties(codeGeneratorConfig.defaultProperties); //codeGeneratorConfig.Configure(properties); string text2 = codeGeneratorConfig.ToString(); File.WriteAllText(text, text2); fabl.Info("Created " + text); fabl.Debug(text2); new EditConfig().Run(args); } else { fabl.Warn(text + " already exists!"); fabl.Info("Use entitas new -f to overwrite the exiting file."); fabl.Info("Use entitas edit to open the exiting file."); } }
public ProxyGeneratorResult GenerateProxies( CodeGeneratorConfig codeGeneratorConfig, IDictionary<Type, IMap> mapDictionary) { var maps = mapDictionary.Values; var parallelMaps = maps.AsParallel(); // create code doms for the proxy classes var trackingClasses = parallelMaps.Select(m => this.CreateTrackingClass(m, codeGeneratorConfig)); var foreignKeyClasses = parallelMaps.Select(m => this.CreateFkClass(m, mapDictionary, codeGeneratorConfig)); var updateClasses = parallelMaps.Select( m => this.CreateUpdateClass(m, mapDictionary, codeGeneratorConfig)); // extract metadata from maps var typeHierarchy = maps.Select(m => m.Type) .Union(maps.SelectMany(m => m.Type.GetAncestorTypes())) .ToArray(); var namespaces = typeHierarchy.Select(m => m.Namespace) .Distinct() .Select(ns => new CodeNamespaceImport(ns)); var references = typeHierarchy.Select(t => t.Assembly) .Distinct() .Select(a => a.Location); return new ProxyGeneratorResult { ProxyTypes = trackingClasses.Concat(foreignKeyClasses) .Concat(updateClasses) .ToArray(), NamespaceImports = namespaces.ToArray(), ReferencedAssemblyLocations = references.ToArray() }; }
static Dictionary <string, int> getContexts(Type[] components) { var defaultContext = new CodeGeneratorConfig(EntitasPreferences.LoadConfig()).contexts[0]; return(components.Aggregate(new Dictionary <string, int>(), (contexts, type) => { var contextNames = ContextsComponentDataProvider.GetContextNames(type); if (contextNames.Length == 0) { contextNames = new [] { defaultContext }; } foreach (var contextName in contextNames) { if (!contexts.ContainsKey(contextName)) { contexts.Add(contextName, 0); } contexts[contextName] += 1; } return contexts; })); }
public override void Run(string[] args) { if (assertPreferences(args)) { var preferences = loadPreferences(args); var config = new CodeGeneratorConfig(); config.Configure(preferences); var cliConfig = new CLIConfig(); cliConfig.Configure(preferences); fabl.Debug(preferences.ToString()); Type[] types = null; Dictionary <string, string> configurables = null; try { types = CodeGeneratorUtil.LoadTypesFromPlugins(preferences); configurables = CodeGeneratorUtil.GetConfigurables( CodeGeneratorUtil.GetUsed <ICodeGeneratorDataProvider>(types, config.dataProviders), CodeGeneratorUtil.GetUsed <ICodeGenerator>(types, config.codeGenerators), CodeGeneratorUtil.GetUsed <ICodeGenFilePostProcessor>(types, config.postProcessors) ); } catch (Exception ex) { printKeyStatus(config.defaultProperties.Keys.ToArray(), cliConfig, preferences); throw ex; } var requiredKeys = config.defaultProperties .Merge(cliConfig.defaultProperties) .Merge(configurables).Keys.ToArray(); printKeyStatus(requiredKeys, cliConfig, preferences); printPluginStatus(types, config); } }
private CodeTypeDeclaration CreateTrackingClass( IMap map, CodeGeneratorConfig codeGeneratorConfig) { var trackingClass = new CodeTypeDeclaration(map.Type.Name + codeGeneratorConfig.TrackedClassSuffix); trackingClass.IsClass = true; trackingClass.TypeAttributes = TypeAttributes.Public; trackingClass.BaseTypes.Add( map.Type.Name + codeGeneratorConfig.ForeignKeyAccessClassSuffix); trackingClass.BaseTypes.Add(typeof(ITrackedEntity)); // add in change tracking properties this.GenerateGetSetProperty(trackingClass, "IsTracking", typeof(bool), FinalPublic); this.GenerateGetSetProperty( trackingClass, "DirtyProperties", typeof(ISet<>).MakeGenericType(typeof(string)), FinalPublic); this.GenerateGetSetProperty( trackingClass, "OldValues", typeof(IDictionary<,>).MakeGenericType(typeof(string), typeof(object)), FinalPublic); this.GenerateGetSetProperty( trackingClass, "NewValues", typeof(IDictionary<,>).MakeGenericType(typeof(string), typeof(object)), FinalPublic); this.GenerateGetSetProperty( trackingClass, "AddedEntities", typeof(IDictionary<,>).MakeGenericType( typeof(string), typeof(IList<>).MakeGenericType(typeof(object))), FinalPublic); this.GenerateGetSetProperty( trackingClass, "DeletedEntities", typeof(IDictionary<,>).MakeGenericType( typeof(string), typeof(IList<>).MakeGenericType(typeof(object))), FinalPublic); // add in a constructor to initialise collections var constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("DirtyProperties"), new CodeObjectCreateExpression( typeof(HashSet<>).MakeGenericType(typeof(string))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("OldValues"), new CodeObjectCreateExpression( typeof(Dictionary<,>).MakeGenericType(typeof(string), typeof(object))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("NewValues"), new CodeObjectCreateExpression( typeof(Dictionary<,>).MakeGenericType(typeof(string), typeof(object))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("AddedEntities"), new CodeObjectCreateExpression( typeof(Dictionary<,>).MakeGenericType( typeof(string), typeof(IList<>).MakeGenericType(typeof(object)))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("DeletedEntities"), new CodeObjectCreateExpression( typeof(Dictionary<,>).MakeGenericType( typeof(string), typeof(IList<>).MakeGenericType(typeof(object)))))); // these constructor statements override the collection properties to use observable collections foreach (var collectionColumn in map.Columns.Where(c => c.Value.Type.IsCollection())) { if ( !collectionColumn.Value.Map.Type.GetProperty(collectionColumn.Value.Name) .GetGetMethod() .IsVirtual) { // TODO: send a warning back to the programmer, did they mean to do this? continue; } constructor.Statements.Add( new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)), new CodeStatement[] { new CodeAssignStatement( new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key), new CodeObjectCreateExpression( "Dashing.CodeGeneration.TrackingCollection<" + trackingClass.Name + "," + collectionColumn.Value.Type.GenericTypeArguments.First() + ">", new CodeThisReferenceExpression(), new CodePrimitiveExpression(collectionColumn.Key))) }, new CodeStatement[] { new CodeAssignStatement( new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key), new CodeObjectCreateExpression( "Dashing.CodeGeneration.TrackingCollection<" + trackingClass.Name + "," + collectionColumn.Value.Type.GenericTypeArguments.First() + ">", new CodeThisReferenceExpression(), new CodePrimitiveExpression(collectionColumn.Key), new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key))) })); } // override value type properties to perform dirty checking foreach ( var valueTypeColumn in map.Columns.Where(c => !c.Value.Type.IsCollection() && !c.Value.IsIgnored)) { if ( !valueTypeColumn.Value.Map.Type.GetProperty(valueTypeColumn.Value.Name) .GetGetMethod() .IsVirtual) { // TODO: send a warning back to the programmer, did they mean to do this? continue; } var prop = this.GenerateGetSetProperty( trackingClass, valueTypeColumn.Key, valueTypeColumn.Value.Type, MemberAttributes.Public | MemberAttributes.Override, true); // override the setter // if isTracking && !this.DirtyProperties.ContainsKey(prop) add to dirty props and add oldvalue bool propertyCanBeNull = valueTypeColumn.Value.Type.IsNullable() || !valueTypeColumn.Value.Type.IsValueType; var changeCheck = new CodeBinaryOperatorExpression(); if (!propertyCanBeNull) { // can't be null so just check values changeCheck.Left = new CodeMethodInvokeExpression( CodeHelpers.BaseProperty(valueTypeColumn.Key), "Equals", new CodePropertySetValueReferenceExpression()); changeCheck.Operator = CodeBinaryOperatorType.IdentityEquality; changeCheck.Right = new CodePrimitiveExpression(false); } else { // can be null, need to be careful of null reference exceptions changeCheck.Left = new CodeBinaryOperatorExpression( CodeHelpers.BasePropertyIsNull(valueTypeColumn.Key), CodeBinaryOperatorType.BooleanAnd, new CodeBinaryOperatorExpression( new CodePropertySetValueReferenceExpression(), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null))); changeCheck.Operator = CodeBinaryOperatorType.BooleanOr; changeCheck.Right = new CodeBinaryOperatorExpression( CodeHelpers.BasePropertyIsNotNull(valueTypeColumn.Key), CodeBinaryOperatorType.BooleanAnd, new CodeBinaryOperatorExpression( new CodeMethodInvokeExpression( CodeHelpers.BaseProperty(valueTypeColumn.Key), "Equals", new CodePropertySetValueReferenceExpression()), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(false))); } prop.SetStatements.Insert( 0, new CodeConditionStatement( CodeHelpers.ThisPropertyIsTrue("IsTracking"), new CodeStatement[] { new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeBinaryOperatorExpression( new CodeMethodInvokeExpression( CodeHelpers.ThisProperty("DirtyProperties"), "Contains", new CodePrimitiveExpression(prop.Name)), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(false)), CodeBinaryOperatorType.BooleanAnd, changeCheck), new CodeStatement[] { new CodeExpressionStatement( new CodeMethodInvokeExpression( CodeHelpers.ThisProperty("DirtyProperties"), "Add", new CodePrimitiveExpression(prop.Name))), new CodeAssignStatement( new CodeIndexerExpression( CodeHelpers.ThisProperty("OldValues"), new CodePrimitiveExpression(prop.Name)), CodeHelpers.BaseField(prop.Name)) }), new CodeAssignStatement( new CodeIndexerExpression( CodeHelpers.ThisProperty("NewValues"), new CodePrimitiveExpression(prop.Name)), new CodePropertySetValueReferenceExpression()) })); } trackingClass.Members.Add(constructor); return trackingClass; }
private CodeTypeDeclaration CreateFkClass( IMap map, IDictionary<Type, IMap> maps, CodeGeneratorConfig codeGeneratorConfig) { // generate the foreign key access class based on the original class var foreignKeyClass = new CodeTypeDeclaration( map.Type.Name + codeGeneratorConfig.ForeignKeyAccessClassSuffix); foreignKeyClass.IsClass = true; foreignKeyClass.TypeAttributes = TypeAttributes.Public; foreignKeyClass.BaseTypes.Add(map.Type); foreach ( var column in map.Columns.Where(c => c.Value.Relationship == RelationshipType.ManyToOne || c.Value.Relationship == RelationshipType.OneToOne)) { if (!column.Value.Map.Type.GetProperty(column.Value.Name).GetGetMethod().IsVirtual || !column.Value.Map.Type.GetProperty(column.Value.Name).CanWrite) { // TODO: send a warning back to the programmer, did they mean to do this? continue; } // create a backing property for storing the FK var backingType = column.Value.DbType.GetCLRType(); if (backingType.IsValueType) { backingType = typeof(Nullable<>).MakeGenericType(backingType); } var foreignKeyBackingProperty = this.GenerateGetSetProperty( foreignKeyClass, column.Value.DbName, backingType, FinalPublic); // create a backing field for storing the related entity var backingField = new CodeMemberField( column.Value.Type, column.Value.Name + codeGeneratorConfig.ForeignKeyAccessEntityFieldSuffix); foreignKeyClass.Members.Add(backingField); // override the property getter and setter to use the backingfield var property = new CodeMemberProperty(); property.Name = column.Value.Name; property.Type = new CodeTypeReference(column.Value.Type); property.Attributes = MemberAttributes.Public | MemberAttributes.Override; property.GetStatements.Add( new CodeConditionStatement( //// if backingField != null or Fk backing field is null return new CodeBinaryOperatorExpression( CodeHelpers.ThisFieldIsNotNull(backingField.Name), CodeBinaryOperatorType.BooleanOr, CodeHelpers.ThisPropertyIsNull(foreignKeyBackingProperty.Name)), new CodeStatement[] { // true new CodeMethodReturnStatement( CodeHelpers.ThisField(backingField.Name)) }, new CodeStatement[] { // false, return new object with foreign key set new CodeVariableDeclarationStatement( column.Value.Type, "val", new CodeObjectCreateExpression( column.Value.Type)), new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeVariableReferenceExpression("val"), maps[column.Value.Type].PrimaryKey.Name), new CodePropertyReferenceExpression( CodeHelpers.ThisProperty( foreignKeyBackingProperty.Name), "Value")), new CodeAssignStatement( CodeHelpers.ThisField(backingField.Name), new CodeVariableReferenceExpression("val")), new CodeMethodReturnStatement( new CodeVariableReferenceExpression("val")) })); property.SetStatements.Add( new CodeAssignStatement( CodeHelpers.ThisField(backingField.Name), new CodePropertySetValueReferenceExpression())); foreignKeyClass.Members.Add(property); } return foreignKeyClass; }
void when_creating_config() { it["creates config from EntitasPreferencesConfig"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString), new string[0]); config.generatedFolderPath.should_be("path/to/folder/"); config.pools.should_be(new [] { "Core", "Meta", "UI" }); config.enabledCodeGenerators.should_be(new [] { "Generator1", "Generator2", "Generator3" }); }; it["gets default values when keys dont exist"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty), new [] { "Gen1, Gen2" }); config.generatedFolderPath.should_be("Assets/Generated/"); config.pools.should_be_empty(); config.enabledCodeGenerators.should_be(new [] { "Gen1", "Gen2" }); }; it["sets values"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString), new string[0]); config.generatedFolderPath = "new/path/"; config.pools = new [] { "Other1", "Other2" }; config.enabledCodeGenerators = new [] { "Generator4", "Generator5" }; config.generatedFolderPath.should_be("new/path/"); config.pools.should_be(new [] { "Other1", "Other2" }); config.enabledCodeGenerators.should_be(new [] { "Generator4", "Generator5" }); }; it["gets string"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString), new string[0]); config.generatedFolderPath = "new/path/"; config.pools = new [] { "Other1", "Other2" }; config.enabledCodeGenerators = new [] { "Generator4", "Generator5" }; config.ToString().should_be( "Entitas.CodeGenerator.GeneratedFolderPath = new/path/\n" + "Entitas.CodeGenerator.Pools = Other1,Other2\n" + "Entitas.CodeGenerator.EnabledCodeGenerators = Generator4,Generator5\n"); }; it["gets string from empty config"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty), new string[0]); config.ToString().should_be( "Entitas.CodeGenerator.GeneratedFolderPath = Assets/Generated/\n" + "Entitas.CodeGenerator.Pools = \n" + "Entitas.CodeGenerator.EnabledCodeGenerators = \n"); }; it["removes empty pools"] = () => { const string configString = "Entitas.CodeGenerator.Pools = ,,Core,,UI,,"; var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString), new string[0]); config.pools.should_be(new [] { "Core", "UI" }); }; it["removes empty enabled code generators"] = () => { const string configString = "Entitas.CodeGenerator.EnabledCodeGenerators = ,,Gen1,,Gen2,,"; var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString), new string[0]); config.enabledCodeGenerators.should_be(new [] { "Gen1", "Gen2" }); }; it["removes trailing comma in pools string"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty), new string[0]); config.pools = new [] { "Meta", string.Empty }; config.ToString().should_be( "Entitas.CodeGenerator.GeneratedFolderPath = Assets/Generated/\n" + "Entitas.CodeGenerator.Pools = Meta\n" + "Entitas.CodeGenerator.EnabledCodeGenerators = \n" ); }; it["removes trailing comma in enabled code generators string"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty), new string[0]); config.enabledCodeGenerators = new [] { "Gen1", string.Empty }; config.ToString().should_be( "Entitas.CodeGenerator.GeneratedFolderPath = Assets/Generated/\n" + "Entitas.CodeGenerator.Pools = \n" + "Entitas.CodeGenerator.EnabledCodeGenerators = Gen1\n" ); }; }
void when_creating_config() { CodeGeneratorConfig config = null; before = () => { config = new CodeGeneratorConfig(); }; context["when input string"] = () => { before = () => { config.Configure(new Preferences(new Properties(configString))); }; it["creates config"] = () => { config.searchPaths.should_be(new [] { "sp1", "sp2" }); config.plugins.should_be(new [] { "p1", "p2" }); config.dataProviders.should_be(new [] { "dp1", "dp2", "dp3" }); config.codeGenerators.should_be(new [] { "cg1", "cg2", "cg3" }); config.postProcessors.should_be(new [] { "pp1", "pp2", "pp3" }); }; context["when setting values"] = () => { before = () => { config.searchPaths = new [] { "newS1", "newS2" }; config.plugins = new [] { "newP1", "newP2" }; config.dataProviders = new [] { "newDp1", "newDp2" }; config.codeGenerators = new [] { "newCg1", "newCg2" }; config.postProcessors = new [] { "newPp1", "newPp2" }; }; it["has updated values"] = () => { config.searchPaths.should_be(new [] { "newS1", "newS2" }); config.plugins.should_be(new [] { "newP1", "newP2" }); config.dataProviders.should_be(new [] { "newDp1", "newDp2" }); config.codeGenerators.should_be(new [] { "newCg1", "newCg2" }); config.postProcessors.should_be(new [] { "newPp1", "newPp2" }); }; it["gets string"] = () => { config.ToString().should_be( "CodeGenerator.SearchPaths = newS1, \\\n" + " newS2\n\n" + "CodeGenerator.Plugins = newP1, \\\n" + " newP2\n\n" + "CodeGenerator.DataProviders = newDp1, \\\n" + " newDp2\n\n" + "CodeGenerator.CodeGenerators = newCg1, \\\n" + " newCg2\n\n" + "CodeGenerator.PostProcessors = newPp1, \\\n" + " newPp2\n\n" ); }; }; }; }
private CodeTypeDeclaration CreateUpdateClass( IMap map, IDictionary<Type, IMap> maps, CodeGeneratorConfig codeGeneratorConfig) { // public class Post_Update var updateClass = new CodeTypeDeclaration(map.Type.Name + codeGeneratorConfig.UpdateClassSuffix) { IsClass = true, TypeAttributes = TypeAttributes.Public }; updateClass.BaseTypes.Add(map.Type); updateClass.BaseTypes.Add(typeof(IUpdateClass)); // public IList<string> UpdatedProperties { get; set; } this.GenerateGetSetProperty( updateClass, "UpdatedProperties", typeof(IList<>).MakeGenericType(typeof(string)), FinalPublic); // public bool IsConstructed { get; set; } this.GenerateGetSetProperty(updateClass, "IsConstructed", typeof(bool), FinalPublic); // public Post_Update() { // this.IsConstructed = true; // this.backingUpdatedProperties = new List<string>(); // } var constructor = new CodeConstructor { Attributes = MemberAttributes.Public }; constructor.Statements.Add(new CodeAssignStatement(CodeHelpers.ThisProperty("IsConstructed"), new CodePrimitiveExpression(true))); constructor.Statements.Add(new CodeAssignStatement(CodeHelpers.ThisProperty("UpdatedProperties"), new CodeObjectCreateExpression(typeof(List<>).MakeGenericType(typeof(string))))); updateClass.Members.Add(constructor); // public override IList<Comment> Comments { // get { // return this.backingComments; // } // set { // if (this.IsConstructed) { // this.UpdatedProperties.Add("Comments"); // } // this.backingComments = value; // } // } foreach (var column in map.Columns.Where(c => !c.Value.IsIgnored)) { var prop = this.GenerateGetSetProperty( updateClass, column.Key, column.Value.Type, MemberAttributes.Public | MemberAttributes.Override, true); var ifIsConstructed = CodeHelpers.ThisPropertyIsTrue("IsConstructed"); var addColumnToUpdatedProperties = new CodeExpressionStatement( new CodeMethodInvokeExpression( CodeHelpers.ThisProperty("UpdatedProperties"), "Add", new CodePrimitiveExpression(column.Key))); prop.SetStatements.Insert(0, new CodeConditionStatement(ifIsConstructed, addColumnToUpdatedProperties)); } return updateClass; }
static bool fixPlugins(HashSet <string> askedRemoveKeys, HashSet <string> askedAddKeys, Type[] types, CodeGeneratorConfig config, Preferences preferences) { var changed = false; var unavailableDataProviders = CodeGeneratorUtil.GetUnavailable <ICodeGeneratorDataProvider>(types, config.dataProviders); var unavailableCodeGenerators = CodeGeneratorUtil.GetUnavailable <ICodeGenerator>(types, config.codeGenerators); var unavailablePostProcessors = CodeGeneratorUtil.GetUnavailable <ICodeGenFilePostProcessor>(types, config.postProcessors); var availableDataProviders = CodeGeneratorUtil.GetAvailable <ICodeGeneratorDataProvider>(types, config.dataProviders); var availableCodeGenerators = CodeGeneratorUtil.GetAvailable <ICodeGenerator>(types, config.codeGenerators); var availablePostProcessors = CodeGeneratorUtil.GetAvailable <ICodeGenFilePostProcessor>(types, config.postProcessors); foreach (var key in unavailableDataProviders) { if (!askedRemoveKeys.Contains(key)) { Helper.RemoveValue("Remove unavailable data provider", key, config.dataProviders, values => config.dataProviders = values, preferences); askedRemoveKeys.Add(key); changed = true; } } foreach (var key in unavailableCodeGenerators) { if (!askedRemoveKeys.Contains(key)) { Helper.RemoveValue("Remove unavailable code generator", key, config.codeGenerators, values => config.codeGenerators = values, preferences); askedRemoveKeys.Add(key); changed = true; } } foreach (var key in unavailablePostProcessors) { if (!askedRemoveKeys.Contains(key)) { Helper.RemoveValue("Remove unavailable post processor", key, config.postProcessors, values => config.postProcessors = values, preferences); askedRemoveKeys.Add(key); changed = true; } } foreach (var key in availableDataProviders) { if (!askedAddKeys.Contains(key)) { Helper.AddValue("Add available data provider", key, config.dataProviders, values => config.dataProviders = values, preferences); askedAddKeys.Add(key); changed = true; } } foreach (var key in availableCodeGenerators) { if (!askedAddKeys.Contains(key)) { Helper.AddValue("Add available code generator", key, config.codeGenerators, values => config.codeGenerators = values, preferences); askedAddKeys.Add(key); changed = true; } } foreach (var key in availablePostProcessors) { if (!askedAddKeys.Contains(key)) { Helper.AddValue("Add available post processor", key, config.postProcessors, values => config.postProcessors = values, preferences); askedAddKeys.Add(key); changed = true; } } return(changed); }
private CodeTypeDeclaration CreateTrackingClass( IMap map, CodeGeneratorConfig codeGeneratorConfig) { var trackingClass = new CodeTypeDeclaration(map.Type.Name + codeGeneratorConfig.TrackedClassSuffix); trackingClass.IsClass = true; trackingClass.TypeAttributes = TypeAttributes.Public; trackingClass.BaseTypes.Add( map.Type.Name + codeGeneratorConfig.ForeignKeyAccessClassSuffix); trackingClass.BaseTypes.Add(typeof(ITrackedEntity)); // add in change tracking properties this.GenerateGetSetProperty(trackingClass, "IsTracking", typeof(bool), FinalPublic); this.GenerateGetSetProperty( trackingClass, "DirtyProperties", typeof(ISet <>).MakeGenericType(typeof(string)), FinalPublic); this.GenerateGetSetProperty( trackingClass, "OldValues", typeof(IDictionary <,>).MakeGenericType(typeof(string), typeof(object)), FinalPublic); this.GenerateGetSetProperty( trackingClass, "NewValues", typeof(IDictionary <,>).MakeGenericType(typeof(string), typeof(object)), FinalPublic); this.GenerateGetSetProperty( trackingClass, "AddedEntities", typeof(IDictionary <,>).MakeGenericType( typeof(string), typeof(IList <>).MakeGenericType(typeof(object))), FinalPublic); this.GenerateGetSetProperty( trackingClass, "DeletedEntities", typeof(IDictionary <,>).MakeGenericType( typeof(string), typeof(IList <>).MakeGenericType(typeof(object))), FinalPublic); // add in a constructor to initialise collections var constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("DirtyProperties"), new CodeObjectCreateExpression( typeof(HashSet <>).MakeGenericType(typeof(string))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("OldValues"), new CodeObjectCreateExpression( typeof(Dictionary <,>).MakeGenericType(typeof(string), typeof(object))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("NewValues"), new CodeObjectCreateExpression( typeof(Dictionary <,>).MakeGenericType(typeof(string), typeof(object))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("AddedEntities"), new CodeObjectCreateExpression( typeof(Dictionary <,>).MakeGenericType( typeof(string), typeof(IList <>).MakeGenericType(typeof(object)))))); constructor.Statements.Add( new CodeAssignStatement( CodeHelpers.ThisField("DeletedEntities"), new CodeObjectCreateExpression( typeof(Dictionary <,>).MakeGenericType( typeof(string), typeof(IList <>).MakeGenericType(typeof(object)))))); // these constructor statements override the collection properties to use observable collections foreach (var collectionColumn in map.Columns.Where(c => c.Value.Type.IsCollection())) { if ( !collectionColumn.Value.Map.Type.GetProperty(collectionColumn.Value.Name) .GetGetMethod() .IsVirtual) { // TODO: send a warning back to the programmer, did they mean to do this? continue; } constructor.Statements.Add( new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)), new CodeStatement[] { new CodeAssignStatement( new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key), new CodeObjectCreateExpression( "Dashing.CodeGeneration.TrackingCollection<" + trackingClass.Name + "," + collectionColumn.Value.Type.GenericTypeArguments.First() + ">", new CodeThisReferenceExpression(), new CodePrimitiveExpression(collectionColumn.Key))) }, new CodeStatement[] { new CodeAssignStatement( new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key), new CodeObjectCreateExpression( "Dashing.CodeGeneration.TrackingCollection<" + trackingClass.Name + "," + collectionColumn.Value.Type.GenericTypeArguments.First() + ">", new CodeThisReferenceExpression(), new CodePrimitiveExpression(collectionColumn.Key), new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), collectionColumn.Key))) })); } // override value type properties to perform dirty checking foreach ( var valueTypeColumn in map.Columns.Where(c => !c.Value.Type.IsCollection() && !c.Value.IsIgnored)) { if ( !valueTypeColumn.Value.Map.Type.GetProperty(valueTypeColumn.Value.Name) .GetGetMethod() .IsVirtual) { // TODO: send a warning back to the programmer, did they mean to do this? continue; } var prop = this.GenerateGetSetProperty( trackingClass, valueTypeColumn.Key, valueTypeColumn.Value.Type, MemberAttributes.Public | MemberAttributes.Override, true); // override the setter // if isTracking && !this.DirtyProperties.ContainsKey(prop) add to dirty props and add oldvalue bool propertyCanBeNull = valueTypeColumn.Value.Type.IsNullable() || !valueTypeColumn.Value.Type.IsValueType; var changeCheck = new CodeBinaryOperatorExpression(); if (!propertyCanBeNull) { // can't be null so just check values changeCheck.Left = new CodeMethodInvokeExpression( CodeHelpers.BaseProperty(valueTypeColumn.Key), "Equals", new CodePropertySetValueReferenceExpression()); changeCheck.Operator = CodeBinaryOperatorType.IdentityEquality; changeCheck.Right = new CodePrimitiveExpression(false); } else { // can be null, need to be careful of null reference exceptions changeCheck.Left = new CodeBinaryOperatorExpression( CodeHelpers.BasePropertyIsNull(valueTypeColumn.Key), CodeBinaryOperatorType.BooleanAnd, new CodeBinaryOperatorExpression( new CodePropertySetValueReferenceExpression(), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null))); changeCheck.Operator = CodeBinaryOperatorType.BooleanOr; changeCheck.Right = new CodeBinaryOperatorExpression( CodeHelpers.BasePropertyIsNotNull(valueTypeColumn.Key), CodeBinaryOperatorType.BooleanAnd, new CodeBinaryOperatorExpression( new CodeMethodInvokeExpression( CodeHelpers.BaseProperty(valueTypeColumn.Key), "Equals", new CodePropertySetValueReferenceExpression()), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(false))); } prop.SetStatements.Insert( 0, new CodeConditionStatement( CodeHelpers.ThisPropertyIsTrue("IsTracking"), new CodeStatement[] { new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeBinaryOperatorExpression( new CodeMethodInvokeExpression( CodeHelpers.ThisProperty("DirtyProperties"), "Contains", new CodePrimitiveExpression(prop.Name)), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(false)), CodeBinaryOperatorType.BooleanAnd, changeCheck), new CodeStatement[] { new CodeExpressionStatement( new CodeMethodInvokeExpression( CodeHelpers.ThisProperty("DirtyProperties"), "Add", new CodePrimitiveExpression(prop.Name))), new CodeAssignStatement( new CodeIndexerExpression( CodeHelpers.ThisProperty("OldValues"), new CodePrimitiveExpression(prop.Name)), CodeHelpers.BaseField(prop.Name)) }), new CodeAssignStatement( new CodeIndexerExpression( CodeHelpers.ThisProperty("NewValues"), new CodePrimitiveExpression(prop.Name)), new CodePropertySetValueReferenceExpression()) })); } trackingClass.Members.Add(constructor); return(trackingClass); }
void when_creating_config() { it["creates config from EntitasPreferencesConfig"] = () => { var config = new CodeGeneratorConfig(new Config(configString)); config.projectPath.should_be("path/to/project/"); config.searchPaths.should_be(new [] { "base1", "base/2/" }); config.assemblies.should_be(new [] { "game.dll", "kit.dll" }); config.plugins.should_be(new [] { "gen1.dll", "gen2.dll" }); config.dataProviders.should_be(new [] { "DataProvider1", "DataProvider2", "DataProvider3" }); config.codeGenerators.should_be(new [] { "Generator1", "Generator2", "Generator3" }); config.postProcessors.should_be(new [] { "PostProcessor1", "PostProcessor2", "PostProcessor3" }); config.targetDirectory.should_be("path/to/folder/"); config.contexts.should_be(new [] { "Core", "Meta", "UI" }); }; it["gets default values when keys dont exist"] = () => { var config = new CodeGeneratorConfig(new Config(string.Empty), new [] { "Data1, Data2" }, new [] { "Gen1, Gen2" }, new [] { "Post1, Post2" }); config.projectPath.should_be("Assembly-CSharp.csproj"); config.searchPaths.should_be(new [] { "Libraries/Entitas", "Libraries/Entitas/Editor", "/Applications/Unity/Unity.app/Contents/Managed", "/Applications/Unity/Unity.app/Contents/Mono/lib/mono/unity" }); config.assemblies.should_be(new [] { "Library/ScriptAssemblies/Assembly-CSharp.dll" }); config.plugins.should_be(new [] { "Entitas.CodeGeneration.Plugins", "Entitas.VisualDebugging.CodeGeneration.Plugins", "Entitas.Blueprints.CodeGeneration.Plugins" }); config.dataProviders.should_be(new [] { "Data1", "Data2" }); config.codeGenerators.should_be(new [] { "Gen1", "Gen2" }); config.postProcessors.should_be(new [] { "Post1", "Post2" }); config.targetDirectory.should_be("Assets/Generated"); config.contexts.should_be(new [] { "Game", "GameState", "Input" }); }; it["sets values"] = () => { var config = new CodeGeneratorConfig(new Config(configString), new string[0], new string[0], new string[0]); config.projectPath = "path/to/newProject/"; config.searchPaths = new [] { "newBase1", "newBase2" }; config.assemblies = new [] { "game.dll", "physics.dll" }; config.plugins = new [] { "myGen1.dll", "myGen2.dll" }; config.dataProviders = new [] { "Data4", "Data5" }; config.codeGenerators = new [] { "Generator4", "Generator5" }; config.postProcessors = new [] { "Post4", "Post5" }; config.targetDirectory = "new/path/"; config.contexts = new [] { "Other1", "Other2" }; config.projectPath.should_be("path/to/newProject/"); config.searchPaths.should_be(new [] { "newBase1", "newBase2" }); config.assemblies.should_be(new [] { "game.dll", "physics.dll" }); config.plugins.should_be(new [] { "myGen1.dll", "myGen2.dll" }); config.dataProviders.should_be(new [] { "Data4", "Data5" }); config.codeGenerators.should_be(new [] { "Generator4", "Generator5" }); config.postProcessors.should_be(new [] { "Post4", "Post5" }); config.targetDirectory.should_be("new/path/"); config.contexts.should_be(new [] { "Other1", "Other2" }); }; it["gets string"] = () => { var config = new CodeGeneratorConfig(new Config(configString)); config.projectPath = "path/to/newProject/"; config.searchPaths = new [] { "newBase1", "newBase2" }; config.assemblies = new [] { "game.dll", "physics.dll" }; config.plugins = new [] { "myGen1.dll", "myGen2.dll" }; config.dataProviders = new [] { "Data4", "Data5" }; config.codeGenerators = new [] { "Generator4", "Generator5" }; config.postProcessors = new [] { "Post4", "Post5" }; config.targetDirectory = "new/path/"; config.contexts = new [] { "Other1", "Other2" }; config.ToString().should_be( "Entitas.CodeGeneration.Project = path/to/newProject/\n" + "Entitas.CodeGeneration.SearchPaths = newBase1, newBase2\n" + "Entitas.CodeGeneration.Assemblies = game.dll, physics.dll\n" + "Entitas.CodeGeneration.Plugins = myGen1.dll, myGen2.dll\n" + "Entitas.CodeGeneration.DataProviders = Data4, Data5\n" + "Entitas.CodeGeneration.CodeGenerators = Generator4, Generator5\n" + "Entitas.CodeGeneration.PostProcessors = Post4, Post5\n" + "Entitas.CodeGeneration.TargetDirectory = new/path/\n" + "Entitas.CodeGeneration.Contexts = Other1, Other2\n" ); }; it["gets string from empty config"] = () => { var config = new CodeGeneratorConfig(new Config(string.Empty)); config.ToString().should_be( "Entitas.CodeGeneration.Project = Assembly-CSharp.csproj\n" + "Entitas.CodeGeneration.SearchPaths = Libraries/Entitas, Libraries/Entitas/Editor, /Applications/Unity/Unity.app/Contents/Managed, /Applications/Unity/Unity.app/Contents/Mono/lib/mono/unity\n" + "Entitas.CodeGeneration.Assemblies = Library/ScriptAssemblies/Assembly-CSharp.dll\n" + "Entitas.CodeGeneration.Plugins = Entitas.CodeGeneration.Plugins, Entitas.VisualDebugging.CodeGeneration.Plugins, Entitas.Blueprints.CodeGeneration.Plugins\n" + "Entitas.CodeGeneration.DataProviders = \n" + "Entitas.CodeGeneration.CodeGenerators = \n" + "Entitas.CodeGeneration.PostProcessors = \n" + "Entitas.CodeGeneration.TargetDirectory = Assets/Generated\n" + "Entitas.CodeGeneration.Contexts = Game, GameState, Input\n" ); }; it["removes empty entries"] = () => { const string configString = "Entitas.CodeGeneration.Contexts = ,,Core,,UI,,"; var config = new CodeGeneratorConfig(new Config(configString)); config.contexts.should_be(new [] { "Core", "UI" }); }; it["removes trailing comma"] = () => { var config = new CodeGeneratorConfig(new Config(string.Empty)); config.contexts = new [] { "Meta", string.Empty }; config.ToString().should_be( "Entitas.CodeGeneration.Project = Assembly-CSharp.csproj\n" + "Entitas.CodeGeneration.SearchPaths = Libraries/Entitas, Libraries/Entitas/Editor, /Applications/Unity/Unity.app/Contents/Managed, /Applications/Unity/Unity.app/Contents/Mono/lib/mono/unity\n" + "Entitas.CodeGeneration.Assemblies = Library/ScriptAssemblies/Assembly-CSharp.dll\n" + "Entitas.CodeGeneration.Plugins = Entitas.CodeGeneration.Plugins, Entitas.VisualDebugging.CodeGeneration.Plugins, Entitas.Blueprints.CodeGeneration.Plugins\n" + "Entitas.CodeGeneration.DataProviders = \n" + "Entitas.CodeGeneration.CodeGenerators = \n" + "Entitas.CodeGeneration.PostProcessors = \n" + "Entitas.CodeGeneration.TargetDirectory = Assets/Generated\n" + "Entitas.CodeGeneration.Contexts = Meta\n" ); }; it["has all keys"] = () => { var keys = CodeGeneratorConfig.keys; keys.should_contain("Entitas.CodeGeneration.Project"); keys.should_contain("Entitas.CodeGeneration.SearchPaths"); keys.should_contain("Entitas.CodeGeneration.Assemblies"); keys.should_contain("Entitas.CodeGeneration.Plugins"); keys.should_contain("Entitas.CodeGeneration.DataProviders"); keys.should_contain("Entitas.CodeGeneration.CodeGenerators"); keys.should_contain("Entitas.CodeGeneration.PostProcessors"); keys.should_contain("Entitas.CodeGeneration.TargetDirectory"); keys.should_contain("Entitas.CodeGeneration.Contexts"); }; }
static void status() { if (File.Exists(Preferences.configPath)) { var fileContent = File.ReadAllText(Preferences.configPath); var properties = new Properties(fileContent); var config = new CodeGeneratorConfig(new Config(fileContent)); _logger.Debug(config.ToString()); Type[] types = null; string[] configurableKeys = null; try { types = CodeGeneratorUtil.LoadTypesFromCodeGeneratorAssemblies(); configurableKeys = getConfigurableKeys( CodeGeneratorUtil.GetUsed <ICodeGeneratorDataProvider>(types, config.dataProviders), CodeGeneratorUtil.GetUsed <ICodeGenerator>(types, config.codeGenerators), CodeGeneratorUtil.GetUsed <ICodeGenFilePostProcessor>(types, config.postProcessors) ); } catch (Exception ex) { foreach (var key in getUnusedKeys(properties)) { _logger.Info("Unused key: " + key); } foreach (var key in getMissingKeys(properties)) { _logger.Warn("Missing key: " + key); } throw ex; } foreach (var key in getUnusedKeys(properties).Where(key => !configurableKeys.Contains(key))) { _logger.Info("Unused key: " + key); } foreach (var key in getMissingKeys(properties)) { _logger.Warn("Missing key: " + key); } foreach (var key in configurableKeys.Where(key => !properties.HasKey(key))) { _logger.Warn("Missing key: " + key); } printUnavailable(CodeGeneratorUtil.GetUnavailable <ICodeGeneratorDataProvider>(types, config.dataProviders)); printUnavailable(CodeGeneratorUtil.GetUnavailable <ICodeGenerator>(types, config.codeGenerators)); printUnavailable(CodeGeneratorUtil.GetUnavailable <ICodeGenFilePostProcessor>(types, config.postProcessors)); printAvailable(CodeGeneratorUtil.GetAvailable <ICodeGeneratorDataProvider>(types, config.dataProviders)); printAvailable(CodeGeneratorUtil.GetAvailable <ICodeGenerator>(types, config.codeGenerators)); printAvailable(CodeGeneratorUtil.GetAvailable <ICodeGenFilePostProcessor>(types, config.postProcessors)); } else { printNoConfig(); } }
static void printCollisions(CodeGeneratorConfig config) { printDuplicates(config.dataProviders); printDuplicates(config.codeGenerators); printDuplicates(config.postProcessors); }
static bool fix(HashSet <string> askedRemoveKeys, HashSet <string> askedAddKeys, Type[] types, CodeGeneratorConfig config, CLIConfig cliConfig, Preferences preferences) { var changed = fixPlugins(askedRemoveKeys, askedAddKeys, types, config, preferences); forceAddKeys(getConfigurables(types, config), preferences); var requiredKeys = config.defaultProperties .Merge(cliConfig.defaultProperties) .Merge(getConfigurables(types, config)).Keys.ToArray(); removeUnusedKeys(askedRemoveKeys, requiredKeys, cliConfig, preferences); return(changed); }
private CodeTypeDeclaration CreateFkClass( IMap map, IDictionary <Type, IMap> maps, CodeGeneratorConfig codeGeneratorConfig) { // generate the foreign key access class based on the original class var foreignKeyClass = new CodeTypeDeclaration( map.Type.Name + codeGeneratorConfig.ForeignKeyAccessClassSuffix); foreignKeyClass.IsClass = true; foreignKeyClass.TypeAttributes = TypeAttributes.Public; foreignKeyClass.BaseTypes.Add(map.Type); foreach ( var column in map.Columns.Where(c => c.Value.Relationship == RelationshipType.ManyToOne || c.Value.Relationship == RelationshipType.OneToOne)) { if (!column.Value.Map.Type.GetProperty(column.Value.Name).GetGetMethod().IsVirtual || !column.Value.Map.Type.GetProperty(column.Value.Name).CanWrite) { // TODO: send a warning back to the programmer, did they mean to do this? continue; } // create a backing property for storing the FK var backingType = column.Value.DbType.GetCLRType(); if (backingType.IsValueType) { backingType = typeof(Nullable <>).MakeGenericType(backingType); } var foreignKeyBackingProperty = this.GenerateGetSetProperty( foreignKeyClass, column.Value.DbName, backingType, FinalPublic); // create a backing field for storing the related entity var backingField = new CodeMemberField( column.Value.Type, column.Value.Name + codeGeneratorConfig.ForeignKeyAccessEntityFieldSuffix); foreignKeyClass.Members.Add(backingField); // override the property getter and setter to use the backingfield var property = new CodeMemberProperty(); property.Name = column.Value.Name; property.Type = new CodeTypeReference(column.Value.Type); property.Attributes = MemberAttributes.Public | MemberAttributes.Override; property.GetStatements.Add( new CodeConditionStatement( //// if backingField != null or Fk backing field is null return new CodeBinaryOperatorExpression( CodeHelpers.ThisFieldIsNotNull(backingField.Name), CodeBinaryOperatorType.BooleanOr, CodeHelpers.ThisPropertyIsNull(foreignKeyBackingProperty.Name)), new CodeStatement[] { // true new CodeMethodReturnStatement( CodeHelpers.ThisField(backingField.Name)) }, new CodeStatement[] { // false, return new object with foreign key set new CodeVariableDeclarationStatement( column.Value.Type, "val", new CodeObjectCreateExpression( column.Value.Type)), new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeVariableReferenceExpression("val"), maps[column.Value.Type].PrimaryKey.Name), new CodePropertyReferenceExpression( CodeHelpers.ThisProperty( foreignKeyBackingProperty.Name), "Value")), new CodeAssignStatement( CodeHelpers.ThisField(backingField.Name), new CodeVariableReferenceExpression("val")), new CodeMethodReturnStatement( new CodeVariableReferenceExpression("val")) })); property.SetStatements.Add( new CodeAssignStatement( CodeHelpers.ThisField(backingField.Name), new CodePropertySetValueReferenceExpression())); foreignKeyClass.Members.Add(property); } return(foreignKeyClass); }
void when_creating_config() { it["creates config from EntitasPreferencesConfig"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString)); config.targetDirectory.should_be("path/to/folder/"); config.contexts.should_be(new [] { "Core", "Meta", "UI" }); config.dataProviders.should_be(new [] { "DataProvider1", "DataProvider2", "DataProvider3" }); config.codeGenerators.should_be(new [] { "Generator1", "Generator2", "Generator3" }); config.postProcessors.should_be(new [] { "PostProcessor1", "PostProcessor2", "PostProcessor3" }); }; it["gets default values when keys dont exist"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty), new [] { "Data1, Data2" }, new [] { "Gen1, Gen2" }, new [] { "Post1, Post2" }); config.targetDirectory.should_be("Assets/Generated/"); config.contexts.should_be(new [] { "Game", "GameState", "Input" }); config.dataProviders.should_be(new [] { "Data1", "Data2" }); config.codeGenerators.should_be(new [] { "Gen1", "Gen2" }); config.postProcessors.should_be(new [] { "Post1", "Post2" }); }; it["sets values"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString), new string[0], new string[0], new string[0]); config.targetDirectory = "new/path/"; config.contexts = new [] { "Other1", "Other2" }; config.dataProviders = new [] { "Data4", "Data5" }; config.codeGenerators = new [] { "Generator4", "Generator5" }; config.postProcessors = new [] { "Post4", "Post5" }; config.targetDirectory.should_be("new/path/"); config.contexts.should_be(new [] { "Other1", "Other2" }); config.dataProviders.should_be(new [] { "Data4", "Data5" }); config.codeGenerators.should_be(new [] { "Generator4", "Generator5" }); config.postProcessors.should_be(new [] { "Post4", "Post5" }); }; it["gets string"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString)); config.targetDirectory = "new/path/"; config.contexts = new [] { "Other1", "Other2" }; config.dataProviders = new [] { "Data4", "Data5" }; config.codeGenerators = new [] { "Generator4", "Generator5" }; config.postProcessors = new [] { "Post4", "Post5" }; config.ToString().should_be( "Entitas.CodeGenerator.TargetDirectory = new/path/\n" + "Entitas.CodeGenerator.Contexts = Other1,Other2\n" + "Entitas.CodeGenerator.DataProviders = Data4,Data5\n" + "Entitas.CodeGenerator.CodeGenerators = Generator4,Generator5\n" + "Entitas.CodeGenerator.PostProcessors = Post4,Post5\n" ); }; it["gets string from empty config"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty)); config.ToString().should_be( "Entitas.CodeGenerator.TargetDirectory = Assets/Generated/\n" + "Entitas.CodeGenerator.Contexts = Game,GameState,Input\n" + "Entitas.CodeGenerator.DataProviders = \n" + "Entitas.CodeGenerator.CodeGenerators = \n" + "Entitas.CodeGenerator.PostProcessors = \n" ); }; it["removes empty contexts"] = () => { const string configString = "Entitas.CodeGenerator.Contexts = ,,Core,,UI,,"; var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString)); config.contexts.should_be(new [] { "Core", "UI" }); }; it["removes empty enabled code generators"] = () => { const string configString = "Entitas.CodeGenerator.CodeGenerators = ,,Gen1,,Gen2,,"; var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(configString)); config.codeGenerators.should_be(new [] { "Gen1", "Gen2" }); }; it["removes trailing comma in contexts string"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty)); config.contexts = new [] { "Meta", string.Empty }; config.ToString().should_be( "Entitas.CodeGenerator.TargetDirectory = Assets/Generated/\n" + "Entitas.CodeGenerator.Contexts = Meta\n" + "Entitas.CodeGenerator.DataProviders = \n" + "Entitas.CodeGenerator.CodeGenerators = \n" + "Entitas.CodeGenerator.PostProcessors = \n" ); }; it["removes trailing comma in enabled code generators string"] = () => { var config = new CodeGeneratorConfig(new EntitasPreferencesConfig(string.Empty)); config.codeGenerators = new [] { "Gen1", string.Empty }; config.ToString().should_be( "Entitas.CodeGenerator.TargetDirectory = Assets/Generated/\n" + "Entitas.CodeGenerator.Contexts = Game,GameState,Input\n" + "Entitas.CodeGenerator.DataProviders = \n" + "Entitas.CodeGenerator.CodeGenerators = Gen1\n" + "Entitas.CodeGenerator.PostProcessors = \n" ); }; }
private CodeTypeDeclaration CreateUpdateClass( IMap map, IDictionary <Type, IMap> maps, CodeGeneratorConfig codeGeneratorConfig) { // public class Post_Update var updateClass = new CodeTypeDeclaration(map.Type.Name + codeGeneratorConfig.UpdateClassSuffix) { IsClass = true, TypeAttributes = TypeAttributes.Public }; updateClass.BaseTypes.Add(map.Type); updateClass.BaseTypes.Add(typeof(IUpdateClass)); // public IList<string> UpdatedProperties { get; set; } this.GenerateGetSetProperty( updateClass, "UpdatedProperties", typeof(IList <>).MakeGenericType(typeof(string)), FinalPublic); // public bool IsConstructed { get; set; } this.GenerateGetSetProperty(updateClass, "IsConstructed", typeof(bool), FinalPublic); // public Post_Update() { // this.IsConstructed = true; // this.backingUpdatedProperties = new List<string>(); // } var constructor = new CodeConstructor { Attributes = MemberAttributes.Public }; constructor.Statements.Add(new CodeAssignStatement(CodeHelpers.ThisProperty("IsConstructed"), new CodePrimitiveExpression(true))); constructor.Statements.Add(new CodeAssignStatement(CodeHelpers.ThisProperty("UpdatedProperties"), new CodeObjectCreateExpression(typeof(List <>).MakeGenericType(typeof(string))))); updateClass.Members.Add(constructor); // public override IList<Comment> Comments { // get { // return this.backingComments; // } // set { // if (this.IsConstructed) { // this.UpdatedProperties.Add("Comments"); // } // this.backingComments = value; // } // } foreach (var column in map.Columns.Where(c => !c.Value.IsIgnored)) { var prop = this.GenerateGetSetProperty( updateClass, column.Key, column.Value.Type, MemberAttributes.Public | MemberAttributes.Override, true); var ifIsConstructed = CodeHelpers.ThisPropertyIsTrue("IsConstructed"); var addColumnToUpdatedProperties = new CodeExpressionStatement( new CodeMethodInvokeExpression( CodeHelpers.ThisProperty("UpdatedProperties"), "Add", new CodePrimitiveExpression(column.Key))); prop.SetStatements.Insert(0, new CodeConditionStatement(ifIsConstructed, addColumnToUpdatedProperties)); } return(updateClass); }