예제 #1
0
            /// <summary>
            /// Finds the first native (non-managed) class for this managed class. This also caches the native parent
            /// class constructor for calling the parent constructor and setting it as the fallback constructor on hotreload.
            /// </summary>
            public void ResolveNativeParentClass()
            {
                NativeParentClassConstructor = IntPtr.Zero;
                if (Address == IntPtr.Zero)
                {
                    return;
                }

                // We could possibly update this code to do a deep search for the first non-C# class (e.g. C# : X : C# : X : UObject).
                // We aren't currently calling the parent constructor in a way which would allow this. If we supported it as-is the C#
                // constructors would get called multiple times when calling the parent constructor.

                IntPtr parentClass = Native_UClass.GetSuperClass(Address);

                while (parentClass != IntPtr.Zero)
                {
                    if (!Native_UObjectBaseUtility.IsA(parentClass, Runtime.Classes.USharpClass))
                    {
                        NativeParentClass            = parentClass;
                        NativeParentClassConstructor = Native_UClass.Get_ClassConstructor(parentClass);
                        break;
                    }
                    parentClass = Native_UClass.GetSuperClass(parentClass);
                }
                Debug.Assert(NativeParentClass != IntPtr.Zero);
            }
예제 #2
0
        /// <summary>
        /// Get the name of the CDO for the this class
        /// </summary>
        /// <returns>The name of the CDO</returns>
        public FName GetDefaultObjectName()
        {
            FName result;

            Native_UClass.GetDefaultObjectName(Address, out result);
            return(result);
        }
예제 #3
0
        private void MoveToTransientPackage(IntPtr obj)
        {
            // Copy of UObjectBase.cpp UClassCompiledInDefer

            // Check if rooted?
            Native_UObjectBaseUtility.RemoveFromRoot(obj);
            Native_UObjectBaseUtility.ClearFlags(obj, EObjectFlags.Standalone | EObjectFlags.Public);

            IntPtr defaultObject = Native_UClass.GetDefaultObject(obj, false);

            if (defaultObject != IntPtr.Zero)
            {
                // Check if rooted?
                Native_UObjectBaseUtility.RemoveFromRoot(defaultObject);
                Native_UObjectBaseUtility.ClearFlags(defaultObject, EObjectFlags.Standalone | EObjectFlags.Public);
            }

            FName oldClassRename = NativeReflection.MakeUniqueObjectName(NativeReflection.GetTransientPackage(),
                                                                         Native_UObjectBase.GetClass(obj), new FName("USharpHotReload_" + Native_UObjectBase.GetFName(obj)));

            using (FStringUnsafe oldClassRenameUnsafe = new FStringUnsafe(oldClassRename.ToString()))
            {
                Native_UObject.Rename(obj, ref oldClassRenameUnsafe.Array, NativeReflection.GetTransientPackage(), ERenameFlags.None);
            }

            Native_UObjectBaseUtility.SetFlags(obj, EObjectFlags.Transient);
            Native_UObjectBaseUtility.AddToRoot(obj);
        }
예제 #4
0
        /// <summary>
        /// Gets the name of the function when exposed to a scripting system (e.g. Python)
        /// </summary>
        public bool GetScriptName(string originalName, out string name)
        {
            string scriptFunctionName    = originalName;
            bool   hasScriptFunctionName = false;

            string scriptName = this.GetMetaData(MDFunc.ScriptName);

            if (!string.IsNullOrEmpty(scriptName))
            {
                scriptFunctionName    = scriptName;
                hasScriptFunctionName = true;
            }
            else
            {
                // Remove the K2_ prefix (do it in a loop just incase there are multiple K2_ prefixes)
                IntPtr ownerClass = Native_UField.GetOwnerClass(Address);
                if (ownerClass != IntPtr.Zero && Native_UClass.HasAnyClassFlags(ownerClass, EClassFlags.Native))
                {
                    while (scriptFunctionName.StartsWith("K2_"))
                    {
                        scriptFunctionName    = scriptFunctionName.Substring(3);
                        hasScriptFunctionName = true;
                    }
                }
            }

            name = scriptFunctionName;

            return(hasScriptFunctionName);
        }
예제 #5
0
 /// <summary>
 /// Add a native function to the internal native function table
 /// </summary>
 /// <param name="name">name of the function</param>
 /// <param name="pointer">pointer to the function</param>
 public void AddNativeFunction(string name, IntPtr pointer)
 {
     using (FStringUnsafe nameUnsafe = new FStringUnsafe(name))
     {
         Native_UClass.AddNativeFunction(Address, ref nameUnsafe.Array, pointer);
     }
 }
