/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.ui.rcp.handlers; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.jubula.client.core.businessprocess.CapBP; import org.eclipse.jubula.client.core.businessprocess.UsedToolkitBP; import org.eclipse.jubula.client.core.businessprocess.progress.ProgressMonitorTracker; import org.eclipse.jubula.client.core.businessprocess.treeoperations.CollectComponentNameUsersOp; import org.eclipse.jubula.client.core.datastructure.CompNameUsageMap; import org.eclipse.jubula.client.core.events.DataChangedEvent; import org.eclipse.jubula.client.core.events.DataEventDispatcher; import org.eclipse.jubula.client.core.events.DataEventDispatcher.DataState; import org.eclipse.jubula.client.core.events.DataEventDispatcher.ProjectState; import org.eclipse.jubula.client.core.events.DataEventDispatcher.UpdateState; import org.eclipse.jubula.client.core.model.ICapPO; import org.eclipse.jubula.client.core.model.ICategoryPO; import org.eclipse.jubula.client.core.model.IExecTestCasePO; import org.eclipse.jubula.client.core.model.INodePO; import org.eclipse.jubula.client.core.model.IProjectPO; import org.eclipse.jubula.client.core.model.IReusedProjectPO; import org.eclipse.jubula.client.core.model.ISpecObjContPO; import org.eclipse.jubula.client.core.model.ISpecTestCasePO; import org.eclipse.jubula.client.core.persistence.GeneralStorage; import org.eclipse.jubula.client.core.persistence.MultipleNodePM; import org.eclipse.jubula.client.core.persistence.NodePM; import org.eclipse.jubula.client.core.persistence.Persistor; import org.eclipse.jubula.client.core.persistence.ProjectPM; import org.eclipse.jubula.client.core.utils.TreeTraverser; import org.eclipse.jubula.client.ui.constants.ContextHelpIds; import org.eclipse.jubula.client.ui.constants.IconConstants; import org.eclipse.jubula.client.ui.handlers.AbstractHandler; import org.eclipse.jubula.client.ui.rcp.Plugin; import org.eclipse.jubula.client.ui.rcp.controllers.MultipleTCBTracker; import org.eclipse.jubula.client.ui.rcp.controllers.PMExceptionHandler; import org.eclipse.jubula.client.ui.rcp.dialogs.ReusedProjectSelectionDialog; import org.eclipse.jubula.client.ui.rcp.i18n.Messages; import org.eclipse.jubula.client.ui.rcp.utils.Utils; import org.eclipse.jubula.client.ui.rcp.views.TestCaseBrowser; import org.eclipse.jubula.client.ui.utils.DialogUtils; import org.eclipse.jubula.client.ui.utils.ErrorHandlingUtil; import org.eclipse.jubula.toolkit.common.businessprocess.ToolkitSupportBP; import org.eclipse.jubula.toolkit.common.exception.ToolkitPluginException; import org.eclipse.jubula.toolkit.common.utils.ToolkitUtils; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.exception.JBException; import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.eclipse.jubula.tools.internal.messagehandling.MessageInfo; import org.eclipse.jubula.tools.internal.xml.businessmodell.Component; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.PlatformUI; /** * @author BREDEX GmbH * @created Oct 15, 2007 */ public class MoveTestCaseHandler extends AbstractHandler { /** * Represents problems with moving one or more Test Cases. * * @author BREDEX GmbH * @created Oct 17, 2007 */ private static class ProblemSet { /** valid problems */ private Set<INodePO> m_problems = new HashSet<INodePO>(); /** list of nodes that are being moved */ private List<INodePO> m_nodesToMove; /** * Constructor * * @param nodesToMove The nodes that are to be moved. */ public ProblemSet(List<INodePO> nodesToMove) { setNodesToMove(new ArrayList<INodePO>()); for (INodePO node : nodesToMove) { addCatChildren(node, getNodesToMove()); } } /** * @return List of valid problems. */ public Set<INodePO> getProblems() { return m_problems; } /** * @return the nodesToMove */ public List<INodePO> getNodesToMove() { return m_nodesToMove; } /** * @param nodesToMove the nodesToMove to set */ private void setNodesToMove(List<INodePO> nodesToMove) { m_nodesToMove = nodesToMove; } } /** * @author BREDEX GmbH * @created 22.01.2013 */ private static class RefreshReusedProjectOperation implements IRunnableWithProgress { /** the selected project */ private IReusedProjectPO m_selectedProject; /** * * @param reusedProject * the reused project which should be refreshed */ public RefreshReusedProjectOperation(IReusedProjectPO reusedProject) { m_selectedProject = reusedProject; } /** * {@inheritDoc} */ public void run(IProgressMonitor monitor) { monitor.beginTask( Messages.RefreshProjectOperationRefreshing, IProgressMonitor.UNKNOWN); ProgressMonitorTracker instance = ProgressMonitorTracker.SINGLETON; instance.setProgressMonitor(monitor); try { IProjectPO referencedProject = ProjectPM .loadReusedProjectInMasterSession(m_selectedProject); GeneralStorage.getInstance().getMasterSession().refresh( referencedProject.getSpecObjCont()); DataEventDispatcher.getInstance() .fireDataChangedListener(new DataChangedEvent( referencedProject, DataState.StructureModified, UpdateState.all)); } catch (ProjectDeletedException e) { PMExceptionHandler.handleProjectDeletedException(); } catch (JBException e) { ErrorHandlingUtil.createMessageDialog(e, null, null); } finally { instance.setProgressMonitor(null); monitor.done(); } } } /** * * {@inheritDoc} */ @SuppressWarnings("unchecked") public Object executeImpl(ExecutionEvent event) { // Gather selected nodes TestCaseBrowser tcb = MultipleTCBTracker.getInstance().getMainTCB(); if (!(tcb.getSelection() instanceof IStructuredSelection)) { return null; } IStructuredSelection sel = (IStructuredSelection)tcb.getSelection(); List<INodePO> selectionList = sel.toList(); List <INodePO> nodesToMove = new ArrayList<INodePO>(); for (INodePO node : selectionList) { MultipleNodePM.collectAffectedNodes(nodesToMove, node); } if (!closeRelatedEditors(nodesToMove)) { return null; } // Check if move is valid ProblemSet moveProblems = getMoveProblem(nodesToMove); if (moveProblems.getProblems().isEmpty()) { Set<IReusedProjectPO> reusedProjects = GeneralStorage.getInstance().getProject().getUsedProjects(); List<String> projectNamesList = new ArrayList<String>(); for (IReusedProjectPO project : reusedProjects) { projectNamesList.add(project.getName()); } String [] projectNames = projectNamesList.toArray(new String [projectNamesList.size()]); ReusedProjectSelectionDialog dialog; if (hasRefDataCube(selectionList)) { dialog = new ReusedProjectSelectionDialog( getActiveShell(), projectNames, Messages.MoveTestCaseDialogShellTitle, Messages.MoveTestCaseDialogCTDHint, true, IconConstants.MOVE_TC_DIALOG_STRING, Messages.MoveTestCaseDialogShellTitle); } else { dialog = new ReusedProjectSelectionDialog( getActiveShell(), projectNames, Messages.MoveTestCaseDialogShellTitle, Messages.MoveTestCaseDialogMessage, IconConstants.MOVE_TC_DIALOG_STRING, Messages.MoveTestCaseDialogShellTitle); } dialog.setHelpAvailable(true); dialog.create(); DialogUtils.setWidgetNameForModalDialog(dialog); Plugin.getHelpSystem().setHelp(dialog.getShell(), ContextHelpIds.TESTCASE_MOVE_EXTERNAL); dialog.open(); if (dialog.getReturnCode() == Window.OK) { // Check which project was selected String selectedName = dialog.getSelectedName(); IReusedProjectPO selectedProject = null; for (IReusedProjectPO project : reusedProjects) { if (selectedName.equals(project.getName())) { selectedProject = project; break; } } doMove(tcb, selectionList, selectedProject); } } else { showProblems(moveProblems); } return null; } /** {@inheritDoc} */ public void setEnabled(boolean enabled) { IProjectPO currentProject = GeneralStorage.getInstance().getProject(); boolean projectAvailable = currentProject == null ? false : !currentProject.getUsedProjects().isEmpty(); super.setEnabled(enabled && projectAvailable); } /** * Closes all editors that are related to elements in the given list. * * @param selectionList List of GuiNodes. * @return <code>true</code> if all editors were successfully closed. * Otherwise, <code>false</code>. */ private boolean closeRelatedEditors(List<INodePO> selectionList) { List<IEditorReference> editorsToClose = new ArrayList<IEditorReference>(); for (INodePO node : selectionList) { IEditorReference editor = Utils.getEditorRefByPO(node); if (editor != null) { editorsToClose.add(editor); } } return Plugin.getActivePage().closeEditors( editorsToClose.toArray( new IEditorReference[editorsToClose.size()]), true); } /** * Performs the moving. * @param tcb the TestCase-Browser. * @param selectionList the selected Nodes to move. * @param selectedProject the selected Project to move to. */ private void doMove(TestCaseBrowser tcb, List<INodePO> selectionList, IReusedProjectPO selectedProject) { // Prepare modification to selected project EntityManager sess = null; Persistor per = Persistor.instance(); try { IProjectPO extProject = ProjectPM.loadReusedProject( selectedProject); sess = per.openSession(); EntityTransaction tx = per.getTransaction(sess); extProject = sess.find(extProject.getClass(), extProject.getId()); List<ICapPO> moveProblem = getMoveProblem(extProject, selectionList); if (!moveProblem.isEmpty()) { ErrorHandlingUtil.createMessageDialog( MessageIDs.E_MOVE_TO_EXT_PROJ_ERROR_TOOLKITLEVEL, null, null); return; } ISpecObjContPO newParent = extProject.getSpecObjCont(); List<MultipleNodePM.AbstractCmdHandle> commands = createCommands(selectionList, newParent, extProject); // Perform move MessageInfo errorMessageInfo = MultipleNodePM.getInstance().executeCommands( commands, sess); DataEventDispatcher.getInstance().fireDataChangedListener( newParent, DataState.StructureModified, UpdateState.all); if (errorMessageInfo == null) { GeneralStorage.getInstance().getMasterSession().refresh( GeneralStorage.getInstance().getProject() .getSpecObjCont()); Plugin.startLongRunning(Messages .RefreshTSBrowserActionProgressMessage); try { PlatformUI.getWorkbench().getProgressService() .run(true, false, new RefreshReusedProjectOperation(selectedProject)); } catch (InvocationTargetException e) { // Already handled within the operation. } catch (InterruptedException e) { Utils.clearClient(); } finally { Plugin.stopLongRunning(); DataEventDispatcher.getInstance().fireProjectStateChanged( ProjectState.opened); } tcb.getTreeViewer().refresh(); } else { ErrorHandlingUtil.createMessageDialog( errorMessageInfo.getMessageId(), errorMessageInfo.getParams(), null); } } catch (JBException e) { ErrorHandlingUtil.createMessageDialog(e, null, null); } catch (ToolkitPluginException tpie) { ErrorHandlingUtil.createMessageDialog( MessageIDs.E_GENERAL_TOOLKIT_ERROR); } finally { per.dropSession(sess); } } /** * Checks if the toolkit of the given selectionLists is compatible with * the given {@link IProjectPO} * @param extProject the {@link IProjectPO} to move the given selectionList to. * @param selectionList the selectionList to move to the given {@link IProjectPO} * @return A List of {@link ICapPO}s which are incompatible or an empty List * if everything is OK. * @throws ToolkitPluginException in case of a ToolkitPlugin error. */ private List<ICapPO> getMoveProblem(IProjectPO extProject, List<INodePO> selectionList) throws ToolkitPluginException { final List<ICapPO> problemCaps = new ArrayList<ICapPO>(); final String extToolkitId = extProject.getToolkit(); final String extToolkitLevel = ToolkitSupportBP.getToolkitLevel( extToolkitId); final List<ICapPO> caps = getCaps(selectionList); for (ICapPO cap : caps) { final String capLevel = UsedToolkitBP.getInstance() .getToolkitLevel(cap); final boolean capLessConcrete = !ToolkitUtils .isToolkitMoreConcrete(capLevel, extToolkitLevel); final Component component = CapBP.getComponent(cap); final String capToolkitID = component.getToolkitDesriptor() .getToolkitID(); if (!(capLessConcrete || capToolkitID.equals(extToolkitId))) { problemCaps.add(cap); } } return problemCaps; } /** * Gets all {@link ICapPO}s which are direct or indirect children of the * given List of {@link GuiNode}s * @param selectionList a List of {@link GuiNode}s * @return a List of {@link ICapPO}s */ private List<ICapPO> getCaps(List<INodePO> selectionList) { List<ICapPO> caps = new ArrayList<ICapPO>(); for (INodePO node : selectionList) { CapBP.getCaps(node, caps); } return caps; } /** * Displays the problems for a proposed move operation. * * @param moveProblems Valid problems with the proposed move operation. */ private void showProblems(ProblemSet moveProblems) { // Display info as to why TCs could not be moved StringBuilder sb = new StringBuilder(); for (INodePO moveProblem : moveProblems.getProblems()) { sb.append(moveProblem.getName()); sb.append(StringConstants.NEWLINE); } ErrorHandlingUtil.createMessageDialog(MessageIDs.I_CANNOT_MOVE_TC, null, new String [] { NLS.bind(Messages.InfoDetailCannotMoveTc, sb.toString()) }); } /** * * @param selectionList All nodes that are to be moved. * @param newParent The new parent for the nodes. * @param extProject where selected nodes moved to * * @return The commands necessary to move the given nodes. */ private List<MultipleNodePM.AbstractCmdHandle> createCommands( List<INodePO> selectionList, ISpecObjContPO newParent, IProjectPO extProject) throws JBException { List<MultipleNodePM.AbstractCmdHandle> commands = new ArrayList<MultipleNodePM.AbstractCmdHandle>(); CompNameUsageMap usageMap = new CompNameUsageMap(); final IProjectPO currenProject = GeneralStorage.getInstance() .getProject(); final Long projId = currenProject.getId(); for (INodePO selNode : selectionList) { commands.add(new MultipleNodePM.MoveNodeHandle(selNode, selNode .getParentNode(), newParent)); List<INodePO> specTcs = new ArrayList<INodePO>(); List<ISpecTestCasePO> specTcPOs = new ArrayList<ISpecTestCasePO>(); addCatChildren(selNode, specTcs); for (INodePO spec : specTcs) { ISpecTestCasePO specTestCasePo = (ISpecTestCasePO) spec; specTcPOs.add(specTestCasePo); CollectComponentNameUsersOp op = new CollectComponentNameUsersOp(projId); new TreeTraverser(specTestCasePo, op, true, 2).traverse(); usageMap.addAll(op.getUsageMap()); for (IExecTestCasePO execTc : NodePM.getInternalExecTestCases( specTestCasePo.getGuid(), specTestCasePo.getParentProjectId())) { commands.add(new MultipleNodePM.UpdateTestCaseRefHandle( execTc, specTestCasePo)); } } commands.add(new MultipleNodePM.UpdateParamNamesHandle(specTcPOs, extProject)); } commands.add(new MultipleNodePM.TransferCompNameHandle(usageMap, projId, extProject)); return commands; } /** * Indicates whether there is a problem with moving the given selection. If * there is a problem, it is described by the return value. * * @param selectionList * The elements that are to be moved * @return a set of problems */ private ProblemSet getMoveProblem(List<INodePO> selectionList) { ProblemSet problems = new ProblemSet(selectionList); getMoveProblem(selectionList, problems); return problems; } /** * Indicates whether there is a problem with moving the given selection. * * @param selectionList * The elements that are to be moved * @param problems * All problems with moving the given nodes. */ private void getMoveProblem(List<INodePO> selectionList, ProblemSet problems) { Long cProjId = GeneralStorage.getInstance().getProject().getId(); for (INodePO node : selectionList) { if (node instanceof IExecTestCasePO) { ISpecTestCasePO refTestCase = ((IExecTestCasePO)node).getSpecTestCase(); if (!problems.getNodesToMove().contains(refTestCase) && cProjId.equals(refTestCase.getParentProjectId())) { problems.getProblems().add(refTestCase); } } else { getMoveProblem(node.getUnmodifiableNodeList(), problems); } } } /** * checks if any of the node has ReferencedDataCubes * @param selectionList the list to be checked * @return returns <code>true</code> if there is one PO with a refDataCube */ private boolean hasRefDataCube(List<INodePO> selectionList) { for (INodePO selNode : selectionList) { List<INodePO> specTcs = new ArrayList<INodePO>(); addCatChildren(selNode, specTcs); for (INodePO spec : specTcs) { if (spec instanceof ISpecTestCasePO) { ISpecTestCasePO specTestCasePo = (ISpecTestCasePO)spec; if (specTestCasePo.getReferencedDataCube() != null) { return true; } Iterator<INodePO> execTcs = specTestCasePo .getAllNodeIter(); while (execTcs.hasNext()) { INodePO exec = execTcs.next(); if (exec instanceof IExecTestCasePO) { IExecTestCasePO execTestCasePo = (IExecTestCasePO)exec; if (execTestCasePo .getReferencedDataCube() != null) { return true; } } } } } } return false; } /** * Adds all spec testcase descendants of the given node to the given * list. * * @param parentNode The parent node * @param nodeList The node list. */ private static void addCatChildren( INodePO parentNode, Collection<INodePO> nodeList) { if (parentNode instanceof ICategoryPO) { for (INodePO node : parentNode.getUnmodifiableNodeList()) { addCatChildren(node, nodeList); } } else if (parentNode instanceof ISpecTestCasePO) { nodeList.add(parentNode); } } }