/* * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved. * * This file is part of the Jspresso framework. * * Jspresso is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Jspresso is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Jspresso. If not, see <http://www.gnu.org/licenses/>. */ package org.jspresso.framework.view.descriptor.basic; import java.util.Collection; import java.util.Locale; import org.jspresso.framework.model.descriptor.IModelDescriptor; import org.jspresso.framework.security.ISecurable; import org.jspresso.framework.util.descriptor.DefaultIconDescriptor; import org.jspresso.framework.util.descriptor.IIconDescriptor; import org.jspresso.framework.util.gate.IGate; import org.jspresso.framework.util.gui.Dimension; import org.jspresso.framework.util.gui.Icon; import org.jspresso.framework.util.i18n.ITranslationProvider; import org.jspresso.framework.util.lang.StringUtils; import org.jspresso.framework.view.action.ActionMap; import org.jspresso.framework.view.descriptor.EBorderType; import org.jspresso.framework.view.descriptor.IViewDescriptor; /** * This is the abstract base descriptor for all views. Its main purpose, since * it cannot be used directly, is to factorize common properties. * * @author Vincent Vandenschrick */ public abstract class BasicViewDescriptor extends DefaultIconDescriptor implements IViewDescriptor { private ActionMap actionMap; private ActionMap secondaryActionMap; private String permId; private String background; private EBorderType borderType; private String font; private String foreground; private Collection<String> grantedRoles; private IModelDescriptor modelDescriptor; private Integer preferredHeight; private Integer preferredWidth; private Collection<IGate> readabilityGates; private boolean readOnly; private Collection<IGate> writabilityGates; private String styleName; /** * Creates a new {@code BasicViewDescriptor} instance. */ protected BasicViewDescriptor() { borderType = EBorderType.NONE; } /** * Gets the actionMap. * * @return the actionMap. */ @Override public ActionMap getActionMap() { return actionMap; } /** * {@inheritDoc} */ @Override public String getPermId() { if (permId != null) { return permId; } return getName(); } /** * {@inheritDoc} */ @Override public String getBackground() { return background; } /** * {@inheritDoc} */ @Override public EBorderType getBorderType() { return borderType; } /** * {@inheritDoc} */ @Override public String getFont() { return font; } /** * {@inheritDoc} */ @Override public String getForeground() { return foreground; } /** * Gets the grantedRoles. * * @return the grantedRoles. */ @Override public Collection<String> getGrantedRoles() { if (grantedRoles == null && getModelDescriptor() != null) { if (getModelDescriptor() instanceof ISecurable) { return ((ISecurable) getModelDescriptor()).getGrantedRoles(); } } return grantedRoles; } /** * {@inheritDoc} */ @Override public String getI18nDescription(ITranslationProvider translationProvider, Locale locale) { if (getDescription() == null) { if (getModelDescriptor() != null) { return getModelDescriptor().getI18nDescription(translationProvider, locale); } } return super.getI18nDescription(translationProvider, locale); } /** * {@inheritDoc} */ @Override public String getI18nName(ITranslationProvider translationProvider, Locale locale) { if (getI18nNameKey() == null) { if (getModelDescriptor() != null) { if (getName() == null || getName().equals(getModelDescriptor().getName())) { return getModelDescriptor().getI18nName(translationProvider, locale); } } } return super.getI18nName(translationProvider, locale); } /** * {@inheritDoc} */ @Override public Icon getIcon() { Icon icon = super.getIcon(); if (icon == null && getModelDescriptor() instanceof IIconDescriptor) { icon = ((IIconDescriptor) getModelDescriptor()).getIcon(); } return icon; } /** * Gets the modelDescriptor. * * @return the modelDescriptor. */ @Override public IModelDescriptor getModelDescriptor() { return modelDescriptor; } /** * {@inheritDoc} */ @Override public Dimension getPreferredSize() { Integer w = getPreferredWidth(); Integer h = getPreferredHeight(); if (w != null || h != null) { Dimension dim = new Dimension(); if (w != null) { dim.setWidth(w); } if (h != null) { dim.setHeight(h); } return dim; } return null; } /** * Sets the preferred size. * * @param preferredSize * the preferred size. */ public void setPreferredSize(Dimension preferredSize) { if (preferredSize == null) { setPreferredWidth(null); setPreferredHeight(null); } else { setPreferredWidth(preferredSize.getWidth()); setPreferredHeight(preferredSize.getHeight()); } } /** * Gets the readabilityGates. * * @return the readabilityGates. */ @Override public Collection<IGate> getReadabilityGates() { // Gates are handled both on model connector and view connector. It is not // necessary to fetch the model gates here. Only component view descriptors // use their model gates since they are often backed by a reference property // connector. // if (readabilityGates == null && getModelDescriptor() != null) { // if (getModelDescriptor() instanceof IGateAccessible) { // return ((IGateAccessible) getModelDescriptor()).getReadabilityGates(); // } // } return readabilityGates; } /** * Gets the writabilityGates. * * @return the writabilityGates. */ @Override public Collection<IGate> getWritabilityGates() { // Gates are handled both on model connector and view connector. It is not // necessary to fetch the model gates here. Only component view descriptors // use their model gates since they are often backed by a reference property // connector. // if (writabilityGates == null && getModelDescriptor() != null) { // if (getModelDescriptor() instanceof IGateAccessible) { // return ((IGateAccessible) getModelDescriptor()).getWritabilityGates(); // } // } return writabilityGates; } /** * {@inheritDoc} */ @Override public boolean isReadOnly() { return readOnly; } /** * Assigns the view action map. An action map is generally represented as a * toolbar attached to the view. The toolbar follows the structure of the * action map : * <ul> * <li>each action list is contained in its own toolbar section which is * visually separated from the other sections. This allows for visually * grouping related actions as they are grouped in the action lists.</li> * <li>each action contained in an action list is represented by a toolbar * button using the action image as icon and translated action description as * toolTip.</li> * </ul> * Depending on the UI channel, the view action map may also be replicated in * a component contextual menu. In that case, the translated action name is * used to label each menu item. The same grouping rules apply for the * contextual menu than for the toolbar. * * @param actionMap * the actionMap to set. */ public void setActionMap(ActionMap actionMap) { this.actionMap = actionMap; } /** * Sets the permanent identifier to this application element. Permanent * identifiers are used by different framework parts, like dynamic security or * record/replay controllers to uniquely identify an application element. * Permanent identifiers are generated by the SJS build based on the element * id but must be explicitly set if Spring XML is used. * * @param permId * fixed id to mark the generated UI component. */ @Override public void setPermId(String permId) { this.permId = permId; } /** * Sets the background color of the UI component. The color must be defined * using its string hexadecimal representation (<i>0xargb</i> encoded). * <p/> * Default value is {@code null}, meaning use UI default. * * @param background * the background to set. */ public void setBackground(String background) { this.background = background; } /** * Sets the border type of the view. This is either a value of the * {@code EBorderType} enum or its equivalent string representation : * <ul> * <li>{@code NONE} for no border</li> * <li>{@code SIMPLE} for a line border</li> * <li>{@code TITLED} for a titled border. The view is then labeled with * its translated name and and icon. Whenever the view name has not been * explicitly set, the model name is used is used.</li> * </ul> * <p/> * Default value is {@code EBorderType.NONE}, i.e. no border. * * @param borderType * the borderType to set. */ public void setBorderType(EBorderType borderType) { this.borderType = borderType; } /** * Allows to customize the font used by the UI component. The font must be * string encoded using the pattern <b>"[name];[style];[size]"</b> : * <ul> * <li><b>[name]</b> is the name of the font, e.g. <i>arial</i>.</li> * <li><b>[style]</b> is PLAIN, BOLD, ITALIC or a union of BOLD and ITALIC * combined with the '|' character, e.g. <i>BOLD|ITALIC</i>.</li> * <li><b>[size]</b> is the size of the font, e.g. <i>10</i>.</li> * </ul> * Any of the above pattern section can be left empty, thus falling back to * the component default. * <p/> * Default value is {@code null}, meaning use default component font. * * @param font * the font to set. */ public void setFont(String font) { this.font = font; } /** * Sets the foreground color of the UI component. The color must be defined * using its string hexadecimal representation (<i>0xargb</i> encoded). * <p/> * Default value is {@code null}, meaning use UI default. * * @param foreground * the foreground to set. */ public void setForeground(String foreground) { this.foreground = foreground; } /** * Assigns the roles that are authorized to use this view. It supports * "<b>!</b>" prefix to negate the role(s). Whenever the user is not * granted sufficient privileges, the view is replaced by an empty section at * runtime. Setting the collection of granted roles to {@code null} * (default value) disables role based authorization on the view level. The * framework then checks for the model roles authorizations and will apply the * same restrictions. If both view and model granted roles collections are * {@code null}, then access is granted to anyone. * * @param grantedRoles * the grantedRoles to set. */ public void setGrantedRoles(Collection<String> grantedRoles) { this.grantedRoles = StringUtils.ensureSpaceFree(grantedRoles); } /** * Assigns the model descriptor backing the view. The model descriptor serves * several purposes : * <ul> * <li>configuration of the view content. For instance whenever a form is * assigned a component model descriptor, it will install 1 field per * component rendering properties, unless otherwise specified in the view * descriptor itself.</li> * <li>configuration of the binding layer. There is no need for the developer * to configure anything for the binding to occur between the view and the * model. Based on their model descriptor, Jspresso will setup all the * necessary plumbing to efficiently synchronize model properties with their * view counterpart bi-directionally. This synchronization occurs implicitly * using the <i>observer</i> pattern and one of the Jspresso key contract is * to guarantee this synchronization seamlessly.</li> * </ul> * Although it is the developer responsibility to make sure the correct model * descriptor is assigned to the view, there are cases where the framework * will infer it. For instance, a composite view will by default transmit its * model descriptor to its children that do not have their model descriptor * explicitly set. This allows for setting the model descriptor only on the * composite view and keep default {@code null} value on the children as * an implicit model inheritance enablement. * * @param modelDescriptor * the modelDescriptor to set. */ public void setModelDescriptor(IModelDescriptor modelDescriptor) { this.modelDescriptor = modelDescriptor; } /** * Allows to set a preferred height (in pixels) for the created peer UI * component. This will override default and give hints to the UI layouting * system. * * @param preferredHeight * the preferredHeight to set. */ public void setPreferredHeight(Integer preferredHeight) { this.preferredHeight = preferredHeight; } /** * Allows to set a preferred width (in pixels) for the created peer UI * component. This will override default and give hints to the UI layouting * system. * * @param preferredWidth * the preferredWidth to set. */ public void setPreferredWidth(Integer preferredWidth) { this.preferredWidth = preferredWidth; } /** * Sets the readabilityGates. * * @param readabilityGates * the readabilityGates to set. * @internal */ public void setReadabilityGates(Collection<IGate> readabilityGates) { this.readabilityGates = readabilityGates; } /** * Allows to set a view read-only, i.e. none of the view part will allow for * updating the underlying model. This is mainly a shortcut to assigning an * "always closed" writability gate. One difference though is that, * since the framework knows that the view will never be updatable, it may * take specific decisions to render properties in a slightly different way, * e.g. instead of using a disabled text field, use a label. * <p/> * Default value is {@code false}, i.e. view is updatable. * * @param readOnly * the readOnly to set. */ public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } /** * Assigns a collection of gates to determine view <i>writability</i>. A view * will be considered writable (updatable) if and only if all gates are open. * This mechanism is mainly used for dynamic UI authorization based on model * state, e.g. a validated invoice should not be editable anymore. * <p/> * View assigned gates will be cloned for each view instance created and * backed by this descriptor. So basically, each view instance will have its * own, unshared collection of writability gates. * <p/> * Jspresso provides a useful set of gate types, like the binary property gate * that open/close based on the value of a boolean property of the view model. * <p/> * By default, view descriptors are not assigned any gates collection, i.e. * there is no writability restriction. Note however that view actual * writability is the combination of view <i>and</i> model writability. * * @param writabilityGates * the writabilityGates to set. */ public void setWritabilityGates(Collection<IGate> writabilityGates) { this.writabilityGates = writabilityGates; } /** * Gets the preferredHeight. * * @return the preferredHeight. */ protected Integer getPreferredHeight() { return preferredHeight; } /** * Gets the preferredWidth. * * @return the preferredWidth. */ protected Integer getPreferredWidth() { return preferredWidth; } /** * Gets the secondaryActionMap. * * @return the secondaryActionMap. */ @Override public ActionMap getSecondaryActionMap() { return secondaryActionMap; } /** * Assigns the view secondary action map. Same rules as the primary action map * apply except that actions in this map should be visually distinguished from * the main action map, e.g. placed in another toolbar. * * @param secondaryActionMap * the secondaryActionMap to set. */ public void setSecondaryActionMap(ActionMap secondaryActionMap) { this.secondaryActionMap = secondaryActionMap; } /** * Gets the styleName. * * @return the styleName. */ @Override public String getStyleName() { return styleName; } /** * Assigns the style name to use for this view. The way it is actually * leveraged depends on the UI channel. It will generally be mapped to some * sort of CSS style name. * <p/> * Default value is {@code null}, meaning that a default style is used. * * @param styleName * the styleName to set. */ public void setStyleName(String styleName) { this.styleName = styleName; } /** * {@inheritDoc} */ @Override public BasicViewDescriptor clone() { BasicViewDescriptor clone = (BasicViewDescriptor) super.clone(); clone.permId = null; return clone; } }