org.ut.biolab.medsavant.client.project.ProjectWizard.java Source code

Java tutorial

Introduction

Here is the source code for org.ut.biolab.medsavant.client.project.ProjectWizard.java

Source

/**
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 *
 * This 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 software 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 software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.ut.biolab.medsavant.client.project;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.*;
import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

import com.jidesoft.dialog.ButtonEvent;
import com.jidesoft.dialog.ButtonNames;
import com.jidesoft.dialog.PageList;
import com.jidesoft.wizard.AbstractWizardPage;
import com.jidesoft.wizard.CompletionWizardPage;
import com.jidesoft.wizard.DefaultWizardPage;
import com.jidesoft.wizard.WizardDialog;
import com.jidesoft.wizard.WizardStyle;
import java.awt.BorderLayout;
import java.util.Arrays;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.ut.biolab.medsavant.MedSavantClient;
import org.ut.biolab.medsavant.shared.format.BasicPatientColumns;
import org.ut.biolab.medsavant.shared.format.BasicVariantColumns;
import org.ut.biolab.medsavant.shared.format.CustomField;
import org.ut.biolab.medsavant.client.view.login.LoginController;
import org.ut.biolab.medsavant.shared.model.Annotation;
import org.ut.biolab.medsavant.shared.model.ProjectDetails;
import org.ut.biolab.medsavant.shared.model.Reference;
import org.ut.biolab.medsavant.client.reference.NewReferenceDialog;
import org.ut.biolab.medsavant.shared.serverapi.ProjectManagerAdapter;
import org.ut.biolab.medsavant.client.util.ClientMiscUtils;
import org.ut.biolab.medsavant.client.util.MedSavantExceptionHandler;
import org.ut.biolab.medsavant.client.util.ProjectWorker;
import org.ut.biolab.medsavant.client.view.component.ProgressWheel;
import org.ut.biolab.medsavant.client.view.util.DialogUtils;
import org.ut.biolab.medsavant.client.view.util.ViewUtil;
import org.ut.biolab.medsavant.shared.model.SessionExpiredException;

/**
 *
 * @author Andrew, rammar
 */
public class ProjectWizard extends WizardDialog implements BasicPatientColumns, BasicVariantColumns {

    private static final Log LOG = LogFactory.getLog(ProjectWizard.class);
    private static final String PAGENAME_NAME = "Project Name";
    private static final String PAGENAME_PATIENTS = "Patients";
    private static final String PAGENAME_VCF = "Custom VCF Fields";
    private static final String PAGENAME_REF = "Reference";
    private static final String PAGENAME_IMPORT = "VCF Import Settings";
    private static final String PAGENAME_NOTIFICATIONS = "Notifications";
    private static final String PAGENAME_COMPLETE = "Finish";
    private static String PAGENAME_CREATE = "Create";
    private final boolean modify;
    private boolean isModified = false;
    private final int projectID;
    private final String originalProjectName;
    private String projectName;
    private CustomField[] customFields;
    private final ProjectDetails[] projectDetails;
    private DefaultTableModel patientFormatModel;
    private DefaultTableModel variantFormatModel;
    private CustomField[] variantFields;
    private List<CheckListItem> checkListItems = new ArrayList<CheckListItem>();
    private boolean variantFieldsChanged = false;
    private final ProjectManagerAdapter manager;
    private JTextField emailField;
    private JCheckBox autoPublish;
    private JCheckBox includeReferenceCheckbox;

    /*
     * modify existing project
     */
    public ProjectWizard(int projID, String projName, CustomField[] fields, ProjectDetails[] details)
            throws SQLException, RemoteException {
        this.modify = true;
        this.projectID = projID;
        this.originalProjectName = projName;
        this.projectName = projName;
        this.customFields = fields;
        this.projectDetails = details;
        PAGENAME_CREATE = "Modify";
        manager = MedSavantClient.ProjectManager;
        setupWizard();
    }

    /*
     * create new project
     */
    public ProjectWizard() throws SQLException, RemoteException {
        modify = false;
        projectID = -1;
        originalProjectName = null;
        projectDetails = new ProjectDetails[0];
        manager = MedSavantClient.ProjectManager;
        setupWizard();
    }