예제 #6
0
        private static unsafe IntPtr FindOriginalVTableOwner(IntPtr baseMostClass, IntPtr ownerClass, IntPtr functionAddress, int vtableIndex)
        {
            // Don't search lower than the target base
            if (ownerClass == baseMostClass)
            {
                return(ownerClass);
            }

            IntPtr originalOwner = ownerClass;

            while ((ownerClass = Native_UClass.GetSuperClass(ownerClass)) != IntPtr.Zero)
            {
                IntPtr  obj    = Native_UClass.GetDefaultObject(ownerClass, true);
                IntPtr *vtable = *(IntPtr **)obj;
                if (vtable[vtableIndex] == functionAddress)
                {
                    originalOwner = ownerClass;
                }

                // Don't search lower than the target base
                if (ownerClass == baseMostClass)
                {
                    return(ownerClass);
                }
            }
            return(originalOwner);
        }
예제 #7
0
파일: UObject.cs 프로젝트: OCEAN623/USharp
        public unsafe T GetInterface <T>() where T : class, IInterface
        {
            T result = this as T;

            if (result != null)
            {
                return(result);
            }
            if (injectedInterfaces == null)
            {
                // If the injected interfaces haven't been set up set them up now

                if (objRef == null)
                {
                    return(null);
                }

                UClass unrealClass = GetClass();
                if (unrealClass as USharpClass != null)
                {
                    // This is a C# defined type. We know if it implements the target interface or not due to the
                    // above "this as T". There isn't any need to inject interfaces into the UObject.
                    return(null);
                }

                FScriptArray *interfacesPtr = (FScriptArray *)Native_UClass.Get_InterfacesRef(unrealClass.Address);
                if (interfacesPtr->ArrayNum != 0)
                {
                    injectedInterfaces = new Dictionary <Type, IInterface>();
                    foreach (FImplementedInterface implementedInterface in unrealClass.Interfaces)
                    {
                        if (implementedInterface.InterfaceClassAddress != IntPtr.Zero)
                        {
                            Type type = UClass.GetTypeFromClassAddress(implementedInterface.InterfaceClassAddress);
                            if (type != null)
                            {
                                IInterface instance = UnrealInterfacePool.New(type, objRef);
                                if (instance != null)
                                {
                                    injectedInterfaces[type] = instance;
                                    if (type == typeof(T))
                                    {
                                        result = instance as T;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // Try and get the interface from the injected interfaces.
                IInterface instance;
                injectedInterfaces.TryGetValue(typeof(T), out instance);
                result = instance as T;
            }
            return(result);
        }
예제 #8
0
 /// <summary>
 /// Finds the common base class that parents the array of classes passed in.
 /// </summary>
 /// <param name="inClasses">the array of classes to find the common base for</param>
 /// <returns>the common base class or NULL</returns>
 public static UClass FindCommonBase(UClass[] inClasses)
 {
     using (TArrayUnsafe <UClass> inClassesUnsafe = new TArrayUnsafe <UClass>())
     {
         inClassesUnsafe.AddRange(inClasses);
         return(GCHelper.Find <UClass>(Native_UClass.FindCommonBaseMany(inClassesUnsafe.Address)));
     }
 }
예제 #9
0
 /// <summary>
 /// This will return whether or not this class implements the passed in class / interface
 /// </summary>
 /// <param name="someInterface">the interface to check and see if this class implements it</param>
 /// <returns></returns>
 public bool ImplementsInterface(UClass someInterface)
 {
     if (someInterface != null && someInterface.HasAnyClassFlags(EClassFlags.Interface))
     {
         return(Native_UClass.ImplementsInterface(Address, someInterface.Address));
     }
     return(false);
 }
예제 #10
0
 public string GetDescription()
 {
     using (FStringUnsafe resultUnsafe = new FStringUnsafe())
     {
         Native_UClass.GetDescription(Address, ref resultUnsafe.Array);
         return(resultUnsafe.Value);
     }
 }
예제 #11
0
 /// <summary>
 /// Gets all default instanced objects (often components).
 /// </summary>
 /// <param name="outDefaultSubobjects">An array to be filled with default subobjects.</param>
 public void GetDefaultObjectSubobjects(out UObject[] outDefaultSubobjects)
 {
     using (TArrayUnsafe <UObject> defaultSubobjectsUnsafe = new TArrayUnsafe <UObject>())
     {
         Native_UClass.GetDefaultObjectSubobjects(Address, defaultSubobjectsUnsafe.Address);
         outDefaultSubobjects = defaultSubobjectsUnsafe.ToArray();
     }
 }
예제 #12
0
 /// <summary>
 /// Translates the hardcoded script config names (engine, editor, input and
 /// game) to their global pendants and otherwise uses config(myini) name to
 /// look for a game specific implementation and creates one based on the
 /// default if it doesn't exist yet.
 /// </summary>
 /// <returns>name of the class specific ini file</returns>
 public string GetConfigName()
 {
     using (FStringUnsafe resultUnsafe = new FStringUnsafe())
     {
         Native_UClass.GetConfigName(Address, ref resultUnsafe.Array);
         return(resultUnsafe.Value);
     }
 }
예제 #13
0
        /// <summary>
        /// Replace a native function in the  internal native function table
        /// </summary>
        /// <param name="inName">name of the function</param>
        /// <param name="inPointer">pointer to the function</param>
        /// <param name="addToFunctionRemapTable">For C++ hot-reloading, UFunctions are patched in a deferred manner and this should be true
        /// For script hot-reloading, script integrations may have a many to 1 mapping of UFunction to native pointer
        /// because dispatch is shared, so the C++ remap table does not work in this case, and this should be false</param>
        /// <returns>true if the function was found and replaced, false if it was not</returns>
        public bool ReplaceNativeFunction(FName inName, IntPtr inPointer, bool addToFunctionRemapTable)
        {
            // WITH_HOT_RELOAD
            if (Native_UClass.ReplaceNativeFunction == null)
            {
                return(false);
            }

            return(Native_UClass.ReplaceNativeFunction(Address, ref inName, inPointer, addToFunctionRemapTable));
        }
예제 #14
0
파일: UObject.cs 프로젝트: OCEAN623/USharp
        /// <summary>
        /// Returns true if this objects class implements the given IInterface derived type
        /// (call this on UObject instances; if you are working with a UClass call ImplementsInterface() instead).<para/>
        /// This is the equivalent of UKismetSystemLibrary::DoesImplementInterface().
        /// This is also the same as obj.GetClass().ImplementsInterface().
        /// </summary>
        /// <param name="type">The IInterface derived type</param>
        /// <returns>True if  this objects class implements the given IInterface derived type</returns>
        public bool DoesImplementInterface(Type type)
        {
            UClass interfaceClass = UClass.GetClass(type);

            if (interfaceClass != null && interfaceClass.ClassFlags.HasFlag(EClassFlags.Interface))
            {
                return(Native_UClass.ImplementsInterface(Native_UObjectBase.GetClass(Address), interfaceClass.Address));
            }
            return(false);
        }
예제 #15
0
        private IntPtr FindFirstNonUSharpClassParentClass(IntPtr unrealClass)
        {
            IntPtr sharpStaticClass = Native_USharpClass.StaticClass();

            while (unrealClass != IntPtr.Zero && !Native_UObjectBaseUtility.IsA(unrealClass, sharpStaticClass))
            {
                unrealClass = Native_UClass.GetSuperClass(unrealClass);
            }
            return(unrealClass);
        }
예제 #16
0
파일: UObject.cs 프로젝트: OCEAN623/USharp
        /// <summary>
        /// Returns true if this objects class implements the given IInterface derived type
        /// (call this on UObject instances; if you are working with a UClass call ImplementsInterface() instead).<para/>
        /// This is the equivalent of UKismetSystemLibrary::DoesImplementInterface().
        /// This is also the same as obj.GetClass().ImplementsInterface().
        /// </summary>
        /// <typeparam name="T">The IInterface derived type</typeparam>
        /// <returns>True if  this objects class implements the given IInterface derived type</returns>
        public bool DoesImplementInterface <T>() where T : IInterface
        {
            IntPtr interfaceClass = UClass.GetInterfaceClassAddress <T>();

            if (interfaceClass != IntPtr.Zero && Native_UClass.GetClassFlags(interfaceClass).HasFlag(EClassFlags.Interface))
            {
                return(Native_UClass.ImplementsInterface(Native_UObjectBase.GetClass(Address), interfaceClass));
            }
            return(false);
        }
예제 #17
0
        private IntPtr FindFirstNativeParentClass(IntPtr unrealClass)
        {
            IntPtr sharpStaticClass = Native_USharpClass.StaticClass();

            while (unrealClass != IntPtr.Zero && (!Native_UClass.HasAnyClassFlags(unrealClass, EClassFlags.Native) ||
                                                  Native_UObjectBaseUtility.IsA(unrealClass, sharpStaticClass)))
            {
                unrealClass = Native_UClass.GetSuperClass(unrealClass);
            }
            return(unrealClass);
        }
예제 #18
0
        /// <summary>
        /// Gets the UClass address holding the interface information for the given path (e.g. "/Script/MovieScene.MovieSceneEasingFunction")
        /// </summary>
        /// <param name="path">The path of the interface</param>
        /// <returns>The address of the UClass interface information for the given path</returns>
        public static IntPtr GetInterfaceClassAddress(string path)
        {
            IntPtr address = GetClassAddress(path);

            // Restrict this to just interfaces
            if (address != IntPtr.Zero && Native_UClass.HasAnyClassFlags(address, EClassFlags.Interface))
            {
                return(address);
            }

            return(IntPtr.Zero);
        }
예제 #19
0
        public bool IsFunctionHidden(string inFunction)
        {
            // WITH_EDITOR || HACK_HEADER_GENERATOR
            if (Native_UClass.IsFunctionHidden == null)
            {
                return(false);
            }

            using (FStringUnsafe inFunctionUnsafe = new FStringUnsafe(inFunction))
            {
                return(Native_UClass.IsFunctionHidden(Address, ref inFunctionUnsafe.Array));
            }
        }
예제 #20
0
        public bool IsAutoCollapseCategory(string inCategory)
        {
            // WITH_EDITOR || HACK_HEADER_GENERATOR
            if (Native_UClass.IsAutoCollapseCategory == null)
            {
                return(false);
            }

            using (FStringUnsafe inCategoryUnsafe = new FStringUnsafe(inCategory))
            {
                return(Native_UClass.IsAutoCollapseCategory(Address, ref inCategoryUnsafe.Array));
            }
        }
예제 #21
0
        public bool IsClassGroupName(string inGroupName)
        {
            // WITH_EDITOR || HACK_HEADER_GENERATOR
            if (Native_UClass.IsClassGroupName == null)
            {
                return(false);
            }

            using (FStringUnsafe inGroupNameUnsafe = new FStringUnsafe(inGroupName))
            {
                return(Native_UClass.IsClassGroupName(Address, ref inGroupNameUnsafe.Array));
            }
        }
예제 #22
0
        private void Constructor(IntPtr objectInitializerPtr)
        {
            Native_UClass.Call_ClassConstructor(ParentClass, objectInitializerPtr);
            FObjectInitializer objectInitializer = new FObjectInitializer(objectInitializerPtr);

            IntPtr sharpStaticClass = Native_USharpClass.StaticClass();
            IntPtr unrealClass      = Native_FObjectInitializer.GetClass(objectInitializerPtr);
            IntPtr sharpClass       = unrealClass;

            while (sharpClass != IntPtr.Zero && !Native_UObjectBaseUtility.IsA(sharpClass, sharpStaticClass))
            {
                sharpClass = Native_UClass.GetSuperClass(sharpClass);
            }

            System.Diagnostics.Debug.Assert(sharpClass != IntPtr.Zero);
        }
예제 #23
0
        public static unsafe void Load()
        {
            vtableRedirects = new List <FunctionRedirect>();
            AddVTableRedirects();

            // We have three classes UDummyObject3 : UDummyObject2 : UDummyObject1 : UObject
            //
            // UDummyObject1 overrides function "X"
            // UDummyObject2 doesn't function "X"
            // UDummyObject3 overrides function "X"
            //
            // Scan the vtable for each dummy object, search for an entry where vtable entry 1==2 && 1!=3
            // - We can assume this entry is our desired vtable index
            // - This may break down in situations where there is multiple inheritance
            // - If this fails to complete properly this will result in a crash (or worse)

            foreach (FunctionRedirect redirect in vtableRedirects)
            {
                IntPtr dummyClass1 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "1");
                IntPtr dummyClass2 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "2");
                IntPtr dummyClass3 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "3");

                IntPtr dummyObject1 = Native_UClass.GetDefaultObject(dummyClass1, true);
                IntPtr dummyObject2 = Native_UClass.GetDefaultObject(dummyClass2, true);
                IntPtr dummyObject3 = Native_UClass.GetDefaultObject(dummyClass3, true);

                IntPtr *dummyVTable1 = *(IntPtr **)dummyObject1;
                IntPtr *dummyVTable2 = *(IntPtr **)dummyObject2;
                IntPtr *dummyVTable3 = *(IntPtr **)dummyObject3;

                for (int i = 0; i < int.MaxValue; i++)
                {
                    IntPtr dummyFunc1 = dummyVTable1[i];
                    IntPtr dummyFunc2 = dummyVTable2[i];
                    IntPtr dummyFunc3 = dummyVTable3[i];

                    if (dummyFunc1 == dummyFunc2 && dummyFunc1 != dummyFunc3)
                    {
                        redirect.NativeCallback = dummyFunc1;
                        redirect.VTableIndex    = i;
                        break;
                    }
                }
            }
        }
예제 #24
0
        public ManagedUnrealClass(Type type, string packageName, string className, IntPtr parentClass)
        {
            Type                        = type;
            PackageName                 = packageName;
            ClassName                   = className;
            ParentClass                 = parentClass;
            ClassConstructor            = Constructor;
            ClassVTableHelperCtorCaller = VTableHelperCtorCaller;
            ClassAddReferencedObjects   = AddReferencedObjects;

            // This is what FKismetCompilerContext::CleanAndSanitizeClass uses
            IntPtr parentClassWithin = Native_UClass.Get_ClassWithin(ParentClass);

            WithinClass = parentClassWithin != IntPtr.Zero ? parentClassWithin : Native_UObject.StaticClass();

            NonUSharpClassParentClass = FindFirstNonUSharpClassParentClass(ParentClass);
            NativeParentClass         = FindFirstNativeParentClass(ParentClass);
        }
예제 #25
0
 /// <summary>
 /// Adds a new default instance map item
 /// </summary>
 /// <param name="newSubobject"></param>
 /// <param name="baseClass"></param>
 public void AddDefaultSubobject(UObject newSubobject, UClass baseClass)
 {
     Native_UClass.AddDefaultSubobject(Address, newSubobject == null ? IntPtr.Zero : newSubobject.Address, baseClass == null ? IntPtr.Zero : baseClass.Address);
 }
예제 #26
0
 public UClass GetSuperClass()
 {
     return(GCHelper.Find <UClass>(Native_UClass.GetSuperClass(Address)));
 }
예제 #27
0
 public int GetDefaultsCount()
 {
     return(Native_UClass.GetDefaultsCount(Address));
 }
예제 #28
0
 /// <summary>
 /// Get the default object from the class
 /// </summary>
 /// <param name="createIfNeeded">if true (default) then the CDO is created if it is NULL.</param>
 /// <returns>the CDO for this class</returns>
 public UObject GetDefaultObject(bool createIfNeeded = true)
 {
     return(GCHelper.Find(Native_UClass.GetDefaultObject(Address, createIfNeeded)));
 }
예제 #29
0
 /// <summary>
 /// Searches for the default instanced object (often a component) by name
 /// </summary>
 /// <param name="toFind"></param>
 /// <returns></returns>
 public UObject GetDefaultSubobjectByName(FName toFind)
 {
     return(GCHelper.Find(Native_UClass.GetDefaultSubobjectByName(Address, ref toFind)));
 }
예제 #30
0
        private static void MetaDataMergeClassCategories(IntPtr metadata, IntPtr obj, Dictionary <FName, string> values)
        {
            // Copying the logic in FClassDeclarationMetaData::MergeClassCategories
            // Engine\Source\Programs\UnrealHeaderTool\Private\ClassDeclarationMetaData.cpp
            // ShowFunctions HideFunctions
            // HideCategories ShowCategories ShowSubCatgories
            // AutoExpandCategories AutoCollapseCategories

            // - How is ShowFunctions / HideFunctions used? Hiding a function doesn't seem to hide it from being
            //   visible in the actions list in Blueprint. If it isn't super important we could skip it.
            // - ShowCategories / HideCategories is important

            // Maybe cache these lists and clear them for each type
            HashSet <string> showCategories             = new HashSet <string>();
            HashSet <string> hideCategories             = new HashSet <string>();
            HashSet <string> showSubCategories          = new HashSet <string>();
            HashSet <string> showFunctions              = new HashSet <string>();
            HashSet <string> hideFunctions              = new HashSet <string>();
            HashSet <string> autoExpandCategories       = new HashSet <string>();
            HashSet <string> autoCollapseCategories     = new HashSet <string>();
            HashSet <string> dontAutoCollapseCategories = new HashSet <string>();
            HashSet <string> classGroupNames            = new HashSet <string>();

            GetMetaDataItems(UMeta.GetKeyName(MDClass.ShowCategories), values, showCategories);
            GetMetaDataItems(UMeta.GetKeyName(MDClass.HideCategories), values, hideCategories);
            GetMetaDataItems(UMeta.GetKeyName(MDClass.ShowFunctions), values, showFunctions);
            GetMetaDataItems(UMeta.GetKeyName(MDClass.HideFunctions), values, hideFunctions);
            GetMetaDataItems(UMeta.GetKeyName(MDClass.AutoExpandCategories), values, autoExpandCategories);
            GetMetaDataItems(UMeta.GetKeyName(MDClass.AutoCollapseCategories), values, autoCollapseCategories);
            GetMetaDataItems(UMeta.GetKeyName(MDClass.DontAutoCollapseCategories), values, dontAutoCollapseCategories);
            GetMetaDataItems(UMeta.GetKeyName(MDClass.ClassGroupNames), values, classGroupNames);

            IntPtr           parentClass                  = Native_UClass.GetSuperClass(obj);
            HashSet <string> parentHideCategories         = new HashSet <string>();
            HashSet <string> parentShowSubCatgories       = new HashSet <string>();
            HashSet <string> parentHideFunctions          = new HashSet <string>();
            HashSet <string> parentAutoExpandCategories   = new HashSet <string>();
            HashSet <string> parentAutoCollapseCategories = new HashSet <string>();

            GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.HideCategories), parentHideCategories);
            GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.ShowCategories), parentShowSubCatgories);
            GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.HideFunctions), parentHideFunctions);
            GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.AutoExpandCategories), parentAutoExpandCategories);
            GetParentMetaDataItems(metadata, parentClass, UMeta.GetKeyName(MDClass.AutoCollapseCategories), parentAutoCollapseCategories);

            // Add parent categories. We store the opposite of HideCategories and HideFunctions in a separate array anyway.
            MetaDataMergeCollection(hideCategories, parentHideCategories);
            MetaDataMergeCollection(showSubCategories, parentShowSubCatgories);
            MetaDataMergeCollection(hideFunctions, parentHideFunctions);

            MetaDataMergeShowCategories(showCategories, hideCategories, showSubCategories);

            // Merge ShowFunctions and HideFunctions
            foreach (string value in showFunctions)
            {
                hideFunctions.Remove(value);
            }
            //showFunctions.Clear();

            // Merge DontAutoCollapseCategories and AutoCollapseCategories
            foreach (string value in dontAutoCollapseCategories)
            {
                autoCollapseCategories.Remove(value);
            }
            //dontAutoCollapseCategories.Clear();

            // The original function then merges ShowFunctions / HideFunctions again? (ShowFunctions will now be empty)

            // Merge AutoExpandCategories and AutoCollapseCategories (we still want to keep AutoExpandCategories though!)
            foreach (string value in autoExpandCategories)
            {
                autoCollapseCategories.Remove(value);
                parentAutoCollapseCategories.Remove(value);
            }

            // Do the same as above but the other way around
            foreach (string value in autoCollapseCategories)
            {
                autoExpandCategories.Remove(value);
                parentAutoExpandCategories.Remove(value);
            }

            // Once AutoExpandCategories and AutoCollapseCategories for THIS class have been parsed, add the parent inherited categories
            MetaDataMergeCollection(autoCollapseCategories, parentAutoCollapseCategories);
            MetaDataMergeCollection(autoExpandCategories, parentAutoExpandCategories);

            SetOrClearMetaDataClassCollection(MDClass.ClassGroupNames, values, classGroupNames);
            SetOrClearMetaDataClassCollection(MDClass.AutoCollapseCategories, values, autoCollapseCategories);
            SetOrClearMetaDataClassCollection(MDClass.HideCategories, values, hideCategories);
            SetOrClearMetaDataClassCollection(MDClass.ShowCategories, values, showSubCategories);
            SetOrClearMetaDataClassCollection(MDClass.HideFunctions, values, hideFunctions);
            SetOrClearMetaDataClassCollection(MDClass.AutoExpandCategories, values, autoExpandCategories);
        }