private static void PopulateClass(TypeSafeDataUnit dataUnit, CodeTypeDeclaration container)
        {
            foreach (var nestedUnit in dataUnit.NestedUnits)
            {
                string className;

                if (!CompilerUtil.GetSafeNameAndVerifyNotDuplicate(nestedUnit.ClassName, container, out className))
                {
                    continue;
                }

                if (className == container.Name)
                {
                    TSLog.LogWarning(LogCategory.Compile, Strings.Warning_NameCannotBeSameAsParent);
                    className = "_" + className;

                    // Check for duplicates again after having added the '_'
                    if (!CompilerUtil.GetSafeNameAndVerifyNotDuplicate(className, container, out className))
                    {
                        continue;
                    }
                }

                var nestedContainer = CompilerUtil.CreateStaticType(className);
                PopulateClass(nestedUnit, nestedContainer);

                container.Members.Add(nestedContainer);
            }

            PopulateClassMembers(dataUnit, container);
        }
        public CodeCompileUnit Compile(TypeSafeDataUnit dataUnit)
        {
            var compileUnit = new CodeCompileUnit();

            compileUnit.UserData.Add(Strings.CompileUnitUserDataKey, dataUnit.FileName);

            CompilerUtil.WriteHeader(compileUnit);

            var ns = new CodeNamespace(Namespace);

            compileUnit.Namespaces.Add(ns);

            var className = CompilerUtil.GetSafeName(dataUnit.ClassName, false, false);

            if (className != dataUnit.ClassName)
            {
                TSLog.LogWarning(LogCategory.Compile,
                                 string.Format("Class name was modified to conform to C# standards ({0} -> {1})", dataUnit.ClassName, className));
            }

            var container = CompilerUtil.CreateStaticType(className);

            CompilerUtil.AddTypeSafeTag(container);

            PopulateClass(dataUnit, container);

            ns.Types.Add(container);
            return(compileUnit);
        }
        public TypeSafeDataUnit GetTypeSafeDataUnit()
        {
            var rootUnit = new TypeSafeDataUnit(TypeSafeUtil.GetFinalClassName(Strings.AudioMixersName)
                                                , typeof(float), new List <TypeSafeDataEntry>(), false, "AudioMixers");

            var mixers = Settings.Instance.AudioMixers;

            foreach (var guid in mixers)
            {
                var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                var asset     = AssetDatabase.LoadAssetAtPath <AudioMixer>(assetPath);

                TSLog.Log(LogCategory.Trace, string.Format("AudioMixer: {0}", assetPath), asset);
                rootUnit.NestedUnits.Add(CreateMixerDataUnit(new AudioMixerControllerWrapper(asset)));
            }

            return(rootUnit);
        }
        private TypeSafeDataUnit CreateAnimatorDataUnit(AnimatorController controller)
        {
            var unit = new TypeSafeDataUnit(controller.name, typeof(string));

            var parametersUnit = new TypeSafeDataUnit("Parameters", typeof(int));

            foreach (var param in controller.parameters)
            {
                TSLog.Log(LogCategory.Trace, string.Format("Parameter: {0} ({1})", param.name, param.nameHash));

                if (parametersUnit.Data.Any(p => p.PropertyName == param.name))
                {
                    TSLog.LogWarning(LogCategory.Compile, string.Format("[AnimatorController] Duplicate parameter name ({0}).", param.name), controller);
                    continue;
                }

                parametersUnit.Data.Add(new TypeSafeDataEntry(param.name, new object[] { param.nameHash }));
            }

            var layersUnit = new TypeSafeDataUnit("Layers", typeof(int));

            for (var i = 0; i < controller.layers.Length; i++)
            {
                var layer = controller.layers[i];
                TSLog.Log(LogCategory.Trace, string.Format("Layer: {0}, Index: {1}", layer.name, i));

                if (layersUnit.Data.Any(p => p.PropertyName == layer.name))
                {
                    TSLog.LogWarning(LogCategory.Compile, string.Format("[AnimatorController] Duplicate layer name ({0}).", layer.name),
                                     controller);
                    continue;
                }

                layersUnit.Data.Add(new TypeSafeDataEntry(layer.name, new object[] { i }));
            }

            unit.NestedUnits.Add(parametersUnit);
            unit.NestedUnits.Add(layersUnit);

            return(unit);
        }
        private TypeSafeDataUnit CreateMixerDataUnit(AudioMixerControllerWrapper wrapper)
        {
            var unit = new TypeSafeDataUnit(wrapper.Mixer.name, typeof(string));

            var parametersUnit = new TypeSafeDataUnit("Parameters", typeof(string));

            foreach (var param in wrapper.exposedParameters)
            {
                TSLog.Log(LogCategory.Trace, string.Format("Parameter: {0}", param.name));

                if (parametersUnit.Data.Any(p => p.PropertyName == param.name))
                {
                    TSLog.LogWarning(LogCategory.Compile, string.Format("[AudioMixer] Duplicate parameter name ({0}).", param.name), wrapper.Mixer);
                    continue;
                }

                parametersUnit.Data.Add(new TypeSafeDataEntry(param.name, new object[] { param.name }));
            }

            var snapshotsUnit = new TypeSafeDataUnit("Snapshots", typeof(string));

            foreach (var snapshot in wrapper.snapshots)
            {
                TSLog.Log(LogCategory.Trace, string.Format("Snapshot: {0}", snapshot.name));

                if (snapshotsUnit.Data.Any(p => p.PropertyName == snapshot.name))
                {
                    TSLog.LogWarning(LogCategory.Compile, string.Format("[AudioMixer] Duplicate snapshot name ({0}).", snapshot.name), wrapper.Mixer);
                    continue;
                }

                snapshotsUnit.Data.Add(new TypeSafeDataEntry(snapshot.name, new object[] { snapshot.name }));
            }

            unit.NestedUnits.Add(parametersUnit);
            unit.NestedUnits.Add(snapshotsUnit);

            return(unit);
        }
        public TypeSafeDataUnit GetTypeSafeDataUnit()
        {
            var unit = new TypeSafeDataUnit(TypeSafeUtil.GetFinalClassName(Strings.TagsTypeName), typeof(string));
            var tags = InternalEditorUtility.tags;

            foreach (var tag in tags)
            {
                var ignore = string.IsNullOrEmpty(tag) || tag.Trim().Length == 0;

                TSLog.Log(LogCategory.Scanner, string.Format("Tag: {0}, (ignore={1})", tag, ignore));

                if (!ignore)
                {
                    unit.Data.Add(new TypeSafeDataEntry(tag, new object[] { tag }));
                }
            }

            unit.EnableAllProperty = true;
            unit.FileName          = "Tags";

            return(unit);
        }
        private static void PopulateClassMembers(TypeSafeDataUnit unit, CodeTypeDeclaration container)
        {
            // Create the list type that is used to store the internal array (not used when All list is disabled)
            var valueListType = new CodeTypeReference(typeof(IList <>))
            {
                TypeArguments = { unit.DataType }
            };

            // Set up the default type for members
            var dataType = new CodeTypeReference(unit.DataType);

            // Use global references
            CompilerUtil.SetTypeGlobalReference(dataType);
            CompilerUtil.SetTypeGlobalReference(valueListType);

            // Expression to make the internal list (not used when All list is disabled)
            var arrayExpression = new CodeArrayCreateExpression(dataType);

            //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Populating {container.Name}");

            foreach (var c in unit.Data)
            {
                // Can't override type if using the All property method (since can't store non-compatible types in the list)
                if (unit.EnableAllProperty && c.OverrideType != null)
                {
                    throw new InvalidOperationException("Cannot override type when using All list.");
                }

                var    name = c.PropertyName;
                string memberName;

                if (
                    !CompilerUtil.GetSafeNameAndVerifyNotDuplicate(name, container, out memberName,
                                                                   !c.OverrideRestrictedNames))
                {
                    continue;
                }

                // Create the expression to initialize this member
                var entryExpression = GetCreateExpression(c, c.OverrideType != null ? c.OverrideType : unit.DataType);
                var isObsolete      = !string.IsNullOrEmpty(c.ObsoleteWarning);

                //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Member (name={memberName}, isObsolete={isObsolete})");

                // Exclude obsolete members from the _all array
                if (unit.EnableAllProperty && !isObsolete)
                {
                    // Add the initializer expression to the internal _all array
                    arrayExpression.Initializers.Add(entryExpression);
                }

                CodeTypeMember member;

                // Create a field if one of the following criteria matches:
                // - Entry is a primitive or string. We duplicate the data (include in _all array and const field) so that const uses are faster
                // - All property is disabled, so we don't have an internal _all array to access
                // - Entry has an obsolete warning and so isn't included in the internal _all array.
                if (!unit.EnableAllProperty || unit.DataType.IsPrimitive || unit.DataType == typeof(string) ||
                    isObsolete)
                {
                    //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Member handled as field");

                    var entryTypeReference = dataType;
                    var attributes         = MemberAttributes.Public;

                    if (c.OverrideType != null)
                    {
                        entryTypeReference = new CodeTypeReference(c.OverrideType);
                    }

                    if (CompilerUtil.IsPrimitiveType(unit.DataType))
                    {
                        attributes |= MemberAttributes.Const;
                    }
                    else
                    {
                        attributes        |= MemberAttributes.Static;
                        entryTypeReference = new CodeTypeReference("readonly global::" + entryTypeReference.BaseType);
                    }

                    // Duplicate data and create a field for data entry
                    member = new CodeMemberField
                    {
                        Name           = memberName,
                        Type           = entryTypeReference,
                        Attributes     = attributes,
                        InitExpression = entryExpression
                    };

                    if (isObsolete)
                    {
                        //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Adding obsolete warning: {c.ObsoleteWarning}");
                        member.CustomAttributes.Add(CompilerUtil.GetObsoleteAttribute(c.ObsoleteWarning, false));
                    }
                }
                else
                {
                    //TSLog.Log(LogCategory.Trace, $"[DataUnitCompiler] Member handled as property");

                    // Otherwise create a property getter to access the internal _all array element
                    member = new CodeMemberProperty
                    {
                        Name          = memberName,
                        Type          = dataType,
                        HasSet        = false,
                        HasGet        = true,
                        Attributes    = MemberAttributes.Static | MemberAttributes.Public,
                        GetStatements =
                        {
                            new CodeMethodReturnStatement(new CodeArrayIndexerExpression(
                                                              new CodeVariableReferenceExpression("__all"),
                                                              new CodePrimitiveExpression(arrayExpression.Initializers.Count - 1)))
                        }
                    };
                }

                container.Members.Add(member);
            }

            if (unit.EnableAllProperty)
            {
                var all = new CodeMemberField(valueListType, "__all")
                {
                    InitExpression =
                        new CodeObjectCreateExpression(
                            new CodeTypeReference(typeof(ReadOnlyCollection <>),
                                                  CodeTypeReferenceOptions.GlobalReference)
                    {
                        TypeArguments = { dataType }
                    }, arrayExpression),
                    Attributes = MemberAttributes.Private | MemberAttributes.Static
                };

                container.Members.Add(all);

                var allPublic = new CodeMemberProperty
                {
                    Name          = "All",
                    Type          = valueListType,
                    GetStatements =
                    {
                        new CodeMethodReturnStatement(new CodeFieldReferenceExpression {
                            FieldName = "__all"
                        })
                    },
                    HasGet     = true,
                    Attributes = MemberAttributes.Public | MemberAttributes.Static
                };

                container.Members.Add(allPublic);
            }
        }