net.sf.taverna.t2.activities.spreadsheet.views.SpreadsheetImportConfigView.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.taverna.t2.activities.spreadsheet.views.SpreadsheetImportConfigView.java

Source

/*******************************************************************************
 * Copyright (C) 2009 The University of Manchester
 *
 *  Modifications to the initial code base are copyright of their
 *  respective authors, or their employers as appropriate.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 ******************************************************************************/
package net.sf.taverna.t2.activities.spreadsheet.views;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;

import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;

import net.sf.taverna.t2.activities.spreadsheet.Range;
import net.sf.taverna.t2.activities.spreadsheet.SpreadsheetUtils;
import net.sf.taverna.t2.activities.spreadsheet.il8n.SpreadsheetImportUIText;
import net.sf.taverna.t2.lang.ui.DialogTextArea;
import net.sf.taverna.t2.lang.ui.icons.Icons;
import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ActivityConfigurationPanel;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;

import uk.org.taverna.commons.services.ServiceRegistry;
import uk.org.taverna.scufl2.api.activity.Activity;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * Configuration panel for the spreadsheet import activity.
 *
 * @author David Withers
 */
@SuppressWarnings("serial")
public class SpreadsheetImportConfigView extends ActivityConfigurationPanel {

    private static final String INCONSISTENT_ROW_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.INCONSISTENT_ROW_MESSAGE");

    private static final String INCONSISTENT_COLUMN_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.INCONSISTENT_COLUMN_MESSAGE");

    private static final String FROM_COLUMN_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.FROM_COLUMN_ERROR_MESSAGE");

    private static final String TO_COLUMN_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.TO_COLUMN_ERROR_MESSAGE");

    private static final String FROM_ROW_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.FROM_ROW_ERROR_MESSAGE");

    private static final String TO_ROW_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.TO_ROW_ERROR_MESSAGE");

    private static final String DEFAULT_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.DEFAULT_MESSAGE");

    private static final String EMPTY_FROM_ROW_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.EMPTY_FROM_ROW_ERROR_MESSAGE");

    private static final String EMPTY_FROM_COLUMN_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.EMPTY_FROM_COLUMN_ERROR_MESSAGE");

    private static final String EMPTY_TO_COLUMN_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.EMPTY_TO_COLUMN_ERROR_MESSAGE");

    private static final String DUPLICATE_PORT_NAME_ERROR_MESSAGE = SpreadsheetImportUIText
            .getString("SpreadsheetImportConfigView.DUPLICATE_PORT_NAME_ERROR_MESSAGE");

    private static Logger logger = Logger.getLogger(SpreadsheetImportConfigView.class);

    private JPanel titlePanel, contentPanel, buttonPanel, page1, page2;

    private JLabel titleLabel, titleIcon, rowLabel, columnLabel;

    private JLabel emptyCellLabel, outputFormatLabel, outputFormatDelimiterLabel, columnMappingLabel;

    private DialogTextArea titleMessage;

    private JTextField columnFromValue, columnToValue, rowFromValue, rowToValue;

    private JTextField emptyCellUserDefinedValue, outputFormatDelimiter;

    private JCheckBox rowSelectAllOption, rowExcludeFirstOption, rowIgnoreBlankRows;

    private ButtonGroup emptyCellButtonGroup, outputFormatButtonGroup;

    private JRadioButton emptyCellEmptyStringOption, emptyCellUserDefinedOption, emptyCellErrorValueOption;

    private JRadioButton outputFormatMultiplePort, outputFormatSinglePort;

    private JTable columnMappingTable;

    private SpreadsheetImportConfigTableModel columnMappingTableModel;

    private JButton nextButton, backButton;

    private CardLayout cardLayout = new CardLayout();

    private Stack<String> warningMessages = new Stack<String>();

    private Stack<String> errorMessages = new Stack<String>();

    private ObjectNode newConfiguration;

    private final ServiceRegistry serviceRegistry;

    /**
     * Constructs a configuration view for an SpreadsheetImport Activity.
     *
     * @param activity
     */
    public SpreadsheetImportConfigView(Activity activity, ServiceRegistry serviceRegistry) {
        super(activity);
        this.serviceRegistry = serviceRegistry;
        initialise();
    }

