org.eclipse.eavp.viz.service.csv.CSVPlot.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.eavp.viz.service.csv.CSVPlot.java

Source

/*******************************************************************************
 * Copyright (c) 2014, 2015 UT-Battelle, LLC.
 * 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:
 *   Initial API and implementation and/or initial documentation -
 *   Jay Jay Billings
 *   Kasper Gammeltoft - viz series refactor
 *   Jordan Deyton - viz multi-series refactor 
 *******************************************************************************/
package org.eclipse.eavp.viz.service.csv;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.beanutils.ConvertUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.eavp.viz.service.AbstractPlot;
import org.eclipse.eavp.viz.service.IPlot;
import org.eclipse.eavp.viz.service.IRenderElementHolder;
import org.eclipse.eavp.viz.service.ISeries;
import org.eclipse.january.geometry.Geometry;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class implements the IPlot interface to provide access to a basic CSV
 * plot using the existing CSV infrastructure in ICE.
 *
 * In addition to the IPlot operations it provides the load() operation that
 * should be called after construction if you want to take the data and series
 * information from the specified URI. Otherwise, it is necessary to manually
 * add the series information. This second option is required to set custom
 * settings for this plot, specifically for the style of the series and
 * specifying which one is the independent variable.
 *
 * @author Jay Jay Billings, Anna Wojtowicz, Alex McCaskey
 * @author Kasper Gammeltoft- Updated to extend MultiPlot, added ISeries
 *         functionality, updated to use {@link CSVPlotRender}s rather than
 *         DrawnPlots.
 *
 */
public class CSVPlot extends AbstractPlot {

    // TODO All series should be stored in the map, including the independent
    // series. This way if the independent series is cleared, its data is not
    // lost.

    /**
     * Logger for handling event messages and other information.
     */
    private static final Logger logger = LoggerFactory.getLogger(CSVPlot.class);

    /**
     * A map containing all dependent series, keyed on the categories.
     */
    private final Map<String, List<ISeries>> dataSeries;

    /**
     * The TextEditor which contains the plot's data in an editable text form.
     */
    private IEditorPart editor;

    /**
     * Whether or not the data has been loaded.
     */
    private final AtomicBoolean loaded = new AtomicBoolean(false);

    /**
     * The default constructor.
     */
    public CSVPlot() {
        dataSeries = new HashMap<String, List<ISeries>>();

        // Initially set the title to an empty string because the CSVPlotEditor
        // does not like null titles.
        setPlotTitle("");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.eavp.viz.service.IVizService#createAdditionalPage(org.
     * eclipse. swt.widgets.Composite, java.net.URI, int)
     */
    @Override
    public String createAdditionalPage(MultiPageEditorPart parent, IFileEditorInput file, int pageNum) {

        // Create the specified page
        switch (pageNum) {

        // Page 2 is the file's data displayed in text
        case 1:

            // Create a text editor with the file as input and add its page with
            // the name Data
            try {
                editor = (IEditorPart) new TextEditor();
                parent.addPage(editor, file);
                return "Data";
            } catch (PartInitException e) {
                logger.error("Error initializing text editor for CSV Plot Editor.");
            }
            break;
        }

        // If the page number is not supported, return null
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.eavp.viz.service.AbstractPlot#getCategories()
     */
    @Override
    public List<String> getCategories() {
        // Use the map of ProxySeries, which has the categories.
        return new ArrayList<String>(dataSeries.keySet());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.eavp.viz.service.AbstractPlot#getDependentSeries(java.lang
     * .String)
     */
    @Override
    public List<ISeries> getDependentSeries(String category) {
        // Use the map of ProxySeries to get a new list of ISeries associated
        // with the category.
        List<ISeries> series = dataSeries.get(category);
        if (series != null) {
            series = new ArrayList<ISeries>(series);
        }
        return series;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.eavp.viz.service.IPlot#getNumAdditionalPages()
     */
    @Override
    public int getNumAdditionalPages() {
        return 1;
    }

    /**
     * Always returns two as CSV plots are always 2D
     * 
     * @see org.eclipse.eavp.viz.service.IVizCanvas#getNumberOfAxes()
     */
    @Override
    public int getNumberOfAxes() {
        // The CSV plots are always 2D
        return 2;
    }

    /**
     * Gets whether or not data has been loaded from the data source.
     * 
     * @return True if data has been loaded, false otherwise.
     */
    public boolean isLoaded() {
        return loaded.get();
    }

    /**
     * This operation loads the data that will be plotted. It uses a separate
     * thread to avoid hanging the UI in the event that the file is large. It
     * does not attempt to load the file if the source is null.
     *
     */
    public void load() {

        URI uri = getDataSource();
        if (uri != null) {
            // Only load the file if it is a CSV file.
            final File file = new File(uri);
            if (file.getName().endsWith(".csv")) {
                // Loading has not completed.
                loaded.set(false);

                // Create the loading thread.
                Thread loadingThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        load(file);
                    }
                });

                // Force the loading thread to report unhandled exceptions to
                // this thread's exception handler.
                loadingThread.setUncaughtExceptionHandler(Thread.currentThread().getUncaughtExceptionHandler());

                // Start the thread
                loadingThread.start();
            } else {
                logger.error(getClass().getName() + ": Failed to load file " + file.getName()
                        + ", it must be of type .csv");
            }
        }

