/******************************************************************************* * 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.Locale; 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.string.FlexibleStringExpander; import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.widget.model.CommonWidgetModels.AutoEntityParameters; import org.apache.ofbiz.widget.model.CommonWidgetModels.AutoServiceParameters; import org.apache.ofbiz.widget.model.CommonWidgetModels.Image; import org.apache.ofbiz.widget.model.CommonWidgetModels.Link; import org.apache.ofbiz.widget.model.CommonWidgetModels.Parameter; import org.apache.ofbiz.widget.portal.PortalPageWorker; import org.apache.ofbiz.widget.renderer.MenuStringRenderer; import org.w3c.dom.Element; /** * Models the <menu-item> element. * * @see <code>widget-menu.xsd</code> */ @SuppressWarnings("serial") public class ModelMenuItem 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 = ModelMenuItem.class.getName(); private final List<ModelAction> actions; private final String align; private final String alignStyle; private final FlexibleStringExpander associatedContentId; private final String cellWidth; private final ModelMenuCondition condition; private final String disabledTitleStyle; private final String disableIfEmpty; private final String entityName; private final Boolean hideIfSelected; private final MenuLink link; private final List<ModelMenuItem> menuItemList; private final ModelMenu modelMenu; private final String overrideName; private final ModelMenuItem parentMenuItem; private final FlexibleStringExpander parentPortalPageId; private final Integer position; private final String selectedStyle; private final String subMenu; private final FlexibleStringExpander title; private final String titleStyle; private final FlexibleStringExpander tooltip; private final String tooltipStyle; private final String widgetStyle; // ===== CONSTRUCTORS ===== public ModelMenuItem(Element menuItemElement, ModelMenu modelMenu) { this(menuItemElement, modelMenu, null); } private ModelMenuItem(Element menuItemElement, ModelMenu modelMenu, ModelMenuItem parentMenuItem) { super(menuItemElement); this.modelMenu = modelMenu; this.parentMenuItem = parentMenuItem; this.entityName = menuItemElement.getAttribute("entity-name"); this.title = FlexibleStringExpander.getInstance(menuItemElement.getAttribute("title")); this.tooltip = FlexibleStringExpander.getInstance(menuItemElement.getAttribute("tooltip")); this.parentPortalPageId = FlexibleStringExpander.getInstance(menuItemElement.getAttribute("parent-portal-page-value")); this.titleStyle = menuItemElement.getAttribute("title-style"); this.disabledTitleStyle = menuItemElement.getAttribute("disabled-title-style"); this.widgetStyle = menuItemElement.getAttribute("widget-style"); this.tooltipStyle = menuItemElement.getAttribute("tooltip-style"); this.selectedStyle = menuItemElement.getAttribute("selected-style"); String hideIfSelected = menuItemElement.getAttribute("hide-if-selected"); if (!hideIfSelected.isEmpty()) if (hideIfSelected.equalsIgnoreCase("true")) this.hideIfSelected = Boolean.TRUE; else this.hideIfSelected = Boolean.FALSE; else this.hideIfSelected = null; this.disableIfEmpty = menuItemElement.getAttribute("disable-if-empty"); this.align = menuItemElement.getAttribute("align"); this.alignStyle = menuItemElement.getAttribute("align-style"); Integer position = null; String positionStr = menuItemElement.getAttribute("position"); if (!positionStr.isEmpty()) { try { position = Integer.valueOf(positionStr); } catch (Exception e) { Debug.logError(e, "Could not convert position attribute of the field element to an integer: [" + positionStr + "], using the default of the menu renderer", module); position = null; } } this.position = position; this.associatedContentId = FlexibleStringExpander.getInstance(menuItemElement.getAttribute("associated-content-id")); this.cellWidth = menuItemElement.getAttribute("cell-width"); this.subMenu = menuItemElement.getAttribute("sub-menu"); Element linkElement = UtilXml.firstChildElement(menuItemElement, "link"); if (linkElement != null) { this.link = new MenuLink(linkElement, this); } else { this.link = null; } // read in add item defs, add/override one by one using the menuItemList and menuItemMap List<? extends Element> itemElements = UtilXml.childElementList(menuItemElement, "menu-item"); if (!itemElements.isEmpty()) { ArrayList<ModelMenuItem> menuItemList = new ArrayList<ModelMenuItem>(); Map<String, ModelMenuItem> menuItemMap = new HashMap<String, ModelMenuItem>(); for (Element itemElement : itemElements) { ModelMenuItem modelMenuItem = new ModelMenuItem(itemElement, modelMenu, this); addUpdateMenuItem(modelMenuItem, menuItemList, menuItemMap); } menuItemList.trimToSize(); this.menuItemList = Collections.unmodifiableList(menuItemList); } else { this.menuItemList = Collections.emptyList(); } // read condition under the "condition" element Element conditionElement = UtilXml.firstChildElement(menuItemElement, "condition"); if (conditionElement != null) { conditionElement = UtilXml.firstChildElement(conditionElement); this.condition = new ModelMenuCondition(this, conditionElement); } else { this.condition = null; } // read all actions under the "actions" element Element actionsElement = UtilXml.firstChildElement(conditionElement, "actions"); if (actionsElement != null) { this.actions = AbstractModelAction.readSubActions(this, actionsElement); } else { this.actions = Collections.emptyList(); } this.overrideName = ""; } // Portal constructor private ModelMenuItem(GenericValue portalPage, ModelMenuItem parentMenuItem, Locale locale) { super(portalPage.getString("portalPageId")); this.actions = Collections.emptyList(); this.align = ""; this.alignStyle = ""; this.associatedContentId = FlexibleStringExpander.getInstance(""); this.cellWidth = ""; this.condition = null; this.disabledTitleStyle = ""; this.disableIfEmpty = ""; this.entityName = ""; this.hideIfSelected = null; this.menuItemList = Collections.emptyList(); this.overrideName = ""; this.parentMenuItem = null; this.parentPortalPageId = FlexibleStringExpander.getInstance(portalPage.getString("parentPortalPageId")); this.position = null; this.selectedStyle = ""; this.subMenu = ""; this.title = FlexibleStringExpander.getInstance((String) portalPage.get("portalPageName", locale)); this.titleStyle = ""; this.tooltip = FlexibleStringExpander.getInstance(""); this.tooltipStyle = ""; this.widgetStyle = ""; this.link = new MenuLink(portalPage, parentMenuItem, locale); this.modelMenu = parentMenuItem.modelMenu; } // Merge constructor private ModelMenuItem(ModelMenuItem existingMenuItem, ModelMenuItem overrideMenuItem) { super(existingMenuItem.getName()); this.modelMenu = existingMenuItem.modelMenu; if (UtilValidate.isNotEmpty(overrideMenuItem.getName())) { this.overrideName = overrideMenuItem.getName(); } else { this.overrideName = existingMenuItem.getName(); } if (UtilValidate.isNotEmpty(overrideMenuItem.entityName)) { this.entityName = overrideMenuItem.entityName; } else { this.entityName = existingMenuItem.entityName; } if (UtilValidate.isNotEmpty(overrideMenuItem.parentPortalPageId)) { this.parentPortalPageId = overrideMenuItem.parentPortalPageId; } else { this.parentPortalPageId = existingMenuItem.parentPortalPageId; } if (UtilValidate.isNotEmpty(overrideMenuItem.title)) { this.title = overrideMenuItem.title; } else { this.title = existingMenuItem.title; } if (UtilValidate.isNotEmpty(overrideMenuItem.tooltip)) { this.tooltip = overrideMenuItem.tooltip; } else { this.tooltip = existingMenuItem.tooltip; } if (UtilValidate.isNotEmpty(overrideMenuItem.titleStyle)) { this.titleStyle = overrideMenuItem.titleStyle; } else { this.titleStyle = existingMenuItem.titleStyle; } if (UtilValidate.isNotEmpty(overrideMenuItem.selectedStyle)) { this.selectedStyle = overrideMenuItem.selectedStyle; } else { this.selectedStyle = existingMenuItem.selectedStyle; } if (UtilValidate.isNotEmpty(overrideMenuItem.widgetStyle)) { this.widgetStyle = overrideMenuItem.widgetStyle; } else { this.widgetStyle = existingMenuItem.widgetStyle; } if (overrideMenuItem.position != null) { this.position = overrideMenuItem.position; } else { this.position = existingMenuItem.position; } this.actions = existingMenuItem.actions; this.align = existingMenuItem.align; this.alignStyle = existingMenuItem.alignStyle; this.associatedContentId = existingMenuItem.associatedContentId; this.cellWidth = existingMenuItem.cellWidth; this.condition = existingMenuItem.condition; this.disabledTitleStyle = existingMenuItem.disabledTitleStyle; this.disableIfEmpty = existingMenuItem.disableIfEmpty; this.hideIfSelected = existingMenuItem.hideIfSelected; this.menuItemList = existingMenuItem.menuItemList; this.parentMenuItem = existingMenuItem.parentMenuItem; this.subMenu = existingMenuItem.subMenu; this.tooltipStyle = existingMenuItem.tooltipStyle; this.link = existingMenuItem.link; } @Override public void accept(ModelWidgetVisitor visitor) throws Exception { visitor.visit(this); } 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 List and Map menuItemList.add(modelMenuItem); menuItemMap.put(modelMenuItem.getName(), modelMenuItem); } } public List<ModelAction> getActions() { return actions; } public String getAlign() { if (!this.align.isEmpty()) { return this.align; } else if (parentMenuItem != null) { return parentMenuItem.getAlign(); } else { return this.modelMenu.getDefaultAlign(); } } public String getAlignStyle() { if (!this.alignStyle.isEmpty()) { return this.alignStyle; } else if (parentMenuItem != null) { return parentMenuItem.getAlignStyle(); } else { return this.modelMenu.getDefaultAlignStyle(); } } public FlexibleStringExpander getAssociatedContentId() { return associatedContentId; } public String getAssociatedContentId(Map<String, Object> context) { String retStr = null; if (this.associatedContentId != null) { retStr = associatedContentId.expandString(context); } if (retStr.isEmpty()) { retStr = this.modelMenu.getDefaultAssociatedContentId(context); } return retStr; } public String getCellWidth() { if (!this.cellWidth.isEmpty()) { return this.cellWidth; } else { return this.modelMenu.getDefaultCellWidth(); } } public ModelMenuCondition getCondition() { return condition; } public String getDisabledTitleStyle() { if (!this.disabledTitleStyle.isEmpty()) { return this.disabledTitleStyle; } else if (parentMenuItem != null) { return parentMenuItem.getDisabledTitleStyle(); } else { return this.modelMenu.getDefaultDisabledTitleStyle(); } } public String getDisableIfEmpty() { return this.disableIfEmpty; } public String getEntityName() { if (!this.entityName.isEmpty()) { return this.entityName; } else if (parentMenuItem != null) { return parentMenuItem.getEntityName(); } else { return this.modelMenu.getDefaultEntityName(); } } public Boolean getHideIfSelected() { if (hideIfSelected != null) { return this.hideIfSelected; } else { return this.modelMenu.getDefaultHideIfSelected(); } } public MenuLink getLink() { return this.link; } public List<ModelMenuItem> getMenuItemList() { return menuItemList; } public ModelMenu getModelMenu() { return modelMenu; } @Override public String getName() { if (!this.overrideName.isEmpty()) { return this.overrideName; } return super.getName(); } public String getOverrideName() { return overrideName; } public ModelMenuItem getParentMenuItem() { return parentMenuItem; } public FlexibleStringExpander getParentPortalPageId() { return parentPortalPageId; } public String getParentPortalPageId(Map<String, Object> context) { return this.parentPortalPageId.expandString(context); } public int getPosition() { if (this.position == null) { return 1; } else { return position.intValue(); } } public String getSelectedStyle() { if (!this.selectedStyle.isEmpty()) { return this.selectedStyle; } else if (parentMenuItem != null) { return parentMenuItem.getSelectedStyle(); } else { return this.modelMenu.getDefaultSelectedStyle(); } } public String getSubMenu() { return subMenu; } public FlexibleStringExpander getTitle() { return title; } public String getTitle(Map<String, Object> context) { return title.expandString(context); } public String getTitleStyle() { if (!this.titleStyle.isEmpty()) { return this.titleStyle; } else if (parentMenuItem != null) { return parentMenuItem.getTitleStyle(); } else { return this.modelMenu.getDefaultTitleStyle(); } } public FlexibleStringExpander getTooltip() { return tooltip; } public String getTooltip(Map<String, Object> context) { if (UtilValidate.isNotEmpty(tooltip)) { return tooltip.expandString(context); } else { return ""; } } public String getTooltipStyle() { if (!this.tooltipStyle.isEmpty()) { return this.tooltipStyle; } else if (parentMenuItem != null) { return parentMenuItem.getTooltipStyle(); } else { return this.modelMenu.getDefaultTooltipStyle(); } } public String getWidgetStyle() { if (!this.widgetStyle.isEmpty()) { return this.widgetStyle; } else if (parentMenuItem != null) { return parentMenuItem.getWidgetStyle(); } else { return this.modelMenu.getDefaultWidgetStyle(); } } public boolean isSelected(Map<String, Object> context) { return getName().equals(modelMenu.getSelectedMenuItemContextFieldName(context)); } public ModelMenuItem mergeOverrideModelMenuItem(ModelMenuItem overrideMenuItem) { return new ModelMenuItem(this, overrideMenuItem); } public void renderMenuItemString(Appendable writer, Map<String, Object> context, MenuStringRenderer menuStringRenderer) throws IOException { if (shouldBeRendered(context)) { AbstractModelAction.runSubActions(actions, context); String parentPortalPageId = getParentPortalPageId(context); if (UtilValidate.isNotEmpty(parentPortalPageId)) { List<GenericValue> portalPages = PortalPageWorker.getPortalPages(parentPortalPageId, context); if (UtilValidate.isNotEmpty(portalPages)) { Locale locale = (Locale) context.get("locale"); for (GenericValue portalPage : portalPages) { if (UtilValidate.isNotEmpty(portalPage.getString("portalPageName"))) { ModelMenuItem localItem = new ModelMenuItem(portalPage, this, locale); menuStringRenderer.renderMenuItem(writer, context, localItem); } } } } else { menuStringRenderer.renderMenuItem(writer, context, this); } } } public boolean shouldBeRendered(Map<String, Object> context) { if (this.condition != null) { return this.condition.getCondition().eval(context); } return true; } public static class MenuLink { private final ModelMenuItem linkMenuItem; private final Link link; public MenuLink(Element linkElement, ModelMenuItem parentMenuItem) { this.linkMenuItem = parentMenuItem; if (linkElement.getAttribute("text").isEmpty()) { linkElement.setAttribute("text", parentMenuItem.getTitle().getOriginal()); } this.link = new Link(linkElement); } public MenuLink(GenericValue portalPage, ModelMenuItem parentMenuItem, Locale locale) { this.linkMenuItem = parentMenuItem; List<Parameter> parameterList = new ArrayList<Parameter>(); if (parentMenuItem.link != null) { parameterList.addAll(parentMenuItem.link.getParameterList()); } parameterList.add(new Parameter("portalPageId", portalPage.getString("portalPageId"), false)); parameterList.add(new Parameter("parentPortalPageId", portalPage.getString("parentPortalPageId"), false)); String target = "showPortalPage"; if (parentMenuItem.link != null) { target = parentMenuItem.link.getTargetExdr().getOriginal(); } this.link = new Link(portalPage, parameterList, target, locale); } public AutoEntityParameters getAutoEntityParameters() { return link.getAutoEntityParameters(); } public AutoServiceParameters getAutoServiceParameters() { return link.getAutoServiceParameters(); } public boolean getEncode() { return link.getEncode(); } public boolean getFullPath() { return link.getFullPath(); } public String getHeight() { return link.getHeight(); } public String getId(Map<String, Object> context) { return link.getId(context); } public FlexibleStringExpander getIdExdr() { return link.getIdExdr(); } public Image getImage() { return link.getImage(); } public String getLinkType() { return link.getLinkType(); } public String getName() { return link.getName(); } public String getName(Map<String, Object> context) { return link.getName(context); } public FlexibleStringExpander getNameExdr() { return link.getNameExdr(); } public List<Parameter> getParameterList() { return link.getParameterList(); } public Map<String, String> getParameterMap(Map<String, Object> context) { return link.getParameterMap(context); } public String getPrefix(Map<String, Object> context) { return link.getPrefix(context); } public FlexibleStringExpander getPrefixExdr() { return link.getPrefixExdr(); } public boolean getSecure() { return link.getSecure(); } public String getStyle(Map<String, Object> context) { return link.getStyle(context); } public FlexibleStringExpander getStyleExdr() { return link.getStyleExdr(); } public String getTarget(Map<String, Object> context) { return link.getTarget(context); } public FlexibleStringExpander getTargetExdr() { return link.getTargetExdr(); } public String getTargetWindow(Map<String, Object> context) { return link.getTargetWindow(context); } public FlexibleStringExpander getTargetWindowExdr() { return link.getTargetWindowExdr(); } public String getText(Map<String, Object> context) { return link.getText(context); } public FlexibleStringExpander getTextExdr() { return link.getTextExdr(); } public String getUrlMode() { return link.getUrlMode(); } public String getWidth() { return link.getWidth(); } public ModelMenuItem getLinkMenuItem() { return linkMenuItem; } public Link getLink() { return link; } public void renderLinkString(Appendable writer, Map<String, Object> context, MenuStringRenderer menuStringRenderer) throws IOException { menuStringRenderer.renderLink(writer, context, this); } } }