/* * 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.application.model; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.jspresso.framework.action.IAction; import org.jspresso.framework.application.view.descriptor.basic.BasicWorkspaceViewDescriptor; import org.jspresso.framework.security.ISecurable; import org.jspresso.framework.security.ISecurityHandler; import org.jspresso.framework.util.automation.IPermIdSource; import org.jspresso.framework.util.bean.AbstractPropertyChangeCapable; import org.jspresso.framework.util.gui.Icon; import org.jspresso.framework.util.gui.IconProvider; import org.jspresso.framework.util.lang.StringUtils; import org.jspresso.framework.view.descriptor.IViewDescriptor; import org.jspresso.framework.view.descriptor.basic.AbstractTreeViewDescriptor; /** * A workspace is an group of functional application modules. You may decide * arbitrarily how to group modules into workspaces but a good approach might be * to design the workspaces based on roles (i.e. business activities). This * helps to clearly separates tasks-unrelated modules and eases authorization * management since a workspace can be granted or forbidden as a whole by * Jspresso security. * <p/> * Workspaces might be graphically represented differently depending on the UI * technology used. For instance, the Swing and ULC channels use a MDI UI in * which each workspace is represented as an internal frame (document). On the * other hand, Flex and qooxdoo channels represent workspaces stacked in an * accordion. Whatever the graphical representation is, there is at most one * workspace active at a time for a user session - either the active (focused) * internal frame or the expanded accordion section. * * @author Vincent Vandenschrick */ public class Workspace extends AbstractPropertyChangeCapable implements ISecurable, IPermIdSource { /** * {@code DESCRIPTION} is "description". */ public static final String DESCRIPTION = "description"; /** * {@code I18N_DESCRIPTION} is "i18nDescription". */ public static final String I18N_DESCRIPTION = "i18nDescription"; /** * {@code I18N_NAME} is "i18nName". */ public static final String I18N_NAME = "i18nName"; /** * {@code MODULES} is "modules". */ public static final String MODULES = "modules"; /** * {@code NAME} is "name". */ public static final String NAME = "name"; /** * {@code HEADER_DESCRIPTION} is "headerDescription". */ public static final String HEADER_DESCRIPTION = "headerDescription"; /** * {@code I18N_HEADER_DESCRIPTION} is "i18nHeaderDescription". */ public static final String I18N_HEADER_DESCRIPTION = "i18nHeaderDescription"; /** * {@code PAGE_HEADER_DESCRIPTION} is "pageHeaderDescription". */ public static final String PAGE_HEADER_DESCRIPTION = "pageHeaderDescription"; /** * {@code I18N_PAGE_HEADER_DESCRIPTION} is "i18nPageHeaderDescription". */ public static final String I18N_PAGE_HEADER_DESCRIPTION = "i18nPageHeaderDescription"; private String description; private String pageHeaderDescription; private String i18nHeaderDescription; private String i18nPageHeaderDescription; private Collection<String> grantedRoles; private String headerDescription; private String i18nDescription; private String i18nName; private Icon icon; private IconProvider iconProvider; private IAction itemSelectionAction; private List<Module> modules; private String name; private boolean startExpanded; private boolean started; private IAction startupAction; private ISecurityHandler securityHandler; private IViewDescriptor viewDescriptor; private String permId; /** * Constructs a new {@code Workspace} instance. */ public Workspace() { startExpanded = true; started = false; } /** * Equality based on name. * <p/> * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (!(obj instanceof Workspace)) { return false; } if (this == obj) { return true; } Workspace rhs = (Workspace) obj; return new EqualsBuilder().append(getName(), rhs.getName()).isEquals(); } /** * Gets the workspace description. It may serve for the workspace view. * * @return the workspace description. */ public String getDescription() { return description; } /** * Gets the grantedRoles. * * @return the grantedRoles. */ @Override public Collection<String> getGrantedRoles() { return grantedRoles; } /** * Gets the i18nDescription. * * @return the i18nDescription. */ public String getI18nDescription() { if (i18nDescription != null) { return i18nDescription; } return getDescription(); } /** * Gets the i18nName. * * @return the i18nName. */ public String getI18nName() { if (i18nName != null) { return i18nName; } return getName(); } /** * Gets the icon. * * @return the icon. */ public Icon getIcon() { return icon; } /** * Gets the item selection action that will be attached to the created view * that displays the workspace. * * @return the item selection action that will be attached to the created view * that displays the workspace */ public IAction getItemSelectionAction() { return itemSelectionAction; } /** * Gets the modules modules. * * @return the list of modules belonging to this workspace. */ public List<Module> getModules() { return getModules(false); } /** * Gets the modules modules. * * @param bypassSecurity * bypasses security restrictions imposed to the user. * @return the list of modules belonging to this workspace. */ public List<Module> getModules(boolean bypassSecurity) { if (modules == null) { return null; } ISecurityHandler sh = getSecurityHandler(); if (sh != null) { for (Iterator<Module> ite = modules.iterator(); ite.hasNext(); ) { Module nextModule = ite.next(); if (!bypassSecurity && !sh.isAccessGranted(nextModule)) { try { sh.pushToSecurityContext(nextModule); ite.remove(); } finally { sh.restoreLastSecurityContextSnapshot(); } } } } return modules; } /** * Gets the workspace name. It may serve for the workspace view. * * @return the workspace name. */ public String getName() { return name; } /** * Gets the startupAction. * * @return the startupAction. */ public IAction getStartupAction() { return startupAction; } /** * Gets the workspace view descriptor. Whenever the view descriptor has not * been set, a default one is generated. * * @return the viewDescriptor. */ public IViewDescriptor getViewDescriptor() { if (viewDescriptor == null) { AbstractTreeViewDescriptor workspaceViewDescriptor = createWorkspaceViewDescriptor(); workspaceViewDescriptor.setName(getName()); workspaceViewDescriptor.setDescription(getDescription()); workspaceViewDescriptor.setIcon(getIcon()); workspaceViewDescriptor.setIconImageURLProvider(getIconProvider()); workspaceViewDescriptor.setItemSelectionAction(getItemSelectionAction()); viewDescriptor = workspaceViewDescriptor; } return viewDescriptor; } /** * Create workspace view descriptor. * * @return the workspace view descriptor */ protected AbstractTreeViewDescriptor createWorkspaceViewDescriptor() { BasicWorkspaceViewDescriptor workspaceViewDescriptor = new BasicWorkspaceViewDescriptor(); workspaceViewDescriptor.setExpanded(isStartExpanded()); return workspaceViewDescriptor; } /** * Hash code based on name. * <p/> * {@inheritDoc} */ @Override public int hashCode() { return new HashCodeBuilder(23, 53).append(name).toHashCode(); } /** * Gets the started. * * @return the started. */ public boolean isStarted() { return started; } /** * Configures the key used to translate actual internationalized workspace * description. The resulting translation will generally be leveraged as a * toolTip on the UI side but its use may be extended for online help. * * @param description * the workspace description. */ public void setDescription(String description) { this.description = description; } /** * Assigns the roles that are authorized to start this workspace. It supports * "<b>!</b>" prefix to negate the role(s). Whenever the user is not * granted sufficient privileges, the workspace is not installed at all in the * application frame. Setting the collection of granted roles to * {@code null} (default value) disables role based authorization on this * workspace. * * @param grantedRoles * the grantedRoles to set. */ public void setGrantedRoles(Collection<String> grantedRoles) { this.grantedRoles = StringUtils.ensureSpaceFree(grantedRoles); } /** * Stores the internationalized workspace description for use in the UI as * toolTip for instance. * * @param i18nDescription * the i18nDescription to set. * @internal */ public void setI18nDescription(String i18nDescription) { this.i18nDescription = i18nDescription; } /** * Stores the internationalized workspace name for use in the UI as workspace * label. * * @param i18nName * the i18nName to set. * @internal */ public void setI18nName(String i18nName) { this.i18nName = i18nName; } /** * Sets the icon image URL of this workspace. Supported URL protocols include * : * <ul> * <li>all JVM supported protocols</li> * <li>the <b>jar:/</b> pseudo URL protocol</li> * <li>the <b>classpath:/</b> pseudo URL protocol</li> * </ul> * * @param iconImageURL * the iconImageURL to set. */ public void setIconImageURL(String iconImageURL) { if (icon == null) { icon = new Icon(); } icon.setIconImageURL(iconImageURL); } /** * Sets the icon preferred width. * * @param iconPreferredWidth * the iconPreferredWidth to set. */ public void setIconPreferredWidth(int iconPreferredWidth) { if (icon == null) { icon = new Icon(); } icon.setWidth(iconPreferredWidth); } /** * Sets the icon preferred width. * * @param iconPreferredHeight * the iconPreferredHeight to set. */ public void setIconPreferredHeight(int iconPreferredHeight) { if (icon == null) { icon = new Icon(); } icon.setHeight(iconPreferredHeight); } /** * Since a workspace is represented as a tree view of modules, this property * can be used to customize an icon image URL provider on the created tree * view (see {@code BasicTreeViewDescriptor.iconImageURLProvider}). * However, the workspace built-in icon image URL provider ( * {@code WorkspaceIconImageURLProvider}) will setup sensible defaults so * that it unlikely has to be changed. * * @param iconImageURLProvider * the iconImageURLProvider to set. */ public void setIconProvider(IconProvider iconImageURLProvider) { this.iconProvider = iconImageURLProvider; } /** * Gets icon provider. * * @return the icon provider */ protected IconProvider getIconProvider() { if (iconProvider == null) { iconProvider = new WorkspaceIconProvider(); } return iconProvider; } /** * Configures the action to be installed as item selection action on the * rendered module tree view - see * {@code BasicTreeViewDescriptor.itemSelectionAction}. The configured * action will then be executed each time a module selection changes in the * workspace. * * @param itemSelectionAction * the itemSelectionAction to set. */ public void setItemSelectionAction(IAction itemSelectionAction) { this.itemSelectionAction = itemSelectionAction; } /** * Installs a list of module(s) into this workspace. Each module may own * sub-modules that form a (potentially complex and dynamic) hierarchy, that * is visually rendered as a tree view. * * @param modules * the modules modules to set. */ public void setModules(List<Module> modules) { List<Module> oldValue = null; if (getModules() != null) { oldValue = new ArrayList<>(getModules()); } this.modules = modules; if (this.modules != null) { for (Module module : this.modules) { module.setSecurityHandler(getSecurityHandler()); } } firePropertyChange(MODULES, oldValue, getModules()); } /** * Configures the key used to translate actual internationalized workspace * name. The resulting translation will be leveraged as the workspace label on * the UI side. * * @param name * the module's name. */ public void setName(String name) { this.name = name; } /** * Sets the started. * * @param started * the started to set. * @internal */ public void setStarted(boolean started) { this.started = started; } /** * Configures an action to be executed the first time the workspace is * "started" by the user. The action will execute in the context of * the workspace but with no specific module selected. It will help * initializing workspace values, notify user, ... * * @param startupAction * the startupAction to set. */ public void setStartupAction(IAction startupAction) { this.startupAction = startupAction; } /** * Configures the security handler used to secure this module. * * @param securityHandler * the security handler. * @internal */ public void setSecurityHandler(ISecurityHandler securityHandler) { this.securityHandler = securityHandler; if (this.modules != null) { for (Module module : this.modules) { module.setSecurityHandler(getSecurityHandler()); } } } /** * based on i18n name. * <p/> * {@inheritDoc} */ @Override public String toString() { if (getI18nName() != null) { return getI18nName(); } return ""; } /** * Gets the securityHandler. * * @return the securityHandler. */ private ISecurityHandler getSecurityHandler() { return securityHandler; } /** * Gets the permId. * * @return the permId. */ @Override public String getPermId() { if (permId != null) { return permId; } return getName(); } /** * 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 * the permId to set. */ @Override public void setPermId(String permId) { this.permId = permId; } /** * Gets header description. * * @return the header description */ public String getHeaderDescription() { return headerDescription; } /** * Configures the key used to translate actual internationalized workspace * header description. The resulting translation will generally be leveraged as a * text header on the UI side. * * @param headerDescription * the header description */ public void setHeaderDescription(String headerDescription) { this.headerDescription = headerDescription; } /** * Gets i 18 n header description. * * @return the i 18 n header description */ public String getI18nHeaderDescription() { if (i18nHeaderDescription != null) { return i18nHeaderDescription; } return getHeaderDescription(); } /** * Sets i 18 n header description. * * @param i18nHeaderDescription * the i 18 n header description */ public void setI18nHeaderDescription(String i18nHeaderDescription) { this.i18nHeaderDescription = i18nHeaderDescription; } /** * Gets page header description. * * @return the page header description */ public String getPageHeaderDescription() { return pageHeaderDescription; } /** * Configures the key used to translate actual internationalized * workspace page header description. The resulting translation will generally be * leveraged as a textual section that explains the workspace goal. * * @param pageHeaderDescription * the page header description */ public void setPageHeaderDescription(String pageHeaderDescription) { this.pageHeaderDescription = pageHeaderDescription; } /** * Gets i 18 n page header description. * * @return the i 18 n page header description */ public String getI18nPageHeaderDescription() { if (i18nPageHeaderDescription != null) { return i18nPageHeaderDescription; } return getPageHeaderDescription(); } /** * Sets i 18 n page header description. * * @param i18nPageHeaderDescription * the 18 n page header description */ public void setI18nPageHeaderDescription(String i18nPageHeaderDescription) { this.i18nPageHeaderDescription = i18nPageHeaderDescription; } /** * Should the module tree start expanded. * * @return the boolean */ public boolean isStartExpanded() { return startExpanded; } /** * Sets expanded. * * @param startExpanded * the expanded */ public void setStartExpanded(boolean startExpanded) { this.startExpanded = startExpanded; } }