Java tutorial
/* * Copyright (c) 2008-2016 Haulmont. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.haulmont.cuba.gui.config; import com.haulmont.bali.util.Dom4j; import com.haulmont.cuba.core.global.AppBeans; import com.haulmont.cuba.core.global.Messages; import com.haulmont.cuba.core.global.Resources; import com.haulmont.cuba.core.sys.AppContext; import com.haulmont.cuba.gui.components.KeyCombination; import com.haulmont.cuba.gui.icons.Icons; import com.haulmont.cuba.gui.theme.ThemeConstants; import com.haulmont.cuba.gui.theme.ThemeConstantsManager; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrTokenizer; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import static com.haulmont.cuba.gui.icons.Icons.ICON_NAME_REGEX; /** * GenericUI class holding information about the main menu structure. */ @Component(MenuConfig.NAME) public class MenuConfig { private final Logger log = LoggerFactory.getLogger(MenuConfig.class); public static final String NAME = "cuba_MenuConfig"; public static final String MENU_CONFIG_XML_PROP = "cuba.menuConfig"; protected List<MenuItem> rootItems = new ArrayList<>(); @Inject protected Resources resources; @Inject protected Messages messages; @Inject protected ThemeConstantsManager themeConstantsManager; protected volatile boolean initialized; protected ReadWriteLock lock = new ReentrantReadWriteLock(); /** * Localized menu item caption. * * @param id screen ID as defined in <code>screens.xml</code> * @deprecated Use {@link MenuConfig#getItemCaption(String)} */ @Deprecated public static String getMenuItemCaption(String id) { MenuConfig menuConfig = AppBeans.get(MenuConfig.NAME); return menuConfig.getItemCaption(id); } public String getItemCaption(String id) { return messages.getMainMessage("menu-config." + id); } protected void checkInitialized() { if (!initialized) { lock.readLock().unlock(); lock.writeLock().lock(); try { if (!initialized) { init(); initialized = true; } } finally { lock.readLock().lock(); lock.writeLock().unlock(); } } } protected void init() { rootItems.clear(); String configName = AppContext.getProperty(MENU_CONFIG_XML_PROP); StrTokenizer tokenizer = new StrTokenizer(configName); for (String location : tokenizer.getTokenArray()) { Resource resource = resources.getResource(location); if (resource.exists()) { InputStream stream = null; try { stream = resource.getInputStream(); Element rootElement = Dom4j.readDocument(stream).getRootElement(); loadMenuItems(rootElement, null); } catch (IOException e) { throw new RuntimeException("Unable to read menu config", e); } finally { IOUtils.closeQuietly(stream); } } else { log.warn("Resource {} not found, ignore it", location); } } } /** * Make the config to reload screens on next request. */ public void reset() { initialized = false; } /** * Main menu root items */ public List<MenuItem> getRootItems() { lock.readLock().lock(); try { checkInitialized(); return Collections.unmodifiableList(rootItems); } finally { lock.readLock().unlock(); } } protected void loadMenuItems(Element parentElement, MenuItem parentItem) { //noinspection unchecked for (Element element : ((List<Element>) parentElement.elements())) { MenuItem menuItem = null; MenuItem currentParentItem = parentItem; MenuItem nextToItem = null; boolean before = true; String nextTo = element.attributeValue("insertBefore"); if (StringUtils.isBlank(nextTo)) { before = false; nextTo = element.attributeValue("insertAfter"); } if (!StringUtils.isBlank(nextTo)) { for (MenuItem rootItem : rootItems) { nextToItem = findItem(nextTo, rootItem); if (nextToItem != null) { if (nextToItem.getParent() != null) currentParentItem = nextToItem.getParent(); break; } } } if ("menu".equals(element.getName())) { String id = element.attributeValue("id"); if (StringUtils.isBlank(id)) { log.warn("Invalid menu-config: 'id' attribute not defined"); } menuItem = new MenuItem(currentParentItem, id); menuItem.setMenu(true); menuItem.setDescriptor(element); loadIcon(element, menuItem); loadShortcut(menuItem, element); loadStylename(element, menuItem); loadExpanded(element, menuItem); loadDescription(element, menuItem); loadMenuItems(element, menuItem); } else if ("item".equals(element.getName())) { menuItem = createMenuItem(element, currentParentItem); if (menuItem == null) { continue; } } else if ("separator".equals(element.getName())) { String id = element.attributeValue("id"); if (StringUtils.isBlank(id)) id = "-"; menuItem = new MenuItem(currentParentItem, id); menuItem.setSeparator(true); if (!StringUtils.isBlank(id)) { menuItem.setDescriptor(element); } } else { log.warn(String.format("Unknown tag '%s' in menu-config", element.getName())); } if (currentParentItem != null) { addItem(currentParentItem.getChildren(), menuItem, nextToItem, before); } else { addItem(rootItems, menuItem, nextToItem, before); } } } protected MenuItem createMenuItem(Element element, MenuItem currentParentItem) { String id = element.attributeValue("id"); String idFromActions; String screen = element.attributeValue("screen"); idFromActions = StringUtils.isNotEmpty(screen) ? screen : null; String runnableClass = element.attributeValue("class"); checkDuplicateAction(idFromActions, runnableClass); idFromActions = StringUtils.isNotEmpty(runnableClass) ? runnableClass : idFromActions; String bean = element.attributeValue("bean"); String beanMethod = element.attributeValue("beanMethod"); if (StringUtils.isNotEmpty(bean) && StringUtils.isEmpty(beanMethod) || StringUtils.isEmpty(bean) && StringUtils.isNotEmpty(beanMethod)) { throw new IllegalStateException("Both bean and beanMethod should be defined."); } checkDuplicateAction(idFromActions, bean, beanMethod); String fqn = bean + "#" + beanMethod; idFromActions = StringUtils.isNotEmpty(bean) && StringUtils.isNotEmpty(beanMethod) ? fqn : idFromActions; if (StringUtils.isEmpty(id) && StringUtils.isEmpty(idFromActions)) { throw new IllegalStateException("MenuItem should have at least one action"); } if (StringUtils.isEmpty(id) && StringUtils.isNotEmpty(idFromActions)) { id = idFromActions; } if (StringUtils.isNotEmpty(id) && StringUtils.isEmpty(idFromActions)) { screen = id; } MenuItem menuItem = new MenuItem(currentParentItem, id); menuItem.setScreen(screen); menuItem.setRunnableClass(runnableClass); menuItem.setBean(bean); menuItem.setBeanMethod(beanMethod); menuItem.setDescriptor(element); loadIcon(element, menuItem); loadShortcut(menuItem, element); loadStylename(element, menuItem); loadDescription(element, menuItem); return menuItem; } protected void checkDuplicateAction(String menuItemId, String... actionDefinition) { boolean actionDefined = true; for (String s : actionDefinition) { actionDefined &= StringUtils.isNotEmpty(s); } if (StringUtils.isNotEmpty(menuItemId) && actionDefined) { throw new IllegalStateException("MenuItem can't have more than one action."); } } protected void loadExpanded(Element element, MenuItem menuItem) { String expanded = element.attributeValue("expanded"); if (StringUtils.isNotEmpty(expanded)) { menuItem.setExpanded(Boolean.parseBoolean(expanded)); } } protected void loadDescription(Element element, MenuItem menuItem) { String description = element.attributeValue("description"); if (StringUtils.isNotBlank(description)) { menuItem.setDescription(description); } } protected void loadStylename(Element element, MenuItem menuItem) { String stylename = element.attributeValue("stylename"); if (StringUtils.isNotBlank(stylename)) { menuItem.setStylename(stylename); } } protected void loadIcon(Element element, MenuItem menuItem) { String icon = element.attributeValue("icon"); if (StringUtils.isNotEmpty(icon)) { menuItem.setIcon(getIconPath(icon)); } } protected String getIconPath(String icon) { if (icon == null || icon.isEmpty()) { return null; } String iconPath = null; if (ICON_NAME_REGEX.matcher(icon).matches()) { iconPath = AppBeans.get(Icons.class).get(icon); } if (StringUtils.isEmpty(iconPath)) { String themeValue = loadThemeString(icon); iconPath = loadResourceString(themeValue); } return iconPath; } protected String loadResourceString(String caption) { return messages.getTools().loadString(messages.getMainMessagePack(), caption); } protected String loadThemeString(String value) { if (value != null && value.startsWith(ThemeConstants.PREFIX)) { value = themeConstantsManager.getConstants().get(value.substring(ThemeConstants.PREFIX.length())); } return value; } protected void addItem(List<MenuItem> items, MenuItem menuItem, MenuItem beforeItem, boolean before) { if (beforeItem == null) { items.add(menuItem); } else { int i = items.indexOf(beforeItem); if (before) items.add(i, menuItem); else items.add(i + 1, menuItem); } } protected MenuItem findItem(String id, MenuItem item) { if (id.equals(item.getId())) return item; else if (!item.getChildren().isEmpty()) { for (MenuItem child : item.getChildren()) { MenuItem menuItem = findItem(id, child); if (menuItem != null) return menuItem; } } return null; } protected void loadShortcut(MenuItem menuItem, Element element) { String shortcut = element.attributeValue("shortcut"); if (shortcut == null || shortcut.isEmpty()) { return; } // If the shortcut string looks like a property, try to get it from the application properties if (shortcut.startsWith("${") && shortcut.endsWith("}")) { String property = AppContext.getProperty(shortcut.substring(2, shortcut.length() - 1)); if (!StringUtils.isEmpty(property)) shortcut = property; else return; } try { menuItem.setShortcut(KeyCombination.create(shortcut)); } catch (IllegalArgumentException e) { log.warn("Invalid menu shortcut value: '" + shortcut + "'"); } } }