org.dawb.workbench.ui.editors.PlotDataEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.dawb.workbench.ui.editors.PlotDataEditor.java

Source

/*
 * Copyright (c) 2012 Diamond Light Source Ltd.
 *
 * 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
 */

package org.dawb.workbench.ui.editors;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import org.dawb.common.ui.monitor.ProgressMonitorWrapper;
import org.dawb.common.ui.plot.tools.HistoryType;
import org.dawb.common.ui.util.EclipseUtils;
import org.dawb.common.ui.util.GridUtils;
import org.dawb.common.ui.views.PlotDataView;
import org.dawb.common.ui.widgets.ActionBarWrapper;
import org.dawb.workbench.ui.Activator;
import org.dawb.workbench.ui.data.PlotDataComponent;
import org.dawb.workbench.ui.editors.preference.EditorConstants;
import org.dawb.workbench.ui.views.PlotDataPage;
import org.dawnsci.common.widgets.editor.ITitledEditor;
import org.dawnsci.plotting.AbstractPlottingSystem;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dawnsci.analysis.api.dataset.IDataset;
import org.eclipse.dawnsci.analysis.api.dataset.ILazyDataset;
import org.eclipse.dawnsci.analysis.api.io.IDataHolder;
import org.eclipse.dawnsci.analysis.api.metadata.IMetadata;
import org.eclipse.dawnsci.plotting.api.IPlottingSystem;
import org.eclipse.dawnsci.plotting.api.PlotType;
import org.eclipse.dawnsci.plotting.api.PlottingFactory;
import org.eclipse.dawnsci.plotting.api.axis.IAxis;
import org.eclipse.dawnsci.plotting.api.expressions.IVariableManager;
import org.eclipse.dawnsci.plotting.api.tool.IToolPageSystem;
import org.eclipse.dawnsci.plotting.api.trace.ITrace;
import org.eclipse.dawnsci.slicing.api.data.ITransferableDataObject;
import org.eclipse.dawnsci.slicing.api.editor.ISelectedPlotting;
import org.eclipse.dawnsci.slicing.api.editor.ISlicablePlottingPart;
import org.eclipse.dawnsci.slicing.api.system.ISliceSystem;
import org.eclipse.dawnsci.slicing.api.system.SliceSource;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IActionBars2;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.IPage;
import org.eclipse.ui.part.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.diamond.scisoft.analysis.io.LoaderFactory;
import uk.ac.diamond.scisoft.analysis.plotclient.ScriptingConnection;

/**
 * An editor which combines a plot with a graph of data sets.
 * 
 * 
 */
