private static MenuItemsTree <MenuItemOrderingNative> GetModeMenuTree(string modeName)
        {
            var mit = new MenuItemsTree <MenuItemOrderingNative>(new MenuItemOrderingNative());

            mit.onlyLeafHaveValue = false;

            var menuData = GetMenusFromModeFile(GetModeIndexById(modeName));

            if (menuData == null)
            {
                if (modeName == k_DefaultModeId)
                {
                    // if there are no menus in the mode file and we are on the default mode, we use the default modes menus directly
                    foreach (var menuItem in s_MenuItemsDefaultMode.Values)
                    {
                        mit.AddChildSearch(new MenuItemOrderingNative(menuItem.name, menuItem.name, menuItem.priority, menuItem.priority));
                    }
                    return(mit);
                }
                else
                {
                    menuData = new List <JSONObject>();
                }
            }
            s_MenuItemsPerMode.TryGetValue(modeName, out var menuItems);
            GetModeMenuTreeRecursive(mit, menuData, menuItems);

            return(mit);
        }
            // In case of renaming it's risky to use this method because the key is the original name (to be able to find it on MenuController::AddMenuItem) but the tree we might want to add this item to can have a totally different name
            private MenuItemsTree <T> AddChildDirectly(T menuItem)
            {
                var child = new MenuItemsTree <T>(menuItem);

                m_Children.Add(child);
                return(child);
            }
        private static void ExtractMenuItemsFromAttributes()
        {
            s_MenuItemsPerMode     = new Dictionary <string, MenuItemsTree <MenuItemScriptCommand> >();
            s_MenuItemsDefaultMode = new Dictionary <string, MenuItemScriptCommand>();

            var menuItems = TypeCache.GetMethodsWithAttribute <MenuItem>();

            // Order the menu items to start with Unity menus before projects menus. That way if there is a duplicate, the project one is flagged as duplicate
            foreach (var methodInfo in menuItems.OrderBy(m => !Utility.FastStartsWith(m.DeclaringType.Assembly.FullName, "UnityEditor", "unityeditor")))
            {
                if (!ValidateMethodForMenuCommand(methodInfo))
                {
                    continue;
                }
                foreach (var attribute in methodInfo.GetCustomAttributes(typeof(MenuItem), false))
                {
                    string   menuName    = SanitizeMenuItemName(((MenuItem)attribute).menuItem);
                    string[] editorModes = ((MenuItem)attribute).editorModes;
                    foreach (var editorMode in editorModes)
                    {
                        if (editorMode == k_DefaultModeId)
                        {
                            if (s_MenuItemsDefaultMode.TryGetValue(menuName, out var menuItem))
                            {
                                menuItem.Update((MenuItem)attribute, methodInfo);
                            }
                            else
                            {
                                s_MenuItemsDefaultMode.Add(menuName, MenuItemScriptCommand.Initialize(menuName, (MenuItem)attribute, methodInfo));
                            }
                        }
                        else
                        {
                            if (s_MenuItemsPerMode.TryGetValue(editorMode, out var menuItemsPerMode))
                            {
                                MenuItemScriptCommand menuItem = menuItemsPerMode.FindItem(menuName);
                                if (menuItem == null)
                                {
                                    menuItemsPerMode.AddChildSearch(MenuItemScriptCommand.Initialize(menuName, (MenuItem)attribute, methodInfo));
                                }
                                else
                                {
                                    menuItem.Update((MenuItem)attribute, methodInfo);
                                }
                            }
                            else
                            {
                                var newMenusPerMode = new MenuItemsTree <MenuItemScriptCommand>();
                                newMenusPerMode.AddChildSearch(MenuItemScriptCommand.Initialize(menuName, (MenuItem)attribute, methodInfo));
                                s_MenuItemsPerMode.Add(editorMode, newMenusPerMode);
                            }
                        }
                    }
                }
            }
            CleanUpInvalidMenuItems();
        }
            private MenuItemsTree <T> AddIntermediateMenuItem(string pathPart, int priority)
            {
                string name = string.IsNullOrEmpty(key) ? pathPart : key + "/" + pathPart;


                var child = new MenuItemsTree <T>(name, priority);

                m_Children.Add(child);
                return(child);
            }
 private static void InsertMenuFromAttributeIfNecessary(MenuItemsTree <MenuItemOrderingNative> menuTree, HashSet <string> addedMenuItemAttributes, MenuItemsTree <MenuItemScriptCommand> menuItems, string menuName, string currentPrefix, int priority, int parentPriority)
 {
     if (currentPrefix == "")
     {
         bool mustAddNow = false;
         if (menuName == null)
         {
             mustAddNow = true;
         }
         else
         {
             mustAddNow = menuName == k_WindowMenuName || menuName == k_HelpMenuName;
         }
         if (mustAddNow)
         {
             foreach (var menuPerMode in menuItems.menuItemChildrenSorted)
             {
                 if (!addedMenuItemAttributes.Contains(menuPerMode.name) &&
                     (menuName == null ||                                                                        // if there are no menus left we should add every menu item attributes that were not added
                      (!menuPerMode.name.StartsWith(k_HelpMenuName + "/") &&                                     // Creating a help menu from attribute should only happen if there are no menus left since Help is the last
                       !(menuName == k_WindowMenuName && menuPerMode.name.StartsWith(k_WindowMenuName + "/"))))) //Creating a window menu from attribute should happen when there are only Help or no menu left, so if the next is Window then we should not add window menu item
                 {
                     addedMenuItemAttributes.Add(menuPerMode.name);
                     menuTree.AddChildSearch(new MenuItemOrderingNative(menuPerMode.name, menuPerMode.name, menuPerMode.priority, menuPerMode.priority));
                 }
             }
         }
     }
     else
     {
         // find if there is a mode specific menu to insert at this position
         var menuItemTree = menuItems.FindTree(currentPrefix.Substring(0, currentPrefix.Length - 1));
         if (menuItemTree != null)
         {
             foreach (var menuPerMode in menuItemTree.menuItemChildrenSorted)
             {
                 if (!addedMenuItemAttributes.Contains(menuPerMode.name) &&
                     (menuPerMode.priority < priority || menuName == null))
                 {
                     addedMenuItemAttributes.Add(menuPerMode.name);
                     if (menuPerMode.name.Substring(currentPrefix.Length).Contains("/"))
                     {
                         menuTree.AddChildSearch(new MenuItemOrderingNative(menuPerMode.name, menuPerMode.name, menuPerMode.priority, menuPerMode.priority));
                     }
                     else
                     {
                         menuTree.AddChildSearch(new MenuItemOrderingNative(menuPerMode.name, menuPerMode.name, menuPerMode.priority, parentPriority));
                     }
                 }
             }
         }
     }
 }
        // Get the next menu item to add (in the right order)
        private static void GetModeMenuTreeRecursive(MenuItemsTree <MenuItemOrderingNative> menuTree, IList menus, MenuItemsTree <MenuItemScriptCommand> menuItemsFromAttributes,
                                                     HashSet <string> addedMenuItemAttributes = null, string currentPrefix = "", string currentOriginalPrefix = "", int parentPriority = 0, int priority = 100)
        {
            if (menus == null)
            {
                return;
            }
            if (addedMenuItemAttributes == null)
            {
                addedMenuItemAttributes = new HashSet <string>();
            }

            for (int index = 0; index < menus.Count; ++index)
            {
                var menuData = menus[index];
                // separator
                if (menuData == null)
                {
                    priority += 100;
                    continue;
                }
                var menu       = menuData as JSONObject;
                var isInternal = JsonUtils.JsonReadBoolean(menu, k_MenuKeyInternal);
                if (isInternal && !Unsupported.IsDeveloperMode())
                {
                    continue;
                }

                var platform = JsonUtils.JsonReadString(menu, k_MenuKeyPlatform);
                // Check the menu item platform
                if (!String.IsNullOrEmpty(platform) && !Application.platform.ToString().ToLowerInvariant().StartsWith(platform.ToLowerInvariant()))
                {
                    continue;
                }

                var menuName     = JsonUtils.JsonReadString(menu, k_MenuKeyName);
                var fullMenuName = currentPrefix + menuName;
                priority = JsonUtils.JsonReadInt(menu, k_MenuKeyPriority, priority);

                // if there is an original full name (complete path) then use it, else if there is an original name (path following currentOriginalPrefix) then use that, else get the menu name
                var originalName = JsonUtils.JsonReadString(menu, k_MenuKeyOriginalFullName, currentOriginalPrefix + JsonUtils.JsonReadString(menu, k_MenuKeyOriginalName, menuName));

                if (menuItemsFromAttributes != null)
                {
                    InsertMenuFromAttributeIfNecessary(menuTree, addedMenuItemAttributes, menuItemsFromAttributes, menuName, currentPrefix, priority, parentPriority);
                }

                // Check if we are a submenu
                if (menu.Contains(k_MenuKeyChildren))
                {
                    if (menu[k_MenuKeyChildren] is IList children)
                    {
                        // we go deeper
                        menuTree.AddChildSearch(new MenuItemOrderingNative(fullMenuName, originalName, priority, parentPriority));
                        GetModeMenuTreeRecursive(menuTree, children, menuItemsFromAttributes, addedMenuItemAttributes, fullMenuName + "/", originalName + "/", priority);
                    }
                    else if (menu[k_MenuKeyChildren] is string wildCard && wildCard == "*")
                    {
                        var menuToAdd = new MenuItemOrderingNative(fullMenuName, originalName, priority, parentPriority, addChildren: true);
                        if (menu.Contains(k_MenuKeyExclude))
                        {
                            if (menu[k_MenuKeyExclude] is IList excludedMenus)
                            {
                                menuToAdd.childrenToExclude = new string[excludedMenus.Count];
                                for (int excludedMenusIndex = 0; excludedMenusIndex < excludedMenus.Count; ++excludedMenusIndex)
                                {
                                    if (excludedMenus[excludedMenusIndex] is JSONObject excludeMenu)
                                    {
                                        menuToAdd.childrenToExclude[excludedMenusIndex] = originalName + "/" + JsonUtils.JsonReadString(excludeMenu, k_MenuKeyName);
                                    }
                                }
                            }
                        }
                        if (menu.Contains(k_MenuKeyNotExclude))
                        {
                            IList notExcludedMenus = menu[k_MenuKeyNotExclude] as IList;
                            if (notExcludedMenus != null)
                            {
                                menuToAdd.childrenToNotExclude = new string[notExcludedMenus.Count];
                                for (int notExcludedMenusIndex = 0; notExcludedMenusIndex < notExcludedMenus.Count; ++notExcludedMenusIndex)
                                {
                                    var notExcludeMenu = notExcludedMenus[notExcludedMenusIndex] as JSONObject;
                                    if (notExcludeMenu != null)
                                    {
                                        menuToAdd.childrenToNotExclude[notExcludedMenusIndex] = originalName + "/" + JsonUtils.JsonReadString(notExcludeMenu, k_MenuKeyName);
                                    }
                                }
                            }
                        }
                        menuTree.AddChildSearch(menuToAdd);
                    }
                }
                else
                {
                    menuTree.AddChildSearch(new MenuItemOrderingNative(fullMenuName, originalName, priority, parentPriority));
                }
                priority++;
            }
            if (menuItemsFromAttributes != null)
            {
                InsertMenuFromAttributeIfNecessary(menuTree, addedMenuItemAttributes, menuItemsFromAttributes, null, currentPrefix, priority, parentPriority);
            }

            // no more menu at this level
        }