org.opennms.features.topology.app.internal.menu.MenuBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.features.topology.app.internal.menu.MenuBuilder.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2012-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.features.topology.app.internal.menu;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.opennms.features.topology.api.OperationContext;
import org.opennms.features.topology.api.topo.VertexRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.vaadin.ui.MenuBar;

/**
 * Builder to create a Menu.
 */
public class MenuBuilder {

    private static final Logger LOG = LoggerFactory.getLogger(MenuBuilder.class);

    private static final String TOP_LEVEL_ADDITIONS = "Additions";

    protected List<MenuItem> m_menuBar = Lists.newArrayList();
    private List<String> m_menuOrder = new ArrayList<>();
    private Map<String, List<String>> m_submenuOrderMap = new HashMap<>();

    /**
     * Creates the provided menu path (e.g. File, Save As)
     *
     * @param path The path to create
     */
    public void createPath(String... path) {
        if (path == null || path.length == 0) {
            return;
        }
        // find menu item if exists
        List<String> pathList = Arrays.asList(path);
        MenuItem parent = findMenuItem(null, pathList);
        if (parent == null) {
            // there is no parent for the current path, we create the path
            // find deepest parent for the path
            List<String> pathToCreate = new ArrayList<>(pathList);
            int index = 0;
            while (parent == null && pathList.size() > 1) {
                pathList = pathList.subList(0, pathList.size() - 1);
                parent = findMenuItem(null, pathList);
                index++;
            }
            // If we found a parent, we have to cut the path to create
            if (parent != null) {
                pathToCreate = pathToCreate.subList(index, pathToCreate.size());
            }
            createPath(parent, pathToCreate);
        }
    }

    /**
     * Adds a menu item for path. If path is empty, it is added as a root entry.
     *
     * @param menuItem The item to add.
     * @param parentPath The item to add to
      */
    public void addMenuItem(MenuItem menuItem, String... parentPath) {
        Objects.requireNonNull(menuItem);
        if (parentPath == null || parentPath.length == 0) {
            // root element
            m_menuBar.add(menuItem);
        } else {
            createPath(parentPath); // create path
            MenuItem parent = findMenuItem(null, Arrays.asList(parentPath));
            if (parent == null) { // it should have been created
                throw new IllegalStateException("No parent element was found, but should have been created");
            }
            parent.addChildren(menuItem);
        }
    }

    private MenuItem createPath(MenuItem currentParent, List<String> parentPath) {
        if (currentParent == null) {
            SimpleMenuItem newParent = new SimpleMenuItem(parentPath.get(0));
            m_menuBar.add(newParent);
            currentParent = newParent;
            parentPath = parentPath.subList(1, parentPath.size());
        }
        if (!parentPath.isEmpty()) {
            for (String eachPath : parentPath) {
                SimpleMenuItem childMenuItem = new SimpleMenuItem(eachPath);
                currentParent.addChildren(childMenuItem);
                currentParent = childMenuItem;
            }
        }
        return currentParent;
    }

    private MenuItem findMenuItem(MenuItem parent, List<String> parentPath) {
        if (parentPath.isEmpty()) {
            return parent;
        }
        List<MenuItem> items = getItemsToCheck(parent);
        if (items.isEmpty()) {
            return parent;
        }
        for (MenuItem eachItem : items) {
            if (eachItem.getLabel().equals(parentPath.get(0))) {
                return findMenuItem(eachItem, parentPath.subList(1, parentPath.size()));
            }
        }
        return null;
    }

    private List<MenuItem> getItemsToCheck(MenuItem parent) {
        if (parent == null) {
            return new ArrayList<>(m_menuBar);
        }
        return parent.getChildren();
    }

    public void setTopLevelMenuOrder(List<String> menuOrder) {
        m_menuOrder = menuOrder;
    }

    public void setSubMenuGroupOrder(Map<String, List<String>> submenOrderMap) {
        m_submenuOrderMap = submenOrderMap;
    }

    protected void determineAndApplyOrder() {
        for (MenuItem eachMenuItem : m_menuBar) {
            int order = determineOrderOfMenuEntry(eachMenuItem.getLabel(), m_menuOrder);
            eachMenuItem.setOrder(order);
            applyOrderToChildren(eachMenuItem);
        }
    }

    protected void applyOrderToChildren(final MenuItem parent) {
        final List<String> submenuOrder = getSubmenuOrderList(parent);
        for (MenuItem eachChild : parent.getChildren()) {
            String groupLabel = getGroupForLabel(eachChild.getLabel(), submenuOrder);
            int order = determineOrderOfMenuEntry(groupLabel, submenuOrder);
            eachChild.setOrder(order);
        }
    }

    private List<String> getSubmenuOrderList(MenuItem menuItem) {
        final String labelWithoutProperties = removeLabelProperties(menuItem.getLabel());
        return m_submenuOrderMap.get(labelWithoutProperties) != null ? m_submenuOrderMap.get(labelWithoutProperties)
                : m_submenuOrderMap.containsKey("default") ? m_submenuOrderMap.get("default") : new ArrayList<>();
    }

