package au.com.vaadinutils.menu; import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.google.common.base.Preconditions; import com.vaadin.navigator.Navigator; import com.vaadin.navigator.View; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; import au.com.vaadinutils.crud.CrudSecurityManager; import au.com.vaadinutils.crud.security.SecurityManagerFactoryProxy; public class MenuBuilder implements Serializable { private static final long serialVersionUID = 1L; private List<ViewMapping> viewMap; Logger logger = LogManager.getLogger(); public MenuBuilder(Navigator navigator, List<ViewMapping> viewMap) { this.viewMap = viewMap; } public MenuBar build() { return build(new LinkedList<String>()); } /** * * @param topLevelMenuOrder * - specify the order of the top level menus * @return */ public MenuBar build(List<String> topLevelMenuOrder) { MenuBar menubar = new MenuBar(); // used to track top level menus added that have no children attached, // they will be removed at the end. Set<MenuItem> unusedTopLevelMenus = new HashSet<>(); for (String menuName : topLevelMenuOrder) { MenuItem item = menubar.addItem(menuName, null); unusedTopLevelMenus.add(item); } for (final ViewMapping viewmap : this.viewMap) { // We don't add a menu item from the default view. if (!viewmap.getViewName().equals("")) { CrudSecurityManager model = SecurityManagerFactoryProxy.getSecurityManager(viewmap.getView()); if (model.canUserView()) { addMenuItems(menubar, unusedTopLevelMenus, viewmap); } } } // remove unused top level menus for (MenuItem item : unusedTopLevelMenus) { menubar.removeItem(item); } return menubar; } private void addMenuItems(MenuBar menubar, Set<MenuItem> unusedTopLevelMenus, final ViewMapping viewmap) { for (Menu menu : getMenuAnnotations(viewmap.getView())) { String path = createMenuPathString(menu); final String[] pathElements = path.split("\\."); if (pathElements.length == 2) { menu.actionType().createLeafItem(new MenuWrapper(menubar), menu, menu.display(), viewmap, menu.atTop()); } else { MenuItem parentMenuItem = getParentMenuItem(menubar, pathElements[1]); unusedTopLevelMenus.remove(parentMenuItem); recursiveAdd(parentMenuItem, menu, viewmap, menu.display(), Arrays.copyOfRange(pathElements, 2, pathElements.length)); } } } private String createMenuPathString(Menu menu) { String path = menu.path(); // All menus should start with the MENUBAR prefix but we // make it // optional here. if (!path.startsWith(Menu.MENUBAR)) { path = Menu.MENUBAR + "." + path; } // Append the menu item name to the end of the path path += "." + menu.display(); return path; } /** * Navigate down the menu hierarchy until we find the right sport to add the * menu item. * * We create the menu structure as we go if needed. * * @param menuItem * * @param menubar * @param displayName * @param pathElements */ private void recursiveAdd(MenuItem menuItem, Menu menu, final ViewMapping viewmap, String displayName, String[] pathElements) { if (pathElements.length > 0) { if (pathElements.length == 1) { // Time to insert the actual menu item // First see if the item is already on the menubar menu.actionType().createLeafItem(new MenuWrapper(menuItem), menu, menu.display(), viewmap, menu.atTop()); } else { // We need to navigate down further String currentPath = pathElements[0]; MenuItem currentItem = getMenuItem(menuItem, currentPath); // , // currentPath); Preconditions.checkNotNull(currentItem); recursiveAdd(currentItem, menu, viewmap, displayName, Arrays.copyOfRange(pathElements, 1, pathElements.length)); } } } /** * Searches for a menu item. If it doesn't exist it will be created. * * @param parentItem * @param displayName * @param currentPath * @return */ private MenuItem getMenuItem(MenuItem parentItem, final String displayName) // , // String // currentPath) { MenuItem currentItem = findMenuItem(parentItem.getChildren(), displayName); if (currentItem == null) { currentItem = parentItem.addItem(displayName, null); } // if (currentItem != null) // { // currentItem = parentItem.addItem(menuName, new MenuBar.Command() // { // private static final long serialVersionUID = 1L; // // public void menuSelected(MenuItem selectedItem) // { // UI.getCurrent().getNavigator().navigateTo(menuName); // } // }); // // } return currentItem; } /** * Searches for a parement menu item. If it doesn't exist it will be * created. * * @param parentItem * @param menuName * @param currentPath * @return */ private MenuItem getParentMenuItem(MenuBar parentItem, final String displayName) // , // String // currentPath) { MenuItem currentItem = findMenuItem(parentItem.getItems(), displayName); if (currentItem == null) { currentItem = parentItem.addItem(displayName, null); /* * , new MenuBar.Command() { private static final long * serialVersionUID = 1L; * * public void menuSelected(MenuItem selectedItem) { if (!parent) * UI.getCurrent().getNavigator().navigateTo(viewName); } }); */ } return currentItem; } private MenuItem findMenuItem(List<MenuItem> list, String currentPath) { MenuItem currentItem = null; if (list != null) { for (MenuItem menuItem : list) { if (menuItem.getText().equals(currentPath)) { currentItem = menuItem; break; } } } return currentItem; } private List<Menu> getMenuAnnotations(Class<? extends View> viewClass) { List<Menu> menus = new LinkedList<Menu>(); Class<? extends View> aClass = viewClass; Annotation annotation = aClass.getAnnotation(Menu.class); if (annotation instanceof Menu) { menus.add((Menu) annotation); } annotation = aClass.getAnnotation(Menus.class); if (annotation instanceof Menus) { for (Menu menu : ((Menus) annotation).menus()) { menus.add(menu); } } return menus; } }