| /******************************************************************************* |
| * 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.ofbiz.widget.screen; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import javolution.util.FastList; |
| import javolution.util.FastMap; |
| |
| import org.ofbiz.base.util.Debug; |
| import org.ofbiz.base.util.GeneralException; |
| import org.ofbiz.base.util.StringUtil; |
| import org.ofbiz.base.util.UtilFormatOut; |
| import org.ofbiz.base.util.UtilGenerics; |
| import org.ofbiz.base.util.UtilMisc; |
| import org.ofbiz.base.util.UtilValidate; |
| import org.ofbiz.base.util.UtilXml; |
| import org.ofbiz.base.util.collections.MapStack; |
| import org.ofbiz.base.util.string.FlexibleStringExpander; |
| import org.ofbiz.entity.Delegator; |
| import org.ofbiz.entity.GenericEntityException; |
| import org.ofbiz.entity.GenericValue; |
| import org.ofbiz.entity.condition.EntityCondition; |
| import org.ofbiz.entity.util.EntityQuery; |
| import org.ofbiz.widget.ModelWidget; |
| import org.ofbiz.widget.ModelWidgetAction; |
| import org.ofbiz.widget.PortalPageWorker; |
| import org.ofbiz.widget.WidgetFactory; |
| import org.ofbiz.widget.WidgetWorker; |
| import org.ofbiz.widget.form.FormFactory; |
| import org.ofbiz.widget.form.FormStringRenderer; |
| import org.ofbiz.widget.form.ModelForm; |
| import org.ofbiz.widget.menu.MenuFactory; |
| import org.ofbiz.widget.menu.MenuStringRenderer; |
| import org.ofbiz.widget.menu.ModelMenu; |
| import org.ofbiz.widget.tree.ModelTree; |
| import org.ofbiz.widget.tree.TreeFactory; |
| import org.ofbiz.widget.tree.TreeStringRenderer; |
| import org.w3c.dom.Element; |
| import org.xml.sax.SAXException; |
| |
| |
| /** |
| * Widget Library - Screen model class |
| */ |
| @SuppressWarnings("serial") |
| public abstract class ModelScreenWidget extends ModelWidget { |
| public static final String module = ModelScreenWidget.class.getName(); |
| |
| protected ModelScreen modelScreen; |
| |
| public ModelScreenWidget(ModelScreen modelScreen, Element widgetElement) { |
| super(widgetElement); |
| this.modelScreen = modelScreen; |
| if (Debug.verboseOn()) Debug.logVerbose("Reading Screen sub-widget with name: " + widgetElement.getNodeName(), module); |
| } |
| |
| public abstract void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException; |
| |
| public abstract String rawString(); |
| |
| public static List<ModelScreenWidget> readSubWidgets(ModelScreen modelScreen, List<? extends Element> subElementList) { |
| List<ModelScreenWidget> subWidgets = FastList.newInstance(); |
| for (Element subElement: subElementList) { |
| subWidgets.add(WidgetFactory.getModelScreenWidget(modelScreen, subElement)); |
| } |
| return subWidgets; |
| } |
| |
| public static void renderSubWidgetsString(List<ModelScreenWidget> subWidgets, Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| if (subWidgets == null) { |
| return; |
| } |
| for (ModelScreenWidget subWidget: subWidgets) { |
| if (Debug.verboseOn()) Debug.logVerbose("Rendering screen " + subWidget.modelScreen.getName() + "; widget class is " + subWidget.getClass().getName(), module); |
| |
| // render the sub-widget itself |
| subWidget.renderWidgetString(writer, context, screenStringRenderer); |
| } |
| } |
| |
| public ModelScreen getModelScreen() { |
| return this.modelScreen; |
| } |
| |
| public static class SectionsRenderer extends HashMap<String, Object> { |
| protected ScreenStringRenderer screenStringRenderer; |
| protected Map<String, Object> context; |
| protected Appendable writer; |
| |
| public SectionsRenderer(Map<String, ? extends Object> sectionMap, Map<String, Object> context, Appendable writer, ScreenStringRenderer screenStringRenderer) { |
| this.putAll(sectionMap); |
| this.context = context; |
| this.writer = writer; |
| this.screenStringRenderer = screenStringRenderer; |
| } |
| |
| /** This is a lot like the ScreenRenderer class and returns an empty String so it can be used more easily with FreeMarker */ |
| public String render(String sectionName) throws GeneralException, IOException { |
| ModelScreenWidget section = (ModelScreenWidget) this.get(sectionName); |
| // if no section by that name, write nothing |
| if (section != null) { |
| section.renderWidgetString(this.writer, this.context, this.screenStringRenderer); |
| } |
| return ""; |
| } |
| } |
| |
| public static class Section extends ModelScreenWidget { |
| public static final String TAG_NAME = "section"; |
| protected ModelScreenCondition condition; |
| protected List<ModelWidgetAction> actions; |
| protected List<ModelScreenWidget> subWidgets; |
| protected List<ModelScreenWidget> failWidgets; |
| public boolean isMainSection = false; |
| |
| public Section(ModelScreen modelScreen, Element sectionElement) { |
| super(modelScreen, sectionElement); |
| |
| // read condition under the "condition" element |
| Element conditionElement = UtilXml.firstChildElement(sectionElement, "condition"); |
| if (conditionElement != null) { |
| this.condition = new ModelScreenCondition(modelScreen, conditionElement); |
| } |
| |
| // read all actions under the "actions" element |
| Element actionsElement = UtilXml.firstChildElement(sectionElement, "actions"); |
| if (actionsElement != null) { |
| this.actions = ModelWidgetAction.readSubActions(modelScreen, actionsElement); |
| } |
| |
| // read sub-widgets |
| Element widgetsElement = UtilXml.firstChildElement(sectionElement, "widgets"); |
| List<? extends Element> subElementList = UtilXml.childElementList(widgetsElement); |
| this.subWidgets = ModelScreenWidget.readSubWidgets(this.modelScreen, subElementList); |
| |
| // read fail-widgets |
| Element failWidgetsElement = UtilXml.firstChildElement(sectionElement, "fail-widgets"); |
| if (failWidgetsElement != null) { |
| List<? extends Element> failElementList = UtilXml.childElementList(failWidgetsElement); |
| this.failWidgets = ModelScreenWidget.readSubWidgets(this.modelScreen, failElementList); |
| } |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| // check the condition, if there is one |
| boolean condTrue = true; |
| if (this.condition != null) { |
| if (!this.condition.eval(context)) { |
| condTrue = false; |
| } |
| } |
| |
| // if condition does not exist or evals to true run actions and render widgets, otherwise render fail-widgets |
| if (condTrue) { |
| // run the actions only if true |
| ModelWidgetAction.runSubActions(this.actions, context); |
| |
| try { |
| // section by definition do not themselves do anything, so this method will generally do nothing, but we'll call it anyway |
| screenStringRenderer.renderSectionBegin(writer, context, this); |
| |
| // render sub-widgets |
| renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer); |
| |
| screenStringRenderer.renderSectionEnd(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering widgets section [" + this.getName() + "] in screen named [" + this.modelScreen.getName() + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } else { |
| try { |
| // section by definition do not themselves do anything, so this method will generally do nothing, but we'll call it anyway |
| screenStringRenderer.renderSectionBegin(writer, context, this); |
| |
| // render sub-widgets |
| renderSubWidgetsString(this.failWidgets, writer, context, screenStringRenderer); |
| |
| screenStringRenderer.renderSectionEnd(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering fail-widgets section [" + this.getName() + "] in screen named [" + this.modelScreen.getName() + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| } |
| |
| @Override |
| public String getBoundaryCommentName() { |
| if (isMainSection) { |
| return modelScreen.getSourceLocation() + "#" + modelScreen.getName(); |
| } else { |
| return name; |
| } |
| } |
| |
| @Override |
| public String rawString() { |
| return "<section" + (UtilValidate.isNotEmpty(this.name)?" name=\"" + this.name + "\"":"") + ">"; |
| } |
| } |
| |
| public static class ColumnContainer extends ModelScreenWidget { |
| public static final String TAG_NAME = "column-container"; |
| private final FlexibleStringExpander idExdr; |
| private final FlexibleStringExpander styleExdr; |
| private final List<Column> columns; |
| |
| public ColumnContainer(ModelScreen modelScreen, Element containerElement) { |
| super(modelScreen, containerElement); |
| this.idExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("id")); |
| this.styleExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("style")); |
| List<? extends Element> subElementList = UtilXml.childElementList(containerElement, "column"); |
| List<Column> columns = new ArrayList<Column>(subElementList.size()); |
| for (Element element : subElementList) { |
| columns.add(new Column(modelScreen, element)); |
| } |
| this.columns = Collections.unmodifiableList(columns); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| try { |
| screenStringRenderer.renderColumnContainer(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering container in screen named [" + this.modelScreen.getName() + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| public List<Column> getColumns() { |
| return this.columns; |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getStyle(Map<String, Object> context) { |
| return this.styleExdr.expandString(context); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<column-container id=\"" + this.idExdr.getOriginal() + "\" style=\"" + this.styleExdr.getOriginal() + "\">"; |
| } |
| } |
| |
| public static class Column { |
| private final FlexibleStringExpander idExdr; |
| private final FlexibleStringExpander styleExdr; |
| private final List<ModelScreenWidget> subWidgets; |
| |
| public Column(ModelScreen modelScreen, Element columnElement) { |
| this.idExdr = FlexibleStringExpander.getInstance(columnElement.getAttribute("id")); |
| this.styleExdr = FlexibleStringExpander.getInstance(columnElement.getAttribute("style")); |
| List<? extends Element> subElementList = UtilXml.childElementList(columnElement); |
| this.subWidgets = Collections.unmodifiableList(readSubWidgets(modelScreen, subElementList)); |
| } |
| |
| public List<ModelScreenWidget> getSubWidgets() { |
| return this.subWidgets; |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getStyle(Map<String, Object> context) { |
| return this.styleExdr.expandString(context); |
| } |
| } |
| |
| public static class Container extends ModelScreenWidget { |
| public static final String TAG_NAME = "container"; |
| protected FlexibleStringExpander idExdr; |
| protected FlexibleStringExpander styleExdr; |
| protected FlexibleStringExpander autoUpdateTargetExdr; |
| protected FlexibleStringExpander autoUpdateInterval; |
| protected List<ModelScreenWidget> subWidgets; |
| |
| public Container(ModelScreen modelScreen, Element containerElement) { |
| super(modelScreen, containerElement); |
| this.idExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("id")); |
| this.styleExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("style")); |
| this.autoUpdateTargetExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("auto-update-target")); |
| String autoUpdateInterval = containerElement.getAttribute("auto-update-interval"); |
| if (autoUpdateInterval.isEmpty()) { |
| autoUpdateInterval = "2"; |
| } |
| this.autoUpdateInterval = FlexibleStringExpander.getInstance(autoUpdateInterval); |
| // read sub-widgets |
| List<? extends Element> subElementList = UtilXml.childElementList(containerElement); |
| this.subWidgets = ModelScreenWidget.readSubWidgets(this.modelScreen, subElementList); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| try { |
| screenStringRenderer.renderContainerBegin(writer, context, this); |
| |
| // render sub-widgets |
| renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer); |
| |
| screenStringRenderer.renderContainerEnd(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering container in screen named [" + this.modelScreen.getName() + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getStyle(Map<String, Object> context) { |
| return this.styleExdr.expandString(context); |
| } |
| |
| public String getAutoUpdateTargetExdr(Map<String, Object> context) { |
| return this.autoUpdateTargetExdr.expandString(context); |
| } |
| |
| public String getAutoUpdateInterval(Map<String, Object> context) { |
| return this.autoUpdateInterval.expandString(context); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<container id=\"" + this.idExdr.getOriginal() + "\" style=\"" + this.styleExdr.getOriginal() + "\" auto-update-target=\"" + this.autoUpdateTargetExdr.getOriginal() + "\">"; |
| } |
| } |
| |
| public static class Screenlet extends ModelScreenWidget { |
| public static final String TAG_NAME = "screenlet"; |
| protected FlexibleStringExpander idExdr; |
| protected FlexibleStringExpander titleExdr; |
| protected Menu navigationMenu = null; |
| protected Menu tabMenu = null; |
| protected Form navigationForm = null; |
| protected boolean collapsible = false; |
| protected FlexibleStringExpander initiallyCollapsed; |
| protected boolean saveCollapsed = true; |
| protected boolean padded = true; |
| protected List<ModelScreenWidget> subWidgets; |
| |
| public Screenlet(ModelScreen modelScreen, Element screenletElement) { |
| super(modelScreen, screenletElement); |
| this.idExdr = FlexibleStringExpander.getInstance(screenletElement.getAttribute("id")); |
| this.collapsible = "true".equals(screenletElement.getAttribute("collapsible")); |
| this.initiallyCollapsed = FlexibleStringExpander.getInstance(screenletElement.getAttribute("initially-collapsed")); |
| if ("true".equals(this.initiallyCollapsed.getOriginal())) { |
| this.collapsible = true; |
| } |
| // By default, for a collapsible screenlet, the collapsed/expanded status must be saved |
| this.saveCollapsed = !("false".equals(screenletElement.getAttribute("save-collapsed"))); |
| |
| this.padded = !"false".equals(screenletElement.getAttribute("padded")); |
| if (this.collapsible && UtilValidate.isEmpty(this.name) && idExdr.isEmpty()) { |
| throw new IllegalArgumentException("Collapsible screenlets must have a name or id [" + this.modelScreen.getName() + "]"); |
| } |
| this.titleExdr = FlexibleStringExpander.getInstance(screenletElement.getAttribute("title")); |
| List<? extends Element> subElementList = UtilXml.childElementList(screenletElement); |
| this.subWidgets = ModelScreenWidget.readSubWidgets(this.modelScreen, subElementList); |
| String navMenuName = screenletElement.getAttribute("navigation-menu-name"); |
| if (UtilValidate.isNotEmpty(navMenuName)) { |
| for (ModelWidget subWidget : this.subWidgets) { |
| if (navMenuName.equals(subWidget.getName()) && subWidget instanceof Menu) { |
| this.navigationMenu = (Menu) subWidget; |
| subWidgets.remove(subWidget); |
| break; |
| } |
| } |
| } |
| String tabMenuName = screenletElement.getAttribute("tab-menu-name"); |
| if (UtilValidate.isNotEmpty(tabMenuName)) { |
| for (ModelWidget subWidget : this.subWidgets) { |
| if (tabMenuName.equals(subWidget.getName()) && subWidget instanceof Menu) { |
| this.tabMenu = (Menu) subWidget; |
| subWidgets.remove(subWidget); |
| break; |
| } |
| } |
| } |
| String formName = screenletElement.getAttribute("navigation-form-name"); |
| if (UtilValidate.isNotEmpty(formName) && this.navigationMenu == null) { |
| for (ModelWidget subWidget : this.subWidgets) { |
| if (formName.equals(subWidget.getName()) && subWidget instanceof Form) { |
| this.navigationForm = (Form) subWidget; |
| // Let's give this a try, it can be removed later if it |
| // proves to cause problems |
| this.padded = false; |
| break; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| boolean collapsed = getInitiallyCollapsed(context); |
| if (this.collapsible) { |
| String preferenceKey = getPreferenceKey(context) + "_collapsed"; |
| Map<String, Object> requestParameters = UtilGenerics.checkMap(context.get("requestParameters")); |
| if (requestParameters != null) { |
| String collapsedParam = (String) requestParameters.get(preferenceKey); |
| if (UtilValidate.isNotEmpty(collapsedParam)) { |
| collapsed = "true".equals(collapsedParam); |
| } |
| } |
| } |
| try { |
| screenStringRenderer.renderScreenletBegin(writer, context, collapsed, this); |
| for (ModelScreenWidget subWidget : this.subWidgets) { |
| screenStringRenderer.renderScreenletSubWidget(writer, context, subWidget, this); |
| } |
| screenStringRenderer.renderScreenletEnd(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering screenlet in screen named [" + this.modelScreen.getName() + "]: "; |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg + e); |
| } |
| } |
| |
| public boolean collapsible() { |
| return this.collapsible; |
| } |
| |
| //initially-collapsed status, which may be overriden by user preference |
| public boolean getInitiallyCollapsed(Map<String, Object> context) { |
| String screenletId = this.getId(context) + "_collapsed"; |
| Map<String, ? extends Object> userPreferences = UtilGenerics.checkMap(context.get("userPreferences")); |
| if (userPreferences != null && userPreferences.containsKey(screenletId)) { |
| return Boolean.valueOf((String)userPreferences.get(screenletId)).booleanValue() ; |
| } |
| |
| return "true".equals(this.initiallyCollapsed.expand(context)); |
| } |
| |
| public boolean saveCollapsed() { |
| return this.saveCollapsed; |
| } |
| public boolean padded() { |
| return this.padded; |
| } |
| |
| public String getPreferenceKey(Map<String, Object> context) { |
| String name = this.idExdr.expandString(context); |
| if (UtilValidate.isEmpty(name)) { |
| name = "screenlet" + "_" + Integer.toHexString(hashCode()); |
| } |
| return name; |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getTitle(Map<String, Object> context) { |
| String title = this.titleExdr.expandString(context); |
| StringUtil.SimpleEncoder simpleEncoder = (StringUtil.SimpleEncoder) context.get("simpleEncoder"); |
| if (simpleEncoder != null) { |
| title = simpleEncoder.encode(title); |
| } |
| return title; |
| } |
| |
| public Menu getNavigationMenu() { |
| return this.navigationMenu; |
| } |
| |
| public Form getNavigationForm() { |
| return this.navigationForm; |
| } |
| |
| public Menu getTabMenu() { |
| return this.tabMenu; |
| } |
| |
| @Override |
| public String rawString() { |
| return "<screenlet id=\"" + this.idExdr.getOriginal() + "\" title=\"" + this.titleExdr.getOriginal() + "\">"; |
| } |
| } |
| |
| public static class HorizontalSeparator extends ModelScreenWidget { |
| public static final String TAG_NAME = "horizontal-separator"; |
| protected FlexibleStringExpander idExdr; |
| protected FlexibleStringExpander styleExdr; |
| |
| public HorizontalSeparator(ModelScreen modelScreen, Element separatorElement) { |
| super(modelScreen, separatorElement); |
| this.idExdr = FlexibleStringExpander.getInstance(separatorElement.getAttribute("id")); |
| this.styleExdr = FlexibleStringExpander.getInstance(separatorElement.getAttribute("style")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| screenStringRenderer.renderHorizontalSeparator(writer, context, this); |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getStyle(Map<String, Object> context) { |
| return this.styleExdr.expandString(context); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<horizontal-separator id=\"" + this.idExdr.getOriginal() + "\" name=\"" + this.idExdr.getOriginal() + "\" style=\"" + this.styleExdr.getOriginal() + "\">"; |
| } |
| } |
| |
| public static class IncludeScreen extends ModelScreenWidget { |
| public static final String TAG_NAME = "include-screen"; |
| protected FlexibleStringExpander nameExdr; |
| protected FlexibleStringExpander locationExdr; |
| protected FlexibleStringExpander shareScopeExdr; |
| |
| public IncludeScreen(ModelScreen modelScreen, Element includeScreenElement) { |
| super(modelScreen, includeScreenElement); |
| this.nameExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("name")); |
| this.locationExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("location")); |
| this.shareScopeExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("share-scope")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| // if we are not sharing the scope, protect it using the MapStack |
| boolean protectScope = !shareScope(context); |
| if (protectScope) { |
| if (!(context instanceof MapStack<?>)) { |
| context = MapStack.create(context); |
| } |
| |
| UtilGenerics.<MapStack<String>>cast(context).push(); |
| |
| // build the widgetpath |
| List<String> widgetTrail = UtilGenerics.toList(context.get("_WIDGETTRAIL_")); |
| if (widgetTrail == null) { |
| widgetTrail = FastList.newInstance(); |
| } |
| |
| String thisName = nameExdr.expandString(context); |
| widgetTrail.add(thisName); |
| context.put("_WIDGETTRAIL_", widgetTrail); |
| } |
| |
| // don't need the renderer here, will just pass this on down to another screen call; screenStringRenderer.renderContainerBegin(writer, context, this); |
| String name = this.getName(context); |
| String location = this.getLocation(context); |
| |
| if (UtilValidate.isEmpty(name)) { |
| if (Debug.verboseOn()) Debug.logVerbose("In the include-screen tag the screen name was empty, ignoring include; in screen [" + this.modelScreen.getName() + "]", module); |
| return; |
| } |
| |
| ScreenFactory.renderReferencedScreen(name, location, this, writer, context, screenStringRenderer); |
| |
| if (protectScope) { |
| UtilGenerics.<MapStack<String>>cast(context).pop(); |
| } |
| } |
| |
| public String getName(Map<String, Object> context) { |
| return this.nameExdr.expandString(context); |
| } |
| |
| public String getLocation(Map<String, Object> context) { |
| return this.locationExdr.expandString(context); |
| } |
| |
| public boolean shareScope(Map<String, Object> context) { |
| String shareScopeString = this.shareScopeExdr.expandString(context); |
| // defaults to false, so anything but true is false |
| return "true".equals(shareScopeString); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<include-screen name=\"" + this.nameExdr.getOriginal() + "\" location=\"" + this.locationExdr.getOriginal() + "\" share-scope=\"" + this.shareScopeExdr.getOriginal() + "\"/>"; |
| } |
| } |
| |
| public static class DecoratorScreen extends ModelScreenWidget { |
| public static final String TAG_NAME = "decorator-screen"; |
| protected FlexibleStringExpander nameExdr; |
| protected FlexibleStringExpander locationExdr; |
| protected Map<String, DecoratorSection> sectionMap = new HashMap<String, DecoratorSection>(); |
| |
| public DecoratorScreen(ModelScreen modelScreen, Element decoratorScreenElement) { |
| super(modelScreen, decoratorScreenElement); |
| this.nameExdr = FlexibleStringExpander.getInstance(decoratorScreenElement.getAttribute("name")); |
| this.locationExdr = FlexibleStringExpander.getInstance(decoratorScreenElement.getAttribute("location")); |
| |
| List<? extends Element> decoratorSectionElementList = UtilXml.childElementList(decoratorScreenElement, "decorator-section"); |
| for (Element decoratorSectionElement: decoratorSectionElementList) { |
| DecoratorSection decoratorSection = new DecoratorSection(modelScreen, decoratorSectionElement); |
| this.sectionMap.put(decoratorSection.getName(), decoratorSection); |
| } |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| // isolate the scope |
| if (!(context instanceof MapStack)) { |
| context = MapStack.create(context); |
| } |
| |
| MapStack contextMs = (MapStack) context; |
| |
| // create a standAloneStack, basically a "save point" for this SectionsRenderer, and make a new "screens" object just for it so it is isolated and doesn't follow the stack down |
| MapStack standAloneStack = contextMs.standAloneChildStack(); |
| standAloneStack.put("screens", new ScreenRenderer(writer, standAloneStack, screenStringRenderer)); |
| SectionsRenderer sections = new SectionsRenderer(this.sectionMap, standAloneStack, writer, screenStringRenderer); |
| |
| // put the sectionMap in the context, make sure it is in the sub-scope, ie after calling push on the MapStack |
| contextMs.push(); |
| context.put("sections", sections); |
| |
| String name = this.getName(context); |
| String location = this.getLocation(context); |
| |
| ScreenFactory.renderReferencedScreen(name, location, this, writer, context, screenStringRenderer); |
| |
| contextMs.pop(); |
| } |
| |
| public String getName(Map<String, Object> context) { |
| return this.nameExdr.expandString(context); |
| } |
| |
| public String getLocation(Map<String, Object> context) { |
| return this.locationExdr.expandString(context); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<decorator-screen name=\"" + this.nameExdr.getOriginal() + "\" location=\"" + this.locationExdr.getOriginal() + "\"/>"; |
| } |
| } |
| |
| public static class DecoratorSection extends ModelScreenWidget { |
| public static final String TAG_NAME = "decorator-section"; |
| protected List<ModelScreenWidget> subWidgets; |
| |
| public DecoratorSection(ModelScreen modelScreen, Element decoratorSectionElement) { |
| super(modelScreen, decoratorSectionElement); |
| // read sub-widgets |
| List<? extends Element> subElementList = UtilXml.childElementList(decoratorSectionElement); |
| this.subWidgets = ModelScreenWidget.readSubWidgets(this.modelScreen, subElementList); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| // render sub-widgets |
| renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<decorator-section name=\"" + this.name + "\">"; |
| } |
| } |
| |
| public static class DecoratorSectionInclude extends ModelScreenWidget { |
| public static final String TAG_NAME = "decorator-section-include"; |
| |
| public DecoratorSectionInclude(ModelScreen modelScreen, Element decoratorSectionElement) { |
| super(modelScreen, decoratorSectionElement); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| Map<String, ? extends Object> preRenderedContent = UtilGenerics.checkMap(context.get("preRenderedContent")); |
| if (preRenderedContent != null && preRenderedContent.containsKey(this.name)) { |
| try { |
| writer.append((String) preRenderedContent.get(this.name)); |
| } catch (IOException e) { |
| String errMsg = "Error rendering pre-rendered content in screen named [" + this.modelScreen.getName() + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } else { |
| SectionsRenderer sections = (SectionsRenderer) context.get("sections"); |
| // for now if sections is null, just log a warning; may be permissible to make the screen for flexible |
| if (sections == null) { |
| Debug.logWarning("In decorator-section-include could not find sections object in the context, not rendering section with name [" + this.name + "]", module); |
| } else { |
| sections.render(this.name); |
| } |
| } |
| } |
| |
| @Override |
| public String rawString() { |
| return "<decorator-section-include name=\"" + this.name + "\">"; |
| } |
| } |
| |
| public static class Label extends ModelScreenWidget { |
| public static final String TAG_NAME = "label"; |
| protected FlexibleStringExpander textExdr; |
| |
| protected FlexibleStringExpander idExdr; |
| protected FlexibleStringExpander styleExdr; |
| |
| public Label(ModelScreen modelScreen, Element labelElement) { |
| super(modelScreen, labelElement); |
| |
| // put the text attribute first, then the pcdata under the element, if both are there of course |
| String textAttr = UtilFormatOut.checkNull(labelElement.getAttribute("text")); |
| String pcdata = UtilFormatOut.checkNull(UtilXml.elementValue(labelElement)); |
| this.textExdr = FlexibleStringExpander.getInstance(textAttr + pcdata); |
| |
| this.idExdr = FlexibleStringExpander.getInstance(labelElement.getAttribute("id")); |
| this.styleExdr = FlexibleStringExpander.getInstance(labelElement.getAttribute("style")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) { |
| try { |
| screenStringRenderer.renderLabel(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering label in screen named [" + this.modelScreen.getName() + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| public String getText(Map<String, Object> context) { |
| String text = this.textExdr.expandString(context); |
| StringUtil.SimpleEncoder simpleEncoder = (StringUtil.SimpleEncoder) context.get("simpleEncoder"); |
| if (simpleEncoder != null) { |
| text = simpleEncoder.encode(text); |
| } |
| return text; |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getStyle(Map<String, Object> context) { |
| return this.styleExdr.expandString(context); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<label id=\"" + this.idExdr.getOriginal() + "\" style=\"" + this.styleExdr.getOriginal() + "\" text=\"" + this.textExdr.getOriginal() + "\"/>"; |
| } |
| } |
| |
| public static class Form extends ModelScreenWidget { |
| public static final String TAG_NAME = "include-form"; |
| protected FlexibleStringExpander nameExdr; |
| protected FlexibleStringExpander locationExdr; |
| protected FlexibleStringExpander shareScopeExdr; |
| protected ModelForm modelForm = null; |
| |
| public Form(ModelScreen modelScreen, Element formElement) { |
| super(modelScreen, formElement); |
| |
| this.nameExdr = FlexibleStringExpander.getInstance(formElement.getAttribute("name")); |
| this.locationExdr = FlexibleStringExpander.getInstance(formElement.getAttribute("location")); |
| this.shareScopeExdr = FlexibleStringExpander.getInstance(formElement.getAttribute("share-scope")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) { |
| // Output format might not support forms, so make form rendering optional. |
| FormStringRenderer formStringRenderer = (FormStringRenderer) context.get("formStringRenderer"); |
| if (formStringRenderer == null) { |
| Debug.logVerbose("FormStringRenderer instance not found in rendering context, form not rendered.", module); |
| return; |
| } |
| boolean protectScope = !shareScope(context); |
| if (protectScope) { |
| if (!(context instanceof MapStack<?>)) { |
| context = MapStack.create(context); |
| } |
| UtilGenerics.<MapStack<String>>cast(context).push(); |
| } |
| ModelForm modelForm = getModelForm(context); |
| try { |
| modelForm.renderFormString(writer, context, formStringRenderer); |
| } catch (IOException e) { |
| String errMsg = "Error rendering included form named [" + name + "] at location [" + this.getLocation(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg + e); |
| } |
| |
| if (protectScope) { |
| UtilGenerics.<MapStack<String>>cast(context).pop(); |
| } |
| } |
| |
| public ModelForm getModelForm(Map<String, Object> context) { |
| ModelForm modelForm = null; |
| String name = this.getName(context); |
| String location = this.getLocation(context); |
| try { |
| modelForm = FormFactory.getFormFromLocation(location, name, this.modelScreen.getDelegator(context).getModelReader(), this.modelScreen.getDispatcher(context).getDispatchContext()); |
| } catch (Exception e) { |
| String errMsg = "Error rendering included form named [" + name + "] at location [" + location + "]: "; |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg + e); |
| } |
| return modelForm; |
| } |
| |
| public String getName(Map<String, Object> context) { |
| return this.nameExdr.expandString(context); |
| } |
| |
| public String getLocation(Map<String, Object> context) { |
| return this.locationExdr.expandString(context); |
| } |
| |
| public boolean shareScope(Map<String, Object> context) { |
| String shareScopeString = this.shareScopeExdr.expandString(context); |
| // defaults to false, so anything but true is false |
| return "true".equals(shareScopeString); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<include-form name=\"" + this.nameExdr.getOriginal() + "\" location=\"" + this.locationExdr.getOriginal() + "\" share-scope=\"" + this.shareScopeExdr.getOriginal() + "\"/>"; |
| } |
| } |
| |
| public static class Tree extends ModelScreenWidget { |
| public static final String TAG_NAME = "include-tree"; |
| protected FlexibleStringExpander nameExdr; |
| protected FlexibleStringExpander locationExdr; |
| protected FlexibleStringExpander shareScopeExdr; |
| |
| public Tree(ModelScreen modelScreen, Element treeElement) { |
| super(modelScreen, treeElement); |
| |
| this.nameExdr = FlexibleStringExpander.getInstance(treeElement.getAttribute("name")); |
| this.locationExdr = FlexibleStringExpander.getInstance(treeElement.getAttribute("location")); |
| this.shareScopeExdr = FlexibleStringExpander.getInstance(treeElement.getAttribute("share-scope")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| // Output format might not support trees, so make tree rendering optional. |
| TreeStringRenderer treeStringRenderer = (TreeStringRenderer) context.get("treeStringRenderer"); |
| if (treeStringRenderer == null) { |
| Debug.logVerbose("TreeStringRenderer instance not found in rendering context, tree not rendered.", module); |
| return; |
| } |
| boolean protectScope = !shareScope(context); |
| if (protectScope) { |
| if (!(context instanceof MapStack<?>)) { |
| context = MapStack.create(context); |
| } |
| UtilGenerics.<MapStack<String>>cast(context).push(); |
| } |
| |
| String name = this.getName(context); |
| String location = this.getLocation(context); |
| ModelTree modelTree = null; |
| try { |
| modelTree = TreeFactory.getTreeFromLocation(this.getLocation(context), this.getName(context), this.modelScreen.getDelegator(context), this.modelScreen.getDispatcher(context)); |
| } catch (IOException e) { |
| String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } catch (SAXException e) { |
| String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } catch (ParserConfigurationException e) { |
| String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| StringBuffer renderBuffer = new StringBuffer(); |
| modelTree.renderTreeString(renderBuffer, context, treeStringRenderer); |
| try { |
| writer.append(renderBuffer.toString()); |
| } catch (IOException e) { |
| String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| |
| if (protectScope) { |
| UtilGenerics.<MapStack<String>>cast(context).pop(); |
| } |
| } |
| |
| public String getName(Map<String, Object> context) { |
| return this.nameExdr.expandString(context); |
| } |
| |
| public String getLocation(Map<String, Object> context) { |
| return this.locationExdr.expandString(context); |
| } |
| |
| public boolean shareScope(Map<String, Object> context) { |
| String shareScopeString = this.shareScopeExdr.expandString(context); |
| // defaults to false, so anything but true is false |
| return "true".equals(shareScopeString); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<include-tree name=\"" + this.nameExdr.getOriginal() + "\" location=\"" + this.locationExdr.getOriginal() + "\" share-scope=\"" + this.shareScopeExdr.getOriginal() + "\"/>"; |
| } |
| } |
| |
| public static class PlatformSpecific extends ModelScreenWidget { |
| public static final String TAG_NAME = "platform-specific"; |
| protected Map<String, ModelScreenWidget> subWidgets; |
| |
| public PlatformSpecific(ModelScreen modelScreen, Element platformSpecificElement) { |
| super(modelScreen, platformSpecificElement); |
| subWidgets = new HashMap<String, ModelScreenWidget>(); |
| List<? extends Element> childElements = UtilXml.childElementList(platformSpecificElement); |
| if (childElements != null) { |
| for (Element childElement: childElements) { |
| if ("html".equals(childElement.getNodeName())) { |
| subWidgets.put("html", new HtmlWidget(modelScreen, childElement)); |
| } else if ("xsl-fo".equals(childElement.getNodeName())) { |
| subWidgets.put("xsl-fo", new HtmlWidget(modelScreen, childElement)); |
| } else if ("xml".equals(childElement.getNodeName())) { |
| subWidgets.put("xml", new HtmlWidget(modelScreen, childElement)); |
| } else { |
| throw new IllegalArgumentException("Tag not supported under the platform-specific tag with name: " + childElement.getNodeName()); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| ModelScreenWidget subWidget = null; |
| subWidget = subWidgets.get(screenStringRenderer.getRendererName()); |
| if (subWidget == null) { |
| // This is here for backward compatibility |
| Debug.logWarning("In platform-dependent could not find template for " + screenStringRenderer.getRendererName() + ", using the one for html.", module); |
| subWidget = subWidgets.get("html"); |
| } |
| if (subWidget != null) { |
| subWidget.renderWidgetString(writer, context, screenStringRenderer); |
| } |
| } |
| |
| @Override |
| public String rawString() { |
| Collection<ModelScreenWidget> subWidgetList = this.subWidgets.values(); |
| StringBuilder subWidgetsRawString = new StringBuilder("<platform-specific>"); |
| for (ModelScreenWidget subWidget: subWidgetList) { |
| subWidgetsRawString.append(subWidget.rawString()); |
| } |
| return subWidgetsRawString.append("</platform-specific>").toString(); |
| } |
| } |
| |
| public static class Content extends ModelScreenWidget { |
| public static final String TAG_NAME = "content"; |
| |
| protected FlexibleStringExpander contentId; |
| protected FlexibleStringExpander editRequest; |
| protected FlexibleStringExpander editContainerStyle; |
| protected FlexibleStringExpander enableEditName; |
| protected boolean xmlEscape = false; |
| protected FlexibleStringExpander dataResourceId; |
| protected String width; |
| protected String height; |
| protected String border; |
| |
| public Content(ModelScreen modelScreen, Element subContentElement) { |
| super(modelScreen, subContentElement); |
| |
| // put the text attribute first, then the pcdata under the element, if both are there of course |
| this.contentId = FlexibleStringExpander.getInstance(subContentElement.getAttribute("content-id")); |
| this.dataResourceId = FlexibleStringExpander.getInstance(subContentElement.getAttribute("dataresource-id")); |
| this.editRequest = FlexibleStringExpander.getInstance(subContentElement.getAttribute("edit-request")); |
| this.editContainerStyle = FlexibleStringExpander.getInstance(subContentElement.getAttribute("edit-container-style")); |
| this.enableEditName = FlexibleStringExpander.getInstance(subContentElement.getAttribute("enable-edit-name")); |
| this.xmlEscape = "true".equals(subContentElement.getAttribute("xml-escape")); |
| this.width = subContentElement.getAttribute("width"); |
| if (UtilValidate.isEmpty(this.width)) this.width="60%"; |
| this.height = subContentElement.getAttribute("height"); |
| if (UtilValidate.isEmpty(this.height)) this.width="400px"; |
| this.border = subContentElement.getAttribute("border"); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) { |
| try { |
| // pushing the contentId on the context as "contentId" is done |
| // because many times there will be embedded "subcontent" elements |
| // that use the syntax: <subcontent content-id="${contentId}"... |
| // and this is a step to make sure that it is there. |
| Delegator delegator = (Delegator) context.get("delegator"); |
| GenericValue content = null; |
| String expandedDataResourceId = getDataResourceId(context); |
| String expandedContentId = getContentId(context); |
| if (!(context instanceof MapStack<?>)) { |
| context = MapStack.create(context); |
| } |
| |
| // This is an important step to make sure that the current contentId is in the context |
| // as templates that contain "subcontent" elements will expect to find the master |
| // contentId in the context as "contentId". |
| UtilGenerics.<MapStack<String>>cast(context).push(); |
| context.put("contentId", expandedContentId); |
| |
| if (UtilValidate.isEmpty(expandedDataResourceId)) { |
| if (UtilValidate.isNotEmpty(expandedContentId)) { |
| content = EntityQuery.use(delegator).from("Content").where("contentId", expandedContentId).cache().queryOne(); |
| } else { |
| String errMsg = "contentId is empty."; |
| Debug.logError(errMsg, module); |
| return; |
| } |
| if (content != null) { |
| expandedDataResourceId = content.getString("dataResourceId"); |
| } else { |
| String errMsg = "Could not find content with contentId [" + expandedContentId + "] "; |
| Debug.logError(errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| GenericValue dataResource = null; |
| if (UtilValidate.isNotEmpty(expandedDataResourceId)) { |
| dataResource = EntityQuery.use(delegator).from("DataResource").where("dataResourceId", expandedDataResourceId).cache().queryOne(); |
| } |
| |
| String mimeTypeId = null; |
| if (dataResource != null) { |
| mimeTypeId = dataResource.getString("mimeTypeId"); |
| } |
| if (UtilValidate.isNotEmpty(content)) { |
| mimeTypeId = content.getString("mimeTypeId"); |
| } |
| |
| if (!(UtilValidate.isNotEmpty(mimeTypeId) |
| && ((mimeTypeId.indexOf("application") >= 0) || (mimeTypeId.indexOf("image")) >= 0))) { |
| screenStringRenderer.renderContentBegin(writer, context, this); |
| screenStringRenderer.renderContentBody(writer, context, this); |
| screenStringRenderer.renderContentEnd(writer, context, this); |
| } |
| UtilGenerics.<MapStack<String>>cast(context).pop(); |
| } catch (IOException e) { |
| String errMsg = "Error rendering content with contentId [" + getContentId(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } catch (GenericEntityException e) { |
| String errMsg = "Error obtaining content with contentId [" + getContentId(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| |
| } |
| |
| public String getContentId(Map<String, Object> context) { |
| return this.contentId.expandString(context); |
| } |
| |
| public String getDataResourceId(Map<String, Object> context) { |
| return this.dataResourceId.expandString(context); |
| } |
| |
| public String getEditRequest(Map<String, Object> context) { |
| return this.editRequest.expandString(context); |
| } |
| |
| public String getEditContainerStyle(Map<String, Object> context) { |
| return this.editContainerStyle.expandString(context); |
| } |
| |
| public String getEnableEditName(Map<String, Object> context) { |
| return this.enableEditName.expandString(context); |
| } |
| |
| public boolean xmlEscape() { |
| return this.xmlEscape; |
| } |
| |
| @Override |
| public String rawString() { |
| // may want to expand this a bit more |
| return "<content content-id=\"" + this.contentId.getOriginal() + "\" xml-escape=\"" + this.xmlEscape + "\"/>"; |
| } |
| |
| public String getWidth() { |
| return this.width; |
| } |
| |
| public String getHeight() { |
| return this.height; |
| } |
| |
| public String getBorder() { |
| return this.border; |
| } |
| } |
| |
| public static class SubContent extends ModelScreenWidget { |
| public static final String TAG_NAME = "sub-content"; |
| protected FlexibleStringExpander contentId; |
| protected FlexibleStringExpander mapKey; |
| protected FlexibleStringExpander editRequest; |
| protected FlexibleStringExpander editContainerStyle; |
| protected FlexibleStringExpander enableEditName; |
| protected boolean xmlEscape = false; |
| |
| public SubContent(ModelScreen modelScreen, Element subContentElement) { |
| super(modelScreen, subContentElement); |
| |
| // put the text attribute first, then the pcdata under the element, if both are there of course |
| this.contentId = FlexibleStringExpander.getInstance(UtilFormatOut.checkNull(subContentElement.getAttribute("content-id"))); |
| this.mapKey = FlexibleStringExpander.getInstance(UtilFormatOut.checkNull(subContentElement.getAttribute("map-key"))); |
| if (this.mapKey.isEmpty()) { |
| this.mapKey = FlexibleStringExpander.getInstance(UtilFormatOut.checkNull(subContentElement.getAttribute("assoc-name"))); |
| } |
| this.editRequest = FlexibleStringExpander.getInstance(UtilFormatOut.checkNull(subContentElement.getAttribute("edit-request"))); |
| this.editContainerStyle = FlexibleStringExpander.getInstance(subContentElement.getAttribute("edit-container-style")); |
| this.enableEditName = FlexibleStringExpander.getInstance(subContentElement.getAttribute("enable-edit-name")); |
| this.xmlEscape = "true".equals(subContentElement.getAttribute("xml-escape")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) { |
| try { |
| screenStringRenderer.renderSubContentBegin(writer, context, this); |
| screenStringRenderer.renderSubContentBody(writer, context, this); |
| screenStringRenderer.renderSubContentEnd(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering subContent with contentId [" + getContentId(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| public String getContentId(Map<String, Object> context) { |
| return this.contentId.expandString(context); |
| } |
| |
| public String getMapKey(Map<String, Object> context) { |
| return this.mapKey.expandString(context); |
| } |
| |
| public String getEditRequest(Map<String, Object> context) { |
| return this.editRequest.expandString(context); |
| } |
| |
| public String getEditContainerStyle(Map<String, Object> context) { |
| return this.editContainerStyle.expandString(context); |
| } |
| |
| public String getEnableEditName(Map<String, Object> context) { |
| return this.enableEditName.expandString(context); |
| } |
| |
| public boolean xmlEscape() { |
| return this.xmlEscape; |
| } |
| |
| @Override |
| public String rawString() { |
| // may want to expand this a bit more |
| return "<sub-content content-id=\"" + this.contentId.getOriginal() + "\" map-key=\"" + this.mapKey.getOriginal() + "\" xml-escape=\"" + this.xmlEscape + "\"/>"; |
| } |
| } |
| |
| public static class Menu extends ModelScreenWidget { |
| public static final String TAG_NAME = "include-menu"; |
| protected FlexibleStringExpander nameExdr; |
| protected FlexibleStringExpander locationExdr; |
| |
| public Menu(ModelScreen modelScreen, Element menuElement) { |
| super(modelScreen, menuElement); |
| |
| this.nameExdr = FlexibleStringExpander.getInstance(menuElement.getAttribute("name")); |
| this.locationExdr = FlexibleStringExpander.getInstance(menuElement.getAttribute("location")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws IOException { |
| // Output format might not support menus, so make menu rendering optional. |
| MenuStringRenderer menuStringRenderer = (MenuStringRenderer) context.get("menuStringRenderer"); |
| if (menuStringRenderer == null) { |
| Debug.logVerbose("MenuStringRenderer instance not found in rendering context, menu not rendered.", module); |
| return; |
| } |
| ModelMenu modelMenu = getModelMenu(context); |
| modelMenu.renderMenuString(writer, context, menuStringRenderer); |
| } |
| |
| public ModelMenu getModelMenu(Map<String, Object> context) { |
| String name = this.getName(context); |
| String location = this.getLocation(context); |
| ModelMenu modelMenu = null; |
| try { |
| modelMenu = MenuFactory.getMenuFromLocation(location, name); |
| } catch (Exception e) { |
| String errMsg = "Error rendering included menu named [" + name + "] at location [" + location + "]: "; |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg + e); |
| } |
| return modelMenu; |
| } |
| |
| public String getName(Map<String, Object> context) { |
| return this.nameExdr.expandString(context); |
| } |
| |
| public String getLocation(Map<String, Object> context) { |
| return this.locationExdr.expandString(context); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<include-menu name=\"" + this.nameExdr.getOriginal() + "\" location=\"" + this.locationExdr.getOriginal() + "\"/>"; |
| } |
| } |
| |
| public static class Link extends ModelScreenWidget { |
| public static final String TAG_NAME = "link"; |
| protected FlexibleStringExpander textExdr; |
| protected FlexibleStringExpander idExdr; |
| protected FlexibleStringExpander styleExdr; |
| protected FlexibleStringExpander targetExdr; |
| protected FlexibleStringExpander targetWindowExdr; |
| protected FlexibleStringExpander prefixExdr; |
| protected FlexibleStringExpander nameExdr; |
| protected Image image; |
| protected String urlMode = "intra-app"; |
| protected boolean fullPath = false; |
| protected boolean secure = false; |
| protected boolean encode = false; |
| protected String linkType; |
| protected String width; |
| protected String height; |
| protected List<WidgetWorker.Parameter> parameterList = FastList.newInstance(); |
| protected WidgetWorker.AutoServiceParameters autoServiceParameters; |
| protected WidgetWorker.AutoEntityParameters autoEntityParameters; |
| |
| public Link(ModelScreen modelScreen, Element linkElement) { |
| super(modelScreen, linkElement); |
| |
| setText(linkElement.getAttribute("text")); |
| setId(linkElement.getAttribute("id")); |
| setStyle(linkElement.getAttribute("style")); |
| setName(linkElement.getAttribute("name")); |
| setTarget(linkElement.getAttribute("target")); |
| setTargetWindow(linkElement.getAttribute("target-window")); |
| setPrefix(linkElement.getAttribute("prefix")); |
| setUrlMode(linkElement.getAttribute("url-mode")); |
| setFullPath(linkElement.getAttribute("full-path")); |
| setSecure(linkElement.getAttribute("secure")); |
| setEncode(linkElement.getAttribute("encode")); |
| Element imageElement = UtilXml.firstChildElement(linkElement, "image"); |
| if (imageElement != null) { |
| this.image = new Image(modelScreen, imageElement); |
| } |
| |
| this.linkType = linkElement.getAttribute("link-type"); |
| List<? extends Element> parameterElementList = UtilXml.childElementList(linkElement, "parameter"); |
| for (Element parameterElement: parameterElementList) { |
| this.parameterList.add(new WidgetWorker.Parameter(parameterElement)); |
| } |
| |
| Element autoServiceParamsElement = UtilXml.firstChildElement(linkElement, "auto-parameters-service"); |
| if (autoServiceParamsElement != null) { |
| autoServiceParameters = new WidgetWorker.AutoServiceParameters(autoServiceParamsElement); |
| } |
| Element autoEntityParamsElement = UtilXml.firstChildElement(linkElement, "auto-parameters-entity"); |
| if (autoEntityParamsElement != null) { |
| autoEntityParameters = new WidgetWorker.AutoEntityParameters(autoEntityParamsElement); |
| } |
| |
| this.width = linkElement.getAttribute("width"); |
| this.height = linkElement.getAttribute("height"); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) { |
| try { |
| screenStringRenderer.renderLink(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering link with id [" + getId(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| public String getText(Map<String, Object> context) { |
| String text = this.textExdr.expandString(context); |
| StringUtil.SimpleEncoder simpleEncoder = (StringUtil.SimpleEncoder) context.get("simpleEncoder"); |
| if (simpleEncoder != null) { |
| text = simpleEncoder.encode(text); |
| } |
| return text; |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getStyle(Map<String, Object> context) { |
| return this.styleExdr.expandString(context); |
| } |
| |
| public String getTarget(Map<String, Object> context) { |
| Map<String, Object> expanderContext = context; |
| StringUtil.SimpleEncoder simpleEncoder = context == null ? null : (StringUtil.SimpleEncoder) context.get("simpleEncoder"); |
| if (simpleEncoder != null) { |
| expanderContext = StringUtil.HtmlEncodingMapWrapper.getHtmlEncodingMapWrapper(context, simpleEncoder); |
| } |
| return this.targetExdr.expandString(expanderContext); |
| } |
| |
| public String getName(Map<String, Object> context) { |
| return this.nameExdr.expandString(context); |
| } |
| |
| public String getTargetWindow(Map<String, Object> context) { |
| return this.targetWindowExdr.expandString(context); |
| } |
| |
| public String getUrlMode() { |
| return this.urlMode; |
| } |
| |
| public String getPrefix(Map<String, Object> context) { |
| return this.prefixExdr.expandString(context); |
| } |
| |
| public boolean getFullPath() { |
| return this.fullPath; |
| } |
| |
| public boolean getSecure() { |
| return this.secure; |
| } |
| |
| public boolean getEncode() { |
| return this.encode; |
| } |
| |
| public Image getImage() { |
| return this.image; |
| } |
| |
| public String getLinkType() { |
| return this.linkType; |
| } |
| |
| public String getWidth() { |
| return this.width; |
| } |
| |
| public String getHeight() { |
| return this.height; |
| } |
| |
| public Map<String, String> getParameterMap(Map<String, Object> context) { |
| Map<String, String> fullParameterMap = FastMap.newInstance(); |
| |
| /* leaving this here... may want to add it at some point like the hyperlink element: |
| Map<String, String> addlParamMap = this.parametersMapAcsr.get(context); |
| if (addlParamMap != null) { |
| fullParameterMap.putAll(addlParamMap); |
| } |
| */ |
| |
| for (WidgetWorker.Parameter parameter: this.parameterList) { |
| fullParameterMap.put(parameter.getName(), parameter.getValue(context)); |
| } |
| |
| if (autoServiceParameters != null) { |
| fullParameterMap.putAll(autoServiceParameters.getParametersMap(context, null)); |
| } |
| if (autoEntityParameters != null) { |
| fullParameterMap.putAll(autoEntityParameters.getParametersMap(context, null)); |
| } |
| |
| return fullParameterMap; |
| } |
| |
| public void setText(String val) { |
| String textAttr = UtilFormatOut.checkNull(val); |
| this.textExdr = FlexibleStringExpander.getInstance(textAttr); |
| } |
| public void setId(String val) { |
| this.idExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setStyle(String val) { |
| this.styleExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setTarget(String val) { |
| this.targetExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setName(String val) { |
| this.nameExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setTargetWindow(String val) { |
| this.targetWindowExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setPrefix(String val) { |
| this.prefixExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setUrlMode(String val) { |
| if (UtilValidate.isNotEmpty(val)) |
| this.urlMode = val; |
| } |
| public void setFullPath(String val) { |
| String sFullPath = val; |
| if (sFullPath != null && sFullPath.equalsIgnoreCase("true")) |
| this.fullPath = true; |
| else |
| this.fullPath = false; |
| } |
| |
| public void setSecure(String val) { |
| String sSecure = val; |
| if (sSecure != null && sSecure.equalsIgnoreCase("true")) |
| this.secure = true; |
| else |
| this.secure = false; |
| } |
| |
| public void setEncode(String val) { |
| String sEncode = val; |
| if (sEncode != null && sEncode.equalsIgnoreCase("true")) |
| this.encode = true; |
| else |
| this.encode = false; |
| } |
| public void setImage(Image img) { |
| this.image = img; |
| } |
| |
| @Override |
| public String rawString() { |
| // may want to add more to this |
| return "<link id=\"" + this.idExdr.getOriginal() + "\" style=\"" + this.styleExdr.getOriginal() + "\" text=\"" + this.textExdr.getOriginal() + "\" target=\"" + this.targetExdr.getOriginal() + "\" name=\"" + this.nameExdr.getOriginal() + "\" url-mode=\"" + this.urlMode + "\"/>"; |
| } |
| } |
| |
| public static class Image extends ModelScreenWidget { |
| public static final String TAG_NAME = "image"; |
| protected FlexibleStringExpander srcExdr; |
| protected FlexibleStringExpander idExdr; |
| protected FlexibleStringExpander styleExdr; |
| protected FlexibleStringExpander widthExdr; |
| protected FlexibleStringExpander heightExdr; |
| protected FlexibleStringExpander borderExdr; |
| protected FlexibleStringExpander alt; |
| protected String urlMode = "content"; |
| |
| public Image(ModelScreen modelScreen, Element imageElement) { |
| super(modelScreen, imageElement); |
| |
| setSrc(imageElement.getAttribute("src")); |
| setId(imageElement.getAttribute("id")); |
| setStyle(imageElement.getAttribute("style")); |
| setWidth(imageElement.getAttribute("width")); |
| setHeight(imageElement.getAttribute("height")); |
| setBorder(imageElement.getAttribute("border")); |
| setAlt(imageElement.getAttribute("alt")); |
| setUrlMode(UtilFormatOut.checkEmpty(imageElement.getAttribute("url-mode"), "content")); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) { |
| try { |
| screenStringRenderer.renderImage(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering image with id [" + getId(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| public String getSrc(Map<String, Object> context) { |
| return this.srcExdr.expandString(context); |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getStyle(Map<String, Object> context) { |
| return this.styleExdr.expandString(context); |
| } |
| |
| public String getWidth(Map<String, Object> context) { |
| return this.widthExdr.expandString(context); |
| } |
| |
| public String getHeight(Map<String, Object> context) { |
| return this.heightExdr.expandString(context); |
| } |
| |
| public String getBorder(Map<String, Object> context) { |
| return this.borderExdr.expandString(context); |
| } |
| |
| public String getAlt(Map<String, Object> context) { |
| String alt = this.alt.expandString(context); |
| StringUtil.SimpleEncoder simpleEncoder = (StringUtil.SimpleEncoder) context.get("simpleEncoder"); |
| if (simpleEncoder != null) { |
| alt = simpleEncoder.encode(alt); |
| } |
| return alt; |
| } |
| |
| public String getUrlMode() { |
| return this.urlMode; |
| } |
| |
| public void setSrc(String val) { |
| String textAttr = UtilFormatOut.checkNull(val); |
| this.srcExdr = FlexibleStringExpander.getInstance(textAttr); |
| } |
| public void setId(String val) { |
| this.idExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setStyle(String val) { |
| this.styleExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setWidth(String val) { |
| this.widthExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setHeight(String val) { |
| this.heightExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setBorder(String val) { |
| this.borderExdr = FlexibleStringExpander.getInstance(val); |
| } |
| public void setAlt(String val) { |
| String altAttr = UtilFormatOut.checkNull(val); |
| this.alt = FlexibleStringExpander.getInstance(altAttr); |
| } |
| |
| public void setUrlMode(String val) { |
| if (UtilValidate.isEmpty(val)) { |
| this.urlMode = "content"; |
| } else { |
| this.urlMode = val; |
| } |
| } |
| |
| @Override |
| public String rawString() { |
| // may want to add more to this |
| return "<image id=\"" + this.idExdr.getOriginal() + "\" style=\"" + this.styleExdr.getOriginal() + "\" src=\"" + this.srcExdr.getOriginal() + "\" url-mode=\"" + this.urlMode + "\"/>"; |
| } |
| } |
| |
| public static class PortalPage extends ModelScreenWidget { |
| public static final String TAG_NAME = "include-portal-page"; |
| protected FlexibleStringExpander idExdr; |
| protected FlexibleStringExpander confModeExdr; |
| protected String originalPortalPageId; |
| protected String actualPortalPageId; |
| protected Boolean usePrivate; |
| |
| public PortalPage(ModelScreen modelScreen, Element portalPageElement) { |
| super(modelScreen, portalPageElement); |
| this.idExdr = FlexibleStringExpander.getInstance(portalPageElement.getAttribute("id")); |
| this.confModeExdr = FlexibleStringExpander.getInstance(portalPageElement.getAttribute("conf-mode")); |
| this.usePrivate = !("false".equals(portalPageElement.getAttribute("use-private"))); |
| } |
| |
| @Override |
| public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException { |
| try { |
| Delegator delegator = (Delegator) context.get("delegator"); |
| GenericValue portalPage = null; |
| List<GenericValue> portalPageColumns = null; |
| List<GenericValue> portalPagePortlets = null; |
| List<GenericValue> portletAttributes = null; |
| |
| String expandedPortalPageId = getId(context); |
| |
| if (UtilValidate.isNotEmpty(expandedPortalPageId)) { |
| if (usePrivate) { |
| portalPage = PortalPageWorker.getPortalPage(expandedPortalPageId, context); |
| } |
| else { |
| portalPage = EntityQuery.use(delegator).from("PortalPage").where("portalPageId", expandedPortalPageId).cache().queryOne(); |
| } |
| if (portalPage == null) { |
| String errMsg = "Could not find PortalPage with portalPageId [" + expandedPortalPageId + "] "; |
| Debug.logError(errMsg, module); |
| throw new RuntimeException(errMsg); |
| } else { |
| actualPortalPageId = portalPage.getString("portalPageId"); |
| originalPortalPageId = portalPage.getString("originalPortalPageId"); |
| portalPageColumns = delegator.findByAnd("PortalPageColumn", UtilMisc.toMap("portalPageId", actualPortalPageId), UtilMisc.toList("columnSeqId"), true); |
| } |
| } else { |
| String errMsg = "portalPageId is empty."; |
| Debug.logError(errMsg, module); |
| return; |
| } |
| |
| // Renders the portalPage header |
| screenStringRenderer.renderPortalPageBegin(writer, context, this); |
| |
| // First column has no previous column |
| String prevColumnSeqId = ""; |
| |
| // Iterates through the PortalPage columns |
| ListIterator <GenericValue>columnsIterator = portalPageColumns.listIterator(); |
| while(columnsIterator.hasNext()) { |
| GenericValue columnValue = columnsIterator.next(); |
| String columnSeqId = columnValue.getString("columnSeqId"); |
| |
| // Renders the portalPageColumn header |
| screenStringRenderer.renderPortalPageColumnBegin(writer, context, this, columnValue); |
| |
| // Get the Portlets located in the current column |
| portalPagePortlets = delegator.findByAnd("PortalPagePortletView", UtilMisc.toMap("portalPageId", portalPage.getString("portalPageId"), "columnSeqId", columnSeqId), UtilMisc.toList("sequenceNum"), false); |
| |
| // First Portlet in a Column has no previous Portlet |
| String prevPortletId = ""; |
| String prevPortletSeqId = ""; |
| |
| // If this is not the last column, get the next columnSeqId |
| String nextColumnSeqId = ""; |
| if (columnsIterator.hasNext()) { |
| nextColumnSeqId = portalPageColumns.get(columnsIterator.nextIndex()).getString("columnSeqId"); |
| } |
| |
| // Iterates through the Portlets in the Column |
| ListIterator <GenericValue>portletsIterator = portalPagePortlets.listIterator(); |
| while(portletsIterator.hasNext()) { |
| GenericValue portletValue = portletsIterator.next(); |
| |
| // If not the last portlet in the column, get the next nextPortletId and nextPortletSeqId |
| String nextPortletId = ""; |
| String nextPortletSeqId = ""; |
| if (portletsIterator.hasNext()) { |
| nextPortletId = portalPagePortlets.get(portletsIterator.nextIndex()).getString("portalPortletId"); |
| nextPortletSeqId = portalPagePortlets.get(portletsIterator.nextIndex()).getString("portletSeqId"); |
| } |
| |
| // Set info to allow portlet movement in the page |
| context.put("prevPortletId", prevPortletId); |
| context.put("prevPortletSeqId", prevPortletSeqId); |
| context.put("nextPortletId", nextPortletId); |
| context.put("nextPortletSeqId", nextPortletSeqId); |
| context.put("prevColumnSeqId", prevColumnSeqId); |
| context.put("nextColumnSeqId", nextColumnSeqId); |
| |
| // Get portlet's attributes |
| portletAttributes = delegator.findList("PortletAttribute", |
| EntityCondition.makeCondition(UtilMisc.toMap("portalPageId", portletValue.get("portalPageId"), "portalPortletId", portletValue.get("portalPortletId"), "portletSeqId", portletValue.get("portletSeqId"))), |
| null, null, null, false); |
| ListIterator <GenericValue>attributesIterator = portletAttributes.listIterator(); |
| while (attributesIterator.hasNext()) { |
| GenericValue attribute = attributesIterator.next(); |
| context.put(attribute.getString("attrName"), attribute.getString("attrValue")); |
| } |
| |
| // Renders the portalPagePortlet |
| screenStringRenderer.renderPortalPagePortletBegin(writer, context, this, portletValue); |
| screenStringRenderer.renderPortalPagePortletBody(writer, context, this, portletValue); |
| screenStringRenderer.renderPortalPagePortletEnd(writer, context, this, portletValue); |
| |
| // Remove the portlet's attributes so that these are not available for other portlets |
| while (attributesIterator.hasPrevious()) { |
| GenericValue attribute = attributesIterator.previous(); |
| context.remove(attribute.getString("attrName")); |
| } |
| |
| // Uses the actual portlet as prevPortlet for next iteration |
| prevPortletId = (String) portletValue.get("portalPortletId"); |
| prevPortletSeqId = (String) portletValue.get("portletSeqId"); |
| } |
| // Renders the portalPageColumn footer |
| screenStringRenderer.renderPortalPageColumnEnd(writer, context, this, columnValue); |
| |
| // Uses the actual columnSeqId as prevColumnSeqId for next iteration |
| prevColumnSeqId = columnSeqId; |
| } |
| // Renders the portalPage footer |
| screenStringRenderer.renderPortalPageEnd(writer, context, this); |
| } catch (IOException e) { |
| String errMsg = "Error rendering PortalPage with portalPageId [" + getId(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } catch (GenericEntityException e) { |
| String errMsg = "Error obtaining PortalPage with portalPageId [" + getId(context) + "]: " + e.toString(); |
| Debug.logError(e, errMsg, module); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| public String getId(Map<String, Object> context) { |
| return this.idExdr.expandString(context); |
| } |
| |
| public String getOriginalPortalPageId() { |
| return this.originalPortalPageId; |
| } |
| |
| public String getActualPortalPageId() { |
| return this.actualPortalPageId; |
| } |
| |
| public String getConfMode(Map<String, Object> context) { |
| return this.confModeExdr.expandString(context); |
| } |
| |
| public String getUsePrivate() { |
| return Boolean.toString(this.usePrivate); |
| } |
| |
| @Override |
| public String rawString() { |
| return "<include-portal-page id=\"" + this.idExdr.getOriginal() + "\" name=\"" + this.idExdr.getOriginal() + "\">"; |
| } |
| } |
| |
| } |