/*******************************************************************************
* 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.editors;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jubula.client.core.businessprocess.CalcTypes;
import org.eclipse.jubula.client.core.businessprocess.CompNamesBP;
import org.eclipse.jubula.client.core.businessprocess.IComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.ObjectMappingEventDispatcher;
import org.eclipse.jubula.client.core.businessprocess.TestCaseParamBP;
import org.eclipse.jubula.client.core.businessprocess.UsedToolkitBP;
import org.eclipse.jubula.client.core.businessprocess.compcheck.CompletenessGuard;
import org.eclipse.jubula.client.core.businessprocess.db.TimestampBP;
import org.eclipse.jubula.client.core.businessprocess.problems.IProblem;
import org.eclipse.jubula.client.core.businessprocess.problems.ProblemFactory;
import org.eclipse.jubula.client.core.businessprocess.problems.ProblemType;
import org.eclipse.jubula.client.core.commands.CAPRecordedCommand;
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.IDataChangedListener;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.IParamChangedListener;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.RecordModeState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.UpdateState;
import org.eclipse.jubula.client.core.model.ICapPO;
import org.eclipse.jubula.client.core.model.ICompNamesPairPO;
import org.eclipse.jubula.client.core.model.IComponentNamePO;
import org.eclipse.jubula.client.core.model.IControllerPO;
import org.eclipse.jubula.client.core.model.IDataSetPO;
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.IParamDescriptionPO;
import org.eclipse.jubula.client.core.model.IParamNodePO;
import org.eclipse.jubula.client.core.model.IParameterInterfacePO;
import org.eclipse.jubula.client.core.model.IPersistentObject;
import org.eclipse.jubula.client.core.model.IProjectPO;
import org.eclipse.jubula.client.core.model.ISpecTestCasePO;
import org.eclipse.jubula.client.core.model.ITDManager;
import org.eclipse.jubula.client.core.model.ITestDataCategoryPO;
import org.eclipse.jubula.client.core.model.ITestSuitePO;
import org.eclipse.jubula.client.core.model.ITimestampPO;
import org.eclipse.jubula.client.core.model.PoMaker;
import org.eclipse.jubula.client.core.model.ReentryProperty;
import org.eclipse.jubula.client.core.persistence.CompNamePM;
import org.eclipse.jubula.client.core.persistence.EditSupport;
import org.eclipse.jubula.client.core.persistence.GeneralStorage;
import org.eclipse.jubula.client.core.persistence.NodePM;
import org.eclipse.jubula.client.core.persistence.PMAlreadyLockedException;
import org.eclipse.jubula.client.core.persistence.PMException;
import org.eclipse.jubula.client.core.utils.AbstractNonPostOperatingTreeNodeOperation;
import org.eclipse.jubula.client.core.utils.ITreeNodeOperation;
import org.eclipse.jubula.client.core.utils.ITreeTraverserContext;
import org.eclipse.jubula.client.core.utils.ModelParamValueConverter;
import org.eclipse.jubula.client.core.utils.ParamValueConverter;
import org.eclipse.jubula.client.core.utils.StringHelper;
import org.eclipse.jubula.client.core.utils.TreeTraverser;
import org.eclipse.jubula.client.ui.constants.CommandIDs;
import org.eclipse.jubula.client.ui.provider.DecoratingCellLabelProvider;
import org.eclipse.jubula.client.ui.rcp.Plugin;
import org.eclipse.jubula.client.ui.rcp.constants.RCPCommandIDs;
import org.eclipse.jubula.client.ui.rcp.controllers.PMExceptionHandler;
import org.eclipse.jubula.client.ui.rcp.controllers.TestExecutionContributor;
import org.eclipse.jubula.client.ui.rcp.controllers.dnd.LocalSelectionTransfer;
import org.eclipse.jubula.client.ui.rcp.controllers.dnd.TreeViewerContainerDragSourceListener;
import org.eclipse.jubula.client.ui.rcp.events.GuiEventDispatcher;
import org.eclipse.jubula.client.ui.rcp.i18n.Messages;
import org.eclipse.jubula.client.ui.rcp.provider.contentprovider.TestCaseEditorContentProvider;
import org.eclipse.jubula.client.ui.rcp.provider.labelprovider.TooltipLabelProvider;
import org.eclipse.jubula.client.ui.utils.CommandHelper;
import org.eclipse.jubula.client.ui.utils.ErrorHandlingUtil;
import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.exception.Assert;
import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException;
import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs;
import org.eclipse.jubula.tools.internal.xml.businessmodell.Component;
import org.eclipse.jubula.tools.internal.xml.businessmodell.ConcreteComponent;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.menus.CommandContributionItem;
/**
* @author BREDEX GmbH
* @created 13.10.2004
*/
@SuppressWarnings("synthetic-access")
public abstract class AbstractTestCaseEditor extends AbstractJBEditor
implements IParamChangedListener {
/** central test data update listener */
private CentralTestDataUpdateListener m_ctdUpdateListener =
new CentralTestDataUpdateListener();
/**
* Creates the initial Context of this Editor.<br>
* Subclasses may override this method.
* @param parent Composite
*/
public void createPartControlImpl(Composite parent) {
createSashForm(parent);
setParentComposite(parent);
// sets the input of the trees.
setInitialInput();
ColumnViewerToolTipSupport.enableFor(getTreeViewer());
DecoratingCellLabelProvider lp = new DecoratingCellLabelProvider(
new TooltipLabelProvider(), Plugin.getDefault()
.getWorkbench().getDecoratorManager()
.getLabelDecorator());
getTreeViewer().setLabelProvider(lp);
DataEventDispatcher ded = DataEventDispatcher.getInstance();
ded.addPropertyChangedListener(this, true);
addDragAndDropSupport(DND.DROP_MOVE,
new Transfer[] {LocalSelectionTransfer.getInstance()});
getEditorHelper().addListeners();
setActionHandlers();
GuiEventDispatcher.getInstance()
.addEditorDirtyStateListener(this, true);
ded.addDataChangedListener(m_ctdUpdateListener, false);
ded.addParamChangedListener(this, true);
}
/**
* adds Drag and Drop support for the trees.
*
* @param operations The DnD operation types to support.
* @param transfers The DnD transfer types to support.
*/
protected void addDragAndDropSupport(
int operations, Transfer[] transfers) {
getMainTreeViewer().addDragSupport(operations, transfers,
new TreeViewerContainerDragSourceListener(getTreeViewer()));
getMainTreeViewer().addDropSupport(operations, transfers,
getViewerDropAdapter());
}
/**
* @return the viewer drop adapter
*/
protected abstract DropTargetListener getViewerDropAdapter();
/**
* @param parent the parent of the SashForm.
* @return the created SashForm.
*/
protected SashForm createSashForm(Composite parent) {
SashForm sashForm = new SashForm(parent, SWT.MULTI | SWT.VERTICAL);
GridLayout compLayout = new GridLayout(1, true);
compLayout.marginWidth = 0;
compLayout.marginHeight = 0;
sashForm.setLayout(compLayout);
GridData gridData = new GridData (GridData.FILL_BOTH);
sashForm.setLayoutData(gridData);
setControl(sashForm);
createMainPart(sashForm);
return sashForm;
}
/**
* Refreshes all referenced Test Data Cubes within the context of this
* editor when Central Test Data changes.
*
* @author BREDEX GmbH
* @created 17.03.2011
*/
private class CentralTestDataUpdateListener
implements IDataChangedListener {
/**
* {@inheritDoc}
*/
public void handleDataChanged(IPersistentObject po,
DataState dataState, UpdateState updateState) {
if (po instanceof ITestDataCategoryPO
&& dataState == DataState.StructureModified
&& updateState != UpdateState.notInEditor) {
ITreeNodeOperation<INodePO> refreshRefDataCubeOp =
new AbstractNonPostOperatingTreeNodeOperation<INodePO>() {
public boolean operate(
ITreeTraverserContext<INodePO> ctx,
INodePO parent, INodePO node,
boolean alreadyVisited) {
if (node instanceof IParamNodePO) {
IParameterInterfacePO referencedCube =
((IParamNodePO)node)
.getReferencedDataCube();
if (referencedCube != null) {
getEditorHelper().getEditSupport()
.getSession().refresh(referencedCube);
}
}
return true;
}
};
TreeTraverser refDataCubeRefresher =
new TreeTraverser(
(INodePO)getEditorHelper().getEditSupport()
.getWorkVersion(),
refreshRefDataCubeOp, true, 2);
refDataCubeRefresher.traverse(true);
}
}
/** {@inheritDoc} */
public void handleDataChanged(DataChangedEvent... events) {
for (DataChangedEvent e : events) {
handleDataChanged(e.getPo(), e.getDataState(),
e.getUpdateState());
}
}
}
@Override
public void setInitialInput() {
getMainTreeViewer().setContentProvider(
new TestCaseEditorContentProvider());
INodePO workVersion =
(INodePO)getEditorHelper().getEditSupport().getWorkVersion();
initTopTreeViewer(workVersion);
runLocalChecks();
}
/**
* run local completeness checks such as test data completeness
*/
public void runLocalChecks() {
INodePO node = (INodePO) getEditorHelper().getEditSupport()
.getWorkVersion();
CompletenessGuard.checkAll(node, new NullProgressMonitor());
}
/**
* @param root the root of the TreeViewer.
*/
protected void initTopTreeViewer(INodePO root) {
try {
getMainTreeViewer().getTree().setRedraw(false);
getMainTreeViewer().setInput(null);
getMainTreeViewer().setInput(new INodePO[] {root});
} finally {
getMainTreeViewer().getTree().setRedraw(true);
getMainTreeViewer().expandToLevel(2);
setSelection(new StructuredSelection(root));
}
}
/**
* @param monitor IProgressMonitor
*/
public void doSave(IProgressMonitor monitor) {
if (!checkCompleteness()) {
return;
}
monitor.beginTask(Messages.EditorsSaveEditors,
IProgressMonitor.UNKNOWN);
try {
EditSupport editSupport = getEditorHelper().getEditSupport();
removeIncorrectCompNamePairs();
fixCompNameReferences();
final IPersistentObject perObj = editSupport.getWorkVersion();
getCompNameCache().clearUnusedCompNames((INodePO) perObj);
if (perObj instanceof ISpecTestCasePO) {
final IProjectPO project = GeneralStorage.getInstance()
.getProject();
UsedToolkitBP.getInstance().addToolkit((ISpecTestCasePO)perObj,
project);
}
TimestampBP.refreshTimestamp((ITimestampPO)perObj);
editSupport.saveWorkVersion();
getCompNameCache().clear();
ObjectMappingEventDispatcher.updateObjectMappings(
(INodePO) perObj);
DataEventDispatcher.getInstance().fireDataChangedListener(
getWorkVersion(), DataState.Saved, UpdateState.all);
getEditorHelper().resetEditableState();
getEditorHelper().setDirty(false);
} catch (PMException e) {
PMExceptionHandler.handlePMExceptionForMasterSession(e);
try {
reOpenEditor(((NodeEditorInput)getEditorInput()).getNode());
} catch (PMException e1) {
PMExceptionHandler.handlePMExceptionForEditor(e, this);
}
} catch (ProjectDeletedException e) {
PMExceptionHandler.handleProjectDeletedException();
} finally {
monitor.done();
}
}
/**
* Replaces Component Name references with the referenced Component Names
* and deletes any Component Name references that are no longer used.
*/
private void fixCompNameReferences() {
// Replace all reference guids with referenced guids
INodePO rootNode =
(INodePO)getEditorHelper().getEditSupport().getWorkVersion();
IComponentNameCache compNameCache = getCompNameCache();
Iterator<INodePO> iter = rootNode.getNodeListIterator();
while (iter.hasNext()) {
INodePO nodePO = iter.next();
if (nodePO instanceof IExecTestCasePO) {
IExecTestCasePO exec = (IExecTestCasePO)nodePO;
List<String> toRemove =
new ArrayList<String>();
List<ICompNamesPairPO> toAdd =
new ArrayList<ICompNamesPairPO>();
for (ICompNamesPairPO pair : exec.getCompNamesPairs()) {
String firstName = pair.getFirstName();
String secondName = pair.getSecondName();
IComponentNamePO firstCompNamePo =
compNameCache.getResCompNamePOByGuid(firstName);
IComponentNamePO secondCompNamePo =
compNameCache.getResCompNamePOByGuid(secondName);
if (!(firstCompNamePo.getGuid().equals(firstName)
&& secondCompNamePo.getGuid().equals(secondName))) {
String componentType = pair.getType();
toRemove.add(firstName);
toAdd.add(
PoMaker.createCompNamesPairPO(
firstCompNamePo.getGuid(),
secondCompNamePo.getGuid(),
componentType));
}
}
for (String stringToRemove : toRemove) {
exec.removeCompNamesPair(stringToRemove);
}
for (ICompNamesPairPO pairToAdd : toAdd) {
exec.addCompNamesPair(pairToAdd);
}
} else if (nodePO instanceof ICapPO) {
ICapPO capPo = (ICapPO)nodePO;
String compNameGuid = capPo.getComponentName();
IComponentNamePO compNamePo =
compNameCache.getResCompNamePOByGuid(compNameGuid);
if (compNamePo != null
&& !compNamePo.getGuid().equals(compNameGuid)) {
capPo.setComponentName(compNamePo.getGuid());
}
}
}
// Delete all unused reference comp names
CompNamePM.removeUnusedCompNames(
GeneralStorage.getInstance().getProject().getId(),
getEditorHelper().getEditSupport().getSession());
}
/**
* Removes incorrect CompNamePair from ExecTC during saving.
*/
private void removeIncorrectCompNamePairs() {
INodePO node =
(INodePO)getEditorHelper().getEditSupport().getWorkVersion();
if (node instanceof ISpecTestCasePO
|| node instanceof ITestSuitePO) {
for (Iterator<INodePO> it = node.getAllNodeIter(); it.hasNext(); ) {
INodePO o = it.next();
if (o instanceof IExecTestCasePO) {
IExecTestCasePO exec = (IExecTestCasePO)o;
ICompNamesPairPO [] pairArray =
exec.getCompNamesPairs().toArray(
new ICompNamesPairPO[
exec.getCompNamesPairs().size()]);
for (ICompNamesPairPO pair : pairArray) {
searchAndSetComponentType(pair);
if (!CompNamesBP.isValidCompNamePair(pair)) {
exec.removeCompNamesPair(pair.getFirstName());
}
}
}
}
} else {
LOG.error(Messages.WrongEditSupportInTestCaseEditor
+ StringConstants.COLON + StringConstants.SPACE + node);
}
}
/**
*
* @param pair the current compNamesPairPO
*/
private void searchAndSetComponentType(final ICompNamesPairPO pair) {
if (!StringUtils.isEmpty(pair.getType())) {
return;
}
EditSupport supp = getEditorHelper().getEditSupport();
IPersistentObject workVersion = supp.getWorkVersion();
if (workVersion instanceof INodePO) {
CalcTypes.recalculateCompNamePairs(getCompNameCache(),
(INodePO) workVersion);
} else {
LOG.warn("class not supported for recalculating component pair " //$NON-NLS-1$
+ workVersion.getClass().getCanonicalName());
}
}
/**
* Checks, if all fields were filled in correctly.
* @return True, if all fields were filled in correctly.
*/
protected boolean checkCompleteness() {
ISpecTestCasePO testCase = (ISpecTestCasePO)getEditorHelper()
.getEditSupport().getWorkVersion();
final Integer mId = MessageIDs.E_CANNOT_SAVE_EDITOR_TC_SP;
if (testCase.getName() == null
|| StringConstants.EMPTY.equals(testCase.getName())) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { Messages.TestCaseEditorNoTcName });
return false;
}
if (testCase.getName().startsWith(BLANK)
|| testCase.getName().endsWith(BLANK)) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { Messages.TestCaseEditorWrongTcName });
return false;
}
Iterator<INodePO> iter = testCase.getNodeListIterator();
while (iter.hasNext()) {
Object node = iter.next();
if (node instanceof ICapPO) {
ICapPO cap = (ICapPO)node;
if (cap.getName() == null
|| StringConstants.EMPTY.equals(cap.getName())) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { Messages.TestCaseEditorNoCapName });
return false;
}
if (cap.getName().startsWith(BLANK)
|| cap.getName().endsWith(BLANK)) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { NLS.bind(
Messages.TestCaseEditorWrongTsName,
cap.getName()) });
return false;
}
if (componentHasDefaultMapping(cap)) {
continue; // there is no component name
}
if (cap.getComponentName() == null
|| StringConstants.EMPTY.equals(
cap.getComponentName())) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { NLS.bind(
Messages.TestCaseEditorNoCompName,
cap.getName()) });
return false;
}
if (cap.getComponentName().startsWith(BLANK) || cap
.getComponentName().endsWith(BLANK)) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { NLS.bind(
Messages.TestCaseEditorWrongCompName2,
cap.getName()) });
return false;
}
}
}
for (Object object : testCase.getAllEventEventExecTC()) {
IEventExecTestCasePO eventTC = (IEventExecTestCasePO)object;
if (StringConstants.EMPTY.equals(eventTC.getName())) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { Messages.TestCaseEditorNoEventTcName });
return false;
}
if (eventTC.getName().startsWith(BLANK)
|| eventTC.getName().endsWith(BLANK)) {
ErrorHandlingUtil.createMessageDialog(mId, null,
new String[] { NLS.bind(
Messages.TestCaseEditorWrongEhName,
eventTC.getName()) });
return false;
}
}
return checkRefsAndCompNames(testCase);
}
/**
*
* @param cap
* the cap from which wee need the component type
* @return if the component has a default mapping
*/
private boolean componentHasDefaultMapping(ICapPO cap) {
Component component = ComponentBuilder.getInstance()
.getCompSystem().findComponent(cap.getComponentType());
if (component.isConcrete()) {
return ((ConcreteComponent) component).hasDefaultMapping();
}
return false;
}
/**
* {@inheritDoc}
*/
protected void checkMasterSessionUpToDate() {
super.checkMasterSessionUpToDate();
ITimestampPO node =
(ITimestampPO)getEditorHelper().getEditSupport().getWorkVersion();
TimestampBP.refreshEditorNodeInMasterSession(node);
}
/**
* Checks if testdata of the current original testCase contains references.
* <p>Checks also the propagated compName.
* @param testCase the current testCase
* @return true, if data was not mixed.
*/
private boolean checkRefsAndCompNames(ISpecTestCasePO testCase) {
ITDManager mgr = testCase.getDataManager();
for (int row = 0; row < mgr.getDataSetCount(); row++) {
IDataSetPO row2 = mgr.getDataSet(row);
for (int col = 0; col < row2.getColumnCount(); col++) {
String data = row2.getValueAt(col);
String uniqueId = mgr.getUniqueIds().get(col);
IParamDescriptionPO desc =
testCase.getParameterForUniqueId(uniqueId);
ParamValueConverter conv =
new ModelParamValueConverter(data, testCase, desc);
for (String refName : conv.getNamesForReferences()) {
ErrorHandlingUtil.createMessageDialog(
MessageIDs.E_CANNOT_SAVE_EDITOR_TC_SP, null,
new String[]{NLS.bind(
Messages.TestCaseEditorContReference,
refName)});
return false;
}
}
}
Iterator<INodePO> iter = testCase.getAllNodeIter();
while (iter.hasNext()) {
INodePO nodePO = iter.next();
if (nodePO instanceof IExecTestCasePO) {
IExecTestCasePO exec = (IExecTestCasePO)nodePO;
for (ICompNamesPairPO pair : exec.getCompNamesPairs()) {
if (pair.getSecondName() == null || StringConstants.EMPTY
.equals(pair.getSecondName())) {
ErrorHandlingUtil.createMessageDialog(
MessageIDs.E_CANNOT_SAVE_EDITOR_TC_SP, null,
new String[]{NLS.bind(
Messages.TestCaseEditorMmissingCompName,
new Object[]{StringHelper.getInstance()
.getMap().get(pair.getType()),
pair.getFirstName(),
exec.getName()})});
return false;
}
}
}
}
return true;
}
/**
* {@inheritDoc}
*/
protected void fillContextMenu(IMenuManager mgr) {
if (getStructuredSelection().getFirstElement() == null) {
return;
}
MenuManager submenuAdd = new MenuManager(Messages.TestSuiteBrowserAdd,
ADD_ID);
MenuManager submenuRefactor = new MenuManager(
Messages.TestCaseEditorRefactor, REFACTOR_ID);
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.REFERENCE_TC);
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.NEW_CAP);
mgr.add(submenuAdd);
CommandHelper.createContributionPushItem(mgr,
IWorkbenchCommandConstants.EDIT_CUT);
CommandHelper.createContributionPushItem(mgr,
IWorkbenchCommandConstants.EDIT_COPY);
CommandHelper.createContributionPushItem(mgr,
IWorkbenchCommandConstants.EDIT_PASTE);
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.TOGGLE_ACTIVE_STATE);
mgr.add(new Separator());
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.EDIT_PARAMETERS);
mgr.add(new GroupMarker("editing")); //$NON-NLS-1$
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.REVERT_CHANGES);
mgr.add(new Separator());
mgr.add(submenuRefactor);
mgr.add(new Separator());
CommandHelper.createContributionPushItem(mgr,
CommandIDs.DELETE_COMMAND_ID);
mgr.add(CommandHelper.createContributionItem(
RCPCommandIDs.FIND,
null, Messages.FindContextMenu,
CommandContributionItem.STYLE_PUSH));
CommandHelper.createContributionPushItem(mgr,
CommandIDs.OPEN_SPECIFICATION_COMMAND_ID);
CommandHelper.createContributionPushItem(mgr,
CommandIDs.SHOW_SPECIFICATION_COMMAND_ID);
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.SHOW_WHERE_USED);
collapseExpandItems(mgr);
CommandHelper.createContributionPushItem(submenuAdd,
RCPCommandIDs.NEW_CONDITIONAL_STATEMENT);
CommandHelper.createContributionPushItem(submenuAdd,
RCPCommandIDs.NEW_WHILE_DO);
CommandHelper.createContributionPushItem(submenuAdd,
RCPCommandIDs.NEW_DO_WHILE);
CommandHelper.createContributionPushItem(submenuAdd,
RCPCommandIDs.NEW_ITERATE_LOOP);
CommandHelper.createContributionPushItem(submenuAdd,
RCPCommandIDs.NEW_TESTCASE);
CommandHelper.createContributionPushItem(submenuAdd,
RCPCommandIDs.ADD_EVENT_HANDLER);
CommandHelper.createContributionPushItem(submenuRefactor,
RCPCommandIDs.EXTRACT_TESTCASE);
CommandHelper.createContributionPushItem(submenuRefactor,
RCPCommandIDs.REPLACE_WITH_TESTCASE);
CommandHelper.createContributionPushItem(submenuRefactor,
RCPCommandIDs.SAVE_AS_NEW);
mgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
mgr.add(new Separator());
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.NEW_COMMENT);
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.EDIT_COMMENT);
}
/**
* Adds the collapse and expand items
* @param mgr the menu manager
*/
protected void collapseExpandItems(IMenuManager mgr) {
mgr.add(new Separator());
CommandHelper.createContributionPushItem(mgr,
CommandIDs.EXPAND_TREE_ITEM_COMMAND_ID);
CommandHelper.createContributionPushItem(mgr,
CommandIDs.COLLAPSE_TREE_ITEM_COMMAND_ID);
mgr.add(new Separator());
}
/**
* Cleanup on closing.
*/
public void dispose() {
try {
DataEventDispatcher ded = DataEventDispatcher.getInstance();
ded.removeParamChangedListener(this);
ded.removeDataChangedListener(m_ctdUpdateListener);
if (CAPRecordedCommand.getRecordListener() == this) {
CAPRecordedCommand.setRecordListener(null);
TestExecutionContributor.getInstance().getClientTest()
.resetToTesting();
}
if (getEditorSite() != null && getEditorSite().getPage() != null) {
ded.fireRecordModeStateChanged(RecordModeState.notRunning);
removeGlobalActionHandler();
}
} finally {
super.dispose();
}
}
/**
* Removes global action handler to prevent memory leaks. Only clears the
* action handlers if handlers for this editor are currently in use. This
* means that if another editor has registered its own handlers, then that
* editor is responsible for clearing its own action handlers.
*/
private void removeGlobalActionHandler() {
getEditorSite().getActionBars().updateActionBars();
}
/** {@inheritDoc} */
public void handleDataChanged(DataChangedEvent... events) {
for (DataChangedEvent e : events) {
handleDataChanged(e.getPo(), e.getDataState(), e.getUpdateState());
}
}
/** {@inheritDoc} */
public void handlePropertyChanged(boolean isCompNameChanged) {
super.handlePropertyChanged(isCompNameChanged);
runLocalChecks();
}
/** {@inheritDoc} */
public void handleParamChanged(Object caller) {
runLocalChecks();
refresh();
}
/**
* {@inheritDoc}
*/
public void handleDataChanged(IPersistentObject po, DataState dataState,
UpdateState updState) {
if (po instanceof INodePO) {
INodePO changedNode = (INodePO)po;
INodePO editorNode = (INodePO)getEditorHelper().getEditSupport()
.getWorkVersion();
boolean isVisibleInEditor = editorNode.indexOf(changedNode) > -1;
isVisibleInEditor |= contains(editorNode, changedNode);
if (editorNode instanceof ISpecTestCasePO) {
isVisibleInEditor |= ((ISpecTestCasePO)editorNode)
.getAllEventEventExecTC().contains(po);
}
switch (dataState) {
case Added:
if (isVisibleInEditor) {
handleNodeAdded(changedNode);
}
break;
case Deleted:
if (!(po instanceof IProjectPO)) {
isVisibleInEditor = true;
refresh();
}
break;
case Renamed:
createPartName();
break;
case StructureModified:
if (isVisibleInEditor) {
getEditorHelper().setDirty(true);
}
if (!handleStructureModified(po)) {
return;
}
break;
default:
}
if (isVisibleInEditor) {
runLocalChecks();
}
getEditorHelper().handleDataChanged(po, dataState);
} else if (po instanceof IComponentNamePO
&& updState != UpdateState.onlyInEditor) {
handleCompNameChanged((IComponentNamePO) po, dataState);
}
}
/**
* Handles Component Name changes
* @param cN the Component Name
* @param state the DataState
*/
private void handleCompNameChanged(IComponentNamePO cN, DataState state) {
String guid = cN.getGuid();
switch (state) {
case Renamed:
getCompNameCache().renamedCompName(cN.getGuid(), cN.getName());
break;
default:
}
}
/**
* @param parent node
* @param changedNode searched node
* @return <code>true</code> if parent node contains changedNode node.
* Otherwise return <code>false</code>.
*/
private boolean contains(INodePO parent, INodePO changedNode) {
for (Iterator it = parent.getAllNodeIter(); it.hasNext(); ) {
if (it.next().equals(changedNode)) {
return true;
}
}
return false;
}
/**
* Handles a PO that has been modified.
*
* @param po The modified object.
* @return <code>false</code> if an error occurs during handling.
* Otherwise, <code>true</code>.
*/
private boolean handleStructureModified(IPersistentObject po) {
if (po instanceof ISpecTestCasePO) {
final ISpecTestCasePO specTestCasePO = (ISpecTestCasePO)po;
final INodePO workVersion = (INodePO)getEditorHelper()
.getEditSupport().getWorkVersion();
final List<IExecTestCasePO> execTestCases = NodePM.
getInternalExecTestCases(specTestCasePO.getGuid(),
specTestCasePO.getParentProjectId());
if (!execTestCases.isEmpty() && containsWorkVersionReuses(
workVersion, specTestCasePO)) {
if (Plugin.getActiveEditor() != this && isDirty()) {
ErrorHandlingUtil.createMessageDialog(
MessageIDs.I_SAVE_AND_REOPEN_EDITOR,
new Object[]{getTitle(),
specTestCasePO.getName()}, null);
return false;
}
try {
reOpenEditor(getEditorHelper().getEditSupport()
.getOriginal());
} catch (PMException e) {
ErrorHandlingUtil.createMessageDialog(
MessageIDs.E_REFRESH_FAILED, null,
new String[] {Messages.ErrorMessageEDITOR_CLOSE});
getSite().getPage().closeEditor(this, false);
}
return false;
}
}
return true;
}
/**
* @param root node, where starts the validation
* @param specTc changed specTc
* @return if editor contains an reusing testcase for given specTestCase
*/
private static boolean containsWorkVersionReuses(INodePO root,
ISpecTestCasePO specTc) {
final Iterator<INodePO> it = root.getNodeListIterator();
final List <INodePO> childList = IteratorUtils.toList(it);
// Add EventHandler to children List!
if (root instanceof ISpecTestCasePO) {
final ISpecTestCasePO rootSpecTc = (ISpecTestCasePO)root;
childList.addAll(rootSpecTc.getAllEventEventExecTC());
}
for (INodePO child : childList) {
if (child instanceof IExecTestCasePO) {
final IExecTestCasePO execTc = (IExecTestCasePO)child;
if (specTc.equals(execTc.getSpecTestCase())) {
return true;
}
if (containsWorkVersionReuses(execTc, specTc)) {
return true;
}
}
}
return false;
}
/**
* Sets the EventHandler properties.
* @param eventHandler the EventHandlerTc
* @param refName the referenced name of EventHandler
* @param eventType the event type
* @param reentryType the reentry type
* @param maxRetries the maximum number of retries
*/
void setEventHandlerProperties(
IEventExecTestCasePO eventHandler,
String refName, String eventType,
String reentryType,
Integer maxRetries) {
eventHandler.setName(refName);
eventHandler.setEventType(eventType);
ReentryProperty[] reentryProps = ReentryProperty.REENTRY_PROP_ARRAY;
for (int i = 0; i < reentryProps.length; i++) {
if (String.valueOf(reentryProps[i]).equals(reentryType)) {
eventHandler.setReentryProp(reentryProps[i]);
break;
}
}
Assert.verify(eventHandler.getReentryProp() != null,
Messages.ErrorWhenSettingReentryProperty
+ StringConstants.EXCLAMATION_MARK);
eventHandler.setMaxRetries(maxRetries);
}
/**
* synchronizes the list of parameter unique ids in TDManagers of ExecTestCases
* and the associated parameter list
* @param root root node of editor
*/
private void updateTDManagerOfExecTestCases(INodePO root) {
Iterator<INodePO> it = root.getAllNodeIter();
while (it.hasNext()) {
INodePO child = it.next();
if (child instanceof IExecTestCasePO) {
((IExecTestCasePO)child).synchronizeParameterIDs();
}
}
}
/**
* Checks and removes unused TestData of IExecTestCasePOs.
*/
protected final void checkAndRemoveUnusedTestData() {
final EditSupport editSupport = getEditorHelper().getEditSupport();
final IPersistentObject workVersion = editSupport.getWorkVersion();
if (!(workVersion instanceof INodePO)) {
return;
}
final INodePO nodePO = (INodePO)workVersion;
final List<IExecTestCasePO> execsWithUnusedTestData = TestCaseParamBP
.getExecTcWithUnusedTestData(nodePO);
if (execsWithUnusedTestData.isEmpty()) {
return;
}
try {
editSupport.lockWorkVersion();
getEditorHelper().setDirty(true);
updateTDManagerOfExecTestCases(nodePO);
doSave(new NullProgressMonitor());
} catch (PMAlreadyLockedException e) {
// ignore, we are only doing housekeeping
} catch (PMException e) {
PMExceptionHandler.handlePMExceptionForMasterSession(e);
}
}
/**
*
* @return the receiver's current selection, if it is an
* {@link IStructuredSelection}. Otherwise, returns an empty
* selection.
*/
protected IStructuredSelection getStructuredSelection() {
ISelection selection = getSelection();
if (selection instanceof IStructuredSelection) {
return (IStructuredSelection)selection;
}
return StructuredSelection.EMPTY;
}
/**
* Refreshes the viewer and updates the expansion state and selection
* based on the added node.
*
* @param addedNode The node that has been added.
*/
private void handleNodeAdded(INodePO addedNode) {
refresh();
setSelection(new StructuredSelection(addedNode));
}
/**
* Checks for empty controllers
* @param node the node
*/
private void checkForEmptyControllers(INodePO node) {
for (Iterator<INodePO> it = node.getNodeListIterator();
it.hasNext(); ) {
INodePO next = it.next();
if (next instanceof IControllerPO) {
removeIncompleteProblems((IControllerPO) next);
}
}
Set<IProblem> copy = new HashSet<IProblem>(node.getProblems());
for (IProblem problem : copy) {
if (problem.equals(ProblemFactory.ERROR_IN_CHILD)) {
node.removeProblem(problem);
break;
}
}
CompletenessGuard.checkEmptyContainer(node);
}
/**
* Removes empty controller error markers from a node
* @param cont the ConditionPO node
*/
private void removeIncompleteProblems(IControllerPO cont) {
Set<IProblem> copy = new HashSet<IProblem>(cont.getProblems());
for (IProblem problem : copy) {
if (problem.getProblemType().equals(
ProblemType.REASON_IF_WITHOUT_TEST)) {
cont.removeProblem(problem);
return;
}
}
}
/**
* @return the icon of the editor
*/
public abstract Image getIcon();
}