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);
        }
        private static CodeMemberMethod GetGenericResourceListMethod(string name)
        {
            var method = new CodeMemberMethod
            {
                Name       = name,
                ReturnType =
                    new CodeTypeReference(typeof(List <>), CodeTypeReferenceOptions.GlobalReference)
                {
                    TypeArguments =
                    {
                        new CodeTypeReference("global::TypeSafe.Resource",
                                              new CodeTypeReference("TResource", CodeTypeReferenceOptions.GenericTypeParameter))
                    }
                },
                TypeParameters =
                {
                    new CodeTypeParameter("TResource")
                    {
                        Constraints ={ CompilerUtil.GetGlobalScopeTypeReference(typeof(Object))                   }
                    }
                },
                Attributes = MemberAttributes.Public | MemberAttributes.Static
            };

            return(method);
        }
        private static CodeExpression GetCreateExpression(TypeSafeDataEntry c, Type t)
        {
            if (t.IsPrimitive)
            {
                if (c.Parameters.Length == 0 || c.Parameters.Length > 1 || !c.Parameters[0].GetType().IsPrimitive ||
                    c.Parameters[0].GetType() != t)
                {
                    throw new ArgumentException("Primitive DataType must have a single parameter of the same type");
                }

                return(new CodePrimitiveExpression(c.Parameters[0]));
            }

            if (t == typeof(string))
            {
                if (c.Parameters.Length == 0 || c.Parameters.Length > 1 || !(c.Parameters[0] is string))
                {
                    throw new ArgumentException("String DataType must have a single string parameter");
                }

                return(new CodePrimitiveExpression(c.Parameters[0]));
            }

            if (!c.Parameters.All(p => p.GetType().IsPrimitive || p is string))
            {
                throw new ArgumentException("Parameters must be all primitive types");
            }

            var dataType = new CodeTypeReference(t);

            CompilerUtil.SetTypeGlobalReference(dataType);

            return(new CodeObjectCreateExpression(dataType,
                                                  c.Parameters.Select(p => new CodePrimitiveExpression(p)).Cast <CodeExpression>().ToArray()));
        }
        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);
        }
        /// <summary>
        /// Get a CodeTypeReference object to the TypeSafe.Resource[T] class where T is <paramref name="t" />.
        /// </summary>
        public static CodeTypeReference GetResourceType(Type t)
        {
            if (t == typeof(GameObject))
            {
                return(new CodeTypeReference("global::TypeSafe.PrefabResource"));
            }

            return(new CodeTypeReference("global::TypeSafe.Resource", CompilerUtil.GetGlobalScopeTypeReference(t)));
        }
        public static void WriteGetContentsGenericMethod(CodeTypeDeclaration type)
        {
            var method = GetGenericResourceListMethod(Strings.GetResourcesMethodName);

            method.Comments.AddRange(CompilerUtil.CreateDocsComment(Strings.GetContentsGenericCommentSummary,
                                                                    Strings.GetContentsGenericCommentReturns));
            method.Statements.Add(
                new CodeMethodReturnStatement(
                    new CodeSnippetExpression(
                        "global::TypeSafe.TypeSafeUtil.GetResourcesOfType<TResource>(GetContents())")));

            type.Members.Add(method);
        }
        public static void CreateGetContentsMethod(CodeTypeDeclaration type)
        {
            var method = new CodeMemberMethod
            {
                Name       = Strings.GetResourcesMethodName,
                ReturnType = GetIResourceIListType(),
                Attributes = MemberAttributes.Public | MemberAttributes.Static
            };

            method.Comments.AddRange(CompilerUtil.CreateDocsComment(Strings.GetContentsCommentSummary,
                                                                    Strings.GetContentsCommentReturns));

            method.Statements.Add(
                new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null, CollectionMemberName)));

            type.Members.Add(method);
        }
        public static void WriteUnloadAllRecursiveMethod(CodeTypeDeclaration type)
        {
            var method = new CodeMemberMethod {
                Name = Strings.UnloadAllRecursiveMethodName
            };

            method.Comments.AddRange(CompilerUtil.CreateDocsComment(Strings.UnloadAllRecursiveCommentSummary));

            method.Statements.Add(
                new CodeMethodInvokeExpression(
                    new CodeTypeReferenceExpression(new CodeTypeReference("TypeSafe.TypeSafeUtil")
            {
                Options = CodeTypeReferenceOptions.GlobalReference
            }), "UnloadAll", new CodeSnippetExpression("GetContentsRecursive()")));

            type.Members.Add(method);
        }
        private static void WriteResourceProperty(CodeTypeDeclaration type, ResourceDefinition rd, int index)
        {
            string name;

            if (!CompilerUtil.GetSafeNameAndVerifyNotDuplicate(rd.Name, type, out name))
            {
                return;
            }

            var resourceType = GetResourceType(rd.Type);

            var attributes = MemberAttributes.Static;

            if (rd.Type.IsNotPublic)
            {
                attributes |= MemberAttributes.Assembly;
            }
            else
            {
                attributes |= MemberAttributes.Public;
            }

            // Create a property to access the array
            var propertyExpression = new CodeMemberProperty
            {
                Name          = name,
                Type          = resourceType,
                HasSet        = false,
                HasGet        = true,
                Attributes    = attributes,
                GetStatements =
                {
                    new CodeMethodReturnStatement(new CodeCastExpression(resourceType,
                                                                         new CodeArrayIndexerExpression(
                                                                             new CodeVariableReferenceExpression(CollectionMemberName),
                                                                             new CodePrimitiveExpression(index))))
                }
            };

            type.Members.Add(propertyExpression);
        }
        private void WriteFolder(CodeTypeDeclaration type, ResourceFolder folder)
        {
            ResourceCompilerUtil.WriteResources(type, folder);

            foreach (var f in folder.Folders)
            {
                var c = CompilerUtil.CreateStaticType(CompilerUtil.GetSafeName(f.Name));
                WriteFolder(c, f);

                type.Members.Add(c);
            }

            ResourceCompilerUtil.CreateGetContentsMethod(type);
            ResourceCompilerUtil.CreateGetContentsRecursiveMethod(type, folder);

            ResourceCompilerUtil.WriteGetContentsGenericMethod(type);
            ResourceCompilerUtil.WriteGetContentsRecursiveGenericMethod(type);

            ResourceCompilerUtil.WriteUnloadAllMethod(type);
            ResourceCompilerUtil.WriteUnloadAllRecursiveMethod(type);
        }
        public CodeCompileUnit Compile(ResourceDatabase database)
        {
            var unit = new CodeCompileUnit();

            unit.UserData.Add(Strings.CompileUnitUserDataKey, "Resources");

            CompilerUtil.WriteHeader(unit);

            var ns = new CodeNamespace(Namespace);

            unit.Namespaces.Add(ns);

            var container = CompilerUtil.CreateStaticType(ClassName);

            CompilerUtil.AddTypeSafeTag(container);

            WriteFolder(container, database.RootFolder);

            ns.Types.Add(container);

            return(unit);
        }
        public static void CreateGetContentsRecursiveMethod(CodeTypeDeclaration type, ResourceFolder folder)
        {
            var cache = new CodeMemberField(GetIResourceIListType(), RecursiveLookupCacheName)
            {
                Attributes = MemberAttributes.Static | MemberAttributes.Private
            };

            type.Members.Add(cache);

            var method = new CodeMemberMethod
            {
                Attributes = MemberAttributes.Static | MemberAttributes.Public,
                Name       = Strings.GetResourcesRecursiveMethodName,
                ReturnType = GetIResourceIListType()
            };

            method.Comments.AddRange(CompilerUtil.CreateDocsComment(Strings.GetContentsRecursiveCommentSummary,
                                                                    Strings.GetContentsRecursiveCommentReturns));

            method.Statements.Add(
                new CodeConditionStatement(
                    new CodeBinaryOperatorExpression(
                        new CodeFieldReferenceExpression(null, RecursiveLookupCacheName),
                        CodeBinaryOperatorType.IdentityInequality,
                        new CodePrimitiveExpression(null)
                        ),
                    new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null, RecursiveLookupCacheName))));

            // Create list
            method.Statements.Add(new CodeVariableDeclarationStatement(GetIResourceListType(), "tmp",
                                                                       new CodeObjectCreateExpression(GetIResourceListType())));

            // Add any resources from this folder
            method.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("tmp"), "AddRange",
                                                                 new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, Strings.GetResourcesMethodName))));

            // Add any resources from subfolders
            foreach (var f in folder.Folders)
            {
                var name = CompilerUtil.GetSafeName(f.Name, true);

                // Skip if this folder hasn't been added to the type for some reason
                if (!CompilerUtil.IsDuplicate(name, type))
                {
                    continue;
                }

                // Add any resources from this folder
                method.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("tmp"),
                                                                     "AddRange",
                                                                     new CodeMethodInvokeExpression(
                                                                         new CodeMethodReferenceExpression(new CodeSnippetExpression(name),
                                                                                                           Strings.GetResourcesRecursiveMethodName))));
            }

            method.Statements.Add(
                new CodeAssignStatement(new CodeFieldReferenceExpression(null, RecursiveLookupCacheName),
                                        new CodeVariableReferenceExpression("tmp")));

            method.Statements.Add(
                new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null, RecursiveLookupCacheName)));

            type.Members.Add(method);
        }
        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);
            }
        }