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));
                     }
                 }
             }
         }
     }
 }
        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);
        }
        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();
        }
        // 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
        }