SiLeBAT/FSK-Lab

View on GitHub
de.bund.bfr.knime.pmm.nodes/src/de/bund/bfr/knime/pmm/microbialdataedit/MicrobialDataEditNodeDialog.java

Summary

Maintainability
F
2 wks
Test Coverage
/*******************************************************************************
 * Copyright (c) 2015 Federal Institute for Risk Assessment (BfR), Germany
 *
 * 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/>.
 *
 * Contributors:
 *     Department Biological Safety - BfR
 *******************************************************************************/
package de.bund.bfr.knime.pmm.microbialdataedit;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import org.hsh.bfr.db.DBKernel;
import org.knime.core.node.BufferedDataTable;
import org.knime.core.node.DataAwareNodeDialogPane;
import org.knime.core.node.InvalidSettingsException;
import org.knime.core.node.NodeSettingsRO;
import org.knime.core.node.NodeSettingsWO;
import org.knime.core.node.NotConfigurableException;

import de.bund.bfr.knime.pmm.common.AgentXml;
import de.bund.bfr.knime.pmm.common.DBUtilities;
import de.bund.bfr.knime.pmm.common.LiteratureItem;
import de.bund.bfr.knime.pmm.common.MatrixXml;
import de.bund.bfr.knime.pmm.common.MdInfoXml;
import de.bund.bfr.knime.pmm.common.MiscXml;
import de.bund.bfr.knime.pmm.common.PmmXmlDoc;
import de.bund.bfr.knime.pmm.common.PmmXmlElementConvertable;
import de.bund.bfr.knime.pmm.common.TimeSeriesXml;
import de.bund.bfr.knime.pmm.common.generictablemodel.KnimeRelationReader;
import de.bund.bfr.knime.pmm.common.generictablemodel.KnimeTuple;
import de.bund.bfr.knime.pmm.common.pmmtablemodel.AttributeUtilities;
import de.bund.bfr.knime.pmm.common.pmmtablemodel.SchemaFactory;
import de.bund.bfr.knime.pmm.common.pmmtablemodel.TimeSeriesSchema;
import de.bund.bfr.knime.pmm.common.ui.QualityComboBox;
import de.bund.bfr.knime.pmm.common.ui.TimeSeriesDialog;
import de.bund.bfr.knime.pmm.common.ui.UI;
import de.bund.bfr.knime.pmm.common.units.Categories;

/**
 * <code>NodeDialog</code> for the "MicrobialDataEdit" Node.
 * 
 * @author Christian Thoens
 */
public class MicrobialDataEditNodeDialog extends DataAwareNodeDialogPane implements ActionListener, MouseListener {

    private JButton addButton;
    private JButton removeButton;
    private JList<String> addedConditionsList;
    private JTable table;

    private Map<Integer, String> addedConditionNames;
    private Map<Integer, List<String>> addedConditionCategories;
    private Map<Integer, String> usedConditionNames;
    private Map<Integer, List<String>> usedConditionCategories;

    /**
     * New pane for configuring the MicrobialDataEdit node.
     */
    protected MicrobialDataEditNodeDialog() {
        addButton = new JButton("Add");
        addButton.addActionListener(this);
        removeButton = new JButton("Remove");
        removeButton.addActionListener(this);
        addedConditionsList = new JList<>();
        table = new JTable();
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        table.getTableHeader().addMouseListener(this);
        table.setAutoCreateRowSorter(true);

        JPanel buttonPanel = new JPanel();

        buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        buttonPanel.add(addButton);
        buttonPanel.add(removeButton);

        JScrollPane scrollPane = new JScrollPane(addedConditionsList);

        scrollPane.setPreferredSize(new Dimension(scrollPane.getPreferredSize().width, 50));

        JPanel listPanel = new JPanel();

        listPanel.setBorder(BorderFactory.createTitledBorder("Conditions to Add"));
        listPanel.setLayout(new BorderLayout());
        listPanel.add(buttonPanel, BorderLayout.NORTH);
        listPanel.add(scrollPane, BorderLayout.CENTER);

        JPanel panel = new JPanel();

        panel.setLayout(new BorderLayout());
        panel.add(listPanel, BorderLayout.NORTH);
        panel.add(new JScrollPane(table), BorderLayout.CENTER);

        addTab("Options", panel, false);
    }

