/* * 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.backend.action.module; import java.util.List; import java.util.Map; import java.util.Stack; import org.jspresso.framework.action.IActionHandler; import org.jspresso.framework.application.backend.BackendException; import org.jspresso.framework.application.backend.action.BackendAction; import org.jspresso.framework.application.model.BeanCollectionModule; import org.jspresso.framework.application.model.BeanModule; import org.jspresso.framework.application.model.Module; import org.jspresso.framework.application.model.Workspace; import org.jspresso.framework.model.descriptor.ICollectionDescriptorProvider; import org.jspresso.framework.model.descriptor.IComponentDescriptor; import org.jspresso.framework.model.descriptor.IComponentDescriptorProvider; import org.jspresso.framework.model.descriptor.IModelDescriptor; import org.jspresso.framework.view.descriptor.IViewDescriptor; /** * This action can be installed on any collection view and will : * <ol> * <li>take the selected elements in the underlying model collection,</li> * <li>create bean modules out of them, using the * {@code childModuleProjectedViewDescriptor} as projected view if it has * been configured,</li> * <li>add the created bean modules as children of the currently selected * module, visualizing them in the workspace navigation tree.</li> * </ol> * Whenever there is no {@code childModuleProjectedViewDescriptor} * configured, and the currently selected module is a bean collection module, * the created modules projected view descriptor is taken from the bean * collection module ({@code elementViewDescriptor}). * * @author Vincent Vandenschrick */ public class AddBeanAsSubModuleAction extends BackendAction { /** * {@code PARENT_WORKSPACE} is "PARENT_WORKSPACE". */ public static final String PARENT_WORKSPACE = "PARENT_WORKSPACE"; /** * {@code PARENT_MODULE_NAME} is "PARENT_MODULE_NAME". */ public static final String PARENT_MODULE_NAME = "PARENT_MODULE_NAME"; /** * {@code PROJECTED_VIEW_DESCRIPTOR} is * "PROJECTED_VIEW_DESCRIPTOR". */ public static final String PROJECTED_VIEW_DESCRIPTOR = "PROJECTED_VIEW_DESCRIPTOR"; private IViewDescriptor childModuleProjectedViewDescriptor; /** * Adds the selected objects as child modules. * <p> * {@inheritDoc} */ @Override public boolean execute(IActionHandler actionHandler, Map<String, Object> context) { List<?> selectedModels = getActionParameter(context); if (selectedModels == null) { selectedModels = getSelectedModels(context); } Module moduleToSelect = null; for (Object nextSelectedModuleObject : selectedModels) { preSelectModuleObject(nextSelectedModuleObject, context); Module parentModule = findDestinationModule(nextSelectedModuleObject, context); if (parentModule == null) { Workspace parentWorkspace = getParentWorkspace(context); String parentWorkspaceName = parentWorkspace!=null ? parentWorkspace.getName() : "null"; throw new BackendException("Destination module not found for workpace '" + parentWorkspaceName + "' and module '" + getParentModuleName(context) + "'"); } IComponentDescriptor<?> beanComponentDescriptor = getBeanComponentDescriptor( parentModule, context); List<Module> childModules = parentModule.getSubModules(); Module newSubModule = null; Module nextSubModule = createChildModule(parentModule, beanComponentDescriptor, nextSelectedModuleObject, context); int nextSubModuleIndex = -1; if (childModules != null) { nextSubModuleIndex = childModules.indexOf(nextSubModule); } if (nextSubModuleIndex < 0) { newSubModule = nextSubModule; if (moduleToSelect == null) { moduleToSelect = nextSubModule; } } else if (moduleToSelect == null) { moduleToSelect = childModules.get(nextSubModuleIndex); } if (newSubModule != null) { parentModule.addSubModule(newSubModule); } postSelectModuleObject(nextSelectedModuleObject, context); } setActionParameter(moduleToSelect, context); return super.execute(actionHandler, context); } /** * Retrieves the bean component descriptor to create the child bean module. * * @param parentModule * the parent module to add the child bean module to. * @param context * the action context. * @return the bean module component descriptor. */ @SuppressWarnings("UnusedParameters") protected IComponentDescriptor<?> getBeanComponentDescriptor( Module parentModule, Map<String, Object> context) { IModelDescriptor modelDescriptor = getModelDescriptor(context); IComponentDescriptor<?> childComponentDescriptor; if (modelDescriptor instanceof ICollectionDescriptorProvider<?>) { childComponentDescriptor = ((ICollectionDescriptorProvider<?>) modelDescriptor) .getCollectionDescriptor().getElementDescriptor(); } else { childComponentDescriptor = ((IComponentDescriptorProvider<?>) modelDescriptor) .getComponentDescriptor(); } return childComponentDescriptor; } /** * Sets the childModuleProjectedViewDescriptor. * * @param childModuleProjectedViewDescriptor * the childModuleProjectedViewDescriptor to set. */ public void setChildModuleProjectedViewDescriptor( IViewDescriptor childModuleProjectedViewDescriptor) { this.childModuleProjectedViewDescriptor = childModuleProjectedViewDescriptor; } /** * Creates a module to be added to the currently selected module as child. * * @param parentModule * the parent module. * @param childComponentDescriptor * the child component descriptor. * @param childModuleObject * the child module object to create the child module for. * @param context * the action context. * @return the created child module. */ @SuppressWarnings("unchecked") protected Module createChildModule(Module parentModule, IComponentDescriptor<?> childComponentDescriptor, Object childModuleObject, Map<String, Object> context) { BeanModule childModule = new BeanModule(); IViewDescriptor projectedViewDescriptor = getChildModuleProjectedViewDescriptor(context); if (projectedViewDescriptor != null) { childModule.setProjectedViewDescriptor(projectedViewDescriptor); } else if (parentModule instanceof BeanCollectionModule) { childModule .setProjectedViewDescriptor(((BeanCollectionModule) parentModule) .getElementViewDescriptor()); } childModule .setComponentDescriptor((IComponentDescriptor<Object>) childComponentDescriptor); childModule.setModuleObject(childModuleObject); childModule.setI18nName(String.valueOf(childModuleObject)); return childModule; } /** * Gets the childModuleProjectedViewDescriptor. * * @param context * the action context. * @return the childModuleProjectedViewDescriptor. */ protected IViewDescriptor getChildModuleProjectedViewDescriptor( Map<String, Object> context) { if (childModuleProjectedViewDescriptor != null) { return childModuleProjectedViewDescriptor; } return (IViewDescriptor) context.get(PROJECTED_VIEW_DESCRIPTOR); } /** * Gets the parent workspace if found in context using key * {@link AddBeanAsSubModuleAction#PARENT_WORKSPACE}. * * @param context * the action context. * @return the target workspace name. */ protected Workspace getParentWorkspace(Map<String, Object> context) { return (Workspace) context.get(PARENT_WORKSPACE); } /** * Gets parent module name if found in context using key * {@link AddBeanAsSubModuleAction#PARENT_MODULE_NAME}. * * @param context * the action context. * @return the target parent module name. */ public static String getParentModuleName(Map<String, Object> context) { return (String) context.get(PARENT_MODULE_NAME); } /** * Finds the parent module where to add the selected component. * * @param component * the component to add as module object. * @param context * the action context. * @return the parent module to add the new module to. */ @SuppressWarnings("UnusedParameters") protected Module findDestinationModule(Object component, Map<String, Object> context) { Workspace workspace = getParentWorkspace(context); String moduleName = getParentModuleName(context); if (workspace == null || moduleName == null) { return getCurrentModule(context); } for (Module m : workspace.getModules()) { Module m2 = findModule(m, moduleName, null); if (m2 != null) { return m2; } } return null; } /** * Notifies that a module object is going to been selected. Does nothing by * default. Override if needed. * * @param moduleObject * the module object that will been selected. * @param context * the action context. */ @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) protected void preSelectModuleObject(Object moduleObject, Map<String, Object> context) { // Empty implementation by default. } /** * Notifies that a module object has been selected. Does nothing by default. * Override if needed. * * @param moduleObject * the module object that has been selected. * @param context * the action context. */ @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) protected void postSelectModuleObject(Object moduleObject, Map<String, Object> context) { // Empty implementation by default. } /** * Finds the existing module for a module object. * * @param rootModule * the root module to start the search from. * @param moduleObject * the module object. * @param context * the action context. * @return the stack of modules containing the found module. */ @SuppressWarnings("UnusedParameters") public Stack<Module> findCurrentModule(Module rootModule, Object moduleObject, Map<String, Object> context) { Stack<Module> stack = new Stack<>(); findCurrentModule(rootModule, moduleObject, stack); return stack; } private Module findCurrentModule(Module rootModule, Object nextSelectedModuleObject, Stack<Module> stack) { stack.push(rootModule); if (rootModule instanceof BeanModule && nextSelectedModuleObject.equals(((BeanModule) rootModule) .getModuleObject())) { return rootModule; } if (rootModule.getSubModules() != null) { for (Module sub : rootModule.getSubModules()) { Module parentModule = findCurrentModule(sub, nextSelectedModuleObject, stack); if (parentModule != null) { return parentModule; } } } stack.pop(); return null; } /** * Finds the existing module for a module object or name. * * @param rootModule * the root module to start the search from. * @param moduleObject * the module object. * @param moduleName * the module name. * @return the stack of modules containing the found module. */ protected Module findModule(Module rootModule, String moduleName, Object moduleObject) { if (moduleName != null && moduleName.equals(rootModule.getName())) { return rootModule; } if (moduleObject != null && (rootModule instanceof BeanModule) && moduleObject.equals(((BeanModule) rootModule).getModuleObject())) { return rootModule; } if (rootModule.getSubModules() != null) { for (Module sub : rootModule.getSubModules()) { Module parentModule = findModule(sub, moduleName, moduleObject); if (parentModule != null) { return parentModule; } } } return null; } }