    private void setupWizard() throws SQLException, RemoteException {
        setTitle(modify ? "Modify Project" : "Create Project");
        WizardStyle.setStyle(WizardStyle.MACOSX_STYLE);

        //add pages
        PageList model = new PageList();
        model.append(getNamePage());
        model.append(getPatientFieldsPage());
        model.append(getVCFFieldsPage());
        model.append(getReferencePage());
        model.append(getVCFImportSettingsPage());
        if (modify) {
            model.append(getNotificationsPage());
            model.append(getCreatePage());
        }
        model.append(getCompletionPage());
        setPageList(model);

        //change next action
        setNextAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    String pagename = getCurrentPage().getTitle();
                    if (pagename.equals(PAGENAME_NAME) && validateProjectName()) {
                        setCurrentPage(PAGENAME_PATIENTS);
                    } else if (pagename.equals(PAGENAME_PATIENTS) && validatePatientFormatModel()) {
                        setCurrentPage(PAGENAME_VCF);
                    } else if (pagename.equals(PAGENAME_VCF) && validateVariantFormatModel()) {
                        setCurrentPage(PAGENAME_REF);
                    } else if (pagename.equals(PAGENAME_REF) && validateReferences()) {
                        setCurrentPage(PAGENAME_IMPORT);
                    } else if (pagename.equals(PAGENAME_IMPORT)) {
                        if (modify) {
                            setCurrentPage(PAGENAME_NOTIFICATIONS);
                        } else {
                            setCurrentPage(PAGENAME_COMPLETE);
                        }
                    } else if (pagename.equals(PAGENAME_NOTIFICATIONS)) {
                        setCurrentPage(PAGENAME_CREATE);
                    } else if (pagename.equals(PAGENAME_CREATE)) {
                        setCurrentPage(PAGENAME_COMPLETE);
                    }
                } catch (Exception ex) {
                    ClientMiscUtils.reportError("Unable to proceed: %s", ex);
                }
            }
        });

        pack();
        setResizable(true);
        setLocationRelativeTo(null);
    }

    private AbstractWizardPage getNamePage() {

        //setup page
        final DefaultWizardPage page = new DefaultWizardPage(PAGENAME_NAME) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.FINISH);
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.BACK);
                if (projectName == null || projectName.equals("")) {
                    fireButtonEvent(ButtonEvent.DISABLE_BUTTON, ButtonNames.NEXT);
                } else {
                    fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);
                }
            }
        };
        page.addText("Choose a name for the project. \n" + "The name cannot already be in use. ");

        //setup text field
        final JTextField namefield = new JTextField();
        namefield.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if (namefield.getText() != null && !namefield.getText().equals("")) {
                    projectName = namefield.getText();
                    page.fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);
                } else {
                    page.fireButtonEvent(ButtonEvent.DISABLE_BUTTON, ButtonNames.NEXT);
                }
            }
        });
        page.addComponent(namefield);
        if (modify) {
            namefield.setText(projectName);
        }

        return page;
    }

    private AbstractWizardPage getPatientFieldsPage() {

        //setup page
        final DefaultWizardPage page = new DefaultWizardPage(PAGENAME_PATIENTS) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.SHOW_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.FINISH);
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);
            }
        };

        page.addText("Add relevant fields for patients. ");

        JScrollPane scrollpane = new JScrollPane();
        scrollpane.setPreferredSize(new Dimension(300, 250));
        scrollpane.getViewport().setBackground(Color.white);

        final JTable table = new JTable() {
            @Override
            public Class<?> getColumnClass(int column) {
                if (column == 2) {
                    return Boolean.class;
                } else {
                    return String.class;
                }
            }
        };

        patientFormatModel = new DefaultTableModel() {
            @Override
            public boolean isCellEditable(int row, int col) {
                return row >= 8;
            }
        };

        patientFormatModel.addColumn("Name");
        patientFormatModel.addColumn("Type");
        patientFormatModel.addColumn("Filterable");
        patientFormatModel.addColumn("Alias");
        patientFormatModel.addColumn("Description");

        patientFormatModel.addRow(new Object[] { FAMILY_ID.getColumnName(), FAMILY_ID.getTypeString(), true,
                FAMILY_ID.getAlias(), "" });
        patientFormatModel.addRow(new Object[] { HOSPITAL_ID.getColumnName(), HOSPITAL_ID.getTypeString(), true,
                HOSPITAL_ID.getAlias(), "" });
        patientFormatModel.addRow(
                new Object[] { IDBIOMOM.getColumnName(), IDBIOMOM.getTypeString(), true, IDBIOMOM.getAlias(), "" });
        patientFormatModel.addRow(
                new Object[] { IDBIODAD.getColumnName(), IDBIODAD.getTypeString(), true, IDBIODAD.getAlias(), "" });
        patientFormatModel.addRow(
                new Object[] { GENDER.getColumnName(), GENDER.getTypeString(), true, GENDER.getAlias(), "" });
        patientFormatModel.addRow(
                new Object[] { AFFECTED.getColumnName(), AFFECTED.getTypeString(), true, AFFECTED.getAlias(), "" });
        patientFormatModel.addRow(
                new Object[] { DNA_IDS.getColumnName(), DNA_IDS.getTypeString(), true, DNA_IDS.getAlias(), "" });
        patientFormatModel.addRow(
                new Object[] { BAM_URL.getColumnName(), BAM_URL.getTypeString(), true, BAM_URL.getAlias(), "" });
        patientFormatModel.addRow(new Object[] { PHENOTYPES.getColumnName(), PHENOTYPES.getTypeString(), true,
                PHENOTYPES.getAlias(), "" });

        if (modify) {
            for (CustomField f : customFields) {
                patientFormatModel.addRow(new Object[] { f.getColumnName(), f.getTypeString(), f.isFilterable(),
                        f.getAlias(), f.getDescription() });
            }
        }

        table.setModel(patientFormatModel);
        table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
        scrollpane.getViewport().add(table);
        page.addComponent(scrollpane);

        JButton addFieldButton = new JButton("Add Field");
        addFieldButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                patientFormatModel.addRow(new Object[5]);
                table.setModel(patientFormatModel);
            }
        });
        page.addComponent(addFieldButton);

        JButton removeFieldButton = new JButton("Remove Field");
        removeFieldButton.setEnabled(false);
        removeFieldButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                int row = table.getSelectedRow();
                // Minus one because patient_id isn't in the table.
                if (row >= BasicPatientColumns.REQUIRED_PATIENT_FIELDS.length - 1) {
                    patientFormatModel.removeRow(row);
                }
                table.setModel(patientFormatModel);
            }
        });
        table.getSelectionModel().addListSelectionListener(
                new RemovalEnabler(BasicPatientColumns.REQUIRED_PATIENT_FIELDS.length - 1, removeFieldButton));
        page.addComponent(removeFieldButton);

        return page;
    }

    private AbstractWizardPage getVCFFieldsPage() {

        //setup page
        final DefaultWizardPage page = new DefaultWizardPage(PAGENAME_VCF) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.SHOW_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.FINISH);
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);
            }
        };

        page.addText("Add extra fields to parse from INFO text in VCF files. ");

        JScrollPane scrollpane = new JScrollPane();
        scrollpane.setPreferredSize(new Dimension(300, 250));
        scrollpane.getViewport().setBackground(Color.white);

        final JTable table = new JTable() {
            @Override
            public Class<?> getColumnClass(int column) {
                if (column == 2) {
                    return Boolean.class;
                } else {
                    return String.class;
                }
            }
        };

        variantFormatModel = new DefaultTableModel();

        variantFormatModel.addColumn("Key");
        variantFormatModel.addColumn("Type");
        variantFormatModel.addColumn("Filterable");
        variantFormatModel.addColumn("Alias");
        variantFormatModel.addColumn("Description");

        if (modify) {
            try {
                int firstRef = manager.getReferenceIDsForProject(LoginController.getSessionID(), projectID)[0];
                CustomField[] fields = manager.getCustomVariantFields(LoginController.getSessionID(), projectID,
                        firstRef,
                        manager.getNewestUpdateID(LoginController.getSessionID(), projectID, firstRef, false));
                for (CustomField f : fields) {
                    //casing of f.getColumnName should match database.
                    variantFormatModel.addRow(new Object[] { f.getColumnName(), f.getTypeString(), f.isFilterable(),
                            f.getAlias(), f.getDescription() });
                }
            } catch (Exception ex) {
                LOG.error("Error getting reference IDs for project.", ex);
            }
        } else {
            variantFormatModel
                    .addRow(new Object[] { AA.getColumnName(), AA.getTypeString(), true, AA.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { AC.getColumnName(), AC.getTypeString(), true, AC.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { AF.getColumnName(), AF.getTypeString(), true, AF.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { AN.getColumnName(), AN.getTypeString(), true, AN.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { BQ.getColumnName(), BQ.getTypeString(), true, BQ.getAlias(), "" });
            variantFormatModel.addRow(
                    new Object[] { CIGAR.getColumnName(), CIGAR.getTypeString(), true, CIGAR.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { DB.getColumnName(), DB.getTypeString(), true, DB.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { DP.getColumnName(), DP.getTypeString(), true, DP.getAlias(), "" });
            //variantFormatModel.addRow(new Object[]{END.getColumnName(), END.getTypeString(), true, END.getAlias(), ""});
            variantFormatModel
                    .addRow(new Object[] { H2.getColumnName(), H2.getTypeString(), true, H2.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { MQ.getColumnName(), MQ.getTypeString(), true, MQ.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { MQ0.getColumnName(), MQ0.getTypeString(), true, MQ0.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { NS.getColumnName(), NS.getTypeString(), true, NS.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { SB.getColumnName(), SB.getTypeString(), true, SB.getAlias(), "" });
            variantFormatModel.addRow(new Object[] { SOMATIC.getColumnName(), SOMATIC.getTypeString(), true,
                    SOMATIC.getAlias(), "" });
            variantFormatModel.addRow(new Object[] { VALIDATED.getColumnName(), VALIDATED.getTypeString(), true,
                    VALIDATED.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { JANNOVAR_EFFECT.getColumnName(), JANNOVAR_EFFECT.getTypeString(),
                            JANNOVAR_EFFECT.isFilterable(), JANNOVAR_EFFECT.getAlias(), "" });
            variantFormatModel
                    .addRow(new Object[] { JANNOVAR_SYMBOL.getColumnName(), JANNOVAR_SYMBOL.getTypeString(),
                            JANNOVAR_SYMBOL.isFilterable(), JANNOVAR_SYMBOL.getAlias(), "" });
            variantFormatModel.addRow(new Object[] { FORMAT.getColumnName(), FORMAT.getTypeString(),
                    FORMAT.isFilterable(), FORMAT.getAlias(), "" });
            variantFormatModel.addRow(new Object[] { SAMPLE_INFO.getColumnName(), SAMPLE_INFO.getTypeString(),
                    SAMPLE_INFO.isFilterable(), SAMPLE_INFO.getAlias(), "" });
        }

        table.setModel(variantFormatModel);
        table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
        scrollpane.getViewport().add(table);
        page.addComponent(scrollpane);

        table.addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
                variantFieldsChanged = true;
            }

            @Override
            public void keyPressed(KeyEvent e) {
                variantFieldsChanged = true;
            }

            @Override
            public void keyReleased(KeyEvent e) {
                variantFieldsChanged = true;
            }
        });

        JButton addFieldButton = new JButton("Add Field");
        addFieldButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                variantFormatModel.addRow(new Object[2]);
                table.setModel(variantFormatModel);
                variantFieldsChanged = true;
            }
        });
        page.addComponent(addFieldButton);

        JButton removeFieldButton = new JButton("Remove Field");
        removeFieldButton.setEnabled(false);
        removeFieldButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                int row = table.getSelectedRow();
                if (row >= 0) {
                    variantFormatModel.removeRow(row);
                    table.setModel(variantFormatModel);
                    variantFieldsChanged = true;
                }
            }
        });
        table.getSelectionModel().addListSelectionListener(new RemovalEnabler(0, removeFieldButton));
        page.addComponent(removeFieldButton);

        return page;
    }

    private String getLengthString(int len) {
        if (len > 0) {
            return "(" + len + ")";
        } else {
            return "";
        }
    }

    private AbstractWizardPage getReferencePage() throws SQLException, RemoteException {
        //setup page
        final DefaultWizardPage page = new DefaultWizardPage(PAGENAME_REF) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.FINISH);
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);
            }
        };
        page.addText("Choose reference genome(s) to add to this project, along\n"
                + "with corresponding annotations. Annotations will be\n"
                + "applied to all variants added to these tables.");

        //setup list
        JScrollPane scrollpane = new JScrollPane();
        scrollpane.setPreferredSize(new Dimension(300, 220));
        scrollpane.getViewport().setBackground(Color.white);

        final JPanel p = new JPanel();
        p.setBackground(Color.white);
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
        refreshReferencePanel(p);
        scrollpane.getViewport().add(p);

        page.addComponent(scrollpane);

        JButton addRefButton = new JButton("New Reference");
        addRefButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                try {
                    new NewReferenceDialog().setVisible(true);
                    refreshReferencePanel(p);
                } catch (Exception ex) {
                    ClientMiscUtils.reportError("Unable to retrieve references and annotations: %s", ex);
                }
            }
        });
        page.addComponent(addRefButton);

        return page;
    }

    private void refreshReferencePanel(JPanel p) throws SQLException, RemoteException {
        try {
            Reference[] references = MedSavantClient.ReferenceManager.getReferences(LoginController.getSessionID());
            Annotation[] annotations = MedSavantClient.AnnotationManagerAdapter
                    .getAnnotations(LoginController.getSessionID());

            p.removeAll();
            checkListItems.clear();
            for (Reference r : references) {
                CheckListItem cli = new CheckListItem(r, annotations);
                checkListItems.add(cli);
                p.add(cli);
            }
            // By default, select the last check-box.  It's either the one they just added, or hg19.
            if (checkListItems.size() > 0) {
                checkListItems.get(checkListItems.size() - 1).setSelected(true);
            }
            p.add(Box.createVerticalGlue());

            p.updateUI();
            p.repaint();
        } catch (SessionExpiredException ex) {
            MedSavantExceptionHandler.handleSessionExpiredException(ex);
            return;
        }
    }

    private AbstractWizardPage getVCFImportSettingsPage() throws SQLException, RemoteException {
        //setup page
        final DefaultWizardPage page = new DefaultWizardPage(PAGENAME_IMPORT) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.FINISH);
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);
            }
        };

        if (modify) {
            page.addComponent(new JLabel(
                    "<html><b>Warning: Changes to this setting will only affect new VCF Uploads.</b></html>"));
        }

        // <html> wraps the text around if it doesn't fit.
        includeReferenceCheckbox = new JCheckBox("<html>Include all VCF lines, including reference calls."
                + "<br/>Highly recommended for pharmacogenetic testing.</html>");
        includeReferenceCheckbox.setSelected(false);
        includeReferenceCheckbox.setFocusable(false);

        page.addComponent(includeReferenceCheckbox);

        return page;
    }

    private AbstractWizardPage getCreatePage() {
        //setup page
        final DefaultWizardPage page = new DefaultWizardPage(PAGENAME_CREATE) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.SHOW_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.FINISH);
                fireButtonEvent(ButtonEvent.DISABLE_BUTTON, ButtonNames.NEXT);
            }
        };
        page.addText("You are now ready to " + (modify ? "make changes to" : "create") + " this project. ");

        final JLabel progressLabel = new JLabel("");

        page.addComponent(progressLabel);

        final JButton workButton = new JButton((modify ? "Modify Project" : "Create Project"));
        final JButton publishButton = new JButton("Publish Variants");

        final JComponent j = new JLabel(
                "<html><p>You may continue. The import process will continue in the< background and you will be notified upon completion.</p></html>");

        workButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                j.setVisible(true);
                page.fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);

                workButton.setEnabled(false);

                new ProjectWorker<Void>("Modifying project", autoPublish.isSelected(),
                        LoginController.getSessionID(), projectID) {
                    @Override
                    protected Void runInBackground() throws Exception {
                        LOG.info("Requesting modification from server");
                        modifyProject(true, true, true, this);
                        LOG.info("Modification complete");
                        return null;
                    }
                }.execute();

                toFront();
            }
        });

        page.addComponent(ViewUtil.alignRight(workButton));

        page.addComponent(j);
        j.setVisible(false);

        if (modify) {
            page.addComponent(ViewUtil.alignRight(publishButton));
            publishButton.setVisible(false);
        }

        return page;
    }

    private AbstractWizardPage getCompletionPage() {
        final ProgressWheel pw = new ProgressWheel();
        final JPanel p = new JPanel();
        p.setOpaque(false);
        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
        final CompletionWizardPage page = new CompletionWizardPage(PAGENAME_COMPLETE) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.NEXT);
                if (modify) {
                    fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.FINISH);
                } else {
                    fireButtonEvent(ButtonEvent.HIDE_BUTTON, ButtonNames.FINISH);

                    new SwingWorker() {
                        @Override
                        protected Object doInBackground() throws Exception {
                            try {
                                createNewProject();
                            } catch (Exception e) {
                                DialogUtils.displayException("Error", "Error trying to create project", e);
                                LOG.error(e);
                                e.printStackTrace();
                            }
                            return null;
                        }

                        @Override
                        protected void done() {
                            pw.setComplete();
                            p.setVisible(true);
                            fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.FINISH);
                            revalidate();
                            repaint();
                        }
                    }.execute();
                }
            }
        };

        if (modify) {
            page.addText("You have completed the project modification process.");
        } else {
            page.addText("Creating project...");
            page.addComponent(pw);
            p.add(new JLabel("Complete."));
            p.add(Box.createHorizontalGlue());
            page.addComponent(p);
            p.setVisible(false);

            /*p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
             p.add(pw, Box.createHorizontalGlue());
             p.add(pw, BorderLayout.CENTER);
             p.add(pw, Box.createHorizontalGlue());
             page.addComponent(p);*/
        }

        return page;
    }

    private boolean validateProjectName() {
        try {
            if (manager.containsProject(LoginController.getSessionID(), projectName)
                    && (!modify || !projectName.equals(originalProjectName))) {
                JOptionPane.showMessageDialog(this, "Project name already in use. ", "Error",
                        JOptionPane.ERROR_MESSAGE);
            } else {
                return true;
            }
        } catch (Exception ex) {
            LOG.error("Error checking for existence of project.", ex);
            DialogUtils.displayException("Error", "Error trying to create project", ex);
        }
        return false;
    }

    private boolean validateReferences() {
        for (CheckListItem cli : checkListItems) {
            if (cli.isSelected()) {
                return true;
            }
        }
        DialogUtils.displayError("You must select at least one reference.");
        return false;
    }

    private boolean validatePatientFormatModel() {
        // 8 is the number of standard patientFields
        List<CustomField> fields = new ArrayList<CustomField>();
        String validationErr = validateFormatModel(fields, patientFormatModel,
                BasicPatientColumns.REQUIRED_PATIENT_FIELDS.length - 1);
        if (validationErr == null) {
            customFields = fields.toArray(new CustomField[0]);
            return true;
        } else {
            DialogUtils.displayError(String.format("Individuals table format contains errors\n%s", validationErr));
            return false;
        }
    }

    private boolean validateVariantFormatModel() {
        List<CustomField> fields = new ArrayList<CustomField>();
        String validationErr = validateFormatModel(fields, variantFormatModel, 0);
        if (validationErr == null) {
            variantFields = fields.toArray(new CustomField[0]);
            return true;
        } else {
            DialogUtils.displayError(String.format("Variant table format contains errors.\n%s", validationErr));
            return false;
        }
    }

    private String validateFormatModel(List<CustomField> fields, DefaultTableModel model, int firstRow) {

        for (int row = firstRow; row < model.getRowCount(); row++) {
            String fieldName = (String) model.getValueAt(row, 0);
            String fieldType = (String) model.getValueAt(row, 1);
            Boolean fieldFilterable = (Boolean) model.getValueAt(row, 2);
            String fieldAlias = (String) model.getValueAt(row, 3);
            String fieldDescription = (String) model.getValueAt(row, 4);

            if (fieldName == null || fieldType == null) {
                continue;
            }

            if (!fieldName.matches("^([a-z]|[A-Z]|_|[0-9])+$")) {// ||
                //!fieldType.matches("^([a-z]|[A-Z])+\\([0-9]+\\)$")) {
                return "Field name can contain only letters, numbers, and underscores.";
            }

            if (!fieldName.equals("") && !fieldType.equals("")) {
                if (fieldFilterable == null) {
                    fieldFilterable = false;
                }
                if (fieldAlias == null) {
                    fieldAlias = fieldName;
                }
                if (fieldDescription == null) {
                    fieldDescription = "";
                }
                fields.add(new CustomField(fieldName, fieldType, fieldFilterable, fieldAlias, fieldDescription));
            }
        }

        return null;
    }

    private int[] mergeAnnIDsWithDefaults(int[] annIDs, int projID, int refID)
            throws RemoteException, SQLException, SessionExpiredException {
        /*LOG.info("WARNING: Debug code, temporarily disabled default annotation installation, line 714 of ProjectWizard.java");
        return annIDs;*/
        // UNCOMMENT THIS BEFORE COMMITTING
        int[] defaults = manager.getDefaultAnnotationIDs(LoginController.getSessionID(), projID, refID);
        Set<Integer> a = new HashSet<Integer>();
        a.addAll(Arrays.asList(ArrayUtils.toObject(annIDs)));
        a.addAll(Arrays.asList(ArrayUtils.toObject(defaults)));
        return ArrayUtils.toPrimitive(a.toArray(new Integer[a.size()]));
    }

    private void createNewProject() throws Exception {
        //create project
        int projID = ProjectController.getInstance().addProject(projectName, customFields);

        //add references and annotations
        for (CheckListItem cli : checkListItems) {
            if (cli.isSelected()) {
                //set custom vcf fields
                int refID = cli.getReference().getID();
                int[] annIDs = mergeAnnIDsWithDefaults(cli.getAnnotationIDs(), projID, refID);
                manager.setCustomVariantFields(LoginController.getSessionID(), projID, refID, 0, variantFields);
                manager.createVariantTable(LoginController.getSessionID(), projID, refID, 0, annIDs, false);

                ProjectController.getInstance().setContainsRefCalls(LoginController.getSessionID(), projID,
                        includeReferenceCheckbox.isSelected());

                //The below has been absorbed into createVariantTable.
                //manager.addTableToMap(LoginController.getSessionID(), projID, refID, 0, false, tablename, annIDs, null);
                //MedSavantClient.VariantManager.publishVariants(LoginController.getSessionID(), projID, refID, -1);
            }
        }
    }

    private int modifyProject(boolean modifyProjectName, boolean modifyPatientFields, boolean modifyVariants,
            ProjectWorker projectWorker) throws Exception {
        int updateID = -1;

        if (modifyProjectName) {
            //change project name
            if (!projectName.equals(originalProjectName)) {
                manager.renameProject(LoginController.getSessionID(), projectID, projectName);
            }
        }

        if (modifyPatientFields) {
            //modify patientFields
            MedSavantClient.PatientManager.updateFields(LoginController.getSessionID(), projectID, customFields);
        }

        if (modifyVariants) {

            //edit references and annotations
            for (CheckListItem cli : checkListItems) {
                ProjectDetails pd = getProjectDetails(cli.getReference().getID());

                LOG.info("Processing ref id " + cli.getReference().getID());
                //skip if not selected and not existing
                if (!cli.isSelected() && pd == null) {
                    LOG.info("Skipping this ref");
                    continue;
                }

                int[] annIDs = cli.getAnnotationIDs();

                //add new ref
                if (pd == null && cli.isSelected()) {
                    int refID = cli.getReference().getID();
                    LOG.info("Adding reference with id " + refID);
                    annIDs = mergeAnnIDsWithDefaults(annIDs, projectID, refID);
                    manager.setCustomVariantFields(LoginController.getSessionID(), projectID, refID, 0,
                            variantFields);
                    manager.createVariantTable(LoginController.getSessionID(), projectID, refID, 0, annIDs, false);
                    //The below has been absorbed into createVariantTable
                    //manager.addTableToMap(LoginController.getSessionID(), projectID, refID, 0, false, tablename, annIDs, null);
                    //MedSavantClient.VariantManager.publishVariants(LoginController.getSessionID(), projectID, refID, -1);
                    continue;
                } else if (pd != null && !cli.isSelected()) {
                    //remove existing ref
                    LOG.info("Removing reference with id " + cli.getReference().getID());
                    manager.removeReferenceForProject(LoginController.getSessionID(), projectID,
                            cli.getReference().getID());
                    continue;
                }

                String email = this.emailField.getText();
                boolean autoPublishWhenComplete = this.autoPublish.isSelected();
                //boolean autoPublishWhenComplete = false;
                updateID = MedSavantClient.VariantManager.updateTable(LoginController.getSessionID(), projectID,
                        cli.getReference().getID(), annIDs, variantFields, autoPublishWhenComplete, email);
            }
        }

        // TODO: this looks like a hack, what is this used to do?
        ProjectController.getInstance().fireEvent(new ProjectEvent(ProjectEvent.Type.REMOVED, projectName));
        ProjectController.getInstance().fireEvent(new ProjectEvent(ProjectEvent.Type.ADDED, projectName));

        return updateID;
    }

    private AbstractWizardPage getNotificationsPage() {
        final DefaultWizardPage page = new DefaultWizardPage(PAGENAME_NOTIFICATIONS) {
            @Override
            public void setupWizardButtons() {
                fireButtonEvent(ButtonEvent.SHOW_BUTTON, ButtonNames.BACK);
                fireButtonEvent(ButtonEvent.ENABLE_BUTTON, ButtonNames.NEXT);
            }
        };

        page.addText(
                "Project modification may take some time. Enter your email address to be notified when the process completes.");

        JPanel p = ViewUtil.getClearPanel();
        ViewUtil.applyHorizontalBoxLayout(p);
        JLabel l = new JLabel("Email: ");
        emailField = new JTextField();
        p.add(l);
        p.add(emailField);
        page.addComponent(p);

        autoPublish = new JCheckBox("Publish data upon import completion");
        autoPublish.setSelected(true);
        page.addComponent(autoPublish);
        page.addText(
                "If you choose not to automatically publish, you will be prompted to publish manually upon completion. Variant publication logs all users out.");

        return page;
    }

    private class CheckListItem extends JPanel {

        private final Reference reference;
        private final Set<Integer> selectedAnnotations = new HashSet<Integer>();
        private final JCheckBox checkBox;
        private final List<JCheckBox> annBoxes = new ArrayList<JCheckBox>();

        public CheckListItem(Reference ref, Annotation[] annotations) {

            this.reference = ref;

            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
            setBackground(Color.WHITE);

            checkBox = new JCheckBox(ref.getName());
            checkBox.setMaximumSize(new Dimension(1000, 20));
            checkBox.setBackground(getBackground());
            checkBox.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    boolean selected = checkBox.isSelected();
                    for (JCheckBox annBox : annBoxes) {
                        annBox.setEnabled(selected);
                    }
                }
            });
            ProjectDetails pd = getProjectDetails(ref.getID());
            boolean selected = pd != null;
            checkBox.setSelected(selected);

            JPanel p = new JPanel();
            p.setMaximumSize(new Dimension(1000, 20));
            p.setBackground(Color.white);
            p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
            p.add(checkBox);
            p.add(Box.createHorizontalGlue());
            add(p);

            for (final Annotation a : annotations) {

                //make sure annotation is for this reference
                if (a.getReferenceID() != reference.getID()) {
                    continue;
                }

                final JCheckBox b1 = new JCheckBox(a.getProgram() + " " + a.getVersion());
                b1.setMaximumSize(new Dimension(1000, 20));
                b1.setBackground(Color.white);
                b1.addItemListener(new ItemListener() {
                    @Override
                    public void itemStateChanged(ItemEvent e) {
                        if (b1.isSelected()) {
                            selectedAnnotations.add(a.getID());
                        } else {
                            selectedAnnotations.remove(a.getID());
                        }
                    }
                });
                annBoxes.add(b1);
                b1.setEnabled(selected);
                b1.setSelected(false);
                if (pd != null && ArrayUtils.contains(pd.getAnnotationIDs(), a.getID())) {
                    b1.setSelected(true);
                    selectedAnnotations.add(a.getID());
                }

                JPanel p1 = new JPanel();
                p1.setMaximumSize(new Dimension(1000, 20));
                p1.setBackground(Color.white);
                p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
                p1.add(Box.createHorizontalStrut(30));
                p1.add(b1);
                p1.add(Box.createHorizontalGlue());
                add(p1);
            }
        }

        public boolean isSelected() {
            return checkBox.isSelected();
        }

        public void setSelected(boolean value) {
            checkBox.setSelected(value);
        }

        public Reference getReference() {
            return reference;
        }

        public int[] getAnnotationIDs() {
            int[] result = new int[selectedAnnotations.size()];
            int i = 0;
            for (Integer ann : selectedAnnotations) {
                result[i++] = ann;
            }
            return result;
        }
    }

    private ProjectDetails getProjectDetails(int referenceId) {
        for (ProjectDetails pd : projectDetails) {
            if (pd.getReferenceID() == referenceId) {
                return pd;
            }
        }
        return null;
    }

    public boolean isModified() {
        return isModified;
    }

    private class RemovalEnabler implements ListSelectionListener {

        private final int lockedRows;
        private final JButton button;

        RemovalEnabler(int n, JButton b) {
            lockedRows = n;
            button = b;
        }

        @Override
        public void valueChanged(ListSelectionEvent lse) {
            int n = ((ListSelectionModel) lse.getSource()).getMinSelectionIndex();
            button.setEnabled(n >= lockedRows);
        }
    };
}