org.intermine.install.swing.AddSourceDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.intermine.install.swing.AddSourceDialog.java

Source

package org.intermine.install.swing;

/*
 * Copyright (C) 2002-2013 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.awt.Color;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.Collator;
import java.util.HashSet;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import org.apache.commons.lang.StringUtils;
import org.intermine.common.swing.ButtonPanel;
import org.intermine.common.swing.GenericMutableComboBoxModel;
import org.intermine.common.swing.GridBagHelper;
import org.intermine.common.swing.Messages;
import org.intermine.common.swing.StandardJDialog;
import org.intermine.common.swing.text.RestrictedInputDocument;
import org.intermine.install.project.event.ProjectListener;
import org.intermine.install.project.event.ProjectListenerSupport;
import org.intermine.install.project.source.PropertyDescriptor;
import org.intermine.install.project.source.SourceInfo;
import org.intermine.install.project.source.SourceInfoLoader;
import org.intermine.modelviewer.project.ObjectFactory;
import org.intermine.modelviewer.project.Project;
import org.intermine.modelviewer.project.Property;
import org.intermine.modelviewer.project.Source;

/**
 * Dialog for adding a data source to a project.
 */
public class AddSourceDialog extends StandardJDialog {
    private static final long serialVersionUID = 7202500451548893592L;

    /**
     * The permitted characters for the data source name.
     * @see RestrictedInputDocument
     */
    public static final String NAME_CHARACTERS = RestrictedInputDocument.LOWER_CASE + RestrictedInputDocument.DIGITS
            + "-_";

    /**
     * The data source name text field.
     * @serial
     */
    private JTextField nameField = new JTextField(new RestrictedInputDocument(NAME_CHARACTERS, true), "", 24);

    /**
     * The model for the data source type combo box.
     * @serial
     */
    private GenericMutableComboBoxModel<String> typeComboModel = new GenericMutableComboBoxModel<String>(
            Collator.getInstance());

    /**
     * The data source type combo box.
     * @serial
     */
    private JComboBox typeCombo = new JComboBox(typeComboModel);

    /**
     * The Action to add the source to the project.
     * @serial
     */
    private Action addAction = new AddAction();

    /**
     * The derived type dialog to use when the type selected needs to be
     * derived.
     * @serial
     */
    private NewDerivedTypeDialog newDerivedDialog;

    /**
     * Support for firing <code>ProjectListener</code> events.
     * @serial
     */
    private ProjectListenerSupport projectListenerSupport = new ProjectListenerSupport(this);

    /**
     * The project being edited.
     */
    private transient Project project;

    /**
     * The set of source names already used in the project.
     * @serial
     */
    private Set<String> sourceNames = new HashSet<String>();

    /**
     * Initialise with a parent Frame.
     * @param owner The parent Frame.
     */
    public AddSourceDialog(Frame owner) {
        super(owner);
        init();
    }

    /**
     * Initialise with a parent Dialog.
     * @param owner The parent Dialog.
     */
    public AddSourceDialog(Dialog owner) {
        super(owner);
        init();
    }

    /**
     * Initialise with a parent Window.
     * @param owner The parent Window.
     */
    public AddSourceDialog(Window owner) {
        super(owner);
        init();
    }

    /**
     * Common initialisation: lays out the child components and wires up the necessary
     * event listeners. 
     */
    private void init() {
        setName("Add Source Dialog");
        setTitle(Messages.getMessage("source.add.title"));

        Container cp = getContentPane();

        GridBagConstraints cons = GridBagHelper.setup(cp);

        cp.add(new JLabel(Messages.getMessage("source.type")), cons);

        cons.gridy++;
        cp.add(new JLabel(Messages.getMessage("source.name")), cons);

        cons.gridx++;
        cons.gridy = 0;
        cons.weightx = 1;
        cp.add(typeCombo, cons);

        cons.gridy++;
        cp.add(nameField, cons);

        cons.gridx = 0;
        cons.gridy++;
        cons.gridwidth = GridBagConstraints.REMAINDER;
        cp.add(new ButtonPanel(addAction, new CancelAction()), cons);

        pack();

        nameField.getDocument().addDocumentListener(new NameDocumentListener());
        typeCombo.addActionListener(new TypeComboListener());
    }

    /**
     * Set the dialog used when a derived type is selected.
     * 
     * @param newDerivedDialog The dialog following this in the creation chain.
     */
    public void setNewDerivedDialog(NewDerivedTypeDialog newDerivedDialog) {
        this.newDerivedDialog = newDerivedDialog;
    }

    /**
     * Initialise this dialog ready for an addition against the given project.
     * 
     * @param project The project to edit.
     */
    public void initialise(Project project) {
        this.project = project;

        if (typeComboModel.isEmpty()) {
            typeComboModel.set(SourceInfoLoader.getInstance().getSourceTypes());
        }

        sourceNames.clear();
        for (Source s : project.getSources().getSource()) {
            sourceNames.add(s.getName());
        }

        clearNameField();
    }

    /**
     * Reset the name field text to empty.
     */
    public void clearNameField() {
        nameField.setText("");
    }

    /**
      * Update the Name Field when the combo box is selected to give a sane default
      *
      */
    protected void updateName() {
        String type = (String) typeCombo.getSelectedItem();
        nameField.setText(type);
    }

