net.bioclipse.ds.ui.views.DSView.java Source code

Java tutorial

Introduction

Here is the source code for net.bioclipse.ds.ui.views.DSView.java

Source

/*******************************************************************************
 * Copyright (c) 2009 Ola Spjuth.
 * 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:
 *     Ola Spjuth - initial API and implementation
 ******************************************************************************/
package net.bioclipse.ds.ui.views;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.bioclipse.cdk.business.ICDKManager;
import net.bioclipse.cdk.domain.CDKMolecule;
import net.bioclipse.cdk.domain.ICDKMolecule;
import net.bioclipse.cdk.jchempaint.editor.JChemPaintEditor;
import net.bioclipse.cdk.ui.sdfeditor.editor.MoleculesEditor;
import net.bioclipse.cdk.ui.sdfeditor.editor.MultiPageMoleculesEditorPart;
import net.bioclipse.core.business.BioclipseException;
import net.bioclipse.core.util.LogUtils;
import net.bioclipse.ds.Activator;
import net.bioclipse.ds.business.IDSManager;
import net.bioclipse.ds.model.DSException;
import net.bioclipse.ds.model.Endpoint;
import net.bioclipse.ds.model.IDSTest;
import net.bioclipse.ds.model.ITestResult;
import net.bioclipse.ds.model.TestRun;
import net.bioclipse.ds.model.report.AbstractTestReportModel;
import net.bioclipse.ds.model.report.DSSingleReportModel;
import net.bioclipse.ds.model.report.ReportHelper;
import net.bioclipse.ds.ui.IDSViewNoCloseEditor;
import net.bioclipse.jobs.BioclipseJob;
import net.bioclipse.jobs.BioclipseJobUpdateHook;

import org.apache.log4j.Logger;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.eclipse.ui.part.*;
import org.eclipse.core.commands.contexts.ContextEvent;
import org.eclipse.core.commands.contexts.ContextManagerEvent;
import org.eclipse.core.commands.contexts.IContextManagerListener;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.*;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.SWT;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.interfaces.IAtomContainer;

public class DSView extends ViewPart implements IPartListener, IContextManagerListener {

    private static final Logger logger = Logger.getLogger(DSView.class);

    public static final String VIEW_ID = "net.bioclipse.ds.ui.views.DSView";

    private static Image questImg;
    private static Image warnImg;
    private static Image crossImg;
    private static Image checkImg;
    private static Image wheelImg;
    private static Image equalImg;

    private TreeViewer viewer;
    private Action runAction;

    //Kepp track of existing mappings from editor to TestRun
    private Map<IWorkbenchPart, List<TestRun>> editorTestMap;

    //Kepp track of existing mappings from editor to TestRun
    private Map<IWorkbenchPart, IPropertyChangeListener> editorListenerMap;

    //The active test runs. Initializes upon test run, and updates on editor switch
    private List<TestRun> activeTestRuns;

    //Tracks the state in the view. True if a run has been made.
    private boolean executed;

    private Action clearAction;

    private Action excludeAction;

    private Action includeAction;

    private Action expandAllAction;

    private Action collapseAllAction;

    private Action refreshAction;

    private List<BioclipseJob<List<ITestResult>>> runningJobs;

    /**
     * Used to store and set selection for a new run
     */
    private IStructuredSelection storedSelection;

    private Action autoRunAction;

    private boolean autorun;

    //The currently shown image in consensusView 
    private Image consensusImage;

    private Text consensusText;

    private Canvas consensusCanvas;

    private Action helpAction;

    private static DSView instance;

    /**
     * The constructor.
     */
    public DSView() {
    }

    public static DSView getInstance() {
        return instance;
    }

    public List<BioclipseJob<List<ITestResult>>> getRunningJobs() {
        return runningJobs;
    }

    public boolean isExecuted() {
        return executed;
    }

    public void setExecuted(boolean executed) {
        this.executed = executed;
    }