    @Override
    protected void initialise() {
        super.initialise();
        newConfiguration = getJson().deepCopy();

        // title
        titlePanel = new JPanel(new BorderLayout());
        titlePanel.setBackground(Color.WHITE);
        addDivider(titlePanel, SwingConstants.BOTTOM, true);

        titleLabel = new JLabel(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.panelTitle"));
        titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 13.5f));
        titleIcon = new JLabel("");
        titleMessage = new DialogTextArea(DEFAULT_MESSAGE);
        titleMessage.setMargin(new Insets(5, 10, 10, 10));
        // titleMessage.setMinimumSize(new Dimension(0, 30));
        titleMessage.setFont(titleMessage.getFont().deriveFont(11f));
        titleMessage.setEditable(false);
        titleMessage.setFocusable(false);
        // titleMessage.setFont(titleLabel.getFont().deriveFont(Font.PLAIN,
        // 12f));

        // column range
        columnLabel = new JLabel(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.columnSectionLabel"));

        JsonNode columnRange = newConfiguration.get("columnRange");
        columnFromValue = new JTextField(new UpperCaseDocument(),
                SpreadsheetUtils.getColumnLabel(columnRange.get("start").intValue()), 4);
        columnFromValue.setMinimumSize(columnFromValue.getPreferredSize());
        columnToValue = new JTextField(new UpperCaseDocument(),
                SpreadsheetUtils.getColumnLabel(columnRange.get("end").intValue()), 4);
        columnToValue.setMinimumSize(columnToValue.getPreferredSize());

        columnFromValue.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
            }

            public void insertUpdate(DocumentEvent e) {
                checkValue(columnFromValue.getText());
            }

            public void removeUpdate(DocumentEvent e) {
                checkValue(columnFromValue.getText());
            }

