All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.gsi.chart.plugins.UpdateAxisLabels Maven / Gradle / Ivy

package de.gsi.chart.plugins;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.gsi.chart.Chart;
import de.gsi.chart.axes.Axis;
import de.gsi.chart.renderer.Renderer;
import de.gsi.chart.utils.FXUtils;
import de.gsi.dataset.DataSet;
import de.gsi.dataset.event.AxisChangeEvent;
import de.gsi.dataset.event.EventListener;
import de.gsi.dataset.event.EventRateLimiter;
import de.gsi.dataset.event.UpdateEvent;

/**
 * This plugin updates the labels (name and unit) of all axes according to DataSet Metadata. For now the axes are only
 * updated, if there is exactly one DataSet in the each Renderer or the Chart.
 *
 * @author akrimm
 */
public class UpdateAxisLabels extends ChartPlugin {
    private static final int UPDATE_RATE_LIMIT = 200; // maximum label update rate

    private static final Logger LOGGER = LoggerFactory.getLogger(UpdateAxisLabels.class);

    // listener bookkeeping
    private Map> rendererDataSetsListeners = new HashMap<>();
    private Map chartDataSetsListeners = new HashMap<>();
    private Map> renderersListeners = new HashMap<>();

    // called whenever renderers are added or removed
    private ListChangeListener renderersListener = (ListChangeListener.Change renderersChange) -> {
        while (renderersChange.next()) {
            if (renderersChange.wasAdded()) {
                for (Renderer renderer : renderersChange.getAddedSubList()) {
                    ListChangeListener dataSetsListener = (ListChangeListener.Change dataSetsChange) -> {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.atDebug().log("update listener -> dataSetsChanged ");
                        }
                        dataSetsChanged(dataSetsChange, renderer);
                    };
                    renderer.getDatasets().forEach(ds -> dataSetChange(new AxisChangeEvent(ds, -1), renderer));
                    renderer.getDatasets().addListener(dataSetsListener);
                    renderersListeners.put(renderer, dataSetsListener);
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.atDebug().addArgument(renderer.getClass().getSimpleName()).addArgument(rendererDataSetsListeners.size()).log("added listener for render {}, number of data set listeners {}");
                    }
                }
            }
            if (renderersChange.wasRemoved()) {
                for (Renderer renderer : renderersChange.getRemoved()) {
                    renderer.getDatasets().removeListener(renderersListeners.get(renderer));
                    renderersListeners.remove(renderer);
                }
            }
        }
    };

    // called whenever the chart for the plugin is changed
    private final ChangeListener chartChangeListener = (change, oldChart, newChart) -> {
        removeRendererAndDataSetListener(oldChart);
        addRendererAndDataSetListener(newChart);
    };

    /**
     * Default Constructor
     */
    public UpdateAxisLabels() {
        super();
        chartProperty().addListener(chartChangeListener);
        addRendererAndDataSetListener(getChart());
    }

    private void addRendererAndDataSetListener(Chart newChart) {
        if (newChart == null) {
            return;
        }
        setupDataSetListeners(null, newChart.getDatasets());
        newChart.getRenderers().addListener(renderersListener);
        newChart.getRenderers().forEach((Renderer r) -> setupDataSetListeners(r, r.getDatasets()));
    }

    // the actual DataSet renaming logic
    private void dataSetChange(UpdateEvent update, Renderer renderer) {
        if (!(update instanceof AxisChangeEvent)) {
            return;
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.atDebug().log("axis - dataSetChange for AxisChangeEvent");
        }
        AxisChangeEvent axisDataUpdate = (AxisChangeEvent) update;
        int dim = axisDataUpdate.getDimension();
        DataSet dataSet = (DataSet) axisDataUpdate.getSource();
        if (renderer == null) { // dataset was added to / is registered at chart
            if (getChart().getDatasets().size() == 1) {
                if (dim == -1) { // update labels for all axes
                    for (int dimIdx = 0; dimIdx < dataSet.getDimension(); dimIdx++) {
                        final int dimIndex = dimIdx;
                        Optional oldAxis = getChart().getAxes().stream().filter(axis -> axis.getDimIndex() == dimIndex).findFirst();
                        oldAxis.ifPresent(a -> a.set(dataSet.getAxisDescription(dimIndex).getName(), dataSet.getAxisDescription(dimIndex).getUnit()));
                    }
                } else { // update label for requested axis
                    Optional oldAxis = getChart().getAxes().stream().filter(axis -> axis.getDimIndex() == dim).findFirst();
                    oldAxis.ifPresent(a -> a.set(dataSet.getAxisDescription(dim).getName(), dataSet.getAxisDescription(dim).getUnit()));
                }
            } else {
                if (LOGGER.isWarnEnabled()) {
                    LOGGER.atWarn().log(
                            "Applying axis information not possible for more than one DataSet added to chart. Please add datasets to separate Renderers");
                }
            }
        } else { // dataset was added to / is registered at renderer
            if (renderer.getDatasets().size() == 1) {
                if (dim == -1) { // update labels for all axes
                    for (int dimIdx = 0; dimIdx < dataSet.getDimension(); dimIdx++) {
                        final int dimIndex = dimIdx;
                        Optional oldAxis = renderer.getAxes().stream().filter(axis -> axis.getDimIndex() == dimIndex).findFirst() //
                                                         .or(() -> getChart().getAxes().stream().filter(axis -> axis.getDimIndex() == dimIndex).findFirst());
                        oldAxis.ifPresent(a -> a.set(dataSet.getAxisDescription(dimIndex).getName(), dataSet.getAxisDescription(dimIndex).getUnit()));
                    }
                } else { // update label for requested axis
                    Optional oldAxis = renderer.getAxes().stream().filter(axis -> axis.getDimIndex() == dim).findFirst() //
                                                     .or(() -> getChart().getAxes().stream().filter(axis -> axis.getDimIndex() == dim).findFirst());
                    oldAxis.ifPresent(a -> a.set(dataSet.getAxisDescription(dim).getName(), dataSet.getAxisDescription(dim).getUnit()));
                }
            } else {
                if (LOGGER.isWarnEnabled()) {
                    LOGGER.atWarn().log(
                            "Applying axis information not possible for more than one DataSet added to renderer. Please add datasets to separate Renderers");
                }
            }
        }
    }

    private void dataSetsChanged(ListChangeListener.Change change, Renderer renderer) {
        Map dataSetListeners;
        if (renderer == null) {
            dataSetListeners = chartDataSetsListeners;
        } else if (rendererDataSetsListeners.containsKey(renderer)) {
            dataSetListeners = rendererDataSetsListeners.get(renderer);
        } else {
            dataSetListeners = new HashMap<>();
            rendererDataSetsListeners.put(renderer, dataSetListeners);
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.atDebug().log("dataSetsChanged added/removed - invoked");
        }
        while (change.next()) {
            if (change.wasAdded()) {
                for (DataSet dataSet : change.getAddedSubList()) {
                    EventListener dataSetListener = update -> FXUtils.runFX(() -> dataSetChange(update, renderer));
                    EventRateLimiter rateLimitedDataSetListener = new EventRateLimiter(dataSetListener, UPDATE_RATE_LIMIT);
                    dataSet.addListener(rateLimitedDataSetListener);
                    dataSetListeners.put(dataSet, rateLimitedDataSetListener);
                    dataSetChange(new AxisChangeEvent(dataSet, -1), renderer); // NOPMD - normal in-loop instantiation
                }
            }
            if (change.wasRemoved()) {
                for (DataSet dataSet : change.getRemoved()) {
                    EventListener listener = dataSetListeners.get(dataSet);
                    if (listener != null) {
                        dataSet.removeListener(listener);
                        dataSetListeners.remove(dataSet);
                    }
                }
            }
        }
    }

    private void removeRendererAndDataSetListener(Chart oldChart) {
        if (oldChart == null) {
            return;
        }
        teardownDataSetListeners(null, oldChart.getDatasets());
        oldChart.getRenderers().removeListener(renderersListener);
        oldChart.getRenderers().forEach((Renderer r) -> teardownDataSetListeners(r, r.getDatasets()));
    }

    // setup all the listeners
    private void setupDataSetListeners(Renderer renderer, ObservableList dataSets) {
        Map dataSetListeners;
        if (renderer == null) {
            dataSetListeners = chartDataSetsListeners;
        } else if (rendererDataSetsListeners.containsKey(renderer)) {
            dataSetListeners = rendererDataSetsListeners.get(renderer);
        } else {
            dataSetListeners = new HashMap<>();
            rendererDataSetsListeners.put(renderer, dataSetListeners);
        }

        ListChangeListener rendererListener = (ListChangeListener.Change change) -> dataSetsChanged(change, renderer);
        dataSets.addListener(rendererListener);
        renderersListeners.put(renderer, rendererListener);

        dataSets.forEach((DataSet dataSet) -> {
            EventListener dataSetListener = update -> FXUtils.runFX(() -> dataSetChange(update, renderer));
            EventRateLimiter rateLimitedDataSetListener = new EventRateLimiter(dataSetListener, UPDATE_RATE_LIMIT);
            dataSet.addListener(rateLimitedDataSetListener);
            dataSetListeners.put(dataSet, rateLimitedDataSetListener);
            dataSetChange(new AxisChangeEvent(dataSet, -1), renderer);
        });
    }

    // remove Listeners
    private void teardownDataSetListeners(Renderer renderer, ObservableList dataSets) {
        Map dataSetListeners;
        if (renderer == null) {
            dataSetListeners = chartDataSetsListeners;
        } else if (rendererDataSetsListeners.containsKey(renderer)) {
            dataSetListeners = rendererDataSetsListeners.get(renderer);
        } else {
            dataSetListeners = new HashMap<>();
            rendererDataSetsListeners.put(renderer, dataSetListeners);
        }

        dataSets.removeListener(renderersListeners.get(renderer));
        renderersListeners.remove(renderer);

        dataSets.forEach((DataSet dataSet) -> {
            dataSet.removeListener(dataSetListeners.get(dataSet));
            dataSetListeners.remove(dataSet);
        });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy