ca.phon.app.query.QueryRunnerPanel.java Source code

Java tutorial

Introduction

Here is the source code for ca.phon.app.query.QueryRunnerPanel.java

Source

/*
 * Phon - An open source tool for research in phonology.
 * Copyright (C) 2005 - 2015, Gregory Hedlund <ghedlund@mun.ca> and Yvan Rose <yrose@mun.ca>
 * Dept of Linguistics, Memorial University <https://phon.ca>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ca.phon.app.query;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableRowSorter;

import org.jdesktop.swingx.JXBusyLabel;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.HighlighterFactory;

import ca.phon.app.log.BufferPanel;
import ca.phon.app.log.BufferWindow;
import ca.phon.app.log.LogBuffer;
import ca.phon.app.opgraph.report.ReportLibrary;
import ca.phon.app.query.EditQueryDialog.ReturnStatus;
import ca.phon.plugin.PluginEntryPointRunner;
import ca.phon.plugin.PluginException;
import ca.phon.project.DefaultProjectFactory;
import ca.phon.project.Project;
import ca.phon.project.ProjectFactory;
import ca.phon.query.db.Query;
import ca.phon.query.db.QueryFactory;
import ca.phon.query.db.QueryManager;
import ca.phon.query.db.ResultSet;
import ca.phon.query.db.ResultSetManager;
import ca.phon.query.db.Script;
import ca.phon.query.script.QueryName;
import ca.phon.query.script.QueryScript;
import ca.phon.query.script.QueryScriptContext;
import ca.phon.query.script.QueryTask;
import ca.phon.script.PhonScriptException;
import ca.phon.script.params.ScriptParam;
import ca.phon.script.params.ScriptParameters;
import ca.phon.session.Session;
import ca.phon.session.SessionPath;
import ca.phon.util.icons.IconManager;
import ca.phon.util.icons.IconSize;
import ca.phon.worker.PhonTask;
import ca.phon.worker.PhonTask.TaskStatus;
import ca.phon.worker.PhonTaskListener;
import ca.phon.worker.PhonWorker;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

public class QueryRunnerPanel extends JPanel {

    private static final long serialVersionUID = 1427147887370979071L;

    private final static Logger LOGGER = Logger.getLogger(QueryRunnerPanel.class.getName());

    /* UI Elements */
    /** The report button */
    private JButton reportButton;

    /** The save results button */
    private JButton saveButton;

    private JXBusyLabel busyLabel;

    /** The table */
    private JXTable resultsTable;

    /**
     * Table model
     */
    private RunnerTableModel tableModel;

    /** Top panel */
    private JPanel topPanel;

    /** Completed Label */
    private JLabel completedLabel;

    /** Hide no-result rows */
    private JCheckBox hideRowsBox;
    private JCheckBox openEditorBox;

    /**
     * Load from temporary folder or project folder?
     */
    private boolean loadFromTemp = true;

    /** Temp project, used for saving result sets until they are 'saved' by the user */
    private Project tempProject;

    /** Project */
    private Project project;

    /** The query */
    private Query query;

    private boolean includeExcluded;

    /**
     * Query script
     */
    private final QueryScript queryScript;

    private final TableRowSorter<RunnerTableModel> resultsTableSorter;

    /**
     * Property change event that is sent when the query is 
     * saved in history.
     * 
     */
    public final static String QUERY_SAVED_PROP = "_query_saved_";

    public QueryRunnerPanel(Project project, QueryScript script, List<SessionPath> selectedSessions,
            boolean includeExcluded) {
        super();
        this.project = project;
        this.queryScript = script;
        this.tableModel = new RunnerTableModel(selectedSessions);
        this.includeExcluded = includeExcluded;
        resultsTableSorter = new TableRowSorter<QueryRunnerPanel.RunnerTableModel>(tableModel);

        final UUID uuid = UUID.randomUUID();
        try {
            final String tmpFolder = System.getProperty("java.io.tmpdir");
            final String tmpProjectFolder = tmpFolder + File.separator + "phon-"
                    + Long.toHexString(uuid.getLeastSignificantBits());
            //         tempProjectFile.mkdirs();
            final ProjectFactory factory = new DefaultProjectFactory();
            tempProject = factory.createProject(new File(tmpProjectFolder));

            for (String corpusName : project.getCorpora()) {
                tempProject.addCorpus(corpusName, "");
                tempProject.setCorpusPath(corpusName, project.getCorpusPath(corpusName));
            }
            tempProject.setRecourceLocation(project.getResourceLocation());
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
        }

        init();
    }

    private void init() {
        setLayout(new BorderLayout());

        // top panel
        FormLayout topLayout = new FormLayout("pref, 3dlu, left:pref, left:pref, fill:pref:grow, pref, right:pref",
                "pref");
        CellConstraints cc = new CellConstraints();
        topPanel = new JPanel(topLayout);

        saveButton = new JButton("Save results");
        ImageIcon saveIcon = IconManager.getInstance().getIcon("actions/document-save-as", IconSize.SMALL);
        saveButton.setIcon(saveIcon);
        saveButton.setEnabled(false);
        saveButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                showSaveQueryDialog();
            }

        });

        reportButton = new JButton("Report");
        ImageIcon ssIcon = IconManager.getInstance().getIcon("mimetypes/x-office-spreadsheet", IconSize.SMALL);
        reportButton.setIcon(ssIcon);
        reportButton.setEnabled(false);
        reportButton.setVisible(true);
        reportButton.addActionListener((e) -> {
            // show menu
            final JMenu menu = new JMenu();
            final ReportLibrary library = new ReportLibrary();
            library.setupMenu(tempProject, query.getUUID().toString(), menu);

            menu.getPopupMenu().show(reportButton, 0, reportButton.getHeight());
        });

        hideRowsBox = new JCheckBox("Hide empty results");
        hideRowsBox.setEnabled(false);
        hideRowsBox.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (hideRowsBox.isSelected()) {
                    final RowFilter<RunnerTableModel, Integer> filter = RowFilter.regexFilter("[1-9][0-9]*", 3);
                    resultsTableSorter.setRowFilter(filter);
                } else {
                    resultsTableSorter.setRowFilter(null);
                }
            }

        });

        openEditorBox = new JCheckBox("Open session with results");
        openEditorBox.setSelected(true);
        openEditorBox.setVisible(false);

        busyLabel = new JXBusyLabel(new Dimension(16, 16));
        busyLabel.setBusy(true);

        String labelText = "Completed: 0/" + tableModel.getRowCount();
        completedLabel = new JLabel(labelText);

        topPanel.add(completedLabel, cc.xy(3, 1));
        topPanel.add(busyLabel, cc.xy(1, 1));
        topPanel.add(openEditorBox, cc.xy(4, 1));
        topPanel.add(saveButton, cc.xy(6, 1));
        topPanel.add(reportButton, cc.xy(7, 1));

        // table

        resultsTable = new JXTable(tableModel);
        resultsTable.addHighlighter(HighlighterFactory.createSimpleStriping());
        resultsTable.setRowSorter(resultsTableSorter);

        resultsTable.getColumn(1).setCellRenderer(statusCellRenderer);
        resultsTable.getColumn(2).setCellRenderer(progressCellRenderer);

        resultsTable.setColumnControlVisible(true);
        resultsTable.setVisibleRowCount(15);
        resultsTable.packAll();

        resultsTable.addMouseListener(tableMouseListener);

        add(new JScrollPane(resultsTable), BorderLayout.CENTER);
        add(topPanel, BorderLayout.NORTH);
    }

    public void startQuery() {
        final PhonWorker worker = PhonWorker.createWorker();
        worker.setFinishWhenQueueEmpty(true);
        worker.invokeLater(queryTask);
        worker.start();
    }

    public void stopQuery() {
        queryTask.shutdown();
    }

    public boolean isRunning() {
        return queryTask.getStatus() == TaskStatus.RUNNING;
    }

    public Project getProject() {
        return project;
    }

    public void setProject(Project project) {
        this.project = project;
    }

    public Query getQuery() {
        return query;
    }

    public void setQuery(Query query) {
        this.query = query;
    }

    public boolean isSaved() {
        return !loadFromTemp;
    }

    private int numberComplete = 0;

    private void taskCompleted() {
        numberComplete++;
        completedLabel.setText("Completed: " + numberComplete + "/" + tableModel.getRowCount());
    }

    /**
     * Show ui for editing query details.
     */
    private void showSaveQueryDialog() {
        final EditQueryDialog queryDialog = new EditQueryDialog(getProject(), getQuery());
        queryDialog.setLocationRelativeTo(this);
        if (queryDialog.showModal() == ReturnStatus.OK)
            saveQuery();
    }

    /**
     * Perform save query.
     */
    private void saveQuery() {
        final Runnable beginningTask = new Runnable() {
            @Override
            public void run() {
                busyLabel.setBusy(true);
                saveButton.setEnabled(false);
            }
        };

        final Runnable successTask = new Runnable() {

            @Override
            public void run() {
                saveButton.setVisible(false);
            }
        };
        final Runnable finalTask = new Runnable() {
            @Override
            public void run() {
                saveButton.setEnabled(true);
                busyLabel.setBusy(false);

            }
        };

        // place save task on background thread
        final Runnable saveTask = new Runnable() {
            public void run() {
                SwingUtilities.invokeLater(beginningTask);
                final QueryManager qManager = QueryManager.getSharedInstance();
                final ResultSetManager rsManager = qManager.createResultSetManager();

                // save query first
                try {
                    rsManager.saveQuery(getProject(), getQuery());

                    // load from temp project
                    for (SessionPath sessionLocation : tableModel.sessions) {
                        final String sessionName = sessionLocation.getCorpus() + "." + sessionLocation.getSession();
                        final ResultSet tempResults = rsManager.loadResultSet(tempProject, getQuery(), sessionName);

                        // save to project
                        rsManager.saveResultSet(getProject(), getQuery(), tempResults);
                    }

                    loadFromTemp = false;

                    SwingUtilities.invokeLater(successTask);
                } catch (IOException e) {
                    LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
                } finally {
                    SwingUtilities.invokeLater(finalTask);
                }
            }
        };
        PhonWorker.getInstance().invokeLater(saveTask);

        super.firePropertyChange(QUERY_SAVED_PROP, Boolean.FALSE, Boolean.TRUE);
    }

    private final PhonTask queryTask = new PhonTask() {

        @Override
        public void performTask() {
            // do nothing if already shutdown
            if (isShutdown())
                return;
            super.setStatus(TaskStatus.RUNNING);

            final QueryManager qm = QueryManager.getSharedInstance();
            final QueryFactory qfactory = qm.createQueryFactory();
            final ResultSetManager rsManager = qm.createResultSetManager();

            // setup query object
            query = qfactory.createQuery(project);
            final QueryScriptContext ctx = queryScript.getQueryContext();

            ScriptParameters scriptParams = new ScriptParameters();
            try {
                scriptParams = ctx.getScriptParameters(ctx.getEvaluatedScope());
            } catch (PhonScriptException e) {
                LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
            }

            final Script qScript = query.getScript();
            qScript.setSource(queryScript.getScript());
            final Map<String, String> sparams = new HashMap<String, String>();
            for (ScriptParam sp : scriptParams) {
                if (sp.hasChanged()) {
                    for (String paramid : sp.getParamIds()) {
                        sparams.put(paramid, sp.getValue(paramid).toString());
                    }
                }
            }
            qScript.setParameters(sparams);
            qScript.setMimeType("text/javascript");

            query.setDate(LocalDateTime.now());

            final QueryName queryName = queryScript.getExtension(QueryName.class);
            String name = (queryName != null ? queryName.getName() : "untitled");
            query.setName(name);

            try {
                rsManager.saveQuery(tempProject, query);
            } catch (IOException e1) {
                e1.printStackTrace();
                LOGGER.log(Level.SEVERE, e1.getLocalizedMessage(), e1);
            }

            busyLabel.setBusy(true);

            int serial = 0;
            for (SessionPath sessionLocation : tableModel.sessions) {
                if (isShutdown())
                    break;
                // load session
                try {
                    final String bufferName = query.getName() + ":" + sessionLocation.toString();

                    final AtomicReference<BufferWindow> buffersRef = new AtomicReference<BufferWindow>();
                    final AtomicReference<LogBuffer> logBufferRef = new AtomicReference<LogBuffer>();

                    final Runnable onEdt = new Runnable() {
                        public void run() {
                            final BufferWindow buffers = BufferWindow.getInstance();
                            final LogBuffer logBuffer = buffers.createBuffer(bufferName).getLogBuffer();
                            buffersRef.set(buffers);
                            logBufferRef.set(logBuffer);
                        }
                    };
                    try {
                        SwingUtilities.invokeAndWait(onEdt);
                    } catch (InterruptedException e1) {
                        LOGGER.log(Level.SEVERE, e1.getLocalizedMessage(), e1);
                    } catch (InvocationTargetException e1) {
                        LOGGER.log(Level.SEVERE, e1.getLocalizedMessage(), e1);
                    }

                    if (logBufferRef.get() != null) {
                        final PrintStream outStream = new PrintStream(logBufferRef.get().getStdOutStream());
                        ctx.redirectStdErr(new PrintStream(logBufferRef.get().getStdErrStream(), false, "UTF-8"));
                        ctx.redirectStdOut(outStream);

                        logBufferRef.get().getDocument().addDocumentListener(new DocumentListener() {

                            @Override
                            public void removeUpdate(DocumentEvent e) {
                            }

                            @Override
                            public void insertUpdate(DocumentEvent e) {
                                if (!buffersRef.get().isVisible()) {
                                    buffersRef.get().showWindow();
                                }
                                buffersRef.get().selectBuffer(logBufferRef.get().getBufferName());
                                logBufferRef.get().getDocument().removeDocumentListener(this);
                            }

                            @Override
                            public void changedUpdate(DocumentEvent e) {
                            }

                        });

                        outStream.flush();
                        outStream.print(LogBuffer.ESCAPE_CODE_PREFIX + BufferPanel.SHOW_BUSY);
                        outStream.flush();
                    }

                    final Session session = project.openSession(sessionLocation.getCorpus(),
                            sessionLocation.getSession());

                    final QueryTask queryTask = new QueryTask(project, session, queryScript, ++serial);
                    queryTask.setIncludeExcludedRecords(includeExcluded);
                    queryTask.addTaskListener(queryTaskListener);

                    queryTask.run();
                    taskCompleted();

                    if (logBufferRef.get() != null) {
                        if (logBufferRef.get().getText().length() == 0) {
                            buffersRef.get().removeBuffer(logBufferRef.get().getBufferName());
                        }
                        final PrintStream outStream = new PrintStream(logBufferRef.get().getStdOutStream());
                        outStream.flush();
                        outStream.print(LogBuffer.ESCAPE_CODE_PREFIX + BufferPanel.STOP_BUSY);
                        outStream.flush();
                    }

                    rsManager.saveResultSet(tempProject, query, queryTask.getResultSet());
                } catch (IOException e) {
                    LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
                }
            }

            final PrintStream bufferOut = queryScript.getQueryContext().getStdOut();
            bufferOut.flush();
            bufferOut.print(LogBuffer.ESCAPE_CODE_PREFIX + BufferPanel.SHOW_TABLE_CODE);
            bufferOut.flush();

            busyLabel.setBusy(false);
            saveButton.setEnabled(true);
            reportButton.setEnabled(true);

            topPanel.remove(completedLabel);
            topPanel.add(hideRowsBox, (new CellConstraints()).xy(3, 1));
            openEditorBox.setVisible(true);
            topPanel.revalidate();
            hideRowsBox.setEnabled(true);

            super.setStatus(TaskStatus.FINISHED);
        }

    };

    private MouseInputAdapter tableMouseListener = new MouseInputAdapter() {

        /* (non-Javadoc)
         * @see javax.swing.event.MouseInputAdapter#mouseClicked(java.awt.event.MouseEvent)
         */
        @Override
        public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() == 2) {
                int selectedIndex = resultsTable.getSelectedRow();
                selectedIndex = resultsTable.convertRowIndexToModel(selectedIndex);
                if (selectedIndex >= 0) {
                    final SessionPath location = tableModel.sessions.get(selectedIndex);
                    //               if(sessionNameObj == null) return;

                    final String sessionName = location.getCorpus() + "." + location.getSession();
                    final QueryManager qm = QueryManager.getSharedInstance();
                    final ResultSetManager rsManager = qm.createResultSetManager();

                    HashMap<String, Object> initInfo = new HashMap<String, Object>();
                    try {
                        final ResultSet rs = rsManager.loadResultSet((loadFromTemp ? tempProject : project), query,
                                sessionName);
                        initInfo.put("resultset", rs);
                    } catch (IOException e1) {
                        LOGGER.log(Level.SEVERE, e1.getLocalizedMessage(), e1);
                    }
                    initInfo.put("project", project);

                    if (loadFromTemp) {
                        initInfo.put("tempProject", tempProject);
                    }
                    initInfo.put("query", query);
                    initInfo.put("opensession", openEditorBox.isSelected());

                    // open editor first....
                    try {
                        PluginEntryPointRunner.executePlugin("ResultSetViewer", initInfo);
                    } catch (PluginException ex) {
                        LOGGER.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
                    }
                }
            }
        }

    };

    private final PhonTaskListener queryTaskListener = new PhonTaskListener() {

        @Override
        public void statusChanged(PhonTask task, TaskStatus oldStatus, TaskStatus newStatus) {
            final QueryTask queryTask = (QueryTask) task;
            final Session session = queryTask.getSession();

            final SessionPath location = new SessionPath(session.getCorpus(), session.getName());
            final int rowIdx = tableModel.sessions.indexOf(location);
            tableModel.setValueAt(newStatus, rowIdx, 1);

            if (newStatus == TaskStatus.RUNNING) {
                boolean autoScrollToRow = (resultsTable.getSelectedRow() < 0)
                        || (resultsTable.getSelectedRow() > 0 && !resultsTable.hasFocus());

                if (autoScrollToRow) {

                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            resultsTable.scrollRowToVisible(rowIdx);
                        }
                    });

                }
            } else {
                // update results row
                tableModel.setValueAt(queryTask.getProperty(PhonTask.PROGRESS_PROP), rowIdx, 2);
                int size = (queryTask.getResultSet() != null ? queryTask.getResultSet().size() : 0);
                tableModel.setValueAt(size, rowIdx, 3);
            }
        }

        @Override
        public void propertyChanged(PhonTask task, String property, Object oldValue, Object newValue) {
            final QueryTask queryTask = (QueryTask) task;
            final Session session = queryTask.getSession();

            final SessionPath location = new SessionPath(session.getCorpus(), session.getName());
            final int rowIdx = tableModel.sessions.indexOf(location);

            if (property.equals(QueryTask.PROGRESS_PROP)) {
                tableModel.setValueAt((Integer) queryTask.getProperty(QueryTask.PROGRESS_PROP), rowIdx, 2);

                int size = (queryTask.getResultSet() != null ? queryTask.getResultSet().size() : 0);
                tableModel.setValueAt(size, rowIdx, 3);
            }
        }

    };

    /**
     * Table model for results
     */
    private class RunnerTableModel extends AbstractTableModel {

        private final List<SessionPath> sessions;

        /*
         * cached values, set by using setValueAt
         */
        private final Map<SessionPath, PhonTask.TaskStatus> statusMap = new HashMap<SessionPath, PhonTask.TaskStatus>();

        private final Map<SessionPath, Integer> progressMap = new HashMap<SessionPath, Integer>();

        private final Map<SessionPath, Integer> resultsMap = new HashMap<SessionPath, Integer>();

        public RunnerTableModel(List<SessionPath> selectedSessions) {
            sessions = new ArrayList<SessionPath>();
            sessions.addAll(selectedSessions);
            Collections.sort(sessions);
        }

        /**
         * column names
         */
        private final String[] colNames = { "Session", "Status", "Progress", "# of Results" };

        @Override
        public int getColumnCount() {
            return colNames.length;
        }

        @Override
        public String getColumnName(int col) {
            return colNames[col];
        }

        @Override
        public int getRowCount() {
            return sessions.size();
        }

        @Override
        public Class<?> getColumnClass(int col) {
            Class<?> retVal = Object.class;

            switch (col) {
            case 0:
                retVal = SessionPath.class;
                break;

            case 1:
                retVal = PhonTask.TaskStatus.class;
                break;

            case 2:
                retVal = Integer.class;
                break;

            case 3:
                retVal = Integer.class;
                break;
            }

            return retVal;
        }

        @Override
        public Object getValueAt(int row, int col) {
            Object retVal = null;
            final SessionPath session = sessions.get(row);

            switch (col) {
            case 0:
                retVal = session;
                break;

            case 1:
                synchronized (statusMap) {
                    PhonTask.TaskStatus status = statusMap.get(session);
                    if (status == null)
                        status = TaskStatus.WAITING;
                    retVal = status;
                }
                break;

            case 2:
                synchronized (progressMap) {
                    Integer progress = progressMap.get(session);
                    if (progress == null)
                        progress = 0;
                    retVal = progress;
                }
                break;

            case 3:
                synchronized (resultsMap) {
                    Integer results = resultsMap.get(session);
                    if (results == null)
                        results = 0;
                    retVal = results;
                }
                break;

            default:
                retVal = "";
                break;
            }

            return retVal;
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            final SessionPath session = sessions.get(rowIndex);

            switch (columnIndex) {
            case 1:
                if (aValue instanceof PhonTask.TaskStatus) {
                    final PhonTask.TaskStatus status = (PhonTask.TaskStatus) aValue;
                    synchronized (statusMap) {
                        statusMap.put(session, status);
                    }
                    fireCellUpdate(rowIndex, columnIndex);
                }
                break;

            case 2:
                if (aValue instanceof Integer) {
                    final Integer progress = (Integer) aValue;
                    synchronized (progressMap) {
                        progressMap.put(session, progress);
                    }
                    fireCellUpdate(rowIndex, columnIndex);
                }
                break;

            case 3:
                if (aValue instanceof Integer) {
                    final Integer results = (Integer) aValue;
                    synchronized (resultsMap) {
                        resultsMap.put(session, results);
                    }
                    fireCellUpdate(rowIndex, columnIndex);
                }
                break;

            default:
                break;
            }
        }

        private void fireCellUpdate(final int row, final int col) {
            final Runnable toRun = new Runnable() {
                @Override
                public void run() {
                    fireTableCellUpdated(row, col);
                }
            };
            SwingUtilities.invokeLater(toRun);
        }

    }

    /* Cell renderers */
    private final DefaultTableCellRenderer statusCellRenderer = new DefaultTableCellRenderer() {

        ImageIcon waitingIcon = IconManager.getInstance().getIcon("actions/free_icon", IconSize.SMALL);

        ImageIcon runningIcon = IconManager.getInstance().getIcon("actions/greenled", IconSize.SMALL);

        ImageIcon errorIcon = IconManager.getInstance().getIcon("status/dialog-error", IconSize.SMALL);

        ImageIcon finishedIcon = IconManager.getInstance().getIcon("actions/ok", IconSize.SMALL);

        ImageIcon terminatedIcon = IconManager.getInstance().getIcon("status/dialog-warning", IconSize.SMALL);

        /* (non-Javadoc)
         * @see javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent(javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
         */
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            JLabel retVal = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row,
                    column);

            TaskStatus status = (TaskStatus) value;

            if (status == TaskStatus.WAITING) {
                retVal.setIcon(waitingIcon);
            } else if (status == TaskStatus.RUNNING) {
                retVal.setIcon(runningIcon);
            } else if (status == TaskStatus.ERROR) {
                retVal.setIcon(errorIcon);
            } else if (status == TaskStatus.TERMINATED) {
                retVal.setIcon(terminatedIcon);
            } else if (status == TaskStatus.FINISHED) {
                retVal.setIcon(finishedIcon);
            }

            return retVal;
        }

    };

    private class ProgressCellRenderer extends JProgressBar implements TableCellRenderer {

        /* (non-Javadoc)
         * @see javax.swing.table.TableCellRenderer#getTableCellRendererComponent(javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
         */
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            if (!(value instanceof Integer)) {
                setValue(0);
                return this;
            }

            Integer v = (Integer) value;

            this.setIndeterminate(false);
            this.setMinimum(0);
            this.setMaximum(100);
            this.setValue(v);

            Dimension d = this.getPreferredSize();
            d.setSize(d.getWidth(), 10);
            this.setPreferredSize(d);

            return this;
        }

    }

    private final ProgressCellRenderer progressCellRenderer = new ProgressCellRenderer();
}