            private void checkValue(String text) {
                if (text.trim().equals("")) {
                    addErrorMessage(EMPTY_FROM_COLUMN_ERROR_MESSAGE);
                } else if (text.trim().matches("[A-Za-z]+")) {
                    String fromColumn = columnFromValue.getText().toUpperCase();
                    String toColumn = columnToValue.getText().toUpperCase();
                    int fromColumnIndex = SpreadsheetUtils.getColumnIndex(fromColumn);
                    int toColumnIndex = SpreadsheetUtils.getColumnIndex(toColumn);
                    if (checkColumnRange(fromColumnIndex, toColumnIndex)) {
                        columnMappingTableModel.setFromColumn(fromColumnIndex);
                        columnMappingTableModel.setToColumn(toColumnIndex);
                        newConfiguration.set("columnRange", newConfiguration.objectNode()
                                .put("start", fromColumnIndex).put("end", toColumnIndex));
                        validatePortNames();
                    }
                    removeErrorMessage(FROM_COLUMN_ERROR_MESSAGE);
                    removeErrorMessage(EMPTY_FROM_COLUMN_ERROR_MESSAGE);
                } else {
                    addErrorMessage(FROM_COLUMN_ERROR_MESSAGE);
                    removeErrorMessage(EMPTY_FROM_COLUMN_ERROR_MESSAGE);
                }
            }

        });

        columnToValue.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
            }

            public void insertUpdate(DocumentEvent e) {
                checkValue(columnToValue.getText());
            }

            public void removeUpdate(DocumentEvent e) {
                checkValue(columnToValue.getText());
            }

            private void checkValue(String text) {
                if (text.trim().equals("")) {
                    addErrorMessage(EMPTY_TO_COLUMN_ERROR_MESSAGE);
                } else if (text.trim().matches("[A-Za-z]+")) {
                    String fromColumn = columnFromValue.getText().toUpperCase();
                    String toColumn = columnToValue.getText().toUpperCase();
                    int fromColumnIndex = SpreadsheetUtils.getColumnIndex(fromColumn);
                    int toColumnIndex = SpreadsheetUtils.getColumnIndex(toColumn);
                    if (checkColumnRange(fromColumnIndex, toColumnIndex)) {
                        columnMappingTableModel.setFromColumn(fromColumnIndex);
                        columnMappingTableModel.setToColumn(toColumnIndex);
                        newConfiguration.set("columnRange", newConfiguration.objectNode()
                                .put("start", fromColumnIndex).put("end", toColumnIndex));
                        validatePortNames();
                    }
                    removeErrorMessage(TO_COLUMN_ERROR_MESSAGE);
                    removeErrorMessage(EMPTY_TO_COLUMN_ERROR_MESSAGE);

                } else {
                    addErrorMessage(TO_COLUMN_ERROR_MESSAGE);
                    removeErrorMessage(EMPTY_TO_COLUMN_ERROR_MESSAGE);
                }
            }
        });

        // row range
        rowLabel = new JLabel(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.rowSectionLabel"));
        addDivider(rowLabel, SwingConstants.TOP, false);

        rowSelectAllOption = new JCheckBox(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.selectAllRowsOption"));
        rowExcludeFirstOption = new JCheckBox(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.excludeHeaderRowOption"));
        rowIgnoreBlankRows = new JCheckBox(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.ignoreBlankRowsOption"));
        rowSelectAllOption.setFocusable(false);
        rowExcludeFirstOption.setFocusable(false);

        JsonNode rowRange = newConfiguration.get("rowRange");
        rowFromValue = new JTextField(new NumericDocument(), String.valueOf(rowRange.get("start").intValue() + 1),
                4);
        if (rowRange.get("end").intValue() == -1) {
            rowToValue = new JTextField(new NumericDocument(), "", 4);
        } else {
            rowToValue = new JTextField(new NumericDocument(), String.valueOf(rowRange.get("end").intValue() + 1),
                    4);
        }
        rowFromValue.setMinimumSize(rowFromValue.getPreferredSize());
        rowToValue.setMinimumSize(rowToValue.getPreferredSize());

        if (newConfiguration.get("allRows").booleanValue()) {
            rowSelectAllOption.setSelected(true);
            rowFromValue.setEditable(false);
            rowFromValue.setEnabled(false);
            rowToValue.setEditable(false);
            rowToValue.setEnabled(false);
        } else {
            rowExcludeFirstOption.setEnabled(false);
        }
        rowExcludeFirstOption.setSelected(newConfiguration.get("excludeFirstRow").booleanValue());
        rowIgnoreBlankRows.setSelected(newConfiguration.get("ignoreBlankRows").booleanValue());

        rowFromValue.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
            }

            public void insertUpdate(DocumentEvent e) {
                checkValue(rowFromValue.getText());
            }

            public void removeUpdate(DocumentEvent e) {
                checkValue(rowFromValue.getText());
            }

            private void checkValue(String text) {
                if (text.trim().equals("")) {
                    addErrorMessage(EMPTY_FROM_ROW_ERROR_MESSAGE);
                } else if (text.trim().matches("[1-9][0-9]*")) {
                    checkRowRange(rowFromValue.getText(), rowToValue.getText());
                    int fromRow = Integer.parseInt(rowFromValue.getText());
                    ((ObjectNode) newConfiguration.get("rowRange")).put("start", fromRow - 1);
                    removeErrorMessage(FROM_ROW_ERROR_MESSAGE);
                    removeErrorMessage(EMPTY_FROM_ROW_ERROR_MESSAGE);
                } else {
                    addErrorMessage(FROM_ROW_ERROR_MESSAGE);
                    removeErrorMessage(EMPTY_FROM_ROW_ERROR_MESSAGE);
                }
            }
        });

        rowToValue.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
            }

            public void insertUpdate(DocumentEvent e) {
                checkValue(rowToValue.getText());
            }

            public void removeUpdate(DocumentEvent e) {
                checkValue(rowToValue.getText());
            }

            private void checkValue(String text) {
                if (text.trim().equals("")) {
                    ((ObjectNode) newConfiguration.get("rowRange")).put("end", -1);
                    removeErrorMessage(TO_ROW_ERROR_MESSAGE);
                    removeErrorMessage(INCONSISTENT_ROW_MESSAGE);
                } else if (text.trim().matches("[0-9]+")) {
                    checkRowRange(rowFromValue.getText(), rowToValue.getText());
                    int toRow = Integer.parseInt(rowToValue.getText());
                    ((ObjectNode) newConfiguration.get("rowRange")).put("end", toRow - 1);
                    removeErrorMessage(TO_ROW_ERROR_MESSAGE);
                } else {
                    addErrorMessage(TO_ROW_ERROR_MESSAGE);
                }
            }
        });

        rowSelectAllOption.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    newConfiguration.put("allRows", true);
                    rowExcludeFirstOption.setEnabled(true);
                    if (rowExcludeFirstOption.isSelected()) {
                        rowFromValue.setText("2");
                    } else {
                        rowFromValue.setText("1");
                    }
                    rowToValue.setText("");
                    rowFromValue.setEditable(false);
                    rowFromValue.setEnabled(false);
                    rowToValue.setEditable(false);
                    rowToValue.setEnabled(false);
                } else {
                    newConfiguration.put("allRows", false);
                    rowExcludeFirstOption.setEnabled(false);
                    rowFromValue.setEditable(true);
                    rowFromValue.setEnabled(true);
                    rowToValue.setEditable(true);
                    rowToValue.setEnabled(true);
                }
            }
        });

        rowExcludeFirstOption.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    newConfiguration.put("excludeFirstRow", true);
                    rowFromValue.setText("2");
                    ((ObjectNode) newConfiguration.get("rowRange")).put("start", 1);
                } else {
                    newConfiguration.put("excludeFirstRow", false);
                    rowFromValue.setText("1");
                    ((ObjectNode) newConfiguration.get("rowRange")).put("start", 0);
                }
            }
        });

        rowIgnoreBlankRows.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                newConfiguration.put("ignoreBlankRows", e.getStateChange() == ItemEvent.SELECTED);
            }
        });

        // empty cells
        emptyCellLabel = new JLabel(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.emptyCellSectionLabel"));
        addDivider(emptyCellLabel, SwingConstants.TOP, false);

        emptyCellButtonGroup = new ButtonGroup();
        emptyCellEmptyStringOption = new JRadioButton(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.emptyStringOption"));
        emptyCellUserDefinedOption = new JRadioButton(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.userDefinedOption"));
        emptyCellErrorValueOption = new JRadioButton(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.generateErrorOption"));
        emptyCellEmptyStringOption.setFocusable(false);
        emptyCellUserDefinedOption.setFocusable(false);
        emptyCellErrorValueOption.setFocusable(false);

        emptyCellUserDefinedValue = new JTextField(newConfiguration.get("emptyCellValue").textValue());

        emptyCellButtonGroup.add(emptyCellEmptyStringOption);
        emptyCellButtonGroup.add(emptyCellUserDefinedOption);
        emptyCellButtonGroup.add(emptyCellErrorValueOption);

        if (newConfiguration.get("emptyCellPolicy").textValue().equals("GENERATE_ERROR")) {
            emptyCellErrorValueOption.setSelected(true);
            emptyCellUserDefinedValue.setEnabled(false);
            emptyCellUserDefinedValue.setEditable(false);
        } else if (newConfiguration.get("emptyCellPolicy").textValue().equals("EMPTY_STRING")) {
            emptyCellEmptyStringOption.setSelected(true);
            emptyCellUserDefinedValue.setEnabled(false);
            emptyCellUserDefinedValue.setEditable(false);
        } else {
            emptyCellUserDefinedOption.setSelected(true);
            emptyCellUserDefinedValue.setText(newConfiguration.get("emptyCellValue").textValue());
            emptyCellUserDefinedValue.setEnabled(true);
            emptyCellUserDefinedValue.setEditable(true);
        }

        emptyCellEmptyStringOption.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                newConfiguration.put("emptyCellPolicy", "EMPTY_STRING");
                emptyCellUserDefinedValue.setEnabled(false);
                emptyCellUserDefinedValue.setEditable(false);
            }
        });
        emptyCellUserDefinedOption.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                newConfiguration.put("emptyCellPolicy", "USER_DEFINED");
                emptyCellUserDefinedValue.setEnabled(true);
                emptyCellUserDefinedValue.setEditable(true);
                emptyCellUserDefinedValue.requestFocusInWindow();
            }
        });
        emptyCellErrorValueOption.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                newConfiguration.put("emptyCellPolicy", "GENERATE_ERROR");
                emptyCellUserDefinedValue.setEnabled(false);
                emptyCellUserDefinedValue.setEditable(false);
            }
        });

        emptyCellUserDefinedValue.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
                newConfiguration.put("emptyCellValue", emptyCellUserDefinedValue.getText());
            }

            public void insertUpdate(DocumentEvent e) {
                newConfiguration.put("emptyCellValue", emptyCellUserDefinedValue.getText());
            }

            public void removeUpdate(DocumentEvent e) {
                newConfiguration.put("emptyCellValue", emptyCellUserDefinedValue.getText());
            }
        });

        // column mappings
        columnMappingLabel = new JLabel(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.columnMappingSectionLabel"));
        addDivider(columnMappingLabel, SwingConstants.TOP, false);

        Map<String, String> columnToPortMapping = new HashMap<>();
        if (newConfiguration.has("columnNames")) {
            for (JsonNode columnName : newConfiguration.get("columnNames")) {
                columnToPortMapping.put(columnName.get("column").textValue(), columnName.get("port").textValue());
            }
        }
        columnMappingTableModel = new SpreadsheetImportConfigTableModel(columnFromValue.getText(),
                columnToValue.getText(), columnToPortMapping);

        columnMappingTable = new JTable();
        columnMappingTable.setRowSelectionAllowed(false);
        columnMappingTable.getTableHeader().setReorderingAllowed(false);
        columnMappingTable.setGridColor(Color.LIGHT_GRAY);
        // columnMappingTable.setFocusable(false);

        columnMappingTable.setColumnModel(new DefaultTableColumnModel() {
            public TableColumn getColumn(int columnIndex) {
                TableColumn column = super.getColumn(columnIndex);
                if (columnIndex == 0) {
                    column.setMaxWidth(100);
                }
                return column;
            }
        });

        TableCellEditor defaultEditor = columnMappingTable.getDefaultEditor(String.class);
        if (defaultEditor instanceof DefaultCellEditor) {
            DefaultCellEditor defaultCellEditor = (DefaultCellEditor) defaultEditor;
            defaultCellEditor.setClickCountToStart(1);
            Component editorComponent = defaultCellEditor.getComponent();
            if (editorComponent instanceof JTextComponent) {
                final JTextComponent textField = (JTextComponent) editorComponent;
                textField.getDocument().addDocumentListener(new DocumentListener() {
                    public void changedUpdate(DocumentEvent e) {
                        updateModel(textField.getText());
                    }

                    public void insertUpdate(DocumentEvent e) {
                        updateModel(textField.getText());
                    }

                    public void removeUpdate(DocumentEvent e) {
                        updateModel(textField.getText());
                    }

                    private void updateModel(String text) {
                        int row = columnMappingTable.getEditingRow();
                        int column = columnMappingTable.getEditingColumn();
                        columnMappingTableModel.setValueAt(text, row, column);

                        ArrayNode columnNames = newConfiguration.arrayNode();
                        Map<String, String> columnToPortMapping = columnMappingTableModel.getColumnToPortMapping();
                        for (Entry<String, String> entry : columnToPortMapping.entrySet()) {
                            columnNames.add(newConfiguration.objectNode().put("column", entry.getKey()).put("port",
                                    entry.getValue()));
                        }
                        newConfiguration.put("columnNames", columnNames);
                        validatePortNames();
                    }

                });
            }
        }

        columnMappingTable.setModel(columnMappingTableModel);

        // output format
        outputFormatLabel = new JLabel(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.outputFormatSectionLabel"));

        outputFormatMultiplePort = new JRadioButton(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.multiplePortOption"));
        outputFormatSinglePort = new JRadioButton(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.singlePortOption"));
        outputFormatMultiplePort.setFocusable(false);
        outputFormatSinglePort.setFocusable(false);

        outputFormatDelimiterLabel = new JLabel(
                SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.userDefinedCsvDelimiter"));
        outputFormatDelimiter = new JTextField(newConfiguration.get("csvDelimiter").textValue(), 5);

        outputFormatButtonGroup = new ButtonGroup();
        outputFormatButtonGroup.add(outputFormatMultiplePort);
        outputFormatButtonGroup.add(outputFormatSinglePort);

        if (newConfiguration.get("outputFormat").textValue().equals("PORT_PER_COLUMN")) {
            outputFormatMultiplePort.setSelected(true);
            outputFormatDelimiterLabel.setEnabled(false);
            outputFormatDelimiter.setEnabled(false);
        } else {
            outputFormatSinglePort.setSelected(true);
            columnMappingLabel.setEnabled(false);
            enableTable(columnMappingTable, false);
        }

        outputFormatMultiplePort.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                outputFormatDelimiterLabel.setEnabled(false);
                outputFormatDelimiter.setEnabled(false);
                columnMappingLabel.setEnabled(true);
                enableTable(columnMappingTable, true);
                newConfiguration.put("outputFormat", "PORT_PER_COLUMN");
            }
        });
        outputFormatSinglePort.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                outputFormatDelimiterLabel.setEnabled(true);
                outputFormatDelimiter.setEnabled(true);
                columnMappingLabel.setEnabled(false);
                enableTable(columnMappingTable, false);
                newConfiguration.put("outputFormat", "SINGLE_PORT");
            }

        });
        outputFormatDelimiter.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
                handleUpdate();
            }

            public void insertUpdate(DocumentEvent e) {
                handleUpdate();
            }

            public void removeUpdate(DocumentEvent e) {
                handleUpdate();
            }

            private void handleUpdate() {
                String text = null;
                try {
                    text = StringEscapeUtils.unescapeJava(outputFormatDelimiter.getText());
                } catch (RuntimeException re) {
                }
                if (text == null || text.length() == 0) {
                    newConfiguration.put("csvDelimiter", ",");
                } else {
                    newConfiguration.put("csvDelimiter", text.substring(0, 1));
                }
            }

        });

        // buttons
        nextButton = new JButton(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.nextButton"));
        nextButton.setFocusable(false);
        nextButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                backButton.setVisible(true);
                nextButton.setVisible(false);
                cardLayout.last(contentPanel);
            }
        });

        backButton = new JButton(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.backButton"));
        backButton.setFocusable(false);
        backButton.setVisible(false);
        backButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                nextButton.setVisible(true);
                backButton.setVisible(false);
                cardLayout.first(contentPanel);
            }
        });

        buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        addDivider(buttonPanel, SwingConstants.TOP, true);

        removeAll();
        layoutPanel();
    }

    @Override
    public void noteConfiguration() {
        setJson(newConfiguration);
        configureInputPorts(serviceRegistry);
        configureOutputPorts(serviceRegistry);
    }

    @Override
    public boolean checkValues() {
        return errorMessages.isEmpty();
    }

    private void layoutPanel() {
        setPreferredSize(new Dimension(450, 400));
        setLayout(new BorderLayout());

        page1 = new JPanel(new GridBagLayout());
        page2 = new JPanel(new GridBagLayout());

        contentPanel = new JPanel(cardLayout);
        contentPanel.add(page1, "page1");
        contentPanel.add(page2, "page2");
        add(contentPanel, BorderLayout.CENTER);

        // title
        titlePanel.setBorder(new CompoundBorder(titlePanel.getBorder(), new EmptyBorder(10, 10, 0, 10)));
        add(titlePanel, BorderLayout.NORTH);
        titlePanel.add(titleLabel, BorderLayout.NORTH);
        titlePanel.add(titleIcon, BorderLayout.WEST);
        titlePanel.add(titleMessage, BorderLayout.CENTER);

        GridBagConstraints c = new GridBagConstraints();
        c.anchor = GridBagConstraints.WEST;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 1;
        c.gridx = 0;
        c.gridwidth = GridBagConstraints.REMAINDER;

        // column range
        c.insets = new Insets(10, 10, 0, 10);
        page1.add(columnLabel, c);

        c.insets = new Insets(10, 25, 0, 0);
        c.gridwidth = 1;
        c.weightx = 0;
        page1.add(new JLabel(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.from")), c);
        c.insets = new Insets(10, 0, 0, 0);
        c.gridx = 1;
        page1.add(columnFromValue, c);
        c.gridx = 2;
        page1.add(new JLabel(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.to")), c);
        c.gridx = 3;
        page1.add(columnToValue, c);

        c.gridx = 0;
        c.weightx = 1;
        c.insets = new Insets(10, 10, 0, 10);
        c.gridwidth = GridBagConstraints.REMAINDER;

        // row range
        page1.add(rowLabel, c);

        c.insets = new Insets(10, 25, 0, 0);
        c.gridwidth = 1;
        c.gridx = 0;
        c.weightx = 0;
        page1.add(new JLabel(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.from")), c);
        c.insets = new Insets(10, 0, 0, 0);
        c.gridx = 1;
        page1.add(rowFromValue, c);
        c.gridx = 2;
        page1.add(new JLabel(SpreadsheetImportUIText.getString("SpreadsheetImportConfigView.to")), c);
        c.gridx = 3;
        page1.add(rowToValue, c);
        c.gridx = 4;
        page1.add(rowSelectAllOption, c);
        c.gridx = 5;
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.insets = new Insets(10, 0, 0, 10);
        page1.add(rowExcludeFirstOption, c);
        c.insets = new Insets(10, 25, 0, 0);
        c.gridx = 0;
        page1.add(rowIgnoreBlankRows, c);

        c.gridx = 0;

        // empty cells
        c.insets = new Insets(10, 10, 10, 10);
        page1.add(emptyCellLabel, c);

        c.insets = new Insets(0, 25, 0, 10);
        page1.add(emptyCellEmptyStringOption, c);
        JPanel userDefinedPanel = new JPanel(new BorderLayout());
        userDefinedPanel.add(emptyCellUserDefinedOption, BorderLayout.WEST);
        userDefinedPanel.add(emptyCellUserDefinedValue, BorderLayout.CENTER);
        page1.add(userDefinedPanel, c);
        c.weighty = 1;
        c.anchor = GridBagConstraints.NORTHWEST;
        page1.add(emptyCellErrorValueOption, c);

        // output format
        c.insets = new Insets(10, 10, 10, 10);
        c.weighty = 0;
        c.weightx = 1;
        page2.add(outputFormatLabel, c);

        c.insets = new Insets(0, 25, 0, 10);
        page2.add(outputFormatMultiplePort, c);
        page2.add(outputFormatSinglePort, c);

        c.insets = new Insets(0, 50, 0, 10);
        JPanel outputFormatDelimiterPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        outputFormatDelimiterPanel.add(outputFormatDelimiterLabel);
        outputFormatDelimiterPanel.add(outputFormatDelimiter);
        page2.add(outputFormatDelimiterPanel, c);

        // column mapping
        c.insets = new Insets(10, 10, 0, 10);
        page2.add(columnMappingLabel, c);

        c.insets = new Insets(10, 10, 10, 10);
        c.fill = GridBagConstraints.BOTH;
        c.weighty = 1;
        page2.add(new JScrollPane(columnMappingTable), c);

        buttonPanel.add(backButton);
        buttonPanel.add(nextButton);
        add(buttonPanel, BorderLayout.SOUTH);
    }

    /**
     * Displays the message with no icon.
     *
     * @param message
     *            the message to display
     */
    public void setMessage(String message) {
        titleIcon.setIcon(null);
        titleMessage.setText(message);
    }

    /**
     * Adds the message to the top of the warning message stack. If the message is already in the
     * stack it is moved to the top. If there are no error messages the message is displayed.
     *
     * @param message
     *            the warning message to add
     */
    public void addWarningMessage(String message) {
        if (warningMessages.contains(message)) {
            warningMessages.remove(message);
        }
        warningMessages.push(message);
        if (errorMessages.isEmpty()) {
            setWarningMessage(message);
        }
    }

    /**
     * Removes the message from the warning message stack. If there are no error messages the next
     * warning message is displayed. If there are no warning messages the default message is
     * displayed.
     *
     * @param message
     *            the warning message to remove
     */
    public void removeWarningMessage(String message) {
        warningMessages.remove(message);
        if (errorMessages.isEmpty()) {
            if (warningMessages.isEmpty()) {
                setMessage(DEFAULT_MESSAGE);
            } else {
                setWarningMessage(warningMessages.peek());
            }
        }
    }

    /**
     * Displays the message and a warning icon.
     *
     * @param message
     *            the warning message to display
     */
    public void setWarningMessage(String message) {
        titleIcon.setIcon(Icons.warningIcon);
        titleMessage.setText(message);
    }

    /**
     * Adds the message to the top of the error message stack. If the message is already in the
     * stack it is moved to the top. The message is then displayed.
     *
     * @param message
     *            the error message to add
     */
    public void addErrorMessage(String message) {
        if (errorMessages.contains(message)) {
            errorMessages.remove(message);
        }
        errorMessages.push(message);
        setErrorMessage(message);
    }

    /**
     * Removes the message from the error message stack and displays the next error message. If
     * there are no error messages the next warning message is displayed. If there are no warning
     * messages the default message is displayed.
     *
     * @param message
     *            the error message to remove
     */
    public void removeErrorMessage(String message) {
        errorMessages.remove(message);
        if (errorMessages.isEmpty()) {
            if (warningMessages.isEmpty()) {
                setMessage(DEFAULT_MESSAGE);
            } else {
                setWarningMessage(warningMessages.peek());
            }
        } else {
            setErrorMessage(errorMessages.peek());
        }
    }

    /**
     * Displays the message and an error icon.
     *
     * @param message
     *            the error message to display
     */
    public void setErrorMessage(String message) {
        titleIcon.setIcon(Icons.severeIcon);
        titleMessage.setText(message);
    }

    protected boolean validatePortNames() {
        boolean isValid = true;
        Range columnRange = SpreadsheetUtils.getRange(newConfiguration.get("columnRange"));
        Map<String, String> mapping = new HashMap<>();
        if (newConfiguration.has("columnNames")) {
            for (JsonNode columnName : newConfiguration.get("columnNames")) {
                mapping.put(columnName.get("column").textValue(), columnName.get("port").textValue());
            }
        }
        Set<String> usedNames = new HashSet<String>();
        for (Entry<String, String> entry : mapping.entrySet()) {
            if (columnRange.contains(SpreadsheetUtils.getColumnIndex(entry.getKey()))) {
                String portName = entry.getValue();
                if (!usedNames.add(portName)) {
                    isValid = false;
                    break;
                }
                if (portName.matches("[A-Z]+")) {
                    if (!mapping.containsKey(portName)) {
                        int columnIndex = SpreadsheetUtils.getColumnIndex(portName);
                        if (columnRange.contains(columnIndex)) {
                            isValid = false;
                            break;
                        }
                    }
                }
            }
        }
        if (isValid) {
            removeErrorMessage(DUPLICATE_PORT_NAME_ERROR_MESSAGE);
        } else {
            addErrorMessage(DUPLICATE_PORT_NAME_ERROR_MESSAGE);
        }
        return isValid;
    }

    protected boolean checkRowRange(String from, String to) {
        boolean result = false;
        try {
            int fromRow = Integer.parseInt(from);
            int toRow = Integer.parseInt(to);
            if (toRow < fromRow) {
                addErrorMessage(INCONSISTENT_ROW_MESSAGE);
            } else {
                removeErrorMessage(INCONSISTENT_ROW_MESSAGE);
                result = true;
            }
        } catch (NumberFormatException e) {
            logger.warn("Problem checking row range", e);
        }
        return result;
    }

    protected boolean checkColumnRange(int fromColumn, int toColumn) {
        boolean result = false;
        if (toColumn < fromColumn) {
            addErrorMessage(INCONSISTENT_COLUMN_MESSAGE);
        } else {
            removeErrorMessage(INCONSISTENT_COLUMN_MESSAGE);
            result = true;
        }
        return result;
    }

    /**
     * Adds a light gray or etched border to the top or bottom of a JComponent.
     *
     * @param component
     */
    protected void addDivider(JComponent component, final int position, final boolean etched) {
        component.setBorder(new Border() {
            private final Color borderColor = new Color(.6f, .6f, .6f);

            public Insets getBorderInsets(Component c) {
                if (position == SwingConstants.TOP) {
                    return new Insets(5, 0, 0, 0);
                } else {
                    return new Insets(0, 0, 5, 0);
                }
            }

            public boolean isBorderOpaque() {
                return false;
            }

            public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
                if (position == SwingConstants.TOP) {
                    if (etched) {
                        g.setColor(borderColor);
                        g.drawLine(x, y, x + width, y);
                        g.setColor(Color.WHITE);
                        g.drawLine(x, y + 1, x + width, y + 1);
                    } else {
                        g.setColor(Color.LIGHT_GRAY);
                        g.drawLine(x, y, x + width, y);
                    }
                } else {
                    if (etched) {
                        g.setColor(borderColor);
                        g.drawLine(x, y + height - 2, x + width, y + height - 2);
                        g.setColor(Color.WHITE);
                        g.drawLine(x, y + height - 1, x + width, y + height - 1);
                    } else {
                        g.setColor(Color.LIGHT_GRAY);
                        g.drawLine(x, y + height - 1, x + width, y + height - 1);
                    }
                }
            }

        });
    }

    private void enableTable(JTable table, boolean enabled) {
        table.setEnabled(enabled);
        Component editor = table.getEditorComponent();
        if (editor != null) {
            editor.setEnabled(enabled);
        }
        if (enabled) {
            table.setForeground(Color.BLACK);
            table.getTableHeader().setForeground(Color.BLACK);
        } else {
            table.setForeground(Color.LIGHT_GRAY);
            table.getTableHeader().setForeground(Color.LIGHT_GRAY);
        }
    }

    static class UpperCaseDocument extends PlainDocument {
        @Override
        public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            if (text.matches("[A-Za-z]+")) {
                text = text.toUpperCase();
                super.replace(offset, length, text, attrs);
            }
        }
    }

    static class NumericDocument extends PlainDocument {
        @Override
        public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            if (text.length() == 0 || text.matches("[0-9]+")) {
                text = text.toUpperCase();
                super.replace(offset, length, text, attrs);
            }
        }
    }

    /**
     * Main method for testing the panel.
     *
     * @param args
     * @throws ActivityConfigurationException
     */
    //   public static void main(String[] args) throws ActivityConfigurationException {
    //      final JFrame frame = new JFrame();
    //      SpreadsheetImportActivity activity = new SpreadsheetImportActivity();
    //      activity.configure(new SpreadsheetImportConfiguration());
    //      final SpreadsheetImportConfigView config = new SpreadsheetImportConfigView(activity);
    //      config.setOkAction(new AbstractAction("Finish") {
    //         public void actionPerformed(ActionEvent arg0) {
    //            Range columnRange = config.getConfiguration().getColumnRange();
    //            String fromColumn = SpreadsheetUtils.getColumnLabel(columnRange.getStart());
    //            String toColumn = SpreadsheetUtils.getColumnLabel(columnRange.getEnd());
    //            System.out.printf("%s (%s) - %s (%s)", fromColumn, columnRange.getStart(),
    //                  toColumn, columnRange.getEnd());
    //            frame.setVisible(false);
    //            frame.dispose();
    //         }
    //      });
    //      config.setCancelAction(new AbstractAction("Cancel") {
    //         public void actionPerformed(ActionEvent arg0) {
    //            frame.setVisible(false);
    //            frame.dispose();
    //         }
    //      });
    //      frame.add(config);
    //      frame.pack();
    //      frame.setVisible(true);
    //   }

}