    @Override
    protected void loadSettingsFrom(NodeSettingsRO settings, BufferedDataTable[] input)
            throws NotConfigurableException {
        SettingsHelper set = new SettingsHelper();

        set.loadSettings(settings);

        KnimeRelationReader reader = new KnimeRelationReader(SchemaFactory.createDataSchema(), input[0]);
        List<KnimeTuple> tuples = new ArrayList<>();
        List<String> ids = new ArrayList<>();
        List<AgentXml> agentList = new ArrayList<>();
        List<String> agentDetailList = new ArrayList<>();
        List<MatrixXml> matrixList = new ArrayList<>();
        List<String> matrixDetailList = new ArrayList<>();
        List<String> commentList = new ArrayList<>();
        List<Integer> qualityScoreList = new ArrayList<>();
        List<Boolean> checkedList = new ArrayList<>();
        List<List<TimeSeriesXml>> timeSeriesList = new ArrayList<>();
        List<List<LiteratureItem>> referenceList = new ArrayList<>();

        Set<Integer> usedMiscIds = new LinkedHashSet<>();
        List<MiscXml> usedMiscs = new ArrayList<>();
        List<List<Double>> usedMiscValues = new ArrayList<>();
        List<List<String>> usedMiscUnits = new ArrayList<>();

        usedConditionNames = new LinkedHashMap<>();
        usedConditionCategories = new LinkedHashMap<>();
        addedConditionNames = new LinkedHashMap<>();
        addedConditionCategories = new LinkedHashMap<>();

        while (reader.hasMoreElements()) {
            tuples.add(reader.nextElement());
        }

        boolean tempMissing = true;
        boolean phMissing = true;
        boolean awMissing = true;

        for (KnimeTuple tuple : tuples) {
            for (PmmXmlElementConvertable el : tuple.getPmmXml(TimeSeriesSchema.ATT_MISC).getElementSet()) {
                MiscXml misc = (MiscXml) el;

                if (usedMiscIds.add(misc.id)) {
                    usedMiscs.add(misc);

                    switch (misc.id) {
                    case AttributeUtilities.ATT_TEMPERATURE_ID:
                        tempMissing = false;
                        break;
                    case AttributeUtilities.ATT_PH_ID:
                        phMissing = false;
                        break;
                    case AttributeUtilities.ATT_AW_ID:
                        awMissing = false;
                        break;
                    }
                }
            }
        }

        if (tempMissing) {
            usedMiscs.add(new MiscXml(AttributeUtilities.ATT_TEMPERATURE_ID, AttributeUtilities.ATT_TEMPERATURE,
                    AttributeUtilities.ATT_TEMPERATURE, null, Arrays.asList(Categories.getTempCategory().getName()),
                    Categories.getTempCategory().getStandardUnit()));
        }

        if (phMissing) {
            usedMiscs
                    .add(new MiscXml(AttributeUtilities.ATT_PH_ID, AttributeUtilities.ATT_PH, AttributeUtilities.ATT_PH,
                            null, Arrays.asList(Categories.getPhCategory().getName()), Categories.getPhUnit()));
        }

        if (awMissing) {
            usedMiscs
                    .add(new MiscXml(AttributeUtilities.ATT_AW_ID, AttributeUtilities.ATT_AW, AttributeUtilities.ATT_AW,
                            null, Arrays.asList(Categories.getAwCategory().getName()), Categories.getAwUnit()));
        }

        for (MiscXml misc : usedMiscs) {
            usedConditionNames.put(misc.id, misc.name);
            usedConditionCategories.put(misc.id, misc.categories);
            usedMiscValues.add(new ArrayList<Double>());
            usedMiscUnits.add(new ArrayList<String>());
        }

        for (KnimeTuple tuple : tuples) {
            String combaseID = tuple.getString(TimeSeriesSchema.ATT_COMBASEID);
            int condID = tuple.getInt(TimeSeriesSchema.ATT_CONDID);
            MdInfoXml infoXml = (MdInfoXml) tuple.getPmmXml(TimeSeriesSchema.ATT_MDINFO).get(0);
            PmmXmlDoc miscXml = tuple.getPmmXml(TimeSeriesSchema.ATT_MISC);
            List<TimeSeriesXml> series = new ArrayList<>();
            List<LiteratureItem> ref = new ArrayList<>();

            for (PmmXmlElementConvertable el : tuple.getPmmXml(TimeSeriesSchema.ATT_TIMESERIES).getElementSet()) {
                series.add((TimeSeriesXml) el);
            }

            for (PmmXmlElementConvertable el : tuple.getPmmXml(TimeSeriesSchema.ATT_LITMD).getElementSet()) {
                ref.add((LiteratureItem) el);
            }

            String id;

            if (combaseID != null) {
                id = combaseID + " (" + condID + ")";
            } else {
                id = condID + "";
            }

            ids.add(id);

            if (set.getAgents().containsKey(id)) {
                agentList.add(set.getAgents().get(id));
            } else {
                agentList.add((AgentXml) tuple.getPmmXml(TimeSeriesSchema.ATT_AGENT).get(0));
            }

            if (set.getAgentDetails().containsKey(id)) {
                agentDetailList.add(set.getAgentDetails().get(id));
            } else {
                agentDetailList.add(((AgentXml) tuple.getPmmXml(TimeSeriesSchema.ATT_AGENT).get(0)).detail);
            }

            if (set.getMatrices().containsKey(id)) {
                matrixList.add(set.getMatrices().get(id));
            } else {
                matrixList.add((MatrixXml) tuple.getPmmXml(TimeSeriesSchema.ATT_MATRIX).get(0));
            }

            if (set.getMatrixDetails().containsKey(id)) {
                matrixDetailList.add(set.getMatrixDetails().get(id));
            } else {
                matrixDetailList.add(((MatrixXml) tuple.getPmmXml(TimeSeriesSchema.ATT_MATRIX).get(0)).detail);
            }

            if (set.getComments().containsKey(id)) {
                commentList.add(set.getComments().get(id));
            } else {
                commentList.add(infoXml.comment);
            }

            if (set.getQualityScores().containsKey(id)) {
                qualityScoreList.add(set.getQualityScores().get(id));
            } else {
                qualityScoreList.add(infoXml.qualityScore);
            }

            if (set.getChecks().containsKey(id)) {
                checkedList.add(set.getChecks().get(id));
            } else {
                checkedList.add(infoXml.checked);
            }

            if (set.getTimeSeries().containsKey(id)) {
                timeSeriesList.add(set.getTimeSeries().get(id));
            } else {
                timeSeriesList.add(series);
            }

            if (set.getReferences().containsKey(id)) {
                referenceList.add(set.getReferences().get(id));
            } else {
                referenceList.add(ref);
            }

            for (int i = 0; i < usedMiscs.size(); i++) {
                Double value = null;
                String unit = null;
                int miscID = usedMiscs.get(i).id;

                if (set.getConditions().containsKey(miscID) && set.getConditionValues().get(miscID).containsKey(id)) {
                    value = set.getConditionValues().get(miscID).get(id);
                    unit = set.getConditionUnits().get(miscID).get(id);
                } else {
                    for (PmmXmlElementConvertable el : miscXml.getElementSet()) {
                        MiscXml cond = (MiscXml) el;

                        if (cond.id == usedMiscs.get(i).id) {
                            value = cond.value;
                            unit = cond.unit;
                            break;
                        }
                    }
                }

                usedMiscValues.get(i).add(value);
                usedMiscUnits.get(i).add(unit);
            }
        }

        for (int id : usedConditionNames.keySet()) {
            set.getAddedConditions().remove(id);
            set.getAddedConditionValues().remove(id);
            set.getAddedConditionUnits().remove(id);
        }

        for (int miscID : set.getAddedConditions().keySet()) {
            addedConditionNames.put(miscID, set.getAddedConditions().get(miscID).name);
            addedConditionCategories.put(miscID, set.getAddedConditions().get(miscID).categories);
        }

        addedConditionsList.setListData(addedConditionNames.values().toArray(new String[0]));

        EditTable tableModel = new EditTable(ids, agentList, agentDetailList, matrixList, matrixDetailList, commentList,
                qualityScoreList, checkedList, timeSeriesList, referenceList, usedMiscs, usedMiscValues, usedMiscUnits);

        for (int miscID : set.getAddedConditions().keySet()) {
            tableModel.addCondition(set.getAddedConditions().get(miscID), set.getAddedConditionValues().get(miscID),
                    set.getAddedConditionUnits().get(miscID));
        }

        table.setModel(tableModel);
        setEditorsAndRenderers();
        UI.packColumns(table);
    }

    @Override
    protected void saveSettingsTo(NodeSettingsWO settings) throws InvalidSettingsException {
        EditTable model = (EditTable) table.getModel();
        SettingsHelper set = new SettingsHelper();

        set.setAddedConditions(model.getAddedConditionMap());
        set.setAddedConditionValues(model.getAddedConditionValueMap());
        set.setAddedConditionUnits(model.getAddedConditionUnitMap());
        set.setConditions(model.getConditionMap());
        set.setConditionValues(model.getConditionValueMap());
        set.setConditionUnits(model.getConditionUnitMap());
        set.setAgents(model.getAgentMap());
        set.setAgentDetails(model.getAgentDetailMap());
        set.setMatrices(model.getMatrixMap());
        set.setMatrixDetails(model.getMatrixDetailMap());
        set.setComments(model.getCommentMap());
        set.setQualityScores(model.getQualityScoreMap());
        set.setChecks(model.getCheckedMap());
        set.setTimeSeries(model.getTimeSeriesMap());
        set.setReferences(model.getReferenceMap());
        set.saveSettings(settings);
    }

    private void setEditorsAndRenderers() {
        table.getColumn(TimeSeriesSchema.ATT_AGENT).setCellRenderer(new AgentRenderer());
        table.getColumn(TimeSeriesSchema.ATT_AGENT).setCellEditor(new AgentEditor());
        table.getColumn(TimeSeriesSchema.ATT_MATRIX).setCellRenderer(new MatrixRenderer());
        table.getColumn(TimeSeriesSchema.ATT_MATRIX).setCellEditor(new MatrixEditor());
        table.getColumn("QualityScore").setCellRenderer(new QualityScoreRenderer());
        table.getColumn("QualityScore").setCellEditor(new QualityScoreEditor());
        table.getColumn(TimeSeriesSchema.ATT_TIMESERIES).setCellRenderer(new ButtonRenderer("View"));
        table.getColumn(TimeSeriesSchema.ATT_TIMESERIES).setCellEditor(new TimeSeriesEditor());
        table.getColumn(AttributeUtilities.getName(TimeSeriesSchema.ATT_LITMD))
                .setCellRenderer(new ButtonRenderer("Edit"));
        table.getColumn(AttributeUtilities.getName(TimeSeriesSchema.ATT_LITMD)).setCellEditor(new ReferencesEditor());
        table.setRowHeight((new JComboBox<String>()).getPreferredSize().height);

        for (int id : usedConditionNames.keySet()) {
            List<String> units = getUnits(usedConditionCategories.get(id));

            table.getColumn(usedConditionNames.get(id) + " Unit")
                    .setCellEditor(new DefaultCellEditor(new JComboBox<>(units.toArray(new String[0]))));
        }

        for (int id : addedConditionNames.keySet()) {
            List<String> units = getUnits(addedConditionCategories.get(id));

            table.getColumn(addedConditionNames.get(id) + " Unit")
                    .setCellEditor(new DefaultCellEditor(new JComboBox<>(units.toArray(new String[0]))));
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == addButton) {
            Integer id = DBKernel.openMiscDBWindow(addButton, null);

            if (id != null) {
                String name = DBKernel.getValue("SonstigeParameter", "ID", id + "", "Parameter") + "";
                String description = DBKernel.getValue("SonstigeParameter", "ID", id + "", "Beschreibung") + "";
                List<String> categories = Arrays.asList(
                        DBKernel.getValue("SonstigeParameter", "ID", id + "", "Kategorie").toString().split(","));

                if (!usedConditionNames.keySet().contains(id) && !addedConditionNames.keySet().contains(id)) {
                    addedConditionNames.put(id, name);
                    addedConditionCategories.put(id, categories);
                    addedConditionsList.setListData(addedConditionNames.values().toArray(new String[0]));
                    ((EditTable) table.getModel()).addCondition(
                            new MiscXml(id, name, description, null, categories, null, DBKernel.getLocalDBUUID()),
                            new LinkedHashMap<String, Double>(), new LinkedHashMap<String, String>());
                    setEditorsAndRenderers();
                    UI.packColumns(table);
                    table.repaint();
                } else {
                    JOptionPane.showMessageDialog(addButton, "Data already contains " + name, "Error",
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        } else if (e.getSource() == removeButton) {
            Set<Integer> removedIDs = new LinkedHashSet<>();

            for (String nameToRemove : addedConditionsList.getSelectedValuesList()) {
                for (int id : addedConditionNames.keySet()) {
                    String name = addedConditionNames.get(id);

                    if (name.equals(nameToRemove)) {
                        removedIDs.add(id);
                        break;
                    }
                }
            }

            for (int id : removedIDs) {
                addedConditionNames.remove(id);
                ((EditTable) table.getModel()).removeCondition(id);
            }

            addedConditionsList.setListData(addedConditionNames.values().toArray(new String[0]));
            setEditorsAndRenderers();
            UI.packColumns(table);
            table.repaint();
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3 && e.getSource() == table.getTableHeader()) {
            int index = table.convertColumnIndexToModel(table.columnAtPoint(e.getPoint()));
            String column = table.getModel().getColumnName(index);
            Integer condId = getKey(usedConditionNames, column) != null ? getKey(usedConditionNames, column)
                    : getKey(addedConditionNames, column);

            if (condId != null) {
                Object result = JOptionPane.showInputDialog(table.getTableHeader(), "Set All Values to?", column,
                        JOptionPane.QUESTION_MESSAGE, null, null, 0.0);
                Double value = null;

                try {
                    value = Double.parseDouble(result.toString());
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(table.getTableHeader(), "Invalid Input!", "Error",
                            JOptionPane.ERROR_MESSAGE);
                } catch (NullPointerException ex) {
                }

                if (value != null) {
                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(value, i, index);
                    }
                }
            } else if (column.endsWith(" Unit")) {
                String c = column.replace(" Unit", "");
                List<String> units = null;

                if (getKey(usedConditionNames, c) != null) {
                    units = getUnits(usedConditionCategories.get(getKey(usedConditionNames, c)));
                } else if (getKey(addedConditionNames, c) != null) {
                    units = getUnits(addedConditionCategories.get(getKey(addedConditionNames, c)));
                }

                String unit = (String) JOptionPane.showInputDialog(table.getTableHeader(), "Set All Values to?", column,
                        JOptionPane.QUESTION_MESSAGE, null, units.toArray(), units.get(0));

                if (unit != null) {
                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(unit, i, index);
                    }
                }
            } else if (column.equals(TimeSeriesSchema.ATT_AGENT)) {
                Integer id = DBKernel.openAgentDBWindow(table.getTableHeader(), null);

                if (id != null) {
                    String name = DBKernel.getValue("Agenzien", "ID", id + "", "Agensname") + "";
                    AgentXml agent = new AgentXml(id, name, null, DBKernel.getLocalDBUUID());

                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(agent, i, index);
                    }
                }
            } else if (column.equals(TimeSeriesSchema.ATT_MATRIX)) {
                Integer id = DBKernel.openMatrixDBWindow(table.getTableHeader(), null);

                if (id != null) {
                    String name = DBKernel.getValue("Matrices", "ID", id + "", "Matrixname") + "";
                    MatrixXml matrix = new MatrixXml(id, name, null, DBKernel.getLocalDBUUID());

                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(matrix, i, index);
                    }
                }
            } else if (column.equals("Comment") || column.equals(AttributeUtilities.AGENT_DETAILS)
                    || column.equals(AttributeUtilities.MATRIX_DETAILS)) {
                String value = (String) JOptionPane.showInputDialog(table.getTableHeader(), "Set All Values to?",
                        column, JOptionPane.QUESTION_MESSAGE, null, null, "");

                if (value != null) {
                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(value, i, index);
                    }
                }
            } else if (column.equals("Checked")) {
                Boolean value = (Boolean) JOptionPane.showInputDialog(table.getTableHeader(), "Set All Values to?",
                        column, JOptionPane.QUESTION_MESSAGE, null, new Object[] { Boolean.TRUE, Boolean.FALSE },
                        Boolean.TRUE);

                if (value != null) {
                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(value, i, index);
                    }
                }
            } else if (column.equals("QualityScore")) {
                QualityScoreDialog dialog = new QualityScoreDialog(table.getTableHeader());

                dialog.setVisible(true);

                if (dialog.isApproved()) {
                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(dialog.getScore(), i, index);
                    }
                }
            } else if (column.equals(AttributeUtilities.getName(TimeSeriesSchema.ATT_LITMD))) {
                ReferencesDialog dialog = new ReferencesDialog(table.getTableHeader(), new ArrayList<LiteratureItem>());

                dialog.setVisible(true);

                if (dialog.isApproved()) {
                    for (int i = 0; i < table.getModel().getRowCount(); i++) {
                        table.getModel().setValueAt(dialog.getRef(), i, index);
                    }
                }
            }
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    private static Integer getKey(Map<Integer, String> map, String value) {
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            if (entry.getValue().equals(value)) {
                return entry.getKey();
            }
        }

        return null;
    }

    private static List<String> getUnits(List<String> categories) {
        List<String> units = new ArrayList<>();

        for (String cat : categories) {
            units.addAll(Categories.getCategory(cat).getAllUnits());
        }

        return units;
    }

    private static class EditTable extends AbstractTableModel {

        private static final long serialVersionUID = 1L;

        private List<String> ids;
        private List<AgentXml> agents;
        private List<String> agentDetails;
        private List<MatrixXml> matrices;
        private List<String> matrixDetails;
        private List<String> comments;
        private List<Integer> qualityScores;
        private List<Boolean> checks;
        private List<List<TimeSeriesXml>> timeSeries;
        private List<List<LiteratureItem>> references;
        private List<MiscXml> conditions;
        private List<List<Double>> conditionValues;
        private List<List<String>> conditionUnits;
        private List<MiscXml> addedConditions;
        private List<List<Double>> addedConditionValues;
        private List<List<String>> addedConditionUnits;

        public EditTable(List<String> ids, List<AgentXml> agents, List<String> agentDetails, List<MatrixXml> matrices,
                List<String> matrixDetails, List<String> comments, List<Integer> qualityScores, List<Boolean> checks,
                List<List<TimeSeriesXml>> timeSeries, List<List<LiteratureItem>> references, List<MiscXml> conditions,
                List<List<Double>> conditionValues, List<List<String>> conditionUnits) {
            this.ids = ids;
            this.agents = agents;
            this.agentDetails = agentDetails;
            this.matrices = matrices;
            this.matrixDetails = matrixDetails;
            this.comments = comments;
            this.qualityScores = qualityScores;
            this.checks = checks;
            this.timeSeries = timeSeries;
            this.references = references;
            this.conditions = conditions;
            this.conditionValues = conditionValues;
            this.conditionUnits = conditionUnits;
            addedConditions = new ArrayList<>();
            addedConditionValues = new ArrayList<>();
            addedConditionUnits = new ArrayList<>();
        }

        public void addCondition(MiscXml condition, Map<String, Double> values, Map<String, String> units) {
            List<Double> valueList = new ArrayList<>();
            List<String> unitList = new ArrayList<>();
            String standardUnit = null;

            if (!condition.categories.isEmpty()) {
                standardUnit = Categories.getCategory(condition.categories.get(0)).getStandardUnit();
            }

            for (String id : ids) {
                valueList.add(values.get(id));

                if (units.containsKey(id)) {
                    unitList.add(units.get(id));
                } else {
                    unitList.add(standardUnit);
                }
            }

            addedConditions.add(condition);
            addedConditionValues.add(valueList);
            addedConditionUnits.add(unitList);
            fireTableStructureChanged();
        }

        public void removeCondition(int id) {
            for (int i = 0; i < addedConditions.size(); i++) {
                if (addedConditions.get(i).id == id) {
                    addedConditions.remove(i);
                    addedConditionValues.remove(i);
                    addedConditionUnits.remove(i);
                    break;
                }
            }

            fireTableStructureChanged();
        }

        public Map<Integer, MiscXml> getAddedConditionMap() {
            Map<Integer, MiscXml> map = new LinkedHashMap<>();

            for (MiscXml cond : addedConditions) {
                map.put(cond.id, cond);
            }

            return map;
        }

        public Map<Integer, Map<String, Double>> getAddedConditionValueMap() {
            Map<Integer, Map<String, Double>> valueMap = new LinkedHashMap<>();

            for (int i = 0; i < addedConditions.size(); i++) {
                valueMap.put(addedConditions.get(i).id, new LinkedHashMap<String, Double>());

                for (int j = 0; j < ids.size(); j++) {
                    valueMap.get(addedConditions.get(i).id).put(ids.get(j), addedConditionValues.get(i).get(j));
                }
            }

            return valueMap;
        }

        public Map<Integer, Map<String, String>> getAddedConditionUnitMap() {
            Map<Integer, Map<String, String>> unitMap = new LinkedHashMap<>();

            for (int i = 0; i < addedConditions.size(); i++) {
                unitMap.put(addedConditions.get(i).id, new LinkedHashMap<String, String>());

                for (int j = 0; j < ids.size(); j++) {
                    unitMap.get(addedConditions.get(i).id).put(ids.get(j), addedConditionUnits.get(i).get(j));
                }
            }

            return unitMap;
        }

        public Map<Integer, MiscXml> getConditionMap() {
            Map<Integer, MiscXml> map = new LinkedHashMap<>();

            for (MiscXml cond : conditions) {
                map.put(cond.id, cond);
            }

            return map;
        }

        public Map<Integer, Map<String, Double>> getConditionValueMap() {
            Map<Integer, Map<String, Double>> valueMap = new LinkedHashMap<>();

            for (int i = 0; i < conditions.size(); i++) {
                valueMap.put(conditions.get(i).id, new LinkedHashMap<String, Double>());

                for (int j = 0; j < ids.size(); j++) {
                    valueMap.get(conditions.get(i).id).put(ids.get(j), conditionValues.get(i).get(j));
                }
            }

            return valueMap;
        }

        public Map<Integer, Map<String, String>> getConditionUnitMap() {
            Map<Integer, Map<String, String>> unitMap = new LinkedHashMap<>();

            for (int i = 0; i < conditions.size(); i++) {
                unitMap.put(conditions.get(i).id, new LinkedHashMap<String, String>());

                for (int j = 0; j < ids.size(); j++) {
                    unitMap.get(conditions.get(i).id).put(ids.get(j), conditionUnits.get(i).get(j));
                }
            }

            return unitMap;
        }

        public Map<String, AgentXml> getAgentMap() {
            Map<String, AgentXml> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), agents.get(i));
            }

            return map;
        }

        public Map<String, String> getAgentDetailMap() {
            Map<String, String> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), agentDetails.get(i));
            }

            return map;
        }

        public Map<String, MatrixXml> getMatrixMap() {
            Map<String, MatrixXml> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), matrices.get(i));
            }

            return map;
        }

        public Map<String, String> getMatrixDetailMap() {
            Map<String, String> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), matrixDetails.get(i));
            }

            return map;
        }

        public Map<String, String> getCommentMap() {
            Map<String, String> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), comments.get(i));
            }

            return map;
        }

        public Map<String, Integer> getQualityScoreMap() {
            Map<String, Integer> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), qualityScores.get(i));
            }

            return map;
        }

        public Map<String, Boolean> getCheckedMap() {
            Map<String, Boolean> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), checks.get(i));
            }

            return map;
        }

        public Map<String, List<TimeSeriesXml>> getTimeSeriesMap() {
            Map<String, List<TimeSeriesXml>> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), timeSeries.get(i));
            }

            return map;
        }

        public Map<String, List<LiteratureItem>> getReferenceMap() {
            Map<String, List<LiteratureItem>> map = new LinkedHashMap<>();

            for (int i = 0; i < getRowCount(); i++) {
                map.put(ids.get(i), references.get(i));
            }

            return map;
        }

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

        @Override
        public int getColumnCount() {
            return addedConditions.size() * 2 + conditions.size() * 2 + 10;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            switch (columnIndex) {
            case 0:
                return ids.get(rowIndex);
            case 1:
                return agents.get(rowIndex);
            case 2:
                return agentDetails.get(rowIndex);
            case 3:
                return matrices.get(rowIndex);
            case 4:
                return matrixDetails.get(rowIndex);
            case 5:
                return comments.get(rowIndex);
            case 6:
                return qualityScores.get(rowIndex);
            case 7:
                return checks.get(rowIndex);
            case 8:
                return timeSeries.get(rowIndex);
            case 9:
                return references.get(rowIndex);
            default:
                int i = columnIndex - 10;

                if (i < conditions.size() * 2) {
                    if (i % 2 == 0) {
                        return conditionValues.get(i / 2).get(rowIndex);
                    } else {
                        return conditionUnits.get(i / 2).get(rowIndex);
                    }
                } else {
                    i -= conditions.size() * 2;

                    if (i % 2 == 0) {
                        return addedConditionValues.get(i / 2).get(rowIndex);
                    } else {
                        return addedConditionUnits.get(i / 2).get(rowIndex);
                    }
                }
            }
        }

        @Override
        public String getColumnName(int column) {
            switch (column) {
            case 0:
                return "ID";
            case 1:
                return TimeSeriesSchema.ATT_AGENT;
            case 2:
                return AttributeUtilities.AGENT_DETAILS;
            case 3:
                return TimeSeriesSchema.ATT_MATRIX;
            case 4:
                return AttributeUtilities.MATRIX_DETAILS;
            case 5:
                return "Comment";
            case 6:
                return "QualityScore";
            case 7:
                return "Checked";
            case 8:
                return TimeSeriesSchema.ATT_TIMESERIES;
            case 9:
                return AttributeUtilities.getName(TimeSeriesSchema.ATT_LITMD);
            default:
                int i = column - 10;

                if (i < conditions.size() * 2) {
                    String cond = conditions.get(i / 2).name;

                    if (i % 2 == 0) {
                        return cond;
                    } else {
                        return cond + " Unit";
                    }
                } else {
                    i -= conditions.size() * 2;

                    String cond = addedConditions.get(i / 2).name;

                    if (i % 2 == 0) {
                        return cond;
                    } else {
                        return cond + " Unit";
                    }
                }
            }
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            switch (columnIndex) {
            case 0:
                return String.class;
            case 1:
                return AgentXml.class;
            case 2:
                return String.class;
            case 3:
                return MatrixXml.class;
            case 4:
                return String.class;
            case 5:
                return String.class;
            case 6:
                return Integer.class;
            case 7:
                return Boolean.class;
            case 8:
                return List.class;
            case 9:
                return List.class;
            default:
                int i = columnIndex - 10;

                if (i % 2 == 0) {
                    return Double.class;
                } else {
                    return String.class;
                }
            }
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columnIndex != 0;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            switch (columnIndex) {
            case 0:
                break;
            case 1:
                agents.set(rowIndex, (AgentXml) aValue);
                break;
            case 2:
                agentDetails.set(rowIndex, (String) aValue);
                break;
            case 3:
                matrices.set(rowIndex, (MatrixXml) aValue);
                break;
            case 4:
                matrixDetails.set(rowIndex, (String) aValue);
                break;
            case 5:
                comments.set(rowIndex, (String) aValue);
                break;
            case 6:
                qualityScores.set(rowIndex, (Integer) aValue);
                break;
            case 7:
                checks.set(rowIndex, (Boolean) aValue);
                break;
            case 8:
                timeSeries.set(rowIndex, (List<TimeSeriesXml>) aValue);
                break;
            case 9:
                references.set(rowIndex, (List<LiteratureItem>) aValue);
                break;
            default:
                int i = columnIndex - 10;

                if (i < conditions.size() * 2) {
                    if (i % 2 == 0) {
                        conditionValues.get(i / 2).set(rowIndex, (Double) aValue);
                    } else {
                        conditionUnits.get(i / 2).set(rowIndex, (String) aValue);
                    }
                } else {
                    i -= conditions.size() * 2;

                    if (i % 2 == 0) {
                        addedConditionValues.get(i / 2).set(rowIndex, (Double) aValue);
                    } else {
                        addedConditionUnits.get(i / 2).set(rowIndex, (String) aValue);
                    }
                }
            }

            fireTableDataChanged();
        }

    }

    private static class AgentRenderer implements TableCellRenderer {

        private JLabel label;

        public AgentRenderer() {
            label = new JLabel();
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            AgentXml agent = (AgentXml) value;

            if (agent != null) {
                label.setText(agent.name);
            } else {
                label.setText("");
            }

            return label;
        }
    }

    private static class AgentEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {

        private static final long serialVersionUID = 1L;

        private JButton button;
        private AgentXml agent;

        public AgentEditor() {
            button = new JButton();
            button.addActionListener(this);
            agent = null;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
                int column) {
            agent = (AgentXml) value;

            return button;
        }

        @Override
        public Object getCellEditorValue() {
            return agent;
        }

        @Override
        public void actionPerformed(ActionEvent arg0) {
            Integer id;

            if (agent != null) {
                id = DBKernel.openAgentDBWindow(button, agent.id);
            } else {
                id = DBKernel.openAgentDBWindow(button, null);
            }

            if (id != null) {
                String name = DBKernel.getValue("Agenzien", "ID", id + "", "Agensname") + "";

                agent = new AgentXml(id, name, null, DBKernel.getLocalDBUUID());
                stopCellEditing();
            }
        }
    }

    private static class MatrixRenderer implements TableCellRenderer {

        private JLabel label;

        public MatrixRenderer() {
            label = new JLabel();
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            MatrixXml matrix = (MatrixXml) value;

            if (matrix != null) {
                label.setText(matrix.name);
            } else {
                label.setText("");
            }

            return label;
        }
    }

    private static class MatrixEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {

        private static final long serialVersionUID = 1L;

        private JButton button;
        private MatrixXml matrix;

        public MatrixEditor() {
            button = new JButton();
            button.addActionListener(this);
            matrix = null;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
                int column) {
            matrix = (MatrixXml) value;

            return button;
        }

        @Override
        public Object getCellEditorValue() {
            return matrix;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Integer id;

            if (matrix != null) {
                id = DBKernel.openMatrixDBWindow(button, matrix.id);
            } else {
                id = DBKernel.openMatrixDBWindow(button, null);
            }

            if (id != null) {
                String name = DBKernel.getValue("Matrices", "ID", id + "", "Matrixname") + "";

                matrix = new MatrixXml(id, name, null, DBKernel.getLocalDBUUID());
                stopCellEditing();
            }
        }
    }

    private static class QualityScoreRenderer extends JComponent implements TableCellRenderer {

        private static final long serialVersionUID = 1L;

        private Color color;

        public QualityScoreRenderer() {
            color = Color.WHITE;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            Integer score = (Integer) value;

            if (score == null) {
                color = Color.WHITE;
            } else if (score == 1) {
                color = Color.GREEN;
            } else if (score == 2) {
                color = Color.YELLOW;
            } else if (score == 3) {
                color = Color.RED;
            }

            return this;
        }

        @Override
        protected void paintComponent(Graphics g) {
            Rectangle rect = g.getClipBounds();

            if (rect != null) {
                g.setColor(color);
                g.fillRect(rect.x, rect.y, rect.width, rect.height);
            }
        }
    }

    private static class QualityScoreEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {

        private static final long serialVersionUID = 1L;

        private Integer score;

        public QualityScoreEditor() {
            score = null;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
                int column) {
            QualityComboBox box = new QualityComboBox();

            box.setQuality((Integer) value);
            box.addActionListener(this);

            return box;
        }

        @Override
        public Object getCellEditorValue() {
            return score;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            score = ((QualityComboBox) e.getSource()).getQuality();
            stopCellEditing();
        }
    }

    private static class ButtonRenderer implements TableCellRenderer {

        private String name;

        public ButtonRenderer(String name) {
            this.name = name;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            return new JButton(name);
        }
    }

    private static class TimeSeriesEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {

        private static final long serialVersionUID = 1L;

        private JButton button;
        private List<TimeSeriesXml> timeSeries;

        public TimeSeriesEditor() {
            button = new JButton("View");
            button.addActionListener(this);
            timeSeries = new ArrayList<>();
        }

        @SuppressWarnings("unchecked")
        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
                int column) {
            timeSeries = (List<TimeSeriesXml>) value;

            return button;
        }

        @Override
        public Object getCellEditorValue() {
            return timeSeries;
        }

        @Override
        public void actionPerformed(ActionEvent arg0) {
            TimeSeriesDialog dialog = new TimeSeriesDialog(button, timeSeries, true);

            dialog.setVisible(true);
        }
    }

    private static class ReferencesEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {

        private static final long serialVersionUID = 1L;

        private JButton button;
        private List<LiteratureItem> references;

        public ReferencesEditor() {
            button = new JButton("Edit");
            button.addActionListener(this);
            references = new ArrayList<>();
        }

        @SuppressWarnings("unchecked")
        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
                int column) {
            references = (List<LiteratureItem>) value;

            return button;
        }

        @Override
        public Object getCellEditorValue() {
            return references;
        }

        @Override
        public void actionPerformed(ActionEvent arg0) {
            ReferencesDialog dialog = new ReferencesDialog(button, references);

            dialog.setVisible(true);

            if (dialog.isApproved()) {
                references = dialog.getRef();
                stopCellEditing();
            }
        }
    }

    private static class ReferencesDialog extends JDialog implements ActionListener {

        private static final long serialVersionUID = 1L;

        private JButton addLiteratureButton;
        private JButton removeLiteratureButton;
        private JList<LiteratureItem> literatureList;

        private JButton okButton;
        private JButton cancelButton;

        private boolean approved;
        private List<LiteratureItem> ref;

        public ReferencesDialog(Component owner, List<LiteratureItem> initRef) {
            super(JOptionPane.getFrameForComponent(owner), AttributeUtilities.getName(TimeSeriesSchema.ATT_LITMD),
                    true);

            approved = false;
            ref = new ArrayList<>(initRef);

            addLiteratureButton = new JButton("Add");
            addLiteratureButton.addActionListener(this);
            removeLiteratureButton = new JButton("Remove");
            removeLiteratureButton.addActionListener(this);
            literatureList = new JList<>();
            literatureList.setListData(ref.toArray(new LiteratureItem[0]));
            okButton = new JButton("OK");
            okButton.addActionListener(this);
            cancelButton = new JButton("Cancel");
            cancelButton.addActionListener(this);

            JPanel northPanel = new JPanel();

            northPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
            northPanel.add(addLiteratureButton);
            northPanel.add(removeLiteratureButton);

            JPanel panel = new JPanel();

            panel.setBorder(BorderFactory.createTitledBorder("Literature"));
            panel.setLayout(new BorderLayout());
            panel.add(northPanel, BorderLayout.NORTH);
            panel.add(new JScrollPane(literatureList), BorderLayout.CENTER);

            JPanel southPanel = new JPanel();

            southPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
            southPanel.add(okButton);
            southPanel.add(cancelButton);

            setLayout(new BorderLayout(5, 5));
            add(northPanel, BorderLayout.NORTH);
            add(panel, BorderLayout.CENTER);
            add(southPanel, BorderLayout.SOUTH);
            pack();
            setResizable(false);
            setLocationRelativeTo(owner);
        }

        public boolean isApproved() {
            return approved;
        }

        public List<LiteratureItem> getRef() {
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == okButton) {
                approved = true;
                dispose();
            } else if (e.getSource() == cancelButton) {
                approved = false;
                dispose();
            } else if (e.getSource() == addLiteratureButton) {
                Integer id = DBKernel.openLiteratureDBWindow(addLiteratureButton, null);
                Set<Integer> ids = new LinkedHashSet<>();

                for (LiteratureItem item : ref) {
                    ids.add(item.id);
                }

                if (id != null && !ids.contains(id)) {
                    LiteratureItem l = DBUtilities.getLiteratureItem(id);

                    ref.add(l);
                    literatureList.setListData(ref.toArray(new LiteratureItem[0]));
                }
            } else if (e.getSource() == removeLiteratureButton) {
                if (literatureList.getSelectedIndices().length > 0) {
                    int[] indices = literatureList.getSelectedIndices();

                    Arrays.sort(indices);

                    for (int i = indices.length - 1; i >= 0; i--) {
                        ref.remove(indices[i]);
                    }

                    literatureList.setListData(ref.toArray(new LiteratureItem[0]));
                }
            }
        }
    }

    private static class QualityScoreDialog extends JDialog implements ActionListener {

        private static final long serialVersionUID = 1L;

        private QualityComboBox box;
        private JButton okButton;
        private JButton cancelButton;

        private boolean approved;
        private Integer score;

        public QualityScoreDialog(Component owner) {
            super(JOptionPane.getFrameForComponent(owner), "QualityScore", true);
            box = new QualityComboBox();
            okButton = new JButton("OK");
            okButton.addActionListener(this);
            cancelButton = new JButton("Cancel");
            cancelButton.addActionListener(this);

            JPanel panel = new JPanel();

            panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            panel.setLayout(new BorderLayout(5, 5));
            panel.add(new JLabel("Set All Values to?"), BorderLayout.NORTH);
            panel.add(box, BorderLayout.CENTER);

            JPanel buttonPanel = new JPanel();

            buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
            buttonPanel.add(okButton);
            buttonPanel.add(cancelButton);

            setLayout(new BorderLayout(5, 5));
            add(panel, BorderLayout.CENTER);
            add(buttonPanel, BorderLayout.SOUTH);
            pack();
            setResizable(false);
            setLocationRelativeTo(owner);

            approved = false;
            score = null;
        }

        public boolean isApproved() {
            return approved;
        }

        public Integer getScore() {
            return score;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == okButton) {
                approved = true;
                score = box.getQuality();
                dispose();
            } else if (e.getSource() == cancelButton) {
                approved = false;
                score = null;
                dispose();
            }
        }
    }
}