org.eclipse.jubula.client.ui.rcp.editors.AbstractTestCaseEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jubula.client.ui.rcp.editors.AbstractTestCaseEditor.java

Source

/*******************************************************************************
 * 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.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();
        addDoubleClickListener(CommandIDs.OPEN_SPECIFICATION_COMMAND_ID, getMainTreeViewer());

        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();
        checkForEmptyControllers(node);
        for (Iterator it = node.getAllNodeIter(); it.hasNext();) {
            CompletenessGuard.checkLocalTestData((INodePO) it.next());
        }
    }

    /**
     * @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);
        CommandHelper.createContributionPushItem(mgr, CommandIDs.EXPAND_TREE_ITEM_COMMAND_ID);

        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);
    }

    /**
     * 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() {
        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;
            }
        }
    }
}