    private int determineOrderOfMenuEntry(String label, List<String> menus) {
        if (menus.contains(label)) {
            return menus.indexOf(label);
        } else {
            if (menus.contains(TOP_LEVEL_ADDITIONS)) {
                return menus.indexOf(TOP_LEVEL_ADDITIONS);
            } else if (menus.contains(TOP_LEVEL_ADDITIONS.toLowerCase())) {
                return menus.indexOf(TOP_LEVEL_ADDITIONS.toLowerCase());
            } else {
                return menus.size();
            }
        }
    }

    /**
     * Converts the current menu configuration to Vaadin's {@link MenuBar} representation.
     *
     * @param targets The current targets (e.g. the selection)
     * @param operationContext The current {@link OperationContext}.
     * @param hooks Optional hooks to be executed after a menu item's command has been executed.
     * @return The converted {@link MenuBar}
     */
    public MenuBar build(List<VertexRef> targets, OperationContext operationContext, Runnable... hooks) {
        MenuBar menuBar = new MenuBar();
        apply(menuBar, targets, operationContext, hooks);
        return menuBar;
    }

    public void apply(MenuBar rootMenu, List<VertexRef> targets, OperationContext operationContext,
            Runnable... hooks) {
        final List<Runnable> hookList = hooks == null ? Collections.emptyList() : Arrays.asList(hooks);

        // Determine the order of the items in the menu
        determineAndApplyOrder();

        // Start building menubar
        List<MenuItem> rootItems = new ArrayList<>(m_menuBar);
        Collections.sort(rootItems);
        for (MenuItem eachRootElement : rootItems) {
            MenuBar.MenuItem menuItem = addItem(
                    () -> rootMenu.addItem(removeLabelProperties(eachRootElement.getLabel()), null),
                    eachRootElement, targets, operationContext, hookList);

            // Add children
            addItems(menuItem, eachRootElement, targets, operationContext, hookList);
        }
    }

    private void addItems(MenuBar.MenuItem currentMenuItem, MenuItem currentParent, List<VertexRef> targets,
            OperationContext operationContext, List<Runnable> hooks) {
        if (currentMenuItem != null) {
            // Now add children
            List<MenuItem> childItems = new ArrayList<>(currentParent.getChildren());
            Collections.sort(childItems);
            String prevGroup = null;
            MenuBar.MenuItem prevMenuItem = null;
            for (MenuItem eachChild : childItems) {
                // add Separators between groups if the previous group changed and we added an element
                // (otherwise we may end up having multiple separators)
                String currentGroup = getGroupForLabel(eachChild.getLabel(), getSubmenuOrderList(currentParent));
                if (prevGroup != null && prevMenuItem != null && !prevGroup.equals(currentGroup)) {
                    currentMenuItem.addSeparator();
                }
                prevGroup = currentGroup;
                prevMenuItem = addItem(
                        () -> currentMenuItem.addItem(removeLabelProperties(eachChild.getLabel()), null), eachChild,
                        targets, operationContext, hooks);

                // add children
                addItems(prevMenuItem, eachChild, targets, operationContext, hooks);
            }
        }
    }

    protected static String getGroupForLabel(String label, List<String> submenuOrder) {
        String group;
        String[] groupParams = label.split("\\?");

        for (String param : groupParams) {
            if (param.contains("group")) {
                String[] keyValue = param.split("=");
                group = keyValue[1];
                if (submenuOrder.contains(group)) {
                    return group;
                }
                if (submenuOrder.contains(group.toLowerCase())) {
                    return group.toLowerCase();
                }
            }
        }
        return null;
    }

    protected static String removeLabelProperties(String commandKey) {
        if (commandKey.contains("?")) {
            return commandKey.substring(0, commandKey.indexOf('?'));
        } else {
            return commandKey;
        }
    }

    private static MenuBar.MenuItem addItem(ItemAddBehaviour<MenuBar.MenuItem> behaviour, MenuItem eachChildElement,
            List<VertexRef> targets, OperationContext operationContext, List<Runnable> hooks) {
        boolean visibility = eachChildElement.isVisible(targets, operationContext);
        if (visibility) { // only add item if it is actually visible
            final MenuBar.MenuItem childMenuItem = behaviour.addItem();
            final boolean enabled = eachChildElement.isEnabled(targets, operationContext);
            final boolean checkable = eachChildElement.isCheckable();
            childMenuItem.setEnabled(enabled);
            childMenuItem.setCheckable(checkable);
            if (checkable) {
                boolean checked = eachChildElement.isChecked(targets, operationContext);
                childMenuItem.setChecked(checked);
            }

            // Add click behaviour if leaf element
            if (!eachChildElement.getChildren().isEmpty() && eachChildElement.getCommand() != null) {
                LOG.warn("The MenuItem {} is not a leaf but defines a command. The command is ignored.",
                        removeLabelProperties(eachChildElement.getLabel()));
            } else {
                if (eachChildElement.getCommand() != null) {
                    childMenuItem.setCommand((MenuBar.Command) selectedItem -> {
                        eachChildElement.getCommand().execute(targets, operationContext);
                        hooks.forEach(hook -> hook.run());
                    });
                }
            }
            return childMenuItem;
        }
        return null;
    }
}