    /**
     * This is a callback that will allow us
     * to create the viewer and initialize it.
     */
    public void createPartControl(Composite parent) {

        DSView.instance = this;

        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 1;
        parent.setLayout(gridLayout);

        viewer = new TreeViewer(parent, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        ColumnViewerToolTipSupport.enableFor(viewer);

        viewer.setContentProvider(new DSViewContentProvider());
        //        viewer.setLabelProvider(new DecoratingLabelProvider(new DSViewLabelProvider(),new DSViewDecorator()));
        viewer.setLabelProvider(new DSViewLabelProvider());
        //        viewer.setSorter(new ViewerSorter());
        viewer.addFilter(new HideNotVisbleFilter());
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                updateActionStates();
            }

        });

        GridData gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
        viewer.getTree().setLayoutData(gridData);

        viewer.setInput(new String[] { "Initializing..." });

        // Create the help context id for the viewer's control
        PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), VIEW_ID);
        makeActions();
        hookContextMenu();
        contributeToActionBars();

        //Create the ConsensusSection at the bottom of View
        Composite consensusComposite = new Composite(parent, SWT.BORDER);
        GridData gridData2 = new GridData(GridData.FILL, GridData.END, true, false);
        gridData2.heightHint = 50;
        consensusComposite.setLayoutData(gridData2);
        GridLayout gridLayout2 = new GridLayout(2, false);
        gridLayout2.marginLeft = 0;
        gridLayout2.marginWidth = 0;
        gridLayout2.marginBottom = 10;
        gridLayout2.marginTop = 0;
        gridLayout2.marginRight = 0;
        gridLayout2.marginHeight = 0;
        consensusComposite.setLayout(gridLayout2);

        //Create components of consensusComposite
        consensusText = new Text(consensusComposite, SWT.BORDER);
        GridData gridData3 = new GridData(GridData.FILL, GridData.BEGINNING, true, true);
        gridData3.heightHint = 40;
        consensusText.setLayoutData(gridData3);

        //Initialize and cache consensus images
        questImg = Activator.getImageDecriptor("icons48/question.png").createImage();
        warnImg = Activator.getImageDecriptor("icons48/warn.png").createImage();
        crossImg = Activator.getImageDecriptor("icons48/cross.png").createImage();
        checkImg = Activator.getImageDecriptor("icons48/check.png").createImage();
        wheelImg = Activator.getImageDecriptor("icons48/wheel.png").createImage();
        equalImg = Activator.getImageDecriptor("icons48/equal.png").createImage();

        //Start off with a question-image
        consensusImage = questImg;
        consensusCanvas = new Canvas(consensusComposite, SWT.NO_REDRAW_RESIZE);
        consensusCanvas.addPaintListener(new PaintListener() {
            public void paintControl(PaintEvent e) {
                e.gc.drawImage(consensusImage, 0, 1);
            }
        });

        GridData gridData4 = new GridData(GridData.END, GridData.BEGINNING, false, false);
        gridData4.widthHint = 50;
        gridData4.heightHint = 50;
        gridData4.minimumHeight = 50;
        gridData4.minimumWidth = 50;
        consensusCanvas.setLayoutData(gridData4);

        //Initialize instance variables
        editorTestMap = new HashMap<IWorkbenchPart, List<TestRun>>();
        editorListenerMap = new HashMap<IWorkbenchPart, IPropertyChangeListener>();
        runningJobs = new ArrayList<BioclipseJob<List<ITestResult>>>();

        //Turn off autorun by default
        setAutorun(false);
        updateActionStates();

        Job job = new Job("Initializing decision support tests") {
            @Override
            protected IStatus run(IProgressMonitor monitor) {

                IDSManager ds = Activator.getDefault().getJavaManager();
                try {
                    monitor.beginTask("Initializing decision support tests", ds.getTests().size() + 1);
                    monitor.worked(1);
                    for (String testID : ds.getTests()) {
                        IDSTest test = ds.getTest(testID);
                        monitor.subTask("Initializing test: " + testID);
                        test.initialize(monitor);
                    }

                    Display.getDefault().asyncExec(new Runnable() {

                        public void run() {

                            //Init viewer with available endpoints
                            IDSManager ds = Activator.getDefault().getJavaManager();
                            try {
                                viewer.setInput(ds.getFullEndpoints().toArray());
                            } catch (BioclipseException e) {
                                LogUtils.handleException(e, logger, Activator.PLUGIN_ID);
                                viewer.setInput(new String[] { "Error initializing tests" });
                            }

                            updateConsensusView();
                            updateActionStates();

                            //Listen for part lifecycle events to react on editors
                            getSite().getWorkbenchWindow().getPartService().addPartListener(DSView.getInstance());

                            //Make viewer post selection to Eclipse
                            getSite().setSelectionProvider(viewer);

                            //Hook us up to react on JCP context changes
                            IContextService contextService = (IContextService) PlatformUI.getWorkbench()
                                    .getService(IContextService.class);
                            contextService.addContextManagerListener(DSView.getInstance());

                            //If editor is open, react on it
                            if (getSite() == null)
                                return;
                            if (getSite().getWorkbenchWindow() == null)
                                return;
                            if (getSite().getWorkbenchWindow().getActivePage() == null)
                                return;

                            IEditorPart openEditor = getSite().getWorkbenchWindow().getActivePage()
                                    .getActiveEditor();
                            if (openEditor != null) {
                                partActivated(openEditor);
                                if (isAutorun()) {
                                    //                                    doRunAllTests();
                                }
                            }
                        }

                    });

                } catch (BioclipseException e1) {
                    return new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                            "All tests could not be initalized: " + e1.getMessage());
                } catch (DSException e) {
                    return new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                            "All tests could not be initalized: " + e.getMessage());
                }

                monitor.done();
                return Status.OK_STATUS;
            }

        };
        job.setUser(false);
        job.schedule();

    }

    private void updateConsensusView() {

        if (activeTestRuns == null) {
            consensusText.setText("Not run");
            consensusImage = questImg;
            consensusCanvas.update();
            consensusCanvas.redraw();
            return;
        }

        boolean running = false;
        boolean notStarted = false;

        //If we have any tests running or not started, do not do consensus
        for (TestRun tr : activeTestRuns) {
            if (tr.getStatus() == TestRun.RUNNING)
                running = true;
            else if (tr.getStatus() == TestRun.NOT_STARTED)
                notStarted = true;
        }

        if (notStarted) {
            consensusText.setText("Tests not started");
            consensusImage = questImg;
        } else if (running) {
            consensusText.setText("Tests running...");
            consensusImage = wheelImg;
        } else {
            int res = getConsensusFromTestRuns();
            if (res == ITestResult.POSITIVE) {
                consensusText.setText("Consensus: POSITIVE");
                consensusImage = warnImg;
            } else if (res == ITestResult.NEGATIVE) {
                consensusText.setText("Consensus: NEGATIVE");
                consensusImage = checkImg;
            } else if (res == ITestResult.INCONCLUSIVE) {
                consensusText.setText("Consensus: INCONCLUSIVE");
                consensusImage = equalImg;
            }
        }

        consensusCanvas.update();
        consensusCanvas.redraw();

    }

    /**
     * A simple consensus voting.
     * TODO: Implement custom solutions for this.
     * @return
     */
    private int getConsensusFromTestRuns() {

        int numpos = 0;
        int numneg = 0;
        int numinc = 0;

        if (activeTestRuns == null)
            return ITestResult.INCONCLUSIVE;

        for (TestRun tr : activeTestRuns) {
            //Only count non-informative and included testruns
            if ((!(tr.getTest().isInformative())) && (!(tr.getTest().isExcluded()))) {

                if (tr.getStatus() == TestRun.FINISHED) {
                    if (tr.getConsensusStatus() == ITestResult.POSITIVE)
                        numpos++;
                    else if (tr.getConsensusStatus() == ITestResult.NEGATIVE)
                        numneg++;
                    else if (tr.getConsensusStatus() == ITestResult.INCONCLUSIVE)
                        numinc++;
                }

            }
        }

        //If no positive results:
        if (numpos == 0)
            return ITestResult.NEGATIVE;

        //If at least one but equal:
        else if (numpos == numneg)
            return ITestResult.INCONCLUSIVE;

        //If at least one but more pos than neg:
        else if (numpos > numneg)
            return ITestResult.POSITIVE;

        //In all other cases:
        else
            return ITestResult.NEGATIVE;

    }

    /**
     * Clean up part listener
     */
    @Override
    public void dispose() {
        super.dispose();
        getSite().getWorkbenchWindow().getPartService().removePartListener(this);

        //Remove context manager listeners
        IContextService contextService = (IContextService) PlatformUI.getWorkbench()
                .getService(IContextService.class);
        contextService.removeContextManagerListener(this);

        //unregister all listeners

        for (IWorkbenchPart part : editorListenerMap.keySet()) {
            IPropertyChangeListener li = editorListenerMap.get(part);
            if (part instanceof JChemPaintEditor) {
                JChemPaintEditor jcp = (JChemPaintEditor) part;
                jcp.removePropertyChangedListener(li);
            }
        }
        editorListenerMap.clear();
        editorListenerMap = null;

    }

    private void hookContextMenu() {
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                updateActionStates();
                DSView.this.fillContextMenu(manager);
            }

        });
        Menu menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);
    }

    private void contributeToActionBars() {
        IActionBars bars = getViewSite().getActionBars();
        fillLocalPullDown(bars.getMenuManager());
        fillLocalToolBar(bars.getToolBarManager());
    }

    private void fillLocalPullDown(IMenuManager manager) {
    }

    private void fillContextMenu(IMenuManager manager) {
        manager.add(runAction);
        manager.add(clearAction);
        manager.add(new Separator());
        manager.add(includeAction);
        manager.add(excludeAction);
        manager.add(new Separator());
        manager.add(refreshAction);
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    private void fillLocalToolBar(IToolBarManager manager) {
        manager.add(autoRunAction);
        manager.add(runAction);
        manager.add(clearAction);
        manager.add(new Separator());
        manager.add(includeAction);
        manager.add(excludeAction);
        manager.add(new Separator());
        manager.add(expandAllAction);
        manager.add(collapseAllAction);
        manager.add(new Separator());
        manager.add(helpAction);
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    private void updateActionStates() {

        if (activeTestRuns != null && activeTestRuns.size() > 0)
            runAction.setEnabled(true);
        else
            runAction.setEnabled(false);

        if (isAutorun()) {
            autoRunAction.setImageDescriptor(Activator.getImageDecriptor("icons/fastforward_dis2.png"));
        } else {
            autoRunAction.setImageDescriptor(Activator.getImageDecriptor("icons/fastforward.png"));
        }

        boolean testSelected = false;
        for (Object obj : ((IStructuredSelection) viewer.getSelection()).toList()) {
            if (obj instanceof TestRun) {
                testSelected = true;
            } else if (obj instanceof IDSTest) {
                testSelected = true;
            }
        }
        if (testSelected) {
            includeAction.setEnabled(true);
            excludeAction.setEnabled(true);
        } else {
            includeAction.setEnabled(false);
            excludeAction.setEnabled(false);
        }
    }

    private void makeActions() {
        runAction = new Action() {
            public void run() {
                doRunAllTests();
            }
        };
        runAction.setText("Run all Tests");
        runAction.setToolTipText("Runs the Decision Support Tests " + "on the active molecule(s)");
        runAction.setImageDescriptor(Activator.getImageDecriptor("icons/smallRun.gif"));
        runAction.setDisabledImageDescriptor(Activator.getImageDecriptor("icons/smallRun_dis.gif"));

        autoRunAction = new Action() {
            public void run() {
                setAutorun(!autorun);
                updateActionStates();
                if (autorun) {
                    //                    setAutoRunPropListener();
                    doRunAllTests();
                } else {
                    //                    clearAutoRunPropListener();
                }
            }
        };
        autoRunAction.setText("Toggle AutoTest");
        autoRunAction.setToolTipText("Turns on/off automatic running of tests on structural changes");
        autoRunAction.setImageDescriptor(Activator.getImageDecriptor("icons/fastforward.png"));
        autoRunAction.setDisabledImageDescriptor(Activator.getImageDecriptor("icons/fastforward_dis2.png"));

        clearAction = new Action() {
            public void run() {
                doClearAllTests();
            }
        };
        clearAction.setText("Clear all Tests");
        clearAction.setToolTipText("Clear all active tests");
        clearAction.setImageDescriptor(Activator.getImageDecriptor("icons/broom.png"));

        excludeAction = new Action() {
            public void run() {
                doExcludeSelectedTests();
            }
        };
        excludeAction.setText("Exclude test");
        excludeAction.setToolTipText("Exclude selected test(s)");
        excludeAction.setImageDescriptor(Activator.getImageDecriptor("icons/item_delete.gif"));
        excludeAction.setDisabledImageDescriptor(Activator.getImageDecriptor("icons/item_delete_dis.gif"));

        includeAction = new Action() {
            public void run() {
                doIncludeSelectedTests();
            }
        };
        includeAction.setText("Include test");
        includeAction.setToolTipText("Include selected test(s)");
        includeAction.setImageDescriptor(Activator.getImageDecriptor("icons/item_add.gif"));
        includeAction.setDisabledImageDescriptor(Activator.getImageDecriptor("icons/item_add_dis.gif"));

        collapseAllAction = new Action() {
            public void run() {
                viewer.collapseAll();
            }
        };
        collapseAllAction.setText("Collapse all");
        collapseAllAction.setToolTipText("Collapse all tests");
        collapseAllAction.setImageDescriptor(Activator.getImageDecriptor("icons/collapseall.gif"));

        expandAllAction = new Action() {
            public void run() {
                viewer.expandAll();
            }
        };
        expandAllAction.setText("Expand all");
        expandAllAction.setToolTipText("Expand all tests to reveal hits");
        expandAllAction.setImageDescriptor(Activator.getImageDecriptor("icons/expandall.gif"));

        refreshAction = new Action() {
            public void run() {
                viewer.refresh();
            }
        };
        refreshAction.setText("Refresh");
        refreshAction.setToolTipText("Force a refresh of all tests' status");
        refreshAction.setImageDescriptor(Activator.getImageDecriptor("icons/refresh2.png"));

        helpAction = new Action() {
            public void run() {
                IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem();
                helpSystem.displayHelpResource("/" + Activator.PLUGIN_ID + "/html/maintopic.html");
            }
        };
        helpAction.setText("Help");
        helpAction.setToolTipText("Open help for teh Decision Support");
        helpAction.setImageDescriptor(Activator.getImageDecriptor("icons/help.gif"));

    }

    protected void doIncludeSelectedTests() {

        IStructuredSelection sel = (IStructuredSelection) viewer.getSelection();
        for (Object obj : sel.toList()) {
            if (obj instanceof IDSTest) {
                IDSTest dstest = (IDSTest) obj;
                dstest.setExcluded(false);
                viewer.refresh(dstest);
            } else if (obj instanceof TestRun) {
                TestRun testrun = (TestRun) obj;
                testrun.setStatus(TestRun.NOT_STARTED);
                testrun.getTest().setExcluded(false);
                viewer.refresh(testrun);
            }
        }

    }

    protected void doExcludeSelectedTests() {

        IStructuredSelection sel = (IStructuredSelection) viewer.getSelection();
        for (Object obj : sel.toList()) {
            if (obj instanceof IDSTest) {
                IDSTest dstest = (IDSTest) obj;
                dstest.setExcluded(true);
                viewer.refresh(dstest);
            } else if (obj instanceof TestRun) {
                TestRun testrun = (TestRun) obj;
                testrun.setStatus(TestRun.EXCLUDED);
                testrun.getTest().setExcluded(true);
                if (testrun.getMatches() != null)
                    testrun.getMatches().clear();
                viewer.refresh(testrun);
            }
        }

    }

    protected void doClearAllTests() {

        IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
        if (editor instanceof JChemPaintEditor) {
            JChemPaintEditor jcp = (JChemPaintEditor) editor;
            doClearAndSetUpNewTestRuns(jcp);
        } else {
            logger.warn("NOT IMPLEMENTED FOR EDITOR: " + editor);
        }

    }

    private void doRunAllTests() {

        logger.debug("Running tests...");

        if (activeTestRuns == null || activeTestRuns.size() <= 0) {
            showMessage("No active testruns to run");
            return;
        }

        //Store active selection
        storedSelection = (IStructuredSelection) viewer.getSelection();

        //Get the molecule from the editor
        //Assumption: All testruns operate on the same molecule
        IEditorPart part = activeTestRuns.get(0).getEditor();
        if (!(part instanceof JChemPaintEditor)) {
            showError("The editor: " + part + " is not " + "supported to run DS tests on.");
            return;
        }
        JChemPaintEditor jcp = (JChemPaintEditor) part;
        final ICDKMolecule mol = jcp.getCDKMolecule();

        //Cancel all running tests
        for (BioclipseJob<List<ITestResult>> job : runningJobs) {
            //Ask job to cancel
            job.cancel();
            logger.debug("Job: " + job.getName() + " asked to cancel.");
        }

        //Wait for all running jobs to cancel
        for (BioclipseJob<List<ITestResult>> job : runningJobs) {
            logger.debug("Waiting for Job: " + job.getName() + " to finish...");
            try {
                job.join();
            } catch (InterruptedException e) {
            }
            logger.debug("Job: " + job.getName() + " finished.");
        }

        //We need to clear previous tests if already run
        if (isExecuted() == true) {
            doClearAndSetUpNewTestRuns(jcp);
            activeTestRuns = editorTestMap.get(part);
            //            partActivated( jcp );
            viewer.refresh();
        }
        setExecuted(true);

        IDSManager ds = Activator.getDefault().getJavaManager();
        for (final TestRun tr : activeTestRuns) {

            if (tr.getTest().getTestErrorMessage().length() < 1) {

                if (tr.getStatus() == TestRun.EXCLUDED || tr.getTest().isExcluded()) {
                    viewer.refresh(tr);
                    logger.debug("===== Test: " + tr + " skipped since excluded.");
                } else {

                    //Start by cloning the molecule. This is to avoid threading 
                    //issues in CDK.
                    try {
                        IAtomContainer cloneAC = (IAtomContainer) mol.getAtomContainer().clone();

                        ICDKMolecule cloneMol = new CDKMolecule(cloneAC);

                        logger.debug("== Testrun: " + tr.getTest().getName() + " started");
                        tr.setStatus(TestRun.RUNNING);
                        tr.setMolecule(cloneMol);
                        viewer.refresh(tr);
                        viewer.setExpandedState(tr, true);

                        runTestAsJobs(cloneMol, ds, tr);

                    } catch (CloneNotSupportedException e) {
                        LogUtils.handleException(e, logger, Activator.PLUGIN_ID);
                    }

                }
            } else {
                logger.debug("The test: " + tr.getTest() + " has an error so not run.");
            }
        }

        logger.debug("===== All testruns started");
    }

    private void runTestAsJobs(final ICDKMolecule mol, IDSManager ds, final TestRun tr) {

        try {

            //Start up a job with the test
            BioclipseJob<List<ITestResult>> job = ds.runTest(tr.getTest().getId(), mol,
                    new BioclipseJobUpdateHook<List<ITestResult>>(tr.getTest().getName()));

            job.addJobChangeListener(new IJobChangeListener() {

                public void aboutToRun(IJobChangeEvent event) {
                }

                public void awake(IJobChangeEvent event) {
                }

                @SuppressWarnings("unchecked")
                public void done(IJobChangeEvent event) {

                    final BioclipseJob<List<ITestResult>> job = (BioclipseJob<List<ITestResult>>) event.getJob();
                    final List<ITestResult> matches = job.getReturnValue();

                    //Update viewer in SWT thread
                    Display.getDefault().asyncExec(new Runnable() {
                        public void run() {

                            logger.debug(" Job done: " + tr.getTest().getName());
                            logger.debug(" Matches: " + matches.size());

                            for (ITestResult result : matches) {
                                result.setTestRun(tr);
                                tr.addResult(result);
                            }
                            tr.setMatches(matches);
                            logger.debug("===== " + tr + " finished");
                            if (tr.getTest().getTestErrorMessage() != "")
                                tr.setStatus(TestRun.ERROR);
                            else
                                tr.setStatus(TestRun.FINISHED);

                            viewer.refresh(tr);
                            viewer.setExpandedState(tr, true);
                            updateConsensusView();

                            //If we previously stored a selection, set it now
                            selectIfStoredSelection(tr);

                            //This job is done, remove from list of running jobs
                            getRunningJobs().remove(job);

                        }

                    });

                }

                public void running(IJobChangeEvent event) {
                }

                public void scheduled(IJobChangeEvent event) {
                }

                public void sleeping(IJobChangeEvent event) {
                }
            });

            //Store ref to job in list
            runningJobs.add(job);

        } catch (Exception e) {
            logger.error("Error running test: " + tr.getTest() + ": " + e.getMessage());
            LogUtils.debugTrace(logger, e);

            tr.setStatus(TestRun.ERROR);

            viewer.refresh(tr);
            updateConsensusView();

        }

    }

    private void selectIfStoredSelection(TestRun tr) {

        if (storedSelection == null)
            return;

        for (Object obj : storedSelection.toList()) {
            //Select this testrun if same id as stored one
            if (obj instanceof TestRun) {
                TestRun storedtr = (TestRun) obj;
                if (storedtr.getTest().getId().equals(tr.getTest().getId())) {
                    viewer.setSelection(new StructuredSelection(tr));
                }
            }
            //If this testrun has no matches, do not try to select them
            else if (tr.getMatches() == null || tr.getMatches().size() <= 0)
                return;
            //Select the first ITestResult if it has same TestRun.Test as stored
            else if (obj instanceof ITestResult) {
                ITestResult storedtres = (ITestResult) obj;
                if (storedtres.getTestRun().getTest().getId().equals(tr.getTest().getId())) {
                    //                    viewer.setSelection( new StructuredSelection(tr.getMatches().get( 0 )) );
                    //TODO: fire explicit property to make this highlight?
                }
            }
        }
    }

    private void showMessage(String message) {
        MessageDialog.openInformation(viewer.getControl().getShell(), "Decision support", message);
    }

    private void showError(String message) {
        MessageDialog.openError(viewer.getControl().getShell(), "Decision support", message);
    }

    /**
     * Passing the focus request to the viewer's control.
     */
    public void setFocus() {
        viewer.getControl().setFocus();
    }

    private IWorkbenchPart getSupportedEditor(IWorkbenchPart part) {
        if (part instanceof JChemPaintEditor) {
            logger.debug("We have a JCP editor for TestsView!");
            return part;
        } else if (part instanceof MoleculesEditor) {
            //TODO: when does this happen?
            return part;
        } else if (part instanceof MultiPageMoleculesEditorPart) {
            logger.debug("We have a MPE editor for TestsView");
            MultiPageMoleculesEditorPart editor = (MultiPageMoleculesEditorPart) part;

            if (editor.isJCPVisible()) {
                //JCP is active
                Object obj = editor.getAdapter(JChemPaintEditor.class);
                if (obj != null) {
                    JChemPaintEditor jcp = (JChemPaintEditor) obj;
                    return jcp;
                }
            }

            //            IContextService contextService = (IContextService) PlatformUI.getWorkbench().
            //                                                      getService(IContextService.class);
            //            for (Object cs : contextService.getActiveContextIds()){
            //                if (MultiPageMoleculesEditorPart.JCP_CONTEXT.equals( cs )){
            //                    //JCP is active
            //                    Object obj = editor.getAdapter(JChemPaintEditor.class);
            //                    if (obj!= null){
            //                        JChemPaintEditor jcp=(JChemPaintEditor)obj;
            //                        return jcp;
            //                    }
            //                }
            //            }

            //            Object obj = editor.getAdapter(JChemPaintEditor.class);
            //            if (obj== null){
            //                logger.debug("     MPE editor for TestsView did not have JCP page to provide");
            //                return null;
            //            }
            //            logger.debug("     MPE editor for TestsView provided JCP page!");
            //            JChemPaintEditor jcp=(JChemPaintEditor)obj;
            //            return jcp;
        }

        logger.debug("No supported editor for TestsView");

        //Not supported editor
        return null;
    }

    private void updateView() {

        if (getSite() == null)
            return;
        if (getSite().getWorkbenchWindow() == null)
            return;
        if (getSite().getWorkbenchWindow().getActivePage() == null)
            return;

        if (getSite().getWorkbenchWindow().getActivePage().getActiveEditor() == null) {
            activeTestRuns = null;
        }

        viewer.refresh();
        //        viewer.expandToLevel( 1 );
        viewer.expandAll();
        updateActionStates();

        //Also update consensus part
        updateConsensusView();
    }

    /**
     * We have a new editor. Look up any listener and use it, if not, create a 
     * new listener. After this, 
     * @param part 
     */
    private void addNewTestRunsAndListener(IWorkbenchPart part) {

        if (part instanceof JChemPaintEditor) {
            final JChemPaintEditor jcp = (JChemPaintEditor) part;
            // Register interest in changes from editor

            //First, try to look up in map
            IPropertyChangeListener jcplistener = editorListenerMap.get(jcp);

            //If not found in map, create a new
            if (jcplistener == null) {

                jcplistener = new IPropertyChangeListener() {
                    public void propertyChange(PropertyChangeEvent event) {

                        if (event.getProperty().equals(JChemPaintEditor.STRUCUTRE_CHANGED_EVENT)) {

                            // editor model has changed
                            // do stuff...
                            logger.debug("TestsView reacting: JCP editor model has changed");

                            //                        storedSelection=(IStructuredSelection) viewer.getSelection();

                            doClearAndSetUpNewTestRuns(jcp);
                            if (isAutorun())
                                doRunAllTests();

                        }
                    }
                };

                jcp.addPropertyChangedListener(jcplistener);
                editorListenerMap.put(jcp, jcplistener);
            }

            doClearAndSetUpNewTestRuns(jcp);

            return;
        }

        else if (part instanceof MoleculesEditor) {
            //            MoleculesEditor moleditor = (MoleculesEditor) part;

            showMessage("MOLTABLE NOT YET SUPPORTED!");
            logger.debug("MOLTABLE NOT YET SUPPORTED!");

            return;
        }

    }

    private void doClearAndSetUpNewTestRuns(JChemPaintEditor jcp) {

        List<TestRun> newTestRuns = new ArrayList<TestRun>();

        IDSManager ds = Activator.getDefault().getJavaManager();

        //Get the endpoints
        try {

            for (Endpoint ep : ds.getFullEndpoints()) {

                //First remove any old TestRuns
                if (ep.getTestruns() != null)
                    ep.getTestruns().clear();

                //Now, create new TestRuns from the tests
                for (IDSTest test : ep.getTests()) {

                    TestRun newTestRun = new TestRun(jcp, test);

                    if (test.getTestErrorMessage() != null && test.getTestErrorMessage().length() > 0) {
                        newTestRun.setStatus(TestRun.ERROR);
                    } else if (test.isExcluded()) {
                        newTestRun.setStatus(TestRun.EXCLUDED);
                    }

                    newTestRuns.add(newTestRun);
                    ep.addTestRun(newTestRun);
                }
            }

        } catch (BioclipseException e1) {
            LogUtils.handleException(e1, logger, Activator.PLUGIN_ID);
        }

        editorTestMap.put(jcp, newTestRuns);
        activeTestRuns = newTestRuns;
        setExecuted(false);
        updateView();
    }

    public DSSingleReportModel waitAndReturnReportModel() {

        //Wait for all jobs to finish
        for (BioclipseJob<List<ITestResult>> job : runningJobs) {
            logger.debug("Waiting for Job: " + job.getName() + " to finish...");
            try {
                job.join();
            } catch (InterruptedException e) {
            }
            logger.debug("Job: " + job.getName() + " finished.");
        }

        if (activeTestRuns == null || activeTestRuns.size() <= 0) {
            logger.error("No active testruns to make a chart from.");
            return null;
        }

        //Set up and return ReportModel
        DSSingleReportModel reportmodel = new DSSingleReportModel();

        //Get mol from first testrun
        TestRun first = activeTestRuns.get(0);
        ICDKMolecule mol = first.getMolecule();

        if (mol == null) {
            logger.error("No molecule to make a chart from.");
            return null;
        }

        //Get name
        String name = (String) mol.getAtomContainer().getProperty(CDKConstants.TITLE);
        if (name == null)
            name = "N/A";
        reportmodel.setCompoundName(name);

        ICDKManager cdk = net.bioclipse.cdk.business.Activator.getDefault().getJavaCDKManager();
        try {
            //Generate SMILES
            String smi = cdk.calculateSMILES(mol);
            reportmodel.setSMILES(smi);

            //Generate Mass
            double mw = cdk.calculateMass(mol);
            reportmodel.setMw(mw);

            //Generate structure image
            byte[] structureImage = ReportHelper.createImage(mol, null, 200, 200, 0.5);
            reportmodel.setQueryStructure(structureImage);
            reportmodel.setConsensusText(ReportHelper.statusToString(getConsensusFromTestRuns()));
            reportmodel.setConsensusImage(ReportHelper.statusToImageData(getConsensusFromTestRuns()));
        } catch (BioclipseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        //Add all testmodels to ReportModel
        for (TestRun tr : activeTestRuns) {
            AbstractTestReportModel testreportmodel = tr.getTest().getReportmodel();
            if (testreportmodel != null) {
                testreportmodel.setName(tr.getTest().getId());
                if (testreportmodel != null) {
                    testreportmodel.setTestrun(tr);
                    reportmodel.addTestModel(testreportmodel);
                }
            }
        }

        return reportmodel;

    }

    private void clearTestRuns() {

        //Clear active testruns
        activeTestRuns = null;

        IDSManager ds = Activator.getDefault().getJavaManager();

        //also clear all testruns on all endpoints
        try {
            for (Endpoint ep : ds.getFullEndpoints()) {

                if (ep.getTestruns() != null)
                    ep.getTestruns().clear();
            }
        } catch (BioclipseException e) {
            LogUtils.handleException(e, logger, Activator.PLUGIN_ID);
        }

    }

    /* ================================
     * Below is for part lifecycle events
     *====================================  */

    /**
     * 
     */
    public void partActivated(IWorkbenchPart part) {
        logger.debug("Part:" + part.getTitle() + " activated");
        if (!(part instanceof IEditorPart))
            return;

        if (part instanceof IDSViewNoCloseEditor) {
            //Do not react on this; no clear, no new tests
            return;
        }

        IWorkbenchPart ppart = getSupportedEditor(part);
        if (ppart == null) {
            clearTestRuns();
            updateView();
            return;
        }

        if (editorTestMap.keySet().contains(ppart)) {
            activeTestRuns = editorTestMap.get(ppart);

            try {

                //For all endpoints, clear old and add these testruns
                IDSManager ds = Activator.getDefault().getJavaManager();
                for (Endpoint ep : ds.getFullEndpoints()) {

                    //First remove any old TestRuns
                    if (ep.getTestruns() != null)
                        ep.getTestruns().clear();

                    //Loop over all tests in this Endpoint
                    for (IDSTest epTest : ep.getTests()) {
                        //For the active testruns, locate those who are of this test
                        for (TestRun tr : activeTestRuns) {
                            if (tr.getTest().getId().equals(epTest.getId())) {
                                ep.addTestRun(tr);
                            }
                        }

                    }

                }
            } catch (BioclipseException e1) {
                LogUtils.handleException(e1, logger, Activator.PLUGIN_ID);
            }

        } else {
            addNewTestRunsAndListener(ppart);
        }
        updateView();

        if (activeTestRuns != null) {
            for (TestRun tr : activeTestRuns) {
                selectIfStoredSelection(tr);
            }
        }
    }

    public void partBroughtToTop(IWorkbenchPart part) {
        //        logger.debug("Part:" + part.getTitle() + " brought to top");
        if (!(part instanceof IEditorPart))
            return;

        if (part instanceof IDSViewNoCloseEditor) {
            //Do not react on this; no clear, no new tests
            return;
        }

        IWorkbenchPart ppart = getSupportedEditor(part);
        if (ppart == null)
            return;

        if (editorTestMap.keySet().contains(ppart)) {
            activeTestRuns = editorTestMap.get(ppart);

            try {

                //For all endpoints, clear old and add these testruns
                IDSManager ds = Activator.getDefault().getJavaManager();
                for (Endpoint ep : ds.getFullEndpoints()) {

                    //First remove any old TestRuns
                    if (ep.getTestruns() != null)
                        ep.getTestruns().clear();

                    //Loop over all tests in this Endpoint
                    for (IDSTest epTest : ep.getTests()) {
                        //For the active testruns, locate those who are of this test
                        for (TestRun tr : activeTestRuns) {
                            if (tr.getTest().getId().equals(epTest.getId())) {
                                ep.addTestRun(tr);
                            }
                        }

                    }

                }
            } catch (BioclipseException e1) {
                LogUtils.handleException(e1, logger, Activator.PLUGIN_ID);
            }

        } else {
            //No existing editor in map, add a new
            addNewTestRunsAndListener(ppart);
        }
        updateView();
    }

    /**
     * If this editor is stored in map, remove the entry to free memory. 
     * Also remove any listeners associated with this editor.
     * 
     * After this, call updateView.
     */
    public void partClosed(IWorkbenchPart part) {
        //      logger.debug("Part:" + part.getTitle() + " closed");
        if (!(part instanceof IEditorPart))
            return;

        if (getSite() == null)
            return;
        if (getSite().getWorkbenchWindow() == null)
            return;
        if (getSite().getWorkbenchWindow().getActivePage() == null)
            return;

        IWorkbenchPart ppart = getSupportedEditor(part);
        if (ppart == null)
            return;

        editorListenerMap.remove(part);
        if (editorTestMap.keySet().contains(ppart)) {
            editorTestMap.remove(ppart);
            if (getSite().getWorkbenchWindow().getActivePage().getActiveEditor() == null) {
                clearTestRuns();
                updateView();
            }
        }
    }

    /**
     * Occurs when lost focus. Does not mean it is not topmost editor, but 
     * could mean an unsupported editor is topmost. Need to verify this and 
     * clean in that case.
     */
    public void partDeactivated(IWorkbenchPart part) {
        //        logger.debug("Part:" + part.getTitle() + " deactivated");
        storedSelection = (IStructuredSelection) viewer.getSelection();
    }

    public void partOpened(IWorkbenchPart part) {
        //        logger.debug("Part:" + part.getTitle() + " opened");
    }

    public void setAutorun(boolean autorun) {

        this.autorun = autorun;
    }

    public boolean isAutorun() {

        return autorun;
    }

    public void fireExternalRun() {

        doRunAllTests();

    }

    public void contextChanged(ContextEvent contextEvent) {

        System.out.println("JCP context changed");
        if (contextEvent.getContext().getId() != MultiPageMoleculesEditorPart.JCP_CONTEXT)
            return;

    }

    public void contextManagerChanged(ContextManagerEvent contextManagerEvent) {

        IContextService contextService = (IContextService) PlatformUI.getWorkbench()
                .getService(IContextService.class);

        if (getSite() == null)
            return;
        if (getSite().getWorkbenchWindow() == null)
            return;
        if (getSite().getWorkbenchWindow().getActivePage() == null)
            return;

        if (contextService.getActiveContextIds().contains(MultiPageMoleculesEditorPart.JCP_CONTEXT)) {

            //MolTableEditor switched to tab JCP
            System.out.println("JCP context activated");
            IEditorPart editor = getSite().getWorkbenchWindow().getActivePage().getActiveEditor();
            if (editor != null) {
                if (editor instanceof MultiPageMoleculesEditorPart) {
                    //Special case when SDF editor JCP is visible since same 
                    //editor, but different molecule.
                    IWorkbenchPart suped = getSupportedEditor(editor);
                    addNewTestRunsAndListener(suped);
                    updateView();

                }
                partActivated(editor);
            }
        } else {
            //MolTableEditor switched to tab other than JCP
            System.out.println("JCP context deactivated");
            IEditorPart editor = getSite().getWorkbenchWindow().getActivePage().getActiveEditor();
            if (editor != null)
                partActivated(editor);
        }
    }

}