| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.ofbiz.widget.model; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.ofbiz.base.util.Debug; |
| import org.apache.ofbiz.base.util.UtilValidate; |
| import org.apache.ofbiz.base.util.UtilXml; |
| import org.apache.ofbiz.base.util.collections.FlexibleMapAccessor; |
| import org.apache.ofbiz.base.util.string.FlexibleStringExpander; |
| import org.apache.ofbiz.widget.renderer.MenuStringRenderer; |
| import org.w3c.dom.Element; |
| |
| /** |
| * Models the <menu> element. |
| * |
| * @see <code>widget-menu.xsd</code> |
| */ |
| @SuppressWarnings("serial") |
| public class ModelMenu extends ModelWidget { |
| |
| /* |
| * ----------------------------------------------------------------------- * |
| * DEVELOPERS PLEASE READ |
| * ----------------------------------------------------------------------- * |
| * |
| * This model is intended to be a read-only data structure that represents |
| * an XML element. Outside of object construction, the class should not |
| * have any behaviors. |
| * |
| * Instances of this class will be shared by multiple threads - therefore |
| * it is immutable. DO NOT CHANGE THE OBJECT'S STATE AT RUN TIME! |
| * |
| */ |
| |
| public static final String module = ModelMenu.class.getName(); |
| |
| private final List<ModelAction> actions; |
| private final String defaultAlign; |
| private final String defaultAlignStyle; |
| private final FlexibleStringExpander defaultAssociatedContentId; |
| private final String defaultCellWidth; |
| private final String defaultDisabledTitleStyle; |
| private final String defaultEntityName; |
| private final Boolean defaultHideIfSelected; |
| private final String defaultMenuItemName; |
| private final String defaultPermissionEntityAction; |
| private final String defaultPermissionOperation; |
| private final String defaultSelectedStyle; |
| private final String defaultTitleStyle; |
| private final String defaultTooltipStyle; |
| private final String defaultWidgetStyle; |
| private final FlexibleStringExpander extraIndex; |
| private final String fillStyle; |
| private final String id; |
| private final FlexibleStringExpander menuContainerStyleExdr; |
| /** This List will contain one copy of each item for each item name in the order |
| * they were encountered in the service, entity, or menu definition; item definitions |
| * with constraints will also be in this list but may appear multiple times for the same |
| * item name. |
| * |
| * When rendering the menu the order in this list should be following and it should not be |
| * necessary to use the Map. The Map is used when loading the menu definition to keep the |
| * list clean and implement the override features for item definitions. |
| */ |
| private final List<ModelMenuItem> menuItemList; |
| /** This Map is keyed with the item name and has a ModelMenuItem for the value; items |
| * with conditions will not be put in this Map so item definition overrides for items |
| * with conditions is not possible. |
| */ |
| private final Map<String, ModelMenuItem> menuItemMap; |
| private final String menuLocation; |
| private final String menuWidth; |
| private final String orientation; |
| private final ModelMenu parentMenu; |
| private final FlexibleMapAccessor<String> selectedMenuItemContextFieldName; |
| private final String target; |
| private final FlexibleStringExpander title; |
| private final String tooltip; |
| private final String type; |
| |
| /** XML Constructor */ |
| public ModelMenu(Element menuElement, String menuLocation) { |
| super(menuElement); |
| ArrayList<ModelAction> actions = new ArrayList<ModelAction>(); |
| String defaultAlign = ""; |
| String defaultAlignStyle = ""; |
| FlexibleStringExpander defaultAssociatedContentId = FlexibleStringExpander.getInstance(""); |
| String defaultCellWidth = ""; |
| String defaultDisabledTitleStyle = ""; |
| String defaultEntityName = ""; |
| Boolean defaultHideIfSelected = Boolean.FALSE; |
| String defaultMenuItemName = ""; |
| String defaultPermissionEntityAction = ""; |
| String defaultPermissionOperation = ""; |
| String defaultSelectedStyle = ""; |
| String defaultTitleStyle = ""; |
| String defaultTooltipStyle = ""; |
| String defaultWidgetStyle = ""; |
| FlexibleStringExpander extraIndex = FlexibleStringExpander.getInstance(""); |
| String fillStyle = ""; |
| String id = ""; |
| FlexibleStringExpander menuContainerStyleExdr = FlexibleStringExpander.getInstance(""); |
| ArrayList<ModelMenuItem> menuItemList = new ArrayList<ModelMenuItem>(); |
| Map<String, ModelMenuItem> menuItemMap = new HashMap<String, ModelMenuItem>(); |
| String menuWidth = ""; |
| String orientation = "horizontal"; |
| FlexibleMapAccessor<String> selectedMenuItemContextFieldName = FlexibleMapAccessor.getInstance(""); |
| String target = ""; |
| FlexibleStringExpander title = FlexibleStringExpander.getInstance(""); |
| String tooltip = ""; |
| String type = ""; |
| // check if there is a parent menu to inherit from |
| ModelMenu parent = null; |
| String parentResource = menuElement.getAttribute("extends-resource"); |
| String parentMenu = menuElement.getAttribute("extends"); |
| if (!parentMenu.isEmpty()) { |
| if (!parentResource.isEmpty()) { |
| try { |
| parent = MenuFactory.getMenuFromLocation(parentResource, parentMenu); |
| } catch (Exception e) { |
| Debug.logError(e, "Failed to load parent menu definition '" + parentMenu + "' at resource '" + parentResource |
| + "'", module); |
| } |
| } else { |
| parentResource = menuLocation; |
| // try to find a menu definition in the same file |
| Element rootElement = menuElement.getOwnerDocument().getDocumentElement(); |
| List<? extends Element> menuElements = UtilXml.childElementList(rootElement, "menu"); |
| for (Element menuElementEntry : menuElements) { |
| if (menuElementEntry.getAttribute("name").equals(parentMenu)) { |
| parent = new ModelMenu(menuElementEntry, parentResource); |
| break; |
| } |
| } |
| if (parent == null) { |
| Debug.logError("Failed to find parent menu definition '" + parentMenu + "' in same document.", module); |
| } |
| } |
| if (parent != null) { |
| type = parent.type; |
| target = parent.target; |
| id = parent.id; |
| title = parent.title; |
| tooltip = parent.tooltip; |
| defaultEntityName = parent.defaultEntityName; |
| defaultTitleStyle = parent.defaultTitleStyle; |
| defaultSelectedStyle = parent.defaultSelectedStyle; |
| defaultWidgetStyle = parent.defaultWidgetStyle; |
| defaultTooltipStyle = parent.defaultTooltipStyle; |
| defaultMenuItemName = parent.defaultMenuItemName; |
| menuItemList.addAll(parent.menuItemList); |
| menuItemMap.putAll(parent.menuItemMap); |
| defaultPermissionOperation = parent.defaultPermissionOperation; |
| defaultPermissionEntityAction = parent.defaultPermissionEntityAction; |
| defaultAssociatedContentId = parent.defaultAssociatedContentId; |
| defaultHideIfSelected = parent.defaultHideIfSelected; |
| orientation = parent.orientation; |
| menuWidth = parent.menuWidth; |
| defaultCellWidth = parent.defaultCellWidth; |
| defaultDisabledTitleStyle = parent.defaultDisabledTitleStyle; |
| defaultAlign = parent.defaultAlign; |
| defaultAlignStyle = parent.defaultAlignStyle; |
| fillStyle = parent.fillStyle; |
| extraIndex = parent.extraIndex; |
| selectedMenuItemContextFieldName = parent.selectedMenuItemContextFieldName; |
| menuContainerStyleExdr = parent.menuContainerStyleExdr; |
| if (parent.actions != null) { |
| actions.addAll(parent.actions); |
| } |
| } |
| } |
| if (!menuElement.getAttribute("type").isEmpty()) |
| type = menuElement.getAttribute("type"); |
| if (!menuElement.getAttribute("target").isEmpty()) |
| target = menuElement.getAttribute("target"); |
| if (!menuElement.getAttribute("id").isEmpty()) |
| id = menuElement.getAttribute("id"); |
| if (!menuElement.getAttribute("title").isEmpty()) |
| title = FlexibleStringExpander.getInstance(menuElement.getAttribute("title")); |
| if (!menuElement.getAttribute("tooltip").isEmpty()) |
| tooltip = menuElement.getAttribute("tooltip"); |
| if (!menuElement.getAttribute("default-entity-name").isEmpty()) |
| defaultEntityName = menuElement.getAttribute("default-entity-name"); |
| if (!menuElement.getAttribute("default-title-style").isEmpty()) |
| defaultTitleStyle = menuElement.getAttribute("default-title-style"); |
| if (!menuElement.getAttribute("default-selected-style").isEmpty()) |
| defaultSelectedStyle = menuElement.getAttribute("default-selected-style"); |
| if (!menuElement.getAttribute("default-widget-style").isEmpty()) |
| defaultWidgetStyle = menuElement.getAttribute("default-widget-style"); |
| if (!menuElement.getAttribute("default-tooltip-style").isEmpty()) |
| defaultTooltipStyle = menuElement.getAttribute("default-tooltip-style"); |
| if (!menuElement.getAttribute("default-menu-item-name").isEmpty()) |
| defaultMenuItemName = menuElement.getAttribute("default-menu-item-name"); |
| if (!menuElement.getAttribute("default-permission-operation").isEmpty()) |
| defaultPermissionOperation = menuElement.getAttribute("default-permission-operation"); |
| if (!menuElement.getAttribute("default-permission-entity-action").isEmpty()) |
| defaultPermissionEntityAction = menuElement.getAttribute("default-permission-entity-action"); |
| if (!menuElement.getAttribute("default-associated-content-id").isEmpty()) |
| defaultAssociatedContentId = FlexibleStringExpander.getInstance(menuElement |
| .getAttribute("default-associated-content-id")); |
| if (!menuElement.getAttribute("orientation").isEmpty()) |
| orientation = menuElement.getAttribute("orientation"); |
| if (!menuElement.getAttribute("menu-width").isEmpty()) |
| menuWidth = menuElement.getAttribute("menu-width"); |
| if (!menuElement.getAttribute("default-cell-width").isEmpty()) |
| defaultCellWidth = menuElement.getAttribute("default-cell-width"); |
| if (!menuElement.getAttribute("default-hide-if-selected").isEmpty()) |
| defaultHideIfSelected = "true".equals(menuElement.getAttribute("default-hide-if-selected").isEmpty()); |
| if (!menuElement.getAttribute("default-disabled-title-style").isEmpty()) |
| defaultDisabledTitleStyle = menuElement.getAttribute("default-disabled-title-style"); |
| if (!menuElement.getAttribute("selected-menuitem-context-field-name").isEmpty()) |
| selectedMenuItemContextFieldName = FlexibleMapAccessor.getInstance(menuElement |
| .getAttribute("selected-menuitem-context-field-name")); |
| if (!menuElement.getAttribute("menu-container-style").isEmpty()) |
| menuContainerStyleExdr = FlexibleStringExpander.getInstance(menuElement.getAttribute("menu-container-style")); |
| if (!menuElement.getAttribute("default-align").isEmpty()) |
| defaultAlign = menuElement.getAttribute("default-align"); |
| if (!menuElement.getAttribute("default-align-style").isEmpty()) |
| defaultAlignStyle = menuElement.getAttribute("default-align-style"); |
| if (!menuElement.getAttribute("fill-style").isEmpty()) |
| fillStyle = menuElement.getAttribute("fill-style"); |
| if (!menuElement.getAttribute("extra-index").isEmpty()) |
| extraIndex = FlexibleStringExpander.getInstance(menuElement.getAttribute("extra-index")); |
| // read all actions under the "actions" element |
| Element actionsElement = UtilXml.firstChildElement(menuElement, "actions"); |
| if (actionsElement != null) { |
| actions.addAll(ModelMenuAction.readSubActions(this, actionsElement)); |
| } |
| actions.trimToSize(); |
| this.actions = Collections.unmodifiableList(actions); |
| this.defaultAlign = defaultAlign; |
| this.defaultAlignStyle = defaultAlignStyle; |
| this.defaultAssociatedContentId = defaultAssociatedContentId; |
| this.defaultCellWidth = defaultCellWidth; |
| this.defaultDisabledTitleStyle = defaultDisabledTitleStyle; |
| this.defaultEntityName = defaultEntityName; |
| this.defaultHideIfSelected = defaultHideIfSelected; |
| this.defaultMenuItemName = defaultMenuItemName; |
| this.defaultPermissionEntityAction = defaultPermissionEntityAction; |
| this.defaultPermissionOperation = defaultPermissionOperation; |
| this.defaultSelectedStyle = defaultSelectedStyle; |
| this.defaultTitleStyle = defaultTitleStyle; |
| this.defaultTooltipStyle = defaultTooltipStyle; |
| this.defaultWidgetStyle = defaultWidgetStyle; |
| this.extraIndex = extraIndex; |
| this.fillStyle = fillStyle; |
| this.id = id; |
| this.menuContainerStyleExdr = menuContainerStyleExdr; |
| List<? extends Element> itemElements = UtilXml.childElementList(menuElement, "menu-item"); |
| for (Element itemElement : itemElements) { |
| ModelMenuItem modelMenuItem = new ModelMenuItem(itemElement, this); |
| addUpdateMenuItem(modelMenuItem, menuItemList, menuItemMap); |
| } |
| menuItemList.trimToSize(); |
| this.menuItemList = Collections.unmodifiableList(menuItemList); |
| this.menuItemMap = Collections.unmodifiableMap(menuItemMap); |
| this.menuLocation = menuLocation; |
| this.menuWidth = menuWidth; |
| this.orientation = orientation; |
| this.parentMenu = parent; |
| this.selectedMenuItemContextFieldName = selectedMenuItemContextFieldName; |
| this.target = target; |
| this.title = title; |
| this.tooltip = tooltip; |
| this.type = type; |
| } |
| |
| @Override |
| public void accept(ModelWidgetVisitor visitor) throws Exception { |
| visitor.visit(this); |
| } |
| |
| /** |
| * add/override modelMenuItem using the menuItemList and menuItemMap |
| * |
| */ |
| private void addUpdateMenuItem(ModelMenuItem modelMenuItem, List<ModelMenuItem> menuItemList, |
| Map<String, ModelMenuItem> menuItemMap) { |
| ModelMenuItem existingMenuItem = menuItemMap.get(modelMenuItem.getName()); |
| if (existingMenuItem != null) { |
| // does exist, update the item by doing a merge/override |
| ModelMenuItem mergedMenuItem = existingMenuItem.mergeOverrideModelMenuItem(modelMenuItem); |
| int existingItemIndex = menuItemList.indexOf(existingMenuItem); |
| menuItemList.set(existingItemIndex, mergedMenuItem); |
| menuItemMap.put(modelMenuItem.getName(), mergedMenuItem); |
| } else { |
| // does not exist, add to Map |
| menuItemList.add(modelMenuItem); |
| menuItemMap.put(modelMenuItem.getName(), modelMenuItem); |
| } |
| } |
| |
| public List<ModelAction> getActions() { |
| return actions; |
| } |
| |
| @Override |
| public String getBoundaryCommentName() { |
| return menuLocation + "#" + getName(); |
| } |
| |
| public String getCurrentMenuName(Map<String, Object> context) { |
| return getName(); |
| } |
| |
| public String getDefaultAlign() { |
| return this.defaultAlign; |
| } |
| |
| public String getDefaultAlignStyle() { |
| return this.defaultAlignStyle; |
| } |
| |
| public FlexibleStringExpander getDefaultAssociatedContentId() { |
| return defaultAssociatedContentId; |
| } |
| |
| public String getDefaultAssociatedContentId(Map<String, Object> context) { |
| return defaultAssociatedContentId.expandString(context); |
| } |
| |
| public String getDefaultCellWidth() { |
| return this.defaultCellWidth; |
| } |
| |
| public String getDefaultDisabledTitleStyle() { |
| return this.defaultDisabledTitleStyle; |
| } |
| |
| public String getDefaultEntityName() { |
| return this.defaultEntityName; |
| } |
| |
| public Boolean getDefaultHideIfSelected() { |
| return this.defaultHideIfSelected; |
| } |
| |
| public String getDefaultMenuItemName() { |
| return this.defaultMenuItemName; |
| } |
| |
| public String getDefaultPermissionEntityAction() { |
| return this.defaultPermissionEntityAction; |
| } |
| |
| public String getDefaultPermissionOperation() { |
| return this.defaultPermissionOperation; |
| } |
| |
| public String getDefaultSelectedStyle() { |
| return this.defaultSelectedStyle; |
| } |
| |
| public String getDefaultTitleStyle() { |
| return this.defaultTitleStyle; |
| } |
| |
| public String getDefaultTooltipStyle() { |
| return this.defaultTooltipStyle; |
| } |
| |
| public String getDefaultWidgetStyle() { |
| return this.defaultWidgetStyle; |
| } |
| |
| public FlexibleStringExpander getExtraIndex() { |
| return extraIndex; |
| } |
| |
| public String getExtraIndex(Map<String, Object> context) { |
| try { |
| return extraIndex.expandString(context); |
| } catch (Exception ex) { |
| return ""; |
| } |
| } |
| |
| public String getFillStyle() { |
| return this.fillStyle; |
| } |
| |
| public String getId() { |
| return this.id; |
| } |
| |
| public String getMenuContainerStyle(Map<String, Object> context) { |
| return menuContainerStyleExdr.expandString(context); |
| } |
| |
| public FlexibleStringExpander getMenuContainerStyleExdr() { |
| return menuContainerStyleExdr; |
| } |
| |
| public List<ModelMenuItem> getMenuItemList() { |
| return menuItemList; |
| } |
| |
| public Map<String, ModelMenuItem> getMenuItemMap() { |
| return menuItemMap; |
| } |
| |
| public String getMenuLocation() { |
| return menuLocation; |
| } |
| |
| public String getMenuWidth() { |
| return this.menuWidth; |
| } |
| |
| public ModelMenuItem getModelMenuItemByName(String name) { |
| return this.menuItemMap.get(name); |
| } |
| |
| public String getOrientation() { |
| return this.orientation; |
| } |
| |
| public ModelMenu getParentMenu() { |
| return parentMenu; |
| } |
| |
| public FlexibleMapAccessor<String> getSelectedMenuItemContextFieldName() { |
| return selectedMenuItemContextFieldName; |
| } |
| |
| public String getSelectedMenuItemContextFieldName(Map<String, Object> context) { |
| String menuItemName = this.selectedMenuItemContextFieldName.get(context); |
| if (UtilValidate.isEmpty(menuItemName)) { |
| return this.defaultMenuItemName; |
| } |
| return menuItemName; |
| } |
| |
| public String getTarget() { |
| return target; |
| } |
| |
| public FlexibleStringExpander getTitle() { |
| return title; |
| } |
| |
| public String getTitle(Map<String, Object> context) { |
| return title.expandString(context); |
| } |
| |
| public String getTooltip() { |
| return this.tooltip; |
| } |
| |
| public String getType() { |
| return this.type; |
| } |
| |
| public int renderedMenuItemCount(Map<String, Object> context) { |
| int count = 0; |
| for (ModelMenuItem item : this.menuItemList) { |
| if (item.shouldBeRendered(context)) |
| count++; |
| } |
| return count; |
| } |
| |
| /** |
| * Renders this menu to a String, i.e. in a text format, as defined with the |
| * MenuStringRenderer implementation. |
| * |
| * @param writer The Writer that the menu text will be written to |
| * @param context Map containing the menu context; the following are |
| * reserved words in this context: parameters (Map), isError (Boolean), |
| * itemIndex (Integer, for lists only, otherwise null), menuName |
| * (String, optional alternate name for menu, defaults to the |
| * value of the name attribute) |
| * @param menuStringRenderer An implementation of the MenuStringRenderer |
| * interface that is responsible for the actual text generation for |
| * different menu elements; implementing you own makes it possible to |
| * use the same menu definitions for many types of menu UIs |
| */ |
| public void renderMenuString(Appendable writer, Map<String, Object> context, MenuStringRenderer menuStringRenderer) |
| throws IOException { |
| AbstractModelAction.runSubActions(this.actions, context); |
| if ("simple".equals(this.type)) { |
| this.renderSimpleMenuString(writer, context, menuStringRenderer); |
| } else { |
| throw new IllegalArgumentException("The type " + this.getType() + " is not supported for menu with name " |
| + this.getName()); |
| } |
| } |
| |
| public void renderSimpleMenuString(Appendable writer, Map<String, Object> context, MenuStringRenderer menuStringRenderer) |
| throws IOException { |
| // render menu open |
| menuStringRenderer.renderMenuOpen(writer, context, this); |
| |
| // render formatting wrapper open |
| menuStringRenderer.renderFormatSimpleWrapperOpen(writer, context, this); |
| |
| // render each menuItem row, except hidden & ignored rows |
| for (ModelMenuItem item : this.menuItemList) { |
| item.renderMenuItemString(writer, context, menuStringRenderer); |
| } |
| // render formatting wrapper close |
| menuStringRenderer.renderFormatSimpleWrapperClose(writer, context, this); |
| |
| // render menu close |
| menuStringRenderer.renderMenuClose(writer, context, this); |
| } |
| |
| public void runActions(Map<String, Object> context) { |
| AbstractModelAction.runSubActions(this.actions, context); |
| } |
| } |