#if UNITY_2018_3_OR_NEWER using System; using System.Linq; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace UnityAtoms.Editor { /// /// Generator that generates new Atom types based on the input data. Used by the `GeneratorEditor`. Only availble in `UNITY_2019_1_OR_NEWER`. /// internal static class Generator { public static string[] GetTemplatePaths() { var templateSearchPath = Runtime.IsUnityAtomsRepo ? Directory.GetParent(Application.dataPath).Parent.FullName : // "Packages" Directory.GetParent(Application.dataPath).FullName; return Directory.GetFiles(templateSearchPath, "UA_Template*.txt", SearchOption.AllDirectories); } public static List CreateTemplateConditions(bool isValueTypeEquatable, string valueTypeNamespace, string subUnityAtomsNamespace, string valueType) { var templateConditions = new List(); templateConditions.Add("TYPE_IS_" + valueType.ToUpper()); if (valueType == "int" || valueType == "float") { templateConditions.Add("IS_NUMERIC");} if (valueType == "Vector2" || valueType == "Vector3") { templateConditions.Add("IS_VECTOR");} if (isValueTypeEquatable) { templateConditions.Add("EQUATABLE"); } if (!string.IsNullOrEmpty(valueTypeNamespace)) { templateConditions.Add("TYPE_HAS_NAMESPACE"); } if (!string.IsNullOrEmpty(subUnityAtomsNamespace)) { templateConditions.Add("HAS_SUB_UA_NAMESPACE"); } return templateConditions; } public static Dictionary CreateTemplateVariablesMap(string valueType, string valueTypeNamespace, string subUnityAtomsNamespace) { var templateVariables = new Dictionary() { { "VALUE_TYPE_NAME", valueType.Replace('.', '_').Capitalize() }, { "VALUE_TYPE", valueType }, { "VALUE_TYPE_NAME_NO_PAIR", valueType.Contains("Pair") ? valueType.Capitalize().Remove(valueType.IndexOf("Pair")) : valueType.Capitalize() } }; if (!string.IsNullOrEmpty(valueTypeNamespace)) { templateVariables.Add("TYPE_NAMESPACE", valueTypeNamespace); } if (!string.IsNullOrEmpty(subUnityAtomsNamespace)) { templateVariables.Add("SUB_UA_NAMESPACE", subUnityAtomsNamespace); } return templateVariables; } /// /// Generate new Atoms based on the input data. /// /// The type of Atom to generate.abstract Eg. double, byte, MyStruct, MyClass. /// Base write path (relative to the Asset folder) where the Atoms are going to be written to. /// Is the `type` provided implementing `IEquatable`? /// A list of `AtomType`s to be generated. /// If the `type` provided is defined under a namespace, provide that namespace here. /// By default the Atoms that gets generated will be under the namespace `UnityAtoms`. If you for example like it to be under `UnityAtoms.MyNamespace` you would then enter `MyNamespace` in this field. /// /// /// namespace MyNamespace /// { /// public struct MyStruct /// { /// public string Text; /// public int Number; /// } /// } /// var generator = new Generator(); /// generator.Generate("MyStruct", "", false, new List<AtomType>() { AtomTypes.ACTION }, "MyNamespace", ""); // Generates an Atom Action of type MyStruct /// /// public static void Generate(AtomReceipe atomReceipe, string baseWritePath, string[] templatePaths, List templateConditions, Dictionary templateVariables) { var (atomType, valueType) = atomReceipe; // TODO: More validation of that the type exists / is correct. if (string.IsNullOrEmpty(valueType)) { Debug.LogWarning($"{Runtime.Constants.LOG_PREFIX} You need to specify a value type. Aborting!"); return; } if (string.IsNullOrEmpty(baseWritePath) || !Directory.Exists(baseWritePath)) { Debug.LogWarning($"{Runtime.Constants.LOG_PREFIX} You need to specify a valid base write path. Aborting!"); return; } Debug.Log($"{Runtime.Constants.LOG_PREFIX} Generating atom {atomType.Name} for value type {valueType}"); List> filesToGenerate = new List>() { new Tuple(atomType.TemplateName, atomType.RelativeFileNameAndPath) }; if (atomType.HasDrawerTemplate) filesToGenerate.Add(new Tuple(atomType.DrawerTemplateName, atomType.RelativeDrawerFileNameAndPath)); if (atomType.HasEditorTemplate) filesToGenerate.Add(new Tuple(atomType.EditorTemplateName, atomType.RelativeEditorFileNameAndPath)); foreach (var (templateName, relativeFilePath) in filesToGenerate) { var templatePath = templatePaths.FirstOrDefault((path) => path.EndsWith(templateName)); if (string.IsNullOrEmpty(templatePath)) { Debug.Log($"{Runtime.Constants.LOG_PREFIX} Template {templateName} for {atomType.DisplayName} not found. Skipping!"); return; } var resolvedRelativeFilePath = Templating.ResolveVariables(templateVariables: templateVariables, toResolve: relativeFilePath); var template = File.ReadAllText(templatePath); var filePath = Path.Combine(baseWritePath, resolvedRelativeFilePath); var fileDirectory = Path.GetDirectoryName(filePath); // Create write directory Directory.CreateDirectory(fileDirectory); // Adjust content var content = Templating.ResolveVariables(templateVariables: templateVariables, toResolve: template); content = Templating.ResolveConditionals(template: content, trueConditions: templateConditions); content = RemoveDuplicateNamespaces(template: content); // Write to file File.WriteAllText(filePath, content); AssetDatabase.ImportAsset(filePath); } } /// /// Removes duplicate namespaces, given content from a template. /// /// The content template to remove namespace from. /// A copy of `content`, but without duplicate namespaces. private static string RemoveDuplicateNamespaces(string template) { var currentIndex = 0; var countNamespaces = new Dictionary(); while (true) { currentIndex = template.IndexOf("using ", currentIndex); if (currentIndex == -1) break; var namespaceStartIndex = currentIndex + 6; var namespaceEndIndex = template.IndexOf(";", namespaceStartIndex); var ns = template.Substring(namespaceStartIndex, namespaceEndIndex - namespaceStartIndex); if (countNamespaces.ContainsKey(ns)) { countNamespaces[ns] = countNamespaces[ns] + 1; } else { countNamespaces.Add(ns, 1); } currentIndex = namespaceEndIndex; } var contentCopy = String.Copy(template); foreach (var kvp in countNamespaces) { if (kvp.Value > 1) { var usingStr = $"using {kvp.Key};"; contentCopy = contentCopy.Remove(contentCopy.IndexOf(usingStr), usingStr.Length+1); } } return contentCopy; } } } #endif