        return;
    }

    /**
     * Attempts to load the CSV series data from the specified file. This
     * operation ignores all data that is marked as comments (i.e. if the line
     * starts with #), and ignores comments after the data in a line as well.
     * This operation takes the first column of CSV data as the independent
     * series, and the rest as normal, dependent series to be added to the plot.
     * Note that only the first dependent series (the second column) will be
     * initially enabled to be drawn on the plot editor.
     * 
     * @param file
     *            The file to load, assumed to be a valid file.
     */
    private void load(File file) {
        // Initially set the name to the file name.
        String plotName = file.getName();
        setPlotTitle(plotName);

        // Configure the list
        ArrayList<String[]> lines = new ArrayList<String[]>();

        try {
            // Grab the contents of the file
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line = null;
            while ((line = reader.readLine()) != null) {
                // Skip lines that pure comments
                if (!line.startsWith("#")) {
                    // Clip the line if it has a comment symbol in it to be
                    // everything before the symbol
                    if (line.contains("#")) {
                        int index = line.indexOf("#");
                        line = line.substring(0, index);
                    }
                    // Clean up any bad stuff on the line
                    String[] lineArray = line.trim().split(",");
                    String[] trimmedLine = new String[lineArray.length];
                    // And clean up any bad stuff on each split piece
                    for (int i = 0; i < lineArray.length; i++) {
                        trimmedLine[i] = lineArray[i].trim();
                    }
                    // Put the lines in the list
                    lines.add(trimmedLine);
                }
            }

            reader.close();

        } catch (IOException e) {
            // Complain
            logger.error(
                    getClass().getName() + " Exception! Could not read in data from file: " + file.getName() + ".",
                    e);
        }

        if (!lines.isEmpty()) {

            // TODO- Some sort of implementation to read in the style
            // configurations for the plot, axes, and series. How to go about
            // this? A large part of the series implementation is not being
            // utilized without some sort of recognition here of the style
            // attributes!

            // Assume that the first line has information about the data
            String[] seriesNames = lines.remove(0);

            // Creates the series that contain the data loaded from the file.
            CSVSeries[] series = new CSVSeries[seriesNames.length];
            for (int i = 0; i < seriesNames.length; i++) {
                series[i] = new CSVSeries();
                series[i].setEnabled(false);
                series[i].setLabel(seriesNames[i]);
            }

            // Sets the first two series to be automatically plotted
            series[0].setEnabled(true);
            if (series.length > 1) {
                series[1].setEnabled(true);
            }

            // Load the data as doubles
            for (int i = 0; i < lines.size(); i++) {
                double[] values = (double[]) ConvertUtils.convert(lines.get(i), Double.TYPE);
                for (int j = 0; j < values.length; j++) {
                    series[j].add(values[j]);
                }
            }

            // If the independent series has not been set, use the default value
            // just created
            if (getIndependentSeries() == null) {
                // Just set the first series as the independent series for now
                setIndependentSeries(series[0]);
            } else {
                // Otherwise, update the series with the new data
                for (CSVSeries s : series) {
                    ISeries current = getIndependentSeries();
                    if (current.getLabel().equals(s.getLabel())) {
                        setIndependentSeries(s);
                        break;
                    }
                }
            }

            // Add the rest of the series as dependent series
            List<ISeries> dependentSeries = new ArrayList<ISeries>(series.length);
            for (int i = 0; i < series.length; i++) {
                dependentSeries.add(series[i]);
            }
            dataSeries.put(IPlot.DEFAULT_CATEGORY, dependentSeries);

        }

        // Loading has completed.
        loaded.set(true);

        // Notify the listeners that loading has completed.
        notifyPlotListeners("loaded", "true");

        return;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.eavp.viz.service.AbstractPlot#redraw()
     */
    @Override
    public void redraw() {
        // Start off by reloading this IPlot's representative data set.
        load();

        // We don't actually draw with a CSVPlot, but with CSVProxyPlots.
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.eavp.viz.service.AbstractPlot#setDataSource(java.net.URI)
     */
    @Override
    public boolean setDataSource(URI uri) throws Exception {
        boolean changed = super.setDataSource(uri);
        if (changed) {
            // Unregister from the old data source URI.
            dataSeries.clear();

            // Register with the new data source URI.
            load();
        }
        return changed;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.eavp.viz.service.IPlot#save(org.eclipse.core.runtime.
     * IProgressMonitor)
     */
    @Override
    public void save(IProgressMonitor monitor) {

        // Only the text editor can be saved
        editor.doSave(monitor);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.eavp.viz.service.IPlot#saveAs()
     */
    @Override
    public void saveAs() {

        // Only the text editor can be saved
        editor.doSaveAs();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.eavp.viz.service.IVizCanvas#getRenderElementHolder(geometry.
     * Geometry)
     */
    @Override
    public IRenderElementHolder getRenderElementHolder(Geometry geometry) {
        // A CSVPlot does not use IRenderElements
        return null;
    }

}