    /**
     * Update the state of the "add" action. It requires a name in the text field
     * that has not already been used and for a type to be selected in the combo box.
     */
    protected void updateState() {
        String name = nameField.getText();
        boolean ok = name.length() > 0;
        ok = ok && !sourceNames.contains(name);
        updateState(ok);
    }

    /**
     * Update the state of the "add" action based on the type combo, assuming the name
     * text field has already been checked.
     * 
     * @param ok Whether the name field is ok.
     */
    protected void updateState(boolean ok) {
        ok = ok && typeCombo.getSelectedIndex() >= 0;
        addAction.setEnabled(ok);
    }

    /**
     * Add a project listener to this dialog.
     * 
     * @param l The ProjectListener.
     * 
     * @see ProjectListenerSupport#addProjectListener
     */
    public void addProjectListener(ProjectListener l) {
        projectListenerSupport.addProjectListener(l);
    }

    /**
     * Remove a project listener from this dialog.
     * 
     * @param l The ProjectListener.
     * 
     * @see ProjectListenerSupport#removeProjectListener
     */
    public void removeProjectListener(ProjectListener l) {
        projectListenerSupport.removeProjectListener(l);
    }

    /**
     * The Action to perform when the user is ready to add the data source.
     */
    private class AddAction extends AbstractAction {
        private static final long serialVersionUID = -1042891780324233318L;

        /**
         * Constructor.
         */
        public AddAction() {
            super(Messages.getMessage("add"));
            setEnabled(false);
        }

        /**
         * Create the new data Source object and add it to the project registered with
         * the dialog. The source is populated with default values if any are available
         * in the source descriptor. If the data source needs to be derived, control is
         * passed on to the NewDerivedTypeDialog as this dialog closes.
         * 
         * @param event The action event object.
         */
        @Override
        public void actionPerformed(ActionEvent event) {
            String name = nameField.getText();
            String type = (String) typeCombo.getSelectedItem();
            assert name.length() > 0 : "No name";
            assert !sourceNames.contains(name) : "Name already used";
            assert type != null : "No type";

            SourceInfo sourceInfo = SourceInfoLoader.getInstance().getSourceInfo(type);
            if (sourceInfo != null) {
                if (sourceInfo.getSource().getDerivation() != null) {
                    newDerivedDialog.initialise(project, sourceInfo, name);
                    newDerivedDialog.setLocation(getLocation());
                    setVisible(false);
                    newDerivedDialog.setVisible(true);
                    return;
                }
            }

            ObjectFactory objFactory = new ObjectFactory();

            Source newSource = objFactory.createSource();
            newSource.setName(name);
            newSource.setType(type);
            newSource.setDump(Boolean.FALSE);
            project.getSources().getSource().add(newSource);

            sourceInfo = SourceInfoLoader.getInstance().getSourceInfo(type);
            if (sourceInfo != null) {
                for (PropertyDescriptor descriptor : sourceInfo.getSource().getProperty()) {
                    String defaultValue = sourceInfo.getDefaults().getProperty(descriptor.getName());

                    if (defaultValue != null || descriptor.isRequired()) {
                        if (defaultValue == null) {
                            defaultValue = "";
                        }

                        Property p = objFactory.createProperty();
                        p.setName(descriptor.getName());
                        switch (descriptor.getType()) {
                        case FILE:
                        case DIRECTORY:
                            p.setLocation(defaultValue);
                            break;

                        default:
                            p.setValue(defaultValue);
                            break;
                        }
                        newSource.getProperty().add(p);
                    }
                }
            }

            projectListenerSupport.fireSourceAdded(project, newSource);

            setVisible(false);
        }
    }

    /**
     * Document listener for the name field. All changes cause the "add" action to be
     * enabled or disabled as the contents of the field become valid or invalid. Also
     * changes the background colour of the field to reflect this state.
     */
    private class NameDocumentListener implements DocumentListener {
        /**
         * The regular background colour.
         */
        private Color normal;

        /**
         * Constructor.
         */
        public NameDocumentListener() {
            normal = nameField.getBackground();
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            update();
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            update();
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            update();
        }

        /**
         * The update tests used by all forms of change (i.e. all methods from
         * <code>DocumentListener</code>).
         * 
         * @see AddSourceDialog#updateState()
         */
        private void update() {
            String name = nameField.getText();
            boolean ok = false;
            boolean setTooltip = false;
            if (StringUtils.isEmpty(name)) {
                nameField.setBackground(ProjectEditor.ERROR_FIELD_COLOR);
            } else if (sourceNames.contains(name)) {
                nameField.setBackground(ProjectEditor.ERROR_FIELD_COLOR);
                setTooltip = true;
            } else {
                nameField.setBackground(normal);
                ok = true;
            }

            if (setTooltip) {
                nameField.setToolTipText(Messages.getMessage("source.duplicatename", name));
            } else {
                nameField.setToolTipText(null);
            }

            updateState(ok);
        }
    }

    /**
     * Listener to the type combo box. Enables or disables the "add" action
     * according to whether anything is selected.
     */
    private class TypeComboListener implements ActionListener {
        /**
         * Update the state of the "add" action as the combo event is received.
         * 
         * @param event The action event object.
         * 
         * @see AddSourceDialog#updateState()
         */
        @Override
        public void actionPerformed(ActionEvent event) {
            updateName();
            updateState();
        }
    }
}