public class PlotDataEditor extends EditorPart
        implements IReusableEditor, ISlicablePlottingPart, ITitledEditor, ISelectedPlotting {

    private static Logger logger = LoggerFactory.getLogger(PlotDataEditor.class);

    // This view is a composite of two other views.
    private IPlottingSystem plottingSystem;
    private PlotType defaultPlotType;
    private Map<Integer, IAxis> axisMap;
    private PlotJob plotJob;
    private InitJob initJob;
    private ActionBarWrapper wrapper;
    private ReentrantLock lock;
    private ITitledEditor parent;

    private ScriptingConnection connection;

    public PlotDataEditor(final PlotType defaultPlotType) {
        this(defaultPlotType, null);
    }

    public PlotDataEditor(final PlotType defaultPlotType, ITitledEditor parent) {

        this.axisMap = new HashMap<Integer, IAxis>(4);
        this.plotJob = new PlotJob();
        this.initJob = new InitJob();
        this.lock = new ReentrantLock();
        this.parent = parent;
        try {
            this.defaultPlotType = defaultPlotType;
            this.plottingSystem = PlottingFactory.createPlottingSystem();

        } catch (Exception ne) {
            logger.error("Cannot locate any plotting systems!", ne);
        }
    }

    @Override
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        setSite(site);
        setInput(input, false);
    }

    @Override
    public Map<String, IDataset> getSelected() {

        final PlotDataComponent dataSetComponent = (PlotDataComponent) getDataSetComponent();
        final Map<String, IDataset> ret = new HashMap<String, IDataset>(3);
        if (dataSetComponent == null)
            return ret;

        final List<ITransferableDataObject> selectedNames = dataSetComponent.getSelections();
        for (ITransferableDataObject object : selectedNames) {
            final IDataset set = object.getData(null);
            if (set == null)
                continue;
            ret.put(set.getName(), set);
        }
        return ret;
    }

    @Override
    public boolean isDirty() {
        return false;
    }

    public void setToolbarsVisible(boolean isVisible) {
        wrapper.setVisible(isVisible);
    }

    @Override
    public void createPartControl(final Composite parent) {

        final Composite main = new Composite(parent, SWT.NONE);
        final GridLayout gridLayout = new GridLayout(1, false);
        main.setLayout(gridLayout);
        GridUtils.removeMargins(main);

        final IActionBars bars = this.getEditorSite().getActionBars();
        this.wrapper = ActionBarWrapper.createActionBars(main, (IActionBars2) bars);

        // NOTE use name of input. This means that although two files of the same
        // name could be opened, the editor name is clearly visible in the GUI and
        // is usually short.
        final String plotName = this.getEditorInput().getName();

        final Composite plot = new Composite(main, SWT.NONE);
        plot.setLayout(new FillLayout());
        plot.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        plottingSystem.createPlotPart(plot, plotName, wrapper, defaultPlotType, this);
        ((AbstractPlottingSystem) plottingSystem).addPropertyChangeListener(new IPropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent event) {
                if (IPlottingSystem.RESCALE_ID.equals(event.getProperty())) {
                    Activator.getDefault().getPreferenceStore().setValue(EditorConstants.RESCALE_SETTING,
                            (Boolean) event.getNewValue());
                } else {
                    setAxisSettings(EditorConstants.XAXIS_PROP_STUB, plottingSystem.getSelectedXAxis());
                    setAxisSettings(EditorConstants.YAXIS_PROP_STUB, plottingSystem.getSelectedYAxis());
                }

            }
        });
        getAxisSettings(EditorConstants.XAXIS_PROP_STUB, plottingSystem.getSelectedXAxis());
        getAxisSettings(EditorConstants.YAXIS_PROP_STUB, plottingSystem.getSelectedYAxis());

        axisMap.put(1, plottingSystem.getSelectedYAxis());
        // FIX to http://jira.diamond.ac.uk/browse/DAWNSCI-380 remove axes until they work
        for (int i = 2; i <= 2; i++) { //(Y4)
            final IAxis yAxis = plottingSystem.createAxis("Y" + i, true, i == 3 || i == 4 ? SWT.RIGHT : SWT.LEFT);
            yAxis.setVisible(false);
            yAxis.setTitle("Y" + i);
            axisMap.put(i, yAxis);
        }

        // Finally
        if (wrapper != null)
            wrapper.update(true);
        initJob.schedule(getEditorInput());

        // We ensure that the view for choosing data sets is visible
        if (EclipseUtils.getActivePage() != null) {
            try {
                EclipseUtils.getActivePage().showView(PlotDataView.ID, null, IWorkbenchPage.VIEW_ACTIVATE);
            } catch (PartInitException e) {
                logger.error("Cannot open " + PlotDataView.ID);
            }
        }

        getEditorSite().setSelectionProvider(plottingSystem.getSelectionProvider());

        plottingSystem.setRescale(
                Activator.getDefault().getPreferenceStore().getBoolean(EditorConstants.RESCALE_SETTING));

        // Script connection
        this.connection = new ScriptingConnection(getPartName());
        connection.setPlottingSystem(plottingSystem);

    }

    protected void setAxisSettings(String propertyStub, IAxis axis) {
        IPreferenceStore store = Activator.getDefault().getPreferenceStore();
        boolean isDateTime = axis.isDateFormatEnabled();
        store.setValue(propertyStub + "isDateTime", isDateTime);
        if (isDateTime) {
            String format = axis.getFormatPattern();
            store.setValue(propertyStub + "dateFormat", format);
        }
        boolean isLog = axis.isLog10();
        store.setValue(propertyStub + "log10", isLog);
    }

    protected void getAxisSettings(String propertyStub, IAxis axis) {
        IPreferenceStore store = Activator.getDefault().getPreferenceStore();
        boolean isDateTime = false;
        if (store.contains(propertyStub + "isDateTime")) {
            isDateTime = store.getBoolean(propertyStub + "isDateTime");
            axis.setDateFormatEnabled(isDateTime);
        }
        if (isDateTime && store.contains(propertyStub + "dateFormat")) {
            axis.setFormatPattern(store.getString(propertyStub + "dateFormat"));
        }
        if (store.contains(propertyStub + "log10")) {
            axis.setLog10(store.getBoolean(propertyStub + "log10"));
        }
    }

    /**
     * Call to change plot default by looking at data
     */
    public void createPlotTypeDefaultFromData() {
        // If the data is only 2D we tell the PlottingSystem to switch to 2D mode.
        boolean is1D = false;

        final PlotDataComponent dataSetComponent = (PlotDataComponent) getDataSetComponent();
        if (dataSetComponent != null) {
            for (ITransferableDataObject set : dataSetComponent.getData()) {
                final int[] shape = set.getShape(true);
                if (shape.length == 1) {
                    is1D = true;
                    break;
                }
            }
        }
        if (!is1D) {
            getPlottingSystem().setPlotType(PlotType.IMAGE);
        }

    }

    private boolean doingUpdate = false;

    /**
     * Must be called in UI thread
     * @param selections
     * @param useTask
     */
    @Override
    public void updatePlot(final ITransferableDataObject[] selections, final ISliceSystem sliceSystem,
            final boolean useTask) {

        if (doingUpdate)
            return;

        if (selections == null || selections.length < 1) {
            if (sliceSystem != null)
                sliceSystem.setVisible(false);
        }

        try {
            doingUpdate = true;
            if (selections == null || selections.length < 1) {
                plottingSystem.reset();
                return;
            }

            final int[] shape = selections[0].getShape(true);
            if (selections.length == 1 && shape.length != 1) {

                ITransferableDataObject object = selections[0];
                sliceSystem.setVisible(true);

                final IVariableManager man = (IVariableManager) getAdapter(IVariableManager.class);
                final ILazyDataset lazy = selections[0].getLazyData(null);
                sliceSystem.setData(new SliceSource(man, lazy, object.getName(),
                        EclipseUtils.getFilePath(getEditorInput()), object.isExpression()));

                return;
            }

            sliceSystem.setVisible(false);

            if (useTask) {
                plotJob.plot(selections, sliceSystem);
            } else {
                createPlot(selections, sliceSystem, new NullProgressMonitor());
            }
        } finally {
            doingUpdate = false;
        }
    }

    private class PlotJob extends Job {

        public PlotJob() {
            super("Plot update");
            setSystem(true);
            setUser(false);
            setPriority(Job.INTERACTIVE);
        }

        private ITransferableDataObject[] selections;
        private ISliceSystem system;

        public void plot(ITransferableDataObject[] selections, ISliceSystem system) {

            cancel();
            this.selections = selections;
            this.system = system;
            schedule();

        }

        public IStatus run(IProgressMonitor monitor) {
            monitor.beginTask("Updating selected DataSets", 100);
            createPlot(selections, system, monitor);
            return Status.OK_STATUS;
        }
    }

    private void createPlot(final ITransferableDataObject[] selections, final ISliceSystem system,
            final IProgressMonitor monitor) {

        if (monitor.isCanceled())
            return;

        final ITransferableDataObject first = selections[0];
        IDataset data = first.getData(new ProgressMonitorWrapper(monitor));
        if (data == null)
            return;
        data = data.squeeze();
        try {
            if (data.getSize() < 0)
                return;
        } catch (Exception ne) {
            return;
        }
        if (data.getRank() > 2)
            return; // Cannot plot more that 2 dims!

        if (data.getRank() == 2) {

            plottingSystem.clear();
            // TODO Data Name
            plottingSystem.createPlot2D(data, null, first.getName(), monitor);

        } else {
            List<ITransferableDataObject> sels = new ArrayList<ITransferableDataObject>(Arrays.asList(selections));

            final IDataset x;
            if (plottingSystem.isXFirst() && sels.size() > 1) {
                x = data;
                sels.remove(0);
            } else {
                x = null;
            }

            if (sels.isEmpty() || (!plottingSystem.isXFirst() && sels.size() == 1)) {

                // TODO Data Name
                final List<ITrace> traces = plottingSystem.updatePlot1D(x, Arrays.asList(data),
                        Arrays.asList(first.getName()), monitor);
                removeOldTraces(traces);
                sync(sels, traces);
                if (plottingSystem.isRescale())
                    plottingSystem.repaint();
                return;
            }

            final Map<Integer, List<IDataset>> ys = sels.isEmpty() ? null : new HashMap<Integer, List<IDataset>>(4);

            final Map<Integer, List<String>> dataNames = sels.isEmpty() ? null
                    : new HashMap<Integer, List<String>>(4);

            // Sort ys by axes (for 2D there is one y)
            if (!sels.isEmpty()) {
                for (int i = 1; i <= 4; i++) {
                    getYS(i, sels, monitor, ys, dataNames);
                }
            }

            final List<ITrace> traces = createPlotSeparateAxes(x, ys, dataNames, monitor);
            sync(sels, traces);

        }

        plottingSystem.repaint();

        monitor.done();
    }

    /**
     * Records any expression names so that they can be available in the history tool.
     * @param sels
     * @param traces
     */
    private void sync(List<ITransferableDataObject> sels, List<ITrace> traces) {
        if (sels == null || traces == null)
            return;
        if (sels.size() == traces.size()) {
            for (int i = 0; i < sels.size(); i++) {
                if (traces.get(i).getUserObject() == null || traces.get(i).getUserObject() instanceof String) {
                    traces.get(i).setUserObject(sels.get(i).getVariable());
                }
            }
        }

    }

    protected List<ITrace> createPlotSeparateAxes(final IDataset x, final Map<Integer, List<IDataset>> ysMap,
            final Map<Integer, List<String>> dNameMap, final IProgressMonitor monitor) {

        final List<ITrace> traces = new ArrayList<ITrace>();
        Display.getDefault().syncExec(new Runnable() {
            public void run() {
                try {
                    for (int i = 1; i <= 4; i++) {
                        final IAxis axis = axisMap.get(i);
                        if (axis == null)
                            continue;
                        if (ysMap.get(i) == null) {
                            axis.setVisible(false);
                            continue;
                        }
                        axis.setVisible(true);
                        plottingSystem.setSelectedYAxis(axis);

                        final List<IDataset> ys = ysMap.get(i);
                        final List<String> dn = dNameMap.get(i);

                        // Tell traces its data name.
                        final List<ITrace> plotted = plottingSystem.updatePlot1D(x, ys, dn, monitor);
                        traces.addAll(plotted);
                    }

                    // Remove traces in the plotting system that were not
                    // in this round of plotting.
                    removeOldTraces(traces);

                } finally {
                    plottingSystem.setSelectedYAxis(axisMap.get(1));
                }
            }
        });

        if (plottingSystem.isRescale())
            plottingSystem.repaint();
        return traces;

    }

    private void removeOldTraces(final List<ITrace> traces) {
        Display.getDefault().syncExec(new Runnable() {
            public void run() {
                final Collection<ITrace> existing = plottingSystem.getTraces();
                existing.removeAll(traces);
                for (ITrace iTrace : existing) {
                    if (iTrace.getUserObject() == HistoryType.HISTORY_PLOT)
                        continue;
                    plottingSystem.removeTrace(iTrace);
                }
            }
        });
    }

    private void getYS(int iyaxis, List<ITransferableDataObject> selections, IProgressMonitor monitor,
            Map<Integer, List<IDataset>> yMap, Map<Integer, List<String>> dMap) {

        List<IDataset> ys = new ArrayList<IDataset>(3);
        List<String> dn = new ArrayList<String>(3);
        for (ITransferableDataObject co : selections) {

            if (co.getYaxis() != iyaxis)
                continue;
            final IDataset y = co.getData(new ProgressMonitorWrapper(monitor));
            ys.add(y.squeeze());
            dn.add(co.getName());
            if (monitor.isCanceled())
                return;
            monitor.worked(1);
        }
        yMap.put(iyaxis, ys.isEmpty() ? null : ys);
        dMap.put(iyaxis, dn.isEmpty() ? null : dn);
    }

    @Override
    public void setInput(final IEditorInput input) {
        setInput(input, true);
    }

    private void setInput(final IEditorInput input, boolean createData) {

        super.setInput(input);
        setPartName(input.getName());

        if (createData)
            initJob.schedule(input);
    }

    /**
     * Method used to select all 1D plots in the order they are extracted.
     * If overide then current selected plots will be ignored.
     */
    public void setAll1DSelected(final boolean overide) {

        final PlotDataComponent dataSetComponent = (PlotDataComponent) getDataSetComponent();
        dataSetComponent.setAll1DSelected(overide);
    }

    private class InitJob extends Job {

        private IEditorInput input;

        InitJob() {
            super("Update data");
            setUser(false);
            setSystem(true);
            setPriority(Job.INTERACTIVE);
        }

        public void schedule(IEditorInput i) {
            cancel();
            input = i;
            schedule();
        }

        @Override
        public IStatus run(final IProgressMonitor monitor) {
            try {
                lock.lock();

                // Load data in Job
                final String path = EclipseUtils.getFilePath(input);
                final IDataHolder dataHolder = LoaderFactory.getData(path, true, true,
                        new ProgressMonitorWrapper(monitor));
                final IMetadata meta = dataHolder.getMetadata();
                if (monitor.isCanceled())
                    return Status.CANCEL_STATUS;

                // Update UI
                Display.getDefault().syncExec(new Runnable() {
                    public void run() {
                        try {
                            if (monitor.isCanceled())
                                return;
                            setFocus();
                            final PlotDataComponent dataSetComponent = (PlotDataComponent) getDataSetComponent();
                            if (dataSetComponent == null)
                                return;
                            dataSetComponent.setData(dataHolder, meta, true);
                            dataSetComponent.refresh();
                            ((AbstractPlottingSystem) getPlottingSystem())
                                    .setRootName(dataSetComponent.getRootName());
                        } catch (Throwable ignored) {
                            // Editor might not be valid but still open.
                        }
                    }
                });
                return Status.OK_STATUS;

            } catch (Exception ne) {
                logger.error("Cannot open nexus", ne);
                return Status.CANCEL_STATUS;
            } finally {
                lock.unlock();
            }
        }
    }

    @Override
    public void setFocus() {

        if (plottingSystem != null) {
            plottingSystem.setFocus();
        }

        final PlotDataComponent pc = (PlotDataComponent) getDataSetComponent();
        if (pc != null && (pc.getData() == null || pc.getData().isEmpty())) {
            initJob.schedule(getEditorInput());
        }
    }

    @Override
    public void doSave(IProgressMonitor monitor) {
        // TODO Auto-generated method stub

    }

    @Override
    public void doSaveAs() {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean isSaveAsAllowed() {
        return false;
    }

    @Override
    public void dispose() {

        connection.dispose();

        if (plottingSystem != null)
            plottingSystem.dispose();

        super.dispose();
    }

    public IVariableManager getDataSetComponent() {

        final IWorkbenchPage wb = EclipseUtils.getActivePage();
        if (wb == null)
            return null;

        final PlotDataView view = (PlotDataView) wb.findView(PlotDataView.ID);
        if (view == null)
            return null;

        IPage page = view.getCurrentPage();
        if (!(page instanceof PlotDataPage))
            return null;
        return ((PlotDataPage) page).getDataSetComponent();
    }

    public ISliceSystem getSliceComponent() {

        final IWorkbenchPage wb = EclipseUtils.getActivePage();
        if (wb == null)
            return null;

        final PlotDataView view = (PlotDataView) wb.findView(PlotDataView.ID);
        if (view == null)
            return null;

        IPage page = view.getCurrentPage();
        if (!(page instanceof PlotDataPage))
            return null;
        return ((PlotDataPage) page).getSliceComponent();
    }

    public IPlottingSystem getPlotWindow() {
        return plottingSystem;
    }

    public IPlottingSystem getPlottingSystem() {
        return this.plottingSystem;
    }

    public Object getAdapter(@SuppressWarnings("rawtypes") final Class clazz) {

        if (clazz == Page.class) {
            try {
                lock.lock();
                return PlotDataPage.getPageFor(this);
            } finally {
                lock.unlock();
            }
        } else if (clazz == IToolPageSystem.class || clazz == IPlottingSystem.class) {
            return getPlottingSystem();
        } else if (clazz == ISliceSystem.class) {
            return getSliceComponent();
        } else if (clazz == IVariableManager.class) {
            return getDataSetComponent();
        }

        return super.getAdapter(clazz);
    }

    public String toString() {
        if (getEditorInput() != null)
            return getEditorInput().getName();
        return super.toString();
    }

    @Override
    public void setPartTitle(String name) {
        super.setPartName(name);
        if (parent != null)
            parent.setPartTitle(name);
    }
}