/*******************************************************************************
* 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.delete;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jubula.client.core.businessprocess.ParamNameBP;
import org.eclipse.jubula.client.core.businessprocess.ParamNameBPDecorator;
import org.eclipse.jubula.client.core.businessprocess.UnusedSpecTestCasesBP;
import org.eclipse.jubula.client.core.businessprocess.progress.ProgressMonitorTracker;
import org.eclipse.jubula.client.core.events.DataEventDispatcher;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.TestresultState;
import org.eclipse.jubula.client.core.model.IEventExecTestCasePO;
import org.eclipse.jubula.client.core.model.IExecTestCasePO;
import org.eclipse.jubula.client.core.model.INodePO;
import org.eclipse.jubula.client.core.model.IPersistentObject;
import org.eclipse.jubula.client.core.model.ISpecTestCasePO;
import org.eclipse.jubula.client.core.persistence.MultipleNodePM;
import org.eclipse.jubula.client.core.persistence.MultipleNodePM.AbstractCmdHandle;
import org.eclipse.jubula.client.core.persistence.MultipleNodePM.DeleteEvHandle;
import org.eclipse.jubula.client.core.persistence.MultipleNodePM.DeleteTCHandle;
import org.eclipse.jubula.client.core.persistence.NodePM;
import org.eclipse.jubula.client.ui.constants.Constants;
import org.eclipse.jubula.client.ui.handlers.AbstractSelectionBasedHandler;
import org.eclipse.jubula.client.ui.rcp.Plugin;
import org.eclipse.jubula.client.ui.rcp.i18n.Messages;
import org.eclipse.jubula.client.ui.rcp.utils.Utils;
import org.eclipse.jubula.client.ui.utils.DialogUtils;
import org.eclipse.jubula.client.ui.utils.ErrorHandlingUtil;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
/**
* @author BREDEX GmbH
* @created 06.10.2016
*/
public class DeleteUnusedHandler extends AbstractSelectionBasedHandler {
/**
* This class is used for the deletion to be executed in a separate task
* coupled with a progress bar.
*
* @author BREDEX GmbH
*/
private class DeleteUnusedOperation implements IRunnableWithProgress {
/**
* the List<INodePO> to delete
*/
private List<INodePO> m_deleteList;
/**
*
* @param deleteList
* the List<INodePO> to delete
*/
public DeleteUnusedOperation(List<INodePO> deleteList) {
m_deleteList = deleteList;
}
/**
* {@inheritDoc}
*/
@Override
public void run(IProgressMonitor monitor) {
ProgressMonitorTracker instance = ProgressMonitorTracker.SINGLETON;
Plugin.startLongRunning(Messages.DeleteUnusedProgress);
instance.setProgressMonitor(monitor);
monitor.beginTask(Messages.UIJobDeletingUnused,
m_deleteList.size());
// we only delete SpecTCs, and they are always independent (no Categories...)
DeleteNodesTransaction.deleteTopNodes(m_deleteList, m_deleteList,
monitor);
monitor.done();
DataEventDispatcher.getInstance()
.fireTestresultChanged(TestresultState.Refresh);
instance.setProgressMonitor(null);
Plugin.stopLongRunning();
}
}
/**
* The maximum amount of nodes that will be deleted to be shown.
*/
private static final int MAX_NODES_DISPLAYED = 10;
/**
* Closes the editor for the given Node.
*
* @param node
* the node of the editor to be closed
*/
private static void closeOpenEditor(IPersistentObject node) {
IEditorPart editor = Utils.getEditorByPO(node);
if (editor != null) {
editor.getSite().getPage().closeEditor(editor, false);
}
}
/**
* Creates a label for the confirm dialog from a List<String> containing the
* names of the items that will be deleted.
*
* @param itemNames
* a List<String> containing the item names
* @return the label for the confirm dialog
*/
private static String createConfirmLabel(List<String> itemNames) {
StringBuilder label = new StringBuilder(
NLS.bind(Messages.DeleteUnusedAction, itemNames.size()));
label.append(StringConstants.NEWLINE);
label.append(StringConstants.NEWLINE);
Iterator iterator = itemNames.iterator();
int size = 0;
while (iterator.hasNext()) {
label.append(Constants.BULLET);
label.append(iterator.next().toString());
label.append(StringConstants.NEWLINE);
if (MAX_NODES_DISPLAYED <= ++size) {
break;
}
}
if (iterator.hasNext()) {
label.append(StringConstants.NEWLINE);
label.append(NLS.bind(Messages.DeleteUnusedActionOverflow,
(itemNames.size() - size)));
}
return label.toString();
}
/**
* Creates delete commands from a List<INodePO>.
*
* @param nodesToDelete
* the nodes to delete
* @return a List<AbstractCmdHandle> for node deletion
*/
private static List<AbstractCmdHandle>
createDeleteCommands(List<INodePO> nodesToDelete) {
List<AbstractCmdHandle> cmds =
new ArrayList<AbstractCmdHandle>(nodesToDelete.size());
ParamNameBPDecorator dec =
new ParamNameBPDecorator(ParamNameBP.getInstance());
for (INodePO node : nodesToDelete) {
dec.clearAllNames();
if (node instanceof ISpecTestCasePO) {
cmds.add(new DeleteTCHandle((ISpecTestCasePO) node, dec));
}
if (node instanceof IEventExecTestCasePO) {
cmds.add(new DeleteEvHandle((IEventExecTestCasePO) node));
}
}
return cmds;
}
/**
* Creates an Object[] with a String with the locations of use of the given
* ISpecTestCasePO.
*
* @param specTcPO
* a SpecTestCasePO
* @param reusesSet
* List <IExecTestCasePO>
* @param nodesToDelete
* List<INodePO>
* @return a String
*/
private static Object[] createLocOfUseArray(ISpecTestCasePO specTcPO,
List<IExecTestCasePO> reusesSet, List<INodePO> nodesToDelete) {
StringBuilder locOfUse = new StringBuilder();
int size = 0;
for (IExecTestCasePO node : reusesSet) {
INodePO parent = node.getParentNode();
if (parent != null && !nodesToDelete.contains(parent)) {
locOfUse.append(Constants.BULLET);
locOfUse.append(parent.getName());
locOfUse.append(StringConstants.NEWLINE);
size++;
}
}
return new Object[] { specTcPO.getName(), size, locOfUse.toString() };
}
/**
* Checks if a set contains any parent node of a specified node.
*
* @param set
* the Set<INodePO>
* @param node
* the specific INodePO
* @return <code>true</code> if any parent is already in set,
* <code>false</code> otherwise
*/
private static boolean doesSetContainAnyParent(Set<INodePO> set,
INodePO node) {
INodePO parent = node.getParentNode();
while (parent != null) {
if (set.contains(parent)) {
return true;
}
parent = parent.getParentNode();
}
return false;
}
/**
* Creates a List<INodePO> from an IStructuredSelection.
*
* @param selection
* the IStructuredSelection
* @return return the List<INodePO>
*/
private static List<INodePO>
selectionToList(IStructuredSelection selection) {
@SuppressWarnings("unchecked")
Set<INodePO> set = new HashSet<INodePO>(selection.toList());
List<INodePO> top = new ArrayList<INodePO>();
for (INodePO node : set) {
if (!doesSetContainAnyParent(set, node)) {
top.add(node);
}
}
return top;
}
/** {@inheritDoc} */
@Override
public Object executeImpl(ExecutionEvent event) {
List<INodePO> nodeList = selectionToList(getSelection());
if (isValid(nodeList)) {
List<INodePO> unusedList = new ArrayList<INodePO>();
for (INodePO node : nodeList) {
unusedList.addAll(UnusedSpecTestCasesBP
.getUnusedSpecTestCases((ISpecTestCasePO) node));
}
if (unusedList.isEmpty()) {
return null;
}
Collections.reverse(unusedList);
if (isConfirmed(unusedList)) {
try {
/*
* Execute the operation.
*/
for (INodePO node : unusedList) {
closeOpenEditor(node);
}
PlatformUI.getWorkbench().getProgressService().run(true,
false, new DeleteUnusedOperation(unusedList));
} catch (InvocationTargetException e) {
/*
* Exception occurred during the operation. The exception
* was already handled by the operation. Do nothing.
*/
} catch (InterruptedException e) {
/*
* The operation was canceled. Do nothing.
*/
}
}
}
return null;
}
/**
* Pops up a "confirm delete" dialog.
*
* @param top
* the List<INodePO> of top nodes to delete
* @return <code>true</code> if "yes" was clicked, <code>false</code>
* otherwise
*/
private boolean isConfirmed(List<INodePO> top) {
List<String> itemNames = new ArrayList<String>();
for (Object obj : top) {
if (obj instanceof INodePO) {
itemNames.add(((INodePO) obj).getName());
} else {
String name = String.valueOf(obj);
if (!StringUtils.isBlank(name)) {
itemNames.add(name);
}
}
}
if (itemNames.isEmpty()) {
return false;
}
MessageDialog dialog = new MessageDialog(getActiveShell(),
Messages.DeleteUnusedActionShellTitle, null,
createConfirmLabel(itemNames), MessageDialog.QUESTION,
new String[] { Messages.DialogMessageButton_YES,
Messages.DialogMessageButton_NO },
0);
dialog.create();
DialogUtils.setWidgetNameForModalDialog(dialog);
dialog.open();
return dialog.getReturnCode() == 0;
}
/**
* Checks if the List<INodePO> of top nodes can be deleted without affecting
* other nodes and pops up an information dialog if they can not be deleted.
*
* @param top
* the List<INodePO> of top nodes to delete
* @return <code>true</code> if the operation is valid, <code>false</code>
* otherwise
*/
private boolean isValid(List<INodePO> top) {
for (INodePO node : top) {
if (node instanceof ISpecTestCasePO) {
ISpecTestCasePO specTcPO = (ISpecTestCasePO) node;
List<IExecTestCasePO> execTestCases;
execTestCases = NodePM.getInternalExecTestCases(
specTcPO.getGuid(), specTcPO.getParentProjectId());
if (!MultipleNodePM.allExecsFromList(top, execTestCases)) {
/**
* The node is reused somewhere else and therefore cannot be
* deleted.
*/
ErrorHandlingUtil.createMessageDialog(
MessageIDs.I_REUSED_SPEC_TCS,
createLocOfUseArray(specTcPO, execTestCases, top),
null);
return false;
}
}
}